import type { AssistantRequirement, AnswerGroundingCheck, RequirementCoverageReport, UnifiedRetrievalResult } from "../types/assistant"; import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer"; import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits"; import type { QuestionTypeClass } from "./questionTypeResolver"; import { resolveQuestionType } from "./questionTypeResolver"; import { composeAssistantAnswer } from "./answerComposer"; import type { CompanyAnchorSet } from "./companyAnchorResolver"; import { isAssistantFollowupApplied, type AssistantFollowupUsage } from "./assistantFollowupUsage"; export interface BuildAssistantDeepTurnCompositionInput { userMessage: string; routeSummary: RouteHintSummary | null; retrievalResults: UnifiedRetrievalResult[]; requirements: AssistantRequirement[]; coverageReport: RequirementCoverageReport; groundingCheck: AnswerGroundingCheck; followupUsage: AssistantFollowupUsage | null | undefined; investigationState: InvestigationStateWithProblemUnits | null | undefined; companyAnchors: CompanyAnchorSet | null; normalizedPayload: NormalizeResponsePayload["normalized"]; featureAnswerPolicyV11: boolean; featureProblemCentricAnswerV1: boolean; featureLifecycleAnswerV1: boolean; hasExplicitPeriodAnchor: (normalizedPayload: NormalizeResponsePayload["normalized"]) => boolean; resolveQuestionTypeFn?: (input: string) => QuestionTypeClass; composeAssistantAnswerFn?: typeof composeAssistantAnswer; } export interface AssistantDeepTurnCompositionOutput { focusDomainHint: string | null; questionTypeClass: QuestionTypeClass; hasPeriodInCompanyAnchors: boolean; normalizationPeriodExplicit: boolean; composition: ReturnType; } function toStringArray(value: string[] | null | undefined): string[] { if (!Array.isArray(value)) { return []; } return value .map((item) => (typeof item === "string" ? item.trim() : "")) .filter((item) => item.length > 0); } function normalizeCompanyAnchorSet(value: Partial | null | undefined): CompanyAnchorSet | null { if (!value || typeof value !== "object" || Array.isArray(value)) { return null; } const source: Partial = value; return { contract_numbers: toStringArray(source.contract_numbers), document_numbers: toStringArray(source.document_numbers), dates: toStringArray(source.dates), amounts: toStringArray(source.amounts), accounts: toStringArray(source.accounts), periods: toStringArray(source.periods), document_types: toStringArray(source.document_types), all: toStringArray(source.all) }; } export function buildAssistantDeepTurnComposition( input: BuildAssistantDeepTurnCompositionInput ): AssistantDeepTurnCompositionOutput { const resolveQuestionTypeSafe = input.resolveQuestionTypeFn ?? resolveQuestionType; const composeAssistantAnswerSafe = input.composeAssistantAnswerFn ?? composeAssistantAnswer; const followupApplied = isAssistantFollowupApplied(input.followupUsage); const focusDomainHint = followupApplied ? input.investigationState?.followup_context?.active_domain ?? input.investigationState?.focus.domain ?? null : null; const questionTypeClass = resolveQuestionTypeSafe(input.userMessage); const companyAnchorSet = normalizeCompanyAnchorSet(input.companyAnchors); const hasPeriodInCompanyAnchors = (Array.isArray(companyAnchorSet?.dates) && companyAnchorSet.dates.some((item) => String(item ?? "").trim().length > 0)) || (Array.isArray(companyAnchorSet?.periods) && companyAnchorSet.periods.some((item) => String(item ?? "").trim().length > 0)); const normalizationPeriodExplicit = input.hasExplicitPeriodAnchor(input.normalizedPayload) || hasPeriodInCompanyAnchors; const composition = composeAssistantAnswerSafe({ userMessage: input.userMessage, routeSummary: input.routeSummary, retrievalResults: input.retrievalResults, requirements: input.requirements, coverageReport: input.coverageReport, groundingCheck: input.groundingCheck, focusDomainHint, questionTypeHint: questionTypeClass, companyAnchors: companyAnchorSet, normalizationPeriodExplicit, enableAnswerPolicyV11: input.featureAnswerPolicyV11, enableProblemCentricAnswerV1: input.featureProblemCentricAnswerV1, enableLifecycleAnswerV1: input.featureLifecycleAnswerV1 }); return { focusDomainHint, questionTypeClass, hasPeriodInCompanyAnchors, normalizationPeriodExplicit, composition }; }