@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.
Files changed (42) hide show
  1. package/.zed/settings.json +10 -0
  2. package/_speakeasy/.github/action-inputs-config.json +53 -0
  3. package/_speakeasy/.github/action-security-config.json +88 -0
  4. package/esm/funcs/call-model.d.ts +94 -9
  5. package/esm/funcs/call-model.js +102 -120
  6. package/esm/index.d.ts +20 -8
  7. package/esm/index.js +20 -7
  8. package/esm/lib/anthropic-compat.d.ts +6 -2
  9. package/esm/lib/anthropic-compat.js +117 -98
  10. package/esm/lib/async-params.d.ts +53 -0
  11. package/esm/lib/async-params.js +76 -0
  12. package/esm/lib/chat-compat.js +4 -0
  13. package/esm/lib/claude-constants.d.ts +22 -0
  14. package/esm/lib/claude-constants.js +20 -0
  15. package/esm/lib/claude-type-guards.d.ts +10 -0
  16. package/esm/lib/claude-type-guards.js +70 -0
  17. package/esm/lib/config.d.ts +2 -2
  18. package/esm/lib/config.js +2 -2
  19. package/esm/lib/model-result.d.ts +18 -25
  20. package/esm/lib/model-result.js +137 -176
  21. package/esm/lib/next-turn-params.d.ts +30 -0
  22. package/esm/lib/next-turn-params.js +129 -0
  23. package/esm/lib/reusable-stream.js +10 -10
  24. package/esm/lib/stop-conditions.d.ts +80 -0
  25. package/esm/lib/stop-conditions.js +104 -0
  26. package/esm/lib/stream-transformers.d.ts +3 -3
  27. package/esm/lib/stream-transformers.js +311 -260
  28. package/esm/lib/stream-type-guards.d.ts +29 -0
  29. package/esm/lib/stream-type-guards.js +109 -0
  30. package/esm/lib/tool-executor.d.ts +7 -6
  31. package/esm/lib/tool-executor.js +4 -0
  32. package/esm/lib/tool-orchestrator.d.ts +7 -7
  33. package/esm/lib/tool-orchestrator.js +38 -10
  34. package/esm/lib/tool-types.d.ts +162 -28
  35. package/esm/lib/tool-types.js +6 -0
  36. package/esm/lib/tool.d.ts +99 -0
  37. package/esm/lib/tool.js +71 -0
  38. package/esm/lib/turn-context.d.ts +50 -0
  39. package/esm/lib/turn-context.js +59 -0
  40. package/esm/sdk/sdk.d.ts +3 -9
  41. package/jsr.json +1 -1
  42. 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
- // Wait for more data - but check conditions after setting up the promise
79
- // to avoid race condition where source completes between check and wait
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
- // Double-check conditions after setting up promise to handle race
87
- if (self.sourceComplete || self.sourceError || consumer.position < self.buffer.length) {
88
- // Resolve immediately if conditions changed
89
- if (consumer.waitingPromise) {
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
  */