mcpField
O
openrouter-typescript-sdk

openrouter-typescript-sdk

github.com/openrouterteam开发309455 次安装1 天前更新
代码仓库

关于 openrouter-typescript-sdk

openrouter-typescript-sdk 是面向 TypeScript 开发者的 OpenRouter SDK 技能,提供统一接口访问模型并构建 Agent 工作流。它分为 Agent 能力包和平台功能包,涵盖工具调用、流式响应及账户管理。

主要功能

  1. 01统一调用 OpenRouter 模型
  2. 02支持文本、多轮和多模态输入
  3. 03流式读取文本与工具调用
  4. 04定义带类型验证的工具
  5. 05管理模型、额度、OAuth 和 API 密钥

适用场景

  1. 01在 TypeScript 应用中调用模型
  2. 02构建自动执行工具的多轮流程
  3. 03将模型响应作为流处理
  4. 04为面向用户的应用实现 OAuth

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/openrouterteam/skills --skill openrouter-typescript-sdk

安装后在 Claude Code 中输入/openrouter-typescript-sdk确认已加载。

附带文件metadata.jsonREADME.md

详细介绍

OpenRouter TypeScript SDK

一款功能全面的 TypeScript SDK,用于与 OpenRouter 统一 API 交互,通过单一的类型安全接口访问 300 多种 AI 模型。此技能使 AI Agent 能够利用 callModel 模式进行文本生成、工具调用、流式处理和多轮对话。

SDK 分为两个包:

  • @openrouter/agent — Agent 功能:callModel、tool()、停止条件、流式处理、格式转换器
  • @openrouter/sdk — 平台功能:模型列表、聊天补全、额度、OAuth、API 密钥管理

安装

# For agent features (callModel, tools, stop conditions)
npm install @openrouter/agent

# For platform features (models, credits, OAuth, API keys)
npm install @openrouter/sdk

设置

从 openrouter.ai/settings/keys 获取 API 密钥,然后进行初始化:

import { OpenRouter } from '@openrouter/agent';

const client = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY
});

身份验证

SDK 支持两种身份验证方式:面向服务端应用的 API 密钥,以及面向用户的应用的 OAuth PKCE 流程。

API 密钥身份验证

主要的身份验证方式是使用 OpenRouter 账户中的 API 密钥。

获取 API 密钥
  1. 访问 openrouter.ai/settings/keys
  2. 创建新的 API 密钥
  3. 将密钥安全地存储在环境变量中
环境设置
export OPENROUTER_API_KEY=sk-or-v1-your-key-here
客户端初始化
import { OpenRouter } from '@openrouter/agent';

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 密钥信息(需要 @openrouter/sdk):

import OpenRouter from '@openrouter/sdk';
const sdkClient = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });

const keyInfo = await sdkClient.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);

参数:

参数类型必填说明
callbackUrlstring是用户授权后应用的回调 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);

参数:

参数类型必填说明
codestring是OAuth 重定向返回的授权码
完整的 OAuth 流程示例
import OpenRouter from '@openrouter/sdk';
import { OpenRouter as Agent } from '@openrouter/agent';
import express from 'express';

const app = express();
// SDK client for OAuth and API key management
const client = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY
});

// 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 an agent client with the user's key
  const userAgent = new Agent({
    apiKey: userApiKey
  });

  const result = userAgent.callModel({
    model: 'openai/gpt-5-nano',
    input: req.body.message
  });

  const text = await result.getText();
  res.json({ response: text });
});
安全最佳实践
  1. 环境变量:将 API 密钥存储在环境变量中,切勿写入代码
  2. 密钥轮换:定期使用密钥管理 API 轮换密钥
  3. 环境隔离:为开发、预发布和生产环境使用不同的密钥
  4. 面向用户的应用使用 OAuth:使用 OAuth PKCE 流程,避免处理用户凭据
  5. 安全存储:将用户 API 密钥加密后存储在数据库中
  6. 最小权限范围:创建仅包含所需权限的密钥

核心概念: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()获取包含令牌用量的完整响应对象
getTextStream()按到达顺序流式传输文本增量
getReasoningStream()流式传输推理令牌(适用于 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/agent/tool';
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/agent/stop-conditions';

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!'
});
上下文对象属性
属性类型描述
numberOfTurnsnumber当前轮数
messagesarray目前为止的所有消息
instructionsstring当前系统指令
totalCostnumber累计成本

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/agent';

// 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/agent';

// 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,SDK 客户端(@openrouter/sdk)还提供对平台 API 端点的访问:

import OpenRouter from '@openrouter/sdk';

const sdkClient = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY
});

// List available models
const models = await sdkClient.models.list();

// Chat completions (alternative to callModel)
const completion = await sdkClient.chat.send({
  model: 'openai/gpt-5-nano',
  messages: [{ role: 'user', content: 'Hello!' }]
});

// Legacy completions format
const legacyCompletion = await sdkClient.completions.generate({
  model: 'openai/gpt-5-nano',
  prompt: 'Once upon a time'
});

// Usage analytics
const activity = await sdkClient.analytics.getUserActivity();

// Credit balance
const credits = await sdkClient.credits.getCredits();

// API key management
const keys = await sdkClient.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 } from '@openrouter/agent';
import { tool } from '@openrouter/agent/tool';
import { stepCountIs, hasToolCall } from '@openrouter/agent/stop-conditions';
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
}

其他资源


SDK 状态:Beta - 请在 GitHub 上报告问题

评论

支持 Markdown