NODEDC_1C/llm_normalizer/backend/src/services/assistantDeepTurnRetrievalR...

184 lines
5.8 KiB
TypeScript

import type { UnifiedRetrievalResult } from "../types/assistant";
import type { AssistantExecutionPlanItem } from "./assistantQueryPlanning";
import { normalizeRetrievalResult } from "./retrievalResultNormalizer";
export interface AssistantLiveTemporalHint {
as_of_date: string | null;
period_from: string | null;
period_to: string | null;
source: string | null;
}
export interface AssistantRetrievalCallRecord {
fragment_id: string;
requirement_ids: string[];
route: string;
status: "skipped" | "executed" | "failed";
query_text: string;
reason: string | null;
}
export type AssistantRetrievalScalar = string | number | boolean | null;
export type AssistantRetrievalFieldValue =
| AssistantRetrievalScalar
| AssistantRetrievalFieldValue[]
| { [key: string]: AssistantRetrievalFieldValue };
export type AssistantRetrievalRecord = Record<string, AssistantRetrievalFieldValue>;
export interface AssistantRetrievalRawResultLike {
status: "ok" | "empty" | "partial" | "error";
result_type: "list" | "summary" | "object" | "chain" | "ranking";
items: AssistantRetrievalRecord[];
summary: AssistantRetrievalRecord;
evidence: AssistantRetrievalRecord[];
why_included: string[];
selection_reason: string[];
risk_factors: string[];
business_interpretation: string[];
confidence: "high" | "medium" | "low";
limitations: string[];
errors: string[];
}
export type AssistantRetrievalRawListItem = AssistantRetrievalFieldValue;
export type AssistantRetrievalRawList = AssistantRetrievalFieldValue[];
export type AssistantRetrievalRawResult =
| AssistantRetrievalRawResultLike
| AssistantRetrievalRawList
| string
| number
| boolean
| null;
export interface AssistantRetrievalRawResultRecord {
fragment_id: string;
route: string;
raw_result: AssistantRetrievalRawResult;
}
export interface AssistantDeepTurnRetrievalExecutionInput {
executionPlan: AssistantExecutionPlanItem[];
liveTemporalHint: AssistantLiveTemporalHint | null;
executeRouteRuntime: (
route: string,
fragmentText: string,
options: {
temporalHint: AssistantLiveTemporalHint | null;
}
) => Promise<AssistantRetrievalRawResult>;
mapNoRouteReason: (reason: string | null) => string;
buildSkippedResult: (item: AssistantExecutionPlanItem) => UnifiedRetrievalResult;
normalizeRetrievalResultFn?: typeof normalizeRetrievalResult;
}
export interface AssistantDeepTurnRetrievalExecutionOutput {
retrievalCalls: AssistantRetrievalCallRecord[];
retrievalResultsRaw: AssistantRetrievalRawResultRecord[];
retrievalResults: UnifiedRetrievalResult[];
}
function buildRouteExecutorErrorRawResult(route: string, message: string): AssistantRetrievalRawResult {
return {
status: "error",
result_type: "summary",
items: [],
summary: {
route
},
evidence: [],
why_included: [],
selection_reason: [],
risk_factors: [],
business_interpretation: [],
confidence: "low",
limitations: ["Route executor failed."],
errors: [message]
};
}
function normalizeRawResult(
value: AssistantRetrievalRawResult | object | null | undefined
): AssistantRetrievalRawResult {
if (value === null || value === undefined) {
return null;
}
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return value;
}
if (Array.isArray(value)) {
return value as AssistantRetrievalRawList;
}
if (typeof value === "object") {
return value as AssistantRetrievalRawResultLike;
}
return null;
}
export async function executeAssistantDeepTurnRetrievalPlan(
input: AssistantDeepTurnRetrievalExecutionInput
): Promise<AssistantDeepTurnRetrievalExecutionOutput> {
const normalizeRetrievalResultSafe = input.normalizeRetrievalResultFn ?? normalizeRetrievalResult;
const retrievalCalls: AssistantRetrievalCallRecord[] = [];
const retrievalResultsRaw: AssistantRetrievalRawResultRecord[] = [];
const retrievalResults: UnifiedRetrievalResult[] = [];
for (const planItem of input.executionPlan) {
if (!planItem.should_execute) {
retrievalCalls.push({
fragment_id: planItem.fragment_id,
requirement_ids: planItem.requirement_ids,
route: planItem.route,
status: "skipped",
query_text: planItem.fragment_text,
reason: input.mapNoRouteReason(planItem.no_route_reason)
});
retrievalResults.push(input.buildSkippedResult(planItem));
continue;
}
retrievalCalls.push({
fragment_id: planItem.fragment_id,
requirement_ids: planItem.requirement_ids,
route: planItem.route,
status: "executed",
query_text: planItem.fragment_text,
reason: null
});
try {
const raw = await input.executeRouteRuntime(planItem.route, planItem.fragment_text, {
temporalHint: input.liveTemporalHint
});
const normalizedRaw = normalizeRawResult(raw);
retrievalResultsRaw.push({
fragment_id: planItem.fragment_id,
route: planItem.route,
raw_result: normalizedRaw
});
retrievalResults.push(
normalizeRetrievalResultSafe(planItem.fragment_id, planItem.requirement_ids, planItem.route, normalizedRaw)
);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
retrievalCalls[retrievalCalls.length - 1].status = "failed";
retrievalCalls[retrievalCalls.length - 1].reason = message;
const rawError = buildRouteExecutorErrorRawResult(planItem.route, message);
retrievalResultsRaw.push({
fragment_id: planItem.fragment_id,
route: planItem.route,
raw_result: rawError
});
retrievalResults.push(
normalizeRetrievalResultSafe(planItem.fragment_id, planItem.requirement_ids, planItem.route, rawError)
);
}
}
return {
retrievalCalls,
retrievalResultsRaw,
retrievalResults
};
}