fix(wechat): stop relay bootstrap from hijacking LLM provider selection.
Memind CI / Test, build, and release guards (push) Failing after 2s

Schedule LLM now binds an explicit provider key and fails closed on network errors so dead relay endpoints cannot block ordinary chat; relay bootstrap is opt-in only.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-01 21:44:08 +08:00
parent 122a25649b
commit dac06753ce
7 changed files with 121 additions and 39 deletions
+39 -5
View File
@@ -19,13 +19,28 @@ function createScheduleService() {
};
}
function createScheduleLlmConfigService(enabled = true) {
return {
async isScheduleLlmEnabled() {
return enabled;
},
async getConfig() {
return {
scheduleLlmEnabled: enabled,
modelProviderKeyId: 'deepseek-key',
model: 'deepseek-v4-pro',
};
},
};
}
test('schedule handler keeps rule-based parsing as first priority', async () => {
let llmCalled = false;
const reply = await handleWechatScheduleIntent({
intent: { agentText: '帮我记一下 跟进合同', msgId: 'msg-1' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: { async isScheduleLlmEnabled() { return true; } },
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
llmCalled = true;
@@ -42,7 +57,7 @@ test('schedule handler can use llm fallback for ambiguous todo text', async () =
intent: { agentText: '下周把合同发给客户,别忘了', msgId: 'msg-2' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: { async isScheduleLlmEnabled() { return true; } },
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
return {
@@ -71,7 +86,7 @@ test('schedule handler does not call llm when switch is off', async () => {
intent: { agentText: '别忘了报销', msgId: 'msg-4' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: { async isScheduleLlmEnabled() { return false; } },
wechatScheduleLlmConfigService: createScheduleLlmConfigService(false),
llmProviderService: {
async createChatCompletion() {
llmCalled = true;
@@ -89,7 +104,7 @@ test('schedule handler falls back quietly when llm request fails', async () => {
intent: { agentText: '别忘了报销', msgId: 'msg-5' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: { async isScheduleLlmEnabled() { return true; } },
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
return { ok: false, message: 'provider unavailable' };
@@ -102,12 +117,31 @@ test('schedule handler falls back quietly when llm request fails', async () => {
assert.match(warnings[0], /provider unavailable/);
});
test('schedule handler falls back quietly when llm request throws', async () => {
const warnings = [];
const reply = await handleWechatScheduleIntent({
intent: { agentText: '什么是机器学习', msgId: 'msg-6' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
throw new TypeError('fetch failed');
},
},
logger: { warn: (...args) => warnings.push(args.join(' ')) },
});
assert.equal(reply, null);
assert.equal(warnings.length, 1);
assert.match(warnings[0], /fetch failed/);
});
test('schedule handler falls through for llm schedule_agent results', async () => {
const reply = await handleWechatScheduleIntent({
intent: { agentText: '明天下午三点提醒我开会', msgId: 'msg-3' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: { async isScheduleLlmEnabled() { return true; } },
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
return {