参考

HTTP API

统一 `/v1/*` 路由下的鉴权边界、请求方式和返回形态。

如果你已经在用 SDK,通常不需要手写 HTTP 请求。

路由规则

所有 Action 自动映射为 HTTP 路由:

动作                     URL
────                     ───
普通 action              POST /v1/{service}/{action}
GET action               GET  /v1/{service}/{action}
Admin action             POST /v1/{service}/{action}
Admin GET                GET  /v1/{service}/{action}

产品侧路由

路由方法用途
/v1/ai/textPOST文本生成(SDK 通路)
/v1/ai/streamPOSTCityModel LanguageModelV3 模型流
/v1/ai/image/createPOST创建图片生成任务
/v1/ai/image/resultPOST查询图片生成任务
/v1/ai/videoPOST视频生成
/v1/ai/chat/completionsPOSTOpenAI 兼容端点
/v1/ai/modelsGET按身份返回模型目录
/v1/accounts/login/startPOST创建登录流程;返回 input、redirect 或 done
/v1/accounts/login/continuePOST提交 input 登录步骤
/v1/accounts/login/resultGET读取登录结果和 user_token
/v1/accounts/meGET读取当前用户及其已验证的 Bureau
/v1/accounts/oauth/callbackGET第三方 OAuth 回调入口
/v1/{service}/{action}POST通用 Action 调用
/v1/servicesGETService 列表

/v1/accounts/oauth/callback 是给 GitHub / Google 这类第三方 OAuth 平台回跳用的固定地址。产品前端通常不直接调用它。

管理端常用路由

路由方法用途
/v1/bureaus/listGETBureau 列表
/v1/bureaus/createPOST创建 Bureau,必须提供 server_url
/v1/bureaus/pausePOST暂停 Bureau
/v1/bureaus/activatePOST启用 Bureau
/v1/bureaus/archivePOST归档 Bureau
/v1/bureaus/server/updatePOST更新 Bureau 唯一 Server 的服务入口
/v1/bureaus/currentGET按 User Token 解析当前 Bureau
/v1/accounts/tokens/issuePOST签发 user_token
/v1/env/listGET环境变量列表
/v1/env/upsertPOST写入环境变量
/v1/env/removePOST删除环境变量
/v1/env/importPOST批量导入 .env
同一条 /v1/{service}/{action} 路由会根据 bearer 凭证决定身份:
  • 不带 token:guest
  • user_tokenuser
  • 带管理员会话 Token:admin

Action 自己声明允许哪些身份访问。默认是 ["user"];传 [] 表示免登录。

产品侧请求格式

认证头

Authorization: Bearer <user_token>
Content-Type: application/json

同一个 /v1/ai/models

  • user_token 访问时,只返回当前可调用模型
  • 管理员会话 Token 访问时,返回全部代码注册模型,并附带 env_requirements

GET /v1/ai/models

user_token 示例:

{
  "items": [
    {
      "id": "deepseek-v4-flash",
      "name": "DeepSeek V4 Flash",
      "description": "DeepSeek text model",
      "modalities": ["text", "stream"],
      "tags": ["deepseek", "text"],
      "price": ["输入:1 credit / 1K tokens", "输出:3 credits / 1K tokens"],
      "meta": {},
      "reasoning": {
        "efforts": [
          { "id": "low", "name": "低" },
          { "id": "high", "name": "高" }
        ],
        "default_effort": "low"
      }
    }
  ]
}

管理员会话 Token 示例:

{
  "items": [
    {
      "id": "deepseek-v4-flash",
      "name": "DeepSeek V4 Flash",
      "description": "",
      "modalities": ["text", "stream", "openai"],
      "tags": ["deepseek", "text"],
      "price": ["输入:1 credit / 1K tokens", "输出:3 credits / 1K tokens"],
      "meta": {},
      "env_requirements": [
        {
          "key": "DEEPSEEK_API_KEY",
          "description": "deepseek API Key",
          "required": true
        }
      ]
    }
  ]
}

POST /v1/ai/text

请求(SDK 通路):

{
  "model": "deepseek-v4-flash",
  "prompt": "写一段欢迎语",
  "reasoning_effort": "high"
}

reasoning_effort 可选,但传入时必须匹配模型目录中的 reasoning.efforts[].id。请求经过模型 fallback 时,AIService 会按照最终执行模型重新校验该档位。

返回:

{
  "id": "msg_xxx",
  "role": "assistant",
  "parts": [
    { "type": "text", "text": "欢迎使用 Downcity" }
  ]
}

POST /v1/ai/stream

这是 CityModel 使用的版本化 LanguageModelV3 transport。请求包含协议版本、模型 ID 和标准模型调用参数:

{
  "protocol": "downcity-language-model-v1",
  "model_id": "deepseek-v4-flash",
  "call": {
    "prompt": [
      { "role": "user", "content": [{ "type": "text", "text": "你好" }] }
    ]
  },
  "reasoning_effort": "high"
}

Federation 通过 SSE 返回版本化 LanguageModelV3StreamPart。产品代码不应手写该 transport 请求:把模型目录返回的 CityModel 交给 Agent / AI SDK,或者调用 city.ai.stream()。后者在客户端将同一模型流转换成 UIMessageChunk

AI 请求的用户和 Bureau 身份完全由 bearer user_token 解析,请求体不参与身份判断。

POST /v1/ai/chat/completions

OpenAI 兼容端点。接收标准 OpenAI chat/completions 格式:

{
  "model": "deepseek-v4-flash",
  "messages": [{ "role": "user", "content": "写一段欢迎语" }],
  "stream": true
}

返回 SSE 流:

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"欢迎"},"finish_reason":null}]}
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{...}}
data: [DONE]

直接用 OpenAI SDK:

const openai = new OpenAI({
  baseURL: "http://127.0.0.1:43127/v1/ai",
  apiKey: "ub_xxx",
});
await openai.chat.completions.create({
  model: "deepseek-v4-flash",
  messages: [{ role: "user", content: "你好" }],
  stream: true,
});

GET /v1/services

{
  "items": [
    { "id": "ai", "name": "AI", "env": [] },
    { "id": "accounts", "name": "Accounts", "env": [] }
  ]
}

env 用于暴露该 service 声明的运行时环境变量需求,便于管理端或调试界面展示依赖项。

POST /v1/{service}/{action}

{ "text": "你好" }

管理端请求格式

认证头

Authorization: Bearer <admin_session_token>
Content-Type: application/json

POST /v1/bureaus/create

{ "name": "Chrome Extension", "server_url": "https://bureau.example.com" }
{ "bureau_id": "product_id", "name": "Chrome Extension", "server": { "bureau_id": "product_id", "server_url": "https://bureau.example.com", ... }, "state": "active", ... }

POST /v1/accounts/tokens/issue

{
  "bureau_id": "product_id",
  "user_id": "user_123",
  "metadata": { "plan": "pro" },
  "ttl": "7d"
}
{ "user_token": "ub_xxx", "bureau_id": "product_id", "user_id": "user_123", ... }

POST /v1/env/upsert

{ "key": "DEEPSEEK_API_KEY", "value": "sk-xxx" }

常见状态码

状态码什么时候返回
200请求成功
401token 缺失、过期、签名无效
403Token 所属 Bureau 已暂停或没有调用权限
404路由不存在、service 未注册
422model 不存在、action 未命中
500action 逻辑抛出未指定 statusCode 的错误