@openrouter/sdk 0.3.11 → 0.3.14
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/esm/index.d.ts +1 -0
- package/esm/index.js +2 -0
- package/esm/lib/anthropic-compat.test.js +3 -0
- package/esm/lib/chat-compat.test.js +3 -0
- package/esm/lib/config.d.ts +2 -2
- package/esm/lib/config.js +2 -2
- package/esm/lib/model-result.d.ts +8 -3
- package/esm/lib/model-result.js +57 -35
- package/esm/lib/tool-event-broadcaster.d.ts +44 -0
- package/esm/lib/tool-event-broadcaster.js +146 -0
- package/esm/lib/tool-executor.d.ts +21 -6
- package/esm/lib/tool-executor.js +57 -8
- package/esm/lib/tool-types.d.ts +18 -18
- package/esm/lib/tool.d.ts +16 -16
- package/esm/models/chatgenerationparams.d.ts +93 -12
- package/esm/models/chatgenerationparams.js +75 -6
- package/esm/models/chatgenerationtokenusage.d.ts +1 -0
- package/esm/models/chatgenerationtokenusage.js +2 -0
- package/esm/models/chatmessagetokenlogprob.d.ts +4 -4
- package/esm/models/chatmessagetokenlogprob.js +4 -5
- package/esm/models/index.d.ts +7 -0
- package/esm/models/index.js +7 -0
- package/esm/models/openairesponsesinputunion.d.ts +15 -5
- package/esm/models/openairesponsesinputunion.js +5 -5
- package/esm/models/openresponseseasyinputmessage.d.ts +41 -16
- package/esm/models/openresponseseasyinputmessage.js +38 -13
- package/esm/models/openresponsesinputmessageitem.d.ts +37 -12
- package/esm/models/openresponsesinputmessageitem.js +33 -9
- package/esm/models/openresponsesnonstreamingresponse.d.ts +5 -2
- package/esm/models/openresponsesnonstreamingresponse.js +8 -2
- package/esm/models/openresponsesreasoning.d.ts +1 -0
- package/esm/models/openresponsesreasoning.js +1 -0
- package/esm/models/openresponsesrequest.d.ts +61 -24
- package/esm/models/openresponsesrequest.js +39 -6
- package/esm/models/operations/getgeneration.d.ts +4 -0
- package/esm/models/operations/getgeneration.js +1 -0
- package/esm/models/percentilelatencycutoffs.d.ts +33 -0
- package/esm/models/percentilelatencycutoffs.js +16 -0
- package/esm/models/percentilestats.d.ts +28 -0
- package/esm/models/percentilestats.js +17 -0
- package/esm/models/percentilethroughputcutoffs.d.ts +33 -0
- package/esm/models/percentilethroughputcutoffs.js +16 -0
- package/esm/models/preferredmaxlatency.d.ts +12 -0
- package/esm/models/preferredmaxlatency.js +12 -0
- package/esm/models/preferredminthroughput.d.ts +12 -0
- package/esm/models/preferredminthroughput.js +12 -0
- package/esm/models/providername.d.ts +3 -2
- package/esm/models/providername.js +3 -2
- package/esm/models/providerpreferences.d.ts +8 -20
- package/esm/models/providerpreferences.js +6 -6
- package/esm/models/publicendpoint.d.ts +6 -0
- package/esm/models/publicendpoint.js +5 -0
- package/esm/models/responseinputimage.d.ts +11 -3
- package/esm/models/responseinputimage.js +9 -2
- package/esm/models/responseinputvideo.d.ts +20 -0
- package/esm/models/responseinputvideo.js +19 -0
- package/esm/models/responseoutputtext.d.ts +38 -0
- package/esm/models/responseoutputtext.js +50 -0
- package/esm/models/responsesoutputitemreasoning.d.ts +30 -1
- package/esm/models/responsesoutputitemreasoning.js +22 -0
- package/esm/models/responsesoutputmodality.d.ts +10 -0
- package/esm/models/responsesoutputmodality.js +12 -0
- package/esm/models/schema0.d.ts +3 -2
- package/esm/models/schema0.js +3 -2
- package/esm/models/schema3.d.ts +1 -0
- package/esm/models/schema3.js +1 -0
- package/jsr.json +1 -1
- package/package.json +6 -7
- package/vitest.config.ts +25 -16
- package/.zed/settings.json +0 -10
package/esm/index.d.ts
CHANGED
|
@@ -17,5 +17,6 @@ export { extractUnsupportedContent, getUnsupportedContentSummary, hasUnsupported
|
|
|
17
17
|
export { tool } from './lib/tool.js';
|
|
18
18
|
export { hasExecuteFunction, isGeneratorTool, isRegularExecuteTool, isToolPreliminaryResultEvent, ToolType, } from './lib/tool-types.js';
|
|
19
19
|
export { buildTurnContext, normalizeInputToArray } from './lib/turn-context.js';
|
|
20
|
+
export { ToolEventBroadcaster } from './lib/tool-event-broadcaster.js';
|
|
20
21
|
export * from './sdk/sdk.js';
|
|
21
22
|
//# sourceMappingURL=index.d.ts.map
|
package/esm/index.js
CHANGED
|
@@ -21,5 +21,7 @@ export { tool } from './lib/tool.js';
|
|
|
21
21
|
export { hasExecuteFunction, isGeneratorTool, isRegularExecuteTool, isToolPreliminaryResultEvent, ToolType, } from './lib/tool-types.js';
|
|
22
22
|
// Turn context helpers
|
|
23
23
|
export { buildTurnContext, normalizeInputToArray } from './lib/turn-context.js';
|
|
24
|
+
// Real-time tool event broadcasting
|
|
25
|
+
export { ToolEventBroadcaster } from './lib/tool-event-broadcaster.js';
|
|
24
26
|
export * from './sdk/sdk.js';
|
|
25
27
|
//# sourceMappingURL=index.js.map
|
|
@@ -9,12 +9,15 @@ function createMockResponse(overrides) {
|
|
|
9
9
|
id: "resp_test",
|
|
10
10
|
object: "response",
|
|
11
11
|
createdAt: Date.now(),
|
|
12
|
+
completedAt: Date.now(),
|
|
12
13
|
model: "openai/gpt-4",
|
|
13
14
|
status: "completed",
|
|
14
15
|
error: null,
|
|
15
16
|
incompleteDetails: null,
|
|
16
17
|
temperature: null,
|
|
17
18
|
topP: null,
|
|
19
|
+
presencePenalty: null,
|
|
20
|
+
frequencyPenalty: null,
|
|
18
21
|
metadata: null,
|
|
19
22
|
tools: [],
|
|
20
23
|
toolChoice: "auto",
|
|
@@ -9,12 +9,15 @@ function createMockResponse(overrides) {
|
|
|
9
9
|
id: "resp_test",
|
|
10
10
|
object: "response",
|
|
11
11
|
createdAt: Date.now(),
|
|
12
|
+
completedAt: Date.now(),
|
|
12
13
|
model: "openai/gpt-4",
|
|
13
14
|
status: "completed",
|
|
14
15
|
error: null,
|
|
15
16
|
incompleteDetails: null,
|
|
16
17
|
temperature: null,
|
|
17
18
|
topP: null,
|
|
19
|
+
presencePenalty: null,
|
|
20
|
+
frequencyPenalty: null,
|
|
18
21
|
metadata: null,
|
|
19
22
|
tools: [],
|
|
20
23
|
toolChoice: "auto",
|
package/esm/lib/config.d.ts
CHANGED
|
@@ -47,8 +47,8 @@ export declare function serverURLFromOptions(options: SDKOptions): URL | null;
|
|
|
47
47
|
export declare const SDK_METADATA: {
|
|
48
48
|
readonly language: "typescript";
|
|
49
49
|
readonly openapiDocVersion: "1.0.0";
|
|
50
|
-
readonly sdkVersion: "0.3.
|
|
50
|
+
readonly sdkVersion: "0.3.14";
|
|
51
51
|
readonly genVersion: "2.788.4";
|
|
52
|
-
readonly userAgent: "speakeasy-sdk/typescript 0.3.
|
|
52
|
+
readonly userAgent: "speakeasy-sdk/typescript 0.3.14 2.788.4 1.0.0 @openrouter/sdk";
|
|
53
53
|
};
|
|
54
54
|
//# sourceMappingURL=config.d.ts.map
|
package/esm/lib/config.js
CHANGED
|
@@ -26,8 +26,8 @@ export function serverURLFromOptions(options) {
|
|
|
26
26
|
export const SDK_METADATA = {
|
|
27
27
|
language: "typescript",
|
|
28
28
|
openapiDocVersion: "1.0.0",
|
|
29
|
-
sdkVersion: "0.3.
|
|
29
|
+
sdkVersion: "0.3.14",
|
|
30
30
|
genVersion: "2.788.4",
|
|
31
|
-
userAgent: "speakeasy-sdk/typescript 0.3.
|
|
31
|
+
userAgent: "speakeasy-sdk/typescript 0.3.14 2.788.4 1.0.0 @openrouter/sdk",
|
|
32
32
|
};
|
|
33
33
|
//# sourceMappingURL=config.js.map
|
|
@@ -37,10 +37,15 @@ export declare class ModelResult<TTools extends readonly Tool[]> {
|
|
|
37
37
|
private initPromise;
|
|
38
38
|
private toolExecutionPromise;
|
|
39
39
|
private finalResponse;
|
|
40
|
-
private
|
|
40
|
+
private toolEventBroadcaster;
|
|
41
41
|
private allToolExecutionRounds;
|
|
42
42
|
private resolvedRequest;
|
|
43
43
|
constructor(options: GetResponseOptions<TTools>);
|
|
44
|
+
/**
|
|
45
|
+
* Get or create the tool event broadcaster (lazy initialization).
|
|
46
|
+
* Ensures only one broadcaster exists for the lifetime of this ModelResult.
|
|
47
|
+
*/
|
|
48
|
+
private ensureBroadcaster;
|
|
44
49
|
/**
|
|
45
50
|
* Type guard to check if a value is a non-streaming response
|
|
46
51
|
*/
|
|
@@ -73,7 +78,7 @@ export declare class ModelResult<TTools extends readonly Tool[]> {
|
|
|
73
78
|
/**
|
|
74
79
|
* Stream all response events as they arrive.
|
|
75
80
|
* Multiple consumers can iterate over this stream concurrently.
|
|
76
|
-
*
|
|
81
|
+
* Preliminary tool results are streamed in REAL-TIME as generator tools yield.
|
|
77
82
|
*/
|
|
78
83
|
getFullResponsesStream(): AsyncIterableIterator<ResponseStreamEvent<InferToolEventsUnion<TTools>>>;
|
|
79
84
|
/**
|
|
@@ -95,7 +100,7 @@ export declare class ModelResult<TTools extends readonly Tool[]> {
|
|
|
95
100
|
getReasoningStream(): AsyncIterableIterator<string>;
|
|
96
101
|
/**
|
|
97
102
|
* Stream tool call argument deltas and preliminary results.
|
|
98
|
-
*
|
|
103
|
+
* Preliminary results are streamed in REAL-TIME as generator tools yield.
|
|
99
104
|
* - Tool call argument deltas as { type: "delta", content: string }
|
|
100
105
|
* - Preliminary results as { type: "preliminary_result", toolCallId, result }
|
|
101
106
|
*/
|
package/esm/lib/model-result.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ToolEventBroadcaster } from './tool-event-broadcaster.js';
|
|
1
2
|
import { betaResponsesSend } from '../funcs/betaResponsesSend.js';
|
|
2
3
|
import { hasAsyncFunctions, resolveAsyncFunctions, } from './async-params.js';
|
|
3
4
|
import { ReusableReadableStream } from './reusable-stream.js';
|
|
@@ -51,12 +52,22 @@ export class ModelResult {
|
|
|
51
52
|
this.initPromise = null;
|
|
52
53
|
this.toolExecutionPromise = null;
|
|
53
54
|
this.finalResponse = null;
|
|
54
|
-
this.
|
|
55
|
+
this.toolEventBroadcaster = null;
|
|
55
56
|
this.allToolExecutionRounds = [];
|
|
56
57
|
// Track resolved request after async function resolution
|
|
57
58
|
this.resolvedRequest = null;
|
|
58
59
|
this.options = options;
|
|
59
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Get or create the tool event broadcaster (lazy initialization).
|
|
63
|
+
* Ensures only one broadcaster exists for the lifetime of this ModelResult.
|
|
64
|
+
*/
|
|
65
|
+
ensureBroadcaster() {
|
|
66
|
+
if (!this.toolEventBroadcaster) {
|
|
67
|
+
this.toolEventBroadcaster = new ToolEventBroadcaster();
|
|
68
|
+
}
|
|
69
|
+
return this.toolEventBroadcaster;
|
|
70
|
+
}
|
|
60
71
|
/**
|
|
61
72
|
* Type guard to check if a value is a non-streaming response
|
|
62
73
|
*/
|
|
@@ -91,8 +102,7 @@ export class ModelResult {
|
|
|
91
102
|
// Already resolved, extract non-function fields
|
|
92
103
|
// Since request is CallModelInput, we need to filter out stopWhen
|
|
93
104
|
// Note: tools are already in API format at this point (converted in callModel())
|
|
94
|
-
|
|
95
|
-
const { stopWhen, ...rest } = this.options.request;
|
|
105
|
+
const { stopWhen: _, ...rest } = this.options.request;
|
|
96
106
|
// Cast to ResolvedCallModelInput - we know it's resolved if hasAsyncFunctions returned false
|
|
97
107
|
baseRequest = rest;
|
|
98
108
|
}
|
|
@@ -215,11 +225,17 @@ export class ModelResult {
|
|
|
215
225
|
if (!tool || !hasExecuteFunction(tool)) {
|
|
216
226
|
continue;
|
|
217
227
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
228
|
+
// Create callback for real-time preliminary results
|
|
229
|
+
const onPreliminaryResult = this.toolEventBroadcaster
|
|
230
|
+
? (callId, resultValue) => {
|
|
231
|
+
this.toolEventBroadcaster?.push({
|
|
232
|
+
type: 'preliminary_result',
|
|
233
|
+
toolCallId: callId,
|
|
234
|
+
result: resultValue,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
: undefined;
|
|
238
|
+
const result = await executeTool(tool, toolCall, turnContext, onPreliminaryResult);
|
|
223
239
|
toolResults.push({
|
|
224
240
|
type: 'function_call_output',
|
|
225
241
|
id: `output_${toolCall.id}`,
|
|
@@ -335,7 +351,7 @@ export class ModelResult {
|
|
|
335
351
|
/**
|
|
336
352
|
* Stream all response events as they arrive.
|
|
337
353
|
* Multiple consumers can iterate over this stream concurrently.
|
|
338
|
-
*
|
|
354
|
+
* Preliminary tool results are streamed in REAL-TIME as generator tools yield.
|
|
339
355
|
*/
|
|
340
356
|
getFullResponsesStream() {
|
|
341
357
|
return async function* () {
|
|
@@ -343,24 +359,29 @@ export class ModelResult {
|
|
|
343
359
|
if (!this.reusableStream) {
|
|
344
360
|
throw new Error('Stream not initialized');
|
|
345
361
|
}
|
|
362
|
+
// Get or create broadcaster for real-time tool events (lazy init prevents race conditions)
|
|
363
|
+
const broadcaster = this.ensureBroadcaster();
|
|
364
|
+
const toolEventConsumer = broadcaster.createConsumer();
|
|
365
|
+
// Start tool execution in background (completes broadcaster when done)
|
|
366
|
+
const executionPromise = this.executeToolsIfNeeded().finally(() => {
|
|
367
|
+
broadcaster.complete();
|
|
368
|
+
});
|
|
346
369
|
const consumer = this.reusableStream.createConsumer();
|
|
347
|
-
// Yield original events
|
|
370
|
+
// Yield original API events
|
|
348
371
|
for await (const event of consumer) {
|
|
349
372
|
yield event;
|
|
350
373
|
}
|
|
351
|
-
//
|
|
352
|
-
await
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
result: result,
|
|
360
|
-
timestamp: Date.now(),
|
|
361
|
-
};
|
|
362
|
-
}
|
|
374
|
+
// Yield tool preliminary results as they arrive (real-time!)
|
|
375
|
+
for await (const event of toolEventConsumer) {
|
|
376
|
+
yield {
|
|
377
|
+
type: 'tool.preliminary_result',
|
|
378
|
+
toolCallId: event.toolCallId,
|
|
379
|
+
result: event.result,
|
|
380
|
+
timestamp: Date.now(),
|
|
381
|
+
};
|
|
363
382
|
}
|
|
383
|
+
// Ensure execution completed (handles errors)
|
|
384
|
+
await executionPromise;
|
|
364
385
|
}.call(this);
|
|
365
386
|
}
|
|
366
387
|
/**
|
|
@@ -423,7 +444,7 @@ export class ModelResult {
|
|
|
423
444
|
}
|
|
424
445
|
/**
|
|
425
446
|
* Stream tool call argument deltas and preliminary results.
|
|
426
|
-
*
|
|
447
|
+
* Preliminary results are streamed in REAL-TIME as generator tools yield.
|
|
427
448
|
* - Tool call argument deltas as { type: "delta", content: string }
|
|
428
449
|
* - Preliminary results as { type: "preliminary_result", toolCallId, result }
|
|
429
450
|
*/
|
|
@@ -433,25 +454,26 @@ export class ModelResult {
|
|
|
433
454
|
if (!this.reusableStream) {
|
|
434
455
|
throw new Error('Stream not initialized');
|
|
435
456
|
}
|
|
436
|
-
//
|
|
457
|
+
// Get or create broadcaster for real-time tool events (lazy init prevents race conditions)
|
|
458
|
+
const broadcaster = this.ensureBroadcaster();
|
|
459
|
+
const toolEventConsumer = broadcaster.createConsumer();
|
|
460
|
+
// Start tool execution in background (completes broadcaster when done)
|
|
461
|
+
const executionPromise = this.executeToolsIfNeeded().finally(() => {
|
|
462
|
+
broadcaster.complete();
|
|
463
|
+
});
|
|
464
|
+
// Yield tool deltas from API stream
|
|
437
465
|
for await (const delta of extractToolDeltas(this.reusableStream)) {
|
|
438
466
|
yield {
|
|
439
467
|
type: 'delta',
|
|
440
468
|
content: delta,
|
|
441
469
|
};
|
|
442
470
|
}
|
|
443
|
-
//
|
|
444
|
-
await
|
|
445
|
-
|
|
446
|
-
for (const [toolCallId, results] of this.preliminaryResults) {
|
|
447
|
-
for (const result of results) {
|
|
448
|
-
yield {
|
|
449
|
-
type: 'preliminary_result',
|
|
450
|
-
toolCallId,
|
|
451
|
-
result: result,
|
|
452
|
-
};
|
|
453
|
-
}
|
|
471
|
+
// Yield tool events as they arrive (real-time!)
|
|
472
|
+
for await (const event of toolEventConsumer) {
|
|
473
|
+
yield event;
|
|
454
474
|
}
|
|
475
|
+
// Ensure execution completed (handles errors)
|
|
476
|
+
await executionPromise;
|
|
455
477
|
}.call(this);
|
|
456
478
|
}
|
|
457
479
|
/**
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A push-based event broadcaster that supports multiple concurrent consumers.
|
|
3
|
+
* Similar to ReusableReadableStream but for push-based events from tool execution.
|
|
4
|
+
*
|
|
5
|
+
* Each consumer gets their own position in the buffer and receives all events
|
|
6
|
+
* from their join point onward. This enables real-time streaming of generator
|
|
7
|
+
* tool preliminary results to multiple consumers simultaneously.
|
|
8
|
+
*
|
|
9
|
+
* @template T - The event type being broadcast
|
|
10
|
+
*/
|
|
11
|
+
export declare class ToolEventBroadcaster<T> {
|
|
12
|
+
private buffer;
|
|
13
|
+
private consumers;
|
|
14
|
+
private nextConsumerId;
|
|
15
|
+
private isComplete;
|
|
16
|
+
private completionError;
|
|
17
|
+
/**
|
|
18
|
+
* Push a new event to all consumers.
|
|
19
|
+
* Events are buffered so late-joining consumers can catch up.
|
|
20
|
+
*/
|
|
21
|
+
push(event: T): void;
|
|
22
|
+
/**
|
|
23
|
+
* Mark the broadcaster as complete - no more events will be pushed.
|
|
24
|
+
* Optionally pass an error to signal failure to all consumers.
|
|
25
|
+
* Cleans up buffer and consumers after completion.
|
|
26
|
+
*/
|
|
27
|
+
complete(error?: Error): void;
|
|
28
|
+
/**
|
|
29
|
+
* Clean up resources after all consumers have finished.
|
|
30
|
+
* Called automatically after complete(), but can be called manually.
|
|
31
|
+
*/
|
|
32
|
+
private cleanup;
|
|
33
|
+
/**
|
|
34
|
+
* Create a new consumer that can independently iterate over events.
|
|
35
|
+
* Consumers can join at any time and will receive events from position 0.
|
|
36
|
+
* Multiple consumers can be created and will all receive the same events.
|
|
37
|
+
*/
|
|
38
|
+
createConsumer(): AsyncIterableIterator<T>;
|
|
39
|
+
/**
|
|
40
|
+
* Notify all waiting consumers that new data is available or stream completed
|
|
41
|
+
*/
|
|
42
|
+
private notifyWaitingConsumers;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=tool-event-broadcaster.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A push-based event broadcaster that supports multiple concurrent consumers.
|
|
3
|
+
* Similar to ReusableReadableStream but for push-based events from tool execution.
|
|
4
|
+
*
|
|
5
|
+
* Each consumer gets their own position in the buffer and receives all events
|
|
6
|
+
* from their join point onward. This enables real-time streaming of generator
|
|
7
|
+
* tool preliminary results to multiple consumers simultaneously.
|
|
8
|
+
*
|
|
9
|
+
* @template T - The event type being broadcast
|
|
10
|
+
*/
|
|
11
|
+
export class ToolEventBroadcaster {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.buffer = [];
|
|
14
|
+
this.consumers = new Map();
|
|
15
|
+
this.nextConsumerId = 0;
|
|
16
|
+
this.isComplete = false;
|
|
17
|
+
this.completionError = null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Push a new event to all consumers.
|
|
21
|
+
* Events are buffered so late-joining consumers can catch up.
|
|
22
|
+
*/
|
|
23
|
+
push(event) {
|
|
24
|
+
if (this.isComplete) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.buffer.push(event);
|
|
28
|
+
this.notifyWaitingConsumers();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Mark the broadcaster as complete - no more events will be pushed.
|
|
32
|
+
* Optionally pass an error to signal failure to all consumers.
|
|
33
|
+
* Cleans up buffer and consumers after completion.
|
|
34
|
+
*/
|
|
35
|
+
complete(error) {
|
|
36
|
+
this.isComplete = true;
|
|
37
|
+
this.completionError = error ?? null;
|
|
38
|
+
this.notifyWaitingConsumers();
|
|
39
|
+
// Schedule cleanup after consumers have processed completion
|
|
40
|
+
queueMicrotask(() => this.cleanup());
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Clean up resources after all consumers have finished.
|
|
44
|
+
* Called automatically after complete(), but can be called manually.
|
|
45
|
+
*/
|
|
46
|
+
cleanup() {
|
|
47
|
+
// Only cleanup if complete and all consumers are done
|
|
48
|
+
if (this.isComplete && this.consumers.size === 0) {
|
|
49
|
+
this.buffer = [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create a new consumer that can independently iterate over events.
|
|
54
|
+
* Consumers can join at any time and will receive events from position 0.
|
|
55
|
+
* Multiple consumers can be created and will all receive the same events.
|
|
56
|
+
*/
|
|
57
|
+
createConsumer() {
|
|
58
|
+
const consumerId = this.nextConsumerId++;
|
|
59
|
+
const state = {
|
|
60
|
+
position: 0,
|
|
61
|
+
waitingPromise: null,
|
|
62
|
+
cancelled: false,
|
|
63
|
+
};
|
|
64
|
+
this.consumers.set(consumerId, state);
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
66
|
+
const self = this;
|
|
67
|
+
return {
|
|
68
|
+
async next() {
|
|
69
|
+
const consumer = self.consumers.get(consumerId);
|
|
70
|
+
if (!consumer) {
|
|
71
|
+
return { done: true, value: undefined };
|
|
72
|
+
}
|
|
73
|
+
if (consumer.cancelled) {
|
|
74
|
+
return { done: true, value: undefined };
|
|
75
|
+
}
|
|
76
|
+
// Return buffered event if available
|
|
77
|
+
if (consumer.position < self.buffer.length) {
|
|
78
|
+
const value = self.buffer[consumer.position];
|
|
79
|
+
consumer.position++;
|
|
80
|
+
return { done: false, value };
|
|
81
|
+
}
|
|
82
|
+
// If complete and caught up, we're done
|
|
83
|
+
if (self.isComplete) {
|
|
84
|
+
self.consumers.delete(consumerId);
|
|
85
|
+
self.cleanup();
|
|
86
|
+
if (self.completionError) {
|
|
87
|
+
throw self.completionError;
|
|
88
|
+
}
|
|
89
|
+
return { done: true, value: undefined };
|
|
90
|
+
}
|
|
91
|
+
// Set up waiting promise FIRST to avoid race condition
|
|
92
|
+
const waitPromise = new Promise((resolve, reject) => {
|
|
93
|
+
consumer.waitingPromise = { resolve, reject };
|
|
94
|
+
// Immediately check if we should resolve after setting up promise
|
|
95
|
+
if (self.isComplete ||
|
|
96
|
+
self.completionError ||
|
|
97
|
+
consumer.position < self.buffer.length) {
|
|
98
|
+
resolve();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
await waitPromise;
|
|
102
|
+
consumer.waitingPromise = null;
|
|
103
|
+
// Recursively try again after waking up
|
|
104
|
+
return this.next();
|
|
105
|
+
},
|
|
106
|
+
async return() {
|
|
107
|
+
const consumer = self.consumers.get(consumerId);
|
|
108
|
+
if (consumer) {
|
|
109
|
+
consumer.cancelled = true;
|
|
110
|
+
self.consumers.delete(consumerId);
|
|
111
|
+
self.cleanup();
|
|
112
|
+
}
|
|
113
|
+
return { done: true, value: undefined };
|
|
114
|
+
},
|
|
115
|
+
async throw(e) {
|
|
116
|
+
const consumer = self.consumers.get(consumerId);
|
|
117
|
+
if (consumer) {
|
|
118
|
+
consumer.cancelled = true;
|
|
119
|
+
self.consumers.delete(consumerId);
|
|
120
|
+
self.cleanup();
|
|
121
|
+
}
|
|
122
|
+
throw e;
|
|
123
|
+
},
|
|
124
|
+
[Symbol.asyncIterator]() {
|
|
125
|
+
return this;
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Notify all waiting consumers that new data is available or stream completed
|
|
131
|
+
*/
|
|
132
|
+
notifyWaitingConsumers() {
|
|
133
|
+
for (const consumer of this.consumers.values()) {
|
|
134
|
+
if (consumer.waitingPromise) {
|
|
135
|
+
if (this.completionError) {
|
|
136
|
+
consumer.waitingPromise.reject(this.completionError);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
consumer.waitingPromise.resolve();
|
|
140
|
+
}
|
|
141
|
+
consumer.waitingPromise = null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=tool-event-broadcaster.js.map
|
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
import type { ZodType } from 'zod';
|
|
1
|
+
import type { $ZodType } from 'zod/v4/core';
|
|
2
2
|
import type { APITool, Tool, ParsedToolCall, ToolExecutionResult, TurnContext } from './tool-types.js';
|
|
3
|
+
import * as z4 from 'zod/v4';
|
|
4
|
+
export declare const ZodError: z4.z.core.$constructor<z4.ZodError<unknown>, z4.z.core.$ZodIssue[]>;
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
+
* Recursively remove keys prefixed with ~ from an object.
|
|
7
|
+
* These are metadata properties (like ~standard from Standard Schema)
|
|
8
|
+
* that should not be sent to downstream providers.
|
|
9
|
+
* @see https://github.com/OpenRouterTeam/typescript-sdk/issues/131
|
|
10
|
+
*
|
|
11
|
+
* When given a Record<string, unknown>, returns Record<string, unknown>.
|
|
12
|
+
* When given unknown, returns unknown (preserves primitives, null, etc).
|
|
6
13
|
*/
|
|
7
|
-
export declare function
|
|
14
|
+
export declare function sanitizeJsonSchema(obj: Record<string, unknown>): Record<string, unknown>;
|
|
15
|
+
export declare function sanitizeJsonSchema(obj: unknown): unknown;
|
|
16
|
+
/**
|
|
17
|
+
* Convert a Zod schema to JSON Schema using Zod v4's toJSONSchema function.
|
|
18
|
+
* Accepts ZodType from the main zod package for user compatibility.
|
|
19
|
+
* The resulting schema is sanitized to remove metadata properties (like ~standard)
|
|
20
|
+
* that would cause 400 errors with downstream providers.
|
|
21
|
+
*/
|
|
22
|
+
export declare function convertZodToJsonSchema(zodSchema: $ZodType): Record<string, unknown>;
|
|
8
23
|
/**
|
|
9
24
|
* Convert tools to OpenRouter API format
|
|
10
25
|
* Accepts readonly arrays for better type compatibility
|
|
@@ -14,12 +29,12 @@ export declare function convertToolsToAPIFormat(tools: readonly Tool[]): APITool
|
|
|
14
29
|
* Validate tool input against Zod schema
|
|
15
30
|
* @throws ZodError if validation fails
|
|
16
31
|
*/
|
|
17
|
-
export declare function validateToolInput<T>(schema: ZodType<T>, args: unknown): T;
|
|
32
|
+
export declare function validateToolInput<T>(schema: $ZodType<T>, args: unknown): T;
|
|
18
33
|
/**
|
|
19
34
|
* Validate tool output against Zod schema
|
|
20
35
|
* @throws ZodError if validation fails
|
|
21
36
|
*/
|
|
22
|
-
export declare function validateToolOutput<T>(schema: ZodType<T>, result: unknown): T;
|
|
37
|
+
export declare function validateToolOutput<T>(schema: $ZodType<T>, result: unknown): T;
|
|
23
38
|
/**
|
|
24
39
|
* Parse tool call arguments from JSON string
|
|
25
40
|
*/
|
package/esm/lib/tool-executor.js
CHANGED
|
@@ -1,15 +1,64 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z4 from 'zod/v4';
|
|
2
2
|
import { hasExecuteFunction, isGeneratorTool, isRegularExecuteTool } from './tool-types.js';
|
|
3
|
+
// Re-export ZodError for convenience
|
|
4
|
+
export const ZodError = z4.ZodError;
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
+
* Typeguard to check if a value is a non-null object (not an array).
|
|
7
|
+
*/
|
|
8
|
+
function isNonNullObject(value) {
|
|
9
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
export function sanitizeJsonSchema(obj) {
|
|
12
|
+
if (obj === null || typeof obj !== 'object') {
|
|
13
|
+
return obj;
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(obj)) {
|
|
16
|
+
return obj.map(sanitizeJsonSchema);
|
|
17
|
+
}
|
|
18
|
+
// At this point, obj is a non-null, non-array object
|
|
19
|
+
// Use typeguard to narrow the type for type-safe property access
|
|
20
|
+
if (!isNonNullObject(obj)) {
|
|
21
|
+
return obj;
|
|
22
|
+
}
|
|
23
|
+
const result = {};
|
|
24
|
+
for (const key of Object.keys(obj)) {
|
|
25
|
+
if (!key.startsWith('~')) {
|
|
26
|
+
result[key] = sanitizeJsonSchema(obj[key]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Typeguard to check if a value is a valid Zod schema compatible with zod/v4.
|
|
33
|
+
* Zod schemas have a _zod property that contains schema metadata.
|
|
34
|
+
*/
|
|
35
|
+
function isZodSchema(value) {
|
|
36
|
+
if (typeof value !== 'object' || value === null) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (!('_zod' in value)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
// After the 'in' check, TypeScript knows value has _zod property
|
|
43
|
+
return typeof value._zod === 'object';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Convert a Zod schema to JSON Schema using Zod v4's toJSONSchema function.
|
|
47
|
+
* Accepts ZodType from the main zod package for user compatibility.
|
|
48
|
+
* The resulting schema is sanitized to remove metadata properties (like ~standard)
|
|
49
|
+
* that would cause 400 errors with downstream providers.
|
|
6
50
|
*/
|
|
7
51
|
export function convertZodToJsonSchema(zodSchema) {
|
|
8
|
-
|
|
9
|
-
|
|
52
|
+
if (!isZodSchema(zodSchema)) {
|
|
53
|
+
throw new Error('Invalid Zod schema provided');
|
|
54
|
+
}
|
|
55
|
+
// Use draft-7 as it's closest to OpenAPI 3.0's JSON Schema variant
|
|
56
|
+
const jsonSchema = z4.toJSONSchema(zodSchema, {
|
|
10
57
|
target: 'draft-7',
|
|
11
58
|
});
|
|
12
|
-
|
|
59
|
+
// jsonSchema is always a Record<string, unknown> from toJSONSchema
|
|
60
|
+
// The overloaded sanitizeJsonSchema preserves this type
|
|
61
|
+
return sanitizeJsonSchema(jsonSchema);
|
|
13
62
|
}
|
|
14
63
|
/**
|
|
15
64
|
* Convert tools to OpenRouter API format
|
|
@@ -29,14 +78,14 @@ export function convertToolsToAPIFormat(tools) {
|
|
|
29
78
|
* @throws ZodError if validation fails
|
|
30
79
|
*/
|
|
31
80
|
export function validateToolInput(schema, args) {
|
|
32
|
-
return
|
|
81
|
+
return z4.parse(schema, args);
|
|
33
82
|
}
|
|
34
83
|
/**
|
|
35
84
|
* Validate tool output against Zod schema
|
|
36
85
|
* @throws ZodError if validation fails
|
|
37
86
|
*/
|
|
38
87
|
export function validateToolOutput(schema, result) {
|
|
39
|
-
return
|
|
88
|
+
return z4.parse(schema, result);
|
|
40
89
|
}
|
|
41
90
|
/**
|
|
42
91
|
* Parse tool call arguments from JSON string
|