HTTP API
SoyaOS 的線協議——OpenAI 相容的 chat completions 加 Scope 事件通道。
Moon 暴露一個故意小、形狀對齊 OpenAI 的 HTTP 表面:已經講 /v1/chat/completions 的客戶端不改一行程式碼就能用。Scope 事件流在一條並行通道裡同步發,需要的客戶端可以一起訂。
下面所有都在 v0 內穩定;新端點只新增、絕不改形狀。
Base URL
https://moon.example.com/v1
solo(筆電)部署:http://127.0.0.1:7474/v1。
鑑權
Authorization: Bearer <api_key>。用 soyaos auth keys create 簽發。
Solo 不需要鑑權。solo 上 Moon 拒絕任何非 loopback 綁定,保證這一點不被破壞。
POST /v1/chat/completions
OpenAI 相容的 chat completions。請求體形狀幾乎和 OpenAI 一樣;SoyaOS 特有欄位放在 soya: 前綴下,OpenAI 客戶端會靜默忽略。
請求
{
"model": "soya:compo",
"messages": [
{ "role": "user", "content": "給一篇關於 X 的文章列大綱" }
],
"stream": true,
"soya": {
"input": { "topic": "X", "tone": "academic" },
"pin_version": "1.4.0",
"cache_mode": "rw"
}
}
| 欄位 | 必填 | 說明 |
|---|---|---|
model | 是 | soya:<name>[@<version>],或透傳名(claude-…、gpt-…)。 |
messages | 是 | 標準 OpenAI 形狀。最後一條 user 訊息是規範輸入。 |
stream | 否 | 預設 false。true 時走 SSE。 |
temperature | 否 | model 是透傳名時透傳給上游 LLM。 |
soya.input | 否 | 比對 SoyaPack inputs schema 的型別化輸入。存在時優先級高於 messages。 |
soya.pin_version | 否 | pin 到具體 SoyaPack 版本(覆蓋 model 裡的 @version)。 |
soya.cache_mode | 否 | off / read / write / rw。預設 rw。 |
回應(非串流)
{
"id": "run_018f3a4b1c2d3e4f",
"object": "chat.completion",
"created": 1715670703,
"model": "soya:[email protected]",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "{ \"summary\": { ... } }"
},
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 1842, "completion_tokens": 612, "total_tokens": 2454 }
}
content 就是 Agent 的最終產物 JSON——跟 soyaos run 最後一列看到的是同一個東西。只想要純文本的客戶端可以把 Agent 設定成產出字串產物。
回應(串流,stream: true)
SSE,兩個通道用 event: 多工:
event: message
data: { "id":"…","choices":[{"delta":{"content":"…"}}] }
event: scope
data: { "kind":"stage_started","run_id":"…","stage":"outline","ts":"…" }
event: scope
data: { "kind":"tool_called","run_id":"…","tool":"fetch","args":{…},"ts":"…" }
event: message
data: { "id":"…","choices":[{"delta":{"content":"…"}}] }
event: done
data: [DONE]
只懂 OpenAI 的客戶端會忽略 event: scope 列,看到的就是普通 OpenAI 串流。想要可觀測性的客戶端兩個一起訂——Scope 事件裡列出的每條都會即時出現在這裡。
GET /v1/models
{
"object": "list",
"data": [
{ "id": "soya:compo", "object": "model", "owned_by": "soya", "soya": { "kind": "Agent", "version": "1.4.0" } },
{ "id": "soya:newsbeam", "object": "model", "owned_by": "soya", "soya": { "kind": "Agent", "version": "0.7.2" } },
{ "id": "claude-sonnet-4-6","object": "model", "owned_by": "anthropic" }
]
}
OpenAI 形狀;soya: 擴充欄位是額外的。如果 Moon 設定了透傳模型名,也會出現在這裡。
GET /v1/runs/{run_id}
拿一次已完成執行的後設資料。
{
"run_id": "run_018f3a4b1c2d3e4f",
"pack": "soya:[email protected]",
"tenant": "acme",
"started": "2026-05-14T08:11:43.512Z",
"completed":"2026-05-14T08:11:48.731Z",
"ok": true,
"artifacts":[
{ "name": "outline", "schema": "outline.v1", "url": "https://…/outline.json" },
{ "name": "guide", "schema": "guide.v1", "url": "https://…/guide.json" }
],
"cost": { "comet_seconds": 5.2, "upstream_tokens": 2454 }
}
GET /v1/runs/{run_id}/scope
按 NDJSON 重播一次完成執行的 Scope 事件流:
curl -H "Authorization: Bearer $KEY" \
https://moon.example.com/v1/runs/run_018f3a/scope | jq -c .
{"kind":"run_started","run_id":"run_018f3a4b1c2d3e4f","ts":"…"}
{"kind":"stage_started","run_id":"run_018f3a4b1c2d3e4f","stage":"outline","ts":"…"}
…
{"kind":"run_completed","run_id":"run_018f3a4b1c2d3e4f","ok":true,"ts":"…"}
用來排查「執行 X 到底發生了什麼」。當時串流發的事件,事後能完整重播。
POST /v1/packs(push)
soyaos pack push 用的端點。Multipart body 含規範化 bundle + .soya/signature.json。Moon 驗簽、配置不可變版本槽位、回傳:
{
"pack": "soya:[email protected]",
"bundle_sha256": "8d2c…",
"signed_by": "chzealot-2026",
"status": "published"
}
完整流程見簽名與發布 SoyaPack。
錯誤 envelope
所有錯誤共享這個形狀:
{
"error": {
"type": "capability_violation",
"message": "egress to api.anthropic.com:443 not in allowlist",
"code": "EGRESS_NOT_ALLOWED",
"run_id": "run_018f3a4b1c2d3e4f"
}
}
頂層 HTTP 狀態:
| 狀態 | 含義 |
|---|---|
200 | 成功。 |
400 | 請求體格式錯或輸入 schema 不通過。 |
401 | API Key 缺失 / 無效。 |
403 | 已認證但權限不足(例如 capability_violation)。 |
404 | 未知 pack / run / model 名。 |
410 | 套件版本已棄用。 |
429 | 限流(按 Key 或按 pack)。 |
500 | Moon 內部錯。 |
502 | 上游 LLM 錯(body 裡 error.upstream 是上游原文)。 |
限流
每個 API Key:預設 60 次/分鐘;管理員可上調。每個 pack:透過 SoyaPack 的 sla: 區塊設定(v1)。
Webhook
在 moon.config.yaml 裡設定(見自管 Moon)。Payload 是 JSON 陣列的 Scope 事件,Moon 用 HMAC-SHA256 對 body 簽名:
X-Soya-Signature: sha256=…
用每租戶的 webhook secret 驗。
OpenAPI
完整 schema 在 /v1/openapi.json 暴露(鑑權後)。按標準方式生 SDK:
curl -H "Authorization: Bearer $KEY" https://moon.example.com/v1/openapi.json > soya.json
openapi-generator-cli generate -i soya.json -g typescript-fetch -o ./client