@aigne/core 1.1.0-beta.6 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -0
- package/lib/cjs/agents/agent.d.ts +37 -33
- package/lib/cjs/agents/agent.js +68 -28
- package/lib/cjs/agents/ai-agent.d.ts +8 -12
- package/lib/cjs/agents/ai-agent.js +17 -35
- package/lib/cjs/agents/mcp-agent.d.ts +25 -9
- package/lib/cjs/agents/mcp-agent.js +61 -14
- package/lib/cjs/agents/memory.d.ts +26 -0
- package/lib/cjs/agents/memory.js +38 -0
- package/lib/cjs/agents/types.d.ts +4 -3
- package/lib/cjs/agents/types.js +11 -1
- package/lib/cjs/agents/user-agent.d.ts +24 -0
- package/lib/cjs/agents/user-agent.js +62 -0
- package/lib/cjs/execution-engine/context.d.ts +35 -5
- package/lib/cjs/execution-engine/execution-engine.d.ts +64 -0
- package/lib/cjs/execution-engine/execution-engine.js +136 -0
- package/lib/cjs/execution-engine/index.d.ts +4 -46
- package/lib/cjs/execution-engine/index.js +17 -193
- package/lib/cjs/execution-engine/message-queue.d.ts +17 -2
- package/lib/cjs/execution-engine/message-queue.js +37 -1
- package/lib/cjs/execution-engine/utils.d.ts +4 -0
- package/lib/cjs/execution-engine/utils.js +32 -0
- package/lib/cjs/index.d.ts +14 -12
- package/lib/cjs/index.js +14 -12
- package/lib/{dts/models/chat.d.ts → cjs/models/chat-model.d.ts} +15 -12
- package/lib/cjs/models/{chat.js → chat-model.js} +5 -5
- package/lib/cjs/models/claude-chat-model.d.ts +17 -0
- package/lib/cjs/models/claude-chat-model.js +210 -0
- package/lib/cjs/models/{chat-openai.d.ts → openai-chat-model.d.ts} +2 -2
- package/lib/cjs/models/{chat-openai.js → openai-chat-model.js} +11 -10
- package/lib/cjs/prompt/prompt-builder.d.ts +14 -14
- package/lib/cjs/prompt/prompt-builder.js +62 -67
- package/lib/cjs/prompt/template.d.ts +9 -21
- package/lib/cjs/prompt/template.js +3 -5
- package/lib/cjs/utils/json-schema.d.ts +4 -0
- package/lib/cjs/utils/json-schema.js +34 -0
- package/lib/cjs/utils/mcp-utils.d.ts +3 -2
- package/lib/cjs/utils/mcp-utils.js +29 -17
- package/lib/cjs/utils/run-chat-loop.d.ts +5 -4
- package/lib/cjs/utils/run-chat-loop.js +25 -4
- package/lib/cjs/utils/type-utils.d.ts +3 -0
- package/lib/cjs/utils/type-utils.js +8 -8
- package/lib/dts/agents/agent.d.ts +37 -33
- package/lib/dts/agents/ai-agent.d.ts +8 -12
- package/lib/dts/agents/mcp-agent.d.ts +25 -9
- package/lib/dts/agents/memory.d.ts +26 -0
- package/lib/dts/agents/types.d.ts +4 -3
- package/lib/dts/agents/user-agent.d.ts +24 -0
- package/lib/dts/execution-engine/context.d.ts +35 -5
- package/lib/dts/execution-engine/execution-engine.d.ts +64 -0
- package/lib/dts/execution-engine/index.d.ts +4 -46
- package/lib/dts/execution-engine/message-queue.d.ts +17 -2
- package/lib/dts/execution-engine/utils.d.ts +4 -0
- package/lib/dts/index.d.ts +14 -12
- package/lib/{esm/models/chat.d.ts → dts/models/chat-model.d.ts} +15 -12
- package/lib/dts/models/claude-chat-model.d.ts +17 -0
- package/lib/dts/models/{chat-openai.d.ts → openai-chat-model.d.ts} +2 -2
- package/lib/dts/prompt/prompt-builder.d.ts +14 -14
- package/lib/dts/prompt/template.d.ts +9 -21
- package/lib/dts/utils/json-schema.d.ts +4 -0
- package/lib/dts/utils/mcp-utils.d.ts +3 -2
- package/lib/dts/utils/run-chat-loop.d.ts +5 -4
- package/lib/dts/utils/type-utils.d.ts +3 -0
- package/lib/esm/agents/agent.d.ts +37 -33
- package/lib/esm/agents/agent.js +67 -24
- package/lib/esm/agents/ai-agent.d.ts +8 -12
- package/lib/esm/agents/ai-agent.js +13 -31
- package/lib/esm/agents/mcp-agent.d.ts +25 -9
- package/lib/esm/agents/mcp-agent.js +54 -8
- package/lib/esm/agents/memory.d.ts +26 -0
- package/lib/esm/agents/memory.js +34 -0
- package/lib/esm/agents/types.d.ts +4 -3
- package/lib/esm/agents/types.js +10 -1
- package/lib/esm/agents/user-agent.d.ts +24 -0
- package/lib/esm/agents/user-agent.js +58 -0
- package/lib/esm/execution-engine/context.d.ts +35 -5
- package/lib/esm/execution-engine/execution-engine.d.ts +64 -0
- package/lib/esm/execution-engine/execution-engine.js +129 -0
- package/lib/esm/execution-engine/index.d.ts +4 -46
- package/lib/esm/execution-engine/index.js +4 -188
- package/lib/esm/execution-engine/message-queue.d.ts +17 -2
- package/lib/esm/execution-engine/message-queue.js +37 -1
- package/lib/esm/execution-engine/utils.d.ts +4 -0
- package/lib/esm/execution-engine/utils.js +28 -0
- package/lib/esm/index.d.ts +14 -12
- package/lib/esm/index.js +14 -12
- package/lib/{cjs/models/chat.d.ts → esm/models/chat-model.d.ts} +15 -12
- package/lib/esm/models/{chat.js → chat-model.js} +4 -4
- package/lib/esm/models/claude-chat-model.d.ts +17 -0
- package/lib/esm/models/claude-chat-model.js +203 -0
- package/lib/esm/models/{chat-openai.d.ts → openai-chat-model.d.ts} +2 -2
- package/lib/esm/models/{chat-openai.js → openai-chat-model.js} +8 -7
- package/lib/esm/prompt/prompt-builder.d.ts +14 -14
- package/lib/esm/prompt/prompt-builder.js +56 -58
- package/lib/esm/prompt/template.d.ts +9 -21
- package/lib/esm/prompt/template.js +3 -5
- package/lib/esm/utils/json-schema.d.ts +4 -0
- package/lib/esm/utils/json-schema.js +30 -0
- package/lib/esm/utils/mcp-utils.d.ts +3 -2
- package/lib/esm/utils/mcp-utils.js +26 -15
- package/lib/esm/utils/run-chat-loop.d.ts +5 -4
- package/lib/esm/utils/run-chat-loop.js +25 -4
- package/lib/esm/utils/type-utils.d.ts +3 -0
- package/lib/esm/utils/type-utils.js +4 -2
- package/package.json +31 -30
|
@@ -1,188 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { addMessagesToInput, userInput } from "../prompt/prompt-builder";
|
|
6
|
-
import { orArrayToArray } from "../utils/type-utils";
|
|
7
|
-
import { MessageQueue, UserInputTopic, UserOutputTopic } from "./message-queue";
|
|
8
|
-
export class ExecutionEngine extends EventEmitter {
|
|
9
|
-
constructor(options) {
|
|
10
|
-
super();
|
|
11
|
-
this.model = options?.model;
|
|
12
|
-
this.tools = options?.tools ?? [];
|
|
13
|
-
if (options?.agents?.length)
|
|
14
|
-
this.addAgent(...options.agents);
|
|
15
|
-
this.initProcessExitHandler();
|
|
16
|
-
}
|
|
17
|
-
messageQueue = new MessageQueue();
|
|
18
|
-
model;
|
|
19
|
-
tools;
|
|
20
|
-
agents = [];
|
|
21
|
-
agentListeners = new Map();
|
|
22
|
-
addAgent(...agents) {
|
|
23
|
-
for (const agent of agents) {
|
|
24
|
-
this.agents.push(agent);
|
|
25
|
-
this.attachAgentSubscriptions(agent);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
attachAgentSubscriptions(agent) {
|
|
29
|
-
for (const topic of orArrayToArray(agent.subscribeTopic)) {
|
|
30
|
-
const listener = async (input) => {
|
|
31
|
-
try {
|
|
32
|
-
const { output } = await this.callAgent(input, agent);
|
|
33
|
-
const topics = typeof agent.publishTopic === "function"
|
|
34
|
-
? await agent.publishTopic(output)
|
|
35
|
-
: agent.publishTopic;
|
|
36
|
-
for (const topic of orArrayToArray(topics)) {
|
|
37
|
-
this.publish(topic, output);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
this.emit("error", error);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
this.subscribe(topic, listener);
|
|
45
|
-
const listeners = this.agentListeners.get(agent) || [];
|
|
46
|
-
listeners.push({ topic, listener });
|
|
47
|
-
this.agentListeners.set(agent, listeners);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
publish(topic, message) {
|
|
51
|
-
this.messageQueue.emit(topic, message);
|
|
52
|
-
}
|
|
53
|
-
subscribe(topic, listener) {
|
|
54
|
-
this.messageQueue.on(topic, listener);
|
|
55
|
-
}
|
|
56
|
-
unsubscribe(topic, listener) {
|
|
57
|
-
this.messageQueue.off(topic, listener);
|
|
58
|
-
}
|
|
59
|
-
async run(_input, _options, ..._agents) {
|
|
60
|
-
if (_input instanceof Agent)
|
|
61
|
-
return this.runChatLoop(_input);
|
|
62
|
-
const [options, agents] = this.splitOptionsAndAgents(_options, ..._agents);
|
|
63
|
-
const input = typeof _input === "string" ? userInput(_input) : _input;
|
|
64
|
-
if (agents.length === 0)
|
|
65
|
-
return this.publishUserInputTopic(input);
|
|
66
|
-
if (options?.concurrency)
|
|
67
|
-
return this.runParallel(input, ...agents);
|
|
68
|
-
return this.runSequential(input, ...agents);
|
|
69
|
-
}
|
|
70
|
-
splitOptionsAndAgents(options, ...agents) {
|
|
71
|
-
if (options instanceof Agent) {
|
|
72
|
-
return [{}, [options, ...agents]];
|
|
73
|
-
}
|
|
74
|
-
return [options, agents];
|
|
75
|
-
}
|
|
76
|
-
async runSequential(input, ...agents) {
|
|
77
|
-
const output = {};
|
|
78
|
-
for (const agent of agents.flat()) {
|
|
79
|
-
const { output: o } = await this.callAgent({ ...input, ...output }, agent);
|
|
80
|
-
Object.assign(output, o);
|
|
81
|
-
}
|
|
82
|
-
return output;
|
|
83
|
-
}
|
|
84
|
-
async runParallel(input, ...agents) {
|
|
85
|
-
const outputs = await Promise.all(agents.map((agent) => this.callAgent(input, agent).then((res) => res.output)));
|
|
86
|
-
return Object.assign({}, ...outputs);
|
|
87
|
-
}
|
|
88
|
-
async publishUserInputTopic(input) {
|
|
89
|
-
this.publish(UserInputTopic, input);
|
|
90
|
-
// TODO: 处理超时、错误、无限循环、饿死等情况
|
|
91
|
-
const result = await new Promise((resolve) => {
|
|
92
|
-
this.messageQueue.on(UserOutputTopic, (result) => resolve(result));
|
|
93
|
-
});
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
async runChatLoop(agent) {
|
|
97
|
-
const inputStream = new TransformStream({});
|
|
98
|
-
const inputWriter = inputStream.writable.getWriter();
|
|
99
|
-
const userAgent = new UserAgent({
|
|
100
|
-
async run(input) {
|
|
101
|
-
const wait = Promise.withResolvers();
|
|
102
|
-
inputWriter.write({ ...wait, input });
|
|
103
|
-
return wait.promise;
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
// Run the loop in a separate async function, so that we can return the userAgent
|
|
107
|
-
(async () => {
|
|
108
|
-
let activeAgent = agent;
|
|
109
|
-
const reader = inputStream.readable.getReader();
|
|
110
|
-
for (;;) {
|
|
111
|
-
const { value, done } = await reader.read();
|
|
112
|
-
if (done)
|
|
113
|
-
break;
|
|
114
|
-
const { input, resolve } = value;
|
|
115
|
-
const { agent, output } = await this.callAgent(input, activeAgent);
|
|
116
|
-
if (agent)
|
|
117
|
-
activeAgent = agent;
|
|
118
|
-
resolve(output);
|
|
119
|
-
}
|
|
120
|
-
})();
|
|
121
|
-
return userAgent;
|
|
122
|
-
}
|
|
123
|
-
async callAgent(input, agent) {
|
|
124
|
-
let activeAgent = agent;
|
|
125
|
-
let output;
|
|
126
|
-
for (;;) {
|
|
127
|
-
output = await activeAgent.call(input, this);
|
|
128
|
-
if (isTransferAgentOutput(output)) {
|
|
129
|
-
// TODO: 不要修改原始对象,可能被外部丢弃
|
|
130
|
-
const transferToolCallId = nanoid();
|
|
131
|
-
Object.assign(input, addMessagesToInput(input, [
|
|
132
|
-
{
|
|
133
|
-
role: "agent",
|
|
134
|
-
name: agent.name,
|
|
135
|
-
toolCalls: [
|
|
136
|
-
{
|
|
137
|
-
id: transferToolCallId,
|
|
138
|
-
type: "function",
|
|
139
|
-
function: {
|
|
140
|
-
name: "transfer_to_agent",
|
|
141
|
-
arguments: {
|
|
142
|
-
to: output[transferAgentOutputKey].agent.name,
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
],
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
role: "tool",
|
|
150
|
-
toolCallId: transferToolCallId,
|
|
151
|
-
content: `Transferred to ${output[transferAgentOutputKey].agent.name}. Adopt persona immediately.`,
|
|
152
|
-
},
|
|
153
|
-
]));
|
|
154
|
-
activeAgent = output[transferAgentOutputKey].agent;
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return {
|
|
161
|
-
agent: activeAgent,
|
|
162
|
-
output,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
async shutdown() {
|
|
166
|
-
for (const tool of this.tools) {
|
|
167
|
-
await tool.shutdown();
|
|
168
|
-
}
|
|
169
|
-
for (const agent of this.agents) {
|
|
170
|
-
await agent.shutdown();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
initProcessExitHandler() {
|
|
174
|
-
const shutdownAndExit = () => this.shutdown().finally(() => process.exit(0));
|
|
175
|
-
process.on("SIGINT", shutdownAndExit);
|
|
176
|
-
process.on("exit", shutdownAndExit);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
export class UserAgent extends Agent {
|
|
180
|
-
options;
|
|
181
|
-
constructor(options) {
|
|
182
|
-
super({ ...options, disableLogging: true });
|
|
183
|
-
this.options = options;
|
|
184
|
-
}
|
|
185
|
-
process(input) {
|
|
186
|
-
return this.options.run(input);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
1
|
+
export * from "./context.js";
|
|
2
|
+
export * from "./execution-engine.js";
|
|
3
|
+
export * from "./message-queue.js";
|
|
4
|
+
export * from "./utils.js";
|
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Message } from "../agents/agent.js";
|
|
2
2
|
export declare const UserInputTopic = "UserInputTopic";
|
|
3
3
|
export declare const UserOutputTopic = "UserOutputTopic";
|
|
4
|
-
export
|
|
4
|
+
export interface MessagePayload {
|
|
5
|
+
role: "user" | "agent";
|
|
6
|
+
source?: string;
|
|
7
|
+
message: Message;
|
|
8
|
+
}
|
|
9
|
+
export type MessageQueueListener = (message: MessagePayload) => void;
|
|
10
|
+
export type MessageRequest = MessagePayload;
|
|
11
|
+
export type Unsubscribe = () => void;
|
|
12
|
+
export declare class MessageQueue {
|
|
13
|
+
private events;
|
|
14
|
+
publish(topic: string | string[], message: MessageRequest): void;
|
|
15
|
+
error(error: Error): void;
|
|
16
|
+
subscribe(topic: string, listener?: undefined): Promise<MessagePayload>;
|
|
17
|
+
subscribe(topic: string, listener: MessageQueueListener): Unsubscribe;
|
|
18
|
+
subscribe(topic: string, listener?: MessageQueueListener): Unsubscribe | Promise<MessagePayload>;
|
|
19
|
+
unsubscribe(topic: string, listener: MessageQueueListener): void;
|
|
5
20
|
}
|
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
+
import { orArrayToArray } from "../utils/type-utils.js";
|
|
2
3
|
export const UserInputTopic = "UserInputTopic";
|
|
3
4
|
export const UserOutputTopic = "UserOutputTopic";
|
|
4
|
-
export class MessageQueue
|
|
5
|
+
export class MessageQueue {
|
|
6
|
+
events = new EventEmitter();
|
|
7
|
+
publish(topic, message) {
|
|
8
|
+
for (const t of orArrayToArray(topic)) {
|
|
9
|
+
this.events.emit(t, message);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
error(error) {
|
|
13
|
+
this.events.emit("error", error);
|
|
14
|
+
}
|
|
15
|
+
subscribe(topic, listener) {
|
|
16
|
+
if (!listener) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const unsubscribe1 = once(this.events, topic, (message) => {
|
|
19
|
+
unsubscribe2();
|
|
20
|
+
resolve(message);
|
|
21
|
+
});
|
|
22
|
+
const unsubscribe2 = once(this.events, "error", (error) => {
|
|
23
|
+
unsubscribe1();
|
|
24
|
+
reject(error);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return on(this.events, topic, listener);
|
|
29
|
+
}
|
|
30
|
+
unsubscribe(topic, listener) {
|
|
31
|
+
this.events.off(topic, listener);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function on(events, event, listener) {
|
|
35
|
+
events.on(event, listener);
|
|
36
|
+
return () => events.off(event, listener);
|
|
37
|
+
}
|
|
38
|
+
function once(events, event, listener) {
|
|
39
|
+
events.once(event, listener);
|
|
40
|
+
return () => events.off(event, listener);
|
|
5
41
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FunctionAgentFn } from "../agents/agent.js";
|
|
2
|
+
import type { Runnable } from "./context.js";
|
|
3
|
+
export declare function sequential(..._agents: [Runnable, ...Runnable[]]): FunctionAgentFn;
|
|
4
|
+
export declare function parallel(..._agents: [Runnable, ...Runnable[]]): FunctionAgentFn;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function sequential(..._agents) {
|
|
2
|
+
let agents = [..._agents];
|
|
3
|
+
return async (input, context) => {
|
|
4
|
+
if (!context)
|
|
5
|
+
throw new Error("Context is required for executing sequential agents. Please provide a valid context.");
|
|
6
|
+
const output = {};
|
|
7
|
+
// Clone the agents to run, so that we can update the agents list during the loop
|
|
8
|
+
const agentsToRun = [...agents];
|
|
9
|
+
agents = [];
|
|
10
|
+
for (const agent of agentsToRun) {
|
|
11
|
+
const [o, transferToAgent] = await context.call(agent, { ...input, ...output }, { returnActiveAgent: true });
|
|
12
|
+
Object.assign(output, o);
|
|
13
|
+
agents.push(transferToAgent);
|
|
14
|
+
}
|
|
15
|
+
return output;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function parallel(..._agents) {
|
|
19
|
+
let agents = [..._agents];
|
|
20
|
+
return async (input, context) => {
|
|
21
|
+
if (!context)
|
|
22
|
+
throw new Error("Context is required for executing parallel agents. Please provide a valid context.");
|
|
23
|
+
const result = await Promise.all(agents.map((agent) => context.call(agent, input, { returnActiveAgent: true })));
|
|
24
|
+
agents = result.map((i) => i[1]);
|
|
25
|
+
const outputs = result.map((i) => i[0]);
|
|
26
|
+
return Object.assign({}, ...outputs);
|
|
27
|
+
};
|
|
28
|
+
}
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
export * from "./agents/agent";
|
|
2
|
-
export * from "./agents/ai-agent";
|
|
3
|
-
export * from "./agents/mcp-agent";
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
6
|
-
export * from "./
|
|
7
|
-
export * from "./
|
|
8
|
-
export * from "./models/chat-
|
|
9
|
-
export * from "./
|
|
10
|
-
export * from "./
|
|
11
|
-
export * from "./
|
|
12
|
-
export * from "./
|
|
1
|
+
export * from "./agents/agent.js";
|
|
2
|
+
export * from "./agents/ai-agent.js";
|
|
3
|
+
export * from "./agents/mcp-agent.js";
|
|
4
|
+
export * from "./agents/memory.js";
|
|
5
|
+
export * from "./agents/types.js";
|
|
6
|
+
export * from "./agents/user-agent.js";
|
|
7
|
+
export * from "./execution-engine/index.js";
|
|
8
|
+
export * from "./models/chat-model.js";
|
|
9
|
+
export * from "./models/claude-chat-model.js";
|
|
10
|
+
export * from "./models/openai-chat-model.js";
|
|
11
|
+
export * from "./prompt/prompt-builder.js";
|
|
12
|
+
export * from "./prompt/template.js";
|
|
13
|
+
export * from "./utils/logger.js";
|
|
14
|
+
export * from "./utils/run-chat-loop.js";
|
package/lib/esm/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
export * from "./agents/agent";
|
|
2
|
-
export * from "./agents/ai-agent";
|
|
3
|
-
export * from "./agents/mcp-agent";
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
6
|
-
export * from "./
|
|
7
|
-
export * from "./
|
|
8
|
-
export * from "./models/chat-
|
|
9
|
-
export * from "./
|
|
10
|
-
export * from "./
|
|
11
|
-
export * from "./
|
|
12
|
-
export * from "./
|
|
1
|
+
export * from "./agents/agent.js";
|
|
2
|
+
export * from "./agents/ai-agent.js";
|
|
3
|
+
export * from "./agents/mcp-agent.js";
|
|
4
|
+
export * from "./agents/memory.js";
|
|
5
|
+
export * from "./agents/types.js";
|
|
6
|
+
export * from "./agents/user-agent.js";
|
|
7
|
+
export * from "./execution-engine/index.js";
|
|
8
|
+
export * from "./models/chat-model.js";
|
|
9
|
+
export * from "./models/claude-chat-model.js";
|
|
10
|
+
export * from "./models/openai-chat-model.js";
|
|
11
|
+
export * from "./prompt/prompt-builder.js";
|
|
12
|
+
export * from "./prompt/template.js";
|
|
13
|
+
export * from "./utils/logger.js";
|
|
14
|
+
export * from "./utils/run-chat-loop.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Agent, type
|
|
1
|
+
import { Agent, type Message } from "../agents/agent.js";
|
|
2
2
|
export declare abstract class ChatModel extends Agent<ChatModelInput, ChatModelOutput> {
|
|
3
3
|
constructor();
|
|
4
4
|
}
|
|
5
|
-
export interface ChatModelInput extends
|
|
5
|
+
export interface ChatModelInput extends Message {
|
|
6
6
|
messages: ChatModelInputMessage[];
|
|
7
7
|
responseFormat?: ChatModelInputResponseFormat;
|
|
8
8
|
tools?: ChatModelInputTool[];
|
|
@@ -12,24 +12,27 @@ export interface ChatModelInput extends AgentInput {
|
|
|
12
12
|
export type Role = "system" | "user" | "agent" | "tool";
|
|
13
13
|
export interface ChatModelInputMessage {
|
|
14
14
|
role: Role;
|
|
15
|
-
content?:
|
|
16
|
-
type: "text";
|
|
17
|
-
text: string;
|
|
18
|
-
} | {
|
|
19
|
-
type: "image_url";
|
|
20
|
-
url: string;
|
|
21
|
-
})[];
|
|
15
|
+
content?: ChatModelInputMessageContent;
|
|
22
16
|
toolCalls?: {
|
|
23
17
|
id: string;
|
|
24
18
|
type: "function";
|
|
25
19
|
function: {
|
|
26
20
|
name: string;
|
|
27
|
-
arguments:
|
|
21
|
+
arguments: Message;
|
|
28
22
|
};
|
|
29
23
|
}[];
|
|
30
24
|
toolCallId?: string;
|
|
31
25
|
name?: string;
|
|
32
26
|
}
|
|
27
|
+
export type ChatModelInputMessageContent = string | (TextContent | ImageUrlContent)[];
|
|
28
|
+
export type TextContent = {
|
|
29
|
+
type: "text";
|
|
30
|
+
text: string;
|
|
31
|
+
};
|
|
32
|
+
export type ImageUrlContent = {
|
|
33
|
+
type: "image_url";
|
|
34
|
+
url: string;
|
|
35
|
+
};
|
|
33
36
|
export type ChatModelInputResponseFormat = {
|
|
34
37
|
type: "text";
|
|
35
38
|
} | {
|
|
@@ -63,7 +66,7 @@ export interface ChatModelOptions {
|
|
|
63
66
|
frequencyPenalty?: number;
|
|
64
67
|
presencePenalty?: number;
|
|
65
68
|
}
|
|
66
|
-
export interface ChatModelOutput extends
|
|
69
|
+
export interface ChatModelOutput extends Message {
|
|
67
70
|
text?: string;
|
|
68
71
|
json?: object;
|
|
69
72
|
toolCalls?: ChatModelOutputToolCall[];
|
|
@@ -73,6 +76,6 @@ export interface ChatModelOutputToolCall {
|
|
|
73
76
|
type: "function";
|
|
74
77
|
function: {
|
|
75
78
|
name: string;
|
|
76
|
-
arguments:
|
|
79
|
+
arguments: Message;
|
|
77
80
|
};
|
|
78
81
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { Agent } from "../agents/agent";
|
|
2
|
+
import { Agent } from "../agents/agent.js";
|
|
3
3
|
export class ChatModel extends Agent {
|
|
4
4
|
constructor() {
|
|
5
5
|
super({
|
|
@@ -32,8 +32,8 @@ const chatModelInputMessageSchema = z.object({
|
|
|
32
32
|
toolCallId: z.string().optional(),
|
|
33
33
|
name: z.string().optional(),
|
|
34
34
|
});
|
|
35
|
-
const chatModelInputResponseFormatSchema = z.
|
|
36
|
-
z.literal("text"),
|
|
35
|
+
const chatModelInputResponseFormatSchema = z.discriminatedUnion("type", [
|
|
36
|
+
z.object({ type: z.literal("text") }),
|
|
37
37
|
z.object({
|
|
38
38
|
type: z.literal("json_schema"),
|
|
39
39
|
jsonSchema: z.object({
|
|
@@ -82,6 +82,6 @@ const chatModelOutputToolCallSchema = z.object({
|
|
|
82
82
|
});
|
|
83
83
|
const chatModelOutputSchema = z.object({
|
|
84
84
|
text: z.string().optional(),
|
|
85
|
-
json: z.unknown().optional(),
|
|
85
|
+
json: z.record(z.unknown()).optional(),
|
|
86
86
|
toolCalls: z.array(chatModelOutputToolCallSchema).optional(),
|
|
87
87
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { ChatModel, type ChatModelInput, type ChatModelOutput } from "./chat-model.js";
|
|
3
|
+
export declare class ClaudeChatModel extends ChatModel {
|
|
4
|
+
config?: {
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
model?: string;
|
|
7
|
+
} | undefined;
|
|
8
|
+
constructor(config?: {
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
} | undefined);
|
|
12
|
+
private _client?;
|
|
13
|
+
get client(): Anthropic;
|
|
14
|
+
process(input: ChatModelInput): Promise<ChatModelOutput>;
|
|
15
|
+
private extractResultFromClaudeStream;
|
|
16
|
+
private requestStructuredOutput;
|
|
17
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { isEmpty } from "lodash-es";
|
|
3
|
+
import { parseJSON } from "../utils/json-schema.js";
|
|
4
|
+
import { isNonNullable } from "../utils/type-utils.js";
|
|
5
|
+
import { ChatModel, } from "./chat-model.js";
|
|
6
|
+
const CHAT_MODEL_CLAUDE_DEFAULT_MODEL = "claude-3-7-sonnet-latest";
|
|
7
|
+
export class ClaudeChatModel extends ChatModel {
|
|
8
|
+
config;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
super();
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
_client;
|
|
14
|
+
get client() {
|
|
15
|
+
if (!this.config?.apiKey)
|
|
16
|
+
throw new Error("Api Key is required for ClaudeChatModel");
|
|
17
|
+
this._client ??= new Anthropic({ apiKey: this.config.apiKey });
|
|
18
|
+
return this._client;
|
|
19
|
+
}
|
|
20
|
+
async process(input) {
|
|
21
|
+
const model = this.config?.model || CHAT_MODEL_CLAUDE_DEFAULT_MODEL;
|
|
22
|
+
const body = {
|
|
23
|
+
model,
|
|
24
|
+
temperature: input.modelOptions?.temperature,
|
|
25
|
+
top_p: input.modelOptions?.topP,
|
|
26
|
+
// TODO: make dynamic based on model https://docs.anthropic.com/en/docs/about-claude/models/all-models
|
|
27
|
+
max_tokens: /claude-3-[5|7]/.test(model) ? 8192 : 4096,
|
|
28
|
+
...convertMessages(input),
|
|
29
|
+
...convertTools(input),
|
|
30
|
+
};
|
|
31
|
+
const stream = this.client.messages.stream({
|
|
32
|
+
...body,
|
|
33
|
+
stream: true,
|
|
34
|
+
});
|
|
35
|
+
const result = await this.extractResultFromClaudeStream(stream);
|
|
36
|
+
// Claude doesn't support json_schema response and tool calls in the same request,
|
|
37
|
+
// so we need to make a separate request for json_schema response when the tool calls is empty
|
|
38
|
+
if (!result.toolCalls?.length && input.responseFormat?.type === "json_schema") {
|
|
39
|
+
return this.requestStructuredOutput(body, input.responseFormat);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
async extractResultFromClaudeStream(stream) {
|
|
44
|
+
let text = "";
|
|
45
|
+
const toolCalls = [];
|
|
46
|
+
for await (const chunk of stream) {
|
|
47
|
+
// handle streaming text
|
|
48
|
+
if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
|
|
49
|
+
text += chunk.delta.text;
|
|
50
|
+
}
|
|
51
|
+
if (chunk.type === "content_block_start" && chunk.content_block.type === "tool_use") {
|
|
52
|
+
toolCalls[chunk.index] = {
|
|
53
|
+
type: "function",
|
|
54
|
+
id: chunk.content_block.id,
|
|
55
|
+
function: {
|
|
56
|
+
name: chunk.content_block.name,
|
|
57
|
+
arguments: {},
|
|
58
|
+
},
|
|
59
|
+
args: "",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (chunk.type === "content_block_delta" && chunk.delta.type === "input_json_delta") {
|
|
63
|
+
const call = toolCalls[chunk.index];
|
|
64
|
+
if (!call)
|
|
65
|
+
throw new Error("Tool call not found");
|
|
66
|
+
call.args += chunk.delta.partial_json;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const result = { text };
|
|
70
|
+
if (toolCalls.length) {
|
|
71
|
+
result.toolCalls = toolCalls
|
|
72
|
+
.map(({ args, ...c }) => ({
|
|
73
|
+
...c,
|
|
74
|
+
function: { ...c.function, arguments: parseJSON(args) },
|
|
75
|
+
}))
|
|
76
|
+
.filter(isNonNullable);
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
async requestStructuredOutput(body, responseFormat) {
|
|
81
|
+
if (responseFormat?.type !== "json_schema") {
|
|
82
|
+
throw new Error("Expected json_schema response format");
|
|
83
|
+
}
|
|
84
|
+
const result = await this.client.messages.create({
|
|
85
|
+
...body,
|
|
86
|
+
tools: [
|
|
87
|
+
{
|
|
88
|
+
name: "generate_json",
|
|
89
|
+
description: "Generate a json result by given context",
|
|
90
|
+
input_schema: responseFormat.jsonSchema.schema,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
tool_choice: {
|
|
94
|
+
type: "tool",
|
|
95
|
+
name: "generate_json",
|
|
96
|
+
disable_parallel_tool_use: true,
|
|
97
|
+
},
|
|
98
|
+
stream: false,
|
|
99
|
+
});
|
|
100
|
+
const jsonTool = result.content.find((i) => i.type === "tool_use" && i.name === "generate_json");
|
|
101
|
+
if (!jsonTool)
|
|
102
|
+
throw new Error("Json tool not found");
|
|
103
|
+
return {
|
|
104
|
+
json: jsonTool.input,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function convertMessages({ messages, responseFormat }) {
|
|
109
|
+
const systemMessages = [];
|
|
110
|
+
const msgs = [];
|
|
111
|
+
for (const msg of messages) {
|
|
112
|
+
if (msg.role === "system") {
|
|
113
|
+
if (typeof msg.content !== "string")
|
|
114
|
+
throw new Error("System message must have content");
|
|
115
|
+
systemMessages.push(msg.content);
|
|
116
|
+
}
|
|
117
|
+
else if (msg.role === "tool") {
|
|
118
|
+
if (!msg.toolCallId)
|
|
119
|
+
throw new Error("Tool message must have toolCallId");
|
|
120
|
+
if (typeof msg.content !== "string")
|
|
121
|
+
throw new Error("Tool message must have string content");
|
|
122
|
+
msgs.push({
|
|
123
|
+
role: "user",
|
|
124
|
+
content: [{ type: "tool_result", tool_use_id: msg.toolCallId, content: msg.content }],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else if (msg.role === "user") {
|
|
128
|
+
if (!msg.content)
|
|
129
|
+
throw new Error("User message must have content");
|
|
130
|
+
msgs.push({ role: "user", content: convertContent(msg.content) });
|
|
131
|
+
}
|
|
132
|
+
else if (msg.role === "agent") {
|
|
133
|
+
if (msg.toolCalls?.length) {
|
|
134
|
+
msgs.push({
|
|
135
|
+
role: "assistant",
|
|
136
|
+
content: msg.toolCalls.map((i) => ({
|
|
137
|
+
type: "tool_use",
|
|
138
|
+
id: i.id,
|
|
139
|
+
name: i.function.name,
|
|
140
|
+
input: i.function.arguments,
|
|
141
|
+
})),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else if (msg.content) {
|
|
145
|
+
msgs.push({ role: "assistant", content: convertContent(msg.content) });
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
throw new Error("Agent message must have content or toolCalls");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (responseFormat?.type === "json_schema") {
|
|
153
|
+
systemMessages.push(`You should provide a json response with schema: ${JSON.stringify(responseFormat.jsonSchema.schema)}`);
|
|
154
|
+
}
|
|
155
|
+
const system = systemMessages.join("\n").trim() || undefined;
|
|
156
|
+
// Claude requires at least one message, so we add a system message if there are no messages
|
|
157
|
+
if (msgs.length === 0) {
|
|
158
|
+
if (!system)
|
|
159
|
+
throw new Error("No messages provided");
|
|
160
|
+
return { messages: [{ role: "user", content: system }] };
|
|
161
|
+
}
|
|
162
|
+
return { messages: msgs, system };
|
|
163
|
+
}
|
|
164
|
+
function convertContent(content) {
|
|
165
|
+
if (typeof content === "string")
|
|
166
|
+
return content;
|
|
167
|
+
if (Array.isArray(content)) {
|
|
168
|
+
return content.map((item) => item.type === "image_url"
|
|
169
|
+
? { type: "image", source: { type: "url", url: item.url } }
|
|
170
|
+
: { type: "text", text: item.text });
|
|
171
|
+
}
|
|
172
|
+
throw new Error("Invalid chat message content");
|
|
173
|
+
}
|
|
174
|
+
function convertTools({ tools, toolChoice, }) {
|
|
175
|
+
let choice;
|
|
176
|
+
if (typeof toolChoice === "object" && "type" in toolChoice && toolChoice.type === "function") {
|
|
177
|
+
choice = {
|
|
178
|
+
type: "tool",
|
|
179
|
+
name: toolChoice.function.name,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
else if (toolChoice === "required") {
|
|
183
|
+
choice = { type: "any" };
|
|
184
|
+
}
|
|
185
|
+
else if (toolChoice === "auto") {
|
|
186
|
+
choice = { type: "auto" };
|
|
187
|
+
}
|
|
188
|
+
else if (toolChoice === "none") {
|
|
189
|
+
choice = { type: "none" };
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
tools: tools?.length
|
|
193
|
+
? tools.map((i) => ({
|
|
194
|
+
name: i.function.name,
|
|
195
|
+
description: i.function.description,
|
|
196
|
+
input_schema: isEmpty(i.function.parameters)
|
|
197
|
+
? { type: "object" }
|
|
198
|
+
: i.function.parameters,
|
|
199
|
+
}))
|
|
200
|
+
: undefined,
|
|
201
|
+
tool_choice: choice,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ChatModel, type ChatModelInput, type ChatModelOutput } from "./chat";
|
|
2
|
-
export declare class
|
|
1
|
+
import { ChatModel, type ChatModelInput, type ChatModelOutput } from "./chat-model.js";
|
|
2
|
+
export declare class OpenAIChatModel extends ChatModel {
|
|
3
3
|
config?: {
|
|
4
4
|
apiKey?: string;
|
|
5
5
|
model?: string;
|