@xsai/stream-text 0.4.0-beta.1 → 0.4.0-beta.3

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
@@ -51,8 +51,9 @@ interface StreamTextResult {
51
51
  messages: Promise<Message[]>;
52
52
  steps: Promise<CompletionStep[]>;
53
53
  textStream: ReadableStream<string>;
54
+ totalUsage: Promise<undefined | Usage>;
54
55
  usage: Promise<undefined | Usage>;
55
56
  }
56
- declare const streamText: (options: StreamTextOptions) => Promise<StreamTextResult>;
57
+ declare const streamText: (options: StreamTextOptions) => StreamTextResult;
57
58
 
58
59
  export { type StreamTextEvent, type StreamTextOptions, type StreamTextResult, streamText };
package/dist/index.js CHANGED
@@ -82,14 +82,16 @@ const transformChunk = () => {
82
82
  });
83
83
  };
84
84
 
85
- const streamText = async (options) => {
85
+ const streamText = (options) => {
86
86
  const steps = [];
87
87
  const messages = structuredClone(options.messages);
88
88
  const maxSteps = options.maxSteps ?? 1;
89
89
  let usage;
90
+ let totalUsage;
90
91
  const resultSteps = new DelayedPromise();
91
92
  const resultMessages = new DelayedPromise();
92
93
  const resultUsage = new DelayedPromise();
94
+ const resultTotalUsage = new DelayedPromise();
93
95
  let eventCtrl;
94
96
  let textCtrl;
95
97
  const eventStream = new ReadableStream({ start: (controller) => eventCtrl = controller });
@@ -102,9 +104,21 @@ const streamText = async (options) => {
102
104
  steps.push(step);
103
105
  void options.onStepFinish?.(step);
104
106
  };
105
- const startStream = async () => {
107
+ const doStream = async () => {
108
+ const { body: stream } = await chat({
109
+ ...options,
110
+ maxSteps: void 0,
111
+ messages,
112
+ stream: true,
113
+ streamOptions: options.streamOptions != null ? objCamelToSnake(options.streamOptions) : void 0
114
+ });
106
115
  const pushUsage = (u) => {
107
116
  usage = u;
117
+ totalUsage = totalUsage ? {
118
+ completion_tokens: totalUsage.completion_tokens + u.completion_tokens,
119
+ prompt_tokens: totalUsage.prompt_tokens + u.prompt_tokens,
120
+ total_tokens: totalUsage.total_tokens + u.total_tokens
121
+ } : { ...u };
108
122
  };
109
123
  let text = "";
110
124
  const pushText = (content) => {
@@ -115,55 +129,47 @@ const streamText = async (options) => {
115
129
  const toolCalls = [];
116
130
  const toolResults = [];
117
131
  let finishReason = "other";
118
- await chat({
119
- ...options,
120
- maxSteps: void 0,
121
- messages,
122
- stream: true,
123
- streamOptions: options.streamOptions != null ? objCamelToSnake(options.streamOptions) : void 0
124
- }).then(
125
- async (res) => res.body.pipeThrough(transformChunk()).pipeTo(new WritableStream({
126
- abort: (reason) => {
127
- eventCtrl?.error(reason);
128
- textCtrl?.error(reason);
129
- },
130
- close: () => {
131
- },
132
- // eslint-disable-next-line sonarjs/cognitive-complexity
133
- write: (chunk) => {
134
- if (chunk.usage)
135
- pushUsage(chunk.usage);
136
- if (chunk.choices == null || chunk.choices.length === 0)
137
- return;
138
- const choice = chunk.choices[0];
139
- if (choice.delta.reasoning_content != null)
140
- pushEvent({ text: choice.delta.reasoning_content, type: "reasoning-delta" });
141
- if (choice.finish_reason != null)
142
- finishReason = choice.finish_reason;
143
- if (choice.delta.tool_calls?.length === 0 || choice.delta.tool_calls == null) {
144
- if (choice.delta.content != null) {
145
- pushEvent({ text: choice.delta.content, type: "text-delta" });
146
- pushText(choice.delta.content);
147
- } else if (choice.delta.refusal != null) {
148
- pushEvent({ error: choice.delta.refusal, type: "error" });
149
- } else if (choice.finish_reason != null) {
150
- pushEvent({ finishReason: choice.finish_reason, type: "finish", usage });
151
- }
152
- } else {
153
- for (const toolCall of choice.delta.tool_calls) {
154
- const { index } = toolCall;
155
- if (!tool_calls.at(index)) {
156
- tool_calls[index] = toolCall;
157
- pushEvent({ toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call-streaming-start" });
158
- } else {
159
- tool_calls[index].function.arguments += toolCall.function.arguments;
160
- pushEvent({ argsTextDelta: toolCall.function.arguments, toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call-delta" });
161
- }
132
+ await stream.pipeThrough(transformChunk()).pipeTo(new WritableStream({
133
+ abort: (reason) => {
134
+ eventCtrl?.error(reason);
135
+ textCtrl?.error(reason);
136
+ },
137
+ close: () => {
138
+ },
139
+ // eslint-disable-next-line sonarjs/cognitive-complexity
140
+ write: (chunk) => {
141
+ if (chunk.usage)
142
+ pushUsage(chunk.usage);
143
+ if (chunk.choices == null || chunk.choices.length === 0)
144
+ return;
145
+ const choice = chunk.choices[0];
146
+ if (choice.delta.reasoning_content != null)
147
+ pushEvent({ text: choice.delta.reasoning_content, type: "reasoning-delta" });
148
+ if (choice.finish_reason != null)
149
+ finishReason = choice.finish_reason;
150
+ if (choice.delta.tool_calls?.length === 0 || choice.delta.tool_calls == null) {
151
+ if (choice.delta.content != null) {
152
+ pushEvent({ text: choice.delta.content, type: "text-delta" });
153
+ pushText(choice.delta.content);
154
+ } else if (choice.delta.refusal != null) {
155
+ pushEvent({ error: choice.delta.refusal, type: "error" });
156
+ } else if (choice.finish_reason != null) {
157
+ pushEvent({ finishReason: choice.finish_reason, type: "finish", usage });
158
+ }
159
+ } else {
160
+ for (const toolCall of choice.delta.tool_calls) {
161
+ const { index } = toolCall;
162
+ if (!tool_calls.at(index)) {
163
+ tool_calls[index] = toolCall;
164
+ pushEvent({ toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call-streaming-start" });
165
+ } else {
166
+ tool_calls[index].function.arguments += toolCall.function.arguments;
167
+ pushEvent({ argsTextDelta: toolCall.function.arguments, toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call-delta" });
162
168
  }
163
169
  }
164
170
  }
165
- }))
166
- );
171
+ }
172
+ }));
167
173
  messages.push({ content: text, role: "assistant", tool_calls });
168
174
  if (tool_calls.length !== 0) {
169
175
  for (const toolCall of tool_calls) {
@@ -195,29 +201,34 @@ const streamText = async (options) => {
195
201
  usage
196
202
  });
197
203
  if (toolCalls.length !== 0 && steps.length < maxSteps)
198
- return async () => startStream();
204
+ return async () => doStream();
199
205
  };
200
- try {
201
- await trampoline(async () => startStream());
202
- eventCtrl?.close();
203
- textCtrl?.close();
204
- } catch (err) {
205
- eventCtrl?.error(err);
206
- textCtrl?.error(err);
207
- resultSteps.reject(err);
208
- resultMessages.reject(err);
209
- resultUsage.reject(err);
210
- } finally {
211
- resultSteps.resolve(steps);
212
- resultMessages.resolve(messages);
213
- resultUsage.resolve(usage);
214
- void options.onFinish?.(steps.at(-1));
215
- }
206
+ void (async () => {
207
+ try {
208
+ await trampoline(async () => doStream());
209
+ eventCtrl?.close();
210
+ textCtrl?.close();
211
+ } catch (err) {
212
+ eventCtrl?.error(err);
213
+ textCtrl?.error(err);
214
+ resultSteps.reject(err);
215
+ resultMessages.reject(err);
216
+ resultUsage.reject(err);
217
+ resultTotalUsage.reject(err);
218
+ } finally {
219
+ resultSteps.resolve(steps);
220
+ resultMessages.resolve(messages);
221
+ resultUsage.resolve(usage);
222
+ resultTotalUsage.resolve(totalUsage);
223
+ void options.onFinish?.(steps.at(-1));
224
+ }
225
+ })();
216
226
  return {
217
227
  fullStream: eventStream,
218
228
  messages: resultMessages.promise,
219
229
  steps: resultSteps.promise,
220
230
  textStream,
231
+ totalUsage: resultTotalUsage.promise,
221
232
  usage: resultUsage.promise
222
233
  };
223
234
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xsai/stream-text",
3
3
  "type": "module",
4
- "version": "0.4.0-beta.1",
4
+ "version": "0.4.0-beta.3",
5
5
  "description": "extra-small AI SDK.",
6
6
  "author": "Moeru AI",
7
7
  "license": "MIT",
@@ -29,12 +29,12 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@xsai/shared-chat": "~0.4.0-beta.1"
32
+ "@xsai/shared-chat": "~0.4.0-beta.3"
33
33
  },
34
34
  "devDependencies": {
35
35
  "valibot": "^1.0.0",
36
- "@xsai/tool": "~0.4.0-beta.1",
37
- "@xsai/shared": "~0.4.0-beta.1"
36
+ "@xsai/shared": "~0.4.0-beta.3",
37
+ "@xsai/tool": "~0.4.0-beta.3"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "pkgroll",