@aigne/core 1.18.6 → 1.19.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 +7 -0
- package/lib/cjs/agents/agent.d.ts +20 -2
- package/lib/cjs/agents/agent.js +24 -13
- package/lib/cjs/agents/ai-agent.js +9 -8
- package/lib/cjs/agents/team-agent.js +1 -1
- package/lib/cjs/aigne/context.d.ts +3 -0
- package/lib/cjs/aigne/context.js +18 -13
- package/lib/cjs/utils/event-stream.d.ts +5 -1
- package/lib/cjs/utils/event-stream.js +88 -23
- package/lib/cjs/utils/stream-utils.d.ts +8 -5
- package/lib/cjs/utils/stream-utils.js +48 -15
- package/lib/cjs/utils/type-utils.d.ts +1 -0
- package/lib/cjs/utils/type-utils.js +5 -0
- package/lib/dts/agents/agent.d.ts +20 -2
- package/lib/dts/aigne/context.d.ts +3 -0
- package/lib/dts/utils/event-stream.d.ts +5 -1
- package/lib/dts/utils/stream-utils.d.ts +8 -5
- package/lib/dts/utils/type-utils.d.ts +1 -0
- package/lib/esm/agents/agent.d.ts +20 -2
- package/lib/esm/agents/agent.js +22 -13
- package/lib/esm/agents/ai-agent.js +10 -9
- package/lib/esm/agents/team-agent.js +2 -2
- package/lib/esm/aigne/context.d.ts +3 -0
- package/lib/esm/aigne/context.js +20 -15
- package/lib/esm/utils/event-stream.d.ts +5 -1
- package/lib/esm/utils/event-stream.js +86 -22
- package/lib/esm/utils/stream-utils.d.ts +8 -5
- package/lib/esm/utils/stream-utils.js +48 -16
- package/lib/esm/utils/type-utils.d.ts +1 -0
- package/lib/esm/utils/type-utils.js +4 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AgentResponseChunk, type AgentResponseProgress, type AgentResponseStream, type Message } from "../agents/agent.js";
|
|
2
|
+
import type { Context } from "../aigne/context.js";
|
|
2
3
|
export declare class EventStreamParser<T> extends TransformStream<string, T | Error> {
|
|
3
4
|
constructor();
|
|
4
5
|
}
|
|
@@ -9,3 +10,6 @@ export declare class AgentResponseStreamParser<O extends Message> extends Transf
|
|
|
9
10
|
export declare class AgentResponseStreamSSE<O extends Message> extends ReadableStream<string> {
|
|
10
11
|
constructor(stream: AgentResponseStream<O>);
|
|
11
12
|
}
|
|
13
|
+
export declare class AgentResponseProgressStream extends ReadableStream<AgentResponseProgress> {
|
|
14
|
+
constructor(context: Context);
|
|
15
|
+
}
|
|
@@ -6,10 +6,11 @@ export declare function objectToAgentResponseStream<T extends Message>(json: T):
|
|
|
6
6
|
export declare function mergeAgentResponseChunk<T extends Message>(output: T, chunk: AgentResponseChunk<T>): T;
|
|
7
7
|
export declare function agentResponseStreamToObject<T extends Message>(stream: AgentResponseStream<T> | AgentProcessAsyncGenerator<T>): Promise<T>;
|
|
8
8
|
export declare function asyncGeneratorToReadableStream<T extends Message>(generator: AgentProcessAsyncGenerator<T>): AgentResponseStream<T>;
|
|
9
|
-
export declare function onAgentResponseStreamEnd<T extends Message>(stream: AgentResponseStream<T>,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
export declare function onAgentResponseStreamEnd<T extends Message>(stream: AgentResponseStream<T>, options?: {
|
|
10
|
+
onChunk?: (chunk: AgentResponseChunk<T>) => PromiseOrValue<AgentResponseChunk<T> | undefined | void>;
|
|
11
|
+
onResult?: (result: T) => PromiseOrValue<Partial<T> | undefined | void>;
|
|
12
|
+
onError?: (error: Error) => PromiseOrValue<Error>;
|
|
13
|
+
}): AgentResponseStream<T>;
|
|
13
14
|
export declare function isAsyncGenerator<T extends AsyncGenerator>(value: AsyncGenerator | unknown): value is T;
|
|
14
15
|
export declare function arrayToAgentProcessAsyncGenerator<T extends Message>(chunks: (AgentResponseChunk<T> | Error)[], result?: Partial<T>): AgentProcessAsyncGenerator<T>;
|
|
15
16
|
export declare function arrayToReadableStream<T>(chunks: (T | Error)[]): ReadableStream<T>;
|
|
@@ -17,8 +18,10 @@ export declare function readableStreamToArray<T>(stream: ReadableStream<T>, opti
|
|
|
17
18
|
catchError: true;
|
|
18
19
|
}): Promise<(T | Error)[]>;
|
|
19
20
|
export declare function readableStreamToArray<T>(stream: ReadableStream<T>, options?: {
|
|
20
|
-
catchError?:
|
|
21
|
+
catchError?: boolean;
|
|
21
22
|
}): Promise<T[]>;
|
|
22
23
|
export declare function stringToAgentResponseStream(str: string, key?: "text" | typeof MESSAGE_KEY | string): AgentResponseStream<Message>;
|
|
23
24
|
export declare function toReadableStream(stream: NodeJS.ReadStream): ReadableStream<Uint8Array<ArrayBufferLike>>;
|
|
24
25
|
export declare function readAllString(stream: NodeJS.ReadStream | ReadableStream): Promise<string>;
|
|
26
|
+
export declare function mergeReadableStreams<T1, T2>(s1: ReadableStream<T1>, s2: ReadableStream<T2>): ReadableStream<T1 | T2>;
|
|
27
|
+
export declare function mergeReadableStreams(...streams: ReadableStream<any>[]): ReadableStream<any>;
|
|
@@ -15,6 +15,7 @@ export declare function isNotEmpty<T>(arr: T[]): arr is [T, ...T[]];
|
|
|
15
15
|
export declare function duplicates<T>(arr: T[], key?: (item: T) => unknown): T[];
|
|
16
16
|
export declare function remove<T>(arr: T[], remove: T[] | ((item: T) => boolean)): T[];
|
|
17
17
|
export declare function unique<T>(arr: T[], key?: (item: T) => unknown): T[];
|
|
18
|
+
export declare function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, ...keys: (K | K[])[]): Omit<T, K>;
|
|
18
19
|
export declare function omitBy<T extends Record<string, unknown>, K extends keyof T>(obj: T, predicate: (value: T[K], key: K) => boolean): Partial<T>;
|
|
19
20
|
export declare function orArrayToArray<T>(value?: T | T[]): T[];
|
|
20
21
|
export declare function createAccessorArray<T>(array: T[], accessor: (array: T[], name: string) => T | undefined): T[] & {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
|
|
2
2
|
import { ZodObject, type ZodType } from "zod";
|
|
3
|
-
import type { Context, UserContext } from "../aigne/context.js";
|
|
3
|
+
import type { AgentEvent, Context, UserContext } from "../aigne/context.js";
|
|
4
4
|
import type { MessagePayload } from "../aigne/message-queue.js";
|
|
5
5
|
import type { Memory, MemoryAgent } from "../memory/memory.js";
|
|
6
6
|
import type { MemoryRecorderInput } from "../memory/recorder.js";
|
|
@@ -594,7 +594,7 @@ export type AgentResponseStream<T> = ReadableStream<AgentResponseChunk<T>>;
|
|
|
594
594
|
*
|
|
595
595
|
* @template T Response data type
|
|
596
596
|
*/
|
|
597
|
-
export type AgentResponseChunk<T> = AgentResponseDelta<T
|
|
597
|
+
export type AgentResponseChunk<T> = AgentResponseDelta<T> | AgentResponseProgress;
|
|
598
598
|
/**
|
|
599
599
|
* Check if a response chunk is empty
|
|
600
600
|
*
|
|
@@ -622,6 +622,24 @@ export interface AgentResponseDelta<T> {
|
|
|
622
622
|
json?: Partial<T> | TransferAgentOutput;
|
|
623
623
|
};
|
|
624
624
|
}
|
|
625
|
+
export declare function isAgentResponseDelta<T>(chunk: AgentResponseChunk<T>): chunk is AgentResponseDelta<T>;
|
|
626
|
+
export interface AgentResponseProgress {
|
|
627
|
+
progress: ({
|
|
628
|
+
event: "agentStarted";
|
|
629
|
+
input: Message;
|
|
630
|
+
} | {
|
|
631
|
+
event: "agentSucceed";
|
|
632
|
+
output: Message;
|
|
633
|
+
} | {
|
|
634
|
+
event: "agentFailed";
|
|
635
|
+
error: Error;
|
|
636
|
+
}) & Omit<AgentEvent, "agent"> & {
|
|
637
|
+
agent: {
|
|
638
|
+
name: string;
|
|
639
|
+
};
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
export declare function isAgentResponseProgress<T>(chunk: AgentResponseChunk<T>): chunk is AgentResponseProgress;
|
|
625
643
|
/**
|
|
626
644
|
* Creates a text delta for streaming responses
|
|
627
645
|
*
|
package/lib/esm/agents/agent.js
CHANGED
|
@@ -319,10 +319,11 @@ export class Agent {
|
|
|
319
319
|
: isAsyncGenerator(response)
|
|
320
320
|
? asyncGeneratorToReadableStream(response)
|
|
321
321
|
: objectToAgentResponseStream(response);
|
|
322
|
-
return this.checkResponseByGuideRails(message, onAgentResponseStreamEnd(stream,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
322
|
+
return this.checkResponseByGuideRails(message, onAgentResponseStreamEnd(stream, {
|
|
323
|
+
onResult: async (result) => {
|
|
324
|
+
return await this.processAgentOutput(parsedInput, result, opts);
|
|
325
|
+
},
|
|
326
|
+
onError: async (error) => {
|
|
326
327
|
return await this.processAgentError(message, error, opts);
|
|
327
328
|
},
|
|
328
329
|
}), opts);
|
|
@@ -422,14 +423,16 @@ export class Agent {
|
|
|
422
423
|
return output;
|
|
423
424
|
const result = await output;
|
|
424
425
|
if (result instanceof ReadableStream) {
|
|
425
|
-
return onAgentResponseStreamEnd(result,
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
426
|
+
return onAgentResponseStreamEnd(result, {
|
|
427
|
+
onResult: async (result) => {
|
|
428
|
+
const error = await this.runGuideRails(input, result, options);
|
|
429
|
+
if (error) {
|
|
430
|
+
return {
|
|
431
|
+
...(await this.onGuideRailError(error)),
|
|
432
|
+
$status: "GuideRailError",
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
},
|
|
433
436
|
});
|
|
434
437
|
}
|
|
435
438
|
const error = await this.runGuideRails(input, result, options);
|
|
@@ -542,7 +545,13 @@ export class Agent {
|
|
|
542
545
|
* @returns True if the chunk is empty
|
|
543
546
|
*/
|
|
544
547
|
export function isEmptyChunk(chunk) {
|
|
545
|
-
return isEmpty(chunk.delta.json) && isEmpty(chunk.delta.text);
|
|
548
|
+
return isAgentResponseDelta(chunk) && isEmpty(chunk.delta.json) && isEmpty(chunk.delta.text);
|
|
549
|
+
}
|
|
550
|
+
export function isAgentResponseDelta(chunk) {
|
|
551
|
+
return "delta" in chunk;
|
|
552
|
+
}
|
|
553
|
+
export function isAgentResponseProgress(chunk) {
|
|
554
|
+
return "progress" in chunk;
|
|
546
555
|
}
|
|
547
556
|
/**
|
|
548
557
|
* Creates a text delta for streaming responses
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { MESSAGE_KEY, PromptBuilder } from "../prompt/prompt-builder.js";
|
|
3
3
|
import { AgentMessageTemplate, ToolMessageTemplate } from "../prompt/template.js";
|
|
4
4
|
import { checkArguments, isEmpty } from "../utils/type-utils.js";
|
|
5
|
-
import { Agent, agentOptionsSchema, } from "./agent.js";
|
|
5
|
+
import { Agent, agentOptionsSchema, isAgentResponseDelta, } from "./agent.js";
|
|
6
6
|
import { ChatModel, } from "./chat-model.js";
|
|
7
7
|
import { isTransferAgentOutput } from "./types.js";
|
|
8
8
|
/**
|
|
@@ -188,8 +188,7 @@ export class AIAgent extends Agent {
|
|
|
188
188
|
});
|
|
189
189
|
const toolsMap = new Map(toolAgents?.map((i) => [i.name, i]));
|
|
190
190
|
if (this.toolChoice === "router") {
|
|
191
|
-
yield* this._processRouter(input, model, modelInput, options, toolsMap);
|
|
192
|
-
return;
|
|
191
|
+
return yield* this._processRouter(input, model, modelInput, options, toolsMap);
|
|
193
192
|
}
|
|
194
193
|
const toolCallMessages = [];
|
|
195
194
|
const outputKey = this.outputKey || MESSAGE_KEY;
|
|
@@ -197,11 +196,13 @@ export class AIAgent extends Agent {
|
|
|
197
196
|
const modelOutput = {};
|
|
198
197
|
const stream = await options.context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
|
|
199
198
|
for await (const value of stream) {
|
|
200
|
-
if (value
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
if (isAgentResponseDelta(value)) {
|
|
200
|
+
if (value.delta.text?.text) {
|
|
201
|
+
yield { delta: { text: { [outputKey]: value.delta.text.text } } };
|
|
202
|
+
}
|
|
203
|
+
if (value.delta.json) {
|
|
204
|
+
Object.assign(modelOutput, value.delta.json);
|
|
205
|
+
}
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
const { toolCalls, json, text } = modelOutput;
|
|
@@ -272,6 +273,6 @@ export class AIAgent extends Agent {
|
|
|
272
273
|
if (!tool)
|
|
273
274
|
throw new Error(`Tool not found: ${call.function.name}`);
|
|
274
275
|
const stream = await options.context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true, sourceAgent: this });
|
|
275
|
-
yield* stream;
|
|
276
|
+
return yield* stream;
|
|
276
277
|
}
|
|
277
278
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mergeAgentResponseChunk } from "../utils/stream-utils.js";
|
|
2
2
|
import { isEmpty } from "../utils/type-utils.js";
|
|
3
|
-
import { Agent, } from "./agent.js";
|
|
3
|
+
import { Agent, isAgentResponseDelta, } from "./agent.js";
|
|
4
4
|
/**
|
|
5
5
|
* Defines the processing modes available for a TeamAgent.
|
|
6
6
|
*
|
|
@@ -155,7 +155,7 @@ export class TeamAgent extends Agent {
|
|
|
155
155
|
if (!done) {
|
|
156
156
|
tasks.set(index, read(index, reader));
|
|
157
157
|
}
|
|
158
|
-
if (value) {
|
|
158
|
+
if (value && isAgentResponseDelta(value)) {
|
|
159
159
|
let { delta: { text, ...delta }, } = value;
|
|
160
160
|
if (text) {
|
|
161
161
|
for (const key of Object.keys(text)) {
|
|
@@ -41,6 +41,7 @@ export type ContextEmitEventMap = {
|
|
|
41
41
|
*/
|
|
42
42
|
export interface InvokeOptions<U extends UserContext = UserContext> extends Partial<Omit<AgentInvokeOptions<U>, "context">> {
|
|
43
43
|
returnActiveAgent?: boolean;
|
|
44
|
+
returnProgressChunks?: boolean;
|
|
44
45
|
disableTransfer?: boolean;
|
|
45
46
|
sourceAgent?: Agent;
|
|
46
47
|
}
|
|
@@ -53,6 +54,8 @@ export interface UserContext extends Record<string, unknown> {
|
|
|
53
54
|
* @hidden
|
|
54
55
|
*/
|
|
55
56
|
export interface Context<U extends UserContext = UserContext> extends TypedEventEmitter<ContextEventMap, ContextEmitEventMap> {
|
|
57
|
+
id: string;
|
|
58
|
+
parentId?: string;
|
|
56
59
|
model?: ChatModel;
|
|
57
60
|
skills?: Agent[];
|
|
58
61
|
usage: ContextUsage;
|
package/lib/esm/aigne/context.js
CHANGED
|
@@ -2,12 +2,13 @@ import equal from "fast-deep-equal";
|
|
|
2
2
|
import { Emitter } from "strict-event-emitter";
|
|
3
3
|
import { v7 } from "uuid";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { Agent, isEmptyChunk, } from "../agents/agent.js";
|
|
5
|
+
import { Agent, isAgentResponseDelta, isEmptyChunk, } from "../agents/agent.js";
|
|
6
6
|
import { isTransferAgentOutput, transferAgentOutputKey, } from "../agents/types.js";
|
|
7
7
|
import { UserAgent } from "../agents/user-agent.js";
|
|
8
8
|
import { createMessage } from "../prompt/prompt-builder.js";
|
|
9
|
+
import { AgentResponseProgressStream } from "../utils/event-stream.js";
|
|
9
10
|
import { promiseWithResolvers } from "../utils/promise.js";
|
|
10
|
-
import { agentResponseStreamToObject, asyncGeneratorToReadableStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
|
|
11
|
+
import { agentResponseStreamToObject, asyncGeneratorToReadableStream, mergeReadableStreams, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
|
|
11
12
|
import { checkArguments, isEmpty, isNil, omitBy, } from "../utils/type-utils.js";
|
|
12
13
|
import { MessageQueue, toMessagePayload, } from "./message-queue.js";
|
|
13
14
|
import { newEmptyContextUsage } from "./usage.js";
|
|
@@ -90,11 +91,9 @@ export class AIGNEContext {
|
|
|
90
91
|
return output;
|
|
91
92
|
}
|
|
92
93
|
const activeAgentPromise = promiseWithResolvers();
|
|
93
|
-
const stream = onAgentResponseStreamEnd(asyncGeneratorToReadableStream(response),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
processChunk(chunk) {
|
|
97
|
-
if (chunk.delta.json) {
|
|
94
|
+
const stream = onAgentResponseStreamEnd(asyncGeneratorToReadableStream(response), {
|
|
95
|
+
onChunk(chunk) {
|
|
96
|
+
if (isAgentResponseDelta(chunk) && chunk.delta.json) {
|
|
98
97
|
return {
|
|
99
98
|
...chunk,
|
|
100
99
|
delta: {
|
|
@@ -103,13 +102,18 @@ export class AIGNEContext {
|
|
|
103
102
|
},
|
|
104
103
|
};
|
|
105
104
|
}
|
|
106
|
-
|
|
105
|
+
},
|
|
106
|
+
onResult({ __activeAgent__: activeAgent }) {
|
|
107
|
+
activeAgentPromise.resolve(activeAgent);
|
|
107
108
|
},
|
|
108
109
|
});
|
|
110
|
+
const finalStream = !options.returnProgressChunks
|
|
111
|
+
? stream
|
|
112
|
+
: mergeReadableStreams(stream, new AgentResponseProgressStream(newContext));
|
|
109
113
|
if (options.returnActiveAgent) {
|
|
110
|
-
return [
|
|
114
|
+
return [finalStream, activeAgentPromise.promise];
|
|
111
115
|
}
|
|
112
|
-
return
|
|
116
|
+
return finalStream;
|
|
113
117
|
});
|
|
114
118
|
});
|
|
115
119
|
publish = ((topic, payload, options) => {
|
|
@@ -164,7 +168,6 @@ class AIGNEContextShared {
|
|
|
164
168
|
this.memories = overrides?.memories ?? [];
|
|
165
169
|
}
|
|
166
170
|
messageQueue;
|
|
167
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
168
171
|
events = new Emitter();
|
|
169
172
|
get model() {
|
|
170
173
|
return this.parent?.model;
|
|
@@ -211,11 +214,13 @@ class AIGNEContextShared {
|
|
|
211
214
|
}
|
|
212
215
|
const stream = await activeAgent.invoke(input, { ...options, context, streaming: true });
|
|
213
216
|
for await (const value of stream) {
|
|
214
|
-
if (value
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
if (isAgentResponseDelta(value)) {
|
|
218
|
+
if (value.delta.json) {
|
|
219
|
+
value.delta.json = omitExistsProperties(result, value.delta.json);
|
|
220
|
+
Object.assign(result, value.delta.json);
|
|
221
|
+
}
|
|
222
|
+
delete value.delta.json?.[transferAgentOutputKey];
|
|
217
223
|
}
|
|
218
|
-
delete value.delta.json?.[transferAgentOutputKey];
|
|
219
224
|
if (isEmptyChunk(value))
|
|
220
225
|
continue;
|
|
221
226
|
yield value;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AgentResponseChunk, type AgentResponseProgress, type AgentResponseStream, type Message } from "../agents/agent.js";
|
|
2
|
+
import type { Context } from "../aigne/context.js";
|
|
2
3
|
export declare class EventStreamParser<T> extends TransformStream<string, T | Error> {
|
|
3
4
|
constructor();
|
|
4
5
|
}
|
|
@@ -9,3 +10,6 @@ export declare class AgentResponseStreamParser<O extends Message> extends Transf
|
|
|
9
10
|
export declare class AgentResponseStreamSSE<O extends Message> extends ReadableStream<string> {
|
|
10
11
|
constructor(stream: AgentResponseStream<O>);
|
|
11
12
|
}
|
|
13
|
+
export declare class AgentResponseProgressStream extends ReadableStream<AgentResponseProgress> {
|
|
14
|
+
constructor(context: Context);
|
|
15
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createParser } from "eventsource-parser";
|
|
2
2
|
import { produce } from "immer";
|
|
3
|
+
import { isAgentResponseDelta, isAgentResponseProgress, } from "../agents/agent.js";
|
|
3
4
|
import { tryOrThrow } from "./type-utils.js";
|
|
4
5
|
export class EventStreamParser extends TransformStream {
|
|
5
6
|
constructor() {
|
|
@@ -12,11 +13,16 @@ export class EventStreamParser extends TransformStream {
|
|
|
12
13
|
controller.enqueue(new Error(`Parse response chunk json error: ${e.message} ${event.data}`));
|
|
13
14
|
});
|
|
14
15
|
if (json) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
switch (event.event) {
|
|
17
|
+
case "error":
|
|
18
|
+
controller.enqueue(new Error(json.message));
|
|
19
|
+
break;
|
|
20
|
+
default: {
|
|
21
|
+
if (!event.event)
|
|
22
|
+
controller.enqueue(json);
|
|
23
|
+
else
|
|
24
|
+
console.warn(`Unknown event type: ${event.event}`, event.data);
|
|
25
|
+
}
|
|
20
26
|
}
|
|
21
27
|
}
|
|
22
28
|
},
|
|
@@ -38,25 +44,35 @@ export class AgentResponseStreamParser extends TransformStream {
|
|
|
38
44
|
controller.terminate();
|
|
39
45
|
return;
|
|
40
46
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
if (isAgentResponseDelta(chunk)) {
|
|
48
|
+
this.json = produce(this.json, (draft) => {
|
|
49
|
+
if (chunk.delta.json)
|
|
50
|
+
Object.assign(draft, chunk.delta.json);
|
|
51
|
+
if (chunk.delta.text) {
|
|
52
|
+
for (const [key, text] of Object.entries(chunk.delta.text)) {
|
|
53
|
+
const original = draft[key];
|
|
54
|
+
const t = (original || "") + (text || "");
|
|
55
|
+
if (t)
|
|
56
|
+
Object.assign(draft, { [key]: t });
|
|
57
|
+
}
|
|
50
58
|
}
|
|
59
|
+
});
|
|
60
|
+
controller.enqueue({
|
|
61
|
+
...chunk,
|
|
62
|
+
delta: {
|
|
63
|
+
...chunk.delta,
|
|
64
|
+
json: this.json,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else if (isAgentResponseProgress(chunk)) {
|
|
69
|
+
if (chunk.progress.event === "agentFailed") {
|
|
70
|
+
const { name, message } = chunk.progress.error;
|
|
71
|
+
chunk.progress.error = new Error(message);
|
|
72
|
+
chunk.progress.error.name = name;
|
|
51
73
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
...chunk,
|
|
55
|
-
delta: {
|
|
56
|
-
...chunk.delta,
|
|
57
|
-
json: this.json,
|
|
58
|
-
},
|
|
59
|
-
});
|
|
74
|
+
controller.enqueue(chunk);
|
|
75
|
+
}
|
|
60
76
|
},
|
|
61
77
|
});
|
|
62
78
|
}
|
|
@@ -73,6 +89,14 @@ export class AgentResponseStreamSSE extends ReadableStream {
|
|
|
73
89
|
controller.close();
|
|
74
90
|
return;
|
|
75
91
|
}
|
|
92
|
+
if (isAgentResponseProgress(value)) {
|
|
93
|
+
if (value.progress.event === "agentFailed") {
|
|
94
|
+
value.progress.error = {
|
|
95
|
+
name: value.progress.error.name,
|
|
96
|
+
message: value.progress.error.message,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
76
100
|
controller.enqueue(`data: ${JSON.stringify(value)}\n\n`);
|
|
77
101
|
}
|
|
78
102
|
catch (error) {
|
|
@@ -83,3 +107,43 @@ export class AgentResponseStreamSSE extends ReadableStream {
|
|
|
83
107
|
});
|
|
84
108
|
}
|
|
85
109
|
}
|
|
110
|
+
export class AgentResponseProgressStream extends ReadableStream {
|
|
111
|
+
constructor(context) {
|
|
112
|
+
super({
|
|
113
|
+
async start(controller) {
|
|
114
|
+
const writeEvent = (eventName, event) => {
|
|
115
|
+
const progress = {
|
|
116
|
+
...event,
|
|
117
|
+
event: eventName,
|
|
118
|
+
agent: { name: event.agent.name },
|
|
119
|
+
};
|
|
120
|
+
controller.enqueue({ progress });
|
|
121
|
+
};
|
|
122
|
+
const close = () => {
|
|
123
|
+
context.off("agentStarted", onAgentStarted);
|
|
124
|
+
context.off("agentSucceed", onAgentSucceed);
|
|
125
|
+
context.off("agentFailed", onAgentFailed);
|
|
126
|
+
controller.close();
|
|
127
|
+
};
|
|
128
|
+
const onAgentStarted = (event) => {
|
|
129
|
+
writeEvent("agentStarted", event);
|
|
130
|
+
};
|
|
131
|
+
const onAgentSucceed = (event) => {
|
|
132
|
+
writeEvent("agentSucceed", event);
|
|
133
|
+
if (event.contextId === context.id) {
|
|
134
|
+
close();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const onAgentFailed = (event) => {
|
|
138
|
+
writeEvent("agentFailed", event);
|
|
139
|
+
if (event.contextId === context.id) {
|
|
140
|
+
close();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
context.on("agentStarted", onAgentStarted);
|
|
144
|
+
context.on("agentSucceed", onAgentSucceed);
|
|
145
|
+
context.on("agentFailed", onAgentFailed);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -6,10 +6,11 @@ export declare function objectToAgentResponseStream<T extends Message>(json: T):
|
|
|
6
6
|
export declare function mergeAgentResponseChunk<T extends Message>(output: T, chunk: AgentResponseChunk<T>): T;
|
|
7
7
|
export declare function agentResponseStreamToObject<T extends Message>(stream: AgentResponseStream<T> | AgentProcessAsyncGenerator<T>): Promise<T>;
|
|
8
8
|
export declare function asyncGeneratorToReadableStream<T extends Message>(generator: AgentProcessAsyncGenerator<T>): AgentResponseStream<T>;
|
|
9
|
-
export declare function onAgentResponseStreamEnd<T extends Message>(stream: AgentResponseStream<T>,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
export declare function onAgentResponseStreamEnd<T extends Message>(stream: AgentResponseStream<T>, options?: {
|
|
10
|
+
onChunk?: (chunk: AgentResponseChunk<T>) => PromiseOrValue<AgentResponseChunk<T> | undefined | void>;
|
|
11
|
+
onResult?: (result: T) => PromiseOrValue<Partial<T> | undefined | void>;
|
|
12
|
+
onError?: (error: Error) => PromiseOrValue<Error>;
|
|
13
|
+
}): AgentResponseStream<T>;
|
|
13
14
|
export declare function isAsyncGenerator<T extends AsyncGenerator>(value: AsyncGenerator | unknown): value is T;
|
|
14
15
|
export declare function arrayToAgentProcessAsyncGenerator<T extends Message>(chunks: (AgentResponseChunk<T> | Error)[], result?: Partial<T>): AgentProcessAsyncGenerator<T>;
|
|
15
16
|
export declare function arrayToReadableStream<T>(chunks: (T | Error)[]): ReadableStream<T>;
|
|
@@ -17,8 +18,10 @@ export declare function readableStreamToArray<T>(stream: ReadableStream<T>, opti
|
|
|
17
18
|
catchError: true;
|
|
18
19
|
}): Promise<(T | Error)[]>;
|
|
19
20
|
export declare function readableStreamToArray<T>(stream: ReadableStream<T>, options?: {
|
|
20
|
-
catchError?:
|
|
21
|
+
catchError?: boolean;
|
|
21
22
|
}): Promise<T[]>;
|
|
22
23
|
export declare function stringToAgentResponseStream(str: string, key?: "text" | typeof MESSAGE_KEY | string): AgentResponseStream<Message>;
|
|
23
24
|
export declare function toReadableStream(stream: NodeJS.ReadStream): ReadableStream<Uint8Array<ArrayBufferLike>>;
|
|
24
25
|
export declare function readAllString(stream: NodeJS.ReadStream | ReadableStream): Promise<string>;
|
|
26
|
+
export declare function mergeReadableStreams<T1, T2>(s1: ReadableStream<T1>, s2: ReadableStream<T2>): ReadableStream<T1 | T2>;
|
|
27
|
+
export declare function mergeReadableStreams(...streams: ReadableStream<any>[]): ReadableStream<any>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import equal from "fast-deep-equal";
|
|
2
|
-
import { isEmptyChunk, } from "../agents/agent.js";
|
|
2
|
+
import { isAgentResponseDelta, isEmptyChunk, } from "../agents/agent.js";
|
|
3
3
|
import { omitBy } from "./type-utils.js";
|
|
4
4
|
import "./stream-polyfill.js";
|
|
5
5
|
export function objectToAgentResponseStream(json) {
|
|
@@ -11,16 +11,18 @@ export function objectToAgentResponseStream(json) {
|
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
13
|
export function mergeAgentResponseChunk(output, chunk) {
|
|
14
|
-
if (chunk
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
if (isAgentResponseDelta(chunk)) {
|
|
15
|
+
if (chunk.delta.text) {
|
|
16
|
+
for (const [key, text] of Object.entries(chunk.delta.text)) {
|
|
17
|
+
const original = output[key];
|
|
18
|
+
const t = (original || "") + (text || "");
|
|
19
|
+
if (t)
|
|
20
|
+
Object.assign(output, { [key]: t });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (chunk.delta.json) {
|
|
24
|
+
Object.assign(output, omitBy(chunk.delta.json, (v) => v === undefined));
|
|
20
25
|
}
|
|
21
|
-
}
|
|
22
|
-
if (chunk.delta.json) {
|
|
23
|
-
Object.assign(output, omitBy(chunk.delta.json, (v) => v === undefined));
|
|
24
26
|
}
|
|
25
27
|
return output;
|
|
26
28
|
}
|
|
@@ -71,7 +73,7 @@ export function asyncGeneratorToReadableStream(generator) {
|
|
|
71
73
|
},
|
|
72
74
|
});
|
|
73
75
|
}
|
|
74
|
-
export function onAgentResponseStreamEnd(stream,
|
|
76
|
+
export function onAgentResponseStreamEnd(stream, options) {
|
|
75
77
|
const json = {};
|
|
76
78
|
const reader = stream.getReader();
|
|
77
79
|
return new ReadableStream({
|
|
@@ -80,18 +82,17 @@ export function onAgentResponseStreamEnd(stream, callback, options) {
|
|
|
80
82
|
while (true) {
|
|
81
83
|
const { value, done } = await reader.read();
|
|
82
84
|
if (done) {
|
|
83
|
-
const result = await
|
|
85
|
+
const result = (await options?.onResult?.(json)) ?? json;
|
|
84
86
|
if (result && !equal(result, json)) {
|
|
85
87
|
let chunk = { delta: { json: result } };
|
|
86
|
-
|
|
87
|
-
chunk = options.processChunk(chunk);
|
|
88
|
+
chunk = (await options?.onChunk?.(chunk)) ?? chunk;
|
|
88
89
|
controller.enqueue(chunk);
|
|
89
90
|
}
|
|
90
91
|
controller.close();
|
|
91
92
|
return;
|
|
92
93
|
}
|
|
93
94
|
mergeAgentResponseChunk(json, value);
|
|
94
|
-
const chunk = options?.
|
|
95
|
+
const chunk = (await options?.onChunk?.(value)) ?? value;
|
|
95
96
|
if (!isEmptyChunk(chunk)) {
|
|
96
97
|
controller.enqueue(chunk);
|
|
97
98
|
break;
|
|
@@ -99,7 +100,7 @@ export function onAgentResponseStreamEnd(stream, callback, options) {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
catch (error) {
|
|
102
|
-
controller.error((await options?.
|
|
103
|
+
controller.error((await options?.onError?.(error)) ?? error);
|
|
103
104
|
}
|
|
104
105
|
},
|
|
105
106
|
});
|
|
@@ -180,3 +181,34 @@ export function toReadableStream(stream) {
|
|
|
180
181
|
export async function readAllString(stream) {
|
|
181
182
|
return (await readableStreamToArray((stream instanceof ReadableStream ? stream : toReadableStream(stream)).pipeThrough(new TextDecoderStream()))).join("");
|
|
182
183
|
}
|
|
184
|
+
export function mergeReadableStreams(...streams) {
|
|
185
|
+
let readers;
|
|
186
|
+
return new ReadableStream({
|
|
187
|
+
async pull(controller) {
|
|
188
|
+
try {
|
|
189
|
+
readers ??= streams.map((s) => ({ reader: s.getReader(), data: [] }));
|
|
190
|
+
while (readers.length) {
|
|
191
|
+
const chunk = await Promise.race(readers.map((i) => {
|
|
192
|
+
i.reading ??= i.reader.read().then((result) => ({ result, item: i }));
|
|
193
|
+
return i.reading;
|
|
194
|
+
}));
|
|
195
|
+
if (chunk.result.value) {
|
|
196
|
+
controller.enqueue(chunk.result.value);
|
|
197
|
+
chunk.item.reading = undefined;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (chunk.result.done) {
|
|
201
|
+
readers = readers.filter((i) => i !== chunk.item);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
controller.close();
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
controller.error(error);
|
|
208
|
+
if (readers)
|
|
209
|
+
for (const item of readers)
|
|
210
|
+
item.reader.releaseLock();
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
}
|
|
@@ -15,6 +15,7 @@ export declare function isNotEmpty<T>(arr: T[]): arr is [T, ...T[]];
|
|
|
15
15
|
export declare function duplicates<T>(arr: T[], key?: (item: T) => unknown): T[];
|
|
16
16
|
export declare function remove<T>(arr: T[], remove: T[] | ((item: T) => boolean)): T[];
|
|
17
17
|
export declare function unique<T>(arr: T[], key?: (item: T) => unknown): T[];
|
|
18
|
+
export declare function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, ...keys: (K | K[])[]): Omit<T, K>;
|
|
18
19
|
export declare function omitBy<T extends Record<string, unknown>, K extends keyof T>(obj: T, predicate: (value: T[K], key: K) => boolean): Partial<T>;
|
|
19
20
|
export declare function orArrayToArray<T>(value?: T | T[]): T[];
|
|
20
21
|
export declare function createAccessorArray<T>(array: T[], accessor: (array: T[], name: string) => T | undefined): T[] & {
|
|
@@ -58,6 +58,10 @@ export function unique(arr, key = (item) => item) {
|
|
|
58
58
|
return true;
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
+
export function omit(obj, ...keys) {
|
|
62
|
+
const flattenedKeys = new Set(keys.flat());
|
|
63
|
+
return Object.fromEntries(Object.entries(obj).filter(([key]) => !flattenedKeys.has(key)));
|
|
64
|
+
}
|
|
61
65
|
export function omitBy(obj, predicate) {
|
|
62
66
|
return Object.fromEntries(Object.entries(obj).filter(([key, value]) => {
|
|
63
67
|
const k = key;
|