mirror of https://github.com/openclaw/openclaw.git
fix(telegram): fix model button selection to actually change model
The synthetic message approach used for Telegram model button callbacks was silently failing - the callback was answered but the model never actually changed. This replaces the broken synthetic /model command with direct session store updates. Changes: - Replace processMessage() synthetic approach with updateSessionStore() - Add applyModelOverrideToSessionEntry() to directly set model override - Update resolveTelegramSessionState() to return sessionKey - Add visual feedback with ✅/❌ emojis and bold text - Remove inline keyboard after successful selection - Fix isDefault detection: selecting default model now clears override instead of pinning it, matching /model command behavior - Auth checks already applied before callback handler (lines 1108-1157) - Properly resolve agent-specific default model from agents.list or agents.defaults to handle shorthand refs like 'gpt-4o' - Add allowlist validation: verify model is in byProvider before persisting to prevent stale buttons from bypassing policy changes Fixes the bug where clicking model buttons appeared to work but didn't actually change the active model. Test: Click /models → select provider → select model → verify message shows 'Model changed to X' and next message uses that model.
This commit is contained in:
parent
65572c2a2b
commit
e62e95b87b
|
|
@ -20,6 +20,7 @@ import {
|
|||
loadSessionStore,
|
||||
resolveSessionStoreEntry,
|
||||
resolveStorePath,
|
||||
updateSessionStore,
|
||||
} from "../config/sessions.js";
|
||||
import type { DmPolicy } from "../config/types.base.js";
|
||||
import type {
|
||||
|
|
@ -33,6 +34,7 @@ import { MediaFetchError } from "../media/fetch.js";
|
|||
import { readChannelAllowFromStore } from "../pairing/pairing-store.js";
|
||||
import { resolveAgentRoute } from "../routing/resolve-route.js";
|
||||
import { resolveThreadSessionKeys } from "../routing/session-key.js";
|
||||
import { applyModelOverrideToSessionEntry } from "../sessions/model-overrides.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import {
|
||||
isSenderAllowed,
|
||||
|
|
@ -300,6 +302,7 @@ export const registerTelegramHandlers = ({
|
|||
}): {
|
||||
agentId: string;
|
||||
sessionEntry: ReturnType<typeof loadSessionStore>[string] | undefined;
|
||||
sessionKey: string;
|
||||
model?: string;
|
||||
} => {
|
||||
const resolvedThreadId =
|
||||
|
|
@ -339,6 +342,7 @@ export const registerTelegramHandlers = ({
|
|||
return {
|
||||
agentId: route.agentId,
|
||||
sessionEntry: entry,
|
||||
sessionKey,
|
||||
model: storedOverride.provider
|
||||
? `${storedOverride.provider}/${storedOverride.model}`
|
||||
: storedOverride.model,
|
||||
|
|
@ -350,6 +354,7 @@ export const registerTelegramHandlers = ({
|
|||
return {
|
||||
agentId: route.agentId,
|
||||
sessionEntry: entry,
|
||||
sessionKey,
|
||||
model: `${provider}/${model}`,
|
||||
};
|
||||
}
|
||||
|
|
@ -357,6 +362,7 @@ export const registerTelegramHandlers = ({
|
|||
return {
|
||||
agentId: route.agentId,
|
||||
sessionEntry: entry,
|
||||
sessionKey,
|
||||
model: typeof modelCfg === "string" ? modelCfg : modelCfg?.primary,
|
||||
};
|
||||
};
|
||||
|
|
@ -1374,16 +1380,69 @@ export const registerTelegramHandlers = ({
|
|||
);
|
||||
return;
|
||||
}
|
||||
// Process model selection as a synthetic message with /model command
|
||||
const syntheticMessage = buildSyntheticTextMessage({
|
||||
base: callbackMessage,
|
||||
from: callback.from,
|
||||
text: `/model ${selection.provider}/${selection.model}`,
|
||||
});
|
||||
await processMessage(buildSyntheticContext(ctx, syntheticMessage), [], storeAllowFrom, {
|
||||
forceWasMentioned: true,
|
||||
messageIdOverride: callback.id,
|
||||
});
|
||||
|
||||
const modelSet = byProvider.get(selection.provider);
|
||||
if (!modelSet?.has(selection.model)) {
|
||||
await editMessageWithButtons(
|
||||
`❌ Model "${selection.provider}/${selection.model}" is not allowed.`,
|
||||
[],
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Directly set model override in session
|
||||
try {
|
||||
// Get session store path
|
||||
const storePath = resolveStorePath(cfg.session?.store, {
|
||||
agentId: sessionState.agentId,
|
||||
});
|
||||
|
||||
const agentConfig = cfg.agents?.list?.find((a) => a.id === sessionState.agentId);
|
||||
const agentModelConfig = agentConfig?.model ?? cfg.agents?.defaults?.model;
|
||||
const rawModelRef =
|
||||
typeof agentModelConfig === "string" ? agentModelConfig : agentModelConfig?.primary;
|
||||
|
||||
let resolvedDefaultRef = sessionState.model;
|
||||
if (rawModelRef) {
|
||||
const trimmed = rawModelRef.trim();
|
||||
if (trimmed.includes("/")) {
|
||||
resolvedDefaultRef = trimmed;
|
||||
} else {
|
||||
const currentProvider = sessionState.model?.split("/")[0];
|
||||
resolvedDefaultRef = currentProvider
|
||||
? `${currentProvider}/${trimmed}`
|
||||
: sessionState.model;
|
||||
}
|
||||
}
|
||||
|
||||
const isDefaultSelection =
|
||||
`${selection.provider}/${selection.model}` === resolvedDefaultRef;
|
||||
|
||||
await updateSessionStore(storePath, (store) => {
|
||||
const sessionKey = sessionState.sessionKey;
|
||||
const entry = store[sessionKey] ?? {};
|
||||
store[sessionKey] = entry;
|
||||
applyModelOverrideToSessionEntry({
|
||||
entry,
|
||||
selection: {
|
||||
provider: selection.provider,
|
||||
model: selection.model,
|
||||
isDefault: isDefaultSelection,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Update message to show success with visual feedback
|
||||
const actionText = isDefaultSelection
|
||||
? "reset to default"
|
||||
: `changed to **${selection.provider}/${selection.model}**`;
|
||||
await editMessageWithButtons(
|
||||
`✅ Model ${actionText}\n\nThis model will be used for your next message.`,
|
||||
[], // Empty buttons = remove inline keyboard
|
||||
);
|
||||
} catch (err) {
|
||||
await editMessageWithButtons(`❌ Failed to change model: ${String(err)}`, []);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue