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; 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; 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 { 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 }; }