@openrouter/sdk 0.3.7 → 0.3.10
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/.zed/settings.json +10 -0
- package/_speakeasy/.github/action-inputs-config.json +53 -0
- package/_speakeasy/.github/action-security-config.json +88 -0
- package/esm/funcs/call-model.d.ts +94 -9
- package/esm/funcs/call-model.js +102 -120
- package/esm/index.d.ts +20 -8
- package/esm/index.js +20 -7
- package/esm/lib/anthropic-compat.d.ts +6 -2
- package/esm/lib/anthropic-compat.js +117 -98
- package/esm/lib/async-params.d.ts +53 -0
- package/esm/lib/async-params.js +76 -0
- package/esm/lib/chat-compat.js +4 -0
- package/esm/lib/claude-constants.d.ts +22 -0
- package/esm/lib/claude-constants.js +20 -0
- package/esm/lib/claude-type-guards.d.ts +10 -0
- package/esm/lib/claude-type-guards.js +70 -0
- package/esm/lib/config.d.ts +2 -2
- package/esm/lib/config.js +2 -2
- package/esm/lib/model-result.d.ts +18 -25
- package/esm/lib/model-result.js +137 -176
- package/esm/lib/next-turn-params.d.ts +30 -0
- package/esm/lib/next-turn-params.js +129 -0
- package/esm/lib/reusable-stream.js +10 -10
- package/esm/lib/stop-conditions.d.ts +80 -0
- package/esm/lib/stop-conditions.js +104 -0
- package/esm/lib/stream-transformers.d.ts +3 -3
- package/esm/lib/stream-transformers.js +311 -260
- package/esm/lib/stream-type-guards.d.ts +29 -0
- package/esm/lib/stream-type-guards.js +109 -0
- package/esm/lib/tool-executor.d.ts +7 -6
- package/esm/lib/tool-executor.js +4 -0
- package/esm/lib/tool-orchestrator.d.ts +7 -7
- package/esm/lib/tool-orchestrator.js +38 -10
- package/esm/lib/tool-types.d.ts +162 -28
- package/esm/lib/tool-types.js +6 -0
- package/esm/lib/tool.d.ts +99 -0
- package/esm/lib/tool.js +71 -0
- package/esm/lib/turn-context.d.ts +50 -0
- package/esm/lib/turn-context.js +59 -0
- package/esm/sdk/sdk.d.ts +3 -9
- package/jsr.json +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard to check if a value is a Record<string, unknown>
|
|
3
|
+
*/
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Build a NextTurnParamsContext from the current request
|
|
9
|
+
* Extracts relevant fields that can be modified by nextTurnParams functions
|
|
10
|
+
*
|
|
11
|
+
* @param request - The current OpenResponsesRequest
|
|
12
|
+
* @returns Context object with current parameter values
|
|
13
|
+
*/
|
|
14
|
+
export function buildNextTurnParamsContext(request) {
|
|
15
|
+
return {
|
|
16
|
+
input: request.input ?? [],
|
|
17
|
+
model: request.model ?? '',
|
|
18
|
+
models: request.models ?? [],
|
|
19
|
+
temperature: request.temperature ?? null,
|
|
20
|
+
maxOutputTokens: request.maxOutputTokens ?? null,
|
|
21
|
+
topP: request.topP ?? null,
|
|
22
|
+
topK: request.topK,
|
|
23
|
+
instructions: request.instructions ?? null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Execute nextTurnParams functions for all called tools
|
|
28
|
+
* Composes functions when multiple tools modify the same parameter
|
|
29
|
+
*
|
|
30
|
+
* @param toolCalls - Tool calls that were executed in this turn
|
|
31
|
+
* @param tools - All available tools
|
|
32
|
+
* @param currentRequest - The current request
|
|
33
|
+
* @returns Object with computed parameter values
|
|
34
|
+
*/
|
|
35
|
+
export async function executeNextTurnParamsFunctions(toolCalls, tools, currentRequest) {
|
|
36
|
+
// Build initial context from current request
|
|
37
|
+
const context = buildNextTurnParamsContext(currentRequest);
|
|
38
|
+
// Collect all nextTurnParams functions from tools (in tools array order)
|
|
39
|
+
const result = {};
|
|
40
|
+
const workingContext = { ...context };
|
|
41
|
+
for (const tool of tools) {
|
|
42
|
+
if (!tool.function.nextTurnParams) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Find tool calls for this tool
|
|
46
|
+
const callsForTool = toolCalls.filter(tc => tc.name === tool.function.name);
|
|
47
|
+
for (const call of callsForTool) {
|
|
48
|
+
// For each parameter function in this tool's nextTurnParams
|
|
49
|
+
// We need to process each key individually to maintain type safety
|
|
50
|
+
const nextParams = tool.function.nextTurnParams;
|
|
51
|
+
// Validate that call.arguments is a record using type guard
|
|
52
|
+
if (!isRecord(call.arguments)) {
|
|
53
|
+
const typeStr = Array.isArray(call.arguments)
|
|
54
|
+
? 'array'
|
|
55
|
+
: typeof call.arguments;
|
|
56
|
+
throw new Error(`Tool call arguments for ${tool.function.name} must be an object, got ${typeStr}`);
|
|
57
|
+
}
|
|
58
|
+
// Process each parameter key with proper typing
|
|
59
|
+
await processNextTurnParamsForCall(nextParams, call.arguments, workingContext, result, tool.function.name);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Process nextTurnParams for a single tool call with full type safety
|
|
66
|
+
*/
|
|
67
|
+
async function processNextTurnParamsForCall(nextParams, params, workingContext, result, toolName) {
|
|
68
|
+
// Type-safe processing for each known parameter key
|
|
69
|
+
// We iterate through keys and use runtime checks instead of casts
|
|
70
|
+
for (const paramKey of Object.keys(nextParams)) {
|
|
71
|
+
const fn = nextParams[paramKey];
|
|
72
|
+
if (typeof fn !== 'function') {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// Validate that paramKey is actually a key of NextTurnParamsContext
|
|
76
|
+
if (!isValidNextTurnParamKey(paramKey)) {
|
|
77
|
+
if (process.env['NODE_ENV'] !== 'production') {
|
|
78
|
+
console.warn(`Invalid nextTurnParams key "${paramKey}" in tool "${toolName}". ` +
|
|
79
|
+
`Valid keys: input, model, models, temperature, maxOutputTokens, topP, topK, instructions`);
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// Execute the function and await the result
|
|
84
|
+
const newValue = await Promise.resolve(fn(params, workingContext));
|
|
85
|
+
// Update both result and workingContext to enable composition
|
|
86
|
+
// Later tools will see modifications made by earlier tools
|
|
87
|
+
setNextTurnParam(result, paramKey, newValue);
|
|
88
|
+
setNextTurnParam(workingContext, paramKey, newValue);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Type guard to check if a string is a valid NextTurnParamsContext key
|
|
93
|
+
*/
|
|
94
|
+
function isValidNextTurnParamKey(key) {
|
|
95
|
+
const validKeys = new Set([
|
|
96
|
+
'input',
|
|
97
|
+
'model',
|
|
98
|
+
'models',
|
|
99
|
+
'temperature',
|
|
100
|
+
'maxOutputTokens',
|
|
101
|
+
'topP',
|
|
102
|
+
'topK',
|
|
103
|
+
'instructions',
|
|
104
|
+
]);
|
|
105
|
+
return validKeys.has(key);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Type-safe setter for NextTurnParamsContext
|
|
109
|
+
* This wrapper is needed because TypeScript doesn't properly narrow the type
|
|
110
|
+
* after the type guard, even though we've validated the key
|
|
111
|
+
*/
|
|
112
|
+
function setNextTurnParam(target, key, value) {
|
|
113
|
+
target[key] = value;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Apply computed nextTurnParams to the current request
|
|
117
|
+
* Returns a new request object with updated parameters
|
|
118
|
+
*
|
|
119
|
+
* @param request - The current request
|
|
120
|
+
* @param computedParams - Computed parameter values from nextTurnParams functions
|
|
121
|
+
* @returns New request with updated parameters
|
|
122
|
+
*/
|
|
123
|
+
export function applyNextTurnParamsToRequest(request, computedParams) {
|
|
124
|
+
return {
|
|
125
|
+
...request,
|
|
126
|
+
...computedParams,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=next-turn-params.js.map
|
|
@@ -75,23 +75,23 @@ export class ReusableReadableStream {
|
|
|
75
75
|
self.consumers.delete(consumerId);
|
|
76
76
|
throw self.sourceError;
|
|
77
77
|
}
|
|
78
|
-
//
|
|
79
|
-
//
|
|
78
|
+
// Set up the waiting promise FIRST to avoid race condition
|
|
79
|
+
// where source completes after the check but before promise is set
|
|
80
80
|
const waitPromise = new Promise((resolve, reject) => {
|
|
81
81
|
consumer.waitingPromise = {
|
|
82
82
|
resolve,
|
|
83
83
|
reject,
|
|
84
84
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
consumer.waitingPromise.resolve();
|
|
91
|
-
consumer.waitingPromise = null;
|
|
85
|
+
// Immediately check if we should resolve after setting up the promise
|
|
86
|
+
// This handles the case where data arrived or source completed
|
|
87
|
+
// between our initial checks and promise creation
|
|
88
|
+
if (self.sourceComplete || self.sourceError || consumer.position < self.buffer.length) {
|
|
89
|
+
resolve();
|
|
92
90
|
}
|
|
93
|
-
}
|
|
91
|
+
});
|
|
94
92
|
await waitPromise;
|
|
93
|
+
// Clear the promise reference after it resolves
|
|
94
|
+
consumer.waitingPromise = null;
|
|
95
95
|
// Recursively try again after waking up
|
|
96
96
|
return this.next();
|
|
97
97
|
},
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { StepResult, StopCondition, Tool } from './tool-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Stop condition that checks if step count equals or exceeds a specific number
|
|
4
|
+
* @param stepCount - The number of steps to allow before stopping
|
|
5
|
+
* @returns StopCondition that returns true when steps.length >= stepCount
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* stopWhen: stepCountIs(5) // Stop after 5 steps
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare function stepCountIs(stepCount: number): StopCondition;
|
|
13
|
+
/**
|
|
14
|
+
* Stop condition that checks if any step contains a tool call with the given name
|
|
15
|
+
* @param toolName - The name of the tool to check for
|
|
16
|
+
* @returns StopCondition that returns true if the tool was called in any step
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* stopWhen: hasToolCall('search') // Stop when search tool is called
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function hasToolCall(toolName: string): StopCondition;
|
|
24
|
+
/**
|
|
25
|
+
* Evaluates an array of stop conditions
|
|
26
|
+
* Returns true if ANY condition returns true (OR logic)
|
|
27
|
+
* @param options - Object containing stopConditions and steps
|
|
28
|
+
* @returns Promise<boolean> indicating if execution should stop
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const shouldStop = await isStopConditionMet({
|
|
33
|
+
* stopConditions: [stepCountIs(5), hasToolCall('search')],
|
|
34
|
+
* steps: allSteps
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function isStopConditionMet<TTools extends readonly Tool[]>(options: {
|
|
39
|
+
readonly stopConditions: ReadonlyArray<StopCondition<TTools>>;
|
|
40
|
+
readonly steps: ReadonlyArray<StepResult<TTools>>;
|
|
41
|
+
}): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Stop when total token usage exceeds a threshold
|
|
44
|
+
* OpenRouter-specific helper using usage data
|
|
45
|
+
*
|
|
46
|
+
* @param maxTokens - Maximum total tokens to allow
|
|
47
|
+
* @returns StopCondition that returns true when token usage exceeds threshold
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* stopWhen: maxTokensUsed(10000) // Stop when total tokens exceed 10,000
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function maxTokensUsed(maxTokens: number): StopCondition;
|
|
55
|
+
/**
|
|
56
|
+
* Stop when total cost exceeds a threshold
|
|
57
|
+
* OpenRouter-specific helper using cost data
|
|
58
|
+
*
|
|
59
|
+
* @param maxCostInDollars - Maximum cost in dollars to allow
|
|
60
|
+
* @returns StopCondition that returns true when cost exceeds threshold
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* stopWhen: maxCost(0.50) // Stop when total cost exceeds $0.50
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function maxCost(maxCostInDollars: number): StopCondition;
|
|
68
|
+
/**
|
|
69
|
+
* Stop when a specific finish reason is encountered
|
|
70
|
+
*
|
|
71
|
+
* @param reason - The finish reason to check for
|
|
72
|
+
* @returns StopCondition that returns true when finish reason matches
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* stopWhen: finishReasonIs('length') // Stop when context length limit is hit
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare function finishReasonIs(reason: string): StopCondition;
|
|
80
|
+
//# sourceMappingURL=stop-conditions.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop condition that checks if step count equals or exceeds a specific number
|
|
3
|
+
* @param stepCount - The number of steps to allow before stopping
|
|
4
|
+
* @returns StopCondition that returns true when steps.length >= stepCount
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* stopWhen: stepCountIs(5) // Stop after 5 steps
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export function stepCountIs(stepCount) {
|
|
12
|
+
return ({ steps }) => steps.length >= stepCount;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Stop condition that checks if any step contains a tool call with the given name
|
|
16
|
+
* @param toolName - The name of the tool to check for
|
|
17
|
+
* @returns StopCondition that returns true if the tool was called in any step
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* stopWhen: hasToolCall('search') // Stop when search tool is called
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function hasToolCall(toolName) {
|
|
25
|
+
return ({ steps }) => {
|
|
26
|
+
return steps.some((step) => step.toolCalls.some((call) => call.name === toolName));
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Evaluates an array of stop conditions
|
|
31
|
+
* Returns true if ANY condition returns true (OR logic)
|
|
32
|
+
* @param options - Object containing stopConditions and steps
|
|
33
|
+
* @returns Promise<boolean> indicating if execution should stop
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const shouldStop = await isStopConditionMet({
|
|
38
|
+
* stopConditions: [stepCountIs(5), hasToolCall('search')],
|
|
39
|
+
* steps: allSteps
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export async function isStopConditionMet(options) {
|
|
44
|
+
const { stopConditions, steps } = options;
|
|
45
|
+
// Evaluate all conditions in parallel
|
|
46
|
+
const results = await Promise.all(stopConditions.map((condition) => Promise.resolve(condition({
|
|
47
|
+
steps,
|
|
48
|
+
}))));
|
|
49
|
+
// Return true if ANY condition is true (OR logic)
|
|
50
|
+
return results.some((result) => result === true);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Stop when total token usage exceeds a threshold
|
|
54
|
+
* OpenRouter-specific helper using usage data
|
|
55
|
+
*
|
|
56
|
+
* @param maxTokens - Maximum total tokens to allow
|
|
57
|
+
* @returns StopCondition that returns true when token usage exceeds threshold
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* stopWhen: maxTokensUsed(10000) // Stop when total tokens exceed 10,000
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export function maxTokensUsed(maxTokens) {
|
|
65
|
+
return ({ steps }) => {
|
|
66
|
+
const totalTokens = steps.reduce((sum, step) => sum + (step.usage?.totalTokens ?? 0), 0);
|
|
67
|
+
return totalTokens >= maxTokens;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Stop when total cost exceeds a threshold
|
|
72
|
+
* OpenRouter-specific helper using cost data
|
|
73
|
+
*
|
|
74
|
+
* @param maxCostInDollars - Maximum cost in dollars to allow
|
|
75
|
+
* @returns StopCondition that returns true when cost exceeds threshold
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* stopWhen: maxCost(0.50) // Stop when total cost exceeds $0.50
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export function maxCost(maxCostInDollars) {
|
|
83
|
+
return ({ steps }) => {
|
|
84
|
+
const totalCost = steps.reduce((sum, step) => sum + (step.usage?.cost ?? 0), 0);
|
|
85
|
+
return totalCost >= maxCostInDollars;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Stop when a specific finish reason is encountered
|
|
90
|
+
*
|
|
91
|
+
* @param reason - The finish reason to check for
|
|
92
|
+
* @returns StopCondition that returns true when finish reason matches
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* stopWhen: finishReasonIs('length') // Stop when context length limit is hit
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export function finishReasonIs(reason) {
|
|
100
|
+
return ({ steps }) => {
|
|
101
|
+
return steps.some((step) => step.finishReason === reason);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=stop-conditions.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as models from '../models/index.js';
|
|
2
2
|
import type { ReusableReadableStream } from './reusable-stream.js';
|
|
3
|
-
import type { ParsedToolCall } from './tool-types.js';
|
|
3
|
+
import type { ParsedToolCall, Tool } from './tool-types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Extract text deltas from responses stream events
|
|
6
6
|
*/
|
|
@@ -43,12 +43,12 @@ export declare function extractTextFromResponse(response: models.OpenResponsesNo
|
|
|
43
43
|
* Extract all tool calls from a completed response
|
|
44
44
|
* Returns parsed tool calls with arguments as objects (not JSON strings)
|
|
45
45
|
*/
|
|
46
|
-
export declare function extractToolCallsFromResponse(response: models.OpenResponsesNonStreamingResponse): ParsedToolCall[];
|
|
46
|
+
export declare function extractToolCallsFromResponse(response: models.OpenResponsesNonStreamingResponse): ParsedToolCall<Tool>[];
|
|
47
47
|
/**
|
|
48
48
|
* Build incremental tool call updates from responses stream events
|
|
49
49
|
* Yields structured tool call objects as they're built from deltas
|
|
50
50
|
*/
|
|
51
|
-
export declare function buildToolCallStream(stream: ReusableReadableStream<models.OpenResponsesStreamEvent>): AsyncIterableIterator<ParsedToolCall
|
|
51
|
+
export declare function buildToolCallStream(stream: ReusableReadableStream<models.OpenResponsesStreamEvent>): AsyncIterableIterator<ParsedToolCall<Tool>>;
|
|
52
52
|
/**
|
|
53
53
|
* Check if a response contains any tool calls
|
|
54
54
|
*/
|