@xsai/stream-text 0.2.0 → 0.2.2

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/dist/index.d.ts CHANGED
@@ -1,4 +1,33 @@
1
- import { ToolCall, FinishReason, Usage, ChatOptions, AssistantMessage, Message, StepType, CompletionToolCall, CompletionToolResult, Tool } from '@xsai/shared-chat';
1
+ import { ToolMessagePart, FinishReason, Usage, ToolCall, ChatOptions, AssistantMessage, Message, StepType, CompletionToolCall, CompletionToolResult, Tool } from '@xsai/shared-chat';
2
+
3
+ type StreamTextEvent = {
4
+ error: unknown;
5
+ type: 'error';
6
+ } | {
7
+ error?: unknown;
8
+ id: string;
9
+ result?: string | ToolMessagePart[];
10
+ type: 'tool-call-result';
11
+ } | {
12
+ finishReason?: FinishReason;
13
+ type: 'finish';
14
+ usage?: Usage;
15
+ } | {
16
+ reasoning: string;
17
+ type: 'reasoning';
18
+ } | {
19
+ refusal: string;
20
+ type: 'refusal';
21
+ } | {
22
+ text: string;
23
+ type: 'text-delta';
24
+ } | {
25
+ toolCall: ToolCall;
26
+ type: 'tool-call';
27
+ } | {
28
+ toolCall: ToolCall;
29
+ type: 'tool-call-delta';
30
+ };
2
31
 
3
32
  interface StreamTextChunkResult {
4
33
  choices: {
@@ -29,6 +58,11 @@ interface StreamTextOptions extends ChatOptions {
29
58
  * @param chunk - The current chunk of the stream.
30
59
  */
31
60
  onChunk?: (chunk: StreamTextChunkResult) => Promise<unknown> | unknown;
61
+ /**
62
+ * Callback function that is called with each event of the stream.
63
+ * @param event - The current event of the stream.
64
+ */
65
+ onEvent?: (event: StreamTextEvent) => Promise<unknown> | unknown;
32
66
  /**
33
67
  * Callback function that is called when the stream is finished.
34
68
  * @param result - The final result of the stream.
@@ -94,4 +128,4 @@ interface StreamTextToolCall extends ToolCall {
94
128
  }
95
129
  declare const streamText: (options: StreamTextOptions) => Promise<StreamTextResult>;
96
130
 
97
- export { type StreamTextChunkResult, type StreamTextOptions, type StreamTextResult, type StreamTextStep, streamText };
131
+ export { type StreamTextChunkResult, type StreamTextEvent, type StreamTextOptions, type StreamTextResult, type StreamTextStep, streamText };
package/dist/index.js CHANGED
@@ -61,6 +61,8 @@ const streamText = async (options) => {
61
61
  };
62
62
  const choiceState = {};
63
63
  let buffer = "";
64
+ let finishReason;
65
+ let usage;
64
66
  let shouldOutputText = true;
65
67
  const endToolCallByIndex = (state, idx) => {
66
68
  if (state.endedToolCallIndex.has(idx)) {
@@ -99,13 +101,21 @@ const streamText = async (options) => {
99
101
  stepCtrl.error(reason);
100
102
  textCtrl.error(reason);
101
103
  },
104
+ close: () => {
105
+ options2.onEvent?.({
106
+ finishReason,
107
+ type: "finish",
108
+ usage
109
+ });
110
+ },
102
111
  // eslint-disable-next-line sonarjs/cognitive-complexity
103
112
  write: async (chunk) => {
104
113
  options2.onChunk?.(chunk);
105
114
  chunkCtrl.enqueue(chunk);
115
+ usage = chunk.usage;
116
+ if (chunk.choices == null || chunk.choices.length === 0)
117
+ return;
106
118
  const choice = chunk.choices[0];
107
- if (!choice)
108
- throw new XSAIError("no choice found");
109
119
  if (choice.delta.tool_calls) {
110
120
  shouldOutputText = false;
111
121
  }
@@ -118,6 +128,7 @@ const streamText = async (options) => {
118
128
  }
119
129
  };
120
130
  if (finish_reason !== void 0) {
131
+ finishReason = finish_reason;
121
132
  step.finishReason = finish_reason;
122
133
  choiceSnapshot.finishReason = finish_reason;
123
134
  if (finish_reason === "length") {
@@ -133,12 +144,25 @@ const streamText = async (options) => {
133
144
  Object.assign(message, rests);
134
145
  if (refusal !== void 0) {
135
146
  message.refusal = (message.refusal || "") + (refusal || "");
147
+ options2.onEvent?.({
148
+ refusal: message.refusal,
149
+ type: "refusal"
150
+ });
136
151
  }
137
152
  if (content !== void 0) {
138
153
  message.content = (message.content || "") + (content || "");
139
154
  shouldOutputText && textCtrl?.enqueue(content);
155
+ options2.onEvent?.({
156
+ text: content,
157
+ type: "text-delta"
158
+ });
140
159
  }
141
- for (const { function: fn, id, index: index2, type } of tool_calls || []) {
160
+ for (const tool_call of tool_calls || []) {
161
+ options2.onEvent?.({
162
+ toolCall: tool_call,
163
+ type: "tool-call-delta"
164
+ });
165
+ const { function: fn, id, index: index2, type } = tool_call;
142
166
  message.toolCalls ??= {};
143
167
  const toolCall = message.toolCalls[index2] ??= {
144
168
  function: {
@@ -202,6 +226,10 @@ const streamText = async (options) => {
202
226
  if (state.toolCallResults[toolCall.id]) {
203
227
  return;
204
228
  }
229
+ options2.onEvent?.({
230
+ toolCall,
231
+ type: "tool-call"
232
+ });
205
233
  try {
206
234
  const { parsedArgs, result, toolName } = await executeTool({
207
235
  abortSignal: options2.abortSignal,
@@ -222,6 +250,11 @@ const streamText = async (options) => {
222
250
  toolCallId: toolCall.id,
223
251
  toolName
224
252
  });
253
+ options2.onEvent?.({
254
+ id: toolCall.id,
255
+ result,
256
+ type: "tool-call-result"
257
+ });
225
258
  } catch (error) {
226
259
  state.toolCallErrors[idx] = error;
227
260
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xsai/stream-text",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "description": "extra-small AI SDK.",
6
6
  "author": "Moeru AI",
7
7
  "license": "MIT",