对话功能根据一段对话生成文本。Sakuranbo AI 有两个对话端点。它们运行相同的模型,计费方式也相同,只是请求和响应的格式不同。
POST /v1/chat/completions兼容 OpenAI Chat Completions API。POST /v1/messages兼容 Anthropic Messages API。
两个端点都需要通过 Authorization: Bearer 发送 API 密钥。详见认证。POST /v1/messages 还接受 x-api-key 请求头中的密钥,Anthropic SDK 就是把密钥放在这个请求头里。
本页的示例使用 sakuranbo/cherrytext-1.0-flash。如需查找文本模型,请调用 GET /v1/models 并带上 task=chat。详见模型 API。非文本模型会返回 404 和 model_not_found。
使用官方 SDK
章节标题“使用官方 SDK”你可以使用官方的 OpenAI 和 Anthropic SDK。把 SDK 指向 Sakuranbo AI,并提供你的 Sakuranbo AI API 密钥。OpenAI SDK 需要包含 /v1 的基础 URL。Anthropic SDK 会自己添加 /v1/messages,因此它们的基础 URL 不包含 /v1。
OpenAI SDK(TypeScript):
import OpenAI from "openai";
const apiKey = process.env.SAKURANBO_API_KEY;if (!apiKey) throw new Error("Set the SAKURANBO_API_KEY environment variable.");
const client = new OpenAI({ baseURL: "https://api.sakuranbo.ai/v1", apiKey,});
const completion = await client.chat.completions.create({ model: "sakuranbo/cherrytext-1.0-flash", messages: [ { role: "system", content: "You are a concise assistant." }, { role: "user", content: "Summarize the benefits of prepaid API credits in two lines." } ], max_tokens: 2048});
console.log(completion.choices[0]?.message.content);OpenAI SDK(Python):
import os
from openai import OpenAI
client = OpenAI( base_url="https://api.sakuranbo.ai/v1", api_key=os.environ["SAKURANBO_API_KEY"],)
completion = client.chat.completions.create( model="sakuranbo/cherrytext-1.0-flash", messages=[ { "role": "system", "content": "You are a concise assistant.", }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ], max_tokens=2048,)
print(completion.choices[0].message.content)Anthropic SDK(TypeScript):
import Anthropic from "@anthropic-ai/sdk";
const apiKey = process.env.SAKURANBO_API_KEY;if (!apiKey) throw new Error("Set the SAKURANBO_API_KEY environment variable.");
// Pass the key as authToken too, so the SDK does not send ANTHROPIC_AUTH_TOKEN from the environment.const client = new Anthropic({ baseURL: "https://api.sakuranbo.ai", apiKey, authToken: apiKey,});
const message = await client.messages.create({ model: "sakuranbo/cherrytext-1.0-flash", max_tokens: 2048, system: "You are a concise assistant.", messages: [ { role: "user", content: "Summarize the benefits of prepaid API credits in two lines." } ]});
console.log(message.content);Anthropic SDK(Python):
import os
import anthropic
api_key = os.environ["SAKURANBO_API_KEY"]
# Pass the key as auth_token too, so the SDK does not send ANTHROPIC_AUTH_TOKEN from the environment.client = anthropic.Anthropic( base_url="https://api.sakuranbo.ai", api_key=api_key, auth_token=api_key,)
message = client.messages.create( model="sakuranbo/cherrytext-1.0-flash", max_tokens=2048, system="You are a concise assistant.", messages=[ { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ],)
print(message.content)请从你的服务器调用 API。不要把 API 密钥放进在浏览器中运行的代码里。
OpenAI 格式
章节标题“OpenAI 格式”POST /v1/chat/completions 接收一个 messages 列表,并返回模型的下一条消息。
curl -X POST "https://api.sakuranbo.ai/v1/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $SAKURANBO_API_KEY" \ -d '{ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant." }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ], "max_tokens": 2048}'const response = await fetch("https://api.sakuranbo.ai/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, }, body: JSON.stringify({ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant." }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ], "max_tokens": 2048 }),});const data = await response.json();console.log(data);import os
import requests
response = requests.post( "https://api.sakuranbo.ai/v1/chat/completions", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", }, json={ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant.", }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ], "max_tokens": 2048, },)print(response.json())生成的消息在 choices[0].message 中,usage 包含 token 数量和费用。详见用量与费用。
请求字段
章节标题“请求字段”| 名称 | 类型 | 必填 | 默认值 | 约束 |
|---|---|---|---|---|
model | string | 是 | ||
messages | object[] | 是 | 至少 1 项 | |
max_tokens | integer | 否 | ||
max_completion_tokens | integer | 否 | ||
temperature | number | 否 | 0 至 2 | |
top_p | number | 否 | 0 至 1 | |
stop | string | string[] | 否 | string[]: 最多 4 项 | |
seed | integer | 否 | 不小于 1 | |
user | string | 否 | 最多 512 个字符 | |
tools | object[] | 否 | 最多 128 项 | |
tool_choice | string | object | 否 | string: 可选值:none, auto, required | |
response_format | object | 否 | ||
n | number | 否 | 可选值:1 | |
stream | boolean | 否 | ||
stream_options | object | null | 否 |
每个模型都设定自己的参数范围和默认值。下表对应示例模型:
| 名称 | 类型 | 必填 | 默认值 | 约束 | 说明 |
|---|---|---|---|---|---|
messages | object[] | 是 | OpenAI Chat Completions 格式的对话消息。 | ||
max_tokens | integer | 否 | 4096 | 1 至 131,072 | 生成 token 数的上限,包含模型用于推理的 token。数值过小可能导致在写出回答之前响应就被截断。余额预留按此值计算。 |
temperature | number | 否 | 1 | 0 至 2 | 采样温度。 |
stream | boolean | 否 | false | 以 Server-Sent Events 流式返回 token。 |
超出范围的 max_tokens 值会返回 400 和 invalid_request。error.details.minimum 和 error.details.maximum 给出上下限。如果请求同时包含 max_tokens 和 max_completion_tokens,则使用 max_completion_tokens。
支持函数工具(tools 和 tool_choice)和 response_format。工具调用会在 choices[0].message.tool_calls 中返回。
消息只能包含文本。其他类型的内容部分(例如图像)会返回 400,error.param 指向该部分。不在字段表中的参数同样会返回 400,error.param 为该参数的名称。
如果 assistant 消息既没有文本也没有 tool_calls 条目,或者 content 数组为空,会返回 400。输出在推理过程中被截断时,可能返回既没有文本也没有工具调用的响应,请不要把这样的响应加入对话历史,并在重试前调高 max_tokens。
Anthropic 格式
章节标题“Anthropic 格式”POST /v1/messages 接收 Anthropic Messages API 格式的对话。
curl -X POST "https://api.sakuranbo.ai/v1/messages" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $SAKURANBO_API_KEY" \ -d '{ "model": "sakuranbo/cherrytext-1.0-flash", "max_tokens": 2048, "system": "You are a concise assistant.", "messages": [ { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ]}'const response = await fetch("https://api.sakuranbo.ai/v1/messages", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, }, body: JSON.stringify({ "model": "sakuranbo/cherrytext-1.0-flash", "max_tokens": 2048, "system": "You are a concise assistant.", "messages": [ { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ] }),});const data = await response.json();console.log(data);import os
import requests
response = requests.post( "https://api.sakuranbo.ai/v1/messages", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", }, json={ "model": "sakuranbo/cherrytext-1.0-flash", "max_tokens": 2048, "system": "You are a concise assistant.", "messages": [ { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ], },)print(response.json())请求字段
章节标题“请求字段”| 名称 | 类型 | 必填 | 默认值 | 约束 |
|---|---|---|---|---|
model | string | 是 | ||
messages | object[] | 是 | 至少 1 项 | |
system | string | object[] | 否 | ||
max_tokens | integer | 是 | ||
temperature | number | 否 | 0 至 1 | |
top_p | number | 否 | 0 至 1 | |
stop_sequences | string[] | 否 | 最多 4 项 | |
stream | boolean | 否 | ||
tools | object[] | 否 | 最多 128 项 | |
tool_choice | object | 否 |
以下几点与 OpenAI 格式不同:
max_tokens为必填。system是顶层字段。角色为system的消息会返回400。temperature的可接受范围遵循 Anthropic API,因此与POST /v1/chat/completions的范围不同。- 不需要
anthropic-version请求头。如果 SDK 发送了它,服务器会忽略。 - 消息只能包含文本和工具块。
image块会返回400。不在字段表中的参数(例如top_k或thinking)同样会返回400。 assistant消息需要非空字符串的content,或包含text或tool_use块的content数组。输出在推理过程中被截断时,响应的content可能是空数组,把它加入对话历史会返回400,请调高max_tokens后重新发送请求。- 所有错误响应体都采用 Anthropic 的格式:
type为error,详细信息在error中,request_id是顶层字段。这包括401、404和405。
流式输出
章节标题“流式输出”把 stream 设为 true,即可以服务器发送事件的形式接收响应。模型一生成首个 token,就会立即发送给你。
curl -N -X POST "https://api.sakuranbo.ai/v1/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $SAKURANBO_API_KEY" \ -d '{ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant." }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ], "max_tokens": 2048, "stream": true, "stream_options": { "include_usage": true }}'const response = await fetch("https://api.sakuranbo.ai/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, }, body: JSON.stringify({ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant." }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ], "max_tokens": 2048, "stream": true, "stream_options": { "include_usage": true } }),});const reader = response.body!.getReader();const decoder = new TextDecoder();while (true) { const { done, value } = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value, { stream: true }));}import os
import requests
response = requests.post( "https://api.sakuranbo.ai/v1/chat/completions", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", }, json={ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant.", }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ], "max_tokens": 2048, "stream": True, "stream_options": { "include_usage": True, }, }, stream=True,)for line in response.iter_lines(decode_unicode=True): if line: print(line)在 OpenAI 格式中,每个事件是一行 data:,内容为 chat.completion.chunk 对象,choices[].delta 携带新增的文本。流以 data: [DONE] 结束。包含费用的 usage 对象会出现在最后一个 choices 数组为空的数据块中,并且仅当你把 stream_options.include_usage 设为 true 时才会出现。
在 Anthropic 格式中,每个事件有一行 event: 和一行 data:。事件按以下顺序出现:
message_start- 对于每个内容块:
content_block_start、一个或多个content_block_delta事件,以及content_block_stop message_delta,其中有stop_reason和usage(包含费用)message_stop
Anthropic SDK 只会根据它们已知的字段构建最终消息,因此其中没有 usage.cost。如需读取费用,请读取 message_delta 事件中的 usage。
流开始之前的失败(例如认证或校验错误)会返回带有对应 HTTP 状态码的普通 JSON 错误。流开始之后的失败无法再改变状态码,状态码仍为 200。在 OpenAI 格式中,流会发送一行带有 error 对象的 data:,然后发送 data: [DONE]。在 Anthropic 格式中,流会发送 event: error 事件,且不会再发送 message_stop。
用量与费用
章节标题“用量与费用”非流式响应在两处报告费用,并在 usage 中给出 token 数量:
usage.cost以amount_micro_usd给出 micro USD 金额,并以amount_usd给出十进制字符串形式的金额。X-Usage-Cost-Usd响应头包含同样的 USD 字符串。usage包含 token 数量。在 OpenAI 格式中是prompt_tokens、completion_tokens、total_tokens和prompt_tokens_details.cached_tokens。在 Anthropic 格式中是input_tokens、output_tokens和cache_read_input_tokens。input_tokens不包含缓存的 token。
流式响应没有 X-Usage-Cost-Usd 响应头。请改为从最后一个 usage 对象读取费用。
计费
章节标题“计费”请求运行之前,Sakuranbo AI 会从你的余额中预留一笔金额。它根据输入的大小和 max_tokens 预估这笔金额。请求结束时,会按真实的 token 数量计算费用,其余的预留回到你的余额。在模型一侧失败且尚未生成任何输出的请求不会计费。
- 由于预留使用
max_tokens,即使请求的真实费用在你的余额之内,它也可能被以402拒绝。请把max_tokens设为你需要的长度。 - 如果流在模型开始生成输出之后中断,即使流以错误结束,该请求也会计费。输出包括推理输出,无论是否已送达你都会计费。Anthropic 格式不返回推理输出,因此即使你没有收到任何文本,请求也可能计费。如果最终的 token 数量没有到达,费用会按预估值计算。输入按预留时使用的上限计算,输出按已经生成的输出(包括推理输出和工具调用)的大小计算,因此费用可能高于真实用量,但绝不会超过预留的金额。
- 如果你在流结束之前关闭连接,请求仍会运行到结束,你需要为生成的全部输出付费。
- 对话请求不使用
Idempotency-Key。重试的请求是一个新请求,会再次计费。
价格请查看模型目录中的模型页面和定价页面,也可以在运行请求之前用 POST /v1/estimate 计算价格。详见费用预估 API。如需读取余额,请参阅余额与 API 密钥限额。
错误
章节标题“错误”| 代码 | HTTP 状态码 | 类型 | 说明 |
|---|---|---|---|
invalid_request | 400 | invalid_request_error | 请求格式有误或取值超出范围。如果有效取值已知,会列在 details.allowed 中。 |
unauthorized | 401 | authentication_error | API 密钥缺失、格式有误、未知、已被吊销或已过期。响应不会说明属于哪一种情况。 |
balance_insufficient | 402 | insufficient_balance | 可用余额低于该请求需要预留的金额。error.details.limit_source 为 balance。 |
key_limit_exceeded | 402 | insufficient_balance | 该请求会使 API 密钥超出其支出上限。error.details.limit_source 为 key,error.details.resets_at 说明计数器何时重新开始。 |
account_suspended | 403 | permission_error | 账户已被停用,图像、视频和对话请求会被拒绝。请联系支持团队。余额、充值、API 密钥和任务仍可使用。 |
model_not_found | 404 | not_found_error | 目录中不存在该模型 ID。对于不是文本模型的模型,对话端点也会返回此代码。 |
payload_too_large | 413 | invalid_request_error | 请求体超过了允许的大小。error.details.max_bytes 为大小上限。 |
rate_limited | 429 | rate_limit_error | 模型提供方正在限制请求。同一地址提交的举报过多时,POST /v1/reports 也会返回该错误代码。请在 Retry-After 响应头给出的时间之后重试。 |
upstream_error | 502 | upstream_error | 上游提供方返回了错误。 |
upstream_unavailable | 503 | overloaded_error | 上游提供方暂时容量不足,请稍后重试。 |
POST /v1/messages 以 Anthropic 的错误格式返回相同的错误代码和 HTTP 状态码。它的
error.type 遵循 Anthropic API,因此可能与“类型”列不同。balance_insufficient 和
key_limit_exceeded 是 billing_error,payload_too_large 是
request_too_large,content_policy_violation 是 invalid_request_error,upstream_error 是
api_error。请根据 error.code 而不是 error.type 判断分支。
对于 429 或 503 响应,请等待 Retry-After 响应头给出的秒数再重试。402 响应带有 error.details.limit_source,其值为 balance 或 key。详见余额与 API 密钥限额。完整的错误代码列表请参阅错误。
所有价格均以 USD 计价,标价已包含我们的加价。