ZZD public AI gateway · zzd_public_ai_gateway_v3 Resource: BaaS · 服务与任务 Canonical map: https://zzd.show/docs/ai/map.json Full handbook: https://zzd.show/llms-full.txt REST capabilities: https://zzd.show/api/v1/capabilities/ # 已绑定服务与慢任务 只有原正常主站编辑器上下文已绑定服务、当前动作获准、相关费用/执行条件已满足时, 才执行一次调用。serviceId/action 使用原服务契约给出的准确值,不创造示例 provider。 方法存在不是授权,绑定列表也不是服务器权限判断的替代。 当前隔离作品的 service_auth/active_service_ids 被清空,通用 service/identity 路由不开放; 那里只能报告当前不支持并回原受权编辑器完成受支持动作,不能创建代理、注入 token 或跨域取主站会话。 ```js // recipe: bound-service-once function serviceRuntime(serviceId) { const ctx = (window.seen && window.seen.runtime) || {}; const access = ctx.access || {}; const runtime = window.AppRuntime; if (location.origin !== "https://zzd.show" || access.mode === "isolated_show" || !Array.isArray(access.active_service_ids) || !access.active_service_ids.includes(serviceId) || !runtime || typeof runtime.callService !== "function") { throw new Error("service_transport_or_binding_unavailable"); } return runtime; } async function callBoundServiceOnce(serviceId, action, approvedInput) { return serviceRuntime(serviceId).callService(serviceId, action, approvedInput); } async function startBoundJobOnce(serviceId, action, approvedInput) { const runtime = serviceRuntime(serviceId); if (typeof runtime.callServiceAsync !== "function") throw new Error("async_unavailable"); const handle = await runtime.callServiceAsync(serviceId, action, approvedInput); return {job_id: handle.job_id}; // retain the non-secret job ID, not handle.raw } ``` 这些函数定义本身不调用服务。AI 必须先核对该动作原输入 schema 和授权, 再由一次明确动作调用相应函数;不得为了测试文档执行真实 provider。 注入 AppRuntime 自动使用绑定、session、可用 CSRF 和既有服务上下文; 业务代码不调用 getServiceAuthToken,不复制其返回值,不覆盖 header 或手动传递 token。 同步 POST 到注入的 `service_base` 下 `/{serviceId}/{action}/`,JSON 为实际 params 及注入的 backend_app_id/space_id/show_pk;异步还包含 `async:1`,缺 space_id 在发请求前拒绝。 服务器同步原 envelope 为 `{ok:true,data:{result,mode:"sync",meta},result,mode,meta}`; SDK 按 result 字段是否存在解包:顶层 result 优先,其次 data.result; false、0、空串和 null 保持原值。没有 result 字段时保留原响应,按具体服务契约使用。 异步 SDK handle 为 `{job_id,id,job,mode,meta,raw}`;只保留 job_id 用于恢复。 ## 观察既有任务;退出不再安排读取 复制 [实时页](/docs/ai/branch/baas-realtime/) 的 `boundedRead`,然后: ```js // recipe: observe-job function observeJob(jobId, onValue, onStatus) { const ctx = (window.seen && window.seen.runtime) || {}; const access = ctx.access || {}; const runtime = window.AppRuntime; if (location.origin !== "https://zzd.show" || access.mode === "isolated_show" || !access.space_id || !runtime || typeof runtime.getSpaceJob !== "function") { throw new Error("job_transport_unavailable"); } return boundedRead(() => runtime.getSpaceJob(jobId), onValue, onStatus, job => ["done", "failed", "canceled"].includes(String(job.status).toLowerCase())); } ``` `const stop = observeJob(savedJobId, renderJob, showSafeStatus)`;离开视图调用 stop。 恢复时沿原 job_id 继续只读,不重新 enqueue 或 callServiceAsync。 GET 是注入 `puzzle_base` 下 `/api/space/{spaceId}/jobs/get/?job_id=...`, 返回原 `{ok:true,data:{job_id,kind,status,attempts,result,error,createTime,updateTime}}`, SDK 解为 data。done/failed/canceled 都是终态,failed 不能显示为成功。 原 `pollSpaceJob(handle,{timeoutMs:30000,intervalMs:1200})` 可接受 handle, 但默认 timeout=0(无限),且没有取消接口;本视图生命周期示例使用上面可停的有限 GET。 stop 不取消服务器任务,当前指南不发明取消端点。timeout 或网络错误保留原任务号并报告结果未知。 `SpaceClient.jobs.enqueue` 虽发送 idempotency_key,当前普通服务端不读取它, 不能宣传安全重放、恰好一次或所有 job kind 自动有执行者。服务异步 POST 同样不能假定幂等, 未知提交结果不重复 POST,不声称已执行 provider 或结算费用。