--- name: ai-model-web description: "Use this skill when a browser/Web app (React, Vue, Angular, Next, Nuxt, static sites, SPAs, dashboards, AI chat UI) needs AI models via @cloudbase/js-sdk. Default routing for page/页面/Web/前端/frontend/网页/H5 AI — call directly from browser, do NOT propose a Node.js proxy. Covers generateText and streamText. Models via ai.createModel with groups cloudbase, hunyuan-exp, or custom-*. Model IDs (deepseek-v4-flash, deepseek-v3.2, hunyuan-2.0-instruct-20251111, glm-5, kimi-k2.6) go in the model field. MUST run two-step preflight before code — see body. Keywords: 页面, Web, 前端, React, Vue, Next, Nuxt, SPA, AI chat UI, generateText, streamText, createModel, hunyuan-exp, Token Credits, TokenHub, Hunyuan, DeepSeek, GLM, Kimi, MiniMax. NOT for Node.js backend (use ai-model-nodejs), Mini Program (use ai-model-wechat), or image generation (Node SDK only)." version: 2.23.6 alwaysApply: false --- ## Standalone Install Note If this environment only installed the current skill, start from the CloudBase main entry and use the published `cloudbase/references/...` paths for sibling skills. - CloudBase main entry: `https://cnb.cool/tencent/cloud/cloudbase/cloudbase-skills/-/git/raw/main/skills/cloudbase/SKILL.md` - Current skill raw source: `https://cnb.cool/tencent/cloud/cloudbase/cloudbase-skills/-/git/raw/main/skills/cloudbase/references/ai-model-web/SKILL.md` Keep local `references/...` paths for files that ship with the current skill directory. When this file points to a sibling skill such as `auth-tool` or `web-development`, use the standalone fallback URL shown next to that reference. ## When to use this skill Use this skill for **calling AI models in browser/Web applications** via `@cloudbase/js-sdk`. > 🧭 **Runtime-plane default for Web.** Any time the user's request is framed around a page, a Web app, the frontend, React/Vue/Next/Nuxt, a dashboard UI, or "add AI to my H5", this skill is the default routing target. **Do NOT first propose a Node.js / cloud-function / CloudRun proxy**; `@cloudbase/js-sdk` can call the model from the browser directly. Only switch to `ai-model-nodejs` if the user explicitly asks for a backend/server call, image generation, or a scenario that truly needs server-side keys or long-running work. This decision is independent of which concrete model the user picks — model names (`deepseek-*`, `glm-*`, `hunyuan-*`, `kimi-*`, …) only affect the `model` field, not the routing plane. **Use it when you need to:** - Integrate AI text generation into a frontend Web app - Stream AI responses for a better UX - Call Hunyuan / DeepSeek / GLM / Kimi / MiniMax models from the browser **Do NOT use for:** - Node.js backend or cloud functions → use the `ai-model-nodejs` skill - WeChat Mini Program → use the `ai-model-wechat` skill - Image generation → use the `ai-model-nodejs` skill (Node SDK only) - Runtimes without a CloudBase SDK (native apps, Python, Go, etc.) → use the `http-api` skill (it now includes the `ai_model` OpenAPI spec for direct HTTP calls; do NOT build a custom HTTP proxy) --- ## ⛔ STOP — `ai.createModel(...)` argument is **not** a vendor / model name Read this before writing any `createModel(...)` line. The single most common mistake when agents generate code for this SDK is hallucinating the argument. There are **exactly three** legal shapes. Anything else is a bug. | ✅ Legal `ai.createModel(...)` argument | When to use it | |----------------------------------------|----------------| | `"cloudbase"` | **The main managed group for new projects** (TokenHub-backed, multi-vendor pool). Vendor + concrete model go into the **`model` field** of `generateText` / `streamText`, e.g. `{ model: "deepseek-v4-flash" }`. **No model is enabled by default — always check `DescribeAIModels` first and, if the target model is missing, enable it with `UpdateAIModel` before calling the SDK.** | | `"hunyuan-exp"` | Only if `DescribeAIModels` explicitly returns this legacy builtin group for the current env (mainly the Mini Program Growth Plan — see `ai-model-wechat`). | | `"custom-"` | A user-defined GroupName you onboarded via `CreateAIModel`. **Must** start with `custom-` (e.g. `custom-kimi`, `custom-openai-compat`). | ### ❌ Do NOT write any of these — they are all wrong ```js ai.createModel("deepseek") // wrong — that's a vendor, not a GroupName ai.createModel("deepseek-v4-flash") // wrong — that's a model name, goes in the `model` field ai.createModel("hunyuan") // wrong — vendor family, not a GroupName ai.createModel("hunyuan-2.0-instruct-20251111") // wrong — model name ai.createModel("glm") / ai.createModel("kimi") / ai.createModel("minimax") // wrong — vendor names ai.createModel("openai") / ai.createModel("moonshot") // wrong — vendor names ai.createModel("custom") // wrong — placeholder; use your real custom- ai.createModel(modelName) // wrong — do not reuse the variable that holds the model id ``` ### ✅ Correct pattern — GroupName vs Model are two different fields ```js const model = ai.createModel("cloudbase"); // ← GroupName await model.generateText({ model: "deepseek-v4-flash", // ← concrete model id messages: [...] }); ``` ### Decision procedure (when the user names a specific model) 1. The user says "use DeepSeek v3.2" / "use hunyuan instruct" / "use Kimi k2.6" / "use GLM-5" / … 2. `createModel("cloudbase")` stays the same. 3. Put the model id into the **`model` field**: `{ model: "deepseek-v3.2" }`, `{ model: "hunyuan-2.0-instruct-20251111" }`, `{ model: "kimi-k2.6" }`, `{ model: "glm-5" }`, … 4. **Never assume the model is already enabled.** Before writing the SDK call, verify it is present in `DescribeAIModels({ GroupName: "cloudbase" }).Models[]`. If missing, call `DescribeManagedAIModelList` to confirm the exact `Model` name the platform supports (case-sensitive — do **not** guess the spelling), then enable it via `UpdateAIModel` with `Status: 1` (remember `Models` is a full replacement, so resend everything already enabled + the new one). > If you are about to type `ai.createModel(` and the thing inside the parentheses is a vendor name, a model name, or a guess — **stop**. It is almost certainly one of the three legal values above. --- ## Mandatory Two-Step Preflight (before any SDK code) Before generating any AI-related SDK code, **run the two-step preflight**: ① eligibility, ② group readiness. Emitting `createModel(...)` straight away and letting the user debug runtime errors is significantly more costly. ### Step 0: obtain the environment ID Call the MCP tool `envQuery` with `action=info` and read `EnvId` from the response. Every subsequent check and purchase link uses this `EnvId`. --- ### Preflight ① — Eligibility (Token Credits resource pack) Call the MCP tool: ``` callCloudApi(service="tcb", action="DescribeEnvPostpayPackage", params={ EnvId }) ``` **Pass conditions (all required):** - `envPostpayPackageInfoList` contains at least one entry - That entry's `postpayPackageId` starts with `pkg_tcb_tokencredits_` - That entry's `status` is NOT in `[3, 4]` (3 / 4 typically mean expired / disabled; trust the live response) - ❌ **Not satisfied** → **stop writing code** and surface this to the user (replacing `{envId}` with the real id): > The current environment has no active Token Credits resource pack. Please purchase one before calling any AI API: > https://buy.cloud.tencent.com/lowcode?buyType=resPack&envId={envId}&resourceType=token > > Let me know once it's done and I'll re-check the resource pack status. - ✅ **Satisfied** → proceed to preflight ②. > Parameter casing is PascalCase by contract. If the call returns `InvalidParameter`, fall back to camelCase (`envId` / `envPostpayPackageInfoList`) and trust the live response. For the Mini Program scenario there is an additional growth-plan branch — switch to the `ai-model-wechat` skill. --- ### Preflight ② — Group readiness (`DescribeAIModels` → `UpdateAIModel` if needed) Eligibility alone is not enough. **Do not write `createModel("cloudbase")` yet.** First confirm that the target `GroupName` exists in the env with `Status=1`, and that the target `Model` is present in its `Models[]`. 1. **List groups configured in the current env:** ``` callCloudApi(service="tcb", action="DescribeAIModels", params={ EnvId }) ``` Returns `AIModelGroups: AIModelGroup[]`, where each `AIModelGroup` includes `GroupName`, `Type` (`builtin` / `custom`), `Models: [{ Model, EnableMCP, Tags }]`, `Status` (1 = on / 2 = off), `BaseUrl`, `Secret`, `Remark`. The main managed `GroupName` is `cloudbase`. 2. **Never assume a model is already enabled.** Inspect `AIModelGroups[?].Models[].Model` for the `cloudbase` group. If the target model (or, when the user did not specify one, the model you intend to default to such as `deepseek-v4-flash`) is missing, jump to step 4 and enable it — do not call `createModel("cloudbase")` yet. If the `cloudbase` group itself is missing or has `Status=2`, also jump to step 4. 3. **User asked for a model that belongs to the managed catalog** (e.g. `deepseek-v3.2`, `hunyuan-2.0-instruct-20251111`, `glm-5`, `kimi-k2.6`, …): check whether that `Model` is already in the `cloudbase` group's `Models[]`. If not, jump to step 4. **Do not guess the exact model id** — verify the canonical spelling in `DescribeManagedAIModelList` first (step 4 covers this). 4. **Enable / add a managed model** (always inspect the authoritative catalog + pricing first): ``` callCloudApi(service="tcb", action="DescribeManagedAIModelList", params={ EnvId }) ``` Returns `ManagedAIModelGroup[]`, where each group lists `GroupName` (e.g. `cloudbase`), `Remark`, and `Models: [{ Model, EnableMCP, ModelSpec{ContextLength, MaxInputToken, MaxOutputToken}, ModelChargingInfo[{Type, InputPrice, OutputPrice, InputOutputUnit, CachePrice}] }]`. **This is the single source of truth for supported model names and pricing — do not infer them from memory. Use the exact `Model` string returned here when calling `UpdateAIModel`.** Also surface the prices to the user before enabling. Then enable (note: `Models` is a **full replacement** — always resend the already-enabled models together with the new one): ``` callCloudApi(service="tcb", action="UpdateAIModel", params={ EnvId, GroupName: "cloudbase", Models: [ // resend every model that DescribeAIModels already showed as enabled { Model: "" }, // append the newly-requested one, using the exact spelling from DescribeManagedAIModelList { Model: "" } ], Status: 1 }) ``` 5. **The requested model is not in the managed catalog** (not found by `DescribeManagedAIModelList`) → jump to the next section, **Custom onboarding (models outside the managed catalog)**. > All Actions use `service=tcb`, `Version=2018-06-08`. Parameters are PascalCase (`EnvId` / `GroupName` / `Models` / `Status`). Fall back to camelCase only if the call returns `InvalidParameter`. --- ## Available Providers and Models `ai.createModel()` accepts exactly three kinds of legal values: ### 1. `"cloudbase"` — the main managed group (recommended) - `GroupName: "cloudbase"`, `Type: "builtin"`, `Remark: "腾讯云开发"` (Tencent CloudBase) - Backed by **Tencent Cloud TokenHub**, a unified managed pool covering multiple vendors — **Hunyuan** (HY 2.0 Instruct, HY 2.0 Think, Hunyuan-role, Hy3 preview, …), **DeepSeek** (DeepSeek-V4-Pro, DeepSeek-V4-Flash, Deepseek-v3.2, Deepseek-v3.1, Deepseek-r1-0528, Deepseek-v3-0324, …), **Zhipu GLM** (GLM-5, GLM-5-Turbo, GLM-5.1, GLM-5V-Turbo), **Kimi** (K2.5, K2.6), **MiniMax** (M2.5, M2.7), and more. The roster evolves — **do not hard-code specific SKUs** in application code; discover at runtime. - **No model is enabled by default.** Always call `DescribeAIModels` first to see what the env has actually enabled; if your target model is missing, call `DescribeManagedAIModelList` for the authoritative catalog + pricing and then `UpdateAIModel` (`Status: 1`, `Models` full-replacement) to enable it before making the SDK call. - Authoritative catalog + pricing: `DescribeManagedAIModelList` - Env-enabled set: `DescribeAIModels` ### 2. `"hunyuan-exp"` — legacy builtin group (kept for compatibility) - Primarily relevant to the Mini Program Growth Plan scenario; do not use from Web unless the env explicitly still has it (switch to the `ai-model-wechat` skill for that flow) - Default model: `hunyuan-2.0-instruct-20251111`; additional hunyuan SKUs must be discovered at runtime via `DescribeAIModels({ GroupName: "hunyuan-exp" }).Models[]` — do not hard-code other IDs ### 3. User-defined GroupName - Onboarded via `CreateAIModel` (see the next section). The custom `GroupName` **MUST start with `custom-`** (e.g. `custom-kimi`, `custom-moonshot`, `custom-openai-compat`). This naming convention prevents future collisions with built-in / vendor GroupNames (`cloudbase`, `hunyuan-exp`, `deepseek`, `glm`, `kimi`, `minimax`, …) that the platform may introduce over time - Examples: `createModel("custom-kimi")`, `createModel("custom-openai-compat")` > **Never** write guesses like `createModel("deepseek")` or `createModel("custom")` unless `DescribeAIModels` explicitly returned that exact `GroupName` (old envs may still carry historical `deepseek` / `hunyuan-exp` builtin groups — that stays legal for compatibility, but new projects should always go through `cloudbase`). --- ## Custom onboarding (models outside the managed catalog) When the user wants to call a **non-managed** model (self-hosted, enterprise-internal, third-party OpenAI-compatible endpoint, …), **do not block**. Guide them through onboarding: ### Option 1: console flow (recommended, user handles it) `https://tcb.cloud.tencent.com/dev?envId={envId}#/ai` ### Option 2: programmatic onboarding (`CreateAIModel`) ``` callCloudApi(service="tcb", action="CreateAIModel", params={ EnvId: "", GroupName: "custom-", // MUST start with "custom-" (e.g. custom-kimi, custom-openai-compat); never start with "cloudbase" BaseUrl: "", Models: [ { Model: "", EnableMCP: true } ], Remark: "", Status: 1, Secret: { ApiKey: "" } }) ``` Once onboarded, confirm with `DescribeAIModels` that the group is ready, then call `ai.createModel("")` from your code. Use `UpdateAIModel` to add/remove models, rotate keys, or change `BaseUrl` (remember `Models` is a **full replacement**). Use `DeleteAIModel` to remove a custom group (builtin groups cannot be deleted). > Custom-model billing is covered by the third-party provider and does not draw from the Token Credits resource pack. Field casing follows the live contract — fall back to camelCase on `InvalidParameter`. --- ## Installation ```bash npm install @cloudbase/js-sdk ``` ## Initialization > ⚠️ **Do not use anonymous sign-in as the default.** Anonymous login is **disabled by default** for new environments, and inactive existing environments have also been automatically disabled. Even when anonymous login is manually enabled, **anonymous users are denied AI model invocation permissions by default**. The AI-model skill does **not** prescribe a specific login UI — delegate that concern: > > - **Enabling / configuring login providers** (phone SMS, email, WeChat Open Platform, username+password, OAuth, …) → follow the **`auth-tool`** skill (backend config via `callCloudApi`). > - **Building the actual sign-in flow in the browser** (login form, callbacks, session guarding) → follow the **`auth-web`** skill (`@cloudbase/js-sdk` auth API, e.g. `signInWithPassword`, `signInWithPhone`, `getSession`). > > Do **not** fall back to `signInAnonymously()` for AI features — anonymous users cannot call AI models. Only use anonymous login for non-AI read-only demos where the user explicitly requests it and accepts the trade-off. ```js import cloudbase from "@cloudbase/js-sdk"; const app = cloudbase.init({ env: "", accessKey: "" // Get it from the CloudBase console }); const auth = app.auth(); // CRITICAL: Use auth.getSession() to check login — NOT the deprecated getLoginState(). // getLoginState() returns uid even without real login (just accessKey), causing false positives. // getSession() returns data.session === undefined when no real login exists. // Anonymous users are DENIED AI model permissions — calling AI without real login will fail. const { data: sessionData } = await auth.getSession(); if (!sessionData?.session || sessionData.session.user?.is_anonymous) { // No real login or anonymous session — route to sign-in page window.location.href = "/login"; return; } const ai = app.ai(); ``` **Important notes:** - Use synchronous initialization with a top-level import - **`accessKey` causes `getLoginState()` to return misleading auth data** — the deprecated `getLoginState()` returns an object with `uid` even without real login, which breaks naive `!!loginState` checks. Use `auth.getSession()` instead: it returns `data.session === undefined` when no real login exists, so `!!data.session` is a reliable auth gate. - The user MUST be authenticated with a verified login (phone, email, WeChat, username+password, custom) before using AI features. Anonymous users are denied AI model permissions. The exact flow is the responsibility of the `auth-web` skill. - Get `accessKey` from the CloudBase console --- ## generateText() — non-streaming > **Prerequisite:** the two-step preflight (eligibility + group readiness) has passed, and the target model has been confirmed present in `DescribeAIModels({ GroupName: "cloudbase" }).Models[]` — if it was not, it should already have been enabled via `UpdateAIModel`. The example below uses `deepseek-v4-flash` only for illustration; substitute the actual model the user asked for. ```js const model = ai.createModel("cloudbase"); const result = await model.generateText({ model: "deepseek-v4-flash", // must already be enabled in this env (DescribeAIModels → UpdateAIModel) messages: [{ role: "user", content: "Give me a one-paragraph intro to Li Bai." }], }); console.log(result.text); // generated text string console.log(result.usage); // { prompt_tokens, completion_tokens, total_tokens } console.log(result.messages); // full message history console.log(result.rawResponses); // raw model responses ``` --- ## streamText() — streaming > **Prerequisite:** the two-step preflight has passed. ```js const model = ai.createModel("cloudbase"); const res = await model.streamText({ model: "deepseek-v4-flash", messages: [{ role: "user", content: "Give me a one-paragraph intro to Li Bai." }], }); // Option 1: iterate the text stream (recommended) for await (let text of res.textStream) { console.log(text); // incremental text chunks } // Option 2: iterate the data stream for full response chunks for await (let data of res.dataStream) { console.log(data); // full response chunk with metadata } // Option 3: access final results const messages = await res.messages; // full message history const usage = await res.usage; // token usage ``` --- ## Error Handling Pattern ```js const model = ai.createModel("cloudbase"); try { const result = await model.generateText({ model: "deepseek-v4-flash", messages: [{ role: "user", content: "Generate a concise onboarding checklist." }], }); console.log(result.text); } catch (error) { console.error("Failed to call CloudBase AI from Web", error); } ``` --- ## Type Definitions ```ts interface BaseChatModelInput { model: string; // required: model name messages: Array; // required: message array temperature?: number; // optional: sampling temperature topP?: number; // optional: nucleus sampling } type ChatModelMessage = | { role: "user"; content: string } | { role: "system"; content: string } | { role: "assistant"; content: string }; interface GenerateTextResult { text: string; // generated text messages: Array; // full message history usage: Usage; // token usage rawResponses: Array; // raw model responses error?: unknown; // error if any } interface StreamTextResult { textStream: AsyncIterable; // incremental text stream dataStream: AsyncIterable; // full data stream messages: Promise;// final message history usage: Promise; // final token usage error?: unknown; // error if any } interface Usage { prompt_tokens: number; completion_tokens: number; total_tokens: number; } ``` --- ## Best Practices 1. **Run the two-step preflight first** — ① eligibility (Token Credits resource pack via `DescribeEnvPostpayPackage`) + ② group readiness (`DescribeAIModels` to inspect what is enabled, `DescribeManagedAIModelList` for the authoritative supported-model catalog, `UpdateAIModel` with a full-replacement `Models[]` and `Status: 1` when the target model is missing). Skipping preflight leads straight to "model not found" / "model not enabled" errors at runtime. 2. **Never assume any model is already enabled** — not `deepseek-v4-flash`, not `hunyuan-*`, not anything. Always verify with `DescribeAIModels` first; if the target is missing, look up the exact `Model` string in `DescribeManagedAIModelList` (do **not** guess the spelling or invent vendor prefixes) and then `UpdateAIModel` to enable it. 3. **`createModel` accepts exactly three kinds of values** — `"cloudbase"` (the main managed group), `"hunyuan-exp"` (legacy builtin, Growth Plan scenarios), or a user-defined GroupName registered via `CreateAIModel` (**MUST start with `custom-`**, e.g. `custom-kimi`, `custom-openai-compat`). **Never** guess with `createModel("deepseek")` / `createModel("kimi")` / `createModel("custom")`. 4. **Do not invent SDK method names or parameters.** This SKILL.md is the authoritative reference for `@cloudbase/js-sdk`'s AI surface — look up the method signature here (or in the Type Definitions section below) before writing code. If a method or field is not documented here, stop and ask, or check the live contract via the MCP tools. No guessing. 5. **Show pricing before enabling a new managed model** — `DescribeManagedAIModelList` returns `ModelSpec` (context length, max input/output tokens) + `ModelChargingInfo` (input / output / cache prices, billing unit). Surface the prices to the user before calling `UpdateAIModel`. 6. **Use streaming for long responses** — better perceived latency and interactivity. 7. **Handle errors gracefully** — wrap AI calls in try/catch. 8. **Keep `accessKey` safe** — use a publishable key, never a secret key. 9. **Initialize early** — set up the SDK at app entry so auth and AI are both ready before routing. 10. **Do NOT use anonymous auth for AI features** — anonymous login is disabled by default for new environments, and anonymous users are denied AI model permissions. Require a verified sign-in (phone, email, username+password, WeChat, custom) before calling any AI API. Delegate provider configuration to the `auth-tool` skill and the browser sign-in flow to the `auth-web` skill; the AI-model skill checks `auth.getSession()` and verifies `loginType` before gating the call. 11. **Distinguish "preflight failure" from "model call failure"** — the former means the user needs to buy a resource pack or call `UpdateAIModel`; the latter is a prompt / parameter / network issue. Give the user different guidance for each. 12. **TypeScript: do NOT use `any` to silence type errors from the SDK.** The SDK ships its own types; if an error shows up, narrow with `unknown` + a type guard, write a precise `interface` for the shape you actually consume, or augment types in a local `.d.ts`. Never `: any`, `as any`, `@ts-ignore`, or `@ts-nocheck`. See the Engineering constitution in the `web-development` skill. 13. **Self-verify before claiming done.** Run `tsc --noEmit` + the project build + open the page with `agent-browser` and actually trigger the AI call. Confirm: (a) the text stream reaches the UI, (b) no new console errors, (c) `result.usage` is non-zero. Saying "it should work" without evidence is not acceptable — follow `web-development/browser-testing.md`.