5de58af9ed
Use a lightweight LLM over active task lists to pick taskId for cancel, exclude page.generate from scheduled-task inbound, and remove single-task auto-cancel so Cursor page generation stays isolated. Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
3.5 KiB
JavaScript
110 lines
3.5 KiB
JavaScript
function parseJsonReply(reply) {
|
|
const text = String(reply ?? '').trim();
|
|
if (!text) return null;
|
|
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
const candidate = fenced?.[1] ?? text;
|
|
try {
|
|
return JSON.parse(candidate);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function buildScheduledTaskListPayload(tasks = []) {
|
|
return tasks.map((task) => ({
|
|
taskId: task.id,
|
|
title: task.title,
|
|
recurrence: task.recurrence,
|
|
hour: task.hour,
|
|
minute: task.minute ?? 0,
|
|
taskSpec: String(task.taskSpec ?? '').slice(0, 240),
|
|
}));
|
|
}
|
|
|
|
export function normalizeScheduledTaskManageLlmDecision(payload, tasks = []) {
|
|
const action = String(payload?.action ?? 'none').trim();
|
|
const taskIds = new Set(tasks.map((task) => String(task.id ?? '').trim()).filter(Boolean));
|
|
|
|
if (action === 'clarify') {
|
|
const message = String(payload?.message ?? '').trim()
|
|
|| '我找到多个定时任务,请告诉我要取消哪一个。';
|
|
return { action: 'clarify', message };
|
|
}
|
|
|
|
if (action === 'cancel') {
|
|
const taskId = String(payload?.taskId ?? '').trim();
|
|
if (taskId && taskIds.has(taskId)) {
|
|
return { action: 'cancel', taskId };
|
|
}
|
|
return { action: 'none' };
|
|
}
|
|
|
|
return { action: 'none' };
|
|
}
|
|
|
|
export async function resolveScheduledTaskCancelWithLlm({
|
|
text,
|
|
tasks = [],
|
|
llmProviderService,
|
|
modelProviderKeyId = null,
|
|
model = null,
|
|
logger = console,
|
|
} = {}) {
|
|
if (!llmProviderService || typeof llmProviderService.createChatCompletion !== 'function') {
|
|
return { action: 'none' };
|
|
}
|
|
if (!Array.isArray(tasks) || tasks.length === 0) {
|
|
return { action: 'none' };
|
|
}
|
|
|
|
const taskList = buildScheduledTaskListPayload(tasks);
|
|
|
|
try {
|
|
const result = await llmProviderService.createChatCompletion({
|
|
...(modelProviderKeyId ? { providerKeyId: modelProviderKeyId } : {}),
|
|
...(model ? { model } : {}),
|
|
temperature: 0,
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: [
|
|
'你是微信服务号定时自动任务管理助手,只做任务选择,不做页面生成。',
|
|
'输入包含用户原话和当前 active 定时任务列表(JSON)。',
|
|
'请严格输出 JSON,不要输出解释。',
|
|
'允许 action:',
|
|
'- cancel:用户明确要取消某个任务,且能从列表中唯一确定 taskId',
|
|
'- clarify:用户想取消但无法唯一确定是哪一个,message 用中文简短追问',
|
|
'- none:不是取消任务,或信息不足且不应猜测',
|
|
'cancel 时必须返回列表里存在的 taskId;禁止编造 taskId。',
|
|
'如果只有一条任务且用户明确要取消/停止定时任务,可以返回该 taskId。',
|
|
'如果用户在要求生成/修改 HTML 页面,而不是管理已有定时任务,返回 none。',
|
|
].join('\n'),
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: JSON.stringify({
|
|
userMessage: String(text ?? '').trim(),
|
|
activeTasks: taskList,
|
|
}),
|
|
},
|
|
],
|
|
});
|
|
|
|
if (!result?.ok) {
|
|
logger?.warn?.(
|
|
'[wechat-scheduled-task-manage-llm] parse skipped:',
|
|
result?.message ?? 'unknown',
|
|
);
|
|
return { action: 'none' };
|
|
}
|
|
|
|
return normalizeScheduledTaskManageLlmDecision(parseJsonReply(result.reply), tasks);
|
|
} catch (err) {
|
|
logger?.warn?.(
|
|
'[wechat-scheduled-task-manage-llm] parse skipped:',
|
|
err instanceof Error ? err.message : err,
|
|
);
|
|
return { action: 'none' };
|
|
}
|
|
}
|