From a51ca9b86382c611e6339fe7925c2e39feea3304 Mon Sep 17 00:00:00 2001 From: NeilJo-GY <43027886+NeilJo-GY@users.noreply.github.com> Date: Sat, 5 Sep 2026 11:06:34 +0800 Subject: [PATCH] fix(composer): do not send or advertise off-shelf official models A leftover session pick could still be sent while the official shelf was loading, and official failures pointed people at Store keys. Drop CN-blocked ids before send, keep the listing on the button, and use official error copy. --- .../src/ranch-shell/RanchChatShell.tsx | 51 +++++++++++++++---- packages/agent-chat/src/ranch-shell/i18n.ts | 8 +-- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/packages/agent-chat/src/ranch-shell/RanchChatShell.tsx b/packages/agent-chat/src/ranch-shell/RanchChatShell.tsx index ffd6224..195b56d 100644 --- a/packages/agent-chat/src/ranch-shell/RanchChatShell.tsx +++ b/packages/agent-chat/src/ranch-shell/RanchChatShell.tsx @@ -396,7 +396,7 @@ function shouldOfferStoreOpenRouterKey( ): boolean { if (code === "insufficient_credits") return false; if (code === "official_not_authorized" || code === "official_model_unsupported") { - return true; + return false; } const blob = `${code || ""} ${message || ""}`; return /invalid[- ]api[- ]key|missing[- ]api[- ]key|no api key|openrouter.*(?:key|credit|quota)|api key not/i.test( @@ -404,6 +404,23 @@ function shouldOfferStoreOpenRouterKey( ); } +function requestedModelForSend( + selected: string | null, + listed: string | null, + official: boolean, + keyGeo: string | null | undefined, +): string | null { + if ( + official && + (keyGeo || "").trim() && + selected && + !officialShelfAllows(selected, keyGeo) + ) { + return listed; + } + return selected; +} + function sendFailureCopy( e: unknown, t: RanchMessages, @@ -419,11 +436,15 @@ function sendFailureCopy( ? t.billingUnavailable : e.code === "unsupported_model" ? t.unsupportedModel - : offerStoreKey - ? t.needOpenRouterKey - : e.code === "model_pricing_unavailable" - ? t.modelPricingUnavailable - : e.message || t.sendFailed; + : e.code === "official_not_authorized" + ? t.officialNotAuthorized + : e.code === "official_model_unsupported" + ? t.officialModelUnsupported + : offerStoreKey + ? t.needOpenRouterKey + : e.code === "model_pricing_unavailable" + ? t.modelPricingUnavailable + : e.message || t.sendFailed; return { text, offerStoreKey }; } @@ -2742,7 +2763,12 @@ export function RanchChatShell(props: RanchChatShellProps) { ? opts.threadId : (activeTopic?.id ?? composerTopic?.id ?? null); await client.sendMessage(chatId, text, mentions, sendThreadId, { - requested_model: selectedModelId, + requested_model: requestedModelForSend( + selectedModelId, + composerModel?.listed_model_id ?? null, + Boolean(composerModel?.official_channel), + composerModel?.official_key_geo, + ), }); if (group && mentions) { if (mentions.length === 1) { @@ -2891,7 +2917,12 @@ export function RanchChatShell(props: RanchChatShellProps) { text, mentions, activeTopic?.id ?? composerTopic?.id ?? null, - { requested_model: selectedModelId }, + { requested_model: requestedModelForSend( + selectedModelId, + composerModel?.listed_model_id ?? null, + Boolean(composerModel?.official_channel), + composerModel?.official_key_geo, + ) }, ); if (group && mentions) { if (mentions.length === 1) { @@ -3151,7 +3182,8 @@ export function RanchChatShell(props: RanchChatShellProps) { catalogPending && stored && !listedChanged && - officialV0SupportsModel(stored) + officialV0SupportsModel(stored) && + officialShelfAllows(stored, row.official_key_geo) ) { setSelectedModelId(stored); } else { @@ -4872,7 +4904,6 @@ export function RanchChatShell(props: RanchChatShellProps) { const visible = filterComposerModels(options, composerMenuQuery); const value = options.find((id) => sameModelId(id, selectedModelId)) || - (official && selectedModelId ? selectedModelId : "") || options.find((id) => sameModelId(id, listed)) || options[0] || ""; diff --git a/packages/agent-chat/src/ranch-shell/i18n.ts b/packages/agent-chat/src/ranch-shell/i18n.ts index 940ef89..ac179ae 100644 --- a/packages/agent-chat/src/ranch-shell/i18n.ts +++ b/packages/agent-chat/src/ranch-shell/i18n.ts @@ -588,12 +588,12 @@ const en: RanchMessages = { billingUnavailable: "Billing check unavailable. Try again in a moment.", unsupportedModel: "This agent does not support that model.", officialNotAuthorized: - "Official hosted inference is not available. Use this agent’s own key, or write a Store OpenRouter key into its runtime.", + "This model is not on the official Host shelf. Pick one from the list.", needOpenRouterKey: "OpenRouter is BYO: the key lives on the agent, not on Interfaze. Host cannot see the key — we only read the heartbeat model. Put a key in its runtime, or buy Store credits for the owner to write one in.", buyOpenRouterCredits: "Buy OpenRouter credits", officialModelUnsupported: - "Official hosted inference is frozen and cannot run thinking/reasoning models. Use the agent’s key, or pick a chat model after writing a Store key.", + "Official hosted inference cannot run thinking/reasoning models. Pick a chat model from the list.", modelPricingUnavailable: "No platform price for that model yet. Pick another or ask the owner to wait for Host pack coverage.", pricingMismatch: (listed, observed) => `Listed ${listed} ≠ ran ${observed} (billed at listing price)`, @@ -1180,12 +1180,12 @@ const zh: RanchMessages = { billingUnavailable: "计费服务暂时不可用,请稍后再试。", unsupportedModel: "该智能体不支持所选模型。", officialNotAuthorized: - "官方代打未开放。请用这只 agent 自己的钥匙,或把 Store OpenRouter 钥匙写进 runtime。", + "这个型号不在官方货架上,请换一个列表里的模型。", needOpenRouterKey: "OpenRouter 是自备钥匙:钥匙在 agent 运行时里,Interfaze 不会代收。Host 看不见钥匙,只认心跳型号。请写进它的 OpenClaw / runtime,或去 Store 买额度让 Owner 写入。", buyOpenRouterCredits: "去 Store 买 OpenRouter 额度", officialModelUnsupported: - "官方代打已冻结,思考/推理模跑不了。请用 agent 自己的钥匙,或写入 Store 钥匙后再选对话模。", + "官方通道跑不了思考/推理模,请换一个列表里的对话模型。", modelPricingUnavailable: "平台暂无该模型报价,请换一个;或等 Host 价包录入后再试。", pricingMismatch: (listed, observed) => `挂牌 ${listed} ≠ 运行 ${observed}(按挂牌价结算)`,