mirror of
https://fastgit.cc/github.com/openclaw/openclaw
synced 2026-04-22 05:42:22 +08:00
refactor: share telegram model selection matcher
This commit is contained in:
143
extensions/telegram/src/bot-model-list.test.ts
Normal file
143
extensions/telegram/src/bot-model-list.test.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { rm } from "node:fs/promises";
|
||||
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
const {
|
||||
answerCallbackQuerySpy,
|
||||
editMessageTextSpy,
|
||||
getLoadConfigMock,
|
||||
getOnHandler,
|
||||
replySpy,
|
||||
telegramBotDepsForTest,
|
||||
telegramBotRuntimeForTest,
|
||||
} = await import("./bot.create-telegram-bot.test-harness.js");
|
||||
|
||||
let createTelegramBotBase: typeof import("./bot.js").createTelegramBot;
|
||||
let setTelegramBotRuntimeForTest: typeof import("./bot.js").setTelegramBotRuntimeForTest;
|
||||
let createTelegramBot: (
|
||||
opts: Parameters<typeof import("./bot.js").createTelegramBot>[0],
|
||||
) => ReturnType<typeof import("./bot.js").createTelegramBot>;
|
||||
|
||||
const loadConfig = getLoadConfigMock();
|
||||
|
||||
describe("createTelegramBot model list callbacks", () => {
|
||||
beforeAll(async () => {
|
||||
({ createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
loadConfig.mockReturnValue({
|
||||
channels: {
|
||||
telegram: { dmPolicy: "open", allowFrom: ["*"] },
|
||||
},
|
||||
});
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
createTelegramBot = (opts) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
telegramDeps: telegramBotDepsForTest,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps provider-scoped current-model markers in model list callbacks", async () => {
|
||||
replySpy.mockClear();
|
||||
editMessageTextSpy.mockClear();
|
||||
|
||||
const storePath = `/tmp/openclaw-telegram-model-list-${process.pid}-${Date.now()}.json`;
|
||||
|
||||
await rm(storePath, { force: true });
|
||||
try {
|
||||
const config = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: "anthropic/claude-opus-4-6",
|
||||
models: {
|
||||
"anthropic/claude-opus-4-6": {},
|
||||
"github-copilot/gpt-5.4": {},
|
||||
"openai-codex/gpt-5.4": {},
|
||||
"openai-codex/gpt-5.3-codex-spark": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
telegram: {
|
||||
dmPolicy: "open",
|
||||
allowFrom: ["*"],
|
||||
},
|
||||
},
|
||||
session: {
|
||||
store: storePath,
|
||||
},
|
||||
} satisfies NonNullable<Parameters<typeof createTelegramBot>[0]["config"]>;
|
||||
|
||||
loadConfig.mockReturnValue(config);
|
||||
createTelegramBot({
|
||||
token: "tok",
|
||||
config,
|
||||
});
|
||||
const callbackHandler = getOnHandler("callback_query") as (
|
||||
ctx: Record<string, unknown>,
|
||||
) => Promise<void>;
|
||||
expect(callbackHandler).toBeDefined();
|
||||
|
||||
await callbackHandler({
|
||||
callbackQuery: {
|
||||
id: "cbq-model-list-1",
|
||||
data: "mdl_sel_github-copilot/gpt-5.4",
|
||||
from: { id: 9, first_name: "Ada", username: "ada_bot" },
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
date: 1736380800,
|
||||
message_id: 18,
|
||||
},
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
|
||||
editMessageTextSpy.mockClear();
|
||||
answerCallbackQuerySpy.mockClear();
|
||||
|
||||
await callbackHandler({
|
||||
callbackQuery: {
|
||||
id: "cbq-model-list-2",
|
||||
data: "mdl_list_openai-codex_1",
|
||||
from: { id: 9, first_name: "Ada", username: "ada_bot" },
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
date: 1736380800,
|
||||
message_id: 18,
|
||||
},
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
|
||||
expect(replySpy).not.toHaveBeenCalled();
|
||||
expect(editMessageTextSpy).toHaveBeenCalledTimes(1);
|
||||
const [chatId, messageId, _text, params] = editMessageTextSpy.mock.calls[0] ?? [];
|
||||
expect(chatId).toBe(1234);
|
||||
expect(messageId).toBe(18);
|
||||
|
||||
const buttonTexts =
|
||||
(
|
||||
params as
|
||||
| {
|
||||
reply_markup?: {
|
||||
inline_keyboard?: Array<Array<{ text: string }>>;
|
||||
};
|
||||
}
|
||||
| undefined
|
||||
)?.reply_markup?.inline_keyboard
|
||||
?.flat()
|
||||
.map((button) => button.text) ?? [];
|
||||
|
||||
expect(buttonTexts).toContain("gpt-5.4");
|
||||
expect(buttonTexts).not.toContain("gpt-5.4 ✓");
|
||||
expect(answerCallbackQuerySpy).toHaveBeenCalledWith("cbq-model-list-2");
|
||||
} finally {
|
||||
await rm(storePath, { force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -9,6 +9,7 @@
|
||||
* - mdl_back - back to providers list
|
||||
*/
|
||||
import { fitsTelegramCallbackData } from "./approval-callback-data.js";
|
||||
import { isCurrentModelSelection } from "./model-selection.js";
|
||||
|
||||
export type ButtonRow = Array<{ text: string; callback_data: string }>;
|
||||
|
||||
@@ -144,20 +145,6 @@ export function resolveModelSelection(params: {
|
||||
};
|
||||
}
|
||||
|
||||
function isCurrentModelSelection(params: {
|
||||
currentModel?: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
}): boolean {
|
||||
const currentModel = params.currentModel?.trim();
|
||||
if (!currentModel) {
|
||||
return false;
|
||||
}
|
||||
return currentModel.includes("/")
|
||||
? currentModel === `${params.provider}/${params.model}`
|
||||
: currentModel === params.model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build provider selection keyboard with 2 providers per row.
|
||||
*/
|
||||
|
||||
14
extensions/telegram/src/model-selection.ts
Normal file
14
extensions/telegram/src/model-selection.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export function isCurrentModelSelection(params: {
|
||||
currentModel?: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
}): boolean {
|
||||
const currentModel = params.currentModel?.trim();
|
||||
if (!currentModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return currentModel.includes("/")
|
||||
? currentModel === `${params.provider}/${params.model}`
|
||||
: currentModel === params.model;
|
||||
}
|
||||
Reference in New Issue
Block a user