@openrouter/sdk 0.1.27 → 0.2.9
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/biome.json +171 -0
- package/esm/funcs/callModel.d.ts +7 -7
- package/esm/funcs/callModel.js +35 -23
- package/esm/funcs/embeddingsGenerate.js +2 -4
- package/esm/funcs/oAuthCreateAuthorizationUrl.d.ts +3 -3
- package/esm/funcs/oAuthCreateAuthorizationUrl.js +30 -14
- package/esm/funcs/oAuthCreateSHA256CodeChallenge.d.ts +2 -2
- package/esm/funcs/oAuthCreateSHA256CodeChallenge.js +13 -12
- package/esm/hooks/registration.d.ts +2 -2
- package/esm/hooks/registration.js +1 -2
- package/esm/lib/config.d.ts +3 -3
- package/esm/lib/config.js +3 -3
- package/esm/lib/encodings.d.ts +1 -0
- package/esm/lib/encodings.js +12 -1
- package/esm/lib/response-wrapper.d.ts +17 -5
- package/esm/lib/response-wrapper.js +119 -68
- package/esm/lib/reusable-stream.js +26 -7
- package/esm/lib/stream-transformers.d.ts +3 -3
- package/esm/lib/stream-transformers.js +43 -41
- package/esm/lib/tool-executor.d.ts +9 -9
- package/esm/lib/tool-executor.js +9 -9
- package/esm/lib/tool-orchestrator.d.ts +3 -3
- package/esm/lib/tool-orchestrator.js +39 -19
- package/esm/lib/tool-types.d.ts +26 -25
- package/esm/lib/tool-types.js +3 -3
- package/esm/models/chatcompletionfinishreason.d.ts +13 -0
- package/esm/models/chatcompletionfinishreason.js +14 -0
- package/esm/models/chatgenerationparams.d.ts +16 -4
- package/esm/models/chatgenerationparams.js +16 -4
- package/esm/models/chatmessagecontentitem.d.ts +2 -19
- package/esm/models/chatmessagecontentitem.js +6 -9
- package/esm/models/chatmessagecontentitemaudio.d.ts +1 -16
- package/esm/models/chatmessagecontentitemaudio.js +2 -16
- package/esm/models/chatmessagecontentitemcachecontrol.d.ts +29 -0
- package/esm/models/chatmessagecontentitemcachecontrol.js +33 -0
- package/esm/models/chatmessagecontentitemtext.d.ts +3 -0
- package/esm/models/chatmessagecontentitemtext.js +12 -0
- package/esm/models/chatmessagecontentitemvideo.d.ts +23 -11
- package/esm/models/chatmessagecontentitemvideo.js +24 -11
- package/esm/models/chatresponsechoice.d.ts +1 -21
- package/esm/models/chatresponsechoice.js +1 -25
- package/esm/models/chatstreamingchoice.d.ts +16 -0
- package/esm/models/chatstreamingchoice.js +24 -0
- package/esm/models/chatstreamingresponsechunk.d.ts +1 -1
- package/esm/models/chatstreamingresponsechunk.js +1 -1
- package/esm/models/completionchoice.d.ts +2 -0
- package/esm/models/completionchoice.js +3 -0
- package/esm/models/completioncreateparams.d.ts +4 -4
- package/esm/models/completioncreateparams.js +4 -4
- package/esm/models/completionresponse.d.ts +1 -0
- package/esm/models/completionresponse.js +1 -0
- package/esm/models/filecitation.d.ts +2 -11
- package/esm/models/filecitation.js +2 -9
- package/esm/models/filepath.d.ts +2 -11
- package/esm/models/filepath.js +2 -10
- package/esm/models/index.d.ts +3 -2
- package/esm/models/index.js +3 -2
- package/esm/models/message.d.ts +2 -2
- package/esm/models/message.js +1 -1
- package/esm/models/openairesponsesannotation.d.ts +2 -2
- package/esm/models/openairesponsesannotation.js +2 -2
- package/esm/models/openairesponsesinputunion.d.ts +5 -45
- package/esm/models/openairesponsesinputunion.js +20 -20
- package/esm/models/openairesponsesrefusalcontent.d.ts +2 -11
- package/esm/models/openairesponsesrefusalcontent.js +2 -9
- package/esm/models/openresponseseasyinputmessage.d.ts +6 -54
- package/esm/models/openresponseseasyinputmessage.js +12 -12
- package/esm/models/openresponseserrorevent.d.ts +1 -8
- package/esm/models/openresponseserrorevent.js +1 -6
- package/esm/models/openresponsesimagegencallcompleted.d.ts +1 -8
- package/esm/models/openresponsesimagegencallcompleted.js +1 -6
- package/esm/models/openresponsesimagegencallgenerating.d.ts +1 -8
- package/esm/models/openresponsesimagegencallgenerating.js +1 -6
- package/esm/models/openresponsesimagegencallinprogress.d.ts +1 -8
- package/esm/models/openresponsesimagegencallinprogress.js +1 -6
- package/esm/models/openresponsesimagegencallpartialimage.d.ts +1 -8
- package/esm/models/openresponsesimagegencallpartialimage.js +1 -6
- package/esm/models/openresponsesinputmessageitem.d.ts +4 -36
- package/esm/models/openresponsesinputmessageitem.js +8 -8
- package/esm/models/openresponsesnonstreamingresponse.d.ts +1 -7
- package/esm/models/openresponsesnonstreamingresponse.js +1 -6
- package/esm/models/openresponsesreasoningdeltaevent.d.ts +1 -8
- package/esm/models/openresponsesreasoningdeltaevent.js +1 -6
- package/esm/models/openresponsesreasoningdoneevent.d.ts +1 -8
- package/esm/models/openresponsesreasoningdoneevent.js +1 -6
- package/esm/models/openresponsesreasoningsummarypartaddedevent.d.ts +1 -8
- package/esm/models/openresponsesreasoningsummarypartaddedevent.js +1 -6
- package/esm/models/openresponsesreasoningsummarytextdeltaevent.d.ts +1 -8
- package/esm/models/openresponsesreasoningsummarytextdeltaevent.js +1 -6
- package/esm/models/openresponsesreasoningsummarytextdoneevent.d.ts +1 -8
- package/esm/models/openresponsesreasoningsummarytextdoneevent.js +1 -6
- package/esm/models/openresponsesrequest.d.ts +26 -44
- package/esm/models/openresponsesrequest.js +15 -32
- package/esm/models/openresponsesstreamevent.d.ts +30 -125
- package/esm/models/openresponsesstreamevent.js +34 -119
- package/esm/models/openresponseswebsearch20250826tool.d.ts +2 -11
- package/esm/models/openresponseswebsearch20250826tool.js +2 -9
- package/esm/models/openresponseswebsearchpreview20250311tool.d.ts +2 -11
- package/esm/models/openresponseswebsearchpreview20250311tool.js +2 -9
- package/esm/models/openresponseswebsearchpreviewtool.d.ts +2 -11
- package/esm/models/openresponseswebsearchpreviewtool.js +2 -9
- package/esm/models/openresponseswebsearchtool.d.ts +2 -11
- package/esm/models/openresponseswebsearchtool.js +2 -9
- package/esm/models/operations/createembeddings.d.ts +5 -21
- package/esm/models/operations/createembeddings.js +3 -22
- package/esm/models/operations/getcredits.d.ts +16 -1
- package/esm/models/operations/getcredits.js +17 -1
- package/esm/models/operations/getparameters.d.ts +5 -1
- package/esm/models/operations/getparameters.js +5 -1
- package/esm/models/providername.d.ts +5 -1
- package/esm/models/providername.js +5 -1
- package/esm/models/responseformattextconfig.d.ts +2 -2
- package/esm/models/responseformattextconfig.js +2 -2
- package/esm/models/responseinputaudio.d.ts +3 -11
- package/esm/models/responseinputaudio.js +2 -9
- package/esm/models/responseinputfile.d.ts +2 -11
- package/esm/models/responseinputfile.js +2 -9
- package/esm/models/responseinputimage.d.ts +3 -11
- package/esm/models/responseinputimage.js +2 -9
- package/esm/models/responseinputtext.d.ts +2 -11
- package/esm/models/responseinputtext.js +2 -9
- package/esm/models/responseoutputtext.d.ts +2 -11
- package/esm/models/responseoutputtext.js +2 -9
- package/esm/models/responsesformatjsonobject.d.ts +2 -11
- package/esm/models/responsesformatjsonobject.js +2 -9
- package/esm/models/responsesformattext.d.ts +2 -11
- package/esm/models/responsesformattext.js +2 -9
- package/esm/models/responsesformattextjsonschemaconfig.d.ts +2 -11
- package/esm/models/responsesformattextjsonschemaconfig.js +2 -9
- package/esm/models/responsesoutputitem.d.ts +13 -1
- package/esm/models/responsesoutputitem.js +6 -6
- package/esm/models/urlcitation.d.ts +2 -11
- package/esm/models/urlcitation.js +2 -9
- package/esm/sdk/oauth.d.ts +2 -2
- package/esm/sdk/oauth.js +2 -3
- package/esm/sdk/sdk.d.ts +9 -8
- package/esm/types/unrecognized.d.ts +7 -1
- package/esm/types/unrecognized.js +9 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/tsconfig.json +0 -1
- package/vitest.config.ts +14 -8
- package/esm/models/chatmessagecontentitemfile.d.ts +0 -36
- package/esm/models/chatmessagecontentitemfile.js +0 -51
- package/esm/models/videourl.d.ts +0 -17
- package/esm/models/videourl.js +0 -21
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { betaResponsesSend } from
|
|
2
|
-
import { ReusableReadableStream } from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { betaResponsesSend } from '../funcs/betaResponsesSend.js';
|
|
2
|
+
import { ReusableReadableStream } from './reusable-stream.js';
|
|
3
|
+
import { buildMessageStream, buildToolCallStream, consumeStreamForCompletion, extractMessageFromResponse, extractReasoningDeltas, extractTextDeltas, extractTextFromResponse, extractToolCallsFromResponse, extractToolDeltas, } from './stream-transformers.js';
|
|
4
|
+
import { executeTool } from './tool-executor.js';
|
|
5
|
+
import { hasExecuteFunction } from './tool-types.js';
|
|
6
6
|
/**
|
|
7
7
|
* A wrapper around a streaming response that provides multiple consumption patterns.
|
|
8
8
|
*
|
|
@@ -29,6 +29,17 @@ export class ResponseWrapper {
|
|
|
29
29
|
this.allToolExecutionRounds = [];
|
|
30
30
|
this.options = options;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Type guard to check if a value is a non-streaming response
|
|
34
|
+
*/
|
|
35
|
+
isNonStreamingResponse(value) {
|
|
36
|
+
return (value !== null &&
|
|
37
|
+
typeof value === "object" &&
|
|
38
|
+
"id" in value &&
|
|
39
|
+
"object" in value &&
|
|
40
|
+
"output" in value &&
|
|
41
|
+
!("toReadableStream" in value));
|
|
42
|
+
}
|
|
32
43
|
/**
|
|
33
44
|
* Initialize the stream if not already started
|
|
34
45
|
* This is idempotent - multiple calls will return the same promise
|
|
@@ -39,7 +50,10 @@ export class ResponseWrapper {
|
|
|
39
50
|
}
|
|
40
51
|
this.initPromise = (async () => {
|
|
41
52
|
// Force stream mode
|
|
42
|
-
const request = {
|
|
53
|
+
const request = {
|
|
54
|
+
...this.options.request,
|
|
55
|
+
stream: true,
|
|
56
|
+
};
|
|
43
57
|
// Create the stream promise
|
|
44
58
|
this.streamPromise = betaResponsesSend(this.options.client, request, this.options.options).then((result) => {
|
|
45
59
|
if (!result.ok) {
|
|
@@ -64,14 +78,14 @@ export class ResponseWrapper {
|
|
|
64
78
|
this.toolExecutionPromise = (async () => {
|
|
65
79
|
await this.initStream();
|
|
66
80
|
if (!this.reusableStream) {
|
|
67
|
-
throw new Error(
|
|
81
|
+
throw new Error('Stream not initialized');
|
|
68
82
|
}
|
|
69
83
|
// Get the initial response
|
|
70
84
|
const initialResponse = await consumeStreamForCompletion(this.reusableStream);
|
|
71
85
|
// Check if we have tools and if auto-execution is enabled
|
|
72
86
|
const shouldAutoExecute = this.options.tools &&
|
|
73
87
|
this.options.tools.length > 0 &&
|
|
74
|
-
initialResponse.output.some((item) =>
|
|
88
|
+
initialResponse.output.some((item) => 'type' in item && item.type === 'function_call');
|
|
75
89
|
if (!shouldAutoExecute) {
|
|
76
90
|
// No tools to execute, use initial response
|
|
77
91
|
this.finalResponse = initialResponse;
|
|
@@ -107,18 +121,22 @@ export class ResponseWrapper {
|
|
|
107
121
|
break;
|
|
108
122
|
}
|
|
109
123
|
// Check if we should continue based on maxToolRounds
|
|
110
|
-
if (typeof maxToolRounds ===
|
|
124
|
+
if (typeof maxToolRounds === 'number') {
|
|
111
125
|
if (currentRound >= maxToolRounds) {
|
|
112
126
|
break;
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
|
-
else if (typeof maxToolRounds ===
|
|
129
|
+
else if (typeof maxToolRounds === 'function') {
|
|
116
130
|
// Function signature: (context: TurnContext) => boolean
|
|
117
131
|
const turnContext = {
|
|
118
132
|
numberOfTurns: currentRound + 1,
|
|
119
133
|
messageHistory: currentInput,
|
|
120
|
-
...(this.options.request.model && {
|
|
121
|
-
|
|
134
|
+
...(this.options.request.model && {
|
|
135
|
+
model: this.options.request.model,
|
|
136
|
+
}),
|
|
137
|
+
...(this.options.request.models && {
|
|
138
|
+
models: this.options.request.models,
|
|
139
|
+
}),
|
|
122
140
|
};
|
|
123
141
|
const shouldContinue = maxToolRounds(turnContext);
|
|
124
142
|
if (!shouldContinue) {
|
|
@@ -135,8 +153,12 @@ export class ResponseWrapper {
|
|
|
135
153
|
const turnContext = {
|
|
136
154
|
numberOfTurns: currentRound + 1, // 1-indexed
|
|
137
155
|
messageHistory: currentInput,
|
|
138
|
-
...(this.options.request.model && {
|
|
139
|
-
|
|
156
|
+
...(this.options.request.model && {
|
|
157
|
+
model: this.options.request.model,
|
|
158
|
+
}),
|
|
159
|
+
...(this.options.request.models && {
|
|
160
|
+
models: this.options.request.models,
|
|
161
|
+
}),
|
|
140
162
|
};
|
|
141
163
|
// Execute all tool calls
|
|
142
164
|
const toolResults = [];
|
|
@@ -151,18 +173,24 @@ export class ResponseWrapper {
|
|
|
151
173
|
this.preliminaryResults.set(toolCall.id, result.preliminaryResults);
|
|
152
174
|
}
|
|
153
175
|
toolResults.push({
|
|
154
|
-
type:
|
|
176
|
+
type: 'function_call_output',
|
|
155
177
|
id: `output_${toolCall.id}`,
|
|
156
178
|
callId: toolCall.id,
|
|
157
179
|
output: result.error
|
|
158
|
-
? JSON.stringify({
|
|
180
|
+
? JSON.stringify({
|
|
181
|
+
error: result.error.message,
|
|
182
|
+
})
|
|
159
183
|
: JSON.stringify(result.result),
|
|
160
184
|
});
|
|
161
185
|
}
|
|
162
186
|
// Build new input with tool results
|
|
163
187
|
// For the Responses API, we need to include the tool results in the input
|
|
164
188
|
const newInput = [
|
|
165
|
-
...(Array.isArray(currentResponse.output)
|
|
189
|
+
...(Array.isArray(currentResponse.output)
|
|
190
|
+
? currentResponse.output
|
|
191
|
+
: [
|
|
192
|
+
currentResponse.output,
|
|
193
|
+
]),
|
|
166
194
|
...toolResults,
|
|
167
195
|
];
|
|
168
196
|
// Update current input for next iteration
|
|
@@ -179,20 +207,51 @@ export class ResponseWrapper {
|
|
|
179
207
|
}
|
|
180
208
|
// Handle the result - it might be a stream or a response
|
|
181
209
|
const value = newResult.value;
|
|
182
|
-
if (value && typeof value ===
|
|
210
|
+
if (value && typeof value === 'object' && 'toReadableStream' in value) {
|
|
183
211
|
// It's a stream, consume it
|
|
184
212
|
const stream = new ReusableReadableStream(value);
|
|
185
213
|
currentResponse = await consumeStreamForCompletion(stream);
|
|
186
214
|
}
|
|
187
|
-
else {
|
|
215
|
+
else if (this.isNonStreamingResponse(value)) {
|
|
188
216
|
currentResponse = value;
|
|
189
217
|
}
|
|
218
|
+
else {
|
|
219
|
+
throw new Error("Unexpected response type from API");
|
|
220
|
+
}
|
|
190
221
|
currentRound++;
|
|
191
222
|
}
|
|
223
|
+
// Validate the final response has required fields
|
|
224
|
+
if (!currentResponse || !currentResponse.id || !currentResponse.output) {
|
|
225
|
+
throw new Error("Invalid final response: missing required fields");
|
|
226
|
+
}
|
|
227
|
+
// Ensure the response is in a completed state (has output content)
|
|
228
|
+
if (!Array.isArray(currentResponse.output) || currentResponse.output.length === 0) {
|
|
229
|
+
throw new Error("Invalid final response: empty or invalid output");
|
|
230
|
+
}
|
|
192
231
|
this.finalResponse = currentResponse;
|
|
193
232
|
})();
|
|
194
233
|
return this.toolExecutionPromise;
|
|
195
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Internal helper to get the message after tool execution
|
|
237
|
+
*/
|
|
238
|
+
async getMessageInternal() {
|
|
239
|
+
await this.executeToolsIfNeeded();
|
|
240
|
+
if (!this.finalResponse) {
|
|
241
|
+
throw new Error("Response not available");
|
|
242
|
+
}
|
|
243
|
+
return extractMessageFromResponse(this.finalResponse);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Internal helper to get the text after tool execution
|
|
247
|
+
*/
|
|
248
|
+
async getTextInternal() {
|
|
249
|
+
await this.executeToolsIfNeeded();
|
|
250
|
+
if (!this.finalResponse) {
|
|
251
|
+
throw new Error("Response not available");
|
|
252
|
+
}
|
|
253
|
+
return extractTextFromResponse(this.finalResponse);
|
|
254
|
+
}
|
|
196
255
|
/**
|
|
197
256
|
* Get the completed message from the response.
|
|
198
257
|
* This will consume the stream until completion, execute any tools, and extract the first message.
|
|
@@ -202,13 +261,7 @@ export class ResponseWrapper {
|
|
|
202
261
|
if (this.messagePromise) {
|
|
203
262
|
return this.messagePromise;
|
|
204
263
|
}
|
|
205
|
-
this.messagePromise = (
|
|
206
|
-
await this.executeToolsIfNeeded();
|
|
207
|
-
if (!this.finalResponse) {
|
|
208
|
-
throw new Error("Response not available");
|
|
209
|
-
}
|
|
210
|
-
return extractMessageFromResponse(this.finalResponse);
|
|
211
|
-
})();
|
|
264
|
+
this.messagePromise = this.getMessageInternal();
|
|
212
265
|
return this.messagePromise;
|
|
213
266
|
}
|
|
214
267
|
/**
|
|
@@ -219,13 +272,7 @@ export class ResponseWrapper {
|
|
|
219
272
|
if (this.textPromise) {
|
|
220
273
|
return this.textPromise;
|
|
221
274
|
}
|
|
222
|
-
this.textPromise = (
|
|
223
|
-
await this.executeToolsIfNeeded();
|
|
224
|
-
if (!this.finalResponse) {
|
|
225
|
-
throw new Error("Response not available");
|
|
226
|
-
}
|
|
227
|
-
return extractTextFromResponse(this.finalResponse);
|
|
228
|
-
})();
|
|
275
|
+
this.textPromise = this.getTextInternal();
|
|
229
276
|
return this.textPromise;
|
|
230
277
|
}
|
|
231
278
|
/**
|
|
@@ -236,7 +283,7 @@ export class ResponseWrapper {
|
|
|
236
283
|
async getResponse() {
|
|
237
284
|
await this.executeToolsIfNeeded();
|
|
238
285
|
if (!this.finalResponse) {
|
|
239
|
-
throw new Error(
|
|
286
|
+
throw new Error('Response not available');
|
|
240
287
|
}
|
|
241
288
|
return this.finalResponse;
|
|
242
289
|
}
|
|
@@ -246,10 +293,10 @@ export class ResponseWrapper {
|
|
|
246
293
|
* Includes preliminary tool result events after tool execution.
|
|
247
294
|
*/
|
|
248
295
|
getFullResponsesStream() {
|
|
249
|
-
return
|
|
296
|
+
return async function* () {
|
|
250
297
|
await this.initStream();
|
|
251
298
|
if (!this.reusableStream) {
|
|
252
|
-
throw new Error(
|
|
299
|
+
throw new Error('Stream not initialized');
|
|
253
300
|
}
|
|
254
301
|
const consumer = this.reusableStream.createConsumer();
|
|
255
302
|
// Yield original events directly
|
|
@@ -262,27 +309,27 @@ export class ResponseWrapper {
|
|
|
262
309
|
for (const [toolCallId, results] of this.preliminaryResults) {
|
|
263
310
|
for (const result of results) {
|
|
264
311
|
yield {
|
|
265
|
-
type:
|
|
312
|
+
type: 'tool.preliminary_result',
|
|
266
313
|
toolCallId,
|
|
267
314
|
result,
|
|
268
315
|
timestamp: Date.now(),
|
|
269
316
|
};
|
|
270
317
|
}
|
|
271
318
|
}
|
|
272
|
-
}.call(this)
|
|
319
|
+
}.call(this);
|
|
273
320
|
}
|
|
274
321
|
/**
|
|
275
322
|
* Stream only text deltas as they arrive.
|
|
276
323
|
* This filters the full event stream to only yield text content.
|
|
277
324
|
*/
|
|
278
325
|
getTextStream() {
|
|
279
|
-
return
|
|
326
|
+
return async function* () {
|
|
280
327
|
await this.initStream();
|
|
281
328
|
if (!this.reusableStream) {
|
|
282
|
-
throw new Error(
|
|
329
|
+
throw new Error('Stream not initialized');
|
|
283
330
|
}
|
|
284
331
|
yield* extractTextDeltas(this.reusableStream);
|
|
285
|
-
}.call(this)
|
|
332
|
+
}.call(this);
|
|
286
333
|
}
|
|
287
334
|
/**
|
|
288
335
|
* Stream incremental message updates as content is added.
|
|
@@ -291,10 +338,10 @@ export class ResponseWrapper {
|
|
|
291
338
|
* Returns AssistantMessage or ToolResponseMessage in chat format.
|
|
292
339
|
*/
|
|
293
340
|
getNewMessagesStream() {
|
|
294
|
-
return
|
|
341
|
+
return async function* () {
|
|
295
342
|
await this.initStream();
|
|
296
343
|
if (!this.reusableStream) {
|
|
297
|
-
throw new Error(
|
|
344
|
+
throw new Error('Stream not initialized');
|
|
298
345
|
}
|
|
299
346
|
// First yield assistant messages from the stream
|
|
300
347
|
yield* buildMessageStream(this.reusableStream);
|
|
@@ -315,8 +362,8 @@ export class ResponseWrapper {
|
|
|
315
362
|
: undefined;
|
|
316
363
|
// Yield tool response message
|
|
317
364
|
yield {
|
|
318
|
-
role:
|
|
319
|
-
content: result !== undefined ? JSON.stringify(result) :
|
|
365
|
+
role: 'tool',
|
|
366
|
+
content: result !== undefined ? JSON.stringify(result) : '',
|
|
320
367
|
toolCallId: toolCall.id,
|
|
321
368
|
};
|
|
322
369
|
}
|
|
@@ -324,25 +371,25 @@ export class ResponseWrapper {
|
|
|
324
371
|
// If tools were executed, yield the final assistant message (if there is one)
|
|
325
372
|
if (this.finalResponse && this.allToolExecutionRounds.length > 0) {
|
|
326
373
|
// Check if the final response contains a message
|
|
327
|
-
const hasMessage = this.finalResponse.output.some((item) =>
|
|
374
|
+
const hasMessage = this.finalResponse.output.some((item) => 'type' in item && item.type === 'message');
|
|
328
375
|
if (hasMessage) {
|
|
329
376
|
yield extractMessageFromResponse(this.finalResponse);
|
|
330
377
|
}
|
|
331
378
|
}
|
|
332
|
-
}.call(this)
|
|
379
|
+
}.call(this);
|
|
333
380
|
}
|
|
334
381
|
/**
|
|
335
382
|
* Stream only reasoning deltas as they arrive.
|
|
336
383
|
* This filters the full event stream to only yield reasoning content.
|
|
337
384
|
*/
|
|
338
385
|
getReasoningStream() {
|
|
339
|
-
return
|
|
386
|
+
return async function* () {
|
|
340
387
|
await this.initStream();
|
|
341
388
|
if (!this.reusableStream) {
|
|
342
|
-
throw new Error(
|
|
389
|
+
throw new Error('Stream not initialized');
|
|
343
390
|
}
|
|
344
391
|
yield* extractReasoningDeltas(this.reusableStream);
|
|
345
|
-
}.call(this)
|
|
392
|
+
}.call(this);
|
|
346
393
|
}
|
|
347
394
|
/**
|
|
348
395
|
* Stream tool call argument deltas and preliminary results.
|
|
@@ -351,14 +398,17 @@ export class ResponseWrapper {
|
|
|
351
398
|
* - Preliminary results as { type: "preliminary_result", toolCallId, result }
|
|
352
399
|
*/
|
|
353
400
|
getToolStream() {
|
|
354
|
-
return
|
|
401
|
+
return async function* () {
|
|
355
402
|
await this.initStream();
|
|
356
403
|
if (!this.reusableStream) {
|
|
357
|
-
throw new Error(
|
|
404
|
+
throw new Error('Stream not initialized');
|
|
358
405
|
}
|
|
359
406
|
// Yield tool deltas as structured events
|
|
360
407
|
for await (const delta of extractToolDeltas(this.reusableStream)) {
|
|
361
|
-
yield {
|
|
408
|
+
yield {
|
|
409
|
+
type: 'delta',
|
|
410
|
+
content: delta,
|
|
411
|
+
};
|
|
362
412
|
}
|
|
363
413
|
// After stream completes, check if tools were executed and emit preliminary results
|
|
364
414
|
await this.executeToolsIfNeeded();
|
|
@@ -366,13 +416,13 @@ export class ResponseWrapper {
|
|
|
366
416
|
for (const [toolCallId, results] of this.preliminaryResults) {
|
|
367
417
|
for (const result of results) {
|
|
368
418
|
yield {
|
|
369
|
-
type:
|
|
419
|
+
type: 'preliminary_result',
|
|
370
420
|
toolCallId,
|
|
371
421
|
result,
|
|
372
422
|
};
|
|
373
423
|
}
|
|
374
424
|
}
|
|
375
|
-
}.call(this)
|
|
425
|
+
}.call(this);
|
|
376
426
|
}
|
|
377
427
|
/**
|
|
378
428
|
* Stream events in chat format (compatibility layer).
|
|
@@ -385,28 +435,29 @@ export class ResponseWrapper {
|
|
|
385
435
|
* this may not be a perfect mapping.
|
|
386
436
|
*/
|
|
387
437
|
getFullChatStream() {
|
|
388
|
-
return
|
|
438
|
+
return async function* () {
|
|
389
439
|
await this.initStream();
|
|
390
440
|
if (!this.reusableStream) {
|
|
391
|
-
throw new Error(
|
|
441
|
+
throw new Error('Stream not initialized');
|
|
392
442
|
}
|
|
393
443
|
const consumer = this.reusableStream.createConsumer();
|
|
394
444
|
for await (const event of consumer) {
|
|
395
|
-
if (!(
|
|
445
|
+
if (!('type' in event)) {
|
|
396
446
|
continue;
|
|
447
|
+
}
|
|
397
448
|
// Transform responses events to chat-like format
|
|
398
449
|
// This is a simplified transformation - you may need to adjust based on your needs
|
|
399
|
-
if (event.type ===
|
|
450
|
+
if (event.type === 'response.output_text.delta') {
|
|
400
451
|
const deltaEvent = event;
|
|
401
452
|
yield {
|
|
402
|
-
type:
|
|
453
|
+
type: 'content.delta',
|
|
403
454
|
delta: deltaEvent.delta,
|
|
404
455
|
};
|
|
405
456
|
}
|
|
406
|
-
else if (event.type ===
|
|
457
|
+
else if (event.type === 'response.completed') {
|
|
407
458
|
const completedEvent = event;
|
|
408
459
|
yield {
|
|
409
|
-
type:
|
|
460
|
+
type: 'message.complete',
|
|
410
461
|
response: completedEvent.response,
|
|
411
462
|
};
|
|
412
463
|
}
|
|
@@ -424,13 +475,13 @@ export class ResponseWrapper {
|
|
|
424
475
|
for (const [toolCallId, results] of this.preliminaryResults) {
|
|
425
476
|
for (const result of results) {
|
|
426
477
|
yield {
|
|
427
|
-
type:
|
|
478
|
+
type: 'tool.preliminary_result',
|
|
428
479
|
toolCallId,
|
|
429
480
|
result,
|
|
430
481
|
};
|
|
431
482
|
}
|
|
432
483
|
}
|
|
433
|
-
}.call(this)
|
|
484
|
+
}.call(this);
|
|
434
485
|
}
|
|
435
486
|
/**
|
|
436
487
|
* Get all tool calls from the completed response (before auto-execution).
|
|
@@ -441,7 +492,7 @@ export class ResponseWrapper {
|
|
|
441
492
|
async getToolCalls() {
|
|
442
493
|
await this.initStream();
|
|
443
494
|
if (!this.reusableStream) {
|
|
444
|
-
throw new Error(
|
|
495
|
+
throw new Error('Stream not initialized');
|
|
445
496
|
}
|
|
446
497
|
const completedResponse = await consumeStreamForCompletion(this.reusableStream);
|
|
447
498
|
return extractToolCallsFromResponse(completedResponse);
|
|
@@ -451,13 +502,13 @@ export class ResponseWrapper {
|
|
|
451
502
|
* Each iteration yields a complete tool call with parsed arguments.
|
|
452
503
|
*/
|
|
453
504
|
getToolCallsStream() {
|
|
454
|
-
return
|
|
505
|
+
return async function* () {
|
|
455
506
|
await this.initStream();
|
|
456
507
|
if (!this.reusableStream) {
|
|
457
|
-
throw new Error(
|
|
508
|
+
throw new Error('Stream not initialized');
|
|
458
509
|
}
|
|
459
510
|
yield* buildToolCallStream(this.reusableStream);
|
|
460
|
-
}.call(this)
|
|
511
|
+
}.call(this);
|
|
461
512
|
}
|
|
462
513
|
/**
|
|
463
514
|
* Cancel the underlying stream and all consumers
|
|
@@ -41,22 +41,34 @@ export class ReusableReadableStream {
|
|
|
41
41
|
async next() {
|
|
42
42
|
const consumer = self.consumers.get(consumerId);
|
|
43
43
|
if (!consumer) {
|
|
44
|
-
return {
|
|
44
|
+
return {
|
|
45
|
+
done: true,
|
|
46
|
+
value: undefined,
|
|
47
|
+
};
|
|
45
48
|
}
|
|
46
49
|
if (consumer.cancelled) {
|
|
47
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
done: true,
|
|
52
|
+
value: undefined,
|
|
53
|
+
};
|
|
48
54
|
}
|
|
49
55
|
// If we have buffered data at this position, return it
|
|
50
56
|
if (consumer.position < self.buffer.length) {
|
|
51
57
|
const value = self.buffer[consumer.position];
|
|
52
58
|
consumer.position++;
|
|
53
59
|
// Note: We don't clean up buffer to allow sequential/reusable access
|
|
54
|
-
return {
|
|
60
|
+
return {
|
|
61
|
+
done: false,
|
|
62
|
+
value,
|
|
63
|
+
};
|
|
55
64
|
}
|
|
56
65
|
// If source is complete and we've read everything, we're done
|
|
57
66
|
if (self.sourceComplete) {
|
|
58
67
|
self.consumers.delete(consumerId);
|
|
59
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
done: true,
|
|
70
|
+
value: undefined,
|
|
71
|
+
};
|
|
60
72
|
}
|
|
61
73
|
// If source had an error, propagate it
|
|
62
74
|
if (self.sourceError) {
|
|
@@ -66,7 +78,10 @@ export class ReusableReadableStream {
|
|
|
66
78
|
// Wait for more data - but check conditions after setting up the promise
|
|
67
79
|
// to avoid race condition where source completes between check and wait
|
|
68
80
|
const waitPromise = new Promise((resolve, reject) => {
|
|
69
|
-
consumer.waitingPromise = {
|
|
81
|
+
consumer.waitingPromise = {
|
|
82
|
+
resolve,
|
|
83
|
+
reject,
|
|
84
|
+
};
|
|
70
85
|
});
|
|
71
86
|
// Double-check conditions after setting up promise to handle race
|
|
72
87
|
if (self.sourceComplete || self.sourceError || consumer.position < self.buffer.length) {
|
|
@@ -86,7 +101,10 @@ export class ReusableReadableStream {
|
|
|
86
101
|
consumer.cancelled = true;
|
|
87
102
|
self.consumers.delete(consumerId);
|
|
88
103
|
}
|
|
89
|
-
return {
|
|
104
|
+
return {
|
|
105
|
+
done: true,
|
|
106
|
+
value: undefined,
|
|
107
|
+
};
|
|
90
108
|
},
|
|
91
109
|
async throw(e) {
|
|
92
110
|
const consumer = self.consumers.get(consumerId);
|
|
@@ -105,8 +123,9 @@ export class ReusableReadableStream {
|
|
|
105
123
|
* Start pumping data from the source stream into the buffer
|
|
106
124
|
*/
|
|
107
125
|
startPump() {
|
|
108
|
-
if (this.pumpStarted)
|
|
126
|
+
if (this.pumpStarted) {
|
|
109
127
|
return;
|
|
128
|
+
}
|
|
110
129
|
this.pumpStarted = true;
|
|
111
130
|
this.sourceReader = this.sourceStream.getReader();
|
|
112
131
|
void (async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as models from
|
|
2
|
-
import { ReusableReadableStream } from
|
|
3
|
-
import { ParsedToolCall } from
|
|
1
|
+
import type * as models from '../models/index.js';
|
|
2
|
+
import type { ReusableReadableStream } from './reusable-stream.js';
|
|
3
|
+
import type { ParsedToolCall } from './tool-types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Extract text deltas from responses stream events
|
|
6
6
|
*/
|