Files
Jyotisha/frontend/src/lib/rectification-agentic/v9/run-diagnostic.ts
T
Jesse_ChenandCursor 52db714ba9 fix(rectification): equal-weight collect, year-cue questions, classifier no/unsure (BUG-641–643)
Ask finance and health like other domains, put year-cued collect questions first, and let the classifier distinguish decline vs skip. Skill 10.0.22.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 21:53:10 +08:00

93 lines
3.4 KiB
TypeScript

/**
* Concrete finish reasons for rectification Agent runs.
*
* Incomplete turns must not be described only as “请稍后再试”. The code is
* durable; the Chinese copy is for the live client and is not persisted as a
* chat message.
*/
export const RECTIFICATION_FINISH_REASONS = [
"stop",
"tool_calls",
"length",
"timeout",
"max_steps",
"provider_error",
"aborted",
"unknown",
] as const;
export type RectificationFinishReason = (typeof RECTIFICATION_FINISH_REASONS)[number];
export type RectificationRunDiagnostic = Readonly<{
runId: string;
modelId: string;
finishReason: RectificationFinishReason;
inputTokens: number | null;
reasoningTokens: number | null;
outputTokens: number | null;
stepCount: number;
toolCallCount: number;
readCasePayloadBytes: number | null;
elapsedMs: number;
lastCompletedTool: string | null;
stateMutationCommitted: boolean;
expectedWrite: "evidence" | "none" | "unknown" | null;
collectIntent: "classified" | "unclassified" | null;
}>;
const USER_COPY: Readonly<Record<string, string>> = {
answer_truncated: "模型输出达到上限,状态已记录。",
length: "模型输出达到上限,状态已记录。",
run_timeout: "服务端运行超时,状态已记录。",
timeout: "服务端运行超时,状态已记录。",
stream_aborted: "服务端运行超时,状态已记录。",
stream_unfinished: "服务端运行超时,状态已记录。",
max_steps: "本轮达到步骤上限,状态已记录。",
tool_calls: "本轮达到步骤上限,状态已记录。",
provider_error: "上游模型连接失败,状态已记录。",
run_failed: "上游模型连接失败,状态已记录。",
empty_stream: "本轮没有生成可展示的回复,状态已记录。",
evidence_not_written: "这件我还没记上。请再说一次大概年月和发生的事。",
};
export function finishReasonFromErrorCode(errorCode: string | null | undefined): RectificationFinishReason {
if (errorCode === "answer_truncated") return "length";
if (errorCode === "run_timeout" || errorCode === "stream_unfinished") return "timeout";
if (errorCode === "stream_aborted") return "aborted";
if (errorCode === "max_steps") return "max_steps";
if (errorCode === "provider_error" || errorCode === "run_failed") return "provider_error";
return "unknown";
}
export function userFacingRunFailure(errorCode: string | null | undefined): string {
if (!errorCode) return "本轮没有完成,状态已记录。";
return USER_COPY[errorCode] ?? "本轮没有完成,状态已记录。";
}
export function isIncompleteRunBanner(text: string): boolean {
return /本轮处理未完成|当前进度已保留|请稍后再试/.test(text);
}
export function mapModelFinishToErrorCode(input: {
finishReason: string | null;
aborted: boolean;
timedOut: boolean;
answerText: string;
stepCount: number;
maxSteps: number;
}): string | null {
if (input.timedOut) return "run_timeout";
if (input.aborted) return "stream_aborted";
if (input.finishReason === "length") return "answer_truncated";
if (input.finishReason === "error") return "provider_error";
if (
(input.finishReason === "tool-calls" || input.finishReason === "tool_calls")
&& !input.answerText.trim()
) {
return "max_steps";
}
if (input.stepCount >= input.maxSteps && !input.answerText.trim()) return "max_steps";
return null;
}