@aigne/core 1.9.0 → 1.10.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 +12 -0
- package/lib/cjs/agents/agent.d.ts +29 -3
- package/lib/cjs/agents/agent.js +47 -22
- package/lib/cjs/agents/ai-agent.d.ts +3 -2
- package/lib/cjs/agents/ai-agent.js +22 -9
- 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/models/chat-model.d.ts +4 -0
- package/lib/cjs/models/chat-model.js +6 -0
- 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 +112 -46
- package/lib/cjs/utils/stream-utils.d.ts +15 -0
- package/lib/cjs/utils/stream-utils.js +159 -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 +29 -3
- package/lib/dts/agents/ai-agent.d.ts +3 -2
- package/lib/dts/agents/user-agent.d.ts +3 -3
- package/lib/dts/execution-engine/context.d.ts +18 -7
- package/lib/dts/models/chat-model.d.ts +4 -0
- 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/stream-utils.d.ts +15 -0
- package/lib/dts/utils/type-utils.d.ts +1 -0
- package/lib/esm/agents/agent.d.ts +29 -3
- package/lib/esm/agents/agent.js +47 -22
- package/lib/esm/agents/ai-agent.d.ts +3 -2
- package/lib/esm/agents/ai-agent.js +23 -10
- 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/models/chat-model.d.ts +4 -0
- package/lib/esm/models/chat-model.js +6 -0
- 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 +112 -45
- package/lib/esm/utils/stream-utils.d.ts +15 -0
- package/lib/esm/utils/stream-utils.js +144 -0
- package/lib/esm/utils/type-utils.d.ts +1 -0
- package/lib/esm/utils/type-utils.js +6 -0
- package/package.json +2 -1
|
@@ -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,104 @@ 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
|
+
});
|
|
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
|
+
});
|
|
240
325
|
}
|
|
241
326
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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;
|
|
327
|
+
catch (error) {
|
|
328
|
+
controller.error(error);
|
|
329
|
+
}
|
|
330
|
+
finally {
|
|
331
|
+
controller.close();
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
return streaming ? result : await agentResponseStreamToObject(result);
|
|
269
336
|
}
|
|
270
337
|
function handleToolCallDelta(toolCalls, call) {
|
|
271
338
|
toolCalls[call.index] ??= {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AgentProcessAsyncGenerator, AgentResponseChunk, AgentResponseStream, Message } from "../agents/agent.js";
|
|
2
|
+
import { type PromiseOrValue } from "./type-utils.js";
|
|
3
|
+
export declare function objectToAgentResponseStream<T extends Message>(json: T): AgentResponseStream<T>;
|
|
4
|
+
export declare function mergeAgentResponseChunk<T extends Message>(output: T, chunk: AgentResponseChunk<T>): T;
|
|
5
|
+
export declare function agentResponseStreamToObject<T extends Message>(stream: AgentResponseStream<T> | AgentProcessAsyncGenerator<T>): Promise<T>;
|
|
6
|
+
export declare function asyncGeneratorToReadableStream<T extends Message>(generator: AgentProcessAsyncGenerator<T>): AgentResponseStream<T>;
|
|
7
|
+
export declare function onAgentResponseStreamEnd<T extends Message>(stream: AgentResponseStream<T>, callback: (result: T) => PromiseOrValue<Partial<T> | void>, options?: {
|
|
8
|
+
errorCallback?: (error: Error) => Error;
|
|
9
|
+
processChunk?: (chunk: AgentResponseChunk<T>) => AgentResponseChunk<T>;
|
|
10
|
+
}): ReadableStream<any>;
|
|
11
|
+
export declare function isAsyncGenerator<T extends AsyncGenerator>(value: AsyncGenerator | unknown): value is T;
|
|
12
|
+
export declare function arrayToAgentProcessAsyncGenerator<T extends Message>(chunks: (AgentResponseChunk<T> | Error)[], result?: Partial<T>): AgentProcessAsyncGenerator<T>;
|
|
13
|
+
export declare function arrayToAgentResponseStream<T>(chunks: (AgentResponseChunk<T> | Error)[]): AgentResponseStream<T>;
|
|
14
|
+
export declare function readableStreamToArray<T>(stream: ReadableStream<T>): Promise<T[]>;
|
|
15
|
+
export declare function readableStreamToAsyncIterator<T>(stream: ReadableStream<T>): AsyncIterable<T>;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import equal from "fast-deep-equal";
|
|
2
|
+
import { omitBy } from "./type-utils.js";
|
|
3
|
+
export function objectToAgentResponseStream(json) {
|
|
4
|
+
return new ReadableStream({
|
|
5
|
+
start(controller) {
|
|
6
|
+
controller.enqueue({ delta: { json } });
|
|
7
|
+
controller.close();
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function mergeAgentResponseChunk(output, chunk) {
|
|
12
|
+
if (chunk.delta.text) {
|
|
13
|
+
for (const [key, text] of Object.entries(chunk.delta.text)) {
|
|
14
|
+
const original = output[key];
|
|
15
|
+
const t = (original || "") + (text || "");
|
|
16
|
+
if (t)
|
|
17
|
+
Object.assign(output, { [key]: t });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (chunk.delta.json) {
|
|
21
|
+
Object.assign(output, omitBy(chunk.delta.json, (v) => v === undefined));
|
|
22
|
+
}
|
|
23
|
+
return output;
|
|
24
|
+
}
|
|
25
|
+
export async function agentResponseStreamToObject(stream) {
|
|
26
|
+
const json = {};
|
|
27
|
+
if (stream instanceof ReadableStream) {
|
|
28
|
+
for await (const value of readableStreamToAsyncIterator(stream)) {
|
|
29
|
+
mergeAgentResponseChunk(json, value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
for (;;) {
|
|
34
|
+
const chunk = await stream.next();
|
|
35
|
+
if (chunk.value) {
|
|
36
|
+
if (chunk.done) {
|
|
37
|
+
Object.assign(json, chunk.value);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
mergeAgentResponseChunk(json, chunk.value);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (chunk.done)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return json;
|
|
48
|
+
}
|
|
49
|
+
export function asyncGeneratorToReadableStream(generator) {
|
|
50
|
+
return new ReadableStream({
|
|
51
|
+
async start(controller) {
|
|
52
|
+
try {
|
|
53
|
+
for (;;) {
|
|
54
|
+
const chunk = await generator.next();
|
|
55
|
+
if (chunk.value) {
|
|
56
|
+
if (chunk.done) {
|
|
57
|
+
controller.enqueue({ delta: { json: chunk.value } });
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
controller.enqueue(chunk.value);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (chunk.done)
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
controller.error(error);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
controller.close();
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
export function onAgentResponseStreamEnd(stream, callback, options) {
|
|
77
|
+
return new ReadableStream({
|
|
78
|
+
async start(controller) {
|
|
79
|
+
try {
|
|
80
|
+
const json = {};
|
|
81
|
+
for await (const value of readableStreamToAsyncIterator(stream)) {
|
|
82
|
+
const chunk = options?.processChunk ? options.processChunk(value) : value;
|
|
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
|
+
}
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "AIGNE core library for building AI-powered applications",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"@modelcontextprotocol/sdk": "^1.9.0",
|
|
67
67
|
"@types/debug": "^4.1.12",
|
|
68
68
|
"debug": "^4.4.0",
|
|
69
|
+
"fast-deep-equal": "^3.1.3",
|
|
69
70
|
"inquirer": "^12.5.2",
|
|
70
71
|
"mustache": "^4.2.0",
|
|
71
72
|
"nanoid": "^5.1.5",
|