中文 ▾

OpenAI Python SDK 自定义 base_url:设置与修复

一根电缆从一个服务器套接字移动到另一个套接字,显示了 SDK 的 base_url 切换

OpenAI SDK 允许你通过一个参数覆盖接口。本页面展示了针对此 API 的 OpenAI Python SDK 自定义 base_url 设置、Node 等效设置、流式输出以及导致大多数首次调用失败的错误。

最后更新于

总结

  • 将 base_url 设置为 https://api.apicompatible.com/v1 并将你的密钥作为 api_key 传递;代码中的其他部分无需更改。
  • 模型 id 为 uncensored。保留旧模型名称是导致首次调用失败的最常见原因。
  • 流式输出通过 stream=True 工作,每个流式请求在结束前都会占用一个并发槽位。
  • 环境变量允许你切换接口而无需修改代码。

简短回答

使用两个参数创建客户端:base_url 和 api_key,并继续使用你已熟悉的方法。该 API 的 Base URL 为 https://api.apicompatible.com/v1,模型 ID 为 uncensored,认证方式为标准 Bearer token,SDK 会根据你的密钥自动构建。

from openai import OpenAI

client = OpenAI(
    base_url="https://api.apicompatible.com/v1",
    api_key="YOUR_API_KEY",
)

resp = client.chat.completions.create(
    model="uncensored",
    messages=[
        {"role": "system", "content": "You are a concise assistant."},
        {"role": "user", "content": "Name three uses for a hash map."},
    ],
)

print(resp.choices[0].message.content)
print(resp.usage)

这就是全部更改。SDK 会在 Base URL 后追加 /chat/completions,因此请求将发送至 POST /v1/chat/completions。如果你还没有密钥,API 密钥 页面会说明登录流程和免费试用额度。

使用环境变量设置 base_url

当你未在代码中传递它们时,官方 Python SDK 会从环境中读取 OPENAI_API_KEY 和 OPENAI_BASE_URL。当同一应用程序在开发和生产环境中针对不同的接口运行时,或者当第三方工具为你构建客户端且没有地方编辑构造函数参数时,这很有用。

export OPENAI_BASE_URL="https://api.apicompatible.com/v1"
export OPENAI_API_KEY="YOUR_API_KEY"

python - <<'EOF'
from openai import OpenAI

client = OpenAI()
r = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(r.choices[0].message.content)
EOF

显式参数优先于环境变量。如果调用发送到错误的主机,请检查代码的其他部分是否使用自己的 base_url 构建了客户端,以及 .env 加载器是否在你的脚本读取变量之前运行。

将密钥保留在源代码控制之外。环境变量或密钥管理器中的密钥易于轮换;已提交的文件中的密钥则不然。

Node 中的相同更改

Node 包对同一选项使用驼峰命名。其他所有内容,包括方法名和响应形状,都与你针对任何 OpenAI 格式接口编写的代码匹配。

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.apicompatible.com/v1",
  apiKey: process.env.API_KEY,
});

const completion = await client.chat.completions.create({
  model: "uncensored",
  messages: [{ role: "user", content: "Give me a one-line commit message for a typo fix." }],
});

console.log(completion.choices[0].message.content);

对于不使用任何 SDK 的原始 HTTP 检查,此 curl 调用发送相同的请求,有助于将 SDK 问题与密钥问题分开:

curl https://api.apicompatible.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"uncensored","messages":[{"role":"user","content":"ping"}]}'

流式输出、异步和重试

流式输出使用服务器发送事件(SSE),SDK 会将其转换为迭代器。传入 stream=True 并在增量到达时读取它们。用量数据在流结束时到达,因此需要处理不包含文本的增量。

from openai import OpenAI

client = OpenAI(
    base_url="https://api.apicompatible.com/v1",
    api_key="YOUR_API_KEY",
)

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Explain idempotency in two sentences."}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
print()

异步客户端接受相同的两个参数。将 OpenAI 替换为 AsyncOpenAI,添加 await,并在流上使用 async for。

一旦你同时运行多个请求,就有两个限制需要注意。完整密钥允许 8 个并发请求和每分钟 300 个请求,试用密钥允许 2 个和 60 个。流式请求在流结束前保持其并发槽位,因此从未排空流的停滞消费者会浪费一个槽位。超过限制时,你会收到带有错误代码 rate_limited 或 concurrency 的 HTTP 429。SDK 默认重试失败的请求几次,你可以使用客户端上的 max_retries 更改该设置。带有 Retry-After: 5 标头的 429 表示临时过载,因此等待五秒是正确的响应。

超时、客户端重用和调试发送的内容

创建一次客户端并重复使用它。每个客户端都保留自己的连接池,因此为每个请求构建新的客户端会浪费时间并使流式输出代码更难理解。下面两个选项都在构造函数上设置,并适用于通过该客户端发出的所有调用。

from openai import OpenAI

client = OpenAI(
    base_url="https://api.apicompatible.com/v1",
    api_key="YOUR_API_KEY",
    timeout=60.0,
    max_retries=2,
)

长生成可能需要一段时间,因此非常短的超时会产生看起来像服务故障的错误。选择适合你最长预期答案的超时时间,并为大输出增加超时时间。

当你无法确定请求使用了哪个 URL 或密钥时,开启 SDK 的调试日志。Python 包的最近版本读取 OPENAI_LOG 环境变量,将其设置为 debug 会打印请求行和响应状态。查看当前 SDK 文档以获取你版本中的确切行为,并在之后移除该变量,因为日志可能包含敏感数据。打印的主机应为 api.apicompatible.com。如果显示其他内容,则不同的客户端实例或环境变量正在覆盖你的设置。

最后一个节省时间的习惯:将 base URL、密钥和模型 id 保留在一个配置模块中。当这三者位于一个位置时,切换接口只需一次编辑和一次审查。

导致首次调用失败的错误

大多数首次调用失败来自简短的原因列表。在怀疑服务之前,按顺序检查它。

base URL 缺少 /v1 或有杂散路径

SDK 仅添加端点名称,因此基础 URL 必须以 /v1 结尾。使用 https://api.apicompatible.com 而不带版本段,或粘贴完整的 /v1/chat/completions 路径,通常会导致未找到错误,因为最终 URL 会出错。如果你不确定 SDK 解析出了什么,请打印 client.base_url。

模型 id 仍然是来自其他提供者的名称

此 API 提供一个模型,其 id 为 uncensored。来自先前设置的遗留模型名称是常见的错误原因,当名称位于配置文件而非代码中时,很容易错过。在你的项目中搜索旧字符串。

密钥来自错误的位置

来自其他提供者的密钥在此处不起作用,反之亦然。如果你同时设置 OPENAI_API_KEY 和显式 api_key,则使用显式值。检查哪个值到达了客户端。

你调用了此 API 未提供的接口

该 API 仅支持文本。它暴露了 /v1/chat/completions、/v1/responses 和 /v1/models。没有图像、音频或嵌入接口,也没有微调,因此使用它们的代码路径必须保留在其原始提供者上。成本和权衡分析 涵盖了这种拆分何时合理。

负载过大

最大请求体为 8 MB。长上下文在提示词加补全总计 100,000 token 内没问题,但 base64 块或巨大的粘贴文件可能会先达到请求体限制。

检查连接和你的余额

在调试应用程序代码之前,调用 GET /v1/models。干净的列表意味着密钥和 URL 正确。每个响应还带有 X-Balance-USD 标头,允许你无需单独调用即可监控支出。使用 Python SDK,使用 with_raw_response 读取标头:

from openai import OpenAI

client = OpenAI(
    base_url="https://api.apicompatible.com/v1",
    api_key="YOUR_API_KEY",
)

raw = client.chat.completions.with_raw_response.create(
    model="uncensored",
    messages=[{"role": "user", "content": "ping"}],
)

print(raw.headers.get("X-Balance-USD"))
print(raw.headers.get("X-Request-Id"))
completion = raw.parse()
print(completion.choices[0].message.content)

联系支持时保留 X-Request-Id 值,因为它标识了确切的请求。失败或被拒绝的请求不会收费,因此错误的调用不花钱。定价为每 1M 输入 token $0.15,每 1M 输出 token $0.50,从预付额度中扣除;定价页面 有详细信息,文档 列出了每个参数。

常见问题

使用自定义 base_url 需要特殊的 SDK 吗?

不需要。标准的 OpenAI Python 和 Node 包接受 base_url 选项。你只需更改该选项和密钥,即可保留现有调用。

是 base_url 还是 baseURL?

Python 使用 base_url,Node.js 使用 baseURL。两者取值相同,均为 https://api.apicompatible.com/v1.

我应该发送哪个模型名称?

将 uncensored 作为模型发送。该 API 仅运行一个模型,因此没有其他可选模型。

为什么我只运行少量请求却收到 HTTP 429?

试用密钥允许 2 个并发请求,且打开的流式输出会占用该配额,直到流结束。请关闭或结束流,或切换到完整密钥以支持 8 个并发请求。

数秒内获取密钥

注册,复制 token,更新端点 URL。这就是全部配置。

注册账户