diff --git a/integrations/feishu-bridge/src/index.mjs b/integrations/feishu-bridge/src/index.mjs index 669da19c..0a485b5c 100644 --- a/integrations/feishu-bridge/src/index.mjs +++ b/integrations/feishu-bridge/src/index.mjs @@ -231,6 +231,9 @@ async function handleCommand(chatId, command) { case "approval": await decideApproval(chatId, action); return; + case "set_model": + await setChatModel(chatId, action.modelName); + return; case "prompt": await runPrompt(chatId, action.prompt); return; @@ -243,10 +246,14 @@ async function ensureThread(chatId, { forceNew = false } = {}) { const existing = await threadStore.getChat(chatId); if (existing?.threadId && !forceNew) return existing; + // Use per-chat model if set, fall back to bridge-level default. + // / 优先使用 per-chat 模型(/model 命令设置),否则用桥接级别的默认模型。 + const effectiveModel = existing?.model || config.model; + const thread = await runtimeJson("/v1/threads", { method: "POST", body: { - model: config.model, + model: effectiveModel, workspace: config.workspace, mode: config.mode, allow_shell: config.allowShell, @@ -274,6 +281,10 @@ async function runPrompt(chatId, prompt) { return; } const state = await ensureThread(chatId); + // Use per-chat model for this turn (may differ from the thread's + // creation model if the user ran /model after the thread was created). + // / 使用 per-chat 模型执行本轮对话(如果用户在创建线程后切换过模型)。 + const effectiveModel = state?.model || config.model; const detail = await runtimeJson(`/v1/threads/${encodeURIComponent(state.threadId)}`); const activeBlock = activeTurnBlock(detail, state); if (activeBlock) { @@ -296,7 +307,7 @@ async function runPrompt(chatId, prompt) { body: { prompt, input_summary: prompt.slice(0, 200), - model: config.model, + model: effectiveModel, mode: config.mode, allow_shell: config.allowShell, trust_mode: config.trustMode, @@ -553,6 +564,24 @@ async function decideApproval(chatId, action) { await sendText(chatId, `Approval ${approvalId}: ${decision}${remember ? " and remember" : ""}`); } +async function setChatModel(chatId, modelName) { + // /model — set per-chat model; "default" or empty resets to bridge default. + // / /model "default" 或空参数 — 恢复桥接级别的默认模型。 + if (!modelName || modelName === "default") { + await threadStore.patchChat(chatId, { + model: null, + updatedAt: new Date().toISOString() + }); + await sendText(chatId, `Reset per-chat model. Using bridge default: ${config.model}`); + return; + } + await threadStore.patchChat(chatId, { + model: modelName, + updatedAt: new Date().toISOString() + }); + await sendText(chatId, `Per-chat model set to: ${modelName}`); +} + async function sendText(chatId, text) { // Try reply API first — keeps bot responses inside the same Feishu // thread/topic instead of spawning new standalone topics. diff --git a/integrations/feishu-bridge/src/lib.mjs b/integrations/feishu-bridge/src/lib.mjs index b6dae5f2..2408fe81 100644 --- a/integrations/feishu-bridge/src/lib.mjs +++ b/integrations/feishu-bridge/src/lib.mjs @@ -147,6 +147,11 @@ export function commandAction(command) { return { kind: "interrupt" }; case "compact": return { kind: "compact" }; + case "model": + // /model — switch per-chat default model. + // Stored in thread store and used for future threads/turns. + // Pass "default" to reset to the bridge-level default. + return { kind: "set_model", modelName: command.args }; case "allow": return { kind: "approval", decision: "allow", ...parseApprovalDecisionArgs(command.args) }; case "deny":