从 Jev 迁移 · API 迁移清单

从 Jev 切换过来的实用清单

把现有的 TypeSafe Jev 集成迁移到 Decision Models 的 Jev 兼容 API:检查配置、测试请求,并有计划地切换流量。

Jev 是 TypeSafe AI 的产品;Decision Models 与 TypeSafe AI 没有关联。

获取 API 密钥了解 EU 层级

切换流量之前

  1. 盘点现有集成

    保存当前配置和有代表性的测试用例。检查问题类型、选项、图像、答案解析和重试逻辑。保留原提供商的配置,以便有计划地回滚。

  2. 设置 base URL 和路径

    文本请求使用 https://api.decisionmodels.io 作为 base URL,以及 POST /v1/systemone。如果你的客户端自己会拼接路径,请去掉已有的 /v1 后缀。请检查实际发出的路径。

  3. 创建单独的 API 密钥

    在控制台中创建 Decision Models 的密钥,并通过 Authorization: Bearer 发送。TypeSafe 的密钥、余额和账户无法转移。请把密钥保存在环境变量中。

  4. 替换模型 ID

    把 TypeSafe 的模型 ID 替换为 Decision Models 中可用的 ID,例如 s1-fast。请查看 GET /v1/models,不要沿用 TypeSafe 的别名。

  5. 明确指定处理区域

    例如设置 S1-Region: eu。取值为 eu 或 global,并受密钥权限限制;不带该请求头时使用密钥的默认值。EU 请求始终留在 EU 境内。在 EU 以外进行 Peer-to-Peer 处理,需要该密钥已同意全球处理。请查阅处理方式与合同条款。

  6. 拆分问题以符合模型限制

    s1-pro 每个请求最多接受 3 个文本问题;s1-fast 接受 1 个,s1-vision 接受 1 个。多出的问题请自行拆分。服务不会悄悄拆分或丢弃它们;超出的问题会返回 400 request_limit_exceeded。另请查看下面的 token 和选项限制。

  7. 检查问题类型和图像

    保持 state,以及以问题名称为键的 questions 对象不变。请使用下面的 criteria 结构。对于图像,通过 POST /v1/multimodal 使用 s1-vision,并在 images 数组中放入恰好一张图像。支持 PNG、JPEG 和 WebP 的 data URL,或公开的 HTTPS URL,解码后不超过 4 MiB、2,000,000 像素。下面的文本示例不测试图像请求。

  8. 读取答案和用量记录

    按问题名称读取 answers 下的结果。记录 usage.input_tokens、usage.output_tokens、usage.decisions 以及返回的 model_version。token 数由模型给出,图像请求也包含图像部分。金额请以控制台中的用量回执为准;不要自行把字符数换算成计费 token。

  9. 测试错误、重试和回滚

    测试无效密钥、余额不足、模型限制、速率限制和超时。请使用下面的错误码。用你自己的用例评估结果,通过验收检查后再切换流量。本地的接口约定测试并不能说明模型质量或生产环境的可用性。

问题类型与当前的输入限制

模型每个问题的输入 token 数问题数选项数
s1-fast8,000116
s1-pro8,192364
s1-vision8,000126

每个问题都包含它的 state、criteria 和提示词模板;s1-vision 还会计入图像部分。请查看完整的输入限制。

迁移时会有哪些变化

核查日期:2026年10月27日。带日期的 Jev 替代方案对比和迁移说明界定了兼容范围:密钥、模型 ID、处理层级、计费、限制、元数据和错误均各自独立。更换模型需要你自行评估输出。我们不声称做到逐字节兼容。

我们的 score 评分标准至少需要两个等级;TypeSafe 公布的 schema 也接受单个等级。超出的问题属于不可重试的错误。

使用 Idempotency-Key 时,正在处理中的请求会返回 202 in_progress。结算完成之后,或结算状态尚不确定时,会返回 409 idempotency_replay_unavailable,附带回执,不会再次扣费,但不提供已存储的答案。如果确认失败、没有答案且未扣费,则允许用相同的密钥和请求体重试。在同一个键下使用不同的请求体,会返回 409 idempotency_conflict。只有在确实要发起一次新的推理时,才使用新的键。

经过测试的文本示例

下面的每个文件都原样针对一个独立的本地 HTTP 模拟服务运行过,该模拟服务实现了我们公布的接口约定。检查内容包括方法、路径、Bearer 认证、S1-Region、JSON 请求体,以及读取带有 usage 和 model_version 的模拟答案。没有发出任何生产环境的推理请求。

请把 DECISIONMODELS_API_KEY 设置为你的密钥。可选的 DECISIONMODELS_BASE_URL 可以指向你本地的模拟服务;否则使用公开的 API base。Python 示例需要 Python 3.10 以及所示的包版本。fetch 示例是兼容 JavaScript 的 TypeScript,使用 Node.js 20.19.4 运行。

LiteLLM 会去掉 typesafe/ 提供商前缀;模拟服务收到的是 s1-fast。只测试了 Ollama 的 Python 客户端,没有测试它的命令行。带类型的 SDK 可能会丢弃 model_version 等额外元数据;直接的 HTTP 示例会保留它们。

curl 7.81.0

于 2026年10月27日 针对我们公布的接口约定测试,使用 curl 7.81.0

curl.sh
curl --fail-with-body --silent --show-error --max-time 15 \
  "${DECISIONMODELS_BASE_URL:-https://api.decisionmodels.io}/v1/systemone" \
  -H "Authorization: Bearer $DECISIONMODELS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'S1-Region: eu' \
  --data '{"model":"s1-fast","state":"Mia owns a red bicycle.","questions":{"color":{"type":"choice","instructions":"Which color is the bicycle?","criteria":{"red":null,"blue":null}}}}'

Python requests 2.32.5

于 2026年10月27日 针对我们公布的接口约定测试,使用 Python requests 2.32.5

requests.py
import os
import requests

base = os.environ.get("DECISIONMODELS_BASE_URL", "https://api.decisionmodels.io")
response = requests.post(
    base + "/v1/systemone",
    headers={
        "Authorization": "Bearer " + os.environ["DECISIONMODELS_API_KEY"],
        "S1-Region": "eu",
    },
    json={
        "model": "s1-fast",
        "state": "Mia owns a red bicycle.",
        "questions": {"color": {
            "type": "choice",
            "instructions": "Which color is the bicycle?",
            "criteria": {"red": None, "blue": None},
        }},
    },
    timeout=15,
)
response.raise_for_status()
print(response.json())

Node.js fetch 20.19.4

于 2026年10月27日 针对我们公布的接口约定测试,使用 Node.js fetch 20.19.4

fetch.ts
// JavaScript-compatible TypeScript; native fetch in Node.js.
const base = process.env.DECISIONMODELS_BASE_URL ?? "https://api.decisionmodels.io";
const response = await fetch(`${base}/v1/systemone`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.DECISIONMODELS_API_KEY}`,
    "Content-Type": "application/json",
    "S1-Region": "eu",
  },
  body: JSON.stringify({
    model: "s1-fast",
    state: "Mia owns a red bicycle.",
    questions: { color: {
      type: "choice",
      instructions: "Which color is the bicycle?",
      criteria: { red: null, blue: null },
    } },
  }),
  signal: AbortSignal.timeout(15000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());

LiteLLM 1.106.4

于 2026年10月27日 针对我们公布的接口约定测试,使用 LiteLLM 1.106.4

litellm.py
import os
import litellm

response = litellm.systemone(
    model="typesafe/s1-fast",
    api_base=os.environ.get("DECISIONMODELS_BASE_URL", "https://api.decisionmodels.io"),
    api_key=os.environ["DECISIONMODELS_API_KEY"],
    extra_headers={"S1-Region": "eu"},
    state="Mia owns a red bicycle.",
    questions={"color": {
        "type": "choice",
        "instructions": "Which color is the bicycle?",
        "criteria": {"red": None, "blue": None},
    }},
    timeout=15,
)
print(response.model_dump())

Ollama Python 0.6.3

于 2026年10月27日 针对我们公布的接口约定测试,使用 Ollama Python 0.6.3

ollama.py
import os
from ollama import Client

client = Client(
    host=os.environ.get("DECISIONMODELS_BASE_URL", "https://api.decisionmodels.io"),
    headers={
        "Authorization": "Bearer " + os.environ["DECISIONMODELS_API_KEY"],
        "S1-Region": "eu",
    },
    timeout=15,
)
response = client.systemone(
    model="s1-fast",
    state="Mia owns a red bicycle.",
    questions={"color": {
        "type": "choice",
        "instructions": "Which color is the bicycle?",
        "criteria": {"red": None, "blue": None},
    }},
)
print(response.model_dump())

客户端文档:LiteLLM System One · Ollama Python 0.6.3。测试仅覆盖所列版本和所示的文本示例。

切换之前先处理好错误

请明确处理这些错误码;完整规则见错误与重试。

下一步

在把生产请求迁移过去之前,请先阅读完整的迁移说明,并运行你自己的测试用例。