openrouter by bobmatnyc/claude-mpm-skills
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill openrouterOpenRouter 提供了一个单一的 API 来访问来自 OpenAI、Anthropic、Google、Meta、Mistral 等公司的 200 多个语言模型。它提供智能路由、流式传输、成本优化和标准化的 OpenAI 兼容接口。
主要特性 :
定价模式 :
安装 :
npm install openai # 使用 OpenAI SDK
# 或
pip install openai # Python
# 在 https://openrouter.ai/keys 注册
export OPENROUTER_API_KEY="sk-or-v1-..."
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://openrouter.ai/api/v1',
apiKey: process.env.OPENROUTER_API_KEY,
defaultHeaders: {
'HTTP-Referer': 'https://your-app.com', // 可选
'X-Title': 'Your App Name', // 可选
}
});
async function chat() {
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [
{ role: 'user', content: '用简单的术语解释量子计算' }
],
});
console.log(completion.choices[0].message.content);
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
async function streamChat() {
const stream = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages: [
{ role: 'user', content: '写一个关于 AI 的短篇故事' }
],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
process.stdout.write(content);
}
}
旗舰模型(最高质量):
const flagshipModels = {
claude: 'anthropic/claude-3.5-sonnet', // 最佳推理能力
gpt4: 'openai/gpt-4-turbo', // 最佳通用能力
gemini: 'google/gemini-pro-1.5', // 最佳长上下文
opus: 'anthropic/claude-3-opus', // 最佳复杂任务处理
};
快速模型(低延迟):
const fastModels = {
claude: 'anthropic/claude-3-haiku', // 最快的 Claude
gpt35: 'openai/gpt-3.5-turbo', // 快速的 GPT
gemini: 'google/gemini-flash-1.5', // 快速的 Gemini
llama: 'meta-llama/llama-3.1-8b-instruct', // 快速的开源模型
};
成本优化模型 :
const budgetModels = {
haiku: 'anthropic/claude-3-haiku', // 每 100 万 Token $0.25/$1.25
gemini: 'google/gemini-flash-1.5', // 每 100 万 Token $0.075/$0.30
llama: 'meta-llama/llama-3.1-8b-instruct', // 每 100 万 Token $0.06/$0.06
mixtral: 'mistralai/mixtral-8x7b-instruct', // 每 100 万 Token $0.24/$0.24
};
专用模型 :
const specializedModels = {
vision: 'openai/gpt-4-vision-preview', // 图像理解
code: 'anthropic/claude-3.5-sonnet', // 代码生成
longContext: 'google/gemini-pro-1.5', // 200 万 Token 上下文
function: 'openai/gpt-4-turbo', // 函数调用
};
interface ModelSelector {
task: 'chat' | 'code' | 'vision' | 'function' | 'summary';
priority: 'quality' | 'speed' | 'cost';
maxCost?: number; // 每 100 万 Token 最大成本
contextSize?: number;
}
function selectModel(criteria: ModelSelector): string {
if (criteria.task === 'vision') {
return 'openai/gpt-4-vision-preview';
}
if (criteria.task === 'code') {
return criteria.priority === 'quality'
? 'anthropic/claude-3.5-sonnet'
: 'meta-llama/llama-3.1-70b-instruct';
}
if (criteria.contextSize && criteria.contextSize > 100000) {
return 'google/gemini-pro-1.5'; // 200 万上下文
}
// 按优先级默认选择
switch (criteria.priority) {
case 'quality':
return 'anthropic/claude-3.5-sonnet';
case 'speed':
return 'anthropic/claude-3-haiku';
case 'cost':
return criteria.maxCost && criteria.maxCost < 0.5
? 'google/gemini-flash-1.5'
: 'anthropic/claude-3-haiku';
default:
return 'openai/gpt-4-turbo';
}
}
// 用法
const model = selectModel({
task: 'code',
priority: 'quality',
});
async function robustStreamingChat(
prompt: string,
model: string = 'anthropic/claude-3.5-sonnet'
) {
try {
const stream = await client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
stream: true,
max_tokens: 4000,
});
let fullResponse = '';
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
if (delta?.content) {
fullResponse += delta.content;
process.stdout.write(delta.content);
}
// 处理函数调用
if (delta?.function_call) {
console.log('\nFunction call:', delta.function_call);
}
// 检查完成原因
if (chunk.choices[0]?.finish_reason) {
console.log(`\n[Finished: ${chunk.choices[0].finish_reason}]`);
}
}
return fullResponse;
} catch (error) {
if (error instanceof Error) {
console.error('Streaming error:', error.message);
}
throw error;
}
}
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ.get("OPENROUTER_API_KEY"),
)
def stream_chat(prompt: str, model: str = "anthropic/claude-3.5-sonnet"):
stream = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
stream=True,
)
full_response = ""
for chunk in stream:
if chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
full_response += content
print(content, end="", flush=True)
print() # 新行
return full_response
import { useState } from 'react';
function StreamingChat() {
const [response, setResponse] = useState('');
const [isStreaming, setIsStreaming] = useState(false);
async function handleSubmit(prompt: string) {
setIsStreaming(true);
setResponse('');
try {
const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: prompt }],
stream: true,
}),
});
const reader = res.body?.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader!.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(line => line.trim());
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
const content = parsed.choices[0]?.delta?.content || '';
setResponse(prev => prev + content);
} catch (e) {
// 跳过无效 JSON
}
}
}
}
} catch (error) {
console.error('Streaming error:', error);
} finally {
setIsStreaming(false);
}
}
return (
<div>
<textarea
value={response}
readOnly
rows={20}
cols={80}
placeholder="响应将在此处显示..."
/>
<button onClick={() => handleSubmit('解释 AI')}>
{isStreaming ? '流式传输中...' : '发送'}
</button>
</div>
);
}
const tools = [
{
type: 'function',
function: {
name: 'get_weather',
description: '获取指定位置的当前天气',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: '城市名称,例如 San Francisco',
},
unit: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
},
},
required: ['location'],
},
},
},
];
async function chatWithFunctions() {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages: [
{ role: 'user', content: '东京的天气怎么样?' }
],
tools,
tool_choice: 'auto',
});
const message = completion.choices[0].message;
if (message.tool_calls) {
for (const toolCall of message.tool_calls) {
console.log('Function:', toolCall.function.name);
console.log('Arguments:', toolCall.function.arguments);
// 执行函数
const args = JSON.parse(toolCall.function.arguments);
const result = await getWeather(args.location, args.unit);
// 返回结果
const followUp = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages: [
{ role: 'user', content: '东京的天气怎么样?' },
message,
{
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result),
},
],
tools,
});
console.log(followUp.choices[0].message.content);
}
}
}
async function multiStepFunctionCall(userQuery: string) {
const messages = [{ role: 'user', content: userQuery }];
let iterationCount = 0;
const maxIterations = 5;
while (iterationCount < maxIterations) {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages,
tools,
tool_choice: 'auto',
});
const message = completion.choices[0].message;
messages.push(message);
if (!message.tool_calls) {
// 没有更多函数调用,返回最终响应
return message.content;
}
// 执行所有函数调用
for (const toolCall of message.tool_calls) {
const functionName = toolCall.function.name;
const args = JSON.parse(toolCall.function.arguments);
// 执行函数(实现你的函数注册表)
const result = await executeFunctionCall(functionName, args);
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result),
});
}
iterationCount++;
}
throw new Error('达到最大迭代次数');
}
import { encoding_for_model } from 'tiktoken';
interface CostEstimate {
promptTokens: number;
completionTokens: number;
promptCost: number;
completionCost: number;
totalCost: number;
}
const modelPricing = {
'anthropic/claude-3.5-sonnet': { input: 3.00, output: 15.00 }, // 每 100 万 Token
'anthropic/claude-3-haiku': { input: 0.25, output: 1.25 },
'openai/gpt-4-turbo': { input: 10.00, output: 30.00 },
'openai/gpt-3.5-turbo': { input: 0.50, output: 1.50 },
'google/gemini-flash-1.5': { input: 0.075, output: 0.30 },
};
function estimateCost(
prompt: string,
expectedCompletion: number,
model: string
): CostEstimate {
const encoder = encoding_for_model('gpt-4'); // 近似值
const promptTokens = encoder.encode(prompt).length;
const completionTokens = expectedCompletion;
const pricing = modelPricing[model] || { input: 0, output: 0 };
const promptCost = (promptTokens / 1_000_000) * pricing.input;
const completionCost = (completionTokens / 1_000_000) * pricing.output;
return {
promptTokens,
completionTokens,
promptCost,
completionCost,
totalCost: promptCost + completionCost,
};
}
// 用法
const estimate = estimateCost(
'解释量子计算',
500, // 预期响应 Token 数
'anthropic/claude-3.5-sonnet'
);
console.log(`Estimated cost: $${estimate.totalCost.toFixed(4)}`);
async function budgetOptimizedChat(
prompt: string,
maxCostPerRequest: number = 0.01 // 最大 $0.01
) {
// 使用昂贵模型估算
const expensiveEstimate = estimateCost(
prompt,
1000,
'anthropic/claude-3.5-sonnet'
);
let selectedModel = 'anthropic/claude-3.5-sonnet';
if (expensiveEstimate.totalCost > maxCostPerRequest) {
// 尝试更便宜的模型
const cheapEstimate = estimateCost(
prompt,
1000,
'anthropic/claude-3-haiku'
);
if (cheapEstimate.totalCost > maxCostPerRequest) {
selectedModel = 'google/gemini-flash-1.5';
} else {
selectedModel = 'anthropic/claude-3-haiku';
}
}
console.log(`Selected model: ${selectedModel}`);
const completion = await client.chat.completions.create({
model: selectedModel,
messages: [{ role: 'user', content: prompt }],
});
return completion.choices[0].message.content;
}
async function batchProcess(prompts: string[], model: string) {
// 通过速率限制并行处理多个提示
const concurrency = 5;
const results = [];
for (let i = 0; i < prompts.length; i += concurrency) {
const batch = prompts.slice(i, i + concurrency);
const batchResults = await Promise.all(
batch.map(prompt =>
client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
max_tokens: 500, // 限制 Token 数以控制成本
})
)
);
results.push(...batchResults);
// 速率限制延迟
if (i + concurrency < prompts.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
return results;
}
const modelFallbackChain = [
'anthropic/claude-3.5-sonnet',
'openai/gpt-4-turbo',
'anthropic/claude-3-haiku',
'google/gemini-flash-1.5',
];
async function chatWithFallback(
prompt: string,
maxRetries: number = 3
): Promise<string> {
for (const model of modelFallbackChain) {
try {
console.log(`Trying model: ${model}`);
const completion = await client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
max_tokens: 2000,
});
return completion.choices[0].message.content || '';
} catch (error) {
console.warn(`Model ${model} failed:`, error);
// 继续尝试下一个模型
if (model === modelFallbackChain[modelFallbackChain.length - 1]) {
throw new Error('All models failed');
}
}
}
throw new Error('No models available');
}
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 5
): Promise<T> {
let lastError: Error;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
// 检查是否为速率限制错误
if (error.status === 429) {
const delay = Math.pow(2, i) * 1000; // 指数退避
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error; // 不可重试的错误
}
}
}
throw lastError!;
}
// 用法
const result = await retryWithBackoff(() =>
client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: 'Hello' }],
})
);
const systemPrompts = {
concise: '你是一个乐于助人的助手。回答要简洁直接。',
detailed: '你是一个知识渊博的专家。提供包含示例的全面答案。',
code: '你是一个专业的程序员。提供干净、注释良好的代码并附上解释。',
creative: '你是一个创意写作助手。要有想象力和吸引力。',
};
async function chatWithPersonality(
prompt: string,
personality: keyof typeof systemPrompts
) {
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [
{ role: 'system', content: systemPrompts[personality] },
{ role: 'user', content: prompt },
],
});
return completion.choices[0].message.content;
}
async function fewShotClassification(text: string) {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages: [
{
role: 'system',
content: '将文本情感分类为积极、消极或中性。',
},
{ role: 'user', content: '我喜欢这个产品!' },
{ role: 'assistant', content: 'positive' },
{ role: 'user', content: '这太糟糕了。' },
{ role: 'assistant', content: 'negative' },
{ role: 'user', content: '它工作正常。' },
{ role: 'assistant', content: 'neutral' },
{ role: 'user', content: text },
],
});
return completion.choices[0].message.content;
}
async function reasoningTask(problem: string) {
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [
{
role: 'user',
content: `${problem}\n\n让我们一步步解决这个问题:\n1.`,
},
],
max_tokens: 3000,
});
return completion.choices[0].message.content;
}
class RateLimitedClient {
private requestQueue: Array<() => Promise<any>> = [];
private processing = false;
private requestsPerMinute = 60;
private requestInterval = 60000 / this.requestsPerMinute;
async enqueue<T>(request: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.requestQueue.push(async () => {
try {
const result = await request();
resolve(result);
} catch (error) {
reject(error);
}
});
this.processQueue();
});
}
private async processQueue() {
if (this.processing || this.requestQueue.length === 0) return;
this.processing = true;
while (this.requestQueue.length > 0) {
const request = this.requestQueue.shift()!;
await request();
await new Promise(resolve => setTimeout(resolve, this.requestInterval));
}
this.processing = false;
}
}
// 用法
const rateLimitedClient = new RateLimitedClient();
const result = await rateLimitedClient.enqueue(() =>
client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: 'Hello' }],
})
);
async function analyzeImage(imageUrl: string, question: string) {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-vision-preview',
messages: [
{
role: 'user',
content: [
{ type: 'text', text: question },
{ type: 'image_url', image_url: { url: imageUrl } },
],
},
],
max_tokens: 1000,
});
return completion.choices[0].message.content;
}
// 用法
const result = await analyzeImage(
'https://example.com/image.jpg',
'这张图片中有哪些物体?'
);
async function compareImages(imageUrls: string[]) {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-vision-preview',
messages: [
{
role: 'user',
content: [
{ type: 'text', text: '比较这些图像并描述差异:' },
...imageUrls.map(url => ({
type: 'image_url' as const,
image_url: { url },
})),
],
},
],
});
return completion.choices[0].message.content;
}
interface ErrorResponse {
error: {
message: string;
type: string;
code: string;
};
}
async function robustCompletion(prompt: string) {
try {
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: prompt }],
});
return completion.choices[0].message.content;
} catch (error: any) {
// 速率限制错误
if (error.status === 429) {
console.error('Rate limit exceeded. Please wait.');
throw new Error('RATE_LIMIT_EXCEEDED');
}
// 无效的 API 密钥
if (error.status === 401) {
console.error('Invalid API key');
throw new Error('INVALID_API_KEY');
}
// 模型未找到
if (error.status === 404) {
console.error('Model not found');
throw new Error('MODEL_NOT_FOUND');
}
// 服务器错误
if (error.status >= 500) {
console.error('OpenRouter server error');
throw new Error('SERVER_ERROR');
}
// 未知错误
console.error('Unknown error:', error);
throw error;
}
}
class LoggingClient {
async chat(prompt: string, model: string) {
const startTime = Date.now();
console.log('[Request]', {
timestamp: new Date().toISOString(),
model,
promptLength: prompt.length,
});
try {
const completion = await client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
});
const duration = Date.now() - startTime;
console.log('[Response]', {
timestamp: new Date().toISOString(),
duration,
usage: completion.usage,
finishReason: completion.choices[0].finish_reason,
});
return completion;
} catch (error) {
console.error('[Error]', {
timestamp: new Date().toISOString(),
duration: Date.now() - startTime,
error,
});
throw error;
}
}
}
模型选择 :
成本优化 :
流式传输 :
错误处理 :
提示工程 :
速率限制 :
安全性 :
监控 :
❌ 在前端暴露 API 密钥 :
// 错误 - API 密钥暴露
const client = new OpenAI({
baseURL: 'https://openrouter.ai/api/v1',
apiKey: 'sk-or-v1-...', // 已暴露!
});
✅ 正确 - 服务器端代理 :
// 后端代理
app.post('/api/chat', async (req, res) => {
const { prompt } = req.body;
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: prompt }],
});
res.json(completion);
});
❌ 未处理流式传输错误 :
// 错误 - 无错误处理
for await (const chunk of stream) {
console.log(chunk.choices[0].delta.content);
}
✅ 正确 - 带错误处理 :
try {
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
process.stdout.write(content);
}
} catch (error) {
console.error('Stream error:', error);
// 实现重试或回退
}
❌ 忽略速率限制 :
// 错误 - 无速率限制
const promises = prompts.map(prompt => chat(prompt));
await Promise.all(promises); // 可能触发速率限制
✅ 正确 - 带速率限制 :
const results = [];
for (let i = 0; i < prompts.length; i += 5) {
const batch = prompts.slice(i, i + 5);
const batchResults = await Promise.all(batch.map(chat));
results.push(...batchResults);
await new Promise(r => setTimeout(r, 1000)); // 批次间延迟
}
const responseCache = new Map<string, string>();
async function cachedChat(prompt: string, model: string) {
const cacheKey = `${model}:${prompt}`;
if (responseCache.has(cacheKey)) {
console.log('Cache hit');
return responseCache.get(cacheKey)!;
}
const completion = await client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
});
const response = completion.choices[0].message.content || '';
responseCache.set(cacheKey, response);
return response;
}
async function parallelChat(prompts: string[], model: string) {
const results = await Promise.all(
prompts.map(prompt =>
client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
})
)
);
return results.map(r => r.choices[0].message.content);
}
每周安装数
95
仓库
GitHub 星标数
18
首次出现
Jan 23, 2026
安全审计
安装于
opencode75
codex74
gemini-cli74
cursor68
github-copilot68
claude-code66
OpenRouter provides a single API to access 200+ language models from OpenAI, Anthropic, Google, Meta, Mistral, and more. It offers intelligent routing, streaming, cost optimization, and standardized OpenAI-compatible interface.
Key Features :
Pricing Model :
Installation :
npm install openai # Use OpenAI SDK
# or
pip install openai # Python
# Sign up at https://openrouter.ai/keys
export OPENROUTER_API_KEY="sk-or-v1-..."
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://openrouter.ai/api/v1',
apiKey: process.env.OPENROUTER_API_KEY,
defaultHeaders: {
'HTTP-Referer': 'https://your-app.com', // Optional
'X-Title': 'Your App Name', // Optional
}
});
async function chat() {
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [
{ role: 'user', content: 'Explain quantum computing in simple terms' }
],
});
console.log(completion.choices[0].message.content);
}
async function streamChat() {
const stream = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages: [
{ role: 'user', content: 'Write a short story about AI' }
],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
process.stdout.write(content);
}
}
Flagship Models (Highest Quality):
const flagshipModels = {
claude: 'anthropic/claude-3.5-sonnet', // Best reasoning
gpt4: 'openai/gpt-4-turbo', // Best general purpose
gemini: 'google/gemini-pro-1.5', // Best long context
opus: 'anthropic/claude-3-opus', // Best complex tasks
};
Fast Models (Low Latency):
const fastModels = {
claude: 'anthropic/claude-3-haiku', // Fastest Claude
gpt35: 'openai/gpt-3.5-turbo', // Fast GPT
gemini: 'google/gemini-flash-1.5', // Fast Gemini
llama: 'meta-llama/llama-3.1-8b-instruct', // Fast open source
};
Cost-Optimized Models :
const budgetModels = {
haiku: 'anthropic/claude-3-haiku', // $0.25/$1.25 per 1M tokens
gemini: 'google/gemini-flash-1.5', // $0.075/$0.30 per 1M tokens
llama: 'meta-llama/llama-3.1-8b-instruct', // $0.06/$0.06 per 1M tokens
mixtral: 'mistralai/mixtral-8x7b-instruct', // $0.24/$0.24 per 1M tokens
};
Specialized Models :
const specializedModels = {
vision: 'openai/gpt-4-vision-preview', // Image understanding
code: 'anthropic/claude-3.5-sonnet', // Code generation
longContext: 'google/gemini-pro-1.5', // 2M token context
function: 'openai/gpt-4-turbo', // Function calling
};
interface ModelSelector {
task: 'chat' | 'code' | 'vision' | 'function' | 'summary';
priority: 'quality' | 'speed' | 'cost';
maxCost?: number; // Max cost per 1M tokens
contextSize?: number;
}
function selectModel(criteria: ModelSelector): string {
if (criteria.task === 'vision') {
return 'openai/gpt-4-vision-preview';
}
if (criteria.task === 'code') {
return criteria.priority === 'quality'
? 'anthropic/claude-3.5-sonnet'
: 'meta-llama/llama-3.1-70b-instruct';
}
if (criteria.contextSize && criteria.contextSize > 100000) {
return 'google/gemini-pro-1.5'; // 2M context
}
// Default selection by priority
switch (criteria.priority) {
case 'quality':
return 'anthropic/claude-3.5-sonnet';
case 'speed':
return 'anthropic/claude-3-haiku';
case 'cost':
return criteria.maxCost && criteria.maxCost < 0.5
? 'google/gemini-flash-1.5'
: 'anthropic/claude-3-haiku';
default:
return 'openai/gpt-4-turbo';
}
}
// Usage
const model = selectModel({
task: 'code',
priority: 'quality',
});
async function robustStreamingChat(
prompt: string,
model: string = 'anthropic/claude-3.5-sonnet'
) {
try {
const stream = await client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
stream: true,
max_tokens: 4000,
});
let fullResponse = '';
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
if (delta?.content) {
fullResponse += delta.content;
process.stdout.write(delta.content);
}
// Handle function calls
if (delta?.function_call) {
console.log('\nFunction call:', delta.function_call);
}
// Check for finish reason
if (chunk.choices[0]?.finish_reason) {
console.log(`\n[Finished: ${chunk.choices[0].finish_reason}]`);
}
}
return fullResponse;
} catch (error) {
if (error instanceof Error) {
console.error('Streaming error:', error.message);
}
throw error;
}
}
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ.get("OPENROUTER_API_KEY"),
)
def stream_chat(prompt: str, model: str = "anthropic/claude-3.5-sonnet"):
stream = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
stream=True,
)
full_response = ""
for chunk in stream:
if chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
full_response += content
print(content, end="", flush=True)
print() # New line
return full_response
import { useState } from 'react';
function StreamingChat() {
const [response, setResponse] = useState('');
const [isStreaming, setIsStreaming] = useState(false);
async function handleSubmit(prompt: string) {
setIsStreaming(true);
setResponse('');
try {
const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: prompt }],
stream: true,
}),
});
const reader = res.body?.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader!.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(line => line.trim());
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
const content = parsed.choices[0]?.delta?.content || '';
setResponse(prev => prev + content);
} catch (e) {
// Skip invalid JSON
}
}
}
}
} catch (error) {
console.error('Streaming error:', error);
} finally {
setIsStreaming(false);
}
}
return (
<div>
<textarea
value={response}
readOnly
rows={20}
cols={80}
placeholder="Response will appear here..."
/>
<button onClick={() => handleSubmit('Explain AI')}>
{isStreaming ? 'Streaming...' : 'Send'}
</button>
</div>
);
}
const tools = [
{
type: 'function',
function: {
name: 'get_weather',
description: 'Get current weather for a location',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'City name, e.g. San Francisco',
},
unit: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
},
},
required: ['location'],
},
},
},
];
async function chatWithFunctions() {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages: [
{ role: 'user', content: 'What is the weather in Tokyo?' }
],
tools,
tool_choice: 'auto',
});
const message = completion.choices[0].message;
if (message.tool_calls) {
for (const toolCall of message.tool_calls) {
console.log('Function:', toolCall.function.name);
console.log('Arguments:', toolCall.function.arguments);
// Execute function
const args = JSON.parse(toolCall.function.arguments);
const result = await getWeather(args.location, args.unit);
// Send result back
const followUp = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages: [
{ role: 'user', content: 'What is the weather in Tokyo?' },
message,
{
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result),
},
],
tools,
});
console.log(followUp.choices[0].message.content);
}
}
}
async function multiStepFunctionCall(userQuery: string) {
const messages = [{ role: 'user', content: userQuery }];
let iterationCount = 0;
const maxIterations = 5;
while (iterationCount < maxIterations) {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages,
tools,
tool_choice: 'auto',
});
const message = completion.choices[0].message;
messages.push(message);
if (!message.tool_calls) {
// No more function calls, return final response
return message.content;
}
// Execute all function calls
for (const toolCall of message.tool_calls) {
const functionName = toolCall.function.name;
const args = JSON.parse(toolCall.function.arguments);
// Execute function (implement your function registry)
const result = await executeFunctionCall(functionName, args);
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result),
});
}
iterationCount++;
}
throw new Error('Max iterations reached');
}
import { encoding_for_model } from 'tiktoken';
interface CostEstimate {
promptTokens: number;
completionTokens: number;
promptCost: number;
completionCost: number;
totalCost: number;
}
const modelPricing = {
'anthropic/claude-3.5-sonnet': { input: 3.00, output: 15.00 }, // per 1M tokens
'anthropic/claude-3-haiku': { input: 0.25, output: 1.25 },
'openai/gpt-4-turbo': { input: 10.00, output: 30.00 },
'openai/gpt-3.5-turbo': { input: 0.50, output: 1.50 },
'google/gemini-flash-1.5': { input: 0.075, output: 0.30 },
};
function estimateCost(
prompt: string,
expectedCompletion: number,
model: string
): CostEstimate {
const encoder = encoding_for_model('gpt-4'); // Approximation
const promptTokens = encoder.encode(prompt).length;
const completionTokens = expectedCompletion;
const pricing = modelPricing[model] || { input: 0, output: 0 };
const promptCost = (promptTokens / 1_000_000) * pricing.input;
const completionCost = (completionTokens / 1_000_000) * pricing.output;
return {
promptTokens,
completionTokens,
promptCost,
completionCost,
totalCost: promptCost + completionCost,
};
}
// Usage
const estimate = estimateCost(
'Explain quantum computing',
500, // Expected response tokens
'anthropic/claude-3.5-sonnet'
);
console.log(`Estimated cost: $${estimate.totalCost.toFixed(4)}`);
async function budgetOptimizedChat(
prompt: string,
maxCostPerRequest: number = 0.01 // $0.01 max
) {
// Estimate with expensive model
const expensiveEstimate = estimateCost(
prompt,
1000,
'anthropic/claude-3.5-sonnet'
);
let selectedModel = 'anthropic/claude-3.5-sonnet';
if (expensiveEstimate.totalCost > maxCostPerRequest) {
// Try cheaper models
const cheapEstimate = estimateCost(
prompt,
1000,
'anthropic/claude-3-haiku'
);
if (cheapEstimate.totalCost > maxCostPerRequest) {
selectedModel = 'google/gemini-flash-1.5';
} else {
selectedModel = 'anthropic/claude-3-haiku';
}
}
console.log(`Selected model: ${selectedModel}`);
const completion = await client.chat.completions.create({
model: selectedModel,
messages: [{ role: 'user', content: prompt }],
});
return completion.choices[0].message.content;
}
async function batchProcess(prompts: string[], model: string) {
// Process multiple prompts in parallel with rate limiting
const concurrency = 5;
const results = [];
for (let i = 0; i < prompts.length; i += concurrency) {
const batch = prompts.slice(i, i + concurrency);
const batchResults = await Promise.all(
batch.map(prompt =>
client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
max_tokens: 500, // Limit tokens to control cost
})
)
);
results.push(...batchResults);
// Rate limiting delay
if (i + concurrency < prompts.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
return results;
}
const modelFallbackChain = [
'anthropic/claude-3.5-sonnet',
'openai/gpt-4-turbo',
'anthropic/claude-3-haiku',
'google/gemini-flash-1.5',
];
async function chatWithFallback(
prompt: string,
maxRetries: number = 3
): Promise<string> {
for (const model of modelFallbackChain) {
try {
console.log(`Trying model: ${model}`);
const completion = await client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
max_tokens: 2000,
});
return completion.choices[0].message.content || '';
} catch (error) {
console.warn(`Model ${model} failed:`, error);
// Continue to next model
if (model === modelFallbackChain[modelFallbackChain.length - 1]) {
throw new Error('All models failed');
}
}
}
throw new Error('No models available');
}
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 5
): Promise<T> {
let lastError: Error;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
// Check if rate limit error
if (error.status === 429) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error; // Non-retryable error
}
}
}
throw lastError!;
}
// Usage
const result = await retryWithBackoff(() =>
client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: 'Hello' }],
})
);
const systemPrompts = {
concise: 'You are a helpful assistant. Be concise and direct.',
detailed: 'You are a knowledgeable expert. Provide comprehensive answers with examples.',
code: 'You are an expert programmer. Provide clean, well-commented code with explanations.',
creative: 'You are a creative writing assistant. Be imaginative and engaging.',
};
async function chatWithPersonality(
prompt: string,
personality: keyof typeof systemPrompts
) {
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [
{ role: 'system', content: systemPrompts[personality] },
{ role: 'user', content: prompt },
],
});
return completion.choices[0].message.content;
}
async function fewShotClassification(text: string) {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-turbo',
messages: [
{
role: 'system',
content: 'Classify text sentiment as positive, negative, or neutral.',
},
{ role: 'user', content: 'I love this product!' },
{ role: 'assistant', content: 'positive' },
{ role: 'user', content: 'This is terrible.' },
{ role: 'assistant', content: 'negative' },
{ role: 'user', content: 'It works fine.' },
{ role: 'assistant', content: 'neutral' },
{ role: 'user', content: text },
],
});
return completion.choices[0].message.content;
}
async function reasoningTask(problem: string) {
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [
{
role: 'user',
content: `${problem}\n\nLet's solve this step by step:\n1.`,
},
],
max_tokens: 3000,
});
return completion.choices[0].message.content;
}
class RateLimitedClient {
private requestQueue: Array<() => Promise<any>> = [];
private processing = false;
private requestsPerMinute = 60;
private requestInterval = 60000 / this.requestsPerMinute;
async enqueue<T>(request: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.requestQueue.push(async () => {
try {
const result = await request();
resolve(result);
} catch (error) {
reject(error);
}
});
this.processQueue();
});
}
private async processQueue() {
if (this.processing || this.requestQueue.length === 0) return;
this.processing = true;
while (this.requestQueue.length > 0) {
const request = this.requestQueue.shift()!;
await request();
await new Promise(resolve => setTimeout(resolve, this.requestInterval));
}
this.processing = false;
}
}
// Usage
const rateLimitedClient = new RateLimitedClient();
const result = await rateLimitedClient.enqueue(() =>
client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: 'Hello' }],
})
);
async function analyzeImage(imageUrl: string, question: string) {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-vision-preview',
messages: [
{
role: 'user',
content: [
{ type: 'text', text: question },
{ type: 'image_url', image_url: { url: imageUrl } },
],
},
],
max_tokens: 1000,
});
return completion.choices[0].message.content;
}
// Usage
const result = await analyzeImage(
'https://example.com/image.jpg',
'What objects are in this image?'
);
async function compareImages(imageUrls: string[]) {
const completion = await client.chat.completions.create({
model: 'openai/gpt-4-vision-preview',
messages: [
{
role: 'user',
content: [
{ type: 'text', text: 'Compare these images and describe the differences:' },
...imageUrls.map(url => ({
type: 'image_url' as const,
image_url: { url },
})),
],
},
],
});
return completion.choices[0].message.content;
}
interface ErrorResponse {
error: {
message: string;
type: string;
code: string;
};
}
async function robustCompletion(prompt: string) {
try {
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: prompt }],
});
return completion.choices[0].message.content;
} catch (error: any) {
// Rate limit errors
if (error.status === 429) {
console.error('Rate limit exceeded. Please wait.');
throw new Error('RATE_LIMIT_EXCEEDED');
}
// Invalid API key
if (error.status === 401) {
console.error('Invalid API key');
throw new Error('INVALID_API_KEY');
}
// Model not found
if (error.status === 404) {
console.error('Model not found');
throw new Error('MODEL_NOT_FOUND');
}
// Server errors
if (error.status >= 500) {
console.error('OpenRouter server error');
throw new Error('SERVER_ERROR');
}
// Unknown error
console.error('Unknown error:', error);
throw error;
}
}
class LoggingClient {
async chat(prompt: string, model: string) {
const startTime = Date.now();
console.log('[Request]', {
timestamp: new Date().toISOString(),
model,
promptLength: prompt.length,
});
try {
const completion = await client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
});
const duration = Date.now() - startTime;
console.log('[Response]', {
timestamp: new Date().toISOString(),
duration,
usage: completion.usage,
finishReason: completion.choices[0].finish_reason,
});
return completion;
} catch (error) {
console.error('[Error]', {
timestamp: new Date().toISOString(),
duration: Date.now() - startTime,
error,
});
throw error;
}
}
}
Model Selection :
Cost Optimization :
Streaming :
Error Handling :
Prompt Engineering :
Rate Limiting :
❌ Exposing API keys in frontend :
// WRONG - API key exposed
const client = new OpenAI({
baseURL: 'https://openrouter.ai/api/v1',
apiKey: 'sk-or-v1-...', // Exposed!
});
✅ Correct - Server-side proxy :
// Backend proxy
app.post('/api/chat', async (req, res) => {
const { prompt } = req.body;
const completion = await client.chat.completions.create({
model: 'anthropic/claude-3.5-sonnet',
messages: [{ role: 'user', content: prompt }],
});
res.json(completion);
});
❌ Not handling streaming errors :
// WRONG - no error handling
for await (const chunk of stream) {
console.log(chunk.choices[0].delta.content);
}
✅ Correct - with error handling :
try {
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
process.stdout.write(content);
}
} catch (error) {
console.error('Stream error:', error);
// Implement retry or fallback
}
❌ Ignoring rate limits :
// WRONG - no rate limiting
const promises = prompts.map(prompt => chat(prompt));
await Promise.all(promises); // May hit rate limits
✅ Correct - with rate limiting :
const results = [];
for (let i = 0; i < prompts.length; i += 5) {
const batch = prompts.slice(i, i + 5);
const batchResults = await Promise.all(batch.map(chat));
results.push(...batchResults);
await new Promise(r => setTimeout(r, 1000)); // Delay between batches
}
const responseCache = new Map<string, string>();
async function cachedChat(prompt: string, model: string) {
const cacheKey = `${model}:${prompt}`;
if (responseCache.has(cacheKey)) {
console.log('Cache hit');
return responseCache.get(cacheKey)!;
}
const completion = await client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
});
const response = completion.choices[0].message.content || '';
responseCache.set(cacheKey, response);
return response;
}
async function parallelChat(prompts: string[], model: string) {
const results = await Promise.all(
prompts.map(prompt =>
client.chat.completions.create({
model,
messages: [{ role: 'user', content: prompt }],
})
)
);
return results.map(r => r.choices[0].message.content);
}
Weekly Installs
95
Repository
GitHub Stars
18
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode75
codex74
gemini-cli74
cursor68
github-copilot68
claude-code66
AI Elements:基于shadcn/ui的AI原生应用组件库,快速构建对话界面
66,200 周安装
Vercel可观测性技能:全面监控Web应用性能、日志、追踪与警报
93 周安装
table-extractor:从PDF和图像提取表格,智能OCR转换CSV/Excel/JSON
93 周安装
Vercel开发最佳实践:Next.js、React服务器组件与AI SDK部署指南
93 周安装
App Store Connect 版本说明生成器 - 自动本地化更新日志与SEO优化
93 周安装
公众号标题优化专家 | 技术/效率工具类内容创作者必备的AI标题诊断与生成工具
93 周安装
TanStack Query 最佳实践:React 服务器状态管理与 TypeScript 类型安全指南
93 周安装
Security :
Monitoring :