@arki-moe/agent-ts 2.2.1 → 2.2.3
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/README.md +2 -2
- package/dist/adapter/openai.js +37 -0
- package/dist/adapter/openrouter.js +37 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +25 -10
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ When `apiKey` is not provided in config, adapters read from the corresponding en
|
|
|
53
53
|
- `Agent(adapterName, config)` - Create Agent
|
|
54
54
|
- `agent.context` - Public property, complete conversation history
|
|
55
55
|
- `agent.registerTool(tool)` - Register tool
|
|
56
|
-
- `agent.run(message)` - Execute tool chain automatically, returns all new `Message[]`
|
|
56
|
+
- `agent.run(message, options?)` - Execute tool chain automatically, returns all new `Message[]`
|
|
57
57
|
- `agent.fork()` - Create a new agent with a copied context
|
|
58
58
|
|
|
59
59
|
### Config
|
|
@@ -68,7 +68,7 @@ When `apiKey` is not provided in config, adapters read from the corresponding en
|
|
|
68
68
|
| `onToolCall` | `(message, args) => boolean \| void \| Promise<boolean \| void>` | Called before each tool execution; return `false` to skip tool execution and `onToolResult` |
|
|
69
69
|
| `onToolResult` | `(message) => void \| Promise<void>` | Called after each tool execution (`message.role === Role.ToolResult`) |
|
|
70
70
|
|
|
71
|
-
`agent.run` always appends new messages to `agent.context`. Multiple tool calls in a single model response are executed in parallel.
|
|
71
|
+
`agent.run` always appends new messages to `agent.context`. Set `options.once = true` to avoid persisting the user message (useful for one-shot hints). Multiple tool calls in a single model response are executed in parallel.
|
|
72
72
|
|
|
73
73
|
`onToolCall` receives parsed JSON args and can mutate them before execution. Returning `false` skips the tool call and does not emit a `ToolResult` message.
|
|
74
74
|
|
package/dist/adapter/openai.js
CHANGED
|
@@ -66,6 +66,24 @@ async function openaiAdapter(config, context, tools) {
|
|
|
66
66
|
}
|
|
67
67
|
if (onStream) {
|
|
68
68
|
let content = "";
|
|
69
|
+
const toolCalls = new Map();
|
|
70
|
+
const upsertToolCall = (tc) => {
|
|
71
|
+
const index = typeof tc?.index === "number" ? tc.index : toolCalls.size;
|
|
72
|
+
let entry = toolCalls.get(index);
|
|
73
|
+
if (!entry) {
|
|
74
|
+
entry = { args: "" };
|
|
75
|
+
toolCalls.set(index, entry);
|
|
76
|
+
}
|
|
77
|
+
if (typeof tc?.id === "string")
|
|
78
|
+
entry.id = tc.id;
|
|
79
|
+
const fn = tc?.function;
|
|
80
|
+
if (fn) {
|
|
81
|
+
if (typeof fn.name === "string")
|
|
82
|
+
entry.name = fn.name;
|
|
83
|
+
if (typeof fn.arguments === "string")
|
|
84
|
+
entry.args += fn.arguments;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
69
87
|
await (0, sse_1.readSse)(res, async (dataLine) => {
|
|
70
88
|
if (dataLine === "[DONE]")
|
|
71
89
|
return;
|
|
@@ -79,11 +97,30 @@ async function openaiAdapter(config, context, tools) {
|
|
|
79
97
|
const delta = parsed?.choices?.[0]?.delta;
|
|
80
98
|
if (!delta)
|
|
81
99
|
return;
|
|
100
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
101
|
+
for (const tc of delta.tool_calls) {
|
|
102
|
+
upsertToolCall(tc);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
82
105
|
if (typeof delta.content === "string") {
|
|
83
106
|
content += delta.content;
|
|
84
107
|
await Promise.resolve(onStream(delta.content));
|
|
85
108
|
}
|
|
86
109
|
});
|
|
110
|
+
if (toolCalls.size > 0) {
|
|
111
|
+
return [...toolCalls.entries()]
|
|
112
|
+
.sort((a, b) => a[0] - b[0])
|
|
113
|
+
.map(([index, tc]) => {
|
|
114
|
+
if (!tc.name)
|
|
115
|
+
throw new Error(`OpenAI streaming tool call missing function name at index ${index}`);
|
|
116
|
+
return {
|
|
117
|
+
role: types_1.Role.ToolCall,
|
|
118
|
+
toolName: tc.name,
|
|
119
|
+
callId: tc.id ?? `call_${index}`,
|
|
120
|
+
argsText: tc.args.length ? tc.args : "{}",
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
}
|
|
87
124
|
return [{ role: types_1.Role.Ai, content }];
|
|
88
125
|
}
|
|
89
126
|
const text = await res.text();
|
|
@@ -76,6 +76,24 @@ async function openrouterAdapter(config, context, tools) {
|
|
|
76
76
|
}
|
|
77
77
|
if (onStream) {
|
|
78
78
|
let content = "";
|
|
79
|
+
const toolCalls = new Map();
|
|
80
|
+
const upsertToolCall = (tc) => {
|
|
81
|
+
const index = typeof tc?.index === "number" ? tc.index : toolCalls.size;
|
|
82
|
+
let entry = toolCalls.get(index);
|
|
83
|
+
if (!entry) {
|
|
84
|
+
entry = { args: "" };
|
|
85
|
+
toolCalls.set(index, entry);
|
|
86
|
+
}
|
|
87
|
+
if (typeof tc?.id === "string")
|
|
88
|
+
entry.id = tc.id;
|
|
89
|
+
const fn = tc?.function;
|
|
90
|
+
if (fn) {
|
|
91
|
+
if (typeof fn.name === "string")
|
|
92
|
+
entry.name = fn.name;
|
|
93
|
+
if (typeof fn.arguments === "string")
|
|
94
|
+
entry.args += fn.arguments;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
79
97
|
await (0, sse_1.readSse)(res, async (dataLine) => {
|
|
80
98
|
if (dataLine === "[DONE]")
|
|
81
99
|
return;
|
|
@@ -89,11 +107,30 @@ async function openrouterAdapter(config, context, tools) {
|
|
|
89
107
|
const delta = parsed?.choices?.[0]?.delta;
|
|
90
108
|
if (!delta)
|
|
91
109
|
return;
|
|
110
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
111
|
+
for (const tc of delta.tool_calls) {
|
|
112
|
+
upsertToolCall(tc);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
92
115
|
if (typeof delta.content === "string") {
|
|
93
116
|
content += delta.content;
|
|
94
117
|
await Promise.resolve(onStream(delta.content));
|
|
95
118
|
}
|
|
96
119
|
});
|
|
120
|
+
if (toolCalls.size > 0) {
|
|
121
|
+
return [...toolCalls.entries()]
|
|
122
|
+
.sort((a, b) => a[0] - b[0])
|
|
123
|
+
.map(([index, tc]) => {
|
|
124
|
+
if (!tc.name)
|
|
125
|
+
throw new Error(`OpenRouter streaming tool call missing function name at index ${index}`);
|
|
126
|
+
return {
|
|
127
|
+
role: types_1.Role.ToolCall,
|
|
128
|
+
toolName: tc.name,
|
|
129
|
+
callId: tc.id ?? `call_${index}`,
|
|
130
|
+
argsText: tc.args.length ? tc.args : "{}",
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
}
|
|
97
134
|
return [{ role: types_1.Role.Ai, content }];
|
|
98
135
|
}
|
|
99
136
|
const text = await res.text();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { AgentConfig, Context, Message, Tool } from "./types";
|
|
1
|
+
import type { AgentConfig, Context, Message, RunOptions, Tool } from "./types";
|
|
2
2
|
export { openaiAdapter } from "./adapter/openai";
|
|
3
3
|
export { openrouterAdapter } from "./adapter/openrouter";
|
|
4
|
-
export type { Adapter, AgentConfig, Context, Message, Tool } from "./types";
|
|
4
|
+
export type { Adapter, AgentConfig, Context, Message, RunOptions, Tool } from "./types";
|
|
5
5
|
export { Role } from "./types";
|
|
6
6
|
export declare class Agent {
|
|
7
7
|
context: Context;
|
|
@@ -14,6 +14,6 @@ export declare class Agent {
|
|
|
14
14
|
private onToolResult?;
|
|
15
15
|
constructor(adapterName: string, config: AgentConfig);
|
|
16
16
|
registerTool(tool: Tool): void;
|
|
17
|
-
run(message: string): Promise<Message[]>;
|
|
17
|
+
run(message: string, options?: RunOptions): Promise<Message[]>;
|
|
18
18
|
fork(): Agent;
|
|
19
19
|
}
|
package/dist/index.js
CHANGED
|
@@ -28,15 +28,31 @@ class Agent {
|
|
|
28
28
|
registerTool(tool) {
|
|
29
29
|
this.tools.push(tool);
|
|
30
30
|
}
|
|
31
|
-
async run(message) {
|
|
32
|
-
|
|
31
|
+
async run(message, options = {}) {
|
|
32
|
+
const once = options.once ?? false;
|
|
33
33
|
const all = [];
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const sessionContext = [...this.context];
|
|
35
|
+
const persistToContext = (msgs) => {
|
|
36
|
+
this.context.push(...msgs);
|
|
37
|
+
};
|
|
38
|
+
const pushToSession = (msgs) => {
|
|
39
|
+
sessionContext.push(...msgs);
|
|
40
|
+
};
|
|
41
|
+
const userMessage = { role: types_1.Role.User, content: message };
|
|
42
|
+
pushToSession([userMessage]);
|
|
43
|
+
if (!once)
|
|
44
|
+
persistToContext([userMessage]);
|
|
45
|
+
const runAdapter = async () => {
|
|
46
|
+
const msgs = await this.adapter(this.config, sessionContext, this.tools);
|
|
47
|
+
pushToSession(msgs);
|
|
48
|
+
persistToContext(msgs);
|
|
49
|
+
all.push(...msgs);
|
|
50
|
+
return msgs;
|
|
51
|
+
};
|
|
52
|
+
let msgs = await runAdapter();
|
|
37
53
|
for (;;) {
|
|
38
54
|
const last = msgs[msgs.length - 1];
|
|
39
|
-
if (this.endCondition(
|
|
55
|
+
if (this.endCondition(sessionContext, last))
|
|
40
56
|
return all;
|
|
41
57
|
const toolCalls = msgs.filter((m) => m.role === types_1.Role.ToolCall);
|
|
42
58
|
if (toolCalls.length === 0)
|
|
@@ -81,11 +97,10 @@ class Agent {
|
|
|
81
97
|
return result;
|
|
82
98
|
}));
|
|
83
99
|
const filteredResults = results.filter((result) => result !== null);
|
|
84
|
-
|
|
100
|
+
pushToSession(filteredResults);
|
|
101
|
+
persistToContext(filteredResults);
|
|
85
102
|
all.push(...filteredResults);
|
|
86
|
-
msgs = await
|
|
87
|
-
this.context.push(...msgs);
|
|
88
|
-
all.push(...msgs);
|
|
103
|
+
msgs = await runAdapter();
|
|
89
104
|
}
|
|
90
105
|
}
|
|
91
106
|
fork() {
|
package/dist/types.d.ts
CHANGED
|
@@ -43,4 +43,7 @@ export type AgentConfig = {
|
|
|
43
43
|
}>) => void | Promise<void>;
|
|
44
44
|
[key: string]: unknown;
|
|
45
45
|
};
|
|
46
|
+
export type RunOptions = {
|
|
47
|
+
once?: boolean;
|
|
48
|
+
};
|
|
46
49
|
export type Adapter = (config: Record<string, unknown>, context: Message[], tools: Tool[]) => Promise<Message[]>;
|