@acosmi/sdk-ts 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to `@acosmi/sdk-ts` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.2.0] - 2026-05-29 — 托管模型图片/视频生成
9
+
10
+ Additive minor。公开类型 / 方法签名零移除、零改名。图片/视频生成与文本模型同属托管模型网关(同 `Client`、同 `models:chat` 鉴权面),仅 `capabilities.supports_image_generation` / `supports_video_generation` 的模型可用。计费结算在营销系统,SDK / 网关只负责调用与用量上报。
11
+
12
+ ### Added
13
+
14
+ - **`Client.generateImage(modelID, req, signal?)`** — 同步图片生成,`POST /managed-models/:id/images/generations`,返回 `ImageGenerationResponse`(`url` / `b64_json` / `revised_prompt`)。内部超时与 chat 同级(11min)容纳上游耗时。
15
+ - **`Client.generateVideo(modelID, req, signal?)`** — 创建异步视频任务,`POST /managed-models/:id/videos/generations`,返回 `VideoTaskResponse`(含 `taskId`)。
16
+ - **`Client.pollVideoTask(modelID, taskID, durationSeconds?, signal?)`** — 轮询视频任务,`GET /managed-models/:id/videos/tasks/:taskId`;`durationSeconds` 透传给网关在 `completed` 时上报真物理量(视频秒数)。
17
+ - 新类型:`ImageGenerationRequest` / `ImageGenerationResponse` / `VideoGenerationRequest` / `VideoTaskResponse`。
18
+ - `ModelCapabilities` 新增可选 `supports_image_generation?` / `supports_video_generation?`(上游未声明时为 `undefined`,调用方不得用模型名 substring 推断)。
19
+ - `doJSONFullRaw(method, path, body, signal?, timeoutMs?)` 新增可选 `timeoutMs`(默认 30s,向后兼容),图片生成传 chat 同级超时。
20
+
21
+ ### 网关侧适配范围
22
+
23
+ - OpenAI 兼容图片端点 + 火山引擎(即梦/豆包)视频任务 + **DashScope 通义万相(wanx)原生异步任务 API(图片 + 视频)**。DashScope 万相图片在网关内部建任务并轮询到终态后同步返回 URL(对 SDK 仍是一次 `generateImage`),视频走 `generateVideo` + `pollVideoTask`。
24
+
8
25
  ## [2.1.0] - 2026-05-28 — 远程控制 CrabCode 多接入面
9
26
 
10
27
  Additive minor。公开类型 / 方法签名零移除、零改名。契约见 `docs/audit/sdk-remote-control-contract-2026-05-27.md`。
package/README.md CHANGED
@@ -114,6 +114,62 @@ if (err) {
114
114
 
115
115
  不传 `endUserId` 时网关从认证身份 HMAC-SHA256 自动派生 32 字符稳定 id, 业务无感知。流式 + 同步 + Anthropic + OpenAI 四条路径均支持。
116
116
 
117
+ ## 图片 / 视频生成(托管模型网关,v2.2+)
118
+
119
+ 图片生成、视频生成与文本对话**同属托管模型网关**(同一个 `Client`、同一套 `models:chat` 鉴权面),**不是工作流**。只有 `capabilities.supports_image_generation` / `supports_video_generation` 为真的模型可调;计费结算在营销系统,SDK / 网关只做调用与用量上报。
120
+
121
+ **先按 capability 筛模型**:
122
+
123
+ ```ts
124
+ const models = await client.listModels();
125
+ const imageModel = models.find((m) => m.capabilities?.supportsImageGeneration);
126
+ const videoModel = models.find((m) => m.capabilities?.supportsVideoGeneration);
127
+ ```
128
+
129
+ ### 图片生成(同步)
130
+
131
+ `generateImage` 一次调用直接拿图(内部超时与 chat 同级 11min,容纳上游耗时;DashScope 万相图片在网关内部建任务并轮询到终态后同步返回,对 SDK 仍是一次调用)。
132
+
133
+ ```ts
134
+ const img = await client.generateImage(imageModel!.id, {
135
+ prompt: '一只在雪地里奔跑的柴犬,电影感光影',
136
+ width: 1024, // 缺省 1024
137
+ height: 1024, // 缺省 1024
138
+ style: 'cinematic', // 可选
139
+ });
140
+ console.log(img.url ?? img.b64_json); // ImageGenerationResponse: url / b64_json / revised_prompt / requestId
141
+ ```
142
+
143
+ ### 视频生成(异步:建任务 → 轮询)
144
+
145
+ `generateVideo` 返回 `taskId`,再用 `pollVideoTask` 轮询到 `completed`。`durationSeconds` 务必回传创建时的秒数——网关在 `completed` 时据此上报真实视频时长用量。
146
+
147
+ ```ts
148
+ const task = await client.generateVideo(videoModel!.id, {
149
+ prompt: '海浪拍打礁石的慢镜头',
150
+ resolution: '1280x720', // 可选
151
+ duration: 5, // 秒
152
+ });
153
+
154
+ let res = task;
155
+ while (res.status !== 'completed' && res.status !== 'failed') {
156
+ await new Promise((r) => setTimeout(r, 3000));
157
+ res = await client.pollVideoTask(videoModel!.id, task.taskId, 5); // 回传 duration=5
158
+ }
159
+ if (res.status === 'failed') throw new Error(res.error);
160
+ console.log(res.videoUrl); // VideoTaskResponse: taskId / status / videoUrl / error / requestId
161
+ ```
162
+
163
+ **请求字段速查**
164
+
165
+ | | 图片 `ImageGenerationRequest` | 视频 `VideoGenerationRequest` |
166
+ | --- | --- | --- |
167
+ | 必填 | `prompt` | `prompt` |
168
+ | 尺寸 | `width` / `height`(缺省 1024) | `resolution`(如 `"1280x720"`) |
169
+ | 其他 | `style` | `duration`(秒) |
170
+
171
+ > 字段是网关**通用契约**;某厂商支持哪些取值由上游模型决定(如万相尺寸 `宽*高` 星号格式由网关代转)。网关适配范围:OpenAI 兼容图片 + 火山引擎(即梦/豆包)视频 + DashScope 通义万相(wanx)原生异步任务(图片+视频)。
172
+
117
173
  ## 双格式红线(设计核心)
118
174
 
119
175
  SDK 同时提供 **Anthropic + OpenAI 两条 endpoint**,**等地位**,对应两个不同下游产品。
@@ -377,6 +433,7 @@ const client = new Client({ serverURL: process.env.ACOSMI_SERVER_URL!, store: ne
377
433
  | ------------ | ------------------------------------------------------------------------------------ |
378
434
  | **Client 构造** | `new Client(cfg)`(同步),`Client.create(cfg)`(async;预加载已有 TokenStore) |
379
435
  | **Chat** | `chat`, `chatStream`, `chatStreamWithUsage`, `chatMessages`, `chatMessagesStream`, `buildChatRequest` |
436
+ | **图片 / 视频生成**(v2.2.0+) | `generateImage`(同步), `generateVideo`(建异步任务), `pollVideoTask`(轮询);仅 `capabilities.supportsImageGeneration` / `supportsVideoGeneration` 模型可用 |
380
437
  | **Agent Runs** | `agentRuns.create`, `agentRuns.stream`, `agentRuns.run`, `agentRuns.cancel`, `agentRuns.get`, `agentRuns.listArtifacts`, `agentRuns.downloadArtifact`, `agentRuns.submitLocalToolResult`, `agentRuns.runWithLocalTools` |
381
438
  | **Agent Runs — 远程控制**(v2.1) | `agentRuns.createRemoteRun`, `agentRuns.streamRemoteControl`;helper:`parseRemoteControlEvent`, `isTerminalRemoteEvent`;scope:`remoteControlScopes()` / `ScopeRemoteControl`(不进 `allScopes()`) |
382
439
  | **Chat Bridge**(v2.1 · types-only) | 无 client 方法(Phase 7B 后端落地);导出类型守卫 `isPlatform`, `isRegion`, `isIntegrationStatus`, `isChannelInboundEvent`, `asCredentialRef` |
@@ -861,7 +918,8 @@ npm run docs # 经 TypeDoc 生成 API 参考到 docs/api/
861
918
 
862
919
  | 版本 | 状态 | 概要 |
863
920
  | --- | --- | --- |
864
- | 2.1.0 | **当前稳定版(npm latest)** | **远程控制 CrabCode 多接入面(2026-05-28)**。`serverURL`/`baseURL`/`baseUrl` Gateway URL 公共契约 + `normalizeGatewayBaseURL`(仅 http/https,拒 ws/wss);`agentRuns.createRemoteRun` / `streamRemoteControl` + 11 事件 `RemoteControlEvent` union + `parseRemoteControlEvent` / `isTerminalRemoteEvent`;`AdapterKind`(6) / `RunnerKind`(3) / `PermissionPolicy` / `WorkspacePolicy`;专用 `remote_control` scope(不进 `allScopes()`,`remoteControlScopes()`);`chatbridge` 第三方聊天平台类型骨架(types-only,无 client 方法,Phase 7B 后端落地);`subscription.getPlanByCode`。wire 约定按平面分(远控 snake_case + 毫秒整数 / chatbridge camelCase,契约 §12-§14)。公开类型 / 方法签名零移除、零改名(additive minor)。 |
921
+ | 2.2.0 | **当前稳定版(npm latest)** | **托管模型图片/视频生成(2026-05-29)**。图片/视频生成与文本模型同属托管模型网关(同 `Client`、同 `models:chat` 鉴权面),仅 `capabilities.supports_image_generation` / `supports_video_generation` 的模型可用;计费结算在营销系统,SDK / 网关只负责调用与用量上报。新增 `client.generateImage(modelID, req, signal?)`(同步,`POST /managed-models/:id/images/generations`)/ `client.generateVideo(modelID, req, signal?)`(建异步任务,返回 `taskId`)/ `client.pollVideoTask(modelID, taskID, durationSeconds?, signal?)`(轮询,`durationSeconds` 透传给网关在 `completed` 时上报视频秒数);新增 `ImageGenerationRequest`/`ImageGenerationResponse`/`VideoGenerationRequest`/`VideoTaskResponse` 类型 + `ModelCapabilities.supportsImageGeneration`/`supportsVideoGeneration` 标志。网关适配 OpenAI 兼容图片 + 火山引擎(即梦/豆包)视频 + DashScope 通义万相(wanx)原生异步任务(图片+视频)。公开类型 / 方法签名零移除、零改名(additive minor)。 |
922
+ | 2.1.0 | 稳定版 | **远程控制 CrabCode 多接入面(2026-05-28)**。`serverURL`/`baseURL`/`baseUrl` Gateway URL 公共契约 + `normalizeGatewayBaseURL`(仅 http/https,拒 ws/wss);`agentRuns.createRemoteRun` / `streamRemoteControl` + 11 事件 `RemoteControlEvent` union + `parseRemoteControlEvent` / `isTerminalRemoteEvent`;`AdapterKind`(6) / `RunnerKind`(3) / `PermissionPolicy` / `WorkspacePolicy`;专用 `remote_control` scope(不进 `allScopes()`,`remoteControlScopes()`);`chatbridge` 第三方聊天平台类型骨架(types-only,无 client 方法,Phase 7B 后端落地);`subscription.getPlanByCode`。wire 约定按平面分(远控 snake_case + 毫秒整数 / chatbridge camelCase,契约 §12-§14)。公开类型 / 方法签名零移除、零改名(additive minor)。 |
865
923
  | 2.0.1 | 稳定版 | **Packaging fix — 纯发布元数据,无源码改动**。`package.json.files` 数组补 `docs/pii-role-matrix.md` + `docs/开发与发布手册.md` 两项,让 v2.0.0 引入的 PII 角色矩阵与开发手册随 npm tarball 一并下发。从 v2.0.0 升级到 v2.0.1 无需任何 review。 |
866
924
  | 2.0.0 | **BREAKING** | **Phase 3 复核 + 全量根治(2026-05-25)**。主仓 9 commit 闭环 20 P0(RBAC 表达式统一 / PII 真落盘加密链 / K7 视频 webhook 幂等 / K8 OCR SSRF / K9 KYC main flow / admin 写端点错误码契约)。**SDK 同步**:新增 `casehall.getMyLawyerCredentialStatus()` + `enterprise.getMyEnterpriseKycStatus()` 律师/企业 OWNER 自查端点;`finance/types.ts` P2-016 PII Javadoc 升级(含 `keyVersion` v1/v2 payload 协议);新建 `docs/pii-role-matrix.md`(4 角色 × 3 PII 级矩阵);admin 写端点错误码改 HTTP 状态码语义(`200+{ok:false}` → `403/404/501`)。**升级指引详见 §"v2.0.0 升级指引"**。SDK 公开类型 / 方法签名零移除、零改名;BREAKING 范围在网关后端契约。 |
867
925
  | 1.9.0 | 稳定版 | **finance 域落地(商品化总规划 P7)**。新增 `client.finance.*`:`listMyInvoices` / `requestInvoice` / `listMyRefunds` / `requestRefund` / `listMyCorporateTransfers` / `initiateCorporateTransfer` / `uploadCorporateTransferProof`(决策 14/15 + R12)。发票 / 退款 / 对公转账三条业务线全量接入;金额一律用 string(json.Number 端口,避免 JS 浮点损失)。 |
@@ -2728,6 +2728,60 @@ var Client = class _Client {
2728
2728
  ctl.dispose();
2729
2729
  }
2730
2730
  }
2731
+ // ===========================================================================
2732
+ // 媒体生成 (v1.3+) — 图片 / 视频生成托管模型 (与 chat 同网关)
2733
+ //
2734
+ // 仅对 capabilities.supports_image_generation / supports_video_generation 的模型有效。
2735
+ // 网关不算钱; 用量由网关上报营销系统结算。
2736
+ // ===========================================================================
2737
+ /** 解析 nexus-v4 {code,message,data} 信封, code!=0 抛 BusinessError, 返回 data。 */
2738
+ unwrapAPIResponse(result) {
2739
+ const env = JSON.parse(new TextDecoder().decode(result));
2740
+ const bizErr = apiResponseBusinessError(env);
2741
+ if (bizErr) throw bizErr;
2742
+ return env.data;
2743
+ }
2744
+ /**
2745
+ * 图片生成 (同步)。POST /managed-models/:id/images/generations
2746
+ *
2747
+ * @param modelID 图片生成托管模型 ID (capabilities.supports_image_generation=true)
2748
+ */
2749
+ async generateImage(modelID, req, signal) {
2750
+ const endpoint = `/managed-models/${encodeURIComponent(modelID)}/images/generations`;
2751
+ const { result } = await this.doJSONFullRaw(
2752
+ "POST",
2753
+ endpoint,
2754
+ req,
2755
+ signal,
2756
+ CHAT_REQUEST_TIMEOUT_MS
2757
+ );
2758
+ return this.unwrapAPIResponse(result);
2759
+ }
2760
+ /**
2761
+ * 创建视频生成任务 (异步)。POST /managed-models/:id/videos/generations
2762
+ * 返回 taskId; 用 pollVideoTask() 轮询直到 status=completed。
2763
+ * 上报真物理量 (视频秒数) 需在 pollVideoTask 时回传 req.duration。
2764
+ *
2765
+ * @param modelID 视频生成托管模型 ID (capabilities.supports_video_generation=true)
2766
+ */
2767
+ async generateVideo(modelID, req, signal) {
2768
+ const endpoint = `/managed-models/${encodeURIComponent(modelID)}/videos/generations`;
2769
+ const { result } = await this.doJSONFullRaw("POST", endpoint, req, signal);
2770
+ return this.unwrapAPIResponse(result);
2771
+ }
2772
+ /**
2773
+ * 轮询视频任务状态。GET /managed-models/:id/videos/tasks/:taskId
2774
+ *
2775
+ * @param durationSeconds 创建时的时长 (秒), 透传给网关在 completed 时上报用量。
2776
+ */
2777
+ async pollVideoTask(modelID, taskID, durationSeconds, signal) {
2778
+ let endpoint = `/managed-models/${encodeURIComponent(modelID)}/videos/tasks/${encodeURIComponent(taskID)}`;
2779
+ if (durationSeconds != null && durationSeconds > 0) {
2780
+ endpoint += `?duration=${encodeURIComponent(String(durationSeconds))}`;
2781
+ }
2782
+ const { result } = await this.doJSONFullRaw("GET", endpoint, null, signal);
2783
+ return this.unwrapAPIResponse(result);
2784
+ }
2731
2785
  /**
2732
2786
  * Anthropic 原生格式同步聊天
2733
2787
  * v0.5.0: 根据 provider 自动路由
@@ -3072,11 +3126,11 @@ var Client = class _Client {
3072
3126
  }
3073
3127
  }
3074
3128
  /** doJSONFull 的 raw bytes 变体 (chat 用, 不立即 JSON.parse) */
3075
- async doJSONFullRaw(method, path, body, signal) {
3076
- return this.doJSONFullRawInternal(method, path, body, signal, false);
3129
+ async doJSONFullRaw(method, path, body, signal, timeoutMs = 3e4) {
3130
+ return this.doJSONFullRawInternal(method, path, body, signal, false, timeoutMs);
3077
3131
  }
3078
- async doJSONFullRawInternal(method, path, body, signal, retried) {
3079
- const ctl = withRequestTimeout(3e4, signal);
3132
+ async doJSONFullRawInternal(method, path, body, signal, retried, timeoutMs = 3e4) {
3133
+ const ctl = withRequestTimeout(timeoutMs, signal);
3080
3134
  try {
3081
3135
  const token = await this.ensureToken(ctl.signal);
3082
3136
  let bodyStr = null;
@@ -3104,7 +3158,7 @@ var Client = class _Client {
3104
3158
  `unauthorized and refresh failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`
3105
3159
  );
3106
3160
  }
3107
- return this.doJSONFullRawInternal(method, path, body, signal, true);
3161
+ return this.doJSONFullRawInternal(method, path, body, signal, true, timeoutMs);
3108
3162
  }
3109
3163
  if (resp.status < 200 || resp.status >= 300) {
3110
3164
  const bodyBytes = await readLimited(resp.body, maxErrorBodySize);