|
31
|
6
|
5
|
1.0.0-rc.7
|
1.0.0-rc.7
|
1791622739
|
0
|
{"scope":"@juhai","name":& {"scope":"@juhai","name":"client-ai","description":"AI 治理远程客户端:把 ai-route.v1 候选契约的四个操作(路由决策 / 读决策 / 用量上报 / 经 LiteLLM 代执行)实现为对平台端点的调用;未配置或传输失败一律拒绝,不静默回退(缺失清单 G-5;开发计划 §3A CL-7)","license":"UNLICENSED","development_dependencies":{"@types/node":"22.19.20","typescript":"5.9.3","vitest":"2.1.9"},"readme":"# @juhai/client-ai\n\nM7 AI 治理的调用方 SDK:把 `ai-route.v1` 候选契约(`contracts/candidates/contracts/ai-route-api.v1.openapi.json`,pending CHG-004)的四个操作实现为对平台端点的调用。缺失清单 G-5;开发计划 §3A CL-7。\n\nM7 不提供内核端口(`modules.json` `provides: []`),本包不实现 `PlatformPorts`、不声明 `@juhai/kernel` peer;与 `client-public-file` 同形。\n\n```ts\nimport { AI_ROUTE_API_VERSION, AI_USAGE_API_VERSION, AI_INVOKE_API_VERSION, createAiRouteClient } from \"@juhai/client-ai\";\n\nconst ai = createAiRouteClient({ baseUrl: \"https://platform.example.com\", token: () =\u003e serviceToken() });\nconst routed = await ai.route({ api_version: AI_ROUTE_API_VERSION, request: { request_id, capability: \"chat\", purpose, agent_id, requested_by, /* … */ estimated_units: 200 } });\nif (routed.ok \u0026\u0026 routed.value.outcome === \"routed\") {\n // A:自己执行(credential_ref 是 secret:// 引用,材料由宿主经 M2 / OpenBao 解析,本包不碰),再按 attempt 上报\n await ai.reportUsage(routed.value.decision_id, { api_version: AI_USAGE_API_VERSION, attempt: 1, status: \"settled\", input_tokens, output_tokens, cost_units });\n // B:provider=litellm 的路由交给平台经 LiteLLM 执行这一 attempt,平台按 LiteLLM 真实 usage 结算\n const out = await ai.invoke(routed.value.decision_id, { api_version: AI_INVOKE_API_VERSION, attempt: 1, messages: [{ role: \"user\", content: \"…\" }] });\n}\n```\n\n| 方法 | 端点 | 成功时校验 |\n|---|---|---|\n| `route` | `POST /api/v1/ai/route` | `request_id` 回显一致;routed 必须 `audited=true`、`expires_at` 未过、`credential_ref` 是 `secret://` 引用、`credential_lease=null`;rejected 必须 `route=null` 且有 reasons |\n| `get` | `GET /api/v1/ai/route/{decisionId}` | 同上,`decision_id` 回显一致 |\n| `reportUsage` | `POST /api/v1/ai/route/{decisionId}/usage` | `decision_id` / `attempt` / `status` 回显一致,数量是十进制串 |\n| `invoke` | `POST /api/v1/ai/route/{decisionId}/invoke` | 同上,并含 `output.content`;默认超时 90 s(`invokeTimeoutMs`) |\n\n| 情形 | 返回 |\n|---|---|\n| 未配置 `baseUrl`,或非 localhost 的 `http://`、带用户名口令 / 查询串 | `{ ok: false, code: \"AI_CLIENT_NOT_CONFIGURED\" }`,不发请求 |\n| 入参缺 `request_id` / `decisionId` 非法 / `attempt` 非整数 | `AI_CLIENT_INVALID_REQUEST`,不发请求 |\n| 网络错误 / 超时 | `AI_CLIENT_TRANSPORT_FAILED`,`retryable: true` |\n| 非 200 且响应体带稳定码 | 原样透传服务端码(`AI_AUDIT_UNAVAILABLE` / `AI_ROUTE_EXPIRED` / `AI_PROVIDER_UNAVAILABLE` / `AI_REQUEST_INVALID` + `reasons` …),`status` 同带;5xx 为 `retryable: true` |\n| 非 200 且没有稳定码 | 5xx → `AI_CLIENT_REMOTE_UNAVAILABLE`(retryable),4xx → `AI_CLIENT_REMOTE_REJECTED` |\n| 200 但不是契约形状 | `AI_CLIENT_INVALID_RESPONSE`——含「routed 却未审计」「credential_ref 不是引用而像密钥」 |\n\n四个方法永不抛错;`retryable` 只是提示,**本包不自动重试**。服务端对 `request_id`(路由)与 `attempt`(用量 / 代执行)幂等:用**同一个** id / attempt 重发即可,换 id 等于新决策、会重复预留预算。\n\n## 与旧分支的关系\n\n旧 `client-ai`(`edcb7a6d` / `061821c9`,从未进主线)没有迁入:`createAiGatewayClient` 走的是契约落地前的草稿协议(`POST /v1/ai/usage` + `usage_id`),与当前服务端不互通;`createAiRouteLedgerClient` 的 GET 守卫要求 `route` 恒为 `null`,读已路由决策必判失败。本包按当前契约重写,单测对只监听 127.0.0.1 的桩逐一覆盖上表。\n\n## 未做\n\n- 未进发布列车(`governance/release-packages.json` `notInTrain` 登记);CI 发布通道与 G-1 正式登记是前置。\n- 未接任何真实上层消费者;契约仍是候选,ai-route 面只在 development / test 显式启用。\n","repository":{"type":"","url":""}}...
|
1
|
Edit
Delete
|