103 lines
4.5 KiB
TypeScript
103 lines
4.5 KiB
TypeScript
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<typeof composeAssistantAnswer>;
|
|
}
|
|
|
|
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<CompanyAnchorSet> | null | undefined): CompanyAnchorSet | null {
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
return null;
|
|
}
|
|
const source: Partial<CompanyAnchorSet> = 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
|
|
};
|
|
}
|