assistant/renderer/taskView.ts

84 lines
6.3 KiB
TypeScript

import { STATUS_LABEL, type ChatMessage, type Task, type TaskFeedbackValue, type TaskSummary } from './types';
const STOP_REASON_LABEL: Record<string, string> = {
target_reached: '已达到本次人数要求,已停止新增采集;字段依据与交付产物见下方。',
target_shortfall: '本阶段已结束,候选人数尚未达到目标;已有结果和未完成范围已保留。',
evidence_stalled: '同一笔记连续取证失败且没有新增内容,已停止重复尝试;其他路线尚未算完成。',
user_stopped: '你已停止任务,已完成的结果和进度已保留。',
cancelled: '你已停止任务,已完成的结果和进度已保留。',
interrupted: '执行被中断,已保存结果可继续使用。',
app_interrupted: '应用重启中断了执行,已有来源和动作记录已保留,可处理后继续。',
application_restart: '应用重启中断了执行,进度和已有结果已保留。',
no_progress: '当前页面没有新增进展,已暂停并保留阶段结果。',
no_new_content: '当前路线暂未出现新内容,可以查看结果或调整路线。',
route_exhausted: '当前检索路线已处理到可访问范围,未代表平台全部内容。',
model_done: '助手主动结束了本阶段,实际覆盖范围见下方说明。',
agent_done: '助手主动结束了本阶段,实际覆盖范围见下方说明。',
model_stopped: '助手结束了本阶段,实际覆盖范围见下方说明。',
model_incomplete: '本阶段尚未完成,已保留实际读取的结果。',
waiting_user: '任务需要你补充信息或处理当前页面。',
awaiting_user: '任务正在等待你回答问题,或完成当前页面的登录与验证。',
platform_limit: '当前阶段受到平台限制,已有成果已保留。',
platform_limited: '页面出现平台限制,受限阶段已暂停,已有成果已保留。',
dm_failed: '私信发送失败,已暂停当前账号后续私信,原因待确认。',
message_failed: '私信发送失败,已暂停当前账号后续私信,原因待确认。',
dm_daily_limit: '页面提示今日私信已达上限,已停止当前账号后续私信。',
platform_daily_limit: '页面提示今日私信已达上限,已停止当前账号后续私信。',
service_error: '上游服务暂时不可用,已有成果已保留。',
model_balance: '模型服务提示余额不足,请处理服务余额后继续;已有成果已保留。',
model_auth: '模型服务鉴权失败,请在模型设置中检查本项目的 Key 和接口配置。',
model_rate_limit: '模型服务正在限流,当前请求未完成;可以稍后继续。',
model_schema_error: '模型动作格式连续校验失败,已停止执行并保留资料;具体错误类型已记入诊断记录。',
model_failure_limit: '连续模型执行错误触发停止,已有结果保留;具体原因见执行记录。',
model_setup: '需要先完成本项目的模型配置,再继续当前任务。',
intent_error: '本次目标理解未能完成,请重试或补充说明;原任务和证据已保留。',
execution_error: '页面执行遇到异常,已保存的结果和动作记录仍可继续使用。',
analysis_error: '来源分析未完成,实际页面证据已保留,可以继续分析。',
checkpoint_error: '执行进度记录暂时无法核对,已暂停,避免把已完成的操作当作新操作重复执行。',
no_sources: '当前没有保存到可核实的页面来源,不代表平台上没有相关需求。',
no_saved_sources: '当前没有保存到可核实的页面来源,不代表平台上没有相关需求。',
export_ready: '已有名单可以导出,未知字段和证据缺项会如实保留。',
outreach_authorization: '跟进计划已整理,尚未执行。请核对目标范围;私信还需核对消息内容。',
agent_reported_failure: '助手结束时报告本阶段尚未完成,已保存实际读取的结果。',
timeout: '本次请求超时,已保存的结果和进度仍可继续使用。',
error: '执行遇到异常,已保存的结果和进度仍可继续使用。',
completed: '已完成本次明确交代的范围。',
goal_completed: '已完成本次明确交代的范围。',
};
export function displayTime(value?: string): string {
if (!value) return '';
const date = new Date(value);
return Number.isNaN(date.valueOf()) ? value : date.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
}
export function statusLabel(task: Pick<TaskSummary, 'status' | 'current_step'>): string {
if (task.current_step === 'preflight_results') return task.status === 'completed' ? '预检完成' : task.status === 'running' ? '预检中' : '预检阶段结果';
if (task.status === 'blocked' && ['results', 'partial_results', 'stage_result', 'stage_results'].includes(task.current_step || '')) return '阶段结果';
return STATUS_LABEL[task.status] || task.status;
}
export function messagesOf(task: Task): ChatMessage[] {
const saved = task.artifacts.messages;
if (Array.isArray(saved) && saved.length) return saved.filter(item => item && typeof item.text === 'string' && ['user', 'assistant', 'system'].includes(item.role));
return task.inputs.goal ? [{ id: `${task.id}-goal`, role: 'user', text: task.inputs.goal }] : [];
}
export function stopReasonText(task: Task): string {
const reviewError = task.artifacts.review_error;
if (typeof reviewError === 'string' && reviewError.trim()) return reviewError;
if (reviewError && typeof reviewError === 'object' && 'message' in reviewError && typeof reviewError.message === 'string' && reviewError.message.trim()) return reviewError.message;
const detail = task.artifacts.stop_detail;
if (typeof detail === 'string' && detail.trim()) return detail;
const reason = task.artifacts.stop_reason;
if (typeof reason === 'object' && reason) return reason.message || STOP_REASON_LABEL[reason.code || ''] || '';
if (typeof reason === 'string') return STOP_REASON_LABEL[reason] || (/[\u3400-\u9fff]/.test(reason) ? reason : '');
return '';
}
export function feedbackOf(task: Task): TaskFeedbackValue | undefined {
const feedback = task.artifacts.feedback;
if (Array.isArray(feedback)) return feedback.at(-1);
return feedback && typeof feedback === 'object' ? feedback : undefined;
}