Permalink to Chat CompletionsChat Completions

POST /v1/chat/completions 是整套 API 里兼容性最好的一条。请求基本逐字节转发给上游,响应也基本 原样回来,所以只要是 OpenAI 兼容客户端能发的东西,在这里都能发。

bash
curl https://hypit.ai/v1/chat/completions \
  -H "Authorization: Bearer $HYPIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.1-pro",
    "messages": [
      {"role": "system", "content": "用一句话回答。"},
      {"role": "user", "content": "Hypit 是什么?"}
    ]
  }'

Permalink to 配合-openai-sdk配合 OpenAI SDK

python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["HYPIT_API_KEY"],
    base_url=os.environ.get("HYPIT_BASE_URL", "https://hypit.ai/v1"),
)

completion = client.chat.completions.create(
    model="gemini-3.1-pro",
    messages=[{"role": "user", "content": "Hypit 是什么?"}],
    max_tokens=200,
)
print(completion.choices[0].message.content)
print(completion.usage)
js
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.HYPIT_API_KEY,
  baseURL: process.env.HYPIT_BASE_URL ?? "https://hypit.ai/v1",
});

const completion = await client.chat.completions.create({
  model: "gemini-3.1-pro",
  messages: [{ role: "user", content: "Hypit 是什么?" }],
  max_tokens: 200,
});
console.log(completion.choices[0].message.content);

Permalink to 我们读什么转发什么我们读什么,转发什么

只有两个字段是必填的,被网关真正读取的也只有这几个:

字段我们为什么读它
model必填——决定路由
messages必填且非空——遍历它找图片分片
stream选择 SSE 路径
stream_options见下
modalities["text","image"] 让支持出图的对话模型返回像素

其余一切——max_tokensmax_completion_tokenstemperaturetop_pstoptoolstool_choiceresponse_formatreasoning_effort,以及任何我们从没听说过的厂商私有字段——都会 原封不动地送到上游。请求体上限 32 MiB

响应体是上游的原文,只有两处例外:

  1. 厂商的账单字段会被剥掉(creditscostcost_usdbalancebilling 及其同类,顶层和 usage 内部都剥),免得别人家的账本泄漏进你的。你真实的花费以额度计,不在那个字段里。
  2. 如果我们把你的模型名映射成了另一个上游模型名,顶层 "model" 字符串会被改写回你请求的那个名字。 没有映射时,厂商自己的细化名(比如 gpt-4o-2024-08-06)会保留。

usage 本身不做改动——上游报什么你看到什么。我们内部会把它归一化用于计费,但这从不改变线上的字节。

Permalink to 流式流式

设置 "stream": true。响应是 text/event-stream,每个 chunk 是上游帧的原文,流以 data: [DONE] 结束——上游没发这个哨兵时由我们补上。

python
stream = client.chat.completions.create(
    model="gemini-3.1-pro",
    messages=[{"role": "user", "content": "写一首关于纸船的俳句。"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
js
const stream = await client.chat.completions.create({
  model: "gemini-3.1-pro",
  messages: [{ role: "user", content: "写一首关于纸船的俳句。" }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
bash
curl -N https://hypit.ai/v1/chat/completions \
  -H "Authorization: Bearer $HYPIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gemini-3.1-pro","stream":true,
       "messages":[{"role":"user","content":"写一首关于纸船的俳句。"}]}'

Permalink to 流式下的-usage流式下的 usage

如果你完全没有传 stream_options,我们会替你补上 {"include_usage": true},这样最后一帧会带 usage。只要你自己传了 stream_options——包括 {"include_usage": false}——我们就原样照办,不动它。

流一旦开始,既不会故障转移,也不会改状态码

第一个字节上线之后,HTTP 状态码就永远是 200 了。之后发生的失败会以一个 SSE 帧的形式送达,帧里 是标准错误信封,随后是 data: [DONE]

text
data: {"error":{"message":"…","type":"upstream_error","code":"stream_broken","param":null}}

data: [DONE]

请对每一帧检查 error,而不只是看 HTTP 状态码。另外注意:流式过程中断开连接不会让这次请求 免于计费——token 已经生成了。

Permalink to 视觉视觉

图片分片的写法与 OpenAI 一致:

json
{
  "model": "gemini-3.1-pro",
  "messages": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "这张图里有什么?" },
        { "type": "image_url", "image_url": { "url": "https://example.com/boat.png" } }
      ]
    }
  ]
}

http(s) 图片 URL 会由网关下载,在选择上游之前就地改写成 data: URL,这样只接受内联图片的 模型也能用。最多四张图并发抓取,总预算 20 秒,超时返回 400 image_fetch_timeoutdata: URL 原样透传,而且总是更快的那条路。

只允许 httphttpsdata: 三种 scheme,抓取过程逐跳拒绝内网与链路本地地址。

Permalink to 错误错误

状态code含义
400empty_body / invalid_request_body请求体缺失或不是 JSON 对象
400missing_model / missing_messages两者都必填
400invalid_message_content某个 content 无法解码
400invalid_media_url / unsupported_media_url_schemeimage_url.url 未通过策略
400image_fetch_timeout / image_fetch_failed引用的图片抓不到
413request_too_large超过 32 MiB
404model_not_found / no_capable_provider这把 Key 下该模型不可用于对话
502empty_response / empty_stream上游什么都没返回
502stream_broken流提前中断——以 SSE 帧形式送达
503no_available_provider所有候选上游都在冷却中
504request_timeout

上游失败会被分类,而不是原样透出:upstream_error(502)、upstream_rate_limited(429)、 upstream_quota_exhausted(402)、upstream_unauthorized(401)和 upstream_rejected_request (厂商自己的 4xx,消息脱敏后转发)。我们返回的任何内容里都不会出现上游名称、base URL、上游模型名 或凭据。

公共错误码见错误与限流