@acosmi/sdk-ts 1.1.0 → 1.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,30 @@ 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
+ ## [1.2.0] — 2026-05-18
9
+
10
+ ### Added
11
+
12
+ - **`ManagedModel.inputModalities`** — 新增可选字段, 类型 `Array<'text' | 'image'>`, 描述模型可接收的用户输入模态; 'image' 表示模型可直接接收 screenshot/image 输入. listModels / listModelsWithStatus 在写缓存与返回前会归一化上游 snake_case 字段名 `input_modalities` → camelCase `inputModalities` (兼容老网关), camelCase 与 snake_case 同时存在时 camelCase 胜.
13
+
14
+ - **`ModelCapabilities.supports_desktop_visual_understanding`** — 新增可选字段, 标识模型适合作为桌面截图解析 sidecar (输入 screenshot, 输出结构化 UI 描述, 供非多模态主模型消费). 与 `inputModalities=['image']` 是正交两件事: 前者描述"模型能不能吃图", 后者描述"运营是否把该模型标为桌面 UI 解析专用 sidecar". `zeroModelCapabilities()` 显式置 `false`, 避免 `undefined` 导致调用方误判.
15
+
16
+ - **`InputModality`** 类型导出 — `'text' | 'image'`.
17
+
18
+ - **Model catalog helpers (4 个)** — CrabCode desktop automation / computer-use 选模型用, 严格按 SDK 字段, 禁止模型名 substring 推断:
19
+ - `modelSupportsInputModality(model, modality): boolean`
20
+ - `modelSupportsImageInput(model): boolean`
21
+ - `findFirstModelByInputModality(models, modality): ManagedModel | null` — 按 catalog 顺序, 跳过 `isEnabled === false`
22
+ - `findDesktopVisualUnderstandingModel(models): ManagedModel | null` — 选择规则: isEnabled !== false + capabilities.supports_desktop_visual_understanding === true + inputModalities 含 'image' + isDefault 优先 / 否则 catalog 顺序第一个
23
+
24
+ ### Notes
25
+
26
+ - 上游 `ManagedModel` 缺失 `inputModalities` 时, SDK 保留 `undefined` 不自动补 `['text']` — 调用方必须保守按 text-only / unknown 处理, 严禁默认假设支持 image.
27
+ - 客户端不应硬编码模型名, 应完全依赖 SDK catalog 能力字段做模型选择.
28
+ - 23 新单测全绿 (8 listModels 归一化 + 15 helpers); 全量 79/79 passed, typecheck/lint/build clean.
29
+
30
+ ---
31
+
8
32
  ## [1.1.0] — 2026-05-06
9
33
 
10
34
  ### Added
package/README.md CHANGED
@@ -7,9 +7,9 @@
7
7
  ## 状态
8
8
 
9
9
  - 端口源:[acosmi-sdk-go](https://github.com/acosmi/acosmi-sdk-go) v1.0.0(与 Go SDK 联动稳定测试版)
10
- - 当前版本:**1.1.0**(新增 SDK-facing Agent Runs 公开 API;详见 [CHANGELOG](./CHANGELOG.md))
11
- - 测试:56/56 vitest 全绿,typecheck/lint/build 0 错误;packed-tarball smoke (`npm run test:pack`) 在 prepublishOnly 闸内
12
- - 包链接:[npm](https://www.npmjs.com/package/@acosmi/sdk-ts/v/1.1.0) · [tarball](https://registry.npmjs.org/@acosmi/sdk-ts/-/sdk-ts-1.1.0.tgz) · [GitHub Release](https://github.com/acosmi/sdk-ts/releases/tag/v1.1.0)
10
+ - 当前版本:**1.2.0**(新增 `ManagedModel.inputModalities` + `ModelCapabilities.supports_desktop_visual_understanding` + 4 catalog helpers,供 CrabCode desktop automation / computer-use 选模型用;详见 [CHANGELOG](./CHANGELOG.md))
11
+ - 测试:79/79 vitest 全绿,typecheck/lint/build 0 错误;packed-tarball smoke (`npm run test:pack`) 在 prepublishOnly 闸内
12
+ - 包链接:[npm](https://www.npmjs.com/package/@acosmi/sdk-ts/v/1.2.0) · [tarball](https://registry.npmjs.org/@acosmi/sdk-ts/-/sdk-ts-1.2.0.tgz) · [GitHub Release](https://github.com/acosmi/sdk-ts/releases/tag/v1.2.0)
13
13
 
14
14
  ## 安装
15
15
 
@@ -181,7 +181,7 @@ const client = new Client({ serverURL: 'https://acosmi.com', tokenStore: new Fil
181
181
  | **Chat** | `chat`, `chatStream`, `chatStreamWithUsage` |
182
182
  | **Agent Runs** | `agentRuns.create`, `agentRuns.stream`, `agentRuns.run`, `agentRuns.cancel`, `agentRuns.get`, `agentRuns.listArtifacts`, `agentRuns.downloadArtifact`, `agentRuns.submitLocalToolResult`, `agentRuns.runWithLocalTools` |
183
183
  | **Auth** | `login`, `logout`, `ensureToken`, `forceRefresh`, `discover`, `authorize`, `exchangeCode`, `refreshToken` |
184
- | **Models** | `listModels`, `listModelsWithStatus`, `getModelCapabilities`, `getQuotaSummary` |
184
+ | **Models** | `listModels`, `listModelsWithStatus`, `getModelCapabilities`, `getQuotaSummary`, `modelSupportsInputModality`, `modelSupportsImageInput`, `findFirstModelByInputModality`, `findDesktopVisualUnderstandingModel` |
185
185
  | **Skills** | `browseSkills`, `browseSkillsList`, `getSkillDetail`, `resolveSkill`, `installSkill`, `downloadSkill`, `uploadSkill`, `generateSkill`, `optimizeSkill`, `validateSkill` |
186
186
  | **Tools** | `listTools`, `getTool` |
187
187
  | **Wallet** | `getWalletStats`, `getWalletTransactions` |
@@ -230,6 +230,38 @@ const resp = await client.chat('claude-opus-4-7', {
230
230
 
231
231
  `AllowedDomains` / `BlockedDomains` 互斥,同时传入抛 `Error`。
232
232
 
233
+ ### 示例:桌面视觉理解 sidecar 选模型(CrabCode desktop automation / computer-use)
234
+
235
+ ```ts
236
+ import {
237
+ Client,
238
+ findDesktopVisualUnderstandingModel,
239
+ modelSupportsImageInput,
240
+ } from '@acosmi/sdk-ts';
241
+
242
+ const client = new Client({ serverURL: 'https://acosmi.com' });
243
+ const models = await client.listModels();
244
+
245
+ // 1) 主模型是否能直接吃截图?
246
+ const primaryCanSeeImages = modelSupportsImageInput(
247
+ models.find((m) => m.modelId === 'deepseek-v4') ?? null,
248
+ );
249
+
250
+ // 2) 不能 → 走桌面视觉 sidecar:截图先送 sidecar 解析为结构化 UI 描述,再喂主模型
251
+ const sidecar = findDesktopVisualUnderstandingModel(models);
252
+ if (!sidecar) {
253
+ throw new Error('No desktop visual understanding model available — 让管理员在网关启用一个 sidecar 模型');
254
+ }
255
+ console.log('sidecar →', sidecar.modelId);
256
+ ```
257
+
258
+ 红线:
259
+
260
+ - `ManagedModel.inputModalities` 用于客户端判断模型可接收的用户输入类型('text' | 'image')。
261
+ - `capabilities.supports_desktop_visual_understanding` 用于选择专门解析桌面截图的视觉 sidecar 模型,与 `inputModalities` 是正交两件事(普通视觉模型不一定擅长 UI 解析)。
262
+ - 客户端不应硬编码模型名做能力推断,应完全依赖 SDK catalog 字段。
263
+ - 上游未下发 `inputModalities` 时 SDK 保持 `undefined`,调用方必须保守按 text-only / unknown 处理。
264
+
233
265
  ### 示例:钱包 + 余额 + 流量包购买
234
266
 
235
267
  ```ts
@@ -1013,6 +1013,45 @@ var init_adapters = __esm({
1013
1013
 
1014
1014
  // src/index.ts
1015
1015
  init_types();
1016
+
1017
+ // src/model-helpers.ts
1018
+ function modelSupportsInputModality(model, modality) {
1019
+ if (!model) return false;
1020
+ const mods = model.inputModalities;
1021
+ if (!Array.isArray(mods)) return false;
1022
+ return mods.includes(modality);
1023
+ }
1024
+ function modelSupportsImageInput(model) {
1025
+ return modelSupportsInputModality(model, "image");
1026
+ }
1027
+ function findFirstModelByInputModality(models, modality) {
1028
+ if (!Array.isArray(models)) return null;
1029
+ for (const m of models) {
1030
+ if (!m) continue;
1031
+ if (m.isEnabled === false) continue;
1032
+ if (!modelSupportsInputModality(m, modality)) continue;
1033
+ return m;
1034
+ }
1035
+ return null;
1036
+ }
1037
+ function findDesktopVisualUnderstandingModel(models) {
1038
+ if (!Array.isArray(models)) return null;
1039
+ const candidates = [];
1040
+ for (const m of models) {
1041
+ if (!m) continue;
1042
+ if (m.isEnabled === false) continue;
1043
+ if (m.capabilities?.supports_desktop_visual_understanding !== true) continue;
1044
+ if (!modelSupportsInputModality(m, "image")) continue;
1045
+ candidates.push(m);
1046
+ }
1047
+ if (candidates.length === 0) return null;
1048
+ for (const m of candidates) {
1049
+ if (m.isDefault === true) return m;
1050
+ }
1051
+ return candidates[0];
1052
+ }
1053
+
1054
+ // src/index.ts
1016
1055
  init_adapters();
1017
1056
 
1018
1057
  // src/auth.ts
@@ -2588,14 +2627,15 @@ var Client = class _Client {
2588
2627
  null,
2589
2628
  signal
2590
2629
  );
2591
- this.modelCache = result.data;
2630
+ const normalized = normalizeInputModalities(result.data);
2631
+ this.modelCache = normalized;
2592
2632
  this.modelCacheTimeMs = Date.now();
2593
2633
  let status = "";
2594
2634
  if (headers) {
2595
2635
  const h = headers.get("X-Entitlement-Filter-Status");
2596
2636
  if (h) status = h;
2597
2637
  }
2598
- return { models: result.data, status };
2638
+ return { models: normalized, status };
2599
2639
  }
2600
2640
  /** 查询当前用户账户级权益总览 (v0.19+) */
2601
2641
  async getQuotaSummary(signal) {
@@ -3228,9 +3268,24 @@ function zeroModelCapabilities() {
3228
3268
  supports_token_efficient: false,
3229
3269
  supports_redact_thinking: false,
3230
3270
  max_input_tokens: 0,
3231
- max_output_tokens: 0
3271
+ max_output_tokens: 0,
3272
+ supports_desktop_visual_understanding: false
3232
3273
  };
3233
3274
  }
3275
+ function normalizeInputModalities(models) {
3276
+ if (!Array.isArray(models)) return models;
3277
+ for (const m of models) {
3278
+ if (!m || typeof m !== "object") continue;
3279
+ if (Array.isArray(m.inputModalities)) continue;
3280
+ const snake = m.input_modalities;
3281
+ if (Array.isArray(snake)) {
3282
+ m.inputModalities = snake.filter(
3283
+ (v) => v === "text" || v === "image"
3284
+ );
3285
+ }
3286
+ }
3287
+ return models;
3288
+ }
3234
3289
  function withRequestTimeout(ms, parent) {
3235
3290
  const ctl = new AbortController();
3236
3291
  const timer = setTimeout(() => ctl.abort(), ms);
@@ -4576,6 +4631,6 @@ Client.prototype.getBugReport = async function(bugID, signal) {
4576
4631
  return resp.data;
4577
4632
  };
4578
4633
 
4579
- export { AgentRunStreamError, AgentRunsClient, AnthropicAdapter, BucketClassCommercial, BucketClassGeneric, BusinessError, Client, LocalStorageTokenStore as DefaultBrowserTokenStore, DefaultRetryPolicy, ErrAuthDenied, ErrBrowserOpen, ErrDiscovery, ErrRegistration, ErrSSLProxy, ErrTimeout, ErrTokenExchange, EventAuthURL, EventComplete, EventError, FileTokenStore, FilterStatusAdminBypass, FilterStatusDisabledByFlag, FilterStatusFallbackMissingUser, FilterStatusFallbackNoBuckets, FilterStatusFallbackTkdistError, FilterStatusFallbackTkdistSkew, FilterStatusInternalBypass, FilterStatusOK, FilterStatusUnknown, HTTPError, InMemoryTokenStore, LocalStorageTokenStore, ModelNotFoundError, NetworkError, OpenAIAdapter, OrderTerminalError, ProviderFormat, RateLimitError, ScopeAI, ScopeAccount, ScopeEntitlements, ScopeModels, ScopeModelsChat, ScopeProfile, ScopeSkillStore, ScopeSkills, ScopeTokenPackages, ScopeTools, ScopeToolsExecute, ScopeWallet, ScopeWalletReadonly, ServerToolTypeWebSearch, StreamError, ThinkingHigh, ThinkingHighMinMaxTokens, ThinkingMax, ThinkingMaxFallbackMaxTokens, ThinkingOff, allScopes, anthropicResponseTextContent, anthropicResponseThinkingContent, anthropicResponseToolUseBlocks, apiResponseBusinessError, apiResponseGetMessage, authorize, bucketInfoIsCommercial, bucketRowIsCommercial, buildBetas, commerceScopes, computeBackoff, defaultRetryable, defaultSafeToRetry, discover, effectivePolicy, exchangeCode, extractAnthropicBlockMeta, fileLockDefaults, getAdapter, getAdapterForModel, isSSLError, modelScopes, newFileTokenStore, newThinkingConfig, newTokenSet, newWebSearchTool, parseNotificationEvent, parseSettlement, parseSourcesEvent, refreshToken, register, revokeToken, sanitize_exports as sanitize, skillScopes, tokenSetIsExpired, uniqueMerge };
4634
+ export { AgentRunStreamError, AgentRunsClient, AnthropicAdapter, BucketClassCommercial, BucketClassGeneric, BusinessError, Client, LocalStorageTokenStore as DefaultBrowserTokenStore, DefaultRetryPolicy, ErrAuthDenied, ErrBrowserOpen, ErrDiscovery, ErrRegistration, ErrSSLProxy, ErrTimeout, ErrTokenExchange, EventAuthURL, EventComplete, EventError, FileTokenStore, FilterStatusAdminBypass, FilterStatusDisabledByFlag, FilterStatusFallbackMissingUser, FilterStatusFallbackNoBuckets, FilterStatusFallbackTkdistError, FilterStatusFallbackTkdistSkew, FilterStatusInternalBypass, FilterStatusOK, FilterStatusUnknown, HTTPError, InMemoryTokenStore, LocalStorageTokenStore, ModelNotFoundError, NetworkError, OpenAIAdapter, OrderTerminalError, ProviderFormat, RateLimitError, ScopeAI, ScopeAccount, ScopeEntitlements, ScopeModels, ScopeModelsChat, ScopeProfile, ScopeSkillStore, ScopeSkills, ScopeTokenPackages, ScopeTools, ScopeToolsExecute, ScopeWallet, ScopeWalletReadonly, ServerToolTypeWebSearch, StreamError, ThinkingHigh, ThinkingHighMinMaxTokens, ThinkingMax, ThinkingMaxFallbackMaxTokens, ThinkingOff, allScopes, anthropicResponseTextContent, anthropicResponseThinkingContent, anthropicResponseToolUseBlocks, apiResponseBusinessError, apiResponseGetMessage, authorize, bucketInfoIsCommercial, bucketRowIsCommercial, buildBetas, commerceScopes, computeBackoff, defaultRetryable, defaultSafeToRetry, discover, effectivePolicy, exchangeCode, extractAnthropicBlockMeta, fileLockDefaults, findDesktopVisualUnderstandingModel, findFirstModelByInputModality, getAdapter, getAdapterForModel, isSSLError, modelScopes, modelSupportsImageInput, modelSupportsInputModality, newFileTokenStore, newThinkingConfig, newTokenSet, newWebSearchTool, parseNotificationEvent, parseSettlement, parseSourcesEvent, refreshToken, register, revokeToken, sanitize_exports as sanitize, skillScopes, tokenSetIsExpired, uniqueMerge };
4580
4635
  //# sourceMappingURL=index.mjs.map
4581
4636
  //# sourceMappingURL=index.mjs.map