迁移到 OpenHub
本指南帮助你从其他 LLM 平台迁移到 OpenHub。
从 OpenAI 迁移
OpenHub 完全兼容 OpenAI API,只需修改两处配置:
Python
from openai import OpenAI
# 旧版 - OpenAI
client = OpenAI(
api_key="sk-..." # OpenAI API Key
)
# 新版 - OpenHub
client = OpenAI(
api_key="YOUR_OPENHUB_API_KEY", # OpenHub API Key
base_url="https://api.myopenhub.com/v1" # 添加这一行
)
# 其他代码无需修改
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Hello"}]
)Node.js
import OpenAI from 'openai';
// 旧版 - OpenAI
const client = new OpenAI({
apiKey: 'sk-...' // OpenAI API Key
});
// 新版 - OpenHub
const client = new OpenAI({
apiKey: 'YOUR_OPENHUB_API_KEY', // OpenHub API Key
baseURL: 'https://api.myopenhub.com/v1' // 添加这一行
});
// 其他代码无需修改
const response = await client.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello' }]
});从其他平台迁移
通义千问
# 旧版 - 通义千问
import dashscope
response = dashscope.Generation.call(
model='qwen-max',
prompt='你好'
)
# 新版 - OpenHub(使用 OpenAI SDK)
from openai import OpenAI
client = OpenAI(
api_key="YOUR_OPENHUB_API_KEY",
base_url="https://api.myopenhub.com/v1"
)
response = client.chat.completions.create(
model='qwen-max',
messages=[{'role': 'user', 'content': '你好'}]
)文心一言
# 旧版 - 文心一言
import qianfan
response = qianfan.ChatCompletion().do(
model='ERNIE-4.0',
messages=[{'role': 'user', 'content': '你好'}]
)
# 新版 - OpenHub(使用 OpenAI SDK)
from openai import OpenAI
client = OpenAI(
api_key="YOUR_OPENHUB_API_KEY",
base_url="https://api.myopenhub.com/v1"
)
response = client.chat.completions.create(
model='ernie-4.0',
messages=[{'role': 'user', 'content': '你好'}]
)Claude
# 旧版 - Anthropic
import anthropic
client = anthropic.Anthropic(api_key="sk-ant-...")
response = client.messages.create(
model="claude-3-opus-20240229",
messages=[{"role": "user", "content": "Hello"}]
)
# 新版 - OpenHub(使用 OpenAI SDK)
from openai import OpenAI
client = OpenAI(
api_key="YOUR_OPENHUB_API_KEY",
base_url="https://api.myopenhub.com/v1"
)
response = client.chat.completions.create(
model='claude-3-opus',
messages=[{'role': 'user', 'content': 'Hello'}]
)配置 AI Agent 工具
OpenClaw
编辑 .env 文件:
# 旧版
OPENAI_API_KEY=sk-...
# OPENAI_BASE_URL 不设置
# 新版
OPENAI_API_KEY=YOUR_OPENHUB_API_KEY
OPENAI_BASE_URL=https://api.myopenhub.com/v1LangChain
# 旧版
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(
openai_api_key="sk-...",
model_name="gpt-4"
)
# 新版
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(
openai_api_key="YOUR_OPENHUB_API_KEY",
openai_api_base="https://api.myopenhub.com/v1",
model_name="gpt-4"
)AutoGPT
编辑 .env 文件:
# 旧版
OPENAI_API_KEY=sk-...
# 新版
OPENAI_API_KEY=YOUR_OPENHUB_API_KEY
OPENAI_API_BASE=https://api.myopenhub.com/v1模型名称映射
OpenHub 使用简化的模型名称:
| 原平台 | 原模型名称 | OpenHub 模型名称 |
|---|---|---|
| OpenAI | gpt-4-0125-preview | gpt-4 |
| OpenAI | gpt-3.5-turbo-0125 | gpt-3.5-turbo |
| Anthropic | claude-3-opus-20240229 | claude-3-opus |
| Anthropic | claude-3-sonnet-20240229 | claude-3-sonnet |
| 通义千问 | qwen-max-0428 | qwen-max |
| 文心一言 | ERNIE-4.0-8K | ernie-4.0 |
提示
OpenHub 会自动使用最新版本的模型,无需指定具体版本号。
使用智能路由节省成本
迁移到 OpenHub 后,建议使用智能路由功能:
# 不指定具体模型,让 OpenHub 自动选择
response = client.chat.completions.create(
model='auto', # 智能路由
messages=[{'role': 'user', 'content': '写一首诗'}]
)智能路由可以节省 50-70% 的成本。
迁移检查清单
- [ ] 获取 OpenHub API Key
- [ ] 更新
base_url配置 - [ ] 更新
api_key配置 - [ ] 测试 API 调用
- [ ] 更新模型名称(如需要)
- [ ] 考虑使用智能路由
- [ ] 监控成本和性能
常见问题
Q: 迁移需要修改很多代码吗?
A: 不需要。只需修改两处配置:api_key 和 base_url。
Q: 所有 OpenAI SDK 功能都支持吗?
A: 是的。OpenHub 完全兼容 OpenAI API。
Q: 可以同时使用多个平台吗?
A: 可以。创建不同的 client 实例即可。
Q: 迁移后成本会增加吗?
A: 不会。OpenHub 的价格通常更低,使用智能路由可节省 50-70% 成本。
获取帮助
- 查看 API 文档
- 加入 Discord 社区
- 发送邮件到 support@myopenhub.com
// 新版 await fetch('/v1/users/login', { method: 'POST', body: JSON.stringify({ email, password }) });
#### API Keys 管理
| 旧版端点 | 新版端点 | 说明 |
|---------|---------|------|
| `GET /v1/keys` | `GET /v1/keys` | 列出 API Keys |
| `POST /v1/keys` | `POST /v1/keys` | 创建 API Key |
| `DELETE /v1/keys/{id}` | `DELETE /v1/keys/{id}` | 删除 API Key |
| `POST /v1/keys/{id}/rotate` | `POST /v1/keys/{id}/rotate` | 轮换 API Key |
**注意**: 旧版 `/api/keys-v2` 已合并到 `/v1/keys`。
#### 渠道管理
| 旧版端点 | 新版端点 | 说明 |
|---------|---------|------|
| `GET /api/channels` | `GET /v1/admin/channels` | 列出渠道 |
| `POST /api/channels` | `POST /v1/admin/channels` | 创建渠道 |
| `PUT /api/channels/{id}` | `PUT /v1/admin/channels/{id}` | 更新渠道 |
| `DELETE /api/channels/{id}` | `DELETE /v1/admin/channels/{id}` | 删除渠道 |
| `POST /api/channels/{id}/health` | `POST /v1/admin/channels/{id}/health` | 健康检查 |
#### LLM 服务
| 旧版端点 | 新版端点 | 说明 |
|---------|---------|------|
| `GET /v1/llm/models` | `GET /v1/llm/models` | 列出模型 |
| `POST /v1/llm/chat/completions` | `POST /v1/llm/chat/completions` | Chat Completions |
#### 计费
| 旧版端点 | 新版端点 | 说明 |
|---------|---------|------|
| `GET /api/balance` | `GET /v1/billing/balance` | 获取余额 |
| `GET /api/plans` | `GET /v1/billing/plans` | 获取套餐列表 |
#### 统计数据
| 旧版端点 | 新版端点 | 说明 |
|---------|---------|------|
| `GET /api/stats` | `GET /v1/stats` | 获取使用统计 |
#### OAuth
| 旧版端点 | 新版端点 | 说明 |
|---------|---------|------|
| `GET /api/oauth/login` | `GET /v1/oauth/login` | OAuth 登录 |
| `GET /api/oauth/callback` | `GET /v1/oauth/callback` | OAuth 回调 |
#### Webhooks
| 旧版端点 | 新版端点 | 说明 |
|---------|---------|------|
| `GET /api/webhooks` | `GET /v1/webhooks` | 列出 Webhooks |
| `POST /api/webhooks` | `POST /v1/webhooks` | 创建 Webhook |
### 3. 更新响应处理
#### 旧版响应格式(不一致)
```javascript
// 有些端点直接返回数据
const data = await response.json();
// 有些端点返回包装格式
const { success, data, message } = await response.json();新版响应格式(统一)
所有端点使用统一的 ApiResponse<T> 格式:
const response = await fetch('/v1/users/me', {
headers: { 'Authorization': `Bearer ${token}` }
});
const result = await response.json();
// result 结构:
// {
// success: boolean,
// data: T | null,
// error: { code: string, message: string } | null,
// message: string | null
// }
if (result.success) {
const user = result.data;
// 处理成功响应
} else {
const errorCode = result.error.code;
const errorMessage = result.error.message;
// 处理错误响应
}4. 更新错误处理
新版错误码
| 错误码 | HTTP 状态码 | 说明 |
|---|---|---|
NOT_FOUND | 404 | 资源不存在 |
FORBIDDEN | 403 | 无权访问 |
UNAUTHORIZED | 401 | 未认证或认证失败 |
INVALID_CREDENTIALS | 401 | 用户名或密码错误 |
VALIDATION_ERROR | 400 | 参数验证失败 |
DATABASE_ERROR | 500 | 数据库错误 |
INTERNAL_ERROR | 500 | 内部服务器错误 |
迁移示例:
// 旧版
try {
const response = await fetch('/v1/keys');
if (response.status === 404) {
console.error('Not found');
}
} catch (error) {
console.error('Error:', error);
}
// 新版
try {
const response = await fetch('/v1/keys');
const result = await response.json();
if (!result.success) {
switch (result.error.code) {
case 'NOT_FOUND':
console.error('Resource not found');
break;
case 'FORBIDDEN':
console.error('Access denied');
break;
default:
console.error('Error:', result.error.message);
}
}
} catch (error) {
console.error('Network error:', error);
}5. 检查响应头
v1 端点响应头
X-API-Version: v1
Content-Type: application/json已废弃端点响应头
如果您仍在使用旧版端点,响应会包含以下警告头:
X-Deprecated: true
X-Deprecated-Message: This endpoint is deprecated. Use /v1/{path} instead
X-Sunset-Date: 2026-06-01检查示例:
const response = await fetch('/v1/keys');
const deprecated = response.headers.get('X-Deprecated');
const sunsetDate = response.headers.get('X-Sunset-Date');
if (deprecated === 'true') {
console.warn(`This endpoint is deprecated and will be removed on ${sunsetDate}`);
}前端迁移示例
TypeScript/JavaScript
旧版:
// client.ts
const API_BASE = '/api';
async function getUser() {
const response = await fetch(`${API_BASE}/me`, {
headers: { 'Authorization': `Bearer ${token}` }
});
return response.json();
}新版:
// config.ts
export const API_BASE = '/v1';
// client.ts
import { API_BASE } from './config';
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: {
code: string;
message: string;
};
message?: string;
}
async function getUser() {
const response = await fetch(`${API_BASE}/users/me`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const result: ApiResponse<User> = await response.json();
if (!result.success) {
throw new Error(result.error?.message || 'Unknown error');
}
return result.data;
}Python
旧版:
import requests
API_BASE = "https://api.myopenhub.com/api"
def get_user(token):
response = requests.get(
f"{API_BASE}/me",
headers={"Authorization": f"Bearer {token}"}
)
return response.json()新版:
import requests
from typing import TypedDict, Optional
API_BASE = "https://api.myopenhub.com/v1"
class ApiError(TypedDict):
code: str
message: str
class ApiResponse(TypedDict):
success: bool
data: Optional[dict]
error: Optional[ApiError]
message: Optional[str]
def get_user(token):
response = requests.get(
f"{API_BASE}/users/me",
headers={"Authorization": f"Bearer {token}"}
)
result: ApiResponse = response.json()
if not result["success"]:
error = result.get("error", {})
raise Exception(error.get("message", "Unknown error"))
return result["data"]Rust
旧版:
const API_BASE: &str = "https://api.myopenhub.com/api";
async fn get_user(token: &str) -> Result<User, Error> {
let response = reqwest::Client::new()
.get(format!("{}/me", API_BASE))
.bearer_auth(token)
.send()
.await?;
response.json().await
}新版:
use serde::{Deserialize, Serialize};
const API_BASE: &str = "https://api.myopenhub.com/v1";
#[derive(Deserialize)]
struct ApiResponse<T> {
success: bool,
data: Option<T>,
error: Option<ApiError>,
message: Option<String>,
}
#[derive(Deserialize)]
struct ApiError {
code: String,
message: String,
}
async fn get_user(token: &str) -> Result<User, Error> {
let response = reqwest::Client::new()
.get(format!("{}/users/me", API_BASE))
.bearer_auth(token)
.send()
.await?;
let result: ApiResponse<User> = response.json().await?;
if !result.success {
let error = result.error.unwrap_or_else(|| ApiError {
code: "UNKNOWN_ERROR".to_string(),
message: "Unknown error".to_string(),
});
return Err(Error::Api(error.message));
}
result.data.ok_or(Error::NoData)
}测试迁移
1. 并行测试
在迁移期间,您可以同时调用新旧端点进行对比测试:
async function testMigration() {
// 调用旧版端点
const oldResponse = await fetch('/v1/keys');
const oldData = await oldResponse.json();
// 调用新版端点
const newResponse = await fetch('/v1/keys');
const newResult = await newResponse.json();
// 对比数据
console.assert(
JSON.stringify(oldData) === JSON.stringify(newResult.data),
'Data mismatch between old and new API'
);
}2. 检查废弃警告
async function checkDeprecation() {
const response = await fetch('/v1/keys');
if (response.headers.get('X-Deprecated') === 'true') {
const message = response.headers.get('X-Deprecated-Message');
const sunsetDate = response.headers.get('X-Sunset-Date');
console.warn(`⚠️ ${message}`);
console.warn(`📅 Sunset date: ${sunsetDate}`);
}
}常见问题
Q: 旧版 API 什么时候会被移除?
A: 旧版 API 将在 2026-06-01 被移除。在此之前,旧版 API 将继续工作,但会返回废弃警告头。
Q: 新旧 API 的功能有差异吗?
A: 没有。新版 API 保持与旧版 API 完全相同的功能和业务逻辑,只是路径和响应格式统一了。
Q: 我需要更新 API Key 吗?
A: 不需要。现有的 API Key 在新旧版本中都可以使用。
Q: 响应格式变化会影响我的应用吗?
A: 是的。新版 API 使用统一的 ApiResponse<T> 格式,您需要更新响应处理代码。请参考上面的迁移示例。
Q: 如何知道我的应用是否还在使用旧版 API?
A: 检查 HTTP 响应头中的 X-Deprecated 字段。如果值为 true,说明您正在使用已废弃的端点。
Q: 迁移后性能会有提升吗?
A: 是的。新版 API 进行了数据库查询优化,响应时间平均提升 20-30%。
获取帮助
如果您在迁移过程中遇到问题,请:
- 查看 API 文档
- 查看 GitHub Issues
- 联系技术支持:support@myopenhub.com
更新日志
- 2024-03-12: 发布迁移指南
- 2024-03-12: v1 API 正式发布