@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,53 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "mode",
|
|
4
|
+
"validation_regex": "/^(direct|pr)$/.source",
|
|
5
|
+
"validation_message": "Must be `direct` or `pr`"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": "speakeasy_version",
|
|
9
|
+
"validation_regex": "/^[\\w.\\-]+$/.source",
|
|
10
|
+
"validation_message": "Letters, numbers, or .-_ only"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "openapi_doc_location",
|
|
14
|
+
"validation_regex": "/^((https?):\\/\\/([\\w\\-]+\\.)+\\w+(\\/.*)?|[\\w.\\-\\/]+)$/i.source",
|
|
15
|
+
"validation_message": "Must be a valid server URL or file path containing letters, numbers, or .-_/ only"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "openapi_doc_auth_header",
|
|
19
|
+
"validation_regex": "/^[A-Za-z\\-]+$/.source",
|
|
20
|
+
"validation_message": "Letters or - only"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "create_release"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "publish_python",
|
|
27
|
+
"language": "python"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "publish_typescript",
|
|
31
|
+
"language": "typescript"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "publish_java",
|
|
35
|
+
"language": "java"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "publish_php",
|
|
39
|
+
"language": "php"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "publish_ruby",
|
|
43
|
+
"language": "ruby"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "publish_csharp",
|
|
47
|
+
"language": "csharp"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "publish_terraform",
|
|
51
|
+
"language": "terraform"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "pypi_token",
|
|
4
|
+
"secret_name": "PYPI_TOKEN",
|
|
5
|
+
"validation_regex": "/^[\\w.\\-]+$/.source",
|
|
6
|
+
"validation_message": "Letters, numbers, or .-_ only",
|
|
7
|
+
"language": "python"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "npm_token",
|
|
11
|
+
"secret_name": "NPM_TOKEN",
|
|
12
|
+
"validation_regex": "/^[\\w.\\-]+$/.source",
|
|
13
|
+
"validation_message": "Letters, numbers, or .-_ only",
|
|
14
|
+
"language": "typescript"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "packagist_username",
|
|
18
|
+
"validation_regex": "/^[\\w.\\-]+$/.source",
|
|
19
|
+
"validation_message": "Letters, numbers, or .-_ only",
|
|
20
|
+
"language": "php"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "packagist_token",
|
|
24
|
+
"validation_regex": "/^[\\w.\\-]+$/.source",
|
|
25
|
+
"validation_message": "Letters, numbers, or .-_ only",
|
|
26
|
+
"language": "php"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "openapi_doc_auth_token",
|
|
30
|
+
"secret_name": "SPEC_TOKEN",
|
|
31
|
+
"validation_regex": "/^[\\w.\\-]+$/.source",
|
|
32
|
+
"validation_message": "Letters, numbers, or .-_ only"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "speakeasy_api_key",
|
|
36
|
+
"secret_name": "SPEAKEASY_API_KEY",
|
|
37
|
+
"validation_regex": "/^[\\w.\\-]+$/.source",
|
|
38
|
+
"validation_message": "Letters, numbers, or .-_ only"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "ossrh_username",
|
|
42
|
+
"secret_name": "MAVEN_USERNAME",
|
|
43
|
+
"validation_regex": "/^[\\w.\\-]+$/.source",
|
|
44
|
+
"validation_message": "Letters, numbers, or .-_ only",
|
|
45
|
+
"language": "java"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "ossrh_password",
|
|
49
|
+
"secret_name": "MAVEN_PASSWORD",
|
|
50
|
+
"language": "java"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "java_gpg_secret_key",
|
|
54
|
+
"validation_regex": "/^[\\w.\\-\\t \\r\\n]+$/.source",
|
|
55
|
+
"validation_message": "Letters, numbers, tabs, spaces, newlines, or .-_ only",
|
|
56
|
+
"language": "java"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "java_gpg_passphrase",
|
|
60
|
+
"language": "java"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "terraform_gpg_secret_key",
|
|
64
|
+
"secret_name": "TERRAFORM_GPG_SECRET_KEY",
|
|
65
|
+
"validation_regex": "/^[\\w.\\-\\t \\r\\n]+$/.source",
|
|
66
|
+
"validation_message": "Letters, numbers, tabs, spaces, newlines, or .-_ only",
|
|
67
|
+
"language": "terraform"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "terraform_gpg_passphrase",
|
|
71
|
+
"secret_name": "TERRAFORM_GPG_PASSPHRASE",
|
|
72
|
+
"language": "terraform"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "rubygems_auth_token",
|
|
76
|
+
"secret_name": "RUBYGEMS_AUTH_TOKEN",
|
|
77
|
+
"language": "ruby"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "nuget_api_key",
|
|
81
|
+
"secret_name": "NUGET_API_KEY",
|
|
82
|
+
"language": "csharp"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "slack_webhook_url",
|
|
86
|
+
"secret_name": "SLACK_WEBHOOK_URL"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { OpenRouterCore } from
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type
|
|
5
|
-
import { ModelResult } from
|
|
1
|
+
import type { OpenRouterCore } from '../core.js';
|
|
2
|
+
import type { CallModelInput } from '../lib/async-params.js';
|
|
3
|
+
import type { RequestOptions } from '../lib/sdks.js';
|
|
4
|
+
import type { Tool } from '../lib/tool-types.js';
|
|
5
|
+
import { ModelResult } from '../lib/model-result.js';
|
|
6
|
+
export type { CallModelInput } from '../lib/async-params.js';
|
|
6
7
|
/**
|
|
7
8
|
* Get a response with multiple consumption patterns
|
|
8
9
|
*
|
|
@@ -25,9 +26,93 @@ import { ModelResult } from "../lib/model-result.js";
|
|
|
25
26
|
* For message format conversion, use the helper functions:
|
|
26
27
|
* - `fromChatMessages()` / `toChatMessage()` for OpenAI chat format
|
|
27
28
|
* - `fromClaudeMessages()` / `toClaudeMessage()` for Anthropic Claude format
|
|
29
|
+
*
|
|
30
|
+
* **Async Function Support:**
|
|
31
|
+
*
|
|
32
|
+
* Any field in CallModelInput can be a function that computes the value dynamically
|
|
33
|
+
* based on the conversation context. You can mix static values and functions in the
|
|
34
|
+
* same request. Functions are resolved before EVERY turn, allowing parameters to
|
|
35
|
+
* adapt as the conversation progresses.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* // Mix static and dynamic values
|
|
40
|
+
* const result = callModel(client, {
|
|
41
|
+
* model: 'gpt-4', // static
|
|
42
|
+
* temperature: (ctx) => Math.min(ctx.numberOfTurns * 0.2, 1.0), // dynamic
|
|
43
|
+
* input: [{ type: 'text', text: 'Hello' }], // static
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Switch models based on conversation length
|
|
50
|
+
* const result = callModel(client, {
|
|
51
|
+
* model: (ctx) => ctx.numberOfTurns > 3 ? 'gpt-4' : 'gpt-3.5-turbo',
|
|
52
|
+
* input: [{ type: 'text', text: 'Complex question' }],
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* // Use async functions to fetch dynamic values
|
|
59
|
+
* const result = callModel(client, {
|
|
60
|
+
* model: 'gpt-4',
|
|
61
|
+
* instructions: async (ctx) => {
|
|
62
|
+
* const userPrefs = await fetchUserPreferences();
|
|
63
|
+
* return `You are a helpful assistant. User preferences: ${userPrefs}`;
|
|
64
|
+
* },
|
|
65
|
+
* input: [{ type: 'text', text: 'Help me' }],
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* Async functions receive `TurnContext` with:
|
|
70
|
+
* - `numberOfTurns`: Current turn number (0-indexed, 0 = initial request)
|
|
71
|
+
* - `messageHistory`: Current conversation messages
|
|
72
|
+
* - `model`: Current model selection (if set)
|
|
73
|
+
* - `models`: Current models array (if set)
|
|
74
|
+
*
|
|
75
|
+
* **Execution Order:**
|
|
76
|
+
* Functions are resolved at the START of each turn in this order:
|
|
77
|
+
* 1. Async functions (parallel resolution)
|
|
78
|
+
* 2. Tool execution (if tools called by model)
|
|
79
|
+
* 3. nextTurnParams functions (if defined on tools)
|
|
80
|
+
* 4. API request with resolved values
|
|
81
|
+
*
|
|
82
|
+
* **Stop Conditions:**
|
|
83
|
+
*
|
|
84
|
+
* Control when tool execution stops using the `stopWhen` parameter:
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* // Stop after 3 steps
|
|
89
|
+
* stopWhen: stepCountIs(3)
|
|
90
|
+
*
|
|
91
|
+
* // Stop when a specific tool is called
|
|
92
|
+
* stopWhen: hasToolCall('finalizeResults')
|
|
93
|
+
*
|
|
94
|
+
* // Multiple conditions (OR logic - stops if ANY is true)
|
|
95
|
+
* stopWhen: [
|
|
96
|
+
* stepCountIs(10), // Safety: max 10 steps
|
|
97
|
+
* maxCost(0.50), // Budget: max $0.50
|
|
98
|
+
* hasToolCall('finalize') // Logic: stop when finalize called
|
|
99
|
+
* ]
|
|
100
|
+
*
|
|
101
|
+
* // Custom condition with full step history
|
|
102
|
+
* stopWhen: ({ steps }) => {
|
|
103
|
+
* const totalCalls = steps.reduce((sum, s) => sum + s.toolCalls.length, 0);
|
|
104
|
+
* return totalCalls >= 20; // Stop after 20 total tool calls
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* Available helper functions:
|
|
109
|
+
* - `stepCountIs(n)` - Stop after n steps
|
|
110
|
+
* - `hasToolCall(name)` - Stop when tool is called
|
|
111
|
+
* - `maxTokensUsed(n)` - Stop when token usage exceeds n
|
|
112
|
+
* - `maxCost(n)` - Stop when cost exceeds n dollars
|
|
113
|
+
* - `finishReasonIs(reason)` - Stop on specific finish reason
|
|
114
|
+
*
|
|
115
|
+
* Default: `stepCountIs(5)` if not specified
|
|
28
116
|
*/
|
|
29
|
-
export declare function callModel(client: OpenRouterCore, request:
|
|
30
|
-
tools?: Tool[];
|
|
31
|
-
maxToolRounds?: MaxToolRounds;
|
|
32
|
-
}, options?: RequestOptions): ModelResult;
|
|
117
|
+
export declare function callModel<TTools extends readonly Tool[]>(client: OpenRouterCore, request: CallModelInput<TTools>, options?: RequestOptions): ModelResult<TTools>;
|
|
33
118
|
//# sourceMappingURL=call-model.d.ts.map
|
package/esm/funcs/call-model.js
CHANGED
|
@@ -1,103 +1,5 @@
|
|
|
1
|
-
import { ModelResult } from
|
|
2
|
-
import { convertToolsToAPIFormat } from
|
|
3
|
-
/**
|
|
4
|
-
* Checks if a message looks like a Claude-style message
|
|
5
|
-
*/
|
|
6
|
-
function isClaudeStyleMessage(msg) {
|
|
7
|
-
if (!msg || typeof msg !== 'object')
|
|
8
|
-
return false;
|
|
9
|
-
// Check if it has a role field that's user or assistant
|
|
10
|
-
const role = msg.role;
|
|
11
|
-
if (role !== 'user' && role !== 'assistant')
|
|
12
|
-
return false;
|
|
13
|
-
// Check if content is an array with Claude-style content blocks
|
|
14
|
-
if (Array.isArray(msg.content)) {
|
|
15
|
-
return msg.content.some((block) => block &&
|
|
16
|
-
typeof block === 'object' &&
|
|
17
|
-
block.type &&
|
|
18
|
-
// Claude content block types (not OpenRouter types)
|
|
19
|
-
(block.type === 'text' || block.type === 'image' || block.type === 'tool_use' || block.type === 'tool_result'));
|
|
20
|
-
}
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Converts Claude-style content blocks to OpenRouter format
|
|
25
|
-
*/
|
|
26
|
-
function convertClaudeContentBlock(block) {
|
|
27
|
-
if (!block || typeof block !== 'object' || !('type' in block)) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
switch (block.type) {
|
|
31
|
-
case 'text': {
|
|
32
|
-
const textBlock = block;
|
|
33
|
-
return {
|
|
34
|
-
type: 'input_text',
|
|
35
|
-
text: textBlock.text,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
case 'image': {
|
|
39
|
-
const imageBlock = block;
|
|
40
|
-
if (imageBlock.source.type === 'url') {
|
|
41
|
-
return {
|
|
42
|
-
type: 'input_image',
|
|
43
|
-
detail: 'auto',
|
|
44
|
-
imageUrl: imageBlock.source.url,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
else if (imageBlock.source.type === 'base64') {
|
|
48
|
-
const dataUri = `data:${imageBlock.source.media_type};base64,${imageBlock.source.data}`;
|
|
49
|
-
return {
|
|
50
|
-
type: 'input_image',
|
|
51
|
-
detail: 'auto',
|
|
52
|
-
imageUrl: dataUri,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
case 'tool_use':
|
|
58
|
-
case 'tool_result':
|
|
59
|
-
// tool_use and tool_result are not handled here as they map to different input types
|
|
60
|
-
return null;
|
|
61
|
-
default:
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Converts a Claude-style message to OpenRouter EasyInputMessage format
|
|
67
|
-
*/
|
|
68
|
-
function convertClaudeMessage(msg) {
|
|
69
|
-
const { role, content } = msg;
|
|
70
|
-
if (typeof content === 'string') {
|
|
71
|
-
return {
|
|
72
|
-
role: role === 'user' ? 'user' : 'assistant',
|
|
73
|
-
content,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
// Convert array of content blocks
|
|
77
|
-
const convertedBlocks = [];
|
|
78
|
-
for (const block of content) {
|
|
79
|
-
const converted = convertClaudeContentBlock(block);
|
|
80
|
-
if (converted) {
|
|
81
|
-
convertedBlocks.push(converted);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
// If all blocks were text, concatenate them into a string
|
|
85
|
-
const allText = convertedBlocks.every(b => b.type === 'input_text');
|
|
86
|
-
if (allText) {
|
|
87
|
-
const text = convertedBlocks
|
|
88
|
-
.map(b => b.text)
|
|
89
|
-
.join('');
|
|
90
|
-
return {
|
|
91
|
-
role: role === 'user' ? 'user' : 'assistant',
|
|
92
|
-
content: text,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
// Otherwise, return as array
|
|
96
|
-
return {
|
|
97
|
-
role: role === 'user' ? 'user' : 'assistant',
|
|
98
|
-
content: convertedBlocks,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
1
|
+
import { ModelResult } from '../lib/model-result.js';
|
|
2
|
+
import { convertToolsToAPIFormat } from '../lib/tool-executor.js';
|
|
101
3
|
/**
|
|
102
4
|
* Get a response with multiple consumption patterns
|
|
103
5
|
*
|
|
@@ -120,36 +22,116 @@ function convertClaudeMessage(msg) {
|
|
|
120
22
|
* For message format conversion, use the helper functions:
|
|
121
23
|
* - `fromChatMessages()` / `toChatMessage()` for OpenAI chat format
|
|
122
24
|
* - `fromClaudeMessages()` / `toClaudeMessage()` for Anthropic Claude format
|
|
25
|
+
*
|
|
26
|
+
* **Async Function Support:**
|
|
27
|
+
*
|
|
28
|
+
* Any field in CallModelInput can be a function that computes the value dynamically
|
|
29
|
+
* based on the conversation context. You can mix static values and functions in the
|
|
30
|
+
* same request. Functions are resolved before EVERY turn, allowing parameters to
|
|
31
|
+
* adapt as the conversation progresses.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // Mix static and dynamic values
|
|
36
|
+
* const result = callModel(client, {
|
|
37
|
+
* model: 'gpt-4', // static
|
|
38
|
+
* temperature: (ctx) => Math.min(ctx.numberOfTurns * 0.2, 1.0), // dynamic
|
|
39
|
+
* input: [{ type: 'text', text: 'Hello' }], // static
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // Switch models based on conversation length
|
|
46
|
+
* const result = callModel(client, {
|
|
47
|
+
* model: (ctx) => ctx.numberOfTurns > 3 ? 'gpt-4' : 'gpt-3.5-turbo',
|
|
48
|
+
* input: [{ type: 'text', text: 'Complex question' }],
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* // Use async functions to fetch dynamic values
|
|
55
|
+
* const result = callModel(client, {
|
|
56
|
+
* model: 'gpt-4',
|
|
57
|
+
* instructions: async (ctx) => {
|
|
58
|
+
* const userPrefs = await fetchUserPreferences();
|
|
59
|
+
* return `You are a helpful assistant. User preferences: ${userPrefs}`;
|
|
60
|
+
* },
|
|
61
|
+
* input: [{ type: 'text', text: 'Help me' }],
|
|
62
|
+
* });
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* Async functions receive `TurnContext` with:
|
|
66
|
+
* - `numberOfTurns`: Current turn number (0-indexed, 0 = initial request)
|
|
67
|
+
* - `messageHistory`: Current conversation messages
|
|
68
|
+
* - `model`: Current model selection (if set)
|
|
69
|
+
* - `models`: Current models array (if set)
|
|
70
|
+
*
|
|
71
|
+
* **Execution Order:**
|
|
72
|
+
* Functions are resolved at the START of each turn in this order:
|
|
73
|
+
* 1. Async functions (parallel resolution)
|
|
74
|
+
* 2. Tool execution (if tools called by model)
|
|
75
|
+
* 3. nextTurnParams functions (if defined on tools)
|
|
76
|
+
* 4. API request with resolved values
|
|
77
|
+
*
|
|
78
|
+
* **Stop Conditions:**
|
|
79
|
+
*
|
|
80
|
+
* Control when tool execution stops using the `stopWhen` parameter:
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // Stop after 3 steps
|
|
85
|
+
* stopWhen: stepCountIs(3)
|
|
86
|
+
*
|
|
87
|
+
* // Stop when a specific tool is called
|
|
88
|
+
* stopWhen: hasToolCall('finalizeResults')
|
|
89
|
+
*
|
|
90
|
+
* // Multiple conditions (OR logic - stops if ANY is true)
|
|
91
|
+
* stopWhen: [
|
|
92
|
+
* stepCountIs(10), // Safety: max 10 steps
|
|
93
|
+
* maxCost(0.50), // Budget: max $0.50
|
|
94
|
+
* hasToolCall('finalize') // Logic: stop when finalize called
|
|
95
|
+
* ]
|
|
96
|
+
*
|
|
97
|
+
* // Custom condition with full step history
|
|
98
|
+
* stopWhen: ({ steps }) => {
|
|
99
|
+
* const totalCalls = steps.reduce((sum, s) => sum + s.toolCalls.length, 0);
|
|
100
|
+
* return totalCalls >= 20; // Stop after 20 total tool calls
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* Available helper functions:
|
|
105
|
+
* - `stepCountIs(n)` - Stop after n steps
|
|
106
|
+
* - `hasToolCall(name)` - Stop when tool is called
|
|
107
|
+
* - `maxTokensUsed(n)` - Stop when token usage exceeds n
|
|
108
|
+
* - `maxCost(n)` - Stop when cost exceeds n dollars
|
|
109
|
+
* - `finishReasonIs(reason)` - Stop on specific finish reason
|
|
110
|
+
*
|
|
111
|
+
* Default: `stepCountIs(5)` if not specified
|
|
123
112
|
*/
|
|
124
113
|
export function callModel(client, request, options) {
|
|
125
|
-
const { tools,
|
|
126
|
-
//
|
|
127
|
-
let processedInput = apiRequest.input;
|
|
128
|
-
if (Array.isArray(apiRequest.input)) {
|
|
129
|
-
const hasClaudeMessages = apiRequest.input.some(isClaudeStyleMessage);
|
|
130
|
-
if (hasClaudeMessages) {
|
|
131
|
-
processedInput = apiRequest.input.map((msg) => {
|
|
132
|
-
if (isClaudeStyleMessage(msg)) {
|
|
133
|
-
return convertClaudeMessage(msg);
|
|
134
|
-
}
|
|
135
|
-
return msg;
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// Convert tools to API format and extract enhanced tools if present
|
|
114
|
+
const { tools, stopWhen, ...apiRequest } = request;
|
|
115
|
+
// Convert tools to API format - no cast needed now that convertToolsToAPIFormat accepts readonly
|
|
140
116
|
const apiTools = tools ? convertToolsToAPIFormat(tools) : undefined;
|
|
141
|
-
// Build the request with converted tools
|
|
117
|
+
// Build the request with converted tools
|
|
118
|
+
// Note: async functions are resolved later in ModelResult.executeToolsIfNeeded()
|
|
119
|
+
// The request can have async fields (functions) or sync fields, and the tools are converted to API format
|
|
142
120
|
const finalRequest = {
|
|
143
121
|
...apiRequest,
|
|
144
|
-
...(processedInput !== undefined && { input: processedInput }),
|
|
145
|
-
...(apiTools !== undefined && { tools: apiTools }),
|
|
146
122
|
};
|
|
123
|
+
if (apiTools !== undefined) {
|
|
124
|
+
finalRequest['tools'] = apiTools;
|
|
125
|
+
}
|
|
147
126
|
return new ModelResult({
|
|
148
127
|
client,
|
|
149
128
|
request: finalRequest,
|
|
150
129
|
options: options ?? {},
|
|
151
|
-
|
|
152
|
-
|
|
130
|
+
// Preserve the exact TTools type instead of widening to Tool[]
|
|
131
|
+
tools: tools,
|
|
132
|
+
...(stopWhen !== undefined && {
|
|
133
|
+
stopWhen,
|
|
134
|
+
}),
|
|
153
135
|
});
|
|
154
136
|
}
|
|
155
137
|
//# sourceMappingURL=call-model.js.map
|
package/esm/index.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
5
|
-
export
|
|
6
|
-
export { fromClaudeMessages, toClaudeMessage } from
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
1
|
+
export type { CallModelInput, FieldOrAsyncFunction, ResolvedCallModelInput, } from './lib/async-params.js';
|
|
2
|
+
export type { Fetcher, HTTPClientOptions } from './lib/http.js';
|
|
3
|
+
export type { ChatStreamEvent, ResponseStreamEvent as EnhancedResponseStreamEvent, InferToolEvent, InferToolEventsUnion, InferToolInput, InferToolOutput, ManualTool, NextTurnParamsContext, NextTurnParamsFunctions, ParsedToolCall, StepResult, StopCondition, StopWhen, Tool, ToolExecutionResult, ToolExecutionResultUnion, ToolPreliminaryResultEvent, ToolStreamEvent, ToolWithExecute, ToolWithGenerator, TurnContext, TypedToolCall, TypedToolCallUnion, Warning, } from './lib/tool-types.js';
|
|
4
|
+
export type { BuildTurnContextOptions } from './lib/turn-context.js';
|
|
5
|
+
export type { ClaudeBase64ImageSource, ClaudeCacheControl, ClaudeCitationCharLocation, ClaudeCitationContentBlockLocation, ClaudeCitationPageLocation, ClaudeCitationSearchResultLocation, ClaudeCitationWebSearchResultLocation, ClaudeContentBlock, ClaudeContentBlockParam, ClaudeImageBlockParam, ClaudeMessage, ClaudeMessageParam, ClaudeRedactedThinkingBlock, ClaudeServerToolUseBlock, ClaudeStopReason, ClaudeTextBlock, ClaudeTextBlockParam, ClaudeTextCitation, ClaudeThinkingBlock, ClaudeToolResultBlockParam, ClaudeToolUseBlock, ClaudeToolUseBlockParam, ClaudeURLImageSource, ClaudeUsage, } from './models/claude-message.js';
|
|
6
|
+
export { fromClaudeMessages, toClaudeMessage } from './lib/anthropic-compat.js';
|
|
7
|
+
export { hasAsyncFunctions, resolveAsyncFunctions, } from './lib/async-params.js';
|
|
8
|
+
export { fromChatMessages, toChatMessage } from './lib/chat-compat.js';
|
|
9
|
+
export { ClaudeContentBlockType, NonClaudeMessageRole } from './lib/claude-constants.js';
|
|
10
|
+
export { isClaudeStyleMessages } from './lib/claude-type-guards.js';
|
|
11
|
+
export * from './lib/config.js';
|
|
12
|
+
export * as files from './lib/files.js';
|
|
13
|
+
export { HTTPClient } from './lib/http.js';
|
|
14
|
+
export { applyNextTurnParamsToRequest, buildNextTurnParamsContext, executeNextTurnParamsFunctions, } from './lib/next-turn-params.js';
|
|
15
|
+
export { finishReasonIs, hasToolCall, isStopConditionMet, maxCost, maxTokensUsed, stepCountIs, } from './lib/stop-conditions.js';
|
|
16
|
+
export { extractUnsupportedContent, getUnsupportedContentSummary, hasUnsupportedContent, } from './lib/stream-transformers.js';
|
|
17
|
+
export { tool } from './lib/tool.js';
|
|
18
|
+
export { hasExecuteFunction, isGeneratorTool, isRegularExecuteTool, isToolPreliminaryResultEvent, ToolType, } from './lib/tool-types.js';
|
|
19
|
+
export { buildTurnContext, normalizeInputToArray } from './lib/turn-context.js';
|
|
20
|
+
export * from './sdk/sdk.js';
|
|
9
21
|
//# sourceMappingURL=index.d.ts.map
|
package/esm/index.js
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
|
3
3
|
*/
|
|
4
|
-
export * from "./lib/config.js";
|
|
5
|
-
export * as files from "./lib/files.js";
|
|
6
|
-
export { HTTPClient } from "./lib/http.js";
|
|
7
|
-
export * from "./sdk/sdk.js";
|
|
8
4
|
// Message format compatibility helpers
|
|
9
|
-
export { fromClaudeMessages, toClaudeMessage } from
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
5
|
+
export { fromClaudeMessages, toClaudeMessage } from './lib/anthropic-compat.js';
|
|
6
|
+
export { hasAsyncFunctions, resolveAsyncFunctions, } from './lib/async-params.js';
|
|
7
|
+
export { fromChatMessages, toChatMessage } from './lib/chat-compat.js';
|
|
8
|
+
// Claude constants and type guards
|
|
9
|
+
export { ClaudeContentBlockType, NonClaudeMessageRole } from './lib/claude-constants.js';
|
|
10
|
+
export { isClaudeStyleMessages } from './lib/claude-type-guards.js';
|
|
11
|
+
export * from './lib/config.js';
|
|
12
|
+
export * as files from './lib/files.js';
|
|
13
|
+
export { HTTPClient } from './lib/http.js';
|
|
14
|
+
// Next turn params helpers
|
|
15
|
+
export { applyNextTurnParamsToRequest, buildNextTurnParamsContext, executeNextTurnParamsFunctions, } from './lib/next-turn-params.js';
|
|
16
|
+
// Stop condition helpers
|
|
17
|
+
export { finishReasonIs, hasToolCall, isStopConditionMet, maxCost, maxTokensUsed, stepCountIs, } from './lib/stop-conditions.js';
|
|
18
|
+
export { extractUnsupportedContent, getUnsupportedContentSummary, hasUnsupportedContent, } from './lib/stream-transformers.js';
|
|
19
|
+
// Tool creation helpers
|
|
20
|
+
export { tool } from './lib/tool.js';
|
|
21
|
+
export { hasExecuteFunction, isGeneratorTool, isRegularExecuteTool, isToolPreliminaryResultEvent, ToolType, } from './lib/tool-types.js';
|
|
22
|
+
// Turn context helpers
|
|
23
|
+
export { buildTurnContext, normalizeInputToArray } from './lib/turn-context.js';
|
|
24
|
+
export * from './sdk/sdk.js';
|
|
12
25
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import type * as models from
|
|
2
|
-
import { convertToClaudeMessage } from
|
|
1
|
+
import type * as models from '../models/index.js';
|
|
2
|
+
import { convertToClaudeMessage } from './stream-transformers.js';
|
|
3
3
|
/**
|
|
4
4
|
* Convert Anthropic Claude-style messages to OpenResponses input format.
|
|
5
5
|
*
|
|
6
6
|
* This function transforms ClaudeMessageParam[] (Anthropic SDK format) to
|
|
7
7
|
* OpenResponsesInput format that can be passed directly to callModel().
|
|
8
8
|
*
|
|
9
|
+
* Note: Some Claude features are lost in conversion as OpenRouter doesn't support them:
|
|
10
|
+
* - cache_control on content blocks
|
|
11
|
+
* - is_error flag on tool_result blocks
|
|
12
|
+
*
|
|
9
13
|
* @example
|
|
10
14
|
* ```typescript
|
|
11
15
|
* import { fromClaudeMessages } from '@openrouter/sdk';
|