ORBIT API
客户 SDK(JavaScript / Python)
两个零依赖的薄客户端,封装四个公开模拟接口:读取模型、提交调用、按 requestId 核对、流式调用。当前只连接固定模拟接口,不会产生真实模型回答或真实扣款。
获取与安装
SDK 位于代码仓库的 sdk/ 目录:JavaScript 需要 Node.js 22 及以上,Python 需要 3.9 及以上,均只用标准库。把 sdk/js 或 sdk/python 目录复制到你的服务端项目中即可。
SDK 不在 npm 或 PyPI 上发布,请不要从公共包仓库安装同名包。
服务端从环境变量 ORBIT_SIM_BASE_URL(服务 Origin,须为 https)和 ORBIT_SIM_API_KEY(专用模拟密钥)读取配置。密钥只放在你自己的服务端,不要写进网页代码、日志或代码仓库;SDK 不会在输出或错误信息里打印密钥。
快速上手
读取模型、提交一次调用,再用同一个 requestId 核对。requestId 是幂等键,发送前先保存。
sdk/examples/quickstart.mjs
// Node.js 22+. Run from the repository root: node sdk/examples/quickstart.mjs
// Reads ORBIT_SIM_BASE_URL and ORBIT_SIM_API_KEY. Keep the key on your server; never log it.
import { OrbitClient, OrbitError } from '../js/orbit-sim.mjs';
const orbit = new OrbitClient({ baseUrl: process.env.ORBIT_SIM_BASE_URL, apiKey: process.env.ORBIT_SIM_API_KEY });
const { models } = await orbit.models();
console.log('models:', models.map(m => m.id).join(', '));
// requestId is the idempotency key. Save it before sending.
const requestId = process.env.ORBIT_SIM_REQUEST_ID || crypto.randomUUID();
try {
const { replayed, record } = await orbit.generate({ model: 'sim-general', message: 'hello', maxOutputTokens: 64, requestId });
console.log('generate:', record.state, 'cost', record.cost, 'replayed', replayed);
} catch (error) {
if (!(error instanceof OrbitError) || !error.outcomeUnknown) throw error;
// The SDK never re-sends a POST whose outcome is unknown. Check the same requestId instead.
const check = await orbit.lookup(error.requestId);
console.log('unknown outcome:', error.code ?? error.name, 'lookup', check.state);
}
const check = await orbit.lookup(requestId);
console.log('lookup:', check.state);
sdk/examples/quickstart.py
# Python 3.9+. Run from the repository root: python3 sdk/examples/quickstart.py
# Reads ORBIT_SIM_BASE_URL and ORBIT_SIM_API_KEY. Keep the key on your server; never log it.
import os
import sys
import uuid
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "python"))
import orbit_sim # noqa: E402
orbit = orbit_sim.OrbitClient(os.environ["ORBIT_SIM_BASE_URL"], os.environ["ORBIT_SIM_API_KEY"])
models = orbit.models()["models"]
print("models:", ", ".join(m["id"] for m in models))
# request_id is the idempotency key. Save it before sending.
request_id = os.environ.get("ORBIT_SIM_REQUEST_ID") or str(uuid.uuid4())
try:
ack = orbit.generate("sim-general", "hello", 64, request_id=request_id)
print("generate:", ack["record"]["state"], "cost", ack["record"]["cost"], "replayed", str(ack["replayed"]).lower())
except orbit_sim.OrbitError as error:
if not error.outcome_unknown:
raise
# The SDK never re-sends a POST whose outcome is unknown. Check the same request_id instead.
check = orbit.lookup(error.request_id)
print("unknown outcome:", error.code or type(error).__name__, "lookup", check["state"])
print("lookup:", orbit.lookup(request_id)["state"])
运行方式
export ORBIT_SIM_BASE_URL=... ORBIT_SIM_API_KEY=...
node sdk/examples/quickstart.mjsexport ORBIT_SIM_BASE_URL=... ORBIT_SIM_API_KEY=...
python3 sdk/examples/quickstart.py流式调用
onFrame 会依次收到 start、delta、fault 帧,文字只是临时内容。只有 stream() 正常返回时,返回的记录才是最终结果;同一 requestId 再次调用会返回原记录(replayed 为 true)。
sdk/examples/stream.mjs
// Node.js 22+. Run from the repository root: node sdk/examples/stream.mjs
// Reads ORBIT_SIM_BASE_URL and ORBIT_SIM_API_KEY. Keep the key on your server; never log it.
import { OrbitClient } from '../js/orbit-sim.mjs';
const orbit = new OrbitClient({ baseUrl: process.env.ORBIT_SIM_BASE_URL, apiKey: process.env.ORBIT_SIM_API_KEY });
const requestId = process.env.ORBIT_SIM_REQUEST_ID || crypto.randomUUID();
// Delta text is provisional. The record is final only when stream() resolves.
const result = await orbit.stream(
{ model: 'sim-general', message: 'hello', maxOutputTokens: 64, requestId },
{ onFrame: frame => { if (frame.type === 'delta') process.stdout.write(frame.text); } },
);
console.log();
console.log('stream:', result.record.state, 'cost', result.record.cost, 'replayed', result.replayed);
sdk/examples/stream.py
# Python 3.9+. Run from the repository root: python3 sdk/examples/stream.py
# Reads ORBIT_SIM_BASE_URL and ORBIT_SIM_API_KEY. Keep the key on your server; never log it.
import os
import sys
import uuid
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "python"))
import orbit_sim # noqa: E402
orbit = orbit_sim.OrbitClient(os.environ["ORBIT_SIM_BASE_URL"], os.environ["ORBIT_SIM_API_KEY"])
request_id = os.environ.get("ORBIT_SIM_REQUEST_ID") or str(uuid.uuid4())
def show(frame):
# Delta text is provisional. The record is final only when stream() returns.
if frame["type"] == "delta":
print(frame["text"], end="", flush=True)
result = orbit.stream("sim-general", "hello", 64, request_id=request_id, on_frame=show)
print()
print("stream:", result["record"]["state"], "cost", result["record"]["cost"], "replayed", str(result["replayed"]).lower())
重试与超时
- 返回 429 且 execution 为 not_started 时(任何接口),按 Retry-After 秒数等待后重发;等待时间超过 60 秒则直接交给调用方。
- 只读接口(models、lookup)超时、断线或返回 502/503 PUBLIC_SIMULATION_UNAVAILABLE 时,间隔 0.5 秒、1 秒重试。
- generate 和 stream 只要结果未知(execution 为 unknown、超时、断线、响应不符合约定、流被截断)就绝不自动重发。
默认最多自动重试 2 次;单次请求超时 20 秒,流式调用整体超时 60 秒,均可调整。
错误类型
所有错误都继承 OrbitError,带有 code、status、execution 和 requestId。outcomeUnknown(Python 为 outcome_unknown)为真时,表示服务端可能已经执行或计费。
| 错误类 | 错误码(HTTP 状态) |
|---|---|
OrbitInvalidRequestError | PUBLIC_SIMULATION_INVALID (400, 405, 413, 415)SIMULATION_INVALID (400) |
OrbitAuthenticationError | PUBLIC_SIMULATION_UNAUTHENTICATED (401) |
OrbitPermissionError | PUBLIC_SIMULATION_FORBIDDEN (403)SIMULATION_KEY_INVALID (403)SIMULATION_KEY_FORBIDDEN (403)SIMULATION_MODEL_NOT_ALLOWED (403) |
OrbitNotFoundError | PUBLIC_SIMULATION_NOT_FOUND (404)SIMULATION_NOT_FOUND (404) |
OrbitConflictError | SIMULATION_INSUFFICIENT_BALANCE (409)SIMULATION_ACCOUNT_FROZEN (409)SIMULATION_KEY_BUDGET (409)SIMULATION_CAPACITY (409) |
OrbitIdempotencyConflictError | SIMULATION_IDEMPOTENCY_CONFLICT (409) |
OrbitRateLimitError | PUBLIC_SIMULATION_RATE_LIMITED (429)SIMULATION_QUOTA (429) |
OrbitUnavailableError | PUBLIC_SIMULATION_UNAVAILABLE (502, 503) |
OrbitTimeoutError | 没有拿到可用响应,结果未知。 |
OrbitConnectionError | 没有拿到可用响应,结果未知。 |
OrbitProtocolError | 没有拿到可用响应,结果未知。 |
OrbitConfigError | 参数或客户端配置有误,请求未发出。 |
结果未知时怎么办
保留原 requestId,调用 lookup 核对。用同一 requestId 和完全相同的参数再次提交,会得到原记录(replayed 为 true);参数不同会收到 OrbitIdempotencyConflictError。lookup 返回 not_found 不能证明此前没有执行;cost 或 actual 为 null 表示待核对,不是零费用。
当前范围
SDK 版本 0.1.0,只覆盖固定模拟接口(sim-general / sim-reasoning)。OpenAI 兼容端点、工具调用和真实路由开放后会同步更新。