fix(router): route casual chat to DeepSeek and reserve Cursor for page tasks
Default unmatched conversation to direct_chat, stop blanket Agent session continuation and generic Agent→Cursor promotion; fix WeChat page retry userPublishDir scope and simplify ChatPanel controls. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+62
-42
@@ -443,14 +443,22 @@ test('classifyWithRules keeps agent session continuation even when llm router fl
|
||||
assert.match(continued.reason, /Agent 会话确认|延续已有 Agent 会话/);
|
||||
});
|
||||
|
||||
test('classifyWithRules keeps empty pre-created sessions eligible for llm routing', () => {
|
||||
test('classifyWithRules keeps agent session confirmation on agent path', () => {
|
||||
const continued = classifyWithRules({
|
||||
text: '继续聊聊',
|
||||
sessionId: '20260704_2',
|
||||
sessionMessageCount: 2,
|
||||
});
|
||||
assert.equal(continued.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(continued.reason, '延续已有 Agent 会话');
|
||||
assert.equal(continued.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.match(continued.reason, /轻量聊天/);
|
||||
|
||||
const confirmed = classifyWithRules({
|
||||
text: '可以',
|
||||
sessionId: '20260704_2',
|
||||
sessionMessageCount: 2,
|
||||
});
|
||||
assert.equal(confirmed.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.match(confirmed.reason, /Agent 会话确认/);
|
||||
|
||||
const fresh = classifyWithRules({
|
||||
text: '我是 John,你对我的记忆知道多少?',
|
||||
@@ -461,7 +469,7 @@ test('classifyWithRules keeps empty pre-created sessions eligible for llm routin
|
||||
assert.match(fresh.reason, /记忆/);
|
||||
});
|
||||
|
||||
test('classifyWithRules keeps memory recall on agent when session already active', () => {
|
||||
test('classifyWithRules keeps memory recall on direct chat even when session already active', () => {
|
||||
const result = classifyWithRules({
|
||||
text: '你记得我说想去哪儿吗',
|
||||
sessionId: '20260704_9',
|
||||
@@ -472,8 +480,23 @@ test('classifyWithRules keeps memory recall on agent when session already active
|
||||
metadata: { displayText: '你记得我说想去哪儿吗' },
|
||||
},
|
||||
});
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.match(result.reason, /延续已有 Agent 会话/);
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.match(result.reason, /记忆|历史对话/);
|
||||
});
|
||||
|
||||
test('classifyWithRules keeps casual FAQ chat on direct chat even when session already active', () => {
|
||||
const result = classifyWithRules({
|
||||
text: '你觉得人都是为了什么而活',
|
||||
sessionId: '20260826_39',
|
||||
sessionMessageCount: 4,
|
||||
userMessage: {
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: '你觉得人都是为了什么而活' }],
|
||||
metadata: { displayText: '你觉得人都是为了什么而活' },
|
||||
},
|
||||
});
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.match(result.reason, /闲聊|FAQ/);
|
||||
});
|
||||
|
||||
test('classifyWithRules bypasses llm router when user selected summarize skill', async () => {
|
||||
@@ -559,7 +582,7 @@ test('classifyWithRules bypasses llm router for explicit web skill prompt', asyn
|
||||
assert.equal(llmCalls.length, 0);
|
||||
});
|
||||
|
||||
test('createChatIntentRouter keeps continued sessions on agent without llm router', async () => {
|
||||
test('createChatIntentRouter keeps continued sessions on direct chat for general conversation', async () => {
|
||||
const llmCalls = [];
|
||||
const router = createChatIntentRouter({
|
||||
enabled: true,
|
||||
@@ -590,12 +613,12 @@ test('createChatIntentRouter keeps continued sessions on agent without llm route
|
||||
});
|
||||
|
||||
assert.equal(llmCalls.length, 0);
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.source, 'rule');
|
||||
assert.match(result.reason, /延续已有 Agent 会话/);
|
||||
assert.match(result.reason, /轻量聊天/);
|
||||
});
|
||||
|
||||
test('createChatIntentRouter uses agent fallback for general conversational analysis when llm router disabled', async () => {
|
||||
test('createChatIntentRouter uses direct chat fallback for general conversational analysis when llm router disabled', async () => {
|
||||
const llmCalls = [];
|
||||
const resolveCalls = [];
|
||||
const router = createChatIntentRouter({
|
||||
@@ -630,8 +653,8 @@ test('createChatIntentRouter uses agent fallback for general conversational anal
|
||||
grantedSkills: ['web'],
|
||||
});
|
||||
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(result.source, 'fallback');
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.source, 'rule');
|
||||
assert.equal(resolveCalls.length, 0);
|
||||
assert.equal(llmCalls.length, 0);
|
||||
});
|
||||
@@ -775,11 +798,11 @@ test('createManagedChatIntentRouter hot-loads admin config and activates llm rou
|
||||
},
|
||||
});
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.source, 'llm');
|
||||
assert.equal(llmCalls.length, 1);
|
||||
assert.equal(result.source, 'rule');
|
||||
assert.equal(llmCalls.length, 0);
|
||||
});
|
||||
|
||||
test('createChatIntentRouter rejects low-confidence llm direct_chat and falls back to agent', async () => {
|
||||
test('createChatIntentRouter rejects low-confidence llm direct_chat and keeps rule default', async () => {
|
||||
const llmCalls = [];
|
||||
const router = createChatIntentRouter({
|
||||
enabled: true,
|
||||
@@ -809,12 +832,12 @@ test('createChatIntentRouter rejects low-confidence llm direct_chat and falls ba
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(result.source, 'fallback');
|
||||
assert.equal(llmCalls.length, 1);
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.source, 'rule');
|
||||
assert.equal(llmCalls.length, 0);
|
||||
});
|
||||
|
||||
test('createChatIntentRouter falls back to agent when llm router fails', async () => {
|
||||
test('createChatIntentRouter keeps direct chat when llm router fails', async () => {
|
||||
const llmCalls = [];
|
||||
const router = createChatIntentRouter({
|
||||
enabled: true,
|
||||
@@ -835,12 +858,12 @@ test('createChatIntentRouter falls back to agent when llm router fails', async (
|
||||
grantedSkills: ['web'],
|
||||
});
|
||||
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(result.source, 'fallback');
|
||||
assert.equal(llmCalls.length, 1);
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.source, 'rule');
|
||||
assert.equal(llmCalls.length, 0);
|
||||
});
|
||||
|
||||
test('createChatIntentRouter uses agent fallback when llm router disabled', async () => {
|
||||
test('createChatIntentRouter uses direct chat when llm router disabled', async () => {
|
||||
const llmCalls = [];
|
||||
const router = createChatIntentRouter({
|
||||
enabled: false,
|
||||
@@ -860,8 +883,8 @@ test('createChatIntentRouter uses agent fallback when llm router disabled', asyn
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(result.source, 'fallback');
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.source, 'rule');
|
||||
assert.equal(llmCalls.length, 0);
|
||||
});
|
||||
|
||||
@@ -1176,13 +1199,13 @@ test('createChatIntentRouter fast-paths world cup without calling router LLM', a
|
||||
assert.equal(result.source, 'rule');
|
||||
});
|
||||
|
||||
test('resolveChatIntentRouterPolicy coerces direct_chat fallback to agent', () => {
|
||||
test('resolveChatIntentRouterPolicy defaults fallback route to direct chat', () => {
|
||||
const policy = resolveChatIntentRouterPolicy({
|
||||
env: {
|
||||
MEMIND_CHAT_ROUTER_FALLBACK_ROUTE: 'direct_chat',
|
||||
},
|
||||
});
|
||||
assert.equal(policy.fallbackRoute, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(policy.fallbackRoute, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
});
|
||||
|
||||
test('coerceRealtimeWebSkill replaces search with web for world cup query', () => {
|
||||
@@ -1259,7 +1282,7 @@ test('createChatIntentRouter coerces LLM search route to web for realtime query'
|
||||
assert.match(result.agentBrief, /web_search/);
|
||||
});
|
||||
|
||||
test('createChatIntentRouter timeout fallback prefers agent even when policy requests direct_chat', async () => {
|
||||
test('createChatIntentRouter timeout fallback keeps direct chat default', async () => {
|
||||
const router = createChatIntentRouter({
|
||||
enabled: true,
|
||||
timeoutMs: 40,
|
||||
@@ -1280,8 +1303,8 @@ test('createChatIntentRouter timeout fallback prefers agent even when policy req
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(result.source, 'fallback');
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.source, 'rule');
|
||||
});
|
||||
|
||||
test('agent memory shadow resolve is independently gated and never marked for injection', async () => {
|
||||
@@ -1592,14 +1615,14 @@ test('createChatIntentRouter limits llm routing to canary users', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(control.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(control.source, 'fallback');
|
||||
assert.equal(control.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(control.source, 'rule');
|
||||
assert.equal(canary.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(canary.source, 'llm');
|
||||
assert.equal(llmCalls.length, 1);
|
||||
assert.equal(canary.source, 'rule');
|
||||
assert.equal(llmCalls.length, 0);
|
||||
});
|
||||
|
||||
test('createChatIntentRouter shadow mode logs llm suggestion without changing route', async () => {
|
||||
test('createChatIntentRouter shadow mode skips llm shadow when rule default matches', async () => {
|
||||
const lines = [];
|
||||
const logger = { warn(line) { lines.push(line); }, info(line) { lines.push(line); } };
|
||||
const router = createChatIntentRouter({
|
||||
@@ -1631,13 +1654,10 @@ test('createChatIntentRouter shadow mode logs llm suggestion without changing ro
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT);
|
||||
assert.equal(result.source, 'fallback');
|
||||
assert.equal(result.llmShadow?.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.llmShadow?.wouldChangeRoute, true);
|
||||
assert.equal(lines.length, 1);
|
||||
assert.match(lines[0], /\[chat-llm-router-shadow\]/);
|
||||
assert.match(lines[0], /wouldChangeRoute":true/);
|
||||
assert.equal(result.route, CHAT_INTENT_ROUTE.DIRECT_CHAT);
|
||||
assert.equal(result.source, 'rule');
|
||||
assert.equal(result.llmShadow, undefined);
|
||||
assert.equal(lines.length, 0);
|
||||
});
|
||||
|
||||
test('logChatLlmRouterShadow emits structured payload', () => {
|
||||
|
||||
Reference in New Issue
Block a user