@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.
@@ -1,4 +1,5 @@
1
- import type { AgentResponseChunk, AgentResponseStream, Message } from "../agents/agent.js";
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>, callback: (result: T) => PromiseOrValue<Partial<T> | void>, options?: {
10
- errorCallback?: (error: Error) => PromiseOrValue<Error>;
11
- processChunk?: (chunk: AgentResponseChunk<T>) => AgentResponseChunk<T>;
12
- }): ReadableStream<any>;
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?: false;
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
  *
@@ -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, async (result) => {
323
- return await this.processAgentOutput(parsedInput, result, opts);
324
- }, {
325
- errorCallback: async (error) => {
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, async (result) => {
426
- const error = await this.runGuideRails(input, result, options);
427
- if (error) {
428
- return {
429
- ...(await this.onGuideRailError(error)),
430
- $status: "GuideRailError",
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.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);
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;
@@ -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), async ({ __activeAgent__: activeAgent }) => {
94
- activeAgentPromise.resolve(activeAgent);
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
- return chunk;
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 [stream, activeAgentPromise.promise];
114
+ return [finalStream, activeAgentPromise.promise];
111
115
  }
112
- return stream;
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.delta.json) {
215
- value.delta.json = omitExistsProperties(result, value.delta.json);
216
- Object.assign(result, value.delta.json);
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 { AgentResponseChunk, AgentResponseStream, Message } from "../agents/agent.js";
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
- if (event.event === "error") {
16
- controller.enqueue(new Error(json.message));
17
- }
18
- else {
19
- controller.enqueue(json);
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
- this.json = produce(this.json, (draft) => {
42
- if (chunk.delta.json)
43
- Object.assign(draft, chunk.delta.json);
44
- if (chunk.delta.text) {
45
- for (const [key, text] of Object.entries(chunk.delta.text)) {
46
- const original = draft[key];
47
- const t = (original || "") + (text || "");
48
- if (t)
49
- Object.assign(draft, { [key]: t });
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
- controller.enqueue({
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>, callback: (result: T) => PromiseOrValue<Partial<T> | void>, options?: {
10
- errorCallback?: (error: Error) => PromiseOrValue<Error>;
11
- processChunk?: (chunk: AgentResponseChunk<T>) => AgentResponseChunk<T>;
12
- }): ReadableStream<any>;
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?: false;
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.delta.text) {
15
- for (const [key, text] of Object.entries(chunk.delta.text)) {
16
- const original = output[key];
17
- const t = (original || "") + (text || "");
18
- if (t)
19
- Object.assign(output, { [key]: t });
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, callback, options) {
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 callback(json);
85
+ const result = (await options?.onResult?.(json)) ?? json;
84
86
  if (result && !equal(result, json)) {
85
87
  let chunk = { delta: { json: result } };
86
- if (options?.processChunk)
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?.processChunk ? options.processChunk(value) : value;
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?.errorCallback?.(error)) ?? error);
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.18.6",
3
+ "version": "1.19.0",
4
4
  "description": "AIGNE core library for building AI-powered applications",
5
5
  "publishConfig": {
6
6
  "access": "public"