@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
@@ -1,12 +1,12 @@
1
- import { OpenResponsesEasyInputMessageRoleUser, OpenResponsesEasyInputMessageRoleAssistant, } from "../models/openresponseseasyinputmessage.js";
2
- import { OpenResponsesInputMessageItemRoleUser, OpenResponsesInputMessageItemRoleSystem, } from "../models/openresponsesinputmessageitem.js";
3
- import { OpenResponsesFunctionCallOutputType } from "../models/openresponsesfunctioncalloutput.js";
4
- import { convertToClaudeMessage } from "./stream-transformers.js";
1
+ import { OpenResponsesEasyInputMessageRoleAssistant, OpenResponsesEasyInputMessageRoleUser, } from '../models/openresponseseasyinputmessage.js';
2
+ import { OpenResponsesFunctionCallOutputType } from '../models/openresponsesfunctioncalloutput.js';
3
+ import { OpenResponsesInputMessageItemRoleUser, OpenResponsesInputMessageItemRoleDeveloper } from '../models/openresponsesinputmessageitem.js';
4
+ import { convertToClaudeMessage } from './stream-transformers.js';
5
5
  /**
6
6
  * Maps Claude role strings to OpenResponses role types
7
7
  */
8
8
  function mapClaudeRole(role) {
9
- if (role === "user") {
9
+ if (role === 'user') {
10
10
  return OpenResponsesEasyInputMessageRoleUser.User;
11
11
  }
12
12
  return OpenResponsesEasyInputMessageRoleAssistant.Assistant;
@@ -36,6 +36,10 @@ function createFunctionCallOutput(callId, output) {
36
36
  * This function transforms ClaudeMessageParam[] (Anthropic SDK format) to
37
37
  * OpenResponsesInput format that can be passed directly to callModel().
38
38
  *
39
+ * Note: Some Claude features are lost in conversion as OpenRouter doesn't support them:
40
+ * - cache_control on content blocks
41
+ * - is_error flag on tool_result blocks
42
+ *
39
43
  * @example
40
44
  * ```typescript
41
45
  * import { fromClaudeMessages } from '@openrouter/sdk';
@@ -55,124 +59,139 @@ export function fromClaudeMessages(messages) {
55
59
  const result = [];
56
60
  for (const msg of messages) {
57
61
  const { role, content } = msg;
58
- if (typeof content === "string") {
62
+ if (typeof content === 'string') {
59
63
  result.push(createEasyInputMessage(role, content));
60
64
  continue;
61
65
  }
62
- const contentItems = [];
63
- let hasStructuredContent = false;
66
+ // Separate content blocks into categories for clearer processing
67
+ const textBlocks = [];
68
+ const imageBlocks = [];
69
+ const toolUseBlocks = [];
70
+ const toolResultBlocks = [];
64
71
  for (const block of content) {
65
72
  switch (block.type) {
66
- case 'text': {
67
- const textBlock = block;
68
- contentItems.push({
69
- type: 'input_text',
70
- text: textBlock.text,
71
- });
72
- // Note: cache_control is lost in conversion (OpenRouter doesn't support it)
73
+ case 'text':
74
+ textBlocks.push(block);
75
+ break;
76
+ case 'image':
77
+ imageBlocks.push(block);
78
+ break;
79
+ case 'tool_use':
80
+ toolUseBlocks.push(block);
73
81
  break;
82
+ case 'tool_result':
83
+ toolResultBlocks.push(block);
84
+ break;
85
+ default: {
86
+ // Exhaustiveness check - TypeScript will error if we don't handle all block types
87
+ const exhaustiveCheck = block;
88
+ throw new Error(`Unhandled content block type: ${JSON.stringify(exhaustiveCheck)}`);
74
89
  }
75
- case 'image': {
76
- const imageBlock = block;
77
- hasStructuredContent = true;
78
- // Convert Claude image source to OpenRouter format
79
- if (imageBlock.source.type === 'url') {
80
- contentItems.push({
81
- type: 'input_image',
82
- detail: 'auto',
83
- imageUrl: imageBlock.source.url,
84
- });
90
+ }
91
+ }
92
+ // Process tool use blocks first (they go directly to result)
93
+ for (const toolUseBlock of toolUseBlocks) {
94
+ result.push({
95
+ type: 'function_call',
96
+ callId: toolUseBlock.id,
97
+ name: toolUseBlock.name,
98
+ arguments: JSON.stringify(toolUseBlock.input),
99
+ id: toolUseBlock.id,
100
+ status: 'completed',
101
+ });
102
+ }
103
+ // Process tool result blocks
104
+ for (const toolResultBlock of toolResultBlocks) {
105
+ let toolOutput = '';
106
+ if (typeof toolResultBlock.content === 'string') {
107
+ toolOutput = toolResultBlock.content;
108
+ }
109
+ else {
110
+ // Extract text and handle images separately
111
+ const textParts = [];
112
+ const imageParts = [];
113
+ for (const part of toolResultBlock.content) {
114
+ if (part.type === 'text') {
115
+ textParts.push(part.text);
85
116
  }
86
- else if (imageBlock.source.type === 'base64') {
87
- // Base64 images: OpenRouter expects a URL, so we use data URI
88
- const dataUri = `data:${imageBlock.source.media_type};base64,${imageBlock.source.data}`;
89
- contentItems.push({
90
- type: 'input_image',
91
- detail: 'auto',
92
- imageUrl: dataUri,
93
- });
117
+ else if (part.type === 'image') {
118
+ imageParts.push(part);
94
119
  }
95
- break;
96
- }
97
- case 'tool_use': {
98
- const toolUseBlock = block;
99
- // Map to OpenResponsesFunctionToolCall
100
- result.push({
101
- type: 'function_call',
102
- callId: toolUseBlock.id,
103
- name: toolUseBlock.name,
104
- arguments: JSON.stringify(toolUseBlock.input),
105
- id: toolUseBlock.id,
106
- status: 'completed', // Tool use in conversation history is already completed
107
- });
108
- break;
109
120
  }
110
- case 'tool_result': {
111
- const toolResultBlock = block;
112
- let toolOutput = '';
113
- if (typeof toolResultBlock.content === 'string') {
114
- toolOutput = toolResultBlock.content;
121
+ toolOutput = textParts.join('');
122
+ // Map images to image_generation_call items
123
+ imageParts.forEach((imagePart, i) => {
124
+ let imageUrl;
125
+ if (imagePart.source.type === 'url') {
126
+ imageUrl = imagePart.source.url;
115
127
  }
116
- else {
117
- // Extract text and handle images separately
118
- const textParts = [];
119
- const imageParts = [];
120
- for (const part of toolResultBlock.content) {
121
- if (part.type === 'text') {
122
- textParts.push(part.text);
123
- }
124
- else if (part.type === 'image') {
125
- imageParts.push(part);
126
- }
127
- }
128
- toolOutput = textParts.join('');
129
- // Map images to image_generation_call items
130
- for (const imagePart of imageParts) {
131
- const imageUrl = imagePart.source.type === 'url'
132
- ? imagePart.source.url
133
- : `data:${imagePart.source.media_type};base64,${imagePart.source.data}`;
134
- result.push({
135
- type: 'image_generation_call',
136
- id: `${toolResultBlock.tool_use_id}-image-${imageParts.indexOf(imagePart)}`,
137
- result: imageUrl,
138
- status: 'completed',
139
- });
140
- }
128
+ else if (imagePart.source.type === 'base64') {
129
+ imageUrl = `data:${imagePart.source.media_type};base64,${imagePart.source.data}`;
141
130
  }
142
- // Add the function call output for the text portion
143
- if (toolOutput || typeof toolResultBlock.content === 'string') {
144
- result.push(createFunctionCallOutput(toolResultBlock.tool_use_id, toolOutput));
131
+ else {
132
+ const exhaustiveCheck = imagePart.source;
133
+ throw new Error(`Unhandled image source type: ${exhaustiveCheck}`);
145
134
  }
146
- break;
135
+ result.push({
136
+ type: 'image_generation_call',
137
+ id: `${toolResultBlock.tool_use_id}-image-${i}`,
138
+ result: imageUrl,
139
+ status: 'completed',
140
+ });
141
+ });
142
+ }
143
+ // Add the function call output for the text portion (if any)
144
+ if (toolOutput.length > 0) {
145
+ result.push(createFunctionCallOutput(toolResultBlock.tool_use_id, toolOutput));
146
+ }
147
+ }
148
+ // Process text and image blocks (these become message content)
149
+ if (textBlocks.length > 0 || imageBlocks.length > 0) {
150
+ const contentItems = [];
151
+ // Add text blocks
152
+ for (const textBlock of textBlocks) {
153
+ contentItems.push({
154
+ type: 'input_text',
155
+ text: textBlock.text,
156
+ });
157
+ }
158
+ // Add image blocks
159
+ for (const imageBlock of imageBlocks) {
160
+ let imageUrl;
161
+ if (imageBlock.source.type === 'url') {
162
+ imageUrl = imageBlock.source.url;
147
163
  }
148
- default: {
149
- const _exhaustiveCheck = block;
150
- throw new Error(`Unhandled content block type: ${_exhaustiveCheck.type}`);
164
+ else if (imageBlock.source.type === 'base64') {
165
+ imageUrl = `data:${imageBlock.source.media_type};base64,${imageBlock.source.data}`;
151
166
  }
167
+ else {
168
+ const exhaustiveCheck = imageBlock.source;
169
+ throw new Error(`Unhandled image source type: ${exhaustiveCheck}`);
170
+ }
171
+ contentItems.push({
172
+ type: 'input_image',
173
+ detail: 'auto',
174
+ imageUrl,
175
+ });
152
176
  }
153
- }
154
- // Use structured format if we have images, otherwise use simple format
155
- if (contentItems.length > 0) {
156
- if (hasStructuredContent) {
157
- // Use OpenResponsesInputMessageItem for messages with images
158
- const messageRole = role === 'user'
159
- ? OpenResponsesInputMessageItemRoleUser.User
160
- : role === 'assistant'
161
- ? OpenResponsesInputMessageItemRoleSystem.System // Assistant messages treated as system in this context
162
- : OpenResponsesInputMessageItemRoleSystem.System;
177
+ // Determine output format based on content
178
+ if (imageBlocks.length > 0) {
179
+ // Use structured format for messages with images
163
180
  result.push({
164
181
  type: 'message',
165
- role: messageRole,
182
+ role: role === 'user'
183
+ ? OpenResponsesInputMessageItemRoleUser.User
184
+ : OpenResponsesInputMessageItemRoleDeveloper.Developer,
166
185
  content: contentItems,
167
186
  });
168
187
  }
169
188
  else {
170
- // Use simple format for text-only messages
189
+ // Use simple string format for text-only messages
171
190
  const textContent = contentItems
172
191
  .filter((item) => item.type === 'input_text')
173
- .map(item => item.text)
192
+ .map((item) => item.text)
174
193
  .join('');
175
- if (textContent) {
194
+ if (textContent.length > 0) {
176
195
  result.push(createEasyInputMessage(role, textContent));
177
196
  }
178
197
  }
@@ -0,0 +1,53 @@
1
+ import type * as models from '../models/index.js';
2
+ import type { StopWhen, Tool, TurnContext } from './tool-types.js';
3
+ /**
4
+ * A field can be either a value of type T or a function that computes T
5
+ */
6
+ export type FieldOrAsyncFunction<T> = T | ((context: TurnContext) => T | Promise<T>);
7
+ /**
8
+ * Input type for callModel function
9
+ * Each field can independently be a static value or a function that computes the value
10
+ * Generic over TTools to enable proper type inference for stopWhen conditions
11
+ */
12
+ export type CallModelInput<TTools extends readonly Tool[] = readonly Tool[]> = {
13
+ [K in keyof Omit<models.OpenResponsesRequest, 'stream' | 'tools'>]?: FieldOrAsyncFunction<models.OpenResponsesRequest[K]>;
14
+ } & {
15
+ tools?: TTools;
16
+ stopWhen?: StopWhen<TTools>;
17
+ };
18
+ /**
19
+ * Resolved CallModelInput (all functions evaluated to values)
20
+ * This is the type after all async functions have been resolved to their values
21
+ */
22
+ export type ResolvedCallModelInput = Omit<models.OpenResponsesRequest, 'stream' | 'tools'> & {
23
+ tools?: never;
24
+ };
25
+ /**
26
+ * Resolve all async functions in CallModelInput to their values
27
+ *
28
+ * @param input - Input with possible functions
29
+ * @param context - Turn context for function execution
30
+ * @returns Resolved input with all values (no functions)
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const resolved = await resolveAsyncFunctions(
35
+ * {
36
+ * model: 'gpt-4',
37
+ * temperature: (ctx) => ctx.numberOfTurns * 0.1,
38
+ * input: 'Hello',
39
+ * },
40
+ * { numberOfTurns: 2, messageHistory: [] }
41
+ * );
42
+ * // resolved.temperature === 0.2
43
+ * ```
44
+ */
45
+ export declare function resolveAsyncFunctions(input: CallModelInput, context: TurnContext): Promise<ResolvedCallModelInput>;
46
+ /**
47
+ * Check if input has any async functions that need resolution
48
+ *
49
+ * @param input - Input to check
50
+ * @returns True if any field is a function
51
+ */
52
+ export declare function hasAsyncFunctions(input: unknown): boolean;
53
+ //# sourceMappingURL=async-params.d.ts.map
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Type guard to check if a value is a parameter function
3
+ * Parameter functions take TurnContext and return a value or promise
4
+ */
5
+ function isParameterFunction(value) {
6
+ return typeof value === 'function';
7
+ }
8
+ /**
9
+ * Build a resolved request object from entries
10
+ * This validates the structure matches the expected ResolvedCallModelInput shape
11
+ */
12
+ function buildResolvedRequest(entries) {
13
+ const obj = Object.fromEntries(entries);
14
+ return obj;
15
+ }
16
+ /**
17
+ * Resolve all async functions in CallModelInput to their values
18
+ *
19
+ * @param input - Input with possible functions
20
+ * @param context - Turn context for function execution
21
+ * @returns Resolved input with all values (no functions)
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const resolved = await resolveAsyncFunctions(
26
+ * {
27
+ * model: 'gpt-4',
28
+ * temperature: (ctx) => ctx.numberOfTurns * 0.1,
29
+ * input: 'Hello',
30
+ * },
31
+ * { numberOfTurns: 2, messageHistory: [] }
32
+ * );
33
+ * // resolved.temperature === 0.2
34
+ * ```
35
+ */
36
+ export async function resolveAsyncFunctions(input, context) {
37
+ // Build array of resolved entries
38
+ const resolvedEntries = [];
39
+ // Iterate over all keys in the input
40
+ for (const [key, value] of Object.entries(input)) {
41
+ // Skip stopWhen - it's handled separately in ModelResult
42
+ // Note: tools are already in API format at this point (converted in callModel()), so we include them
43
+ if (key === 'stopWhen') {
44
+ continue;
45
+ }
46
+ if (isParameterFunction(value)) {
47
+ try {
48
+ // Execute the function with context and store the result
49
+ const result = await Promise.resolve(value(context));
50
+ resolvedEntries.push([key, result]);
51
+ }
52
+ catch (error) {
53
+ // Wrap errors with context about which field failed
54
+ throw new Error(`Failed to resolve async function for field "${key}": ${error instanceof Error ? error.message : String(error)}`);
55
+ }
56
+ }
57
+ else {
58
+ // Not a function, use as-is
59
+ resolvedEntries.push([key, value]);
60
+ }
61
+ }
62
+ return buildResolvedRequest(resolvedEntries);
63
+ }
64
+ /**
65
+ * Check if input has any async functions that need resolution
66
+ *
67
+ * @param input - Input to check
68
+ * @returns True if any field is a function
69
+ */
70
+ export function hasAsyncFunctions(input) {
71
+ if (!input || typeof input !== 'object') {
72
+ return false;
73
+ }
74
+ return Object.values(input).some((value) => typeof value === 'function');
75
+ }
76
+ //# sourceMappingURL=async-params.js.map
@@ -26,6 +26,10 @@ function mapChatRole(role) {
26
26
  return OpenResponsesEasyInputMessageRoleAssistant.Assistant;
27
27
  case "developer":
28
28
  return OpenResponsesEasyInputMessageRoleDeveloper.Developer;
29
+ default: {
30
+ const exhaustiveCheck = role;
31
+ throw new Error(`Unhandled role type: ${exhaustiveCheck}`);
32
+ }
29
33
  }
30
34
  }
31
35
  /**
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Claude-specific content block types
3
+ * Used for detecting Claude message format
4
+ */
5
+ export declare const ClaudeContentBlockType: {
6
+ readonly Text: "text";
7
+ readonly Image: "image";
8
+ readonly ToolUse: "tool_use";
9
+ readonly ToolResult: "tool_result";
10
+ };
11
+ export type ClaudeContentBlockType = (typeof ClaudeContentBlockType)[keyof typeof ClaudeContentBlockType];
12
+ /**
13
+ * Message roles that are NOT supported in Claude format
14
+ * Used for distinguishing Claude vs OpenAI format
15
+ */
16
+ export declare const NonClaudeMessageRole: {
17
+ readonly System: "system";
18
+ readonly Developer: "developer";
19
+ readonly Tool: "tool";
20
+ };
21
+ export type NonClaudeMessageRole = (typeof NonClaudeMessageRole)[keyof typeof NonClaudeMessageRole];
22
+ //# sourceMappingURL=claude-constants.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Claude-specific content block types
3
+ * Used for detecting Claude message format
4
+ */
5
+ export const ClaudeContentBlockType = {
6
+ Text: "text",
7
+ Image: "image",
8
+ ToolUse: "tool_use",
9
+ ToolResult: "tool_result",
10
+ };
11
+ /**
12
+ * Message roles that are NOT supported in Claude format
13
+ * Used for distinguishing Claude vs OpenAI format
14
+ */
15
+ export const NonClaudeMessageRole = {
16
+ System: "system",
17
+ Developer: "developer",
18
+ Tool: "tool",
19
+ };
20
+ //# sourceMappingURL=claude-constants.js.map
@@ -0,0 +1,10 @@
1
+ import type * as models from "../models/index.js";
2
+ /**
3
+ * Check if input is in Claude message format
4
+ * Uses structural analysis to detect Claude-specific patterns
5
+ *
6
+ * @param input - Input to check
7
+ * @returns True if input appears to be Claude format
8
+ */
9
+ export declare function isClaudeStyleMessages(input: unknown): input is models.ClaudeMessageParam[];
10
+ //# sourceMappingURL=claude-type-guards.d.ts.map
@@ -0,0 +1,70 @@
1
+ import { ClaudeContentBlockType, NonClaudeMessageRole, } from "./claude-constants.js";
2
+ function isRecord(value) {
3
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4
+ }
5
+ function isNonClaudeRole(role) {
6
+ return (role === NonClaudeMessageRole.System ||
7
+ role === NonClaudeMessageRole.Developer ||
8
+ role === NonClaudeMessageRole.Tool);
9
+ }
10
+ function isClaudeToolResultBlock(block) {
11
+ if (!isRecord(block))
12
+ return false;
13
+ return block["type"] === ClaudeContentBlockType.ToolResult;
14
+ }
15
+ function isClaudeImageBlockWithSource(block) {
16
+ if (!isRecord(block))
17
+ return false;
18
+ return (block["type"] === ClaudeContentBlockType.Image &&
19
+ "source" in block &&
20
+ isRecord(block["source"]));
21
+ }
22
+ function isClaudeToolUseBlockWithId(block) {
23
+ if (!isRecord(block))
24
+ return false;
25
+ return (block["type"] === ClaudeContentBlockType.ToolUse &&
26
+ "id" in block &&
27
+ typeof block["id"] === "string");
28
+ }
29
+ function hasClaudeSpecificBlocks(content) {
30
+ for (const block of content) {
31
+ if (isClaudeToolResultBlock(block))
32
+ return true;
33
+ if (isClaudeImageBlockWithSource(block))
34
+ return true;
35
+ if (isClaudeToolUseBlockWithId(block))
36
+ return true;
37
+ }
38
+ return false;
39
+ }
40
+ /**
41
+ * Check if input is in Claude message format
42
+ * Uses structural analysis to detect Claude-specific patterns
43
+ *
44
+ * @param input - Input to check
45
+ * @returns True if input appears to be Claude format
46
+ */
47
+ export function isClaudeStyleMessages(input) {
48
+ if (!Array.isArray(input) || input.length === 0) {
49
+ return false;
50
+ }
51
+ for (const msg of input) {
52
+ if (!isRecord(msg))
53
+ continue;
54
+ if (!("role" in msg))
55
+ continue;
56
+ if ("type" in msg)
57
+ continue; // Claude messages don't have top-level "type"
58
+ // If we find a non-Claude role, it's not Claude format
59
+ if (isNonClaudeRole(msg["role"])) {
60
+ return false;
61
+ }
62
+ // If we find Claude-specific content blocks, it's Claude format
63
+ const content = msg["content"];
64
+ if (Array.isArray(content) && hasClaudeSpecificBlocks(content)) {
65
+ return true;
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+ //# sourceMappingURL=claude-type-guards.js.map
@@ -1,3 +1,4 @@
1
+ import { SDKHooks } from "../hooks/hooks.js";
1
2
  import { HTTPClient } from "./http.js";
2
3
  import { Logger } from "./logger.js";
3
4
  import { RetryConfig } from "./retries.js";
@@ -40,13 +41,14 @@ export type SDKOptions = {
40
41
  retryConfig?: RetryConfig;
41
42
  timeoutMs?: number;
42
43
  debugLogger?: Logger;
44
+ hooks?: SDKHooks;
43
45
  };
44
46
  export declare function serverURLFromOptions(options: SDKOptions): URL | null;
45
47
  export declare const SDK_METADATA: {
46
48
  readonly language: "typescript";
47
49
  readonly openapiDocVersion: "1.0.0";
48
- readonly sdkVersion: "0.3.7";
50
+ readonly sdkVersion: "0.3.11";
49
51
  readonly genVersion: "2.788.4";
50
- readonly userAgent: "speakeasy-sdk/typescript 0.3.7 2.788.4 1.0.0 @openrouter/sdk";
52
+ readonly userAgent: "speakeasy-sdk/typescript 0.3.11 2.788.4 1.0.0 @openrouter/sdk";
51
53
  };
52
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.7",
29
+ sdkVersion: "0.3.11",
30
30
  genVersion: "2.788.4",
31
- userAgent: "speakeasy-sdk/typescript 0.3.7 2.788.4 1.0.0 @openrouter/sdk",
31
+ userAgent: "speakeasy-sdk/typescript 0.3.11 2.788.4 1.0.0 @openrouter/sdk",
32
32
  };
33
33
  //# sourceMappingURL=config.js.map
@@ -1,13 +1,14 @@
1
- import type { OpenRouterCore } from "../core.js";
2
- import type * as models from "../models/index.js";
3
- import type { RequestOptions } from "./sdks.js";
4
- import type { ChatStreamEvent, EnhancedResponseStreamEvent, Tool, MaxToolRounds, ParsedToolCall, ToolStreamEvent } from "./tool-types.js";
5
- export interface GetResponseOptions {
6
- request: models.OpenResponsesRequest;
1
+ import type { OpenRouterCore } from '../core.js';
2
+ import type * as models from '../models/index.js';
3
+ import type { CallModelInput } from './async-params.js';
4
+ import type { RequestOptions } from './sdks.js';
5
+ import type { ResponseStreamEvent, InferToolEventsUnion, ParsedToolCall, StopWhen, Tool, ToolStreamEvent } from './tool-types.js';
6
+ export interface GetResponseOptions<TTools extends readonly Tool[]> {
7
+ request: CallModelInput<TTools>;
7
8
  client: OpenRouterCore;
8
9
  options?: RequestOptions;
9
- tools?: Tool[];
10
- maxToolRounds?: MaxToolRounds;
10
+ tools?: TTools;
11
+ stopWhen?: StopWhen<TTools>;
11
12
  }
12
13
  /**
13
14
  * A wrapper around a streaming response that provides multiple consumption patterns.
@@ -25,8 +26,10 @@ export interface GetResponseOptions {
25
26
  *
26
27
  * All consumption patterns can be used concurrently thanks to the underlying
27
28
  * ReusableReadableStream implementation.
29
+ *
30
+ * @template TTools - The tools array type to enable typed tool calls and results
28
31
  */
29
- export declare class ModelResult {
32
+ export declare class ModelResult<TTools extends readonly Tool[]> {
30
33
  private reusableStream;
31
34
  private streamPromise;
32
35
  private textPromise;
@@ -36,7 +39,8 @@ export declare class ModelResult {
36
39
  private finalResponse;
37
40
  private preliminaryResults;
38
41
  private allToolExecutionRounds;
39
- constructor(options: GetResponseOptions);
42
+ private resolvedRequest;
43
+ constructor(options: GetResponseOptions<TTools>);
40
44
  /**
41
45
  * Type guard to check if a value is a non-streaming response
42
46
  */
@@ -71,7 +75,7 @@ export declare class ModelResult {
71
75
  * Multiple consumers can iterate over this stream concurrently.
72
76
  * Includes preliminary tool result events after tool execution.
73
77
  */
74
- getFullResponsesStream(): AsyncIterableIterator<EnhancedResponseStreamEvent>;
78
+ getFullResponsesStream(): AsyncIterableIterator<ResponseStreamEvent<InferToolEventsUnion<TTools>>>;
75
79
  /**
76
80
  * Stream only text deltas as they arrive.
77
81
  * This filters the full event stream to only yield text content.
@@ -95,30 +99,19 @@ export declare class ModelResult {
95
99
  * - Tool call argument deltas as { type: "delta", content: string }
96
100
  * - Preliminary results as { type: "preliminary_result", toolCallId, result }
97
101
  */
98
- getToolStream(): AsyncIterableIterator<ToolStreamEvent>;
99
- /**
100
- * Stream events in chat format (compatibility layer).
101
- * Note: This transforms responses API events into a chat-like format.
102
- * Includes preliminary tool result events after tool execution.
103
- *
104
- * @remarks
105
- * This is a compatibility method that attempts to transform the responses API
106
- * stream into a format similar to the chat API. Due to differences in the APIs,
107
- * this may not be a perfect mapping.
108
- */
109
- getFullChatStream(): AsyncIterableIterator<ChatStreamEvent>;
102
+ getToolStream(): AsyncIterableIterator<ToolStreamEvent<InferToolEventsUnion<TTools>>>;
110
103
  /**
111
104
  * Get all tool calls from the completed response (before auto-execution).
112
105
  * Note: If tools have execute functions, they will be automatically executed
113
106
  * and this will return the tool calls from the initial response.
114
107
  * Returns structured tool calls with parsed arguments.
115
108
  */
116
- getToolCalls(): Promise<ParsedToolCall[]>;
109
+ getToolCalls(): Promise<ParsedToolCall<TTools[number]>[]>;
117
110
  /**
118
111
  * Stream structured tool call objects as they're completed.
119
112
  * Each iteration yields a complete tool call with parsed arguments.
120
113
  */
121
- getToolCallsStream(): AsyncIterableIterator<ParsedToolCall>;
114
+ getToolCallsStream(): AsyncIterableIterator<ParsedToolCall<TTools[number]>>;
122
115
  /**
123
116
  * Cancel the underlying stream and all consumers
124
117
  */