From openrouter-pack
Migrates Python/TypeScript code from direct OpenAI/Anthropic APIs to OpenRouter: updates base_url, api_key, headers, model IDs, and response parsing.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin openrouter-packThis skill is limited to using the following tools:
!`npm list openai 2>/dev/null | head -5`
Migrates OpenAI SDK to OpenRouter via base_url, api_key changes, and model ID prefixes. Supports Python/TypeScript clients for 400+ models including streaming and tools.
Migrates OpenAI, Anthropic, or other LLM SDK code to Groq with model mappings, API diffs, and provider abstraction for zero-downtime traffic shifting.
Guides migration from OpenAI, Gemini, or other LLMs to Anthropic Claude API with mappings, Python code examples, tool use translation, and multi-provider abstraction.
Share bugs, ideas, or general feedback.
!npm list openai 2>/dev/null | head -5
!pip show openai 2>/dev/null | head -5
Migrating to OpenRouter from a direct provider API (OpenAI, Anthropic) is minimal: change base_url and api_key, add two headers. The OpenAI SDK works natively with OpenRouter. This skill covers migrating from direct APIs, switching between models, upgrading SDK versions, and running comparison tests.
# BEFORE: Direct OpenAI
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=200,
)
# AFTER: Via OpenRouter (3 lines changed)
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1", # ← Changed
api_key=os.environ["OPENROUTER_API_KEY"], # ← Changed
default_headers={ # ← Added
"HTTP-Referer": "https://my-app.com",
"X-Title": "my-app",
},
)
response = client.chat.completions.create(
model="openai/gpt-4o", # ← Add provider prefix
messages=[{"role": "user", "content": "Hello"}],
max_tokens=200,
)
# BEFORE: Direct Anthropic SDK
import anthropic
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=200,
messages=[{"role": "user", "content": "Hello"}],
)
content = response.content[0].text
# AFTER: Via OpenRouter (using OpenAI SDK instead of Anthropic SDK)
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={
"HTTP-Referer": "https://my-app.com",
"X-Title": "my-app",
},
)
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet", # OpenRouter model ID
messages=[{"role": "user", "content": "Hello"}],
max_tokens=200,
)
content = response.choices[0].message.content # OpenAI response format
// BEFORE: Direct OpenAI
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// AFTER: Via OpenRouter
const client = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey: process.env.OPENROUTER_API_KEY,
defaultHeaders: {
"HTTP-Referer": "https://my-app.com",
"X-Title": "my-app",
},
});
// Change model from "gpt-4o" to "openai/gpt-4o"
MIGRATION_CHECKLIST = {
"config": [
"base_url changed to https://openrouter.ai/api/v1",
"API key changed to OPENROUTER_API_KEY (sk-or-v1-...)",
"HTTP-Referer and X-Title headers added",
"Model IDs prefixed with provider/ (e.g., openai/gpt-4o)",
],
"code": [
"All client initialization updated",
"Model IDs updated in all routes/configs",
"Error handling covers OpenRouter-specific codes (402, 408)",
"Streaming still works with new endpoint",
"Tool/function calling still works",
],
"testing": [
"Same prompts produce comparable quality output",
"Latency within acceptable range (expect +50-100ms)",
"Token counts match expectations",
"Cost tracking updated for OpenRouter pricing",
"Fallback chain tested",
],
"operations": [
"Credit balance sufficient for expected usage",
"Per-key credit limits configured",
"Monitoring updated to track OpenRouter metrics",
"Alerting on new error codes (402, 408)",
"Rollback plan documented",
],
}
| Direct Provider | OpenRouter ID |
|---|---|
gpt-4o | openai/gpt-4o |
gpt-4o-mini | openai/gpt-4o-mini |
o1 | openai/o1 |
claude-3-5-sonnet-20241022 | anthropic/claude-3.5-sonnet |
claude-3-haiku-20240307 | anthropic/claude-3-haiku |
gemini-2.0-flash | google/gemini-2.0-flash-001 |
llama-3.1-8b-instruct | meta-llama/llama-3.1-8b-instruct |
def compare_migration(prompt: str, old_model: str, new_model: str):
"""Run same prompt through old and new configurations to compare."""
import time
# New: OpenRouter
or_client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "migration-test"},
)
start = time.monotonic()
or_response = or_client.chat.completions.create(
model=new_model,
messages=[{"role": "user", "content": prompt}],
max_tokens=200, temperature=0,
)
or_latency = (time.monotonic() - start) * 1000
return {
"openrouter": {
"model": or_response.model,
"content": or_response.choices[0].message.content[:100],
"tokens": or_response.usage.prompt_tokens + or_response.usage.completion_tokens,
"latency_ms": round(or_latency),
},
}
# Test
result = compare_migration(
"What is 2+2?",
old_model="gpt-4o",
new_model="openai/gpt-4o",
)
print(json.dumps(result, indent=2))
import os
USE_OPENROUTER = os.environ.get("USE_OPENROUTER", "false").lower() == "true"
def get_llm_client():
"""Feature flag for gradual migration."""
if USE_OPENROUTER:
return OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
else:
return OpenAI(api_key=os.environ["OPENAI_API_KEY"])
def get_model_id(model: str) -> str:
"""Map model IDs based on current backend."""
if USE_OPENROUTER and "/" not in model:
MODEL_MAP = {"gpt-4o": "openai/gpt-4o", "gpt-4o-mini": "openai/gpt-4o-mini"}
return MODEL_MAP.get(model, f"openai/{model}")
return model
| Error | Cause | Fix |
|---|---|---|
| 401 after migration | Using old API key with new base_url | Update to OpenRouter API key (sk-or-v1-...) |
model_not_found | Missing provider prefix | Add openai/ or anthropic/ prefix to model ID |
| Different response format | Switched from Anthropic SDK to OpenAI SDK | Update response parsing: .choices[0].message.content |
| Higher latency | OpenRouter proxy overhead | Expected: +50-100ms; use streaming to mask it |