@openrouter/sdk 0.3.7 → 0.3.11

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 +4 -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 +9 -7
  31. package/esm/lib/tool-executor.js +7 -1
  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 +163 -29
  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,29 @@
1
+ import type * as models from '../models/index.js';
2
+ /**
3
+ * Type guards for OpenResponses stream events
4
+ * These enable proper TypeScript narrowing without type casts
5
+ */
6
+ export declare function isOutputTextDeltaEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseOutputTextDelta;
7
+ export declare function isReasoningDeltaEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesReasoningDeltaEvent;
8
+ export declare function isFunctionCallArgumentsDeltaEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseFunctionCallArgumentsDelta;
9
+ export declare function isOutputItemAddedEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseOutputItemAdded;
10
+ export declare function isOutputItemDoneEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseOutputItemDone;
11
+ export declare function isResponseCompletedEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseCompleted;
12
+ export declare function isResponseFailedEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseFailed;
13
+ export declare function isResponseIncompleteEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseIncomplete;
14
+ export declare function isFunctionCallArgumentsDoneEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseFunctionCallArgumentsDone;
15
+ export declare function isOutputMessage(item: unknown): item is models.ResponsesOutputMessage;
16
+ export declare function isFunctionCallOutputItem(item: unknown): item is models.ResponsesOutputItemFunctionCall;
17
+ export declare function isReasoningOutputItem(item: unknown): item is models.ResponsesOutputItemReasoning;
18
+ export declare function isWebSearchCallOutputItem(item: unknown): item is models.ResponsesWebSearchCallOutput;
19
+ export declare function isFileSearchCallOutputItem(item: unknown): item is models.ResponsesOutputItemFileSearchCall;
20
+ export declare function isImageGenerationCallOutputItem(item: unknown): item is models.ResponsesImageGenerationCall;
21
+ export declare function isOutputTextPart(part: unknown): part is models.ResponseOutputText;
22
+ export declare function isRefusalPart(part: unknown): part is models.OpenAIResponsesRefusalContent;
23
+ export declare function isFileCitationAnnotation(annotation: unknown): annotation is models.FileCitation;
24
+ export declare function isURLCitationAnnotation(annotation: unknown): annotation is models.URLCitation;
25
+ export declare function isFilePathAnnotation(annotation: unknown): annotation is models.FilePath;
26
+ export declare function hasTypeProperty(item: unknown): item is {
27
+ type: string;
28
+ };
29
+ //# sourceMappingURL=stream-type-guards.d.ts.map
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Type guards for OpenResponses stream events
3
+ * These enable proper TypeScript narrowing without type casts
4
+ */
5
+ // Stream event type guards
6
+ export function isOutputTextDeltaEvent(event) {
7
+ return 'type' in event && event.type === 'response.output_text.delta';
8
+ }
9
+ export function isReasoningDeltaEvent(event) {
10
+ return 'type' in event && event.type === 'response.reasoning_text.delta';
11
+ }
12
+ export function isFunctionCallArgumentsDeltaEvent(event) {
13
+ return 'type' in event && event.type === 'response.function_call_arguments.delta';
14
+ }
15
+ export function isOutputItemAddedEvent(event) {
16
+ return 'type' in event && event.type === 'response.output_item.added';
17
+ }
18
+ export function isOutputItemDoneEvent(event) {
19
+ return 'type' in event && event.type === 'response.output_item.done';
20
+ }
21
+ export function isResponseCompletedEvent(event) {
22
+ return 'type' in event && event.type === 'response.completed';
23
+ }
24
+ export function isResponseFailedEvent(event) {
25
+ return 'type' in event && event.type === 'response.failed';
26
+ }
27
+ export function isResponseIncompleteEvent(event) {
28
+ return 'type' in event && event.type === 'response.incomplete';
29
+ }
30
+ export function isFunctionCallArgumentsDoneEvent(event) {
31
+ return 'type' in event && event.type === 'response.function_call_arguments.done';
32
+ }
33
+ // Output item type guards
34
+ export function isOutputMessage(item) {
35
+ return (typeof item === 'object' &&
36
+ item !== null &&
37
+ 'type' in item &&
38
+ item.type === 'message');
39
+ }
40
+ export function isFunctionCallOutputItem(item) {
41
+ return (typeof item === 'object' &&
42
+ item !== null &&
43
+ 'type' in item &&
44
+ item.type === 'function_call');
45
+ }
46
+ export function isReasoningOutputItem(item) {
47
+ return (typeof item === 'object' &&
48
+ item !== null &&
49
+ 'type' in item &&
50
+ item.type === 'reasoning');
51
+ }
52
+ export function isWebSearchCallOutputItem(item) {
53
+ return (typeof item === 'object' &&
54
+ item !== null &&
55
+ 'type' in item &&
56
+ item.type === 'web_search_call');
57
+ }
58
+ export function isFileSearchCallOutputItem(item) {
59
+ return (typeof item === 'object' &&
60
+ item !== null &&
61
+ 'type' in item &&
62
+ item.type === 'file_search_call');
63
+ }
64
+ export function isImageGenerationCallOutputItem(item) {
65
+ return (typeof item === 'object' &&
66
+ item !== null &&
67
+ 'type' in item &&
68
+ item.type === 'image_generation_call');
69
+ }
70
+ // Content part type guards
71
+ export function isOutputTextPart(part) {
72
+ return (typeof part === 'object' &&
73
+ part !== null &&
74
+ 'type' in part &&
75
+ part.type === 'output_text');
76
+ }
77
+ export function isRefusalPart(part) {
78
+ return (typeof part === 'object' &&
79
+ part !== null &&
80
+ 'type' in part &&
81
+ part.type === 'refusal');
82
+ }
83
+ // Annotation type guards for Claude conversion
84
+ export function isFileCitationAnnotation(annotation) {
85
+ return (typeof annotation === 'object' &&
86
+ annotation !== null &&
87
+ 'type' in annotation &&
88
+ annotation.type === 'file_citation');
89
+ }
90
+ export function isURLCitationAnnotation(annotation) {
91
+ return (typeof annotation === 'object' &&
92
+ annotation !== null &&
93
+ 'type' in annotation &&
94
+ annotation.type === 'url_citation');
95
+ }
96
+ export function isFilePathAnnotation(annotation) {
97
+ return (typeof annotation === 'object' &&
98
+ annotation !== null &&
99
+ 'type' in annotation &&
100
+ annotation.type === 'file_path');
101
+ }
102
+ // Helper to check if output has a type property
103
+ export function hasTypeProperty(item) {
104
+ return (typeof item === 'object' &&
105
+ item !== null &&
106
+ 'type' in item &&
107
+ typeof item.type === 'string');
108
+ }
109
+ //# sourceMappingURL=stream-type-guards.js.map
@@ -1,13 +1,15 @@
1
- import type { ZodType } from 'zod/v4';
1
+ import type { ZodType } from 'zod';
2
2
  import type { APITool, Tool, ParsedToolCall, ToolExecutionResult, TurnContext } from './tool-types.js';
3
3
  /**
4
4
  * Convert a Zod schema to JSON Schema using Zod v4's toJSONSchema function
5
+ * Uses type assertion to bridge zod (user schemas) and zod/v4 (toJSONSchema)
5
6
  */
6
7
  export declare function convertZodToJsonSchema(zodSchema: ZodType): Record<string, unknown>;
7
8
  /**
8
9
  * Convert tools to OpenRouter API format
10
+ * Accepts readonly arrays for better type compatibility
9
11
  */
10
- export declare function convertToolsToAPIFormat(tools: Tool[]): APITool[];
12
+ export declare function convertToolsToAPIFormat(tools: readonly Tool[]): APITool[];
11
13
  /**
12
14
  * Validate tool input against Zod schema
13
15
  * @throws ZodError if validation fails
@@ -25,19 +27,19 @@ export declare function parseToolCallArguments(argumentsString: string): unknown
25
27
  /**
26
28
  * Execute a regular (non-generator) tool
27
29
  */
28
- export declare function executeRegularTool(tool: Tool, toolCall: ParsedToolCall, context: TurnContext): Promise<ToolExecutionResult>;
30
+ export declare function executeRegularTool(tool: Tool, toolCall: ParsedToolCall<Tool>, context: TurnContext): Promise<ToolExecutionResult<Tool>>;
29
31
  /**
30
32
  * Execute a generator tool and collect preliminary and final results
31
33
  * - Intermediate yields are validated against eventSchema (preliminary events)
32
34
  * - Last yield is validated against outputSchema (final result sent to model)
33
35
  * - Generator must emit at least one value
34
36
  */
35
- export declare function executeGeneratorTool(tool: Tool, toolCall: ParsedToolCall, context: TurnContext, onPreliminaryResult?: (toolCallId: string, result: unknown) => void): Promise<ToolExecutionResult>;
37
+ export declare function executeGeneratorTool(tool: Tool, toolCall: ParsedToolCall<Tool>, context: TurnContext, onPreliminaryResult?: (toolCallId: string, result: unknown) => void): Promise<ToolExecutionResult<Tool>>;
36
38
  /**
37
39
  * Execute a tool call
38
40
  * Automatically detects if it's a regular or generator tool
39
41
  */
40
- export declare function executeTool(tool: Tool, toolCall: ParsedToolCall, context: TurnContext, onPreliminaryResult?: (toolCallId: string, result: unknown) => void): Promise<ToolExecutionResult>;
42
+ export declare function executeTool(tool: Tool, toolCall: ParsedToolCall<Tool>, context: TurnContext, onPreliminaryResult?: (toolCallId: string, result: unknown) => void): Promise<ToolExecutionResult<Tool>>;
41
43
  /**
42
44
  * Find a tool by name in the tools array
43
45
  */
@@ -45,9 +47,9 @@ export declare function findToolByName(tools: Tool[], name: string): Tool | unde
45
47
  /**
46
48
  * Format tool execution result as a string for sending to the model
47
49
  */
48
- export declare function formatToolResultForModel(result: ToolExecutionResult): string;
50
+ export declare function formatToolResultForModel(result: ToolExecutionResult<Tool>): string;
49
51
  /**
50
52
  * Create a user-friendly error message for tool execution errors
51
53
  */
52
- export declare function formatToolExecutionError(error: Error, toolCall: ParsedToolCall): string;
54
+ export declare function formatToolExecutionError(error: Error, toolCall: ParsedToolCall<Tool>): string;
53
55
  //# sourceMappingURL=tool-executor.d.ts.map
@@ -2,15 +2,18 @@ import { toJSONSchema, ZodError } from 'zod/v4';
2
2
  import { hasExecuteFunction, isGeneratorTool, isRegularExecuteTool } from './tool-types.js';
3
3
  /**
4
4
  * Convert a Zod schema to JSON Schema using Zod v4's toJSONSchema function
5
+ * Uses type assertion to bridge zod (user schemas) and zod/v4 (toJSONSchema)
5
6
  */
6
7
  export function convertZodToJsonSchema(zodSchema) {
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
9
  const jsonSchema = toJSONSchema(zodSchema, {
8
- target: 'openapi-3.0',
10
+ target: 'draft-7',
9
11
  });
10
12
  return jsonSchema;
11
13
  }
12
14
  /**
13
15
  * Convert tools to OpenRouter API format
16
+ * Accepts readonly arrays for better type compatibility
14
17
  */
15
18
  export function convertToolsToAPIFormat(tools) {
16
19
  return tools.map((tool) => ({
@@ -55,6 +58,8 @@ export async function executeRegularTool(tool, toolCall, context) {
55
58
  }
56
59
  try {
57
60
  // Validate input - the schema validation ensures type safety at runtime
61
+ // validateToolInput returns z.infer<typeof tool.function.inputSchema>
62
+ // which is exactly the type expected by execute
58
63
  const validatedInput = validateToolInput(tool.function.inputSchema, toolCall.arguments);
59
64
  // Execute tool with context
60
65
  const result = await Promise.resolve(tool.function.execute(validatedInput, context));
@@ -94,6 +99,7 @@ export async function executeGeneratorTool(tool, toolCall, context, onPreliminar
94
99
  }
95
100
  try {
96
101
  // Validate input - the schema validation ensures type safety at runtime
102
+ // The inputSchema's inferred type matches the execute function's parameter type by construction
97
103
  const validatedInput = validateToolInput(tool.function.inputSchema, toolCall.arguments);
98
104
  // Execute generator and collect all results
99
105
  const preliminaryResults = [];
@@ -4,7 +4,6 @@ import type { APITool, Tool, ToolExecutionResult } from './tool-types.js';
4
4
  * Options for tool execution
5
5
  */
6
6
  export interface ToolExecutionOptions {
7
- maxRounds?: number;
8
7
  onPreliminaryResult?: (toolCallId: string, result: unknown) => void;
9
8
  }
10
9
  /**
@@ -13,7 +12,7 @@ export interface ToolExecutionOptions {
13
12
  export interface ToolOrchestrationResult {
14
13
  finalResponse: models.OpenResponsesNonStreamingResponse;
15
14
  allResponses: models.OpenResponsesNonStreamingResponse[];
16
- toolExecutionResults: ToolExecutionResult[];
15
+ toolExecutionResults: ToolExecutionResult<Tool>[];
17
16
  conversationInput: models.OpenResponsesInput;
18
17
  }
19
18
  /**
@@ -22,29 +21,30 @@ export interface ToolOrchestrationResult {
22
21
  *
23
22
  * @param sendRequest - Function to send a request and get a response
24
23
  * @param initialInput - Starting input for the conversation
24
+ * @param initialRequest - Full initial request with all parameters
25
25
  * @param tools - Enhanced tools with Zod schemas and execute functions
26
26
  * @param apiTools - Converted tools in API format (JSON Schema)
27
27
  * @param options - Execution options
28
28
  * @returns Result containing final response and all execution data
29
29
  */
30
- export declare function executeToolLoop(sendRequest: (input: models.OpenResponsesInput, tools: APITool[]) => Promise<models.OpenResponsesNonStreamingResponse>, initialInput: models.OpenResponsesInput, tools: Tool[], apiTools: APITool[], options?: ToolExecutionOptions): Promise<ToolOrchestrationResult>;
30
+ export declare function executeToolLoop(sendRequest: (input: models.OpenResponsesInput, tools: APITool[]) => Promise<models.OpenResponsesNonStreamingResponse>, initialInput: models.OpenResponsesInput, initialRequest: models.OpenResponsesRequest, tools: Tool[], apiTools: APITool[], options?: ToolExecutionOptions): Promise<ToolOrchestrationResult>;
31
31
  /**
32
32
  * Convert tool execution results to a map for easy lookup
33
33
  */
34
- export declare function toolResultsToMap(results: ToolExecutionResult[]): Map<string, {
34
+ export declare function toolResultsToMap(results: ToolExecutionResult<Tool>[]): Map<string, {
35
35
  result: unknown;
36
36
  preliminaryResults?: unknown[];
37
37
  }>;
38
38
  /**
39
39
  * Build a summary of tool executions for debugging/logging
40
40
  */
41
- export declare function summarizeToolExecutions(results: ToolExecutionResult[]): string;
41
+ export declare function summarizeToolExecutions(results: ToolExecutionResult<Tool>[]): string;
42
42
  /**
43
43
  * Check if any tool executions had errors
44
44
  */
45
- export declare function hasToolExecutionErrors(results: ToolExecutionResult[]): boolean;
45
+ export declare function hasToolExecutionErrors(results: ToolExecutionResult<Tool>[]): boolean;
46
46
  /**
47
47
  * Get all tool execution errors
48
48
  */
49
- export declare function getToolExecutionErrors(results: ToolExecutionResult[]): Error[];
49
+ export declare function getToolExecutionErrors(results: ToolExecutionResult<Tool>[]): Error[];
50
50
  //# sourceMappingURL=tool-orchestrator.d.ts.map
@@ -1,30 +1,34 @@
1
1
  import { extractToolCallsFromResponse, responseHasToolCalls } from './stream-transformers.js';
2
+ import { isFunctionCallOutputItem } from './stream-type-guards.js';
2
3
  import { executeTool, findToolByName } from './tool-executor.js';
3
4
  import { hasExecuteFunction } from './tool-types.js';
5
+ import { buildTurnContext } from './turn-context.js';
6
+ import { executeNextTurnParamsFunctions, applyNextTurnParamsToRequest } from './next-turn-params.js';
4
7
  /**
5
8
  * Execute tool calls and manage multi-turn conversations
6
9
  * This orchestrates the loop of: request -> tool calls -> execute -> send results -> repeat
7
10
  *
8
11
  * @param sendRequest - Function to send a request and get a response
9
12
  * @param initialInput - Starting input for the conversation
13
+ * @param initialRequest - Full initial request with all parameters
10
14
  * @param tools - Enhanced tools with Zod schemas and execute functions
11
15
  * @param apiTools - Converted tools in API format (JSON Schema)
12
16
  * @param options - Execution options
13
17
  * @returns Result containing final response and all execution data
14
18
  */
15
- export async function executeToolLoop(sendRequest, initialInput, tools, apiTools, options = {}) {
16
- const maxRounds = options.maxRounds ?? 5;
19
+ export async function executeToolLoop(sendRequest, initialInput, initialRequest, tools, apiTools, options = {}) {
17
20
  const onPreliminaryResult = options.onPreliminaryResult;
18
21
  const allResponses = [];
19
22
  const toolExecutionResults = [];
20
23
  let conversationInput = initialInput;
24
+ let currentRequest = { ...initialRequest };
21
25
  let currentRound = 0;
22
26
  let currentResponse;
23
27
  // Initial request
24
28
  currentResponse = await sendRequest(conversationInput, apiTools);
25
29
  allResponses.push(currentResponse);
26
- // Loop until no more tool calls or max rounds reached
27
- while (responseHasToolCalls(currentResponse) && currentRound < maxRounds) {
30
+ // Loop until no more tool calls (model decides when to stop)
31
+ while (responseHasToolCalls(currentResponse)) {
28
32
  currentRound++;
29
33
  // Extract tool calls from response
30
34
  const toolCalls = extractToolCallsFromResponse(currentResponse);
@@ -56,11 +60,26 @@ export async function executeToolLoop(sendRequest, initialInput, tools, apiTools
56
60
  // Tool has no execute function - return null to filter out
57
61
  return null;
58
62
  }
59
- // Build turn context
60
- const turnContext = {
61
- numberOfTurns: currentRound,
62
- messageHistory: conversationInput,
63
+ // Find the raw tool call from the response output
64
+ const rawToolCall = currentResponse.output.find((item) => isFunctionCallOutputItem(item) && item.callId === toolCall.id);
65
+ if (!rawToolCall) {
66
+ throw new Error(`Could not find raw tool call for ${toolCall.id}`);
67
+ }
68
+ // Convert to OpenResponsesFunctionToolCall format
69
+ const openResponsesToolCall = {
70
+ type: 'function_call',
71
+ callId: rawToolCall.callId,
72
+ name: rawToolCall.name,
73
+ arguments: rawToolCall.arguments,
74
+ id: rawToolCall.callId,
75
+ status: rawToolCall.status,
63
76
  };
77
+ // Build turn context with full information
78
+ const turnContext = buildTurnContext({
79
+ numberOfTurns: currentRound,
80
+ toolCall: openResponsesToolCall,
81
+ turnRequest: currentRequest,
82
+ });
64
83
  // Execute the tool
65
84
  return executeTool(tool, toolCall, turnContext, onPreliminaryResult);
66
85
  });
@@ -90,10 +109,17 @@ export async function executeToolLoop(sendRequest, initialInput, tools, apiTools
90
109
  }
91
110
  });
92
111
  toolExecutionResults.push(...roundResults);
112
+ // Execute nextTurnParams functions for tools that were called
113
+ const computedParams = await executeNextTurnParamsFunctions(toolCalls, tools, currentRequest);
114
+ // Apply computed parameters to request
115
+ if (Object.keys(computedParams).length > 0) {
116
+ currentRequest = applyNextTurnParamsToRequest(currentRequest, computedParams);
117
+ conversationInput = currentRequest.input ?? conversationInput;
118
+ }
93
119
  // Build array input with all output from previous response plus tool results
94
120
  // The API expects continuation via previousResponseId, not by including outputs
95
121
  // For now, we'll keep the conversation going via previousResponseId
96
- conversationInput = initialInput; // Keep original input
122
+ // conversationInput is updated above if nextTurnParams modified it
97
123
  // Note: The OpenRouter Responses API uses previousResponseId for continuation
98
124
  // Tool results are automatically associated with the previous response's tool calls
99
125
  // Send updated conversation to API - this should use previousResponseId
@@ -147,6 +173,8 @@ export function hasToolExecutionErrors(results) {
147
173
  * Get all tool execution errors
148
174
  */
149
175
  export function getToolExecutionErrors(results) {
150
- return results.filter((result) => result.error !== undefined).map((result) => result.error);
176
+ return results
177
+ .filter((result) => result.error !== undefined)
178
+ .map((result) => result.error);
151
179
  }
152
180
  //# sourceMappingURL=tool-orchestrator.js.map