fix: stabilize /model reasoning-effort selection (v0.3.5)
Keep pending model outside the picker map, omit undefined reasoningEffort in selectModel payload, auto-apply single-effort models, and surface RPC errors. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Vendored
+2
@@ -19,6 +19,8 @@ export declare class TelegramBridge {
|
|||||||
private readonly maxMessageLength;
|
private readonly maxMessageLength;
|
||||||
private readonly bindings;
|
private readonly bindings;
|
||||||
private readonly pickers;
|
private readonly pickers;
|
||||||
|
/** chatId → model awaiting reasoning-effort pick (kept outside picker so list refreshes won't drop it). */
|
||||||
|
private readonly pendingModels;
|
||||||
private polling;
|
private polling;
|
||||||
private offset;
|
private offset;
|
||||||
private pollPromise;
|
private pollPromise;
|
||||||
|
|||||||
+32
-9
@@ -12,7 +12,8 @@ const WS_CB = 'ws:';
|
|||||||
const SID_CB = 'sid:';
|
const SID_CB = 'sid:';
|
||||||
const BACK_WS_CB = 'wb';
|
const BACK_WS_CB = 'wb';
|
||||||
const MODEL_CB = 'mdl:';
|
const MODEL_CB = 'mdl:';
|
||||||
const EFFORT_CB = 'me:';
|
/** Use eff: (not me:) — short prefix, no collision with other callbacks. */
|
||||||
|
const EFFORT_CB = 'eff:';
|
||||||
const BACK_MODEL_CB = 'mb';
|
const BACK_MODEL_CB = 'mb';
|
||||||
const MAX_BUTTONS = 40;
|
const MAX_BUTTONS = 40;
|
||||||
function lastContextKeyboard() {
|
function lastContextKeyboard() {
|
||||||
@@ -37,6 +38,8 @@ export class TelegramBridge {
|
|||||||
maxMessageLength;
|
maxMessageLength;
|
||||||
bindings = new Map();
|
bindings = new Map();
|
||||||
pickers = new Map();
|
pickers = new Map();
|
||||||
|
/** chatId → model awaiting reasoning-effort pick (kept outside picker so list refreshes won't drop it). */
|
||||||
|
pendingModels = new Map();
|
||||||
polling = false;
|
polling = false;
|
||||||
offset;
|
offset;
|
||||||
pollPromise;
|
pollPromise;
|
||||||
@@ -89,6 +92,7 @@ export class TelegramBridge {
|
|||||||
// Never dispose host agents — only clear remote bindings.
|
// Never dispose host agents — only clear remote bindings.
|
||||||
this.bindings.clear();
|
this.bindings.clear();
|
||||||
this.pickers.clear();
|
this.pickers.clear();
|
||||||
|
this.pendingModels.clear();
|
||||||
if (this.pollPromise) {
|
if (this.pollPromise) {
|
||||||
await this.pollPromise.catch(() => { });
|
await this.pollPromise.catch(() => { });
|
||||||
this.pollPromise = undefined;
|
this.pollPromise = undefined;
|
||||||
@@ -196,21 +200,33 @@ export class TelegramBridge {
|
|||||||
await this.client.sendMessage(chatId, MSG.PICKER_STALE);
|
await this.client.sendMessage(chatId, MSG.PICKER_STALE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (option.efforts && option.efforts.length > 0) {
|
const efforts = (option.efforts ?? []).filter((e) => e.id);
|
||||||
|
if (efforts.length === 1) {
|
||||||
|
// Single effort (often only "off") — apply immediately, no second tap.
|
||||||
|
await this.applyModel(chatId, cq.id, { ...option, efforts }, efforts[0].id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (efforts.length > 1) {
|
||||||
|
const pending = { ...option, efforts };
|
||||||
|
this.pendingModels.set(String(chatId), pending);
|
||||||
if (picker)
|
if (picker)
|
||||||
picker.pendingModel = option;
|
picker.pendingModel = pending;
|
||||||
await this.client.answerCallbackQuery(cq.id);
|
await this.client.answerCallbackQuery(cq.id);
|
||||||
await this.sendEffortPicker(chatId, option);
|
await this.sendEffortPicker(chatId, pending);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.applyModel(chatId, cq.id, option);
|
await this.applyModel(chatId, cq.id, option);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.startsWith(EFFORT_CB)) {
|
if (data.startsWith(EFFORT_CB) || data.startsWith('me:')) {
|
||||||
const index = Number(data.slice(EFFORT_CB.length));
|
// Accept legacy "me:" callbacks from older bot messages.
|
||||||
const pending = picker?.pendingModel;
|
const rawIndex = data.startsWith(EFFORT_CB)
|
||||||
|
? data.slice(EFFORT_CB.length)
|
||||||
|
: data.slice('me:'.length);
|
||||||
|
const index = Number(rawIndex);
|
||||||
|
const pending = this.pendingModels.get(String(chatId)) ?? picker?.pendingModel;
|
||||||
const effort = pending?.efforts?.[index];
|
const effort = pending?.efforts?.[index];
|
||||||
if (!pending || !effort) {
|
if (!pending || !effort?.id) {
|
||||||
await this.client.answerCallbackQuery(cq.id, '列表已过期');
|
await this.client.answerCallbackQuery(cq.id, '列表已过期');
|
||||||
await this.client.sendMessage(chatId, MSG.PICKER_STALE);
|
await this.client.sendMessage(chatId, MSG.PICKER_STALE);
|
||||||
return;
|
return;
|
||||||
@@ -421,19 +437,26 @@ export class TelegramBridge {
|
|||||||
await this.client.sendMessage(chatId, MSG.NEED_BIND);
|
await this.client.sendMessage(chatId, MSG.NEED_BIND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Ensure session is live before selectModel (same as followup path).
|
||||||
|
await this.ensureLiveAgent(binding.sessionId);
|
||||||
try {
|
try {
|
||||||
const selected = await selectSessionModel(this.ctx, binding.sessionId, {
|
const selected = await selectSessionModel(this.ctx, binding.sessionId, {
|
||||||
provider: option.provider,
|
provider: option.provider,
|
||||||
model: option.model,
|
model: option.model,
|
||||||
reasoningEffort,
|
reasoningEffort,
|
||||||
});
|
});
|
||||||
|
this.pendingModels.delete(String(chatId));
|
||||||
|
const picker = this.pickers.get(String(chatId));
|
||||||
|
if (picker)
|
||||||
|
picker.pendingModel = undefined;
|
||||||
await this.client.answerCallbackQuery(callbackId, '已切换');
|
await this.client.answerCallbackQuery(callbackId, '已切换');
|
||||||
await this.client.sendMessage(chatId, MSG.MODEL_SET(formatModel(selected)));
|
await this.client.sendMessage(chatId, MSG.MODEL_SET(formatModel(selected)));
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.ctx.logger.warn(`dsh-telegram-channel: selectModel failed: ${this.redact(err)}`);
|
this.ctx.logger.warn(`dsh-telegram-channel: selectModel failed: ${this.redact(err)}`);
|
||||||
|
const detail = err instanceof Error ? err.message : String(err);
|
||||||
await this.client.answerCallbackQuery(callbackId, '切换失败');
|
await this.client.answerCallbackQuery(callbackId, '切换失败');
|
||||||
await this.client.sendMessage(chatId, MSG.MODEL_FAILED);
|
await this.client.sendMessage(chatId, MSG.MODEL_FAILED(detail));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
liveAgents() {
|
liveAgents() {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -20,7 +20,7 @@ export declare const MSG: {
|
|||||||
readonly MODEL_UNROUTABLE: (current: string) => string;
|
readonly MODEL_UNROUTABLE: (current: string) => string;
|
||||||
readonly MODEL_EMPTY: (current: string) => string;
|
readonly MODEL_EMPTY: (current: string) => string;
|
||||||
readonly MODEL_SET: (selected: string) => string;
|
readonly MODEL_SET: (selected: string) => string;
|
||||||
readonly MODEL_FAILED: "切换模型失败。请稍后重试或在 Web 中切换。";
|
readonly MODEL_FAILED: (detail?: string) => string;
|
||||||
readonly unknown: (command: string) => string;
|
readonly unknown: (command: string) => string;
|
||||||
};
|
};
|
||||||
export type ParsedCommand = {
|
export type ParsedCommand = {
|
||||||
|
|||||||
+6
-1
@@ -53,7 +53,12 @@ export const MSG = {
|
|||||||
MODEL_SET(selected) {
|
MODEL_SET(selected) {
|
||||||
return `已切换模型:${selected}\n下一回合生效。`;
|
return `已切换模型:${selected}\n下一回合生效。`;
|
||||||
},
|
},
|
||||||
MODEL_FAILED: '切换模型失败。请稍后重试或在 Web 中切换。',
|
MODEL_FAILED(detail) {
|
||||||
|
const tip = '切换模型失败。请稍后重试或在 Web 中切换。';
|
||||||
|
if (!detail)
|
||||||
|
return tip;
|
||||||
|
return `${tip}\n详情:${detail}`;
|
||||||
|
},
|
||||||
unknown(command) {
|
unknown(command) {
|
||||||
return `未知命令 ${command}。发送 /help 查看可用命令。`;
|
return `未知命令 ${command}。发送 /help 查看可用命令。`;
|
||||||
},
|
},
|
||||||
|
|||||||
+7
-3
@@ -53,12 +53,16 @@ export async function selectSessionModel(ctx, sessionId, selection) {
|
|||||||
if (!api?.sessions?.selectModel) {
|
if (!api?.sessions?.selectModel) {
|
||||||
throw new Error('apiProxy unavailable (use ctx.get / inject apiProxy)');
|
throw new Error('apiProxy unavailable (use ctx.get / inject apiProxy)');
|
||||||
}
|
}
|
||||||
const raw = await call(api.sessions.selectModel, {
|
// Omit undefined reasoningEffort — some validators treat explicit undefined as invalid.
|
||||||
|
const payload = {
|
||||||
sessionId,
|
sessionId,
|
||||||
provider: selection.provider,
|
provider: selection.provider,
|
||||||
model: selection.model,
|
model: selection.model,
|
||||||
reasoningEffort: selection.reasoningEffort,
|
};
|
||||||
});
|
if (selection.reasoningEffort !== undefined && selection.reasoningEffort !== '') {
|
||||||
|
payload.reasoningEffort = selection.reasoningEffort;
|
||||||
|
}
|
||||||
|
const raw = await call(api.sessions.selectModel, payload);
|
||||||
const value = unwrap(raw);
|
const value = unwrap(raw);
|
||||||
if (!value?.selected) {
|
if (!value?.selected) {
|
||||||
throw new Error(unwrapError(raw) ?? 'failed to select model');
|
throw new Error(unwrapError(raw) ?? 'failed to select model');
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dsh-telegram-channel",
|
"name": "dsh-telegram-channel",
|
||||||
"version": "0.3.4",
|
"version": "0.3.5",
|
||||||
"description": "DeepSeek Harness Telegram mobile remote: workspace→session, /last context, /model — dsh-plugin",
|
"description": "DeepSeek Harness Telegram mobile remote: workspace→session, /last context, /model — dsh-plugin",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"telegram",
|
"telegram",
|
||||||
|
|||||||
+31
-9
@@ -64,7 +64,8 @@ const WS_CB = 'ws:'
|
|||||||
const SID_CB = 'sid:'
|
const SID_CB = 'sid:'
|
||||||
const BACK_WS_CB = 'wb'
|
const BACK_WS_CB = 'wb'
|
||||||
const MODEL_CB = 'mdl:'
|
const MODEL_CB = 'mdl:'
|
||||||
const EFFORT_CB = 'me:'
|
/** Use eff: (not me:) — short prefix, no collision with other callbacks. */
|
||||||
|
const EFFORT_CB = 'eff:'
|
||||||
const BACK_MODEL_CB = 'mb'
|
const BACK_MODEL_CB = 'mb'
|
||||||
const MAX_BUTTONS = 40
|
const MAX_BUTTONS = 40
|
||||||
|
|
||||||
@@ -94,6 +95,8 @@ export class TelegramBridge {
|
|||||||
|
|
||||||
private readonly bindings = new Map<string, Binding>()
|
private readonly bindings = new Map<string, Binding>()
|
||||||
private readonly pickers = new Map<string, PickerState>()
|
private readonly pickers = new Map<string, PickerState>()
|
||||||
|
/** chatId → model awaiting reasoning-effort pick (kept outside picker so list refreshes won't drop it). */
|
||||||
|
private readonly pendingModels = new Map<string, ModelOption>()
|
||||||
private polling = false
|
private polling = false
|
||||||
private offset: number | undefined
|
private offset: number | undefined
|
||||||
private pollPromise: Promise<void> | undefined
|
private pollPromise: Promise<void> | undefined
|
||||||
@@ -149,6 +152,7 @@ export class TelegramBridge {
|
|||||||
// Never dispose host agents — only clear remote bindings.
|
// Never dispose host agents — only clear remote bindings.
|
||||||
this.bindings.clear()
|
this.bindings.clear()
|
||||||
this.pickers.clear()
|
this.pickers.clear()
|
||||||
|
this.pendingModels.clear()
|
||||||
if (this.pollPromise) {
|
if (this.pollPromise) {
|
||||||
await this.pollPromise.catch(() => {})
|
await this.pollPromise.catch(() => {})
|
||||||
this.pollPromise = undefined
|
this.pollPromise = undefined
|
||||||
@@ -261,20 +265,32 @@ export class TelegramBridge {
|
|||||||
await this.client.sendMessage(chatId, MSG.PICKER_STALE)
|
await this.client.sendMessage(chatId, MSG.PICKER_STALE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (option.efforts && option.efforts.length > 0) {
|
const efforts = (option.efforts ?? []).filter((e) => e.id)
|
||||||
if (picker) picker.pendingModel = option
|
if (efforts.length === 1) {
|
||||||
|
// Single effort (often only "off") — apply immediately, no second tap.
|
||||||
|
await this.applyModel(chatId, cq.id, { ...option, efforts }, efforts[0]!.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (efforts.length > 1) {
|
||||||
|
const pending = { ...option, efforts }
|
||||||
|
this.pendingModels.set(String(chatId), pending)
|
||||||
|
if (picker) picker.pendingModel = pending
|
||||||
await this.client.answerCallbackQuery(cq.id)
|
await this.client.answerCallbackQuery(cq.id)
|
||||||
await this.sendEffortPicker(chatId, option)
|
await this.sendEffortPicker(chatId, pending)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await this.applyModel(chatId, cq.id, option)
|
await this.applyModel(chatId, cq.id, option)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (data.startsWith(EFFORT_CB)) {
|
if (data.startsWith(EFFORT_CB) || data.startsWith('me:')) {
|
||||||
const index = Number(data.slice(EFFORT_CB.length))
|
// Accept legacy "me:" callbacks from older bot messages.
|
||||||
const pending = picker?.pendingModel
|
const rawIndex = data.startsWith(EFFORT_CB)
|
||||||
|
? data.slice(EFFORT_CB.length)
|
||||||
|
: data.slice('me:'.length)
|
||||||
|
const index = Number(rawIndex)
|
||||||
|
const pending = this.pendingModels.get(String(chatId)) ?? picker?.pendingModel
|
||||||
const effort = pending?.efforts?.[index]
|
const effort = pending?.efforts?.[index]
|
||||||
if (!pending || !effort) {
|
if (!pending || !effort?.id) {
|
||||||
await this.client.answerCallbackQuery(cq.id, '列表已过期')
|
await this.client.answerCallbackQuery(cq.id, '列表已过期')
|
||||||
await this.client.sendMessage(chatId, MSG.PICKER_STALE)
|
await this.client.sendMessage(chatId, MSG.PICKER_STALE)
|
||||||
return
|
return
|
||||||
@@ -503,18 +519,24 @@ export class TelegramBridge {
|
|||||||
await this.client.sendMessage(chatId, MSG.NEED_BIND)
|
await this.client.sendMessage(chatId, MSG.NEED_BIND)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Ensure session is live before selectModel (same as followup path).
|
||||||
|
await this.ensureLiveAgent(binding.sessionId)
|
||||||
try {
|
try {
|
||||||
const selected = await selectSessionModel(this.ctx, binding.sessionId, {
|
const selected = await selectSessionModel(this.ctx, binding.sessionId, {
|
||||||
provider: option.provider,
|
provider: option.provider,
|
||||||
model: option.model,
|
model: option.model,
|
||||||
reasoningEffort,
|
reasoningEffort,
|
||||||
})
|
})
|
||||||
|
this.pendingModels.delete(String(chatId))
|
||||||
|
const picker = this.pickers.get(String(chatId))
|
||||||
|
if (picker) picker.pendingModel = undefined
|
||||||
await this.client.answerCallbackQuery(callbackId, '已切换')
|
await this.client.answerCallbackQuery(callbackId, '已切换')
|
||||||
await this.client.sendMessage(chatId, MSG.MODEL_SET(formatModel(selected)))
|
await this.client.sendMessage(chatId, MSG.MODEL_SET(formatModel(selected)))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.ctx.logger.warn(`dsh-telegram-channel: selectModel failed: ${this.redact(err)}`)
|
this.ctx.logger.warn(`dsh-telegram-channel: selectModel failed: ${this.redact(err)}`)
|
||||||
|
const detail = err instanceof Error ? err.message : String(err)
|
||||||
await this.client.answerCallbackQuery(callbackId, '切换失败')
|
await this.client.answerCallbackQuery(callbackId, '切换失败')
|
||||||
await this.client.sendMessage(chatId, MSG.MODEL_FAILED)
|
await this.client.sendMessage(chatId, MSG.MODEL_FAILED(detail))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -52,7 +52,11 @@ export const MSG = {
|
|||||||
MODEL_SET(selected: string): string {
|
MODEL_SET(selected: string): string {
|
||||||
return `已切换模型:${selected}\n下一回合生效。`
|
return `已切换模型:${selected}\n下一回合生效。`
|
||||||
},
|
},
|
||||||
MODEL_FAILED: '切换模型失败。请稍后重试或在 Web 中切换。',
|
MODEL_FAILED(detail?: string): string {
|
||||||
|
const tip = '切换模型失败。请稍后重试或在 Web 中切换。'
|
||||||
|
if (!detail) return tip
|
||||||
|
return `${tip}\n详情:${detail}`
|
||||||
|
},
|
||||||
unknown(command: string): string {
|
unknown(command: string): string {
|
||||||
return `未知命令 ${command}。发送 /help 查看可用命令。`
|
return `未知命令 ${command}。发送 /help 查看可用命令。`
|
||||||
},
|
},
|
||||||
|
|||||||
+12
-3
@@ -89,12 +89,21 @@ export async function selectSessionModel(
|
|||||||
if (!api?.sessions?.selectModel) {
|
if (!api?.sessions?.selectModel) {
|
||||||
throw new Error('apiProxy unavailable (use ctx.get / inject apiProxy)')
|
throw new Error('apiProxy unavailable (use ctx.get / inject apiProxy)')
|
||||||
}
|
}
|
||||||
const raw = await call(api.sessions.selectModel, {
|
// Omit undefined reasoningEffort — some validators treat explicit undefined as invalid.
|
||||||
|
const payload: {
|
||||||
|
sessionId: string
|
||||||
|
provider: string
|
||||||
|
model: string
|
||||||
|
reasoningEffort?: string
|
||||||
|
} = {
|
||||||
sessionId,
|
sessionId,
|
||||||
provider: selection.provider,
|
provider: selection.provider,
|
||||||
model: selection.model,
|
model: selection.model,
|
||||||
reasoningEffort: selection.reasoningEffort,
|
}
|
||||||
})
|
if (selection.reasoningEffort !== undefined && selection.reasoningEffort !== '') {
|
||||||
|
payload.reasoningEffort = selection.reasoningEffort
|
||||||
|
}
|
||||||
|
const raw = await call(api.sessions.selectModel, payload)
|
||||||
const value = unwrap<{ selected: { provider: string; model: string; reasoningEffort?: string } }>(raw)
|
const value = unwrap<{ selected: { provider: string; model: string; reasoningEffort?: string } }>(raw)
|
||||||
if (!value?.selected) {
|
if (!value?.selected) {
|
||||||
throw new Error(unwrapError(raw) ?? 'failed to select model')
|
throw new Error(unwrapError(raw) ?? 'failed to select model')
|
||||||
|
|||||||
+93
-1
@@ -440,7 +440,99 @@ test('/model lists and selects via apiProxy', async () => {
|
|||||||
sessionId: 'live-mdl',
|
sessionId: 'live-mdl',
|
||||||
provider: 'deepseek',
|
provider: 'deepseek',
|
||||||
model: 'chat',
|
model: 'chat',
|
||||||
reasoningEffort: undefined,
|
})
|
||||||
|
assert.match(sent.at(-1)!.text, /已切换模型/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/model effort picker applies reasoningEffort', async () => {
|
||||||
|
const sent: SentMessage[] = []
|
||||||
|
const followups: UserMessage[] = []
|
||||||
|
const agent = makeAgent('live-eff', followups)
|
||||||
|
let selected: unknown
|
||||||
|
const ctx = {
|
||||||
|
logger: { info() {}, warn() {}, error() {} },
|
||||||
|
agents: {
|
||||||
|
list: () => [agent],
|
||||||
|
roots: () => [agent],
|
||||||
|
get: () => agent,
|
||||||
|
},
|
||||||
|
apiProxy: {
|
||||||
|
sessions: {
|
||||||
|
models: async () => rpcOk({
|
||||||
|
current: { provider: 'deepseek', model: 'reasoner' },
|
||||||
|
routable: true,
|
||||||
|
groups: [{
|
||||||
|
id: 'deepseek',
|
||||||
|
name: 'DeepSeek',
|
||||||
|
models: [{
|
||||||
|
id: 'reasoner',
|
||||||
|
name: 'Reasoner',
|
||||||
|
reasoning: {
|
||||||
|
efforts: [
|
||||||
|
{ id: 'high', name: 'High' },
|
||||||
|
{ id: 'max', name: 'Max' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
selectModel: async (req: { payload: unknown }) => {
|
||||||
|
selected = req.payload
|
||||||
|
const p = req.payload as { provider: string; model: string; reasoningEffort?: string }
|
||||||
|
return rpcOk({
|
||||||
|
selected: {
|
||||||
|
provider: p.provider,
|
||||||
|
model: p.model,
|
||||||
|
reasoningEffort: p.reasoningEffort,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on() { return () => {} },
|
||||||
|
}
|
||||||
|
const bridge = new TelegramBridge(ctx as any, {
|
||||||
|
token: 't',
|
||||||
|
allowedUserIds: [1],
|
||||||
|
allowAllUsers: false,
|
||||||
|
client: fakeClient(sent),
|
||||||
|
sleep: async () => {},
|
||||||
|
})
|
||||||
|
await bridge.processUpdate({
|
||||||
|
update_id: 1,
|
||||||
|
callback_query: {
|
||||||
|
id: 'bind',
|
||||||
|
from: { id: 1 },
|
||||||
|
message: { message_id: 1, date: 0, chat: { id: 10, type: 'private' } },
|
||||||
|
data: `${BIND_CB_PREFIX}live-eff`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await bridge.processUpdate(messageUpdate(10, 1, '/model', 2))
|
||||||
|
await bridge.processUpdate({
|
||||||
|
update_id: 3,
|
||||||
|
callback_query: {
|
||||||
|
id: 'pick-r',
|
||||||
|
from: { id: 1 },
|
||||||
|
message: { message_id: 2, date: 0, chat: { id: 10, type: 'private' } },
|
||||||
|
data: 'mdl:0',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.match(sent.at(-1)!.text, /reasoning effort|请选择/)
|
||||||
|
assert.equal(sent.at(-1)!.replyMarkup?.inline_keyboard?.[0]?.[0]?.callback_data, 'eff:0')
|
||||||
|
await bridge.processUpdate({
|
||||||
|
update_id: 4,
|
||||||
|
callback_query: {
|
||||||
|
id: 'pick-eff',
|
||||||
|
from: { id: 1 },
|
||||||
|
message: { message_id: 3, date: 0, chat: { id: 10, type: 'private' } },
|
||||||
|
data: 'eff:1',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.deepEqual(selected, {
|
||||||
|
sessionId: 'live-eff',
|
||||||
|
provider: 'deepseek',
|
||||||
|
model: 'reasoner',
|
||||||
|
reasoningEffort: 'max',
|
||||||
})
|
})
|
||||||
assert.match(sent.at(-1)!.text, /已切换模型/)
|
assert.match(sent.at(-1)!.text, /已切换模型/)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user