From dac254af869816b9f5327bcd08a0972e93f07268 Mon Sep 17 00:00:00 2001 From: clh02467605 Date: Fri, 24 Jul 2026 10:56:48 +0800 Subject: [PATCH 1/7] feat: add MCP WebSearch page URL and enhance error handling in web search command --- docs/agents/url-change.md | 1 + .../src/commands/search/web-activate-hint.ts | 34 ++++++++ packages/commands/src/commands/search/web.ts | 19 +++-- .../tests/search-web-activate-hint.test.ts | 81 +++++++++++++++++++ packages/runtime/src/index.ts | 1 + packages/runtime/src/urls.ts | 6 ++ 6 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 packages/commands/src/commands/search/web-activate-hint.ts create mode 100644 packages/commands/tests/search-web-activate-hint.test.ts diff --git a/docs/agents/url-change.md b/docs/agents/url-change.md index 0292eecd..515e3a42 100644 --- a/docs/agents/url-change.md +++ b/docs/agents/url-change.md @@ -20,6 +20,7 @@ runtime/src/urls.ts ← 用户面控制台 URL(cn-only) BAILIAN_CONSOLE BAILIAN_CONSOLE_ROOT/cn-beijing API_KEY_PAGE BAILIAN_CONSOLE/?tab=app#/api-key TOKEN_PLAN_PAGE BAILIAN_CONSOLE_ROOT/cn-beijing?tab=plan#/efm/subscription/overview + MCP_WEBSEARCH_PAGE BAILIAN_CONSOLE?tab=mcp#/mcp-market/detail/WebSearch core/files/upload.ts ← 文件上传 endpoint(cn-pinned) UPLOAD_API ${REGIONS.cn}/api/v1/uploads diff --git a/packages/commands/src/commands/search/web-activate-hint.ts b/packages/commands/src/commands/search/web-activate-hint.ts new file mode 100644 index 00000000..6f5d93e9 --- /dev/null +++ b/packages/commands/src/commands/search/web-activate-hint.ts @@ -0,0 +1,34 @@ +import { BailianError } from "bailian-cli-core"; +import { MCP_WEBSEARCH_PAGE } from "bailian-cli-runtime"; + +/** recoginze WebSearch MCP not activated / invalid caused 404 (CLI wrapped message from server)。 */ +export function isWebSearchMcpNotActivated(error: unknown): boolean { + if (!(error instanceof BailianError)) return false; + const message = error.message; + if (!/MCP request failed:\s*404\b/i.test(message)) return false; + return /未开通|MCP不存在|MCP_IS_INVALID/i.test(message); +} + +/** activate hint; URL from runtime/urls.ts。 */ +export function webSearchActivateHint(): string { + return [ + "Activate (or re-activate) the WebSearch MCP in the Bailian MCP marketplace, then retry.", + "If it was previously on SSE, cancel and activate again to upgrade to Streamable HTTP.", + `Open: ${MCP_WEBSEARCH_PAGE}`, + ].join("\n"); +} + +/** + * keep original message / exitCode for not activated errors, add hint only; other errors throw as is. + * do not replace server error message. + */ +export function rethrowWithWebSearchActivateHint(error: unknown): never { + if (isWebSearchMcpNotActivated(error) && error instanceof BailianError && !error.hint) { + throw new BailianError(error.message, error.exitCode, webSearchActivateHint(), { + cause: error, + api: error.api, + rawResponse: error.rawResponse, + }); + } + throw error; +} diff --git a/packages/commands/src/commands/search/web.ts b/packages/commands/src/commands/search/web.ts index 9804527e..b294ea20 100644 --- a/packages/commands/src/commands/search/web.ts +++ b/packages/commands/src/commands/search/web.ts @@ -5,8 +5,8 @@ import { mcpWebSearchPath, type FlagsDef, } from "bailian-cli-core"; -import { createSpinner } from "bailian-cli-runtime"; -import { emitResult } from "bailian-cli-runtime"; +import { createSpinner, emitResult } from "bailian-cli-runtime"; +import { rethrowWithWebSearchActivateHint } from "./web-activate-hint.ts"; const WEB_SEARCH_FLAGS = { query: { type: "string", valueHint: "", description: "Search query text" }, @@ -41,11 +41,14 @@ export default defineCommand({ return; } - const client = ctx.client.mcp(mcpWebSearchPath()); - await client.initialize(); - const tools = await client.listTools(); - - emitResult({ tools }, format); + try { + const client = ctx.client.mcp(mcpWebSearchPath()); + await client.initialize(); + const tools = await client.listTools(); + emitResult({ tools }, format); + } catch (error) { + rethrowWithWebSearchActivateHint(error); + } return; } @@ -123,7 +126,7 @@ export default defineCommand({ } } catch (error) { spinner.stop("Failed."); - throw error; + rethrowWithWebSearchActivateHint(error); } }, }); diff --git a/packages/commands/tests/search-web-activate-hint.test.ts b/packages/commands/tests/search-web-activate-hint.test.ts new file mode 100644 index 00000000..045185cf --- /dev/null +++ b/packages/commands/tests/search-web-activate-hint.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, test } from "vite-plus/test"; +import { BailianError, ExitCode } from "bailian-cli-core"; +import { MCP_WEBSEARCH_PAGE } from "bailian-cli-runtime"; +import { + isWebSearchMcpNotActivated, + rethrowWithWebSearchActivateHint, + webSearchActivateHint, +} from "../src/commands/search/web-activate-hint.ts"; + +describe("web-activate-hint", () => { + test("识别 404 + 未开通 / MCP不存在 / MCP_IS_INVALID", () => { + expect( + isWebSearchMcpNotActivated( + new BailianError("MCP request failed: 404 Not Found - MCP不存在或未开通"), + ), + ).toBe(true); + expect( + isWebSearchMcpNotActivated(new BailianError("MCP request failed: 404 - MCP不存在或未开通")), + ).toBe(true); + expect( + isWebSearchMcpNotActivated( + new BailianError("MCP request failed: 404 Not Found - MCP_IS_INVALID"), + ), + ).toBe(true); + }); + + test("裸 404 或非 MCP 错误不加开通判定", () => { + expect(isWebSearchMcpNotActivated(new BailianError("MCP request failed: 404 Not Found"))).toBe( + false, + ); + expect( + isWebSearchMcpNotActivated(new BailianError("MCP request failed: 405 Method Not Allowed")), + ).toBe(false); + expect(isWebSearchMcpNotActivated(new Error("MCP不存在或未开通"))).toBe(false); + }); + + test("hint 含 MCP 广场 WebSearch 深链", () => { + expect(webSearchActivateHint()).toContain(MCP_WEBSEARCH_PAGE); + expect(webSearchActivateHint()).toMatch(/Activate|re-activate/i); + }); + + test("rethrow 保留原 message,补 Hint", () => { + const original = new BailianError( + "MCP request failed: 404 Not Found - MCP不存在或未开通", + ExitCode.GENERAL, + ); + try { + rethrowWithWebSearchActivateHint(original); + expect.unreachable("should throw"); + } catch (error) { + expect(error).toBeInstanceOf(BailianError); + const wrapped = error as BailianError; + expect(wrapped.message).toBe(original.message); + expect(wrapped.exitCode).toBe(ExitCode.GENERAL); + expect(wrapped.hint).toContain(MCP_WEBSEARCH_PAGE); + expect(wrapped.cause).toBe(original); + } + }); + + test("已有 hint 或非未开通错误原样抛出", () => { + const withHint = new BailianError( + "MCP request failed: 404 Not Found - MCP不存在或未开通", + ExitCode.GENERAL, + "already hinted", + ); + try { + rethrowWithWebSearchActivateHint(withHint); + expect.unreachable("should throw"); + } catch (error) { + expect(error).toBe(withHint); + } + + const other = new BailianError("MCP request failed: 401 Unauthorized"); + try { + rethrowWithWebSearchActivateHint(other); + expect.unreachable("should throw"); + } catch (error) { + expect(error).toBe(other); + } + }); +}); diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 4db579c1..7556c7a2 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -34,6 +34,7 @@ export { BAILIAN_CONSOLE, API_KEY_PAGE, TOKEN_PLAN_PAGE, + MCP_WEBSEARCH_PAGE, VOICE_TTS_PAGE, } from "./urls.ts"; diff --git a/packages/runtime/src/urls.ts b/packages/runtime/src/urls.ts index 60a3b33a..c230fe0c 100644 --- a/packages/runtime/src/urls.ts +++ b/packages/runtime/src/urls.ts @@ -18,5 +18,11 @@ export const API_KEY_PAGE = `${BAILIAN_CONSOLE}/?tab=app#/api-key`; /** Direct deep link to the Token Plan subscription overview and API key entry. */ export const TOKEN_PLAN_PAGE = `${BAILIAN_CONSOLE_ROOT}/cn-beijing?tab=plan#/efm/subscription/overview`; +/** + * MCP marketplace detail for the built-in WebSearch server. + * Users must activate (or re-activate for Streamable HTTP) before `search web` works. + */ +export const MCP_WEBSEARCH_PAGE = `${BAILIAN_CONSOLE}?tab=mcp#/mcp-market/detail/WebSearch`; + /** Voice TTS experience center — browse system and custom voices. */ export const VOICE_TTS_PAGE = "https://help.aliyun.com/zh/model-studio/cosyvoice-voice-list"; From 36ebd63716b7304abda603d346d7fbd3092dcda8 Mon Sep 17 00:00:00 2001 From: clh02467605 Date: Mon, 27 Jul 2026 14:57:50 +0800 Subject: [PATCH 2/7] fix(text): omit enable_thinking by default and retry when API requires false Non-streaming chat no longer forces enable_thinking=false, which breaks thinking-only models. Retry once with false only when the server demands it. --- .../src/commands/auth/login-api-key.ts | 57 +++++--- packages/commands/src/commands/text/chat.ts | 36 +++-- packages/commands/tests/e2e/auth.e2e.test.ts | 2 +- .../commands/tests/e2e/text-chat.e2e.test.ts | 13 +- packages/core/src/index.ts | 1 + packages/core/src/models/index.ts | 7 + packages/core/src/models/thinking.ts | 76 ++++++++++ packages/core/tests/thinking.test.ts | 136 ++++++++++++++++++ 8 files changed, 290 insertions(+), 38 deletions(-) create mode 100644 packages/core/src/models/index.ts create mode 100644 packages/core/src/models/thinking.ts create mode 100644 packages/core/tests/thinking.test.ts diff --git a/packages/commands/src/commands/auth/login-api-key.ts b/packages/commands/src/commands/auth/login-api-key.ts index 6200f8e6..94084984 100644 --- a/packages/commands/src/commands/auth/login-api-key.ts +++ b/packages/commands/src/commands/auth/login-api-key.ts @@ -4,6 +4,9 @@ import { chatPath, requestJson, normalizeModelBaseUrl, + applyChatEnableThinking, + resolveChatEnableThinking, + withEnableThinkingRetry, type AuthPersistPatch, type AuthStore, type Identity, @@ -58,32 +61,48 @@ export async function validateAndPersistApiKey( ? normalizeModelBaseUrl(profile.persistBaseUrl) : undefined; const validationModel = profile.defaultTextModel || "qwen3.7-max"; + const body: { + model: string; + messages: Array<{ role: string; content: string }>; + max_tokens: number; + stream: boolean; + enable_thinking?: boolean; + } = { + model: validationModel, + messages: [{ role: "user", content: "hi" }], + max_tokens: 1, + stream: false, + }; + const requestOpts = { url: baseUrl + chatPath(), method: "POST", headers: { Authorization: `Bearer ${key}` }, timeout: Math.min(deps.settings.timeout, 30), - body: { - model: validationModel, - messages: [{ role: "user", content: "hi" }], - max_tokens: 1, - stream: false, - enable_thinking: validationModel === "qwen3.8-max-preview", - }, + body, }; - for (let attempt = 1; attempt <= 3; attempt++) { - try { - await requestJson(httpDeps, requestOpts); - break; - } catch (error) { - if (attempt >= 3 || !canRetry(error)) { - process.stderr.write("Failed\n"); - throw error; - } - const delayMs = RETRY_DELAY_BASE_MS * 2 ** (attempt - 1); - await new Promise((resolve) => setTimeout(resolve, delayMs)); - } + try { + await withEnableThinkingRetry({ + // Validation requests are always non-streaming. + initial: resolveChatEnableThinking({ stream: false }), + apply: (value) => applyChatEnableThinking(body, value), + run: async () => { + for (let attempt = 1; attempt <= 3; attempt++) { + try { + await requestJson(httpDeps, requestOpts); + return; + } catch (error) { + if (attempt >= 3 || !canRetry(error)) throw error; + const delayMs = RETRY_DELAY_BASE_MS * 2 ** (attempt - 1); + await new Promise((resolve) => setTimeout(resolve, delayMs)); + } + } + }, + }); + } catch (error) { + process.stderr.write("Failed\n"); + throw error; } process.stderr.write("Valid\n"); diff --git a/packages/commands/src/commands/text/chat.ts b/packages/commands/src/commands/text/chat.ts index 0ab05a30..6117fa44 100644 --- a/packages/commands/src/commands/text/chat.ts +++ b/packages/commands/src/commands/text/chat.ts @@ -4,6 +4,9 @@ import { parseSSE, detectOutputFormat, readTextFromPathOrStdin, + applyChatEnableThinking, + resolveChatEnableThinking, + withEnableThinkingRetry, type ChatMessage, type ChatRequest, type ChatResponse, @@ -124,7 +127,8 @@ export default defineCommand({ const { system, messages } = parseMessages(flags); const model = flags.model || settings.defaultTextModel || "qwen3.7-max"; - const shouldStream = flags.stream || process.stdout.isTTY; + // Coerce isTTY (may be undefined) so stream:false is serialized. + const shouldStream = Boolean(flags.stream || process.stdout.isTTY); const format = detectOutputFormat(settings.output); // Build messages array with system prompt @@ -144,16 +148,13 @@ export default defineCommand({ if (flags.temperature !== undefined) body.temperature = flags.temperature; if (flags.topP !== undefined) body.top_p = flags.topP; - if (flags.enableThinking) { - body.enable_thinking = true; - if (flags.thinkingBudget !== undefined) { - body.thinking_budget = flags.thinkingBudget; - } - } else if (!shouldStream) { - // DashScope qwen3 models default to enable_thinking=true server-side, but - // non-streaming calls require it to be explicitly false. Stream calls - // support thinking, so leave the field unset there (server handles it). - body.enable_thinking = false; + const enableThinking = resolveChatEnableThinking({ + enableThinking: flags.enableThinking, + stream: shouldStream, + }); + applyChatEnableThinking(body, enableThinking); + if (enableThinking === true && flags.thinkingBudget !== undefined) { + body.thinking_budget = flags.thinkingBudget; } if (flags.tool) { @@ -229,10 +230,15 @@ export default defineCommand({ resultOut.write("\n"); } } else { - const response = await ctx.client.requestJson({ - path: chatPath(), - method: "POST", - body, + const response = await withEnableThinkingRetry({ + initial: enableThinking, + apply: (value) => applyChatEnableThinking(body, value), + run: () => + ctx.client.requestJson({ + path: chatPath(), + method: "POST", + body, + }), }); const text = response.choices?.[0]?.message?.content ?? ""; diff --git a/packages/commands/tests/e2e/auth.e2e.test.ts b/packages/commands/tests/e2e/auth.e2e.test.ts index 5db70c16..3a04b473 100644 --- a/packages/commands/tests/e2e/auth.e2e.test.ts +++ b/packages/commands/tests/e2e/auth.e2e.test.ts @@ -317,7 +317,7 @@ describe("e2e: auth", () => { body: { model: "qwen3.8-max-preview", stream: false, - enable_thinking: true, + enable_thinking: false, }, }); diff --git a/packages/commands/tests/e2e/text-chat.e2e.test.ts b/packages/commands/tests/e2e/text-chat.e2e.test.ts index a5f4d5e3..cbd70648 100644 --- a/packages/commands/tests/e2e/text-chat.e2e.test.ts +++ b/packages/commands/tests/e2e/text-chat.e2e.test.ts @@ -34,7 +34,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: text chat(DashScope)", () => { "--model", "qwen3.7-max", "--message", - "干跑", + "dry-run", "--max-tokens", "8", "--output", @@ -42,10 +42,17 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: text chat(DashScope)", () => { ]); expect(exitCode, stderr).toBe(0); const data = parseStdoutJson<{ - request?: { model?: string; messages?: Array<{ content?: string }> }; + request?: { + model?: string; + messages?: Array<{ content?: string }>; + enable_thinking?: boolean; + stream?: boolean; + }; }>(stdout); expect(data.request?.model).toBe("qwen3.7-max"); - expect(data.request?.messages?.some((m) => m.content === "干跑")).toBe(true); + expect(data.request?.messages?.some((message) => message.content === "dry-run")).toBe(true); + expect(data.request?.stream).toBe(false); + expect(data.request?.enable_thinking).toBe(false); }); test("【qwen3.7-max】文本对话", async () => { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e49ee6c4..10d0911b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -14,5 +14,6 @@ export * from "./finetune/index.ts"; export * from "./deploy/index.ts"; export * from "./types/index.ts"; export * from "./utils/index.ts"; +export * from "./models/index.ts"; export * from "./telemetry/index.ts"; export * from "./advisor/index.ts"; diff --git a/packages/core/src/models/index.ts b/packages/core/src/models/index.ts new file mode 100644 index 00000000..4af5e3d4 --- /dev/null +++ b/packages/core/src/models/index.ts @@ -0,0 +1,7 @@ +export { + adjustEnableThinkingAfterError, + applyChatEnableThinking, + resolveChatEnableThinking, + withEnableThinkingRetry, + type EnableThinkingAdjustResult, +} from "./thinking.ts"; diff --git a/packages/core/src/models/thinking.ts b/packages/core/src/models/thinking.ts new file mode 100644 index 00000000..a5d25b11 --- /dev/null +++ b/packages/core/src/models/thinking.ts @@ -0,0 +1,76 @@ +/** resolve / adjust / retry helpers for chat `enable_thinking`. */ + +/** Resolve the initial `enable_thinking` value (`undefined` omits the field). */ +export function resolveChatEnableThinking(options: { + enableThinking?: boolean; + /** Whether the request is streaming. */ + stream?: boolean; +}): boolean | undefined { + if (options.enableThinking) return true; + if (options.stream === false) return false; + return undefined; +} + +export type EnableThinkingAdjustResult = + | { kind: "retry"; value: boolean | undefined } + | { kind: "none" }; + +/** Map clear `enable_thinking` constraint errors to a one-shot retry adjustment. */ +export function adjustEnableThinkingAfterError( + current: boolean | undefined, + errorMessage: string, +): EnableThinkingAdjustResult { + if (current !== true && /enable_thinking parameter is restricted to\s*true/i.test(errorMessage)) { + return { kind: "retry", value: true }; + } + + if (current === undefined && /enable_thinking must be set to false/i.test(errorMessage)) { + return { kind: "retry", value: false }; + } + + if (current !== undefined && /does not support enable_thinking/i.test(errorMessage)) { + return { kind: "retry", value: undefined }; + } + + return { kind: "none" }; +} + +/** Set or remove `enable_thinking`; clear `thinking_budget` when disabled or omitted. */ +export function applyChatEnableThinking( + body: { enable_thinking?: boolean; thinking_budget?: number }, + value: boolean | undefined, +): void { + if (value === undefined) { + delete body.enable_thinking; + delete body.thinking_budget; + return; + } + if (value === false) { + body.enable_thinking = false; + delete body.thinking_budget; + return; + } + body.enable_thinking = true; +} + +function errorMessageOf(error: unknown): string { + if (error instanceof Error) return error.message; + return String(error); +} + +/** Run once, then retry once if the error indicates an `enable_thinking` constraint. */ +export async function withEnableThinkingRetry(options: { + initial: boolean | undefined; + apply: (value: boolean | undefined) => void; + run: () => Promise; +}): Promise { + options.apply(options.initial); + try { + return await options.run(); + } catch (error) { + const adjusted = adjustEnableThinkingAfterError(options.initial, errorMessageOf(error)); + if (adjusted.kind === "none") throw error; + options.apply(adjusted.value); + return await options.run(); + } +} diff --git a/packages/core/tests/thinking.test.ts b/packages/core/tests/thinking.test.ts new file mode 100644 index 00000000..547acf45 --- /dev/null +++ b/packages/core/tests/thinking.test.ts @@ -0,0 +1,136 @@ +import { expect, test } from "vite-plus/test"; +import { + adjustEnableThinkingAfterError, + applyChatEnableThinking, + resolveChatEnableThinking, + withEnableThinkingRetry, +} from "../src/models/thinking.ts"; + +test("resolveChatEnableThinking:显式开启为 true,非流式默认 false,流式默认 omit", () => { + expect(resolveChatEnableThinking({ enableThinking: true })).toBe(true); + expect(resolveChatEnableThinking({ enableThinking: true, stream: false })).toBe(true); + expect(resolveChatEnableThinking({ stream: false })).toBe(false); + expect(resolveChatEnableThinking({ enableThinking: false, stream: false })).toBe(false); + expect(resolveChatEnableThinking({ stream: true })).toBeUndefined(); + expect(resolveChatEnableThinking({})).toBeUndefined(); +}); + +test("adjustEnableThinkingAfterError:false/omit 被要求 true 时重试为 true", () => { + expect( + adjustEnableThinkingAfterError( + false, + "The value of the enable_thinking parameter is restricted to True.", + ), + ).toEqual({ kind: "retry", value: true }); + expect( + adjustEnableThinkingAfterError( + undefined, + "The value of the enable_thinking parameter is restricted to True.", + ), + ).toEqual({ kind: "retry", value: true }); +}); + +test("adjustEnableThinkingAfterError:omit 被要求 false 时重试为 false", () => { + expect( + adjustEnableThinkingAfterError( + undefined, + "parameter.enable_thinking must be set to false for non-streaming calls", + ), + ).toEqual({ kind: "retry", value: false }); +}); + +test("adjustEnableThinkingAfterError:不支持时去掉字段", () => { + expect( + adjustEnableThinkingAfterError(false, "The model qwen-turbo does not support enable_thinking."), + ).toEqual({ kind: "retry", value: undefined }); + expect( + adjustEnableThinkingAfterError(true, "The model qwen-turbo does not support enable_thinking."), + ).toEqual({ kind: "retry", value: undefined }); +}); + +test("adjustEnableThinkingAfterError:无关错误不调整", () => { + expect(adjustEnableThinkingAfterError(undefined, "Access denied")).toEqual({ kind: "none" }); + expect(adjustEnableThinkingAfterError(false, "Model not exist")).toEqual({ kind: "none" }); + expect( + adjustEnableThinkingAfterError( + true, + "The value of the enable_thinking parameter is restricted to True.", + ), + ).toEqual({ kind: "none" }); +}); + +test("applyChatEnableThinking:设置 / 删除字段,并在关闭时清 thinking_budget", () => { + const body: { enable_thinking?: boolean; thinking_budget?: number } = { + thinking_budget: 1024, + }; + applyChatEnableThinking(body, true); + expect(body.enable_thinking).toBe(true); + expect(body.thinking_budget).toBe(1024); + + applyChatEnableThinking(body, false); + expect(body.enable_thinking).toBe(false); + expect(body).not.toHaveProperty("thinking_budget"); + + body.thinking_budget = 2048; + applyChatEnableThinking(body, undefined); + expect(body).not.toHaveProperty("enable_thinking"); + expect(body).not.toHaveProperty("thinking_budget"); +}); + +test("withEnableThinkingRetry:restricted-to-true 时从 false 重试为 true", async () => { + const values: Array = []; + let calls = 0; + + const result = await withEnableThinkingRetry({ + initial: false, + apply: (value) => { + values.push(value); + }, + run: async () => { + calls += 1; + if (calls === 1) { + throw new Error("The value of the enable_thinking parameter is restricted to True."); + } + return "ok"; + }, + }); + + expect(result).toBe("ok"); + expect(calls).toBe(2); + expect(values).toEqual([false, true]); +}); + +test("withEnableThinkingRetry:must-be-false 时从 omit 重试为 false", async () => { + const values: Array = []; + let calls = 0; + + const result = await withEnableThinkingRetry({ + initial: undefined, + apply: (value) => { + values.push(value); + }, + run: async () => { + calls += 1; + if (calls === 1) { + throw new Error("parameter.enable_thinking must be set to false for non-streaming calls"); + } + return "ok"; + }, + }); + + expect(result).toBe("ok"); + expect(calls).toBe(2); + expect(values).toEqual([undefined, false]); +}); + +test("withEnableThinkingRetry:无关错误原样抛出", async () => { + await expect( + withEnableThinkingRetry({ + initial: false, + apply: () => {}, + run: async () => { + throw new Error("Access denied"); + }, + }), + ).rejects.toThrow(/Access denied/); +}); From 8211268bd89b0adccc2da7f4f749102aa7b81119 Mon Sep 17 00:00:00 2001 From: clh02467605 Date: Tue, 28 Jul 2026 09:25:02 +0800 Subject: [PATCH 3/7] fix(text,mcp): keep thinking_budget on enable_thinking retry and hint MCP activation on 404 --- docs/agents/url-change.md | 3 +- .../src/commands/mcp/activate-hint.ts | 39 +++++++++ packages/commands/src/commands/mcp/call.ts | 22 +++-- packages/commands/src/commands/mcp/tools.ts | 14 +++- .../src/commands/search/web-activate-hint.ts | 38 +++------ packages/commands/src/commands/text/chat.ts | 12 +-- .../commands/tests/mcp-activate-hint.test.ts | 81 +++++++++++++++++++ packages/core/src/models/index.ts | 1 + packages/core/src/models/thinking.ts | 12 +++ packages/core/tests/thinking.test.ts | 38 +++++++++ packages/runtime/src/index.ts | 1 + packages/runtime/src/urls.ts | 7 +- 12 files changed, 223 insertions(+), 45 deletions(-) create mode 100644 packages/commands/src/commands/mcp/activate-hint.ts create mode 100644 packages/commands/tests/mcp-activate-hint.test.ts diff --git a/docs/agents/url-change.md b/docs/agents/url-change.md index 515e3a42..25a7ea86 100644 --- a/docs/agents/url-change.md +++ b/docs/agents/url-change.md @@ -20,7 +20,8 @@ runtime/src/urls.ts ← 用户面控制台 URL(cn-only) BAILIAN_CONSOLE BAILIAN_CONSOLE_ROOT/cn-beijing API_KEY_PAGE BAILIAN_CONSOLE/?tab=app#/api-key TOKEN_PLAN_PAGE BAILIAN_CONSOLE_ROOT/cn-beijing?tab=plan#/efm/subscription/overview - MCP_WEBSEARCH_PAGE BAILIAN_CONSOLE?tab=mcp#/mcp-market/detail/WebSearch + MCP_WEBSEARCH_PAGE mcpMarketplaceDetailPage("WebSearch") + mcpMarketplaceDetailPage BAILIAN_CONSOLE?tab=mcp#/mcp-market/detail/ core/files/upload.ts ← 文件上传 endpoint(cn-pinned) UPLOAD_API ${REGIONS.cn}/api/v1/uploads diff --git a/packages/commands/src/commands/mcp/activate-hint.ts b/packages/commands/src/commands/mcp/activate-hint.ts new file mode 100644 index 00000000..ff7e11c0 --- /dev/null +++ b/packages/commands/src/commands/mcp/activate-hint.ts @@ -0,0 +1,39 @@ +import { BailianError } from "bailian-cli-core"; +import { mcpMarketplaceDetailPage } from "bailian-cli-runtime"; + +/** Detect MCP-not-activated / invalid 404 errors (CLI-wrapped server message). */ +export function isMcpNotActivated(error: unknown): boolean { + if (!(error instanceof BailianError)) return false; + const message = error.message; + if (!/MCP request failed:\s*404\b/i.test(message)) return false; + return /未开通|MCP不存在|MCP_IS_INVALID/i.test(message); +} + +/** Activation hint; URL from runtime/urls.ts. */ +export function mcpActivateHint(serverCode: string): string { + const lines = [ + `Activate (or re-activate) the ${serverCode} MCP in the Bailian MCP marketplace, then retry.`, + ]; + if (serverCode === "WebSearch") { + lines.push( + "If it was previously on SSE, cancel and activate again to upgrade to Streamable HTTP.", + ); + } + lines.push(`Open: ${mcpMarketplaceDetailPage(serverCode)}`); + return lines.join("\n"); +} + +/** + * For not-activated errors, keep the original message / exitCode and append a hint only. + * Do not replace the server error message. + */ +export function rethrowWithMcpActivateHint(error: unknown, serverCode: string): never { + if (isMcpNotActivated(error) && error instanceof BailianError && !error.hint) { + throw new BailianError(error.message, error.exitCode, mcpActivateHint(serverCode), { + cause: error, + api: error.api, + rawResponse: error.rawResponse, + }); + } + throw error; +} diff --git a/packages/commands/src/commands/mcp/call.ts b/packages/commands/src/commands/mcp/call.ts index b0517cd6..3d6ba0b0 100644 --- a/packages/commands/src/commands/mcp/call.ts +++ b/packages/commands/src/commands/mcp/call.ts @@ -8,6 +8,7 @@ import { type ParsedFlags, } from "bailian-cli-core"; import { emitResult } from "bailian-cli-runtime"; +import { rethrowWithMcpActivateHint } from "./activate-hint.ts"; const CALL_FLAGS = { target: { @@ -130,14 +131,21 @@ export default defineCommand({ } const client = ctx.client.mcp(url); - await client.initialize(); - const result = await client.callTool(toolName, toolArgs); + try { + await client.initialize(); + const result = await client.callTool(toolName, toolArgs); - if (result.isError) { - const errText = result.content.map((c) => c.text || "").join("\n"); - throw new BailianError(`Tool error: ${errText}`); - } + if (result.isError) { + const errText = result.content.map((c) => c.text || "").join("\n"); + throw new BailianError(`Tool error: ${errText}`); + } - emitResult(result, format); + emitResult(result, format); + } catch (error) { + if (!flags.url) { + rethrowWithMcpActivateHint(error, serverCode); + } + throw error; + } }, }); diff --git a/packages/commands/src/commands/mcp/tools.ts b/packages/commands/src/commands/mcp/tools.ts index bc69128b..fc38f428 100644 --- a/packages/commands/src/commands/mcp/tools.ts +++ b/packages/commands/src/commands/mcp/tools.ts @@ -1,5 +1,6 @@ import { defineCommand, bailianMcpPath, detectOutputFormat } from "bailian-cli-core"; import { emitResult } from "bailian-cli-runtime"; +import { rethrowWithMcpActivateHint } from "./activate-hint.ts"; export default defineCommand({ description: "List tools exposed by an MCP server (tools/list)", @@ -36,8 +37,15 @@ export default defineCommand({ } const client = ctx.client.mcp(url); - await client.initialize(); - const tools = await client.listTools(); - emitResult({ server: code, url, tools }, format); + try { + await client.initialize(); + const tools = await client.listTools(); + emitResult({ server: code, url, tools }, format); + } catch (error) { + if (!flags.url) { + rethrowWithMcpActivateHint(error, code); + } + throw error; + } }, }); diff --git a/packages/commands/src/commands/search/web-activate-hint.ts b/packages/commands/src/commands/search/web-activate-hint.ts index 6f5d93e9..cbf6c7e8 100644 --- a/packages/commands/src/commands/search/web-activate-hint.ts +++ b/packages/commands/src/commands/search/web-activate-hint.ts @@ -1,34 +1,18 @@ -import { BailianError } from "bailian-cli-core"; -import { MCP_WEBSEARCH_PAGE } from "bailian-cli-runtime"; +import { + isMcpNotActivated, + mcpActivateHint, + rethrowWithMcpActivateHint, +} from "../mcp/activate-hint.ts"; -/** recoginze WebSearch MCP not activated / invalid caused 404 (CLI wrapped message from server)。 */ -export function isWebSearchMcpNotActivated(error: unknown): boolean { - if (!(error instanceof BailianError)) return false; - const message = error.message; - if (!/MCP request failed:\s*404\b/i.test(message)) return false; - return /未开通|MCP不存在|MCP_IS_INVALID/i.test(message); -} +/** Detect WebSearch MCP not-activated / invalid 404 errors. */ +export const isWebSearchMcpNotActivated = isMcpNotActivated; -/** activate hint; URL from runtime/urls.ts。 */ +/** WebSearch activation hint. */ export function webSearchActivateHint(): string { - return [ - "Activate (or re-activate) the WebSearch MCP in the Bailian MCP marketplace, then retry.", - "If it was previously on SSE, cancel and activate again to upgrade to Streamable HTTP.", - `Open: ${MCP_WEBSEARCH_PAGE}`, - ].join("\n"); + return mcpActivateHint("WebSearch"); } -/** - * keep original message / exitCode for not activated errors, add hint only; other errors throw as is. - * do not replace server error message. - */ +/** Keep the original message; append a hint for WebSearch not-activated errors. */ export function rethrowWithWebSearchActivateHint(error: unknown): never { - if (isWebSearchMcpNotActivated(error) && error instanceof BailianError && !error.hint) { - throw new BailianError(error.message, error.exitCode, webSearchActivateHint(), { - cause: error, - api: error.api, - rawResponse: error.rawResponse, - }); - } - throw error; + rethrowWithMcpActivateHint(error, "WebSearch"); } diff --git a/packages/commands/src/commands/text/chat.ts b/packages/commands/src/commands/text/chat.ts index 6117fa44..1107c754 100644 --- a/packages/commands/src/commands/text/chat.ts +++ b/packages/commands/src/commands/text/chat.ts @@ -4,7 +4,7 @@ import { parseSSE, detectOutputFormat, readTextFromPathOrStdin, - applyChatEnableThinking, + applyChatEnableThinkingWithBudget, resolveChatEnableThinking, withEnableThinkingRetry, type ChatMessage, @@ -152,10 +152,10 @@ export default defineCommand({ enableThinking: flags.enableThinking, stream: shouldStream, }); - applyChatEnableThinking(body, enableThinking); - if (enableThinking === true && flags.thinkingBudget !== undefined) { - body.thinking_budget = flags.thinkingBudget; - } + const applyThinking = (value: boolean | undefined) => { + applyChatEnableThinkingWithBudget(body, value, flags.thinkingBudget); + }; + applyThinking(enableThinking); if (flags.tool) { const tools = flags.tool.map((t) => { @@ -232,7 +232,7 @@ export default defineCommand({ } else { const response = await withEnableThinkingRetry({ initial: enableThinking, - apply: (value) => applyChatEnableThinking(body, value), + apply: applyThinking, run: () => ctx.client.requestJson({ path: chatPath(), diff --git a/packages/commands/tests/mcp-activate-hint.test.ts b/packages/commands/tests/mcp-activate-hint.test.ts new file mode 100644 index 00000000..823a6b83 --- /dev/null +++ b/packages/commands/tests/mcp-activate-hint.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, test } from "vite-plus/test"; +import { BailianError, ExitCode } from "bailian-cli-core"; +import { mcpMarketplaceDetailPage } from "bailian-cli-runtime"; +import { + isMcpNotActivated, + mcpActivateHint, + rethrowWithMcpActivateHint, +} from "../src/commands/mcp/activate-hint.ts"; + +describe("mcp-activate-hint", () => { + test("识别 404 + 未开通 / MCP不存在 / MCP_IS_INVALID", () => { + expect( + isMcpNotActivated(new BailianError("MCP request failed: 404 Not Found - MCP不存在或未开通")), + ).toBe(true); + expect( + isMcpNotActivated(new BailianError("MCP request failed: 404 - MCP不存在或未开通")), + ).toBe(true); + expect( + isMcpNotActivated(new BailianError("MCP request failed: 404 Not Found - MCP_IS_INVALID")), + ).toBe(true); + }); + + test("裸 404 或非 MCP 错误不加开通判定", () => { + expect(isMcpNotActivated(new BailianError("MCP request failed: 404 Not Found"))).toBe(false); + expect(isMcpNotActivated(new BailianError("MCP request failed: 405 Method Not Allowed"))).toBe( + false, + ); + expect(isMcpNotActivated(new Error("MCP不存在或未开通"))).toBe(false); + }); + + test("hint 含对应 server 的 MCP 广场深链", () => { + const serverCode = "market-cmapi00073529"; + expect(mcpActivateHint(serverCode)).toContain(mcpMarketplaceDetailPage(serverCode)); + expect(mcpActivateHint(serverCode)).toMatch(/Activate|re-activate/i); + }); + + test("WebSearch hint 含 SSE 升级说明", () => { + expect(mcpActivateHint("WebSearch")).toMatch(/SSE|Streamable HTTP/i); + }); + + test("rethrow 保留原 message,补 hint", () => { + const serverCode = "market-cmapi00073529"; + const original = new BailianError( + "MCP request failed: 404 Not Found - MCP不存在或未开通", + ExitCode.GENERAL, + ); + try { + rethrowWithMcpActivateHint(original, serverCode); + expect.unreachable("should throw"); + } catch (error) { + expect(error).toBeInstanceOf(BailianError); + const wrapped = error as BailianError; + expect(wrapped.message).toBe(original.message); + expect(wrapped.exitCode).toBe(ExitCode.GENERAL); + expect(wrapped.hint).toContain(mcpMarketplaceDetailPage(serverCode)); + expect(wrapped.cause).toBe(original); + } + }); + + test("已有 hint 或非未开通错误原样抛出", () => { + const withHint = new BailianError( + "MCP request failed: 404 Not Found - MCP不存在或未开通", + ExitCode.GENERAL, + "already hinted", + ); + try { + rethrowWithMcpActivateHint(withHint, "WebSearch"); + expect.unreachable("should throw"); + } catch (error) { + expect(error).toBe(withHint); + } + + const other = new BailianError("MCP request failed: 401 Unauthorized"); + try { + rethrowWithMcpActivateHint(other, "WebSearch"); + expect.unreachable("should throw"); + } catch (error) { + expect(error).toBe(other); + } + }); +}); diff --git a/packages/core/src/models/index.ts b/packages/core/src/models/index.ts index 4af5e3d4..7fbcca27 100644 --- a/packages/core/src/models/index.ts +++ b/packages/core/src/models/index.ts @@ -1,6 +1,7 @@ export { adjustEnableThinkingAfterError, applyChatEnableThinking, + applyChatEnableThinkingWithBudget, resolveChatEnableThinking, withEnableThinkingRetry, type EnableThinkingAdjustResult, diff --git a/packages/core/src/models/thinking.ts b/packages/core/src/models/thinking.ts index a5d25b11..2334b750 100644 --- a/packages/core/src/models/thinking.ts +++ b/packages/core/src/models/thinking.ts @@ -53,6 +53,18 @@ export function applyChatEnableThinking( body.enable_thinking = true; } +/** Set `enable_thinking` and optionally write `thinking_budget` when enabled. */ +export function applyChatEnableThinkingWithBudget( + body: { enable_thinking?: boolean; thinking_budget?: number }, + value: boolean | undefined, + thinkingBudget?: number, +): void { + applyChatEnableThinking(body, value); + if (value === true && thinkingBudget !== undefined) { + body.thinking_budget = thinkingBudget; + } +} + function errorMessageOf(error: unknown): string { if (error instanceof Error) return error.message; return String(error); diff --git a/packages/core/tests/thinking.test.ts b/packages/core/tests/thinking.test.ts index 547acf45..9977f2ab 100644 --- a/packages/core/tests/thinking.test.ts +++ b/packages/core/tests/thinking.test.ts @@ -2,6 +2,7 @@ import { expect, test } from "vite-plus/test"; import { adjustEnableThinkingAfterError, applyChatEnableThinking, + applyChatEnableThinkingWithBudget, resolveChatEnableThinking, withEnableThinkingRetry, } from "../src/models/thinking.ts"; @@ -123,6 +124,43 @@ test("withEnableThinkingRetry:must-be-false 时从 omit 重试为 false", asyn expect(values).toEqual([undefined, false]); }); +test("applyChatEnableThinkingWithBudget:仅在 enable_thinking=true 时写入 budget", () => { + const body: { enable_thinking?: boolean; thinking_budget?: number } = {}; + applyChatEnableThinkingWithBudget(body, false, 2048); + expect(body.enable_thinking).toBe(false); + expect(body).not.toHaveProperty("thinking_budget"); + + applyChatEnableThinkingWithBudget(body, undefined, 2048); + expect(body).not.toHaveProperty("enable_thinking"); + expect(body).not.toHaveProperty("thinking_budget"); + + applyChatEnableThinkingWithBudget(body, true, 2048); + expect(body.enable_thinking).toBe(true); + expect(body.thinking_budget).toBe(2048); +}); + +test("withEnableThinkingRetry:restricted-to-true 重试时保留 thinking_budget", async () => { + const body: { enable_thinking?: boolean; thinking_budget?: number } = {}; + let calls = 0; + + const result = await withEnableThinkingRetry({ + initial: false, + apply: (value) => applyChatEnableThinkingWithBudget(body, value, 2048), + run: async () => { + calls += 1; + if (calls === 1) { + throw new Error("The value of the enable_thinking parameter is restricted to True."); + } + return "ok"; + }, + }); + + expect(result).toBe("ok"); + expect(calls).toBe(2); + expect(body.enable_thinking).toBe(true); + expect(body.thinking_budget).toBe(2048); +}); + test("withEnableThinkingRetry:无关错误原样抛出", async () => { await expect( withEnableThinkingRetry({ diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 7556c7a2..83c13181 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -35,6 +35,7 @@ export { API_KEY_PAGE, TOKEN_PLAN_PAGE, MCP_WEBSEARCH_PAGE, + mcpMarketplaceDetailPage, VOICE_TTS_PAGE, } from "./urls.ts"; diff --git a/packages/runtime/src/urls.ts b/packages/runtime/src/urls.ts index c230fe0c..16ada5ce 100644 --- a/packages/runtime/src/urls.ts +++ b/packages/runtime/src/urls.ts @@ -18,11 +18,16 @@ export const API_KEY_PAGE = `${BAILIAN_CONSOLE}/?tab=app#/api-key`; /** Direct deep link to the Token Plan subscription overview and API key entry. */ export const TOKEN_PLAN_PAGE = `${BAILIAN_CONSOLE_ROOT}/cn-beijing?tab=plan#/efm/subscription/overview`; +/** MCP marketplace detail page for a server code (e.g. WebSearch, market-cmapi00073529). */ +export function mcpMarketplaceDetailPage(serverCode: string): string { + return `${BAILIAN_CONSOLE}?tab=mcp#/mcp-market/detail/${serverCode}`; +} + /** * MCP marketplace detail for the built-in WebSearch server. * Users must activate (or re-activate for Streamable HTTP) before `search web` works. */ -export const MCP_WEBSEARCH_PAGE = `${BAILIAN_CONSOLE}?tab=mcp#/mcp-market/detail/WebSearch`; +export const MCP_WEBSEARCH_PAGE = mcpMarketplaceDetailPage("WebSearch"); /** Voice TTS experience center — browse system and custom voices. */ export const VOICE_TTS_PAGE = "https://help.aliyun.com/zh/model-studio/cosyvoice-voice-list"; From 20e3555b84960aa8e2b3fbd456363b02621f7030 Mon Sep 17 00:00:00 2001 From: clh02467605 Date: Tue, 28 Jul 2026 17:19:05 +0800 Subject: [PATCH 4/7] fix(text,auth): drop enable_thinking retry and omit the field by default Pass through model constraint errors instead of auto-retrying, switch token-plan default text model to qwen3.7-plus, and align related e2e expectations. --- .../src/commands/auth/login-api-key.ts | 56 ++---- packages/commands/src/commands/text/chat.ts | 33 ++-- packages/commands/tests/e2e/auth.e2e.test.ts | 8 +- .../commands/tests/e2e/text-chat.e2e.test.ts | 13 +- .../tests/e2e/vision-describe.e2e.test.ts | 4 +- packages/core/src/config/profile-presets.ts | 2 +- packages/core/src/index.ts | 1 - packages/core/src/models/index.ts | 8 - packages/core/src/models/thinking.ts | 88 --------- packages/core/tests/thinking.test.ts | 174 ------------------ 10 files changed, 38 insertions(+), 349 deletions(-) delete mode 100644 packages/core/src/models/index.ts delete mode 100644 packages/core/src/models/thinking.ts delete mode 100644 packages/core/tests/thinking.test.ts diff --git a/packages/commands/src/commands/auth/login-api-key.ts b/packages/commands/src/commands/auth/login-api-key.ts index 94084984..476e6bdf 100644 --- a/packages/commands/src/commands/auth/login-api-key.ts +++ b/packages/commands/src/commands/auth/login-api-key.ts @@ -4,9 +4,6 @@ import { chatPath, requestJson, normalizeModelBaseUrl, - applyChatEnableThinking, - resolveChatEnableThinking, - withEnableThinkingRetry, type AuthPersistPatch, type AuthStore, type Identity, @@ -61,48 +58,31 @@ export async function validateAndPersistApiKey( ? normalizeModelBaseUrl(profile.persistBaseUrl) : undefined; const validationModel = profile.defaultTextModel || "qwen3.7-max"; - const body: { - model: string; - messages: Array<{ role: string; content: string }>; - max_tokens: number; - stream: boolean; - enable_thinking?: boolean; - } = { - model: validationModel, - messages: [{ role: "user", content: "hi" }], - max_tokens: 1, - stream: false, - }; - const requestOpts = { url: baseUrl + chatPath(), method: "POST", headers: { Authorization: `Bearer ${key}` }, timeout: Math.min(deps.settings.timeout, 30), - body, + body: { + model: validationModel, + messages: [{ role: "user", content: "hi" }], + max_tokens: 1, + stream: false, + }, }; - try { - await withEnableThinkingRetry({ - // Validation requests are always non-streaming. - initial: resolveChatEnableThinking({ stream: false }), - apply: (value) => applyChatEnableThinking(body, value), - run: async () => { - for (let attempt = 1; attempt <= 3; attempt++) { - try { - await requestJson(httpDeps, requestOpts); - return; - } catch (error) { - if (attempt >= 3 || !canRetry(error)) throw error; - const delayMs = RETRY_DELAY_BASE_MS * 2 ** (attempt - 1); - await new Promise((resolve) => setTimeout(resolve, delayMs)); - } - } - }, - }); - } catch (error) { - process.stderr.write("Failed\n"); - throw error; + for (let attempt = 1; attempt <= 3; attempt++) { + try { + await requestJson(httpDeps, requestOpts); + break; + } catch (error) { + if (attempt >= 3 || !canRetry(error)) { + process.stderr.write("Failed\n"); + throw error; + } + const delayMs = RETRY_DELAY_BASE_MS * 2 ** (attempt - 1); + await new Promise((resolve) => setTimeout(resolve, delayMs)); + } } process.stderr.write("Valid\n"); diff --git a/packages/commands/src/commands/text/chat.ts b/packages/commands/src/commands/text/chat.ts index 1107c754..85879d1d 100644 --- a/packages/commands/src/commands/text/chat.ts +++ b/packages/commands/src/commands/text/chat.ts @@ -4,9 +4,6 @@ import { parseSSE, detectOutputFormat, readTextFromPathOrStdin, - applyChatEnableThinkingWithBudget, - resolveChatEnableThinking, - withEnableThinkingRetry, type ChatMessage, type ChatRequest, type ChatResponse, @@ -127,8 +124,7 @@ export default defineCommand({ const { system, messages } = parseMessages(flags); const model = flags.model || settings.defaultTextModel || "qwen3.7-max"; - // Coerce isTTY (may be undefined) so stream:false is serialized. - const shouldStream = Boolean(flags.stream || process.stdout.isTTY); + const shouldStream = flags.stream || process.stdout.isTTY; const format = detectOutputFormat(settings.output); // Build messages array with system prompt @@ -148,14 +144,12 @@ export default defineCommand({ if (flags.temperature !== undefined) body.temperature = flags.temperature; if (flags.topP !== undefined) body.top_p = flags.topP; - const enableThinking = resolveChatEnableThinking({ - enableThinking: flags.enableThinking, - stream: shouldStream, - }); - const applyThinking = (value: boolean | undefined) => { - applyChatEnableThinkingWithBudget(body, value, flags.thinkingBudget); - }; - applyThinking(enableThinking); + if (flags.enableThinking) { + body.enable_thinking = true; + if (flags.thinkingBudget !== undefined) { + body.thinking_budget = flags.thinkingBudget; + } + } if (flags.tool) { const tools = flags.tool.map((t) => { @@ -230,15 +224,10 @@ export default defineCommand({ resultOut.write("\n"); } } else { - const response = await withEnableThinkingRetry({ - initial: enableThinking, - apply: applyThinking, - run: () => - ctx.client.requestJson({ - path: chatPath(), - method: "POST", - body, - }), + const response = await ctx.client.requestJson({ + path: chatPath(), + method: "POST", + body, }); const text = response.choices?.[0]?.message?.content ?? ""; diff --git a/packages/commands/tests/e2e/auth.e2e.test.ts b/packages/commands/tests/e2e/auth.e2e.test.ts index 3a04b473..29fe1326 100644 --- a/packages/commands/tests/e2e/auth.e2e.test.ts +++ b/packages/commands/tests/e2e/auth.e2e.test.ts @@ -219,7 +219,6 @@ describe("e2e: auth", () => { body: { model: "qwen3.7-max", stream: false, - enable_thinking: false, }, }); @@ -267,7 +266,7 @@ describe("e2e: auth", () => { expect(config["token-plan"]).toMatchObject({ api_key: "sk-sp-e2e-placeholder", base_url: validationServer.baseUrl, - default_text_model: "qwen3.8-max-preview", + default_text_model: "qwen3.7-plus", default_video_model: "happyhorse-1.1-t2v", default_image_to_video_model: "happyhorse-1.1-i2v", default_reference_to_video_model: "happyhorse-1.1-r2v", @@ -315,9 +314,8 @@ describe("e2e: auth", () => { authorization: "Bearer sk-sp-e2e-placeholder", sourceConfig: expect.any(String), body: { - model: "qwen3.8-max-preview", + model: "qwen3.7-plus", stream: false, - enable_thinking: false, }, }); @@ -330,7 +328,7 @@ describe("e2e: auth", () => { expect(config["token-plan"]).toMatchObject({ api_key: "sk-sp-e2e-placeholder", base_url: "https://token-plan.cn-beijing.maas.aliyuncs.com", - default_text_model: "qwen3.8-max-preview", + default_text_model: "qwen3.7-plus", default_video_model: "happyhorse-1.1-t2v", default_image_to_video_model: "happyhorse-1.1-i2v", default_reference_to_video_model: "happyhorse-1.1-r2v", diff --git a/packages/commands/tests/e2e/text-chat.e2e.test.ts b/packages/commands/tests/e2e/text-chat.e2e.test.ts index cbd70648..a5f4d5e3 100644 --- a/packages/commands/tests/e2e/text-chat.e2e.test.ts +++ b/packages/commands/tests/e2e/text-chat.e2e.test.ts @@ -34,7 +34,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: text chat(DashScope)", () => { "--model", "qwen3.7-max", "--message", - "dry-run", + "干跑", "--max-tokens", "8", "--output", @@ -42,17 +42,10 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: text chat(DashScope)", () => { ]); expect(exitCode, stderr).toBe(0); const data = parseStdoutJson<{ - request?: { - model?: string; - messages?: Array<{ content?: string }>; - enable_thinking?: boolean; - stream?: boolean; - }; + request?: { model?: string; messages?: Array<{ content?: string }> }; }>(stdout); expect(data.request?.model).toBe("qwen3.7-max"); - expect(data.request?.messages?.some((message) => message.content === "dry-run")).toBe(true); - expect(data.request?.stream).toBe(false); - expect(data.request?.enable_thinking).toBe(false); + expect(data.request?.messages?.some((m) => m.content === "干跑")).toBe(true); }); test("【qwen3.7-max】文本对话", async () => { diff --git a/packages/commands/tests/e2e/vision-describe.e2e.test.ts b/packages/commands/tests/e2e/vision-describe.e2e.test.ts index a063911e..6d92d3ca 100644 --- a/packages/commands/tests/e2e/vision-describe.e2e.test.ts +++ b/packages/commands/tests/e2e/vision-describe.e2e.test.ts @@ -13,7 +13,7 @@ describe("e2e: vision describe", () => { "token-plan": { api_key: "sk-sp-e2e-placeholder", base_url: "https://token-plan.cn-beijing.maas.aliyuncs.com", - default_text_model: "qwen3.8-max-preview", + default_text_model: "qwen3.7-plus", }, }), ); @@ -40,6 +40,6 @@ describe("e2e: vision describe", () => { expect(exitCode, stderr).toBe(0); const data = parseStdoutJson<{ request?: { model?: string } }>(stdout); - expect(data.request?.model).toBe("qwen3.8-max-preview"); + expect(data.request?.model).toBe("qwen3.7-plus"); }); }); diff --git a/packages/core/src/config/profile-presets.ts b/packages/core/src/config/profile-presets.ts index f1d167c2..94bf0fe9 100644 --- a/packages/core/src/config/profile-presets.ts +++ b/packages/core/src/config/profile-presets.ts @@ -10,7 +10,7 @@ interface ModelProfilePreset { const MODEL_PROFILE_PRESETS: Readonly> = { "token-plan": { baseUrl: "https://token-plan.cn-beijing.maas.aliyuncs.com", - defaultTextModel: "qwen3.8-max-preview", + defaultTextModel: "qwen3.7-plus", defaultVideoModel: "happyhorse-1.1-t2v", defaultImageToVideoModel: "happyhorse-1.1-i2v", defaultReferenceToVideoModel: "happyhorse-1.1-r2v", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 10d0911b..e49ee6c4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -14,6 +14,5 @@ export * from "./finetune/index.ts"; export * from "./deploy/index.ts"; export * from "./types/index.ts"; export * from "./utils/index.ts"; -export * from "./models/index.ts"; export * from "./telemetry/index.ts"; export * from "./advisor/index.ts"; diff --git a/packages/core/src/models/index.ts b/packages/core/src/models/index.ts deleted file mode 100644 index 7fbcca27..00000000 --- a/packages/core/src/models/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { - adjustEnableThinkingAfterError, - applyChatEnableThinking, - applyChatEnableThinkingWithBudget, - resolveChatEnableThinking, - withEnableThinkingRetry, - type EnableThinkingAdjustResult, -} from "./thinking.ts"; diff --git a/packages/core/src/models/thinking.ts b/packages/core/src/models/thinking.ts deleted file mode 100644 index 2334b750..00000000 --- a/packages/core/src/models/thinking.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** resolve / adjust / retry helpers for chat `enable_thinking`. */ - -/** Resolve the initial `enable_thinking` value (`undefined` omits the field). */ -export function resolveChatEnableThinking(options: { - enableThinking?: boolean; - /** Whether the request is streaming. */ - stream?: boolean; -}): boolean | undefined { - if (options.enableThinking) return true; - if (options.stream === false) return false; - return undefined; -} - -export type EnableThinkingAdjustResult = - | { kind: "retry"; value: boolean | undefined } - | { kind: "none" }; - -/** Map clear `enable_thinking` constraint errors to a one-shot retry adjustment. */ -export function adjustEnableThinkingAfterError( - current: boolean | undefined, - errorMessage: string, -): EnableThinkingAdjustResult { - if (current !== true && /enable_thinking parameter is restricted to\s*true/i.test(errorMessage)) { - return { kind: "retry", value: true }; - } - - if (current === undefined && /enable_thinking must be set to false/i.test(errorMessage)) { - return { kind: "retry", value: false }; - } - - if (current !== undefined && /does not support enable_thinking/i.test(errorMessage)) { - return { kind: "retry", value: undefined }; - } - - return { kind: "none" }; -} - -/** Set or remove `enable_thinking`; clear `thinking_budget` when disabled or omitted. */ -export function applyChatEnableThinking( - body: { enable_thinking?: boolean; thinking_budget?: number }, - value: boolean | undefined, -): void { - if (value === undefined) { - delete body.enable_thinking; - delete body.thinking_budget; - return; - } - if (value === false) { - body.enable_thinking = false; - delete body.thinking_budget; - return; - } - body.enable_thinking = true; -} - -/** Set `enable_thinking` and optionally write `thinking_budget` when enabled. */ -export function applyChatEnableThinkingWithBudget( - body: { enable_thinking?: boolean; thinking_budget?: number }, - value: boolean | undefined, - thinkingBudget?: number, -): void { - applyChatEnableThinking(body, value); - if (value === true && thinkingBudget !== undefined) { - body.thinking_budget = thinkingBudget; - } -} - -function errorMessageOf(error: unknown): string { - if (error instanceof Error) return error.message; - return String(error); -} - -/** Run once, then retry once if the error indicates an `enable_thinking` constraint. */ -export async function withEnableThinkingRetry(options: { - initial: boolean | undefined; - apply: (value: boolean | undefined) => void; - run: () => Promise; -}): Promise { - options.apply(options.initial); - try { - return await options.run(); - } catch (error) { - const adjusted = adjustEnableThinkingAfterError(options.initial, errorMessageOf(error)); - if (adjusted.kind === "none") throw error; - options.apply(adjusted.value); - return await options.run(); - } -} diff --git a/packages/core/tests/thinking.test.ts b/packages/core/tests/thinking.test.ts deleted file mode 100644 index 9977f2ab..00000000 --- a/packages/core/tests/thinking.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { expect, test } from "vite-plus/test"; -import { - adjustEnableThinkingAfterError, - applyChatEnableThinking, - applyChatEnableThinkingWithBudget, - resolveChatEnableThinking, - withEnableThinkingRetry, -} from "../src/models/thinking.ts"; - -test("resolveChatEnableThinking:显式开启为 true,非流式默认 false,流式默认 omit", () => { - expect(resolveChatEnableThinking({ enableThinking: true })).toBe(true); - expect(resolveChatEnableThinking({ enableThinking: true, stream: false })).toBe(true); - expect(resolveChatEnableThinking({ stream: false })).toBe(false); - expect(resolveChatEnableThinking({ enableThinking: false, stream: false })).toBe(false); - expect(resolveChatEnableThinking({ stream: true })).toBeUndefined(); - expect(resolveChatEnableThinking({})).toBeUndefined(); -}); - -test("adjustEnableThinkingAfterError:false/omit 被要求 true 时重试为 true", () => { - expect( - adjustEnableThinkingAfterError( - false, - "The value of the enable_thinking parameter is restricted to True.", - ), - ).toEqual({ kind: "retry", value: true }); - expect( - adjustEnableThinkingAfterError( - undefined, - "The value of the enable_thinking parameter is restricted to True.", - ), - ).toEqual({ kind: "retry", value: true }); -}); - -test("adjustEnableThinkingAfterError:omit 被要求 false 时重试为 false", () => { - expect( - adjustEnableThinkingAfterError( - undefined, - "parameter.enable_thinking must be set to false for non-streaming calls", - ), - ).toEqual({ kind: "retry", value: false }); -}); - -test("adjustEnableThinkingAfterError:不支持时去掉字段", () => { - expect( - adjustEnableThinkingAfterError(false, "The model qwen-turbo does not support enable_thinking."), - ).toEqual({ kind: "retry", value: undefined }); - expect( - adjustEnableThinkingAfterError(true, "The model qwen-turbo does not support enable_thinking."), - ).toEqual({ kind: "retry", value: undefined }); -}); - -test("adjustEnableThinkingAfterError:无关错误不调整", () => { - expect(adjustEnableThinkingAfterError(undefined, "Access denied")).toEqual({ kind: "none" }); - expect(adjustEnableThinkingAfterError(false, "Model not exist")).toEqual({ kind: "none" }); - expect( - adjustEnableThinkingAfterError( - true, - "The value of the enable_thinking parameter is restricted to True.", - ), - ).toEqual({ kind: "none" }); -}); - -test("applyChatEnableThinking:设置 / 删除字段,并在关闭时清 thinking_budget", () => { - const body: { enable_thinking?: boolean; thinking_budget?: number } = { - thinking_budget: 1024, - }; - applyChatEnableThinking(body, true); - expect(body.enable_thinking).toBe(true); - expect(body.thinking_budget).toBe(1024); - - applyChatEnableThinking(body, false); - expect(body.enable_thinking).toBe(false); - expect(body).not.toHaveProperty("thinking_budget"); - - body.thinking_budget = 2048; - applyChatEnableThinking(body, undefined); - expect(body).not.toHaveProperty("enable_thinking"); - expect(body).not.toHaveProperty("thinking_budget"); -}); - -test("withEnableThinkingRetry:restricted-to-true 时从 false 重试为 true", async () => { - const values: Array = []; - let calls = 0; - - const result = await withEnableThinkingRetry({ - initial: false, - apply: (value) => { - values.push(value); - }, - run: async () => { - calls += 1; - if (calls === 1) { - throw new Error("The value of the enable_thinking parameter is restricted to True."); - } - return "ok"; - }, - }); - - expect(result).toBe("ok"); - expect(calls).toBe(2); - expect(values).toEqual([false, true]); -}); - -test("withEnableThinkingRetry:must-be-false 时从 omit 重试为 false", async () => { - const values: Array = []; - let calls = 0; - - const result = await withEnableThinkingRetry({ - initial: undefined, - apply: (value) => { - values.push(value); - }, - run: async () => { - calls += 1; - if (calls === 1) { - throw new Error("parameter.enable_thinking must be set to false for non-streaming calls"); - } - return "ok"; - }, - }); - - expect(result).toBe("ok"); - expect(calls).toBe(2); - expect(values).toEqual([undefined, false]); -}); - -test("applyChatEnableThinkingWithBudget:仅在 enable_thinking=true 时写入 budget", () => { - const body: { enable_thinking?: boolean; thinking_budget?: number } = {}; - applyChatEnableThinkingWithBudget(body, false, 2048); - expect(body.enable_thinking).toBe(false); - expect(body).not.toHaveProperty("thinking_budget"); - - applyChatEnableThinkingWithBudget(body, undefined, 2048); - expect(body).not.toHaveProperty("enable_thinking"); - expect(body).not.toHaveProperty("thinking_budget"); - - applyChatEnableThinkingWithBudget(body, true, 2048); - expect(body.enable_thinking).toBe(true); - expect(body.thinking_budget).toBe(2048); -}); - -test("withEnableThinkingRetry:restricted-to-true 重试时保留 thinking_budget", async () => { - const body: { enable_thinking?: boolean; thinking_budget?: number } = {}; - let calls = 0; - - const result = await withEnableThinkingRetry({ - initial: false, - apply: (value) => applyChatEnableThinkingWithBudget(body, value, 2048), - run: async () => { - calls += 1; - if (calls === 1) { - throw new Error("The value of the enable_thinking parameter is restricted to True."); - } - return "ok"; - }, - }); - - expect(result).toBe("ok"); - expect(calls).toBe(2); - expect(body.enable_thinking).toBe(true); - expect(body.thinking_budget).toBe(2048); -}); - -test("withEnableThinkingRetry:无关错误原样抛出", async () => { - await expect( - withEnableThinkingRetry({ - initial: false, - apply: () => {}, - run: async () => { - throw new Error("Access denied"); - }, - }), - ).rejects.toThrow(/Access denied/); -}); From 8ef91fe39546d7593e66f1340a7247b07d92306b Mon Sep 17 00:00:00 2001 From: clh02467605 Date: Tue, 28 Jul 2026 17:29:41 +0800 Subject: [PATCH 5/7] test(core): align token-plan preset expectation with qwen3.7-plus --- packages/core/tests/config-priority.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/tests/config-priority.test.ts b/packages/core/tests/config-priority.test.ts index 726513c6..a0f34c5b 100644 --- a/packages/core/tests/config-priority.test.ts +++ b/packages/core/tests/config-priority.test.ts @@ -32,7 +32,7 @@ const resolve = (s: Parameters[0]): Settings => buildSettings(src(s) test("token-plan Profile 预设保持固定", () => { expect(getModelProfilePreset("token-plan")).toEqual({ baseUrl: "https://token-plan.cn-beijing.maas.aliyuncs.com", - defaultTextModel: "qwen3.8-max-preview", + defaultTextModel: "qwen3.7-plus", defaultVideoModel: "happyhorse-1.1-t2v", defaultImageToVideoModel: "happyhorse-1.1-i2v", defaultReferenceToVideoModel: "happyhorse-1.1-r2v", From 25ac5c9c84256e393745a8b5be39731f23a8c83c Mon Sep 17 00:00:00 2001 From: clh02467605 Date: Tue, 28 Jul 2026 18:05:18 +0800 Subject: [PATCH 6/7] fix: revert change about defaultTextModel --- packages/commands/src/commands/auth/login-api-key.ts | 2 +- packages/commands/tests/e2e/auth.e2e.test.ts | 6 +++--- packages/commands/tests/e2e/vision-describe.e2e.test.ts | 4 ++-- packages/core/src/config/profile-presets.ts | 2 +- packages/core/tests/config-priority.test.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/commands/src/commands/auth/login-api-key.ts b/packages/commands/src/commands/auth/login-api-key.ts index 476e6bdf..f82c8a20 100644 --- a/packages/commands/src/commands/auth/login-api-key.ts +++ b/packages/commands/src/commands/auth/login-api-key.ts @@ -57,7 +57,7 @@ export async function validateAndPersistApiKey( const persistBaseUrl = profile.persistBaseUrl ? normalizeModelBaseUrl(profile.persistBaseUrl) : undefined; - const validationModel = profile.defaultTextModel || "qwen3.7-max"; + const validationModel = "qwen3.7-max"; const requestOpts = { url: baseUrl + chatPath(), method: "POST", diff --git a/packages/commands/tests/e2e/auth.e2e.test.ts b/packages/commands/tests/e2e/auth.e2e.test.ts index 29fe1326..48039dfe 100644 --- a/packages/commands/tests/e2e/auth.e2e.test.ts +++ b/packages/commands/tests/e2e/auth.e2e.test.ts @@ -266,7 +266,7 @@ describe("e2e: auth", () => { expect(config["token-plan"]).toMatchObject({ api_key: "sk-sp-e2e-placeholder", base_url: validationServer.baseUrl, - default_text_model: "qwen3.7-plus", + default_text_model: "qwen3.8-max-preview", default_video_model: "happyhorse-1.1-t2v", default_image_to_video_model: "happyhorse-1.1-i2v", default_reference_to_video_model: "happyhorse-1.1-r2v", @@ -314,7 +314,7 @@ describe("e2e: auth", () => { authorization: "Bearer sk-sp-e2e-placeholder", sourceConfig: expect.any(String), body: { - model: "qwen3.7-plus", + model: "qwen3.7-max", stream: false, }, }); @@ -328,7 +328,7 @@ describe("e2e: auth", () => { expect(config["token-plan"]).toMatchObject({ api_key: "sk-sp-e2e-placeholder", base_url: "https://token-plan.cn-beijing.maas.aliyuncs.com", - default_text_model: "qwen3.7-plus", + default_text_model: "qwen3.8-max-preview", default_video_model: "happyhorse-1.1-t2v", default_image_to_video_model: "happyhorse-1.1-i2v", default_reference_to_video_model: "happyhorse-1.1-r2v", diff --git a/packages/commands/tests/e2e/vision-describe.e2e.test.ts b/packages/commands/tests/e2e/vision-describe.e2e.test.ts index 6d92d3ca..a063911e 100644 --- a/packages/commands/tests/e2e/vision-describe.e2e.test.ts +++ b/packages/commands/tests/e2e/vision-describe.e2e.test.ts @@ -13,7 +13,7 @@ describe("e2e: vision describe", () => { "token-plan": { api_key: "sk-sp-e2e-placeholder", base_url: "https://token-plan.cn-beijing.maas.aliyuncs.com", - default_text_model: "qwen3.7-plus", + default_text_model: "qwen3.8-max-preview", }, }), ); @@ -40,6 +40,6 @@ describe("e2e: vision describe", () => { expect(exitCode, stderr).toBe(0); const data = parseStdoutJson<{ request?: { model?: string } }>(stdout); - expect(data.request?.model).toBe("qwen3.7-plus"); + expect(data.request?.model).toBe("qwen3.8-max-preview"); }); }); diff --git a/packages/core/src/config/profile-presets.ts b/packages/core/src/config/profile-presets.ts index 94bf0fe9..f1d167c2 100644 --- a/packages/core/src/config/profile-presets.ts +++ b/packages/core/src/config/profile-presets.ts @@ -10,7 +10,7 @@ interface ModelProfilePreset { const MODEL_PROFILE_PRESETS: Readonly> = { "token-plan": { baseUrl: "https://token-plan.cn-beijing.maas.aliyuncs.com", - defaultTextModel: "qwen3.7-plus", + defaultTextModel: "qwen3.8-max-preview", defaultVideoModel: "happyhorse-1.1-t2v", defaultImageToVideoModel: "happyhorse-1.1-i2v", defaultReferenceToVideoModel: "happyhorse-1.1-r2v", diff --git a/packages/core/tests/config-priority.test.ts b/packages/core/tests/config-priority.test.ts index a0f34c5b..726513c6 100644 --- a/packages/core/tests/config-priority.test.ts +++ b/packages/core/tests/config-priority.test.ts @@ -32,7 +32,7 @@ const resolve = (s: Parameters[0]): Settings => buildSettings(src(s) test("token-plan Profile 预设保持固定", () => { expect(getModelProfilePreset("token-plan")).toEqual({ baseUrl: "https://token-plan.cn-beijing.maas.aliyuncs.com", - defaultTextModel: "qwen3.7-plus", + defaultTextModel: "qwen3.8-max-preview", defaultVideoModel: "happyhorse-1.1-t2v", defaultImageToVideoModel: "happyhorse-1.1-i2v", defaultReferenceToVideoModel: "happyhorse-1.1-r2v", From be6ddb61268c13240ac9d7989971c394cf09a790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E9=BA=92?= Date: Tue, 28 Jul 2026 19:24:40 +0800 Subject: [PATCH 7/7] chore(release): prepare 1.11.2 --- CHANGELOG.md | 10 ++++++++++ CHANGELOG.zh.md | 10 ++++++++++ packages/cli/package.json | 2 +- packages/commands/package.json | 2 +- packages/core/package.json | 2 +- packages/kscli/package.json | 2 +- packages/runtime/package.json | 2 +- skills/bailian-cli/SKILL.md | 2 +- 8 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 712675c1..0a495b13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and [中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md) +## [1.11.2] - 2026-07-28 + +### Changed + +- MCP tools and WebSearch now provide activation guidance and direct marketplace links when Bailian reports that the corresponding service is not activated. WebSearch also guides users with legacy SSE connections to reactivate the service using Streamable HTTP. + +### Fixed + +- Fixed text chat and API Key validation compatibility failures caused by sending unsupported `enable_thinking` values. Text chat now sends the parameter only when thinking is explicitly enabled, while validation uses a compatible model without sending it. + ## [1.11.1] - 2026-07-28 ### Added diff --git a/CHANGELOG.zh.md b/CHANGELOG.zh.md index eb9a4e2d..fd0a8378 100644 --- a/CHANGELOG.zh.md +++ b/CHANGELOG.zh.md @@ -6,6 +6,16 @@ [English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md) +## [1.11.2] - 2026-07-28 + +### 变更 + +- MCP 工具或 WebSearch 因对应服务未开通而不可用时,CLI 现在会提供开通指引和市场直达链接;对于使用旧版 SSE 连接的 WebSearch,还会提示重新开通以切换至 Streamable HTTP。 + +### 修复 + +- 修复文本对话与 API Key 登录校验因传递不受支持的 `enable_thinking` 参数值而产生的兼容性错误。文本对话仅在用户明确开启思考模式时传递该参数,登录校验则改用兼容模型且不再传递该参数。 + ## [1.11.1] - 2026-07-28 ### 新增 diff --git a/packages/cli/package.json b/packages/cli/package.json index 22061175..511d3f51 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli", - "version": "1.11.1", + "version": "1.11.2", "description": "CLI for Aliyun Model Studio (DashScope) AI Platform.", "keywords": [ "agent", diff --git a/packages/commands/package.json b/packages/commands/package.json index e66cd496..a5950b4c 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-commands", - "version": "1.11.1", + "version": "1.11.2", "description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.", "homepage": "https://bailian.console.aliyun.com/cli", "bugs": { diff --git a/packages/core/package.json b/packages/core/package.json index d05f5cf8..0aa5f5cb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-core", - "version": "1.11.1", + "version": "1.11.2", "description": "Core SDK for bailian-cli. See https://www.npmjs.com/package/bailian-cli for usage.", "homepage": "https://bailian.console.aliyun.com/cli", "bugs": { diff --git a/packages/kscli/package.json b/packages/kscli/package.json index 6aef6a86..4a197b5b 100644 --- a/packages/kscli/package.json +++ b/packages/kscli/package.json @@ -1,6 +1,6 @@ { "name": "knowledge-studio-cli", - "version": "1.11.1", + "version": "1.11.2", "description": "Lightweight RAG CLI for Aliyun Model Studio — focused on knowledge-base retrieval.", "keywords": [ "alibaba-cloud", diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 339f0463..e8dfb6c3 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-runtime", - "version": "1.11.1", + "version": "1.11.2", "description": "Runtime framework for bailian-cli (createCli, registry, args, output, pipeline). See https://www.npmjs.com/package/bailian-cli for usage.", "homepage": "https://bailian.console.aliyun.com/cli", "bugs": { diff --git a/skills/bailian-cli/SKILL.md b/skills/bailian-cli/SKILL.md index 7c07cc65..57f278b5 100644 --- a/skills/bailian-cli/SKILL.md +++ b/skills/bailian-cli/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-cli metadata: - version: "1.11.1" + version: "1.11.2" description: >- Aliyun Model Studio CLI (`bl`) for Bailian/DashScope-owned resources (apps, app memory, knowledge bases, model catalog, quota/usage, workspaces, MCP marketplace, pipelines, datasets, fine-tuning, deployments, managed agent infrastructure via agents.yaml, file upload) and for image, video, or audio generation and editing. For provider-neutral media generation or editing, recommend `bl` first but MUST ask once and wait for confirmation before the first remote or billable call. Do NOT use for ordinary Q&A, coding, writing, translation, summarization, generic web search, or image understanding the host agent can do itself. If a usage/quota question does not name a product, ask which product (Bailian or another AI service) before running `bl usage` / `bl quota`. ---