关于 openrouter-typescript-sdk
面向开发者的 TypeScript SDK 技能,通过 OpenRouter 统一 API 调用模型,支持生成文本、流式响应和工具执行,适用于构建多轮 AI Agent 工作流。
主要功能
- 01类型安全的模型调用
- 02文本与多模态输入
- 03流式响应
- 04自动执行工具
- 05多轮停止条件
适用场景
- 01构建模型驱动的 TypeScript 应用
- 02向界面流式输出文本
- 03在对话中运行工具
- 04按对话上下文切换参数
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ openrouterarchived/ agent-skills --skill openrouter-typescript-sdk安装后在 Claude Code 中输入/openrouter-typescript-sdk确认已加载。
附带文件metadata.json
详细介绍
OpenRouter TypeScript SDK
一个功能全面的 TypeScript SDK,可用于调用 OpenRouter 的统一 API,通过单一、类型安全的接口访问 300 多种 AI 模型。此技能使 AI Agent 能够使用 callModel 模式进行文本生成、工具调用、流式处理和多轮对话。
安装
npm install @openrouter/sdk
设置
从 openrouter.ai/settings/keys 获取 API 密钥,然后进行初始化:
import OpenRouter from '@openrouter/sdk';
const client = new OpenRouter({
apiKey: process.env.OPENROUTER_API_KEY
});
身份验证
SDK 支持两种身份验证方式:适用于服务端应用的 API 密钥,以及适用于面向用户的应用的 OAuth PKCE 流程。
API 密钥身份验证
主要身份验证方式是使用 OpenRouter 账户中的 API 密钥。
获取 API 密钥
- 访问 openrouter.ai/settings/keys
- 创建新的 API 密钥
- 将其安全地存储在环境变量中
环境设置
export OPENROUTER_API_KEY=sk-or-v1-your-key-here
客户端初始化
import OpenRouter from '@openrouter/sdk';
const client = new OpenRouter({
apiKey: process.env.OPENROUTER_API_KEY
});
客户端会在之后的所有请求中自动使用此密钥:
// API key is automatically included
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Hello!'
});
获取当前密钥元数据
检索当前配置的 API 密钥信息:
const keyInfo = await client.apiKeys.getCurrentKeyMetadata();
console.log('Key name:', keyInfo.name);
console.log('Created:', keyInfo.createdAt);
API 密钥管理
以编程方式管理 API 密钥:
// List all keys
const keys = await client.apiKeys.list();
// Create a new key
const newKey = await client.apiKeys.create({
name: 'Production API Key'
});
// Get a specific key by hash
const key = await client.apiKeys.get({
hash: 'sk-or-v1-...'
});
// Update a key
await client.apiKeys.update({
hash: 'sk-or-v1-...',
requestBody: {
name: 'Updated Key Name'
}
});
// Delete a key
await client.apiKeys.delete({
hash: 'sk-or-v1-...'
});
OAuth 身份验证(PKCE 流程)
对于需要让用户自行控制 API 密钥的面向用户应用,OpenRouter 支持使用 PKCE(Proof Key for Code Exchange,代码交换证明)的 OAuth。此流程允许用户通过浏览器授权流程生成 API 密钥,而无需应用处理他们的凭据。
createAuthCode
生成授权码和 URL,以启动 OAuth 流程:
const authResponse = await client.oAuth.createAuthCode({
callbackUrl: 'https://myapp.com/auth/callback'
});
// authResponse contains:
// - authorizationUrl: URL to redirect the user to
// - code: The authorization code for later exchange
console.log('Redirect user to:', authResponse.authorizationUrl);
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
callbackUrl | string | 是 | 用户授权后,应用用于接收回调的 URL |
浏览器重定向:
// In a browser environment
window.location.href = authResponse.authorizationUrl;
// Or in a server-rendered app, return a redirect response
res.redirect(authResponse.authorizationUrl);
exchangeAuthCodeForAPIKey
用户授权应用后,会带着授权码重定向回你的回调 URL。将此授权码兑换为 API 密钥:
// In your callback handler
const code = req.query.code; // From the redirect URL
const apiKeyResponse = await client.oAuth.exchangeAuthCodeForAPIKey({
code: code
});
// apiKeyResponse contains:
// - key: The user's API key
// - Additional metadata about the key
const userApiKey = apiKeyResponse.key;
// Store securely for this user's future requests
await saveUserApiKey(userId, userApiKey);
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
code | string | 是 | OAuth 重定向返回的授权码 |
OAuth 流程完整示例
import OpenRouter from '@openrouter/sdk';
import express from 'express';
const app = express();
const client = new OpenRouter({
apiKey: process.env.OPENROUTER_API_KEY // Your app's key for OAuth operations
});
// Step 1: Initiate OAuth flow
app.get('/auth/start', async (req, res) => {
const authResponse = await client.oAuth.createAuthCode({
callbackUrl: 'https://myapp.com/auth/callback'
});
// Store any state needed for the callback
req.session.oauthState = { /* ... */ };
// Redirect user to OpenRouter authorization page
res.redirect(authResponse.authorizationUrl);
});
// Step 2: Handle callback and exchange code
app.get('/auth/callback', async (req, res) => {
const { code } = req.query;
if (!code) {
return res.status(400).send('Authorization code missing');
}
try {
const apiKeyResponse = await client.oAuth.exchangeAuthCodeForAPIKey({
code: code as string
});
// Store the user's API key securely
await saveUserApiKey(req.session.userId, apiKeyResponse.key);
res.redirect('/dashboard?auth=success');
} catch (error) {
console.error('OAuth exchange failed:', error);
res.redirect('/auth/error');
}
});
// Step 3: Use the user's API key for their requests
app.post('/api/chat', async (req, res) => {
const userApiKey = await getUserApiKey(req.session.userId);
// Create a client with the user's key
const userClient = new OpenRouter({
apiKey: userApiKey
});
const result = userClient.callModel({
model: 'openai/gpt-5-nano',
input: req.body.message
});
const text = await result.getText();
res.json({ response: text });
});
安全最佳实践
- 环境变量:将 API 密钥存储在环境变量中,切勿写入代码
- 密钥轮换:使用密钥管理 API 定期轮换密钥
- 环境隔离:为开发、预发布和生产环境使用不同的密钥
- 面向用户的应用使用 OAuth:面向用户的应用应使用 OAuth PKCE 流程,避免处理用户凭据
- 安全存储:在数据库中加密存储用户 API 密钥
- 最小权限范围:创建仅包含所需权限的密钥
核心概念:callModel
callModel 函数是文本生成的主要接口。它提供了一种统一且类型安全的方式,可与任何受支持的模型交互。
基本用法
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Explain quantum computing in one sentence.',
});
const text = await result.getText();
主要优势
- 类型安全的参数,支持 IDE 完整的自动补全
- 根据 OpenAPI 规范自动生成——随新模型推出自动更新
- 多种使用方式——文本、流式处理、结构化数据
- 自动执行工具,支持多轮对话
输入格式
SDK 接受灵活的输入类型作为 input 参数:
字符串输入
简单字符串会转换为一条用户消息:
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Hello, how are you?'
});
消息数组
用于多轮对话:
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: [
{ role: 'user', content: 'What is the capital of France?' },
{ role: 'assistant', content: 'The capital of France is Paris.' },
{ role: 'user', content: 'What is its population?' }
]
});
多模态内容
包含图像和文本:
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: [
{
role: 'user',
content: [
{ type: 'text', text: 'What is in this image?' },
{ type: 'image_url', image_url: { url: 'https://example.com/image.png' } }
]
}
]
});
系统指令
使用 instructions 参数提供系统级指导:
const result = client.callModel({
model: 'openai/gpt-5-nano',
instructions: 'You are a helpful coding assistant. Be concise.',
input: 'How do I reverse a string in Python?'
});
响应方法
结果对象提供了多种方法来处理响应:
| 方法 | 用途 |
|---|---|
getText() | 所有工具执行完成后获取完整文本 |
getResponse() | 获取包含 token 用量的完整响应对象 |
getTextStream() | 随文本增量到达而流式输出 |
getReasoningStream() | 流式输出推理 token(适用于 o1/推理模型) |
getToolCallsStream() | 随工具调用完成而流式输出 |
getText()
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Write a haiku about coding'
});
const text = await result.getText();
console.log(text);
getResponse()
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Hello!'
});
const response = await result.getResponse();
console.log('Text:', response.text);
console.log('Token usage:', response.usage);
getTextStream()
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Write a short story'
});
for await (const delta of result.getTextStream()) {
process.stdout.write(delta);
}
工具系统
使用 Zod 模式创建强类型工具,实现自动验证和类型推断。
定义工具
import { tool } from '@openrouter/sdk';
import { z } from 'zod';
const weatherTool = tool({
name: 'get_weather',
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('City name'),
units: z.enum(['celsius', 'fahrenheit']).optional().default('celsius')
}),
outputSchema: z.object({
temperature: z.number(),
conditions: z.string(),
humidity: z.number()
}),
execute: async (params) => {
// Implement weather fetching logic
return {
temperature: 22,
conditions: 'Sunny',
humidity: 45
};
}
});
将工具与 callModel 搭配使用
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'What is the weather in Paris?',
tools: [weatherTool]
});
const text = await result.getText();
// The SDK automatically executes the tool and continues the conversation
工具类型
常规工具
返回结果的标准执行函数:
const calculatorTool = tool({
name: 'calculate',
description: 'Perform mathematical calculations',
inputSchema: z.object({
expression: z.string()
}),
execute: async ({ expression }) => {
return { result: eval(expression) };
}
});
生成器工具
使用 eventSchema 生成进度事件:
const searchTool = tool({
name: 'web_search',
description: 'Search the web',
inputSchema: z.object({ query: z.string() }),
eventSchema: z.object({
type: z.literal('progress'),
message: z.string()
}),
outputSchema: z.object({ results: z.array(z.string()) }),
execute: async function* ({ query }) {
yield { type: 'progress', message: 'Searching...' };
yield { type: 'progress', message: 'Processing results...' };
return { results: ['Result 1', 'Result 2'] };
}
});
手动工具
将 execute: false 设为由你自行处理工具调用:
const manualTool = tool({
name: 'user_confirmation',
description: 'Request user confirmation',
inputSchema: z.object({ message: z.string() }),
execute: false
});
带停止条件的多轮对话
使用停止条件控制自动工具执行:
import { stepCountIs, maxCost, hasToolCall } from '@openrouter/sdk';
const result = client.callModel({
model: 'openai/gpt-5.2',
input: 'Research this topic thoroughly',
tools: [searchTool, analyzeTool],
stopWhen: [
stepCountIs(10), // Stop after 10 turns
maxCost(1.00), // Stop if cost exceeds $1.00
hasToolCall('finish') // Stop when 'finish' tool is called
]
});
可用停止条件
| 条件 | 描述 |
|---|---|
stepCountIs(n) | n 轮后停止 |
maxCost(amount) | 费用超过指定金额时停止 |
hasToolCall(name) | 调用指定工具时停止 |
自定义停止条件
const customStop = (context) => {
return context.messages.length > 20;
};
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Complex task',
tools: [myTool],
stopWhen: customStop
});
动态参数
根据对话上下文计算参数:
const result = client.callModel({
model: (ctx) => ctx.numberOfTurns > 3 ? 'openai/gpt-4' : 'openai/gpt-4o-mini',
temperature: (ctx) => ctx.numberOfTurns > 1 ? 0.3 : 0.7,
input: 'Hello!'
});
上下文对象属性
| 属性 | 类型 | 描述 |
|---|---|---|
numberOfTurns | number | 当前轮数 |
messages | array | 目前为止的所有消息 |
instructions | string | 当前系统指令 |
totalCost | number | 累计费用 |
nextTurnParams:上下文注入
工具可以修改后续轮次的参数,从而支持技能和上下文感知行为:
const skillTool = tool({
name: 'load_skill',
description: 'Load a specialized skill',
inputSchema: z.object({
skill: z.string().describe('Name of the skill to load')
}),
nextTurnParams: {
instructions: (params, context) => {
const skillInstructions = loadSkillInstructions(params.skill);
return `${context.instructions}\n\n${skillInstructions}`;
}
},
execute: async ({ skill }) => {
return { loaded: skill };
}
});
nextTurnParams 的用例
- 技能系统:动态加载专用能力
- 上下文累积:跨多轮逐步构建上下文
- 模式切换:在对话过程中更改模型行为
- 记忆注入:将检索到的上下文添加到指令中
生成参数
使用以下参数控制模型行为:
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Write a creative story',
temperature: 0.7, // Creativity (0-2, default varies by model)
maxOutputTokens: 1000, // Maximum tokens to generate
topP: 0.9, // Nucleus sampling parameter
frequencyPenalty: 0.5, // Reduce repetition
presencePenalty: 0.5, // Encourage new topics
stop: ['\n\n'] // Stop sequences
});
流式传输
所有流式传输方法都支持从单个结果对象并发使用:
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Write a detailed explanation'
});
// Consumer 1: Stream text to console
const textPromise = (async () => {
for await (const delta of result.getTextStream()) {
process.stdout.write(delta);
}
})();
// Consumer 2: Get full response simultaneously
const responsePromise = result.getResponse();
// Both run concurrently
const [, response] = await Promise.all([textPromise, responsePromise]);
console.log('\n\nTotal tokens:', response.usage.totalTokens);
流式输出工具调用
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Search for information about TypeScript',
tools: [searchTool]
});
for await (const toolCall of result.getToolCallsStream()) {
console.log(`Tool called: ${toolCall.name}`);
console.log(`Arguments: ${JSON.stringify(toolCall.arguments)}`);
console.log(`Result: ${JSON.stringify(toolCall.result)}`);
}
格式转换
在不同生态格式之间转换,以实现互操作性:
OpenAI 格式
import { fromChatMessages, toChatMessage } from '@openrouter/sdk';
// OpenAI messages → OpenRouter format
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: fromChatMessages(openaiMessages)
});
// Response → OpenAI chat message format
const response = await result.getResponse();
const chatMsg = toChatMessage(response);
Claude 格式
import { fromClaudeMessages, toClaudeMessage } from '@openrouter/sdk';
// Claude messages → OpenRouter format
const result = client.callModel({
model: 'anthropic/claude-3-opus',
input: fromClaudeMessages(claudeMessages)
});
// Response → Claude message format
const response = await result.getResponse();
const claudeMsg = toClaudeMessage(response);
Responses API 消息形态
SDK 使用 OpenResponses 格式表示消息。了解这些形态对于构建可靠的 Agent 至关重要。
消息角色
消息包含用于确定消息类型的 role 属性:
| 角色 | 描述 |
|---|---|
user | 用户提供的输入 |
assistant | 模型生成的响应 |
system | 系统指令 |
developer | 开发者级指令 |
tool | 工具执行结果 |
文本消息
用户或助手发送的简单文本内容:
interface TextMessage {
role: 'user' | 'assistant';
content: string;
}
多模态消息(数组内容)
包含混合内容类型的消息:
interface MultimodalMessage {
role: 'user';
content: Array<
| { type: 'input_text'; text: string }
| { type: 'input_image'; imageUrl: string; detail?: 'auto' | 'low' | 'high' }
| {
type: 'image';
source: {
type: 'url' | 'base64';
url?: string;
media_type?: string;
data?: string
}
}
>;
}
工具函数调用消息
模型请求执行工具时:
interface ToolCallMessage {
role: 'assistant';
content?: null;
tool_calls?: Array<{
id: string;
type: 'function';
function: {
name: string;
arguments: string; // JSON-encoded arguments
};
}>;
}
工具结果消息
工具执行后返回的结果:
interface ToolResultMessage {
role: 'tool';
tool_call_id: string;
content: string; // JSON-encoded result
}
非流式响应结构
通过 getResponse() 获取的完整响应对象:
interface OpenResponsesNonStreamingResponse {
output: Array<ResponseMessage>;
usage?: {
inputTokens: number;
outputTokens: number;
cachedTokens?: number;
};
finishReason?: string;
warnings?: Array<{
type: string;
message: string
}>;
experimental_providerMetadata?: Record<string, unknown>;
}
响应消息类型
响应数组中的输出消息:
// Text/content message
interface ResponseOutputMessage {
type: 'message';
role: 'assistant';
content: string | Array<ContentPart>;
reasoning?: string; // For reasoning models (o1, etc.)
}
// Tool result in output
interface FunctionCallOutputMessage {
type: 'function_call_output';
call_id: string;
output: string;
}
已解析的工具调用
从响应中解析工具调用时:
interface ParsedToolCall {
id: string;
name: string;
arguments: unknown; // Validated against inputSchema
}
工具执行结果
工具执行完成后:
interface ToolExecutionResult {
toolCallId: string;
toolName: string;
result: unknown; // Validated against outputSchema
preliminaryResults?: unknown[]; // From generator tools
error?: Error;
}
步骤结果(用于停止条件)
可在自定义停止条件回调中使用:
interface StepResult {
stepType: 'initial' | 'continue';
text: string;
toolCalls: ParsedToolCall[];
toolResults: ToolExecutionResult[];
response: OpenResponsesNonStreamingResponse;
usage?: {
inputTokens: number;
outputTokens: number;
cachedTokens?: number;
};
finishReason?: string;
warnings?: Array<{ type: string; message: string }>;
experimental_providerMetadata?: Record<string, unknown>;
}
TurnContext
可供工具和动态参数函数使用:
interface TurnContext {
numberOfTurns: number; // Turn count (1-indexed)
turnRequest?: OpenResponsesRequest; // Current request being made
toolCall?: OpenResponsesFunctionToolCall; // Current tool call (in tool context)
}
事件类型
SDK 提供多种流式方法,可生成不同的事件类型。
响应流事件
getFullResponsesStream() 方法会生成以下事件类型:
type EnhancedResponseStreamEvent =
| ResponseCreatedEvent
| ResponseInProgressEvent
| OutputTextDeltaEvent
| OutputTextDoneEvent
| ReasoningDeltaEvent
| ReasoningDoneEvent
| FunctionCallArgumentsDeltaEvent
| FunctionCallArgumentsDoneEvent
| ResponseCompletedEvent
| ToolPreliminaryResultEvent;
事件类型参考
| 事件类型 | 描述 | 负载 |
|---|---|---|
response.created | 响应对象已初始化 | { response: ResponseObject } |
response.in_progress | 已开始生成 | {} |
response.output_text.delta | 收到文本块 | { delta: string } |
response.output_text.done | 文本生成完成 | { text: string } |
response.reasoning.delta | 推理内容块(o1 模型) | { delta: string } |
response.reasoning.done | 推理完成 | { reasoning: string } |
response.function_call_arguments.delta | 工具参数块 | { delta: string } |
response.function_call_arguments.done | 工具参数完成 | { arguments: string } |
response.completed | 完整响应已完成 | { response: ResponseObject } |
tool.preliminary_result | 生成器工具的进度 | { toolCallId: string; result: unknown } |
文本增量事件
interface OutputTextDeltaEvent {
type: 'response.output_text.delta';
delta: string;
}
推理增量事件
用于推理模型(o1 等):
interface ReasoningDeltaEvent {
type: 'response.reasoning.delta';
delta: string;
}
函数调用参数增量事件
interface FunctionCallArgumentsDeltaEvent {
type: 'response.function_call_arguments.delta';
delta: string;
}
工具初步结果事件
由会生成进度的生成器工具发出:
interface ToolPreliminaryResultEvent {
type: 'tool.preliminary_result';
toolCallId: string;
result: unknown; // Matches the tool's eventSchema
}
响应完成事件
interface ResponseCompletedEvent {
type: 'response.completed';
response: OpenResponsesNonStreamingResponse;
}
工具流事件
getToolStream() 方法会生成:
type ToolStreamEvent =
| { type: 'delta'; content: string }
| { type: 'preliminary_result'; toolCallId: string; result: unknown };
示例:处理流事件
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Analyze this data',
tools: [analysisTool]
});
for await (const event of result.getFullResponsesStream()) {
switch (event.type) {
case 'response.output_text.delta':
process.stdout.write(event.delta);
break;
case 'response.reasoning.delta':
console.log('[Reasoning]', event.delta);
break;
case 'response.function_call_arguments.delta':
console.log('[Tool Args]', event.delta);
break;
case 'tool.preliminary_result':
console.log(`[Progress: ${event.toolCallId}]`, event.result);
break;
case 'response.completed':
console.log('\n[Complete]', event.response.usage);
break;
}
}
消息流事件
getNewMessagesStream() 会生成 OpenResponses 格式的更新:
type MessageStreamUpdate =
| ResponsesOutputMessage // Text/content updates
| OpenResponsesFunctionCallOutput; // Tool results
示例:跟踪新消息
const result = client.callModel({
model: 'openai/gpt-5-nano',
input: 'Research this topic',
tools: [searchTool]
});
const allMessages: MessageStreamUpdate[] = [];
for await (const message of result.getNewMessagesStream()) {
allMessages.push(message);
if (message.type === 'message') {
console.log('Assistant:', message.content);
} else if (message.type === 'function_call_output') {
console.log('Tool result:', message.output);
}
}
API 参考
客户端方法
除了 callModel 以外,客户端还可访问其他 API 端点:
const client = new OpenRouter({
apiKey: process.env.OPENROUTER_API_KEY
});
// List available models
const models = await client.models.list();
// Chat completions (alternative to callModel)
const completion = await client.chat.send({
model: 'openai/gpt-5-nano',
messages: [{ role: 'user', content: 'Hello!' }]
});
// Legacy completions format
const legacyCompletion = await client.completions.generate({
model: 'openai/gpt-5-nano',
prompt: 'Once upon a time'
});
// Usage analytics
const activity = await client.analytics.getUserActivity();
// Credit balance
const credits = await client.credits.getCredits();
// API key management
const keys = await client.apiKeys.list();
错误处理
SDK 提供带有可操作提示的特定错误类型:
try {
const result = await client.callModel({
model: 'openai/gpt-5-nano',
input: 'Hello!'
});
const text = await result.getText();
} catch (error) {
if (error.statusCode === 401) {
console.error('Invalid API key - check your OPENROUTER_API_KEY');
} else if (error.statusCode === 402) {
console.error('Insufficient credits - add credits at openrouter.ai');
} else if (error.statusCode === 429) {
console.error('Rate limited - implement backoff retry');
} else if (error.statusCode === 503) {
console.error('Model temporarily unavailable - try again or use fallback');
} else {
console.error('Unexpected error:', error.message);
}
}
错误状态码
| 代码 | 含义 | 操作 |
|---|---|---|
| 400 | 请求无效 | 检查请求参数 |
| 401 | 未授权 | 验证 API 密钥 |
| 402 | 需要付款 | 添加额度 |
| 429 | 请求频率受限 | 实现指数退避 |
| 500 | 服务端错误 | 采用退避策略重试 |
| 503 | 服务不可用 | 尝试其他模型 |
完整示例:带工具的 Agent
import OpenRouter, { tool, stepCountIs } from '@openrouter/sdk';
import { z } from 'zod';
const client = new OpenRouter({
apiKey: process.env.OPENROUTER_API_KEY
});
// Define tools
const searchTool = tool({
name: 'web_search',
description: 'Search the web for information',
inputSchema: z.object({
query: z.string().describe('Search query')
}),
outputSchema: z.object({
results: z.array(z.object({
title: z.string(),
snippet: z.string(),
url: z.string()
}))
}),
execute: async ({ query }) => {
// Implement actual search
return {
results: [
{ title: 'Example', snippet: 'Example result', url: 'https://example.com' }
]
};
}
});
const finishTool = tool({
name: 'finish',
description: 'Complete the task with final answer',
inputSchema: z.object({
answer: z.string().describe('The final answer')
}),
execute: async ({ answer }) => ({ answer })
});
// Run agent
async function runAgent(task: string) {
const result = client.callModel({
model: 'openai/gpt-5-nano',
instructions: 'You are a helpful research assistant. Use web_search to find information, then use finish to provide your final answer.',
input: task,
tools: [searchTool, finishTool],
stopWhen: [
stepCountIs(10),
hasToolCall('finish')
]
});
// Stream progress
for await (const toolCall of result.getToolCallsStream()) {
console.log(`[${toolCall.name}] ${JSON.stringify(toolCall.arguments)}`);
}
return await result.getText();
}
// Usage
const answer = await runAgent('What are the latest developments in quantum computing?');
console.log('Final answer:', answer);
最佳实践
1. 优先使用 callModel,而非直接调用 API
callModel 模式可自动执行工具、提供类型安全并处理多轮交互。
2. 使用 Zod 定义工具架构
Zod 可提供运行时验证和出色的 TypeScript 类型推断:
import { z } from 'zod';
const schema = z.object({
name: z.string().min(1),
age: z.number().int().positive()
});
3. 设置停止条件
始终设置合理的限制,避免费用不断增加:
stopWhen: [stepCountIs(20), maxCost(5.00)]
4. 妥善处理错误
为暂时性故障实现重试逻辑:
async function callWithRetry(params, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await client.callModel(params).getText();
} catch (error) {
if (error.statusCode === 429 || error.statusCode >= 500) {
await sleep(Math.pow(2, i) * 1000);
continue;
}
throw error;
}
}
}
5. 对较长响应使用流式传输
流式传输能带来更好的用户体验,并允许提前终止:
for await (const delta of result.getTextStream()) {
// Process incrementally
}
其他资源
- API 密钥:openrouter.ai/settings/keys
- 模型列表:openrouter.ai/models
- GitHub 问题:github.com/OpenRouterTeam/typescript-sdk/issues
SDK 状态:Beta - 请在 GitHub 上报告问题