@aigne/core 1.9.0 → 1.11.0
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 +25 -0
- package/LICENSE +93 -0
- package/README.md +89 -0
- package/README.zh.md +89 -0
- package/lib/cjs/agents/agent.d.ts +30 -3
- package/lib/cjs/agents/agent.js +51 -22
- package/lib/cjs/agents/ai-agent.d.ts +14 -11
- package/lib/cjs/agents/ai-agent.js +36 -17
- package/lib/cjs/agents/user-agent.d.ts +3 -3
- package/lib/cjs/agents/user-agent.js +14 -8
- package/lib/cjs/execution-engine/context.d.ts +18 -7
- package/lib/cjs/execution-engine/context.js +76 -28
- package/lib/cjs/loader/agent-js.d.ts +2 -2
- package/lib/cjs/loader/agent-js.js +4 -5
- package/lib/cjs/loader/agent-yaml.d.ts +8 -5
- package/lib/cjs/loader/agent-yaml.js +13 -1
- package/lib/cjs/loader/index.d.ts +4 -4
- package/lib/cjs/loader/index.js +4 -15
- package/lib/cjs/models/chat-model.d.ts +4 -0
- package/lib/cjs/models/chat-model.js +6 -0
- package/lib/cjs/models/claude-chat-model.d.ts +3 -1
- package/lib/cjs/models/claude-chat-model.js +75 -60
- package/lib/cjs/models/open-router-chat-model.d.ts +1 -0
- package/lib/cjs/models/open-router-chat-model.js +1 -0
- package/lib/cjs/models/openai-chat-model.d.ts +12 -3
- package/lib/cjs/models/openai-chat-model.js +110 -46
- package/lib/cjs/utils/camelize.d.ts +13 -0
- package/lib/cjs/utils/camelize.js +16 -0
- package/lib/cjs/utils/stream-utils.d.ts +17 -0
- package/lib/cjs/utils/stream-utils.js +165 -0
- package/lib/cjs/utils/type-utils.d.ts +1 -0
- package/lib/cjs/utils/type-utils.js +7 -0
- package/lib/dts/agents/agent.d.ts +30 -3
- package/lib/dts/agents/ai-agent.d.ts +14 -11
- package/lib/dts/agents/user-agent.d.ts +3 -3
- package/lib/dts/execution-engine/context.d.ts +18 -7
- package/lib/dts/loader/agent-js.d.ts +2 -2
- package/lib/dts/loader/agent-yaml.d.ts +8 -5
- package/lib/dts/loader/index.d.ts +4 -4
- package/lib/dts/models/chat-model.d.ts +4 -0
- package/lib/dts/models/claude-chat-model.d.ts +3 -1
- package/lib/dts/models/open-router-chat-model.d.ts +1 -0
- package/lib/dts/models/openai-chat-model.d.ts +12 -3
- package/lib/dts/utils/camelize.d.ts +13 -0
- package/lib/dts/utils/stream-utils.d.ts +17 -0
- package/lib/dts/utils/type-utils.d.ts +1 -0
- package/lib/esm/agents/agent.d.ts +30 -3
- package/lib/esm/agents/agent.js +51 -23
- package/lib/esm/agents/ai-agent.d.ts +14 -11
- package/lib/esm/agents/ai-agent.js +37 -18
- package/lib/esm/agents/user-agent.d.ts +3 -3
- package/lib/esm/agents/user-agent.js +15 -9
- package/lib/esm/execution-engine/context.d.ts +18 -7
- package/lib/esm/execution-engine/context.js +78 -30
- package/lib/esm/loader/agent-js.d.ts +2 -2
- package/lib/esm/loader/agent-js.js +4 -5
- package/lib/esm/loader/agent-yaml.d.ts +8 -5
- package/lib/esm/loader/agent-yaml.js +13 -1
- package/lib/esm/loader/index.d.ts +4 -4
- package/lib/esm/loader/index.js +4 -15
- package/lib/esm/models/chat-model.d.ts +4 -0
- package/lib/esm/models/chat-model.js +6 -0
- package/lib/esm/models/claude-chat-model.d.ts +3 -1
- package/lib/esm/models/claude-chat-model.js +75 -60
- package/lib/esm/models/open-router-chat-model.d.ts +1 -0
- package/lib/esm/models/open-router-chat-model.js +1 -0
- package/lib/esm/models/openai-chat-model.d.ts +12 -3
- package/lib/esm/models/openai-chat-model.js +110 -45
- package/lib/esm/utils/camelize.d.ts +13 -0
- package/lib/esm/utils/camelize.js +10 -0
- package/lib/esm/utils/stream-utils.d.ts +17 -0
- package/lib/esm/utils/stream-utils.js +149 -0
- package/lib/esm/utils/type-utils.d.ts +1 -0
- package/lib/esm/utils/type-utils.js +6 -0
- package/package.json +4 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { parseJSON } from "../utils/json-schema.js";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
4
|
import { mergeUsage } from "../utils/model-utils.js";
|
|
5
|
+
import { agentResponseStreamToObject } from "../utils/stream-utils.js";
|
|
6
6
|
import { checkArguments, isEmpty, isNonNullable } from "../utils/type-utils.js";
|
|
7
7
|
import { ChatModel, } from "./chat-model.js";
|
|
8
8
|
const CHAT_MODEL_CLAUDE_DEFAULT_MODEL = "claude-3-7-sonnet-latest";
|
|
@@ -39,7 +39,7 @@ export class ClaudeChatModel extends ChatModel {
|
|
|
39
39
|
get modelOptions() {
|
|
40
40
|
return this.options?.modelOptions;
|
|
41
41
|
}
|
|
42
|
-
async process(input) {
|
|
42
|
+
async process(input, _context, options) {
|
|
43
43
|
const model = this.options?.model || CHAT_MODEL_CLAUDE_DEFAULT_MODEL;
|
|
44
44
|
const disableParallelToolUse = input.modelOptions?.parallelToolCalls === false ||
|
|
45
45
|
this.modelOptions?.parallelToolCalls === false;
|
|
@@ -56,6 +56,9 @@ export class ClaudeChatModel extends ChatModel {
|
|
|
56
56
|
...body,
|
|
57
57
|
stream: true,
|
|
58
58
|
});
|
|
59
|
+
if (options?.streaming && input.responseFormat?.type !== "json_schema") {
|
|
60
|
+
return this.extractResultFromClaudeStream(stream, true);
|
|
61
|
+
}
|
|
59
62
|
const result = await this.extractResultFromClaudeStream(stream);
|
|
60
63
|
// Claude doesn't support json_schema response and tool calls in the same request,
|
|
61
64
|
// so we need to make a separate request for json_schema response when the tool calls is empty
|
|
@@ -69,67 +72,79 @@ export class ClaudeChatModel extends ChatModel {
|
|
|
69
72
|
}
|
|
70
73
|
return result;
|
|
71
74
|
}
|
|
72
|
-
async extractResultFromClaudeStream(stream) {
|
|
75
|
+
async extractResultFromClaudeStream(stream, streaming) {
|
|
73
76
|
const logs = [];
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
77
|
+
const result = new ReadableStream({
|
|
78
|
+
async start(controller) {
|
|
79
|
+
try {
|
|
80
|
+
const toolCalls = [];
|
|
81
|
+
let usage;
|
|
82
|
+
let model;
|
|
83
|
+
for await (const chunk of stream) {
|
|
84
|
+
if (chunk.type === "message_start") {
|
|
85
|
+
if (!model) {
|
|
86
|
+
model = chunk.message.model;
|
|
87
|
+
controller.enqueue({ delta: { json: { model } } });
|
|
88
|
+
}
|
|
89
|
+
const { input_tokens, output_tokens } = chunk.message.usage;
|
|
90
|
+
usage = {
|
|
91
|
+
inputTokens: input_tokens,
|
|
92
|
+
outputTokens: output_tokens,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (chunk.type === "message_delta" && usage) {
|
|
96
|
+
usage.outputTokens = chunk.usage.output_tokens;
|
|
97
|
+
}
|
|
98
|
+
logs.push(JSON.stringify(chunk));
|
|
99
|
+
// handle streaming text
|
|
100
|
+
if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
|
|
101
|
+
controller.enqueue({ delta: { text: { text: chunk.delta.text } } });
|
|
102
|
+
}
|
|
103
|
+
if (chunk.type === "content_block_start" && chunk.content_block.type === "tool_use") {
|
|
104
|
+
toolCalls[chunk.index] = {
|
|
105
|
+
type: "function",
|
|
106
|
+
id: chunk.content_block.id,
|
|
107
|
+
function: {
|
|
108
|
+
name: chunk.content_block.name,
|
|
109
|
+
arguments: {},
|
|
110
|
+
},
|
|
111
|
+
args: "",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (chunk.type === "content_block_delta" && chunk.delta.type === "input_json_delta") {
|
|
115
|
+
const call = toolCalls[chunk.index];
|
|
116
|
+
if (!call)
|
|
117
|
+
throw new Error("Tool call not found");
|
|
118
|
+
call.args += chunk.delta.partial_json;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
controller.enqueue({ delta: { json: { usage } } });
|
|
122
|
+
if (toolCalls.length) {
|
|
123
|
+
controller.enqueue({
|
|
124
|
+
delta: {
|
|
125
|
+
json: {
|
|
126
|
+
toolCalls: toolCalls
|
|
127
|
+
.map(({ args, ...c }) => ({
|
|
128
|
+
...c,
|
|
129
|
+
function: {
|
|
130
|
+
...c.function,
|
|
131
|
+
// NOTE: claude may return a blank string for empty object (the tool's input schema is a empty object)
|
|
132
|
+
arguments: args.trim() ? parseJSON(args) : {},
|
|
133
|
+
},
|
|
134
|
+
}))
|
|
135
|
+
.filter(isNonNullable),
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
controller.close();
|
|
95
141
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
type: "function",
|
|
99
|
-
id: chunk.content_block.id,
|
|
100
|
-
function: {
|
|
101
|
-
name: chunk.content_block.name,
|
|
102
|
-
arguments: {},
|
|
103
|
-
},
|
|
104
|
-
args: "",
|
|
105
|
-
};
|
|
142
|
+
catch (error) {
|
|
143
|
+
controller.error(error);
|
|
106
144
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
throw new Error("Tool call not found");
|
|
111
|
-
call.args += chunk.delta.partial_json;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
const result = { usage, model, text };
|
|
115
|
-
if (toolCalls.length) {
|
|
116
|
-
result.toolCalls = toolCalls
|
|
117
|
-
.map(({ args, ...c }) => ({
|
|
118
|
-
...c,
|
|
119
|
-
function: {
|
|
120
|
-
...c.function,
|
|
121
|
-
// NOTE: claude may return a blank string for empty object (the tool's input schema is a empty object)
|
|
122
|
-
arguments: args.trim() ? parseJSON(args) : {},
|
|
123
|
-
},
|
|
124
|
-
}))
|
|
125
|
-
.filter(isNonNullable);
|
|
126
|
-
}
|
|
127
|
-
return result;
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
logger.core("Failed to process Claude stream", { error, logs });
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
return streaming ? result : await agentResponseStreamToObject(result);
|
|
133
148
|
}
|
|
134
149
|
async requestStructuredOutput(body, responseFormat) {
|
|
135
150
|
if (responseFormat?.type !== "json_schema") {
|
|
@@ -2,4 +2,5 @@ import { OpenAIChatModel, type OpenAIChatModelOptions } from "./openai-chat-mode
|
|
|
2
2
|
export declare class OpenRouterChatModel extends OpenAIChatModel {
|
|
3
3
|
constructor(options?: OpenAIChatModelOptions);
|
|
4
4
|
protected apiKeyEnvName: string;
|
|
5
|
+
protected supportsParallelToolCalls: boolean;
|
|
5
6
|
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
|
|
3
|
-
import type { Stream } from "openai/streaming.js";
|
|
4
3
|
import { z } from "zod";
|
|
4
|
+
import type { AgentCallOptions, AgentResponse } from "../agents/agent.js";
|
|
5
|
+
import type { Context } from "../execution-engine/context.js";
|
|
5
6
|
import { ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput, type Role } from "./chat-model.js";
|
|
7
|
+
export interface OpenAIChatModelCapabilities {
|
|
8
|
+
supportsNativeStructuredOutputs: boolean;
|
|
9
|
+
supportsEndWithSystemMessage: boolean;
|
|
10
|
+
supportsToolsUseWithJsonSchema: boolean;
|
|
11
|
+
supportsParallelToolCalls: boolean;
|
|
12
|
+
supportsToolsEmptyParameters: boolean;
|
|
13
|
+
supportsTemperature: boolean;
|
|
14
|
+
}
|
|
6
15
|
export interface OpenAIChatModelOptions {
|
|
7
16
|
apiKey?: string;
|
|
8
17
|
baseURL?: string;
|
|
@@ -71,9 +80,10 @@ export declare class OpenAIChatModel extends ChatModel {
|
|
|
71
80
|
protected supportsToolsUseWithJsonSchema: boolean;
|
|
72
81
|
protected supportsParallelToolCalls: boolean;
|
|
73
82
|
protected supportsToolsEmptyParameters: boolean;
|
|
83
|
+
protected supportsTemperature: boolean;
|
|
74
84
|
get client(): OpenAI;
|
|
75
85
|
get modelOptions(): ChatModelOptions | undefined;
|
|
76
|
-
process(input: ChatModelInput): Promise<ChatModelOutput
|
|
86
|
+
process(input: ChatModelInput, _context: Context, options?: AgentCallOptions): Promise<AgentResponse<ChatModelOutput>>;
|
|
77
87
|
private getParallelToolCalls;
|
|
78
88
|
private getRunMessages;
|
|
79
89
|
private getRunResponseFormat;
|
|
@@ -87,4 +97,3 @@ export declare function toolsFromInputTools(tools?: ChatModelInputTool[], option
|
|
|
87
97
|
addTypeToEmptyParameters?: boolean;
|
|
88
98
|
}): ChatCompletionTool[] | undefined;
|
|
89
99
|
export declare function jsonSchemaToOpenAIJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
|
|
90
|
-
export declare function extractResultFromStream(stream: Stream<OpenAI.Chat.Completions.ChatCompletionChunk>, jsonMode?: boolean): Promise<ChatModelOutput>;
|
|
@@ -4,9 +4,14 @@ import { z } from "zod";
|
|
|
4
4
|
import { parseJSON } from "../utils/json-schema.js";
|
|
5
5
|
import { mergeUsage } from "../utils/model-utils.js";
|
|
6
6
|
import { getJsonOutputPrompt } from "../utils/prompts.js";
|
|
7
|
+
import { agentResponseStreamToObject } from "../utils/stream-utils.js";
|
|
7
8
|
import { checkArguments, isNonNullable } from "../utils/type-utils.js";
|
|
8
9
|
import { ChatModel, } from "./chat-model.js";
|
|
9
10
|
const CHAT_MODEL_OPENAI_DEFAULT_MODEL = "gpt-4o-mini";
|
|
11
|
+
const OPENAI_CHAT_MODEL_CAPABILITIES = {
|
|
12
|
+
"o4-mini": { supportsParallelToolCalls: false, supportsTemperature: false },
|
|
13
|
+
"o3-mini": { supportsParallelToolCalls: false, supportsTemperature: false },
|
|
14
|
+
};
|
|
10
15
|
export const openAIChatModelOptionsSchema = z.object({
|
|
11
16
|
apiKey: z.string().optional(),
|
|
12
17
|
baseURL: z.string().optional(),
|
|
@@ -29,6 +34,8 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
29
34
|
this.options = options;
|
|
30
35
|
if (options)
|
|
31
36
|
checkArguments(this.name, openAIChatModelOptionsSchema, options);
|
|
37
|
+
const preset = options?.model ? OPENAI_CHAT_MODEL_CAPABILITIES[options.model] : undefined;
|
|
38
|
+
Object.assign(this, preset);
|
|
32
39
|
}
|
|
33
40
|
_client;
|
|
34
41
|
apiKeyEnvName = "OPENAI_API_KEY";
|
|
@@ -38,6 +45,7 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
38
45
|
supportsToolsUseWithJsonSchema = true;
|
|
39
46
|
supportsParallelToolCalls = true;
|
|
40
47
|
supportsToolsEmptyParameters = true;
|
|
48
|
+
supportsTemperature = true;
|
|
41
49
|
get client() {
|
|
42
50
|
const apiKey = this.options?.apiKey || process.env[this.apiKeyEnvName] || this.apiKeyDefault;
|
|
43
51
|
if (!apiKey)
|
|
@@ -51,14 +59,17 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
51
59
|
get modelOptions() {
|
|
52
60
|
return this.options?.modelOptions;
|
|
53
61
|
}
|
|
54
|
-
async process(input) {
|
|
62
|
+
async process(input, _context, options) {
|
|
63
|
+
const messages = await this.getRunMessages(input);
|
|
55
64
|
const body = {
|
|
56
65
|
model: this.options?.model || CHAT_MODEL_OPENAI_DEFAULT_MODEL,
|
|
57
|
-
temperature:
|
|
66
|
+
temperature: this.supportsTemperature
|
|
67
|
+
? (input.modelOptions?.temperature ?? this.modelOptions?.temperature)
|
|
68
|
+
: undefined,
|
|
58
69
|
top_p: input.modelOptions?.topP ?? this.modelOptions?.topP,
|
|
59
70
|
frequency_penalty: input.modelOptions?.frequencyPenalty ?? this.modelOptions?.frequencyPenalty,
|
|
60
71
|
presence_penalty: input.modelOptions?.presencePenalty ?? this.modelOptions?.presencePenalty,
|
|
61
|
-
messages
|
|
72
|
+
messages,
|
|
62
73
|
stream_options: {
|
|
63
74
|
include_usage: true,
|
|
64
75
|
},
|
|
@@ -74,6 +85,9 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
74
85
|
parallel_tool_calls: this.getParallelToolCalls(input),
|
|
75
86
|
response_format: responseFormat,
|
|
76
87
|
});
|
|
88
|
+
if (options?.streaming && input.responseFormat?.type !== "json_schema") {
|
|
89
|
+
return await extractResultFromStream(stream, false, true);
|
|
90
|
+
}
|
|
77
91
|
const result = await extractResultFromStream(stream, jsonMode);
|
|
78
92
|
if (!this.supportsToolsUseWithJsonSchema &&
|
|
79
93
|
!result.toolCalls?.length &&
|
|
@@ -221,51 +235,102 @@ export function jsonSchemaToOpenAIJsonSchema(schema) {
|
|
|
221
235
|
}
|
|
222
236
|
return schema;
|
|
223
237
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
async function extractResultFromStream(stream, jsonMode, streaming) {
|
|
239
|
+
const result = new ReadableStream({
|
|
240
|
+
async start(controller) {
|
|
241
|
+
try {
|
|
242
|
+
let text = "";
|
|
243
|
+
let refusal = "";
|
|
244
|
+
const toolCalls = [];
|
|
245
|
+
let model;
|
|
246
|
+
for await (const chunk of stream) {
|
|
247
|
+
const choice = chunk.choices?.[0];
|
|
248
|
+
if (!model) {
|
|
249
|
+
model = chunk.model;
|
|
250
|
+
controller.enqueue({
|
|
251
|
+
delta: {
|
|
252
|
+
json: {
|
|
253
|
+
model,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
if (choice?.delta.tool_calls?.length) {
|
|
259
|
+
for (const call of choice.delta.tool_calls) {
|
|
260
|
+
// Gemini not support tool call delta
|
|
261
|
+
if (call.index !== undefined) {
|
|
262
|
+
handleToolCallDelta(toolCalls, call);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
handleCompleteToolCall(toolCalls, call);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (choice?.delta.content) {
|
|
270
|
+
text += choice.delta.content;
|
|
271
|
+
if (!jsonMode) {
|
|
272
|
+
controller.enqueue({
|
|
273
|
+
delta: {
|
|
274
|
+
text: {
|
|
275
|
+
text: choice.delta.content,
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (choice?.delta.refusal) {
|
|
282
|
+
refusal += choice.delta.refusal;
|
|
283
|
+
if (!jsonMode) {
|
|
284
|
+
controller.enqueue({
|
|
285
|
+
delta: {
|
|
286
|
+
text: { text: choice.delta.refusal },
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (chunk.usage) {
|
|
292
|
+
controller.enqueue({
|
|
293
|
+
delta: {
|
|
294
|
+
json: {
|
|
295
|
+
usage: {
|
|
296
|
+
inputTokens: chunk.usage.prompt_tokens,
|
|
297
|
+
outputTokens: chunk.usage.completion_tokens,
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
}
|
|
237
303
|
}
|
|
238
|
-
|
|
239
|
-
|
|
304
|
+
text = text || refusal;
|
|
305
|
+
if (jsonMode && text) {
|
|
306
|
+
controller.enqueue({
|
|
307
|
+
delta: {
|
|
308
|
+
json: {
|
|
309
|
+
json: parseJSON(text),
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
});
|
|
240
313
|
}
|
|
314
|
+
if (toolCalls.length) {
|
|
315
|
+
controller.enqueue({
|
|
316
|
+
delta: {
|
|
317
|
+
json: {
|
|
318
|
+
toolCalls: toolCalls.map(({ args, ...c }) => ({
|
|
319
|
+
...c,
|
|
320
|
+
function: { ...c.function, arguments: parseJSON(args) },
|
|
321
|
+
})),
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
controller.close();
|
|
241
327
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
outputTokens: chunk.usage.completion_tokens,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
const result = {
|
|
253
|
-
usage,
|
|
254
|
-
model,
|
|
255
|
-
};
|
|
256
|
-
if (jsonMode && text) {
|
|
257
|
-
result.json = parseJSON(text);
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
result.text = text;
|
|
261
|
-
}
|
|
262
|
-
if (toolCalls.length) {
|
|
263
|
-
result.toolCalls = toolCalls.map(({ args, ...c }) => ({
|
|
264
|
-
...c,
|
|
265
|
-
function: { ...c.function, arguments: parseJSON(args) },
|
|
266
|
-
}));
|
|
267
|
-
}
|
|
268
|
-
return result;
|
|
328
|
+
catch (error) {
|
|
329
|
+
controller.error(error);
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
return streaming ? result : await agentResponseStreamToObject(result);
|
|
269
334
|
}
|
|
270
335
|
function handleToolCallDelta(toolCalls, call) {
|
|
271
336
|
toolCalls[call.index] ??= {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function customCamelize<T extends Record<string, unknown>, K extends KeyofUnion<T> = never>(obj: T, { shallowKeys }?: {
|
|
2
|
+
shallowKeys?: K[];
|
|
3
|
+
}): CustomCamelize<T, K>;
|
|
4
|
+
type KeyofUnion<U> = U extends Record<string, unknown> ? keyof U : never;
|
|
5
|
+
type CamelCase<S extends string> = S extends `${infer P1}_${infer P2}${infer P3}` ? `${P1}${Uppercase<P2>}${CamelCase<P3>}` : S;
|
|
6
|
+
declare const _unique: unique symbol;
|
|
7
|
+
type _never = typeof _unique;
|
|
8
|
+
type ExtractTypeFromUnion<T, U> = Extract<T, U> extends never ? _never : Extract<T, U> extends Array<infer E> ? Array<E> : U;
|
|
9
|
+
type ExtractTypeWithConstFromUnion<T, U> = Exclude<T, Exclude<T, U>>;
|
|
10
|
+
export type CustomCamelize<T, ShallowKeys extends KeyofUnion<T> | undefined = undefined> = ExtractTypeFromUnion<T, never> extends never ? never : ExtractTypeFromUnion<T, Date> extends Date ? ExtractTypeWithConstFromUnion<T, Date> | CustomCamelize<Exclude<T, Date>> : ExtractTypeFromUnion<T, RegExp> extends RegExp ? ExtractTypeWithConstFromUnion<T, RegExp> | CustomCamelize<Exclude<T, RegExp>> : ExtractTypeFromUnion<T, Array<unknown>> extends Array<infer U> ? Array<CustomCamelize<U>> | CustomCamelize<Exclude<T, Array<U>>> : ExtractTypeFromUnion<T, Function> extends Function ? ExtractTypeWithConstFromUnion<T, Function> | CustomCamelize<Exclude<T, Function>> : ExtractTypeFromUnion<T, object> extends object ? {
|
|
11
|
+
[K in keyof T as Uncapitalize<CamelCase<string & K>>]: K extends Exclude<ShallowKeys, undefined> ? T[K] : CustomCamelize<T[K]>;
|
|
12
|
+
} : T;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import camelize from "camelize-ts";
|
|
2
|
+
export function customCamelize(obj, { shallowKeys = [] } = {}) {
|
|
3
|
+
const shallow = Object.fromEntries(shallowKeys?.filter((key) => key in obj).map((key) => [key, obj[key]]) ?? []);
|
|
4
|
+
const deep = Object.fromEntries(Object.entries(obj).filter(([key]) => !shallowKeys?.includes(key)));
|
|
5
|
+
return {
|
|
6
|
+
...camelize(shallow, true),
|
|
7
|
+
...camelize(deep),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
const _unique = Symbol();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type AgentProcessAsyncGenerator, type AgentResponseChunk, type AgentResponseStream, type Message } from "../agents/agent.js";
|
|
2
|
+
import type { MESSAGE_KEY } from "../prompt/prompt-builder.js";
|
|
3
|
+
import { type PromiseOrValue } from "./type-utils.js";
|
|
4
|
+
export declare function objectToAgentResponseStream<T extends Message>(json: T): AgentResponseStream<T>;
|
|
5
|
+
export declare function mergeAgentResponseChunk<T extends Message>(output: T, chunk: AgentResponseChunk<T>): T;
|
|
6
|
+
export declare function agentResponseStreamToObject<T extends Message>(stream: AgentResponseStream<T> | AgentProcessAsyncGenerator<T>): Promise<T>;
|
|
7
|
+
export declare function asyncGeneratorToReadableStream<T extends Message>(generator: AgentProcessAsyncGenerator<T>): AgentResponseStream<T>;
|
|
8
|
+
export declare function onAgentResponseStreamEnd<T extends Message>(stream: AgentResponseStream<T>, callback: (result: T) => PromiseOrValue<Partial<T> | void>, options?: {
|
|
9
|
+
errorCallback?: (error: Error) => Error;
|
|
10
|
+
processChunk?: (chunk: AgentResponseChunk<T>) => AgentResponseChunk<T>;
|
|
11
|
+
}): ReadableStream<any>;
|
|
12
|
+
export declare function isAsyncGenerator<T extends AsyncGenerator>(value: AsyncGenerator | unknown): value is T;
|
|
13
|
+
export declare function arrayToAgentProcessAsyncGenerator<T extends Message>(chunks: (AgentResponseChunk<T> | Error)[], result?: Partial<T>): AgentProcessAsyncGenerator<T>;
|
|
14
|
+
export declare function arrayToAgentResponseStream<T>(chunks: (AgentResponseChunk<T> | Error)[]): AgentResponseStream<T>;
|
|
15
|
+
export declare function readableStreamToArray<T>(stream: ReadableStream<T>): Promise<T[]>;
|
|
16
|
+
export declare function readableStreamToAsyncIterator<T>(stream: ReadableStream<T>): AsyncIterable<T>;
|
|
17
|
+
export declare function stringToAgentResponseStream(str: string, key?: "text" | typeof MESSAGE_KEY | string): AgentResponseStream<Message>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import equal from "fast-deep-equal";
|
|
2
|
+
import { isEmptyChunk, } from "../agents/agent.js";
|
|
3
|
+
import { omitBy } from "./type-utils.js";
|
|
4
|
+
export function objectToAgentResponseStream(json) {
|
|
5
|
+
return new ReadableStream({
|
|
6
|
+
start(controller) {
|
|
7
|
+
controller.enqueue({ delta: { json } });
|
|
8
|
+
controller.close();
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function mergeAgentResponseChunk(output, chunk) {
|
|
13
|
+
if (chunk.delta.text) {
|
|
14
|
+
for (const [key, text] of Object.entries(chunk.delta.text)) {
|
|
15
|
+
const original = output[key];
|
|
16
|
+
const t = (original || "") + (text || "");
|
|
17
|
+
if (t)
|
|
18
|
+
Object.assign(output, { [key]: t });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (chunk.delta.json) {
|
|
22
|
+
Object.assign(output, omitBy(chunk.delta.json, (v) => v === undefined));
|
|
23
|
+
}
|
|
24
|
+
return output;
|
|
25
|
+
}
|
|
26
|
+
export async function agentResponseStreamToObject(stream) {
|
|
27
|
+
const json = {};
|
|
28
|
+
if (stream instanceof ReadableStream) {
|
|
29
|
+
for await (const value of readableStreamToAsyncIterator(stream)) {
|
|
30
|
+
mergeAgentResponseChunk(json, value);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
for (;;) {
|
|
35
|
+
const chunk = await stream.next();
|
|
36
|
+
if (chunk.value) {
|
|
37
|
+
if (chunk.done) {
|
|
38
|
+
Object.assign(json, chunk.value);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
mergeAgentResponseChunk(json, chunk.value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (chunk.done)
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return json;
|
|
49
|
+
}
|
|
50
|
+
export function asyncGeneratorToReadableStream(generator) {
|
|
51
|
+
return new ReadableStream({
|
|
52
|
+
async start(controller) {
|
|
53
|
+
try {
|
|
54
|
+
for (;;) {
|
|
55
|
+
const chunk = await generator.next();
|
|
56
|
+
if (chunk.value) {
|
|
57
|
+
if (chunk.done) {
|
|
58
|
+
controller.enqueue({ delta: { json: chunk.value } });
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
controller.enqueue(chunk.value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (chunk.done)
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
controller.close();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
controller.error(error);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export function onAgentResponseStreamEnd(stream, callback, options) {
|
|
76
|
+
return new ReadableStream({
|
|
77
|
+
async start(controller) {
|
|
78
|
+
try {
|
|
79
|
+
const json = {};
|
|
80
|
+
for await (const value of readableStreamToAsyncIterator(stream)) {
|
|
81
|
+
const chunk = options?.processChunk ? options.processChunk(value) : value;
|
|
82
|
+
if (!isEmptyChunk(chunk))
|
|
83
|
+
controller.enqueue(chunk);
|
|
84
|
+
mergeAgentResponseChunk(json, value);
|
|
85
|
+
}
|
|
86
|
+
const result = await callback(json);
|
|
87
|
+
if (result && !equal(result, json)) {
|
|
88
|
+
let chunk = { delta: { json: result } };
|
|
89
|
+
if (options?.processChunk)
|
|
90
|
+
chunk = options.processChunk(chunk);
|
|
91
|
+
controller.enqueue(chunk);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
controller.error(options?.errorCallback?.(error) ?? error);
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
controller.close();
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export function isAsyncGenerator(value) {
|
|
104
|
+
return typeof value === "object" && value !== null && Symbol.asyncIterator in value;
|
|
105
|
+
}
|
|
106
|
+
export async function* arrayToAgentProcessAsyncGenerator(chunks, result) {
|
|
107
|
+
for (const chunk of chunks) {
|
|
108
|
+
if (chunk instanceof Error)
|
|
109
|
+
throw chunk;
|
|
110
|
+
yield chunk;
|
|
111
|
+
}
|
|
112
|
+
if (result !== undefined)
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
export function arrayToAgentResponseStream(chunks) {
|
|
116
|
+
return new ReadableStream({
|
|
117
|
+
start(controller) {
|
|
118
|
+
for (const chunk of chunks) {
|
|
119
|
+
if (chunk instanceof Error) {
|
|
120
|
+
controller.error(chunk);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
controller.enqueue(chunk);
|
|
124
|
+
}
|
|
125
|
+
controller.close();
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
export async function readableStreamToArray(stream) {
|
|
130
|
+
const result = [];
|
|
131
|
+
for await (const value of readableStreamToAsyncIterator(stream)) {
|
|
132
|
+
result.push(value);
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
export async function* readableStreamToAsyncIterator(stream) {
|
|
137
|
+
const reader = stream.getReader();
|
|
138
|
+
while (true) {
|
|
139
|
+
const { value, done } = await reader.read();
|
|
140
|
+
if (done)
|
|
141
|
+
break;
|
|
142
|
+
yield value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export function stringToAgentResponseStream(str, key = "text") {
|
|
146
|
+
const segmenter = new Intl.Segmenter(undefined, { granularity: "word" });
|
|
147
|
+
const segments = segmenter.segment(str);
|
|
148
|
+
return arrayToAgentResponseStream(Array.from(segments).map((segment) => ({ delta: { text: { [key]: segment.segment } } })));
|
|
149
|
+
}
|
|
@@ -7,6 +7,7 @@ export declare function isEmpty(obj: unknown): boolean;
|
|
|
7
7
|
export declare function isNonNullable<T>(value: T): value is NonNullable<T>;
|
|
8
8
|
export declare function isNotEmpty<T>(arr: T[]): arr is [T, ...T[]];
|
|
9
9
|
export declare function duplicates<T>(arr: T[], key?: (item: T) => unknown): T[];
|
|
10
|
+
export declare function omitBy<T extends Record<string, unknown>, K extends keyof T>(obj: T, predicate: (value: T[K], key: K) => boolean): Partial<T>;
|
|
10
11
|
export declare function orArrayToArray<T>(value?: T | T[]): T[];
|
|
11
12
|
export declare function createAccessorArray<T>(array: T[], accessor: (array: T[], name: string) => T | undefined): T[] & {
|
|
12
13
|
[key: string]: T;
|
|
@@ -31,6 +31,12 @@ export function duplicates(arr, key = (item) => item) {
|
|
|
31
31
|
}
|
|
32
32
|
return Array.from(duplicates);
|
|
33
33
|
}
|
|
34
|
+
export function omitBy(obj, predicate) {
|
|
35
|
+
return Object.fromEntries(Object.entries(obj).filter(([key, value]) => {
|
|
36
|
+
const k = key;
|
|
37
|
+
return !predicate(value, k);
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
34
40
|
export function orArrayToArray(value) {
|
|
35
41
|
if (isNil(value))
|
|
36
42
|
return [];
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "AIGNE core library for building AI-powered applications",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"author": "Arcblock <blocklet@arcblock.io> https://github.com/blocklet",
|
|
9
9
|
"homepage": "https://github.com/AIGNE-io/aigne-framework",
|
|
10
|
-
"license": "
|
|
10
|
+
"license": "Elastic-2.0",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
13
|
"url": "git+https://github.com/AIGNE-io/aigne-framework"
|
|
@@ -65,7 +65,9 @@
|
|
|
65
65
|
"@aigne/json-schema-to-zod": "^1.3.3",
|
|
66
66
|
"@modelcontextprotocol/sdk": "^1.9.0",
|
|
67
67
|
"@types/debug": "^4.1.12",
|
|
68
|
+
"camelize-ts": "^3.0.0",
|
|
68
69
|
"debug": "^4.4.0",
|
|
70
|
+
"fast-deep-equal": "^3.1.3",
|
|
69
71
|
"inquirer": "^12.5.2",
|
|
70
72
|
"mustache": "^4.2.0",
|
|
71
73
|
"nanoid": "^5.1.5",
|