#4339

fix(ai-chat): standard puterIds and chat-only admission for the Alibaba catalog

Opensabrimetewants to merge intofrom

Summary

GET /puterai/chat/models lists 55 ids that don't follow the <service>:<vendor>/<model> format (kimi/kimi-k3, zhipu/glm-5.2, glm-5.3-prime, qwen3.7-plus-2026-05-26, text-embedding-v3, fun-asr, ...). All of them come from the Alibaba provider's remote catalog refresh.

Why. mapAlibabaCatalogModel admits DashScope feed entries that aren't in the curated ALIBABA_MODELS list, and those entries never get a puterId, so ChatCompletionDriver.list() falls back to the raw feed id. Separately, the chat-capability gate only ran when the feed omitted response_modality, so any model whose response includes Text was admitted, including ASR, rerank, embedding, image generation, realtime omni and TTS models that can't serve chat completions (and have no verified pricing, so they'd fail at request time anyway).

What (two commits so they can be judged separately):

  1. Uncurated feed entries get puterId = alibaba:<vendor>/<id>. Vendor-prefixed feed ids keep their org (alibaba:kimi/kimi-k3); unprefixed ids take the org of the curated sibling with the same family prefix (deepseek-v4.1-flash-1017 -> alibaba:deepseek/..., glm-5.3-prime -> alibaba:zhipu/...), falling back to qwen. A feed entry whose synthesized puterId collides with a curated entry's (feed zhipu/glm-5.2 vs curated glm-5.2) is skipped so the driver doesn't bucket two entries under one id and list it twice.
  2. The admission gate applies to every feed entry: declared capabilities must include TG/Reasoning/VU, request and response modalities (when declared) must include Text, and signal-less entries are admitted only when curated. A rejected curated entry simply keeps its curated data, since the catalog map is seeded from the curated list.

Curated entries are unaffected; ...existing still carries their own puterId, aliases and prices.

Testing

  • 12 new regression tests in AlibabaProvider.test.ts (direct mapAlibabaCatalogModel cases plus a puterId assertion on the paged-fixture flow); all fail before the fix.
  • vitest run on providers/alibaba, modelCatalogs.test.ts, providerConsistency.test.ts, ChatCompletionDriver.test.ts: 276 passed.
  • npm run typecheck, eslint and prettier clean on catalog.ts.

Activity

3