入口、密钥与当前版本

TypeSafe 面向规则判断、分类和评分,使用 POST https://monkeytheme.com/v1/systemone,返回完整 JSON,不使用聊天消息协议或流式输出。在 注册 或 登录 后,到 密钥管理 选择 TypeSafe 分组密钥。

当前后端仅接受 jev-latest。 模型广场虽列出 jev-1.13.0、jev-1.14.0,目前不承诺固定版本可调用;不要把广场展示当成可调用证据。本文所有请求使用 jev-latest,不保证它当前映射到某个固定版本。需要复现时保存请求、时间和实际返回的 model,并先确认版本支持状态。

示例没有发送真实请求,没有虚构返回值。运行会发送你的数据并可能计费。密钥仅从环境读取,不能放入网页前端或 Git。

bash
export TYPESAFE_API_KEY="YOUR_API_KEY"
powershell
$env:TYPESAFE_API_KEY="YOUR_API_KEY"

使用 Bash/WSL 的最小 cURL 请求(需要已设置环境变量;运行将发送请求):

bash
curl --fail-with-body --connect-timeout 15 --max-time 120   "https://monkeytheme.com/v1/systemone"   -H "Authorization: Bearer $TYPESAFE_API_KEY"   -H "Content-Type: application/json"   --data-binary '{"model":"jev-latest","state":"Please replace my damaged parcel.","questions":{"replacement":{"type":"noul","instructions":"Is a replacement requested?"}}}'

state、questions 与答案结构

请求包含 model、state 和非空对象 questions。state 可为字符串、对象或数组,保存待评估材料;questions 的键是你定义的结果 ID,每题用 instructions 写清要判断什么。返回 answers 使用相同 ID。不要发送 messages、prompt 或 stream=true。图片资产使用顶层 assets。

typecriteria如何读取
noul可选的 true / false 描述对象answers.ID.noul 为 0–1 概率,不是布尔值;业务自行设置阈值
choice选项 ID 到描述的对象,参考协议为 1–255 项answers.ID.choice 是选项 ID;同时检查 confidence 与 probabilities
score按低到高排列的描述数组,参考协议为 1–10 级,从 0 编号answers.ID.score 是概率加权分数,可能是小数;参考 legend、confidence、probabilities
box用 instructions 描述一个具体目标图片定位实验题型;MonkeyAI 当前可用性未验证,不能当成已支持

同一请求内各题独立评估,共享 state;不能要求第二题读取第一题尚未返回的答案。依赖前一步判断的流程应由应用拆成多次调用。confidence 与概率含义不同,0.8 等阈值需要用自己的验证集校准,不能把概率直接当成真假或准确率保证。

Python:判断、分类、评分与文件模式

Python 3 只需标准库。保存为 typesafe_demo.py,文本示例运行 python3 typesafe_demo.py text;文件模式运行 python3 typesafe_demo.py file。图片模式为待验证示例,确认渠道能力后再使用。示例实际打印响应,不预设任何成功答案。

python
import base64
import json
import os
import sys
import urllib.error
import urllib.request
import uuid
from pathlib import Path

# python typesafe_demo.py text|file|image

def evaluate(body):
    request = urllib.request.Request(
        "https://monkeytheme.com/v1/systemone",
        data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
        headers={"Authorization": "Bearer " + os.environ["TYPESAFE_API_KEY"],
                 "Content-Type": "application/json"}, method="POST")
    try:
        with urllib.request.urlopen(request, timeout=120) as response:
            result = json.load(response)
    except urllib.error.HTTPError as error:
        # Do not log credentials or private request contents.
        raise RuntimeError(f"HTTP {error.code}; inspect a redacted error body locally") from error
    if not isinstance(result.get("answers"), dict):
        raise ValueError("Response has no answers object")
    return result

try:
    mode = sys.argv[1]
    body = {"model": "jev-latest", "state": {"note":
        "The delivery arrived damaged. Can someone arrange a replacement this week?"},
        "questions": {
            "replacement": {"type": "noul", "instructions": "Is a replacement requested?",
                "criteria": {"true": "A replacement is requested.",
                             "false": "No replacement is requested."}},
            "route": {"type": "choice", "instructions": "Select the responsible team.",
                "criteria": {"delivery": "Damaged or missing deliveries.",
                             "account": "Account access.", "other": "Other issues."}},
            "priority": {"type": "score", "instructions": "Assess the stated time pressure.",
                "criteria": ["No deadline.", "Within a week.", "Immediate action required."]}}}
    if mode == "file":
        body["state"] = Path("document.txt").read_text(encoding="utf-8")
        body["questions"] = {"renewal": {"type": "noul",
            "instructions": "Does the document require an explicit renewal decision?"}}
    elif mode == "image":
        # Unverified on MonkeyAI: confirm image capability before using this mode.
        ref = str(uuid.uuid4())
        body["state"] = {"photo": ref}
        body["assets"] = {ref: {"type": "image", "format": "png",
            "data_base64": base64.b64encode(Path("input.png").read_bytes()).decode("ascii")}}
        body["questions"] = {"label_visible": {"type": "noul",
            "instructions": "Is a shipping label visible in the image?"}}
    elif mode != "text":
        raise ValueError("Use text, file or image")
    result = evaluate(body)
    print(json.dumps(result, ensure_ascii=False, indent=2))
    if mode == "text":
        probability = result["answers"]["replacement"]["noul"]
        if isinstance(probability, bool) or not isinstance(probability, (int, float)) or not 0 <= probability <= 1:
            raise ValueError("Invalid noul probability")
        # 0.8 is an illustrative policy threshold, not a service guarantee.
        print("Review for replacement:", probability >= 0.8)
    print("usage:", result.get("usage"))
except (OSError, ValueError, KeyError, TypeError, RuntimeError) as error:
    print("TypeSafe request failed:", error, file=sys.stderr)
    sys.exit(1)

Node.js:相同任务,无额外依赖

使用支持 fetch 与 AbortSignal.timeout 的 Node.js 18+(建议维护中的 LTS)。保存为 typesafe_demo.mjs,运行 node typesafe_demo.mjs text、file 或确认能力后的 image。HTTP 错误、JSON 错误、文件读取错误和超时都会走错误分支;不会自动重试产生额外请求。

javascript
import fs from "node:fs/promises";
import {randomUUID} from "node:crypto";

// node typesafe_demo.mjs text|file|image
async function evaluate(body) {
  const key = process.env.TYPESAFE_API_KEY;
  if (!key) throw new Error("Set TYPESAFE_API_KEY");
  const response = await fetch("https://monkeytheme.com/v1/systemone", {
    method: "POST", headers: {Authorization: `Bearer ${key}`, "Content-Type": "application/json"},
    body: JSON.stringify(body), signal: AbortSignal.timeout(120000)
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}; inspect a redacted error body locally`);
  const result = await response.json();
  if (!result.answers || typeof result.answers !== "object" || Array.isArray(result.answers))
    throw new Error("Response has no answers object");
  return result;
}
try {
  const mode = process.argv[2];
  const body = {model: "jev-latest", state: {note:
    "The delivery arrived damaged. Can someone arrange a replacement this week?"},
    questions: {
      replacement: {type: "noul", instructions: "Is a replacement requested?",
        criteria: {true: "A replacement is requested.", false: "No replacement is requested."}},
      route: {type: "choice", instructions: "Select the responsible team.",
        criteria: {delivery: "Damaged or missing deliveries.", account: "Account access.", other: "Other issues."}},
      priority: {type: "score", instructions: "Assess the stated time pressure.",
        criteria: ["No deadline.", "Within a week.", "Immediate action required."]}
    }};
  if (mode === "file") {
    body.state = await fs.readFile("document.txt", "utf8");
    body.questions = {renewal: {type: "noul",
      instructions: "Does the document require an explicit renewal decision?"}};
  } else if (mode === "image") {
    // Unverified on MonkeyAI: confirm image capability before using this mode.
    const ref = randomUUID();
    body.state = {photo: ref};
    body.assets = {[ref]: {type: "image", format: "png",
      data_base64: (await fs.readFile("input.png")).toString("base64")}};
    body.questions = {label_visible: {type: "noul",
      instructions: "Is a shipping label visible in the image?"}};
  } else if (mode !== "text") throw new Error("Use text, file or image");
  const result = await evaluate(body);
  console.log(JSON.stringify(result, null, 2));
  if (mode === "text") {
    const probability = result.answers.replacement.noul;
    if (typeof probability !== "number" || probability < 0 || probability > 1)
      throw new Error("Invalid noul probability");
    console.log("Review for replacement:", probability >= 0.8);
  }
  console.log("usage:", result.usage);
} catch (error) {
  console.error("TypeSafe request failed:", error.message);
  process.exitCode = 1;
}

图片输入与 Box:未验证能力

图片模式展示参考协议的 JSON + Base64 结构:为图片创建 UUID,在 assets 中放 type: image、format: png 和不带 data URL 前缀、不换行的 data_base64,再用 state.photo 引用该 UUID。JPEG 要同步修改文件名和 format 为 jpg 或 jpeg。重复引用可能增加展开后的输入;共用图片尽量只在 state 引用一次。图片 URL 和本地路径不能替代文件字节,也不能假定服务自动下载它们。multipart、SDK 自动上传及压缩请求体在本站未验证。

参考资料描述静态 PNG/JPEG、每边 280–1568 像素、单图 16 MiB、请求体 32 MiB;这些不是已验证的 MonkeyAI 限制或容量承诺,本站及当前别名实际支持的格式、尺寸和上限仍待确认。先校正方向,必要时缩放或补边;Base64 通常增加约三分之一体积。不要据此承诺动画或视频输入。

Box 同样未验证;不能因广场列出 1.14.0 就认定可调用或可定位。待确认后,题目结构为下方示意片段(不是独立请求,也不应直接加入生产示例):

json
{"target": {"type": "box", "instructions": "Locate the shipping label in the referenced image."}}

参考坐标为归一化的 left/top/right/bottom,原点为左上角。对发送图片宽 W、高 H,像素框为 (left×W, top×H, right×W, bottom×H);取整并限制边界,缩放或裁剪后需换算回原图。没有目标时也可能返回框,宜先用 noul 验证目标存在,再决定是否采纳坐标。confidence 可能为 null,不应解释成 0。多个目标拆成明确的单目标问题。上述说明用于理解协议,不代表当前服务已能返回 Box。

长文件与上下文边界

在当前目录准备 UTF-8 的 document.txt,两种语言的 file 模式都会读取内容放入 state,检查文档是否要求明确续约决定。文件名、网址本身不会被读取;PDF、Office 和扫描件须先本地提取文本或 OCR,不要直接把二进制解码成 UTF-8。

参考资料提到整次请求与 state 加单题的 262K Token 上限;需确认 MonkeyAI 当前 jev-latest 的上下文上限、入口大小或长上下文计费,不能将参考协议上限视为本站容量保证。输入预算包括 state、问题及图片,字符数不是 Token 数。逐段处理超大文件,给每段稳定 ID,在应用中汇总;注意跨段依赖可能影响判断。不要将某段未提到条款误判为整份文档不存在条款。

用量与计费

读取响应中的 usage.input_tokens、usage.output_tokens,并到 实时价格 与账户用量记录核对。清单中 TypeSafe 采用 Token 计费且输出标价为零;实际有效费率仍取决于当前分组规则。若规则确认为仅输入计费,估算为输入 Token ÷ 1,000,000 × 每百万输入有效价格。不要用四舍五入的显示价精确对账,也不要未经确认宣称图片无附加费或长上下文费率不变。图像会影响输入用量;失败或重试的计费应查记录,不凭空推断。

排错与上线前检查

现象检查与处理
401 / 403环境变量、完整密钥、有效期、余额和 TypeSafe 分组;不要打印密钥
404 / 协议错误固定使用 /v1/systemone;Chat、Responses、Messages 都不能代替它
model_not_found / 固定版本被拒当前仅使用 jev-latest;不保证固定版本可用,不要循环重试同一版本
400 / 422检查非空 questions、state、type、instructions、criteria;不发送 stream;图片还需核对引用与编码
413减小文件或图片,考虑 Base64 膨胀及网关限制,拆分请求
max_tokens_exceeded同时缩减 state、问题与图片,避免重复引用;确认实际上下文上限
429 / 503降低并发,按 Retry-After(如有)做有上限退避;持续失败时保留脱敏请求 ID 给支持人员
超时 / 非 JSON / answers 缺失标记失败,保留脱敏状态与内容类型;核查网关响应,不构造假的答案

使用前请确认别名映射、响应字段、图片和 Box 能力、输入限制、运行时兼容性与实际计费;文本请求成功不代表所有能力均可用。