import { describe, expect, it, vi } from "vitest"; import { runAssistantDeepTurnAttemptRuntime } from "../src/services/assistantDeepTurnAttemptRuntimeAdapter"; function buildInput(overrides: Record = {}) { return { sessionId: "asst-1", questionId: "msg-q1", userMessage: "почему не закрыт долг", payload: { llmProvider: "openai", apiKey: "key", model: "gpt-5", baseUrl: "http://localhost", temperature: 0.2, maxOutputTokens: 400, context: {} }, runtimeAnalysisContext: { active: true, as_of_date: "2020-07-31", period_from: null, period_to: null, source: "analysis_context" }, sessionInvestigationState: null, addressRuntimeMetaForDeep: null, featureInvestigationStateV1: true, featureStateFollowupBindingV1: true, featureContractsV11: true, featureAnswerPolicyV11: true, featureProblemCentricAnswerV1: true, featureLifecycleAnswerV1: true, buildFollowupStateBinding: () => ({ normalizedQuestion: "normalized-question", mergedContext: {}, usage: null }), normalize: async () => ({ trace_id: "trace-1", prompt_version: "normalizer_v2_0_2", schema_version: "normalized_query_v2_0_2", normalized: { fragments: [] }, route_hint_summary: { mode: "deterministic_v2", decisions: [] } }) as any, resolveBusinessScopeAlignment: ({ routeSummary }) => ({ route_summary_resolved: routeSummary }), inferP0DomainFromMessage: () => null, resolveBusinessScopeFromLiveContext: ({ current }) => current, extractRequirements: () => ({ requirements: [], byFragment: new Map() }), toExecutionPlan: () => [], enforceRbpLiveRoutePlan: ({ executionPlan }) => ({ executionPlan, audit: {} }), enforceFaLiveRoutePlan: ({ executionPlan }) => ({ executionPlan, audit: {} }), executeRouteRuntime: async () => ({ status: "ok", result_type: "summary", items: [], summary: {}, evidence: [], limitations: [] }), mapNoRouteReason: () => "no_route", buildSkippedResult: () => ({}) as any, evaluateCoverage: () => ({ requirements: [], coverage: {} }), checkGrounding: () => ({ status: "no_grounded_answer", reasons: [] }), collectRbpLiveRouteAudit: () => ({}), collectFaLiveRouteAudit: () => ({}), hasExplicitPeriodAnchor: () => false, extractDroppedIntentSegments: () => [], buildDebugRoutes: () => [], extractExecutionState: () => [], sanitizeReply: (value: string) => value, persistInvestigationState: () => {}, messageIdFactory: () => "msg-a1", appendItem: () => {}, getSession: () => ({ session_id: "asst-1", items: [] }), persistSession: () => {}, cloneConversation: () => [], logEvent: () => {}, ...overrides } as any; } describe("assistant deep turn attempt runtime adapter", () => { it("orchestrates normalization, analysis and response attempts in order", async () => { const callOrder: string[] = []; const normalizationRuntime = { followupBinding: { normalizedQuestion: "NQ", mergedContext: {}, usage: { applied: true } }, normalizePayload: { userQuestion: "NQ" }, normalized: { trace_id: "trace-1", prompt_version: "normalizer_v2_0_2", schema_version: "normalized_query_v2_0_2", normalized: { fragments: [] }, route_hint_summary: { mode: "deterministic_v2", decisions: [] } } } as any; const analysisRuntime = { resolvedRouteSummary: { mode: "deterministic_v2", decisions: [] }, requirementExtraction: { requirements: [], byFragment: new Map() }, coverageEvaluation: { requirements: [], coverage: {} }, groundingCheck: { status: "no_grounded_answer", reasons: [] }, executionPlan: [], retrievalCalls: [], retrievalResultsRaw: [], retrievalResults: [], questionTypeClass: "single_fact_lookup", companyAnchors: {}, businessScopeResolution: {}, temporalGuard: {}, polarityGuardResult: { audit: {} }, claimAnchorAudit: { claim_type: "unknown" }, targetedEvidenceResult: { audit: {} }, evidenceGateResult: { audit: {} }, rbpLiveRouteAudit: {}, faLiveRouteAudit: {}, groundedAnswerEligibilityGuard: {}, composition: { reply_type: "partial_coverage" } } as any; const runDeepTurnNormalizationRuntime = vi.fn(async () => { callOrder.push("normalization"); return normalizationRuntime; }); const runDeepTurnAnalysisAttemptRuntime = vi.fn(async (input) => { callOrder.push("analysis"); expect(input.normalizedPayload).toEqual(normalizationRuntime.normalized.normalized); expect(input.routeSummary).toEqual(normalizationRuntime.normalized.route_hint_summary); expect(input.followupUsage).toEqual({ applied: true }); return analysisRuntime; }); const runDeepTurnResponseAttemptRuntime = vi.fn((input) => { callOrder.push("response"); expect(input.normalizedQuestion).toBe("NQ"); expect(input.deepTurnAnalysisRuntime).toBe(analysisRuntime); expect(input.followupApplied).toBe(true); return { response: { ok: true }, debug: { trace_id: "trace-1" } }; }); const runtime = await runAssistantDeepTurnAttemptRuntime( buildInput({ runDeepTurnNormalizationRuntime, runDeepTurnAnalysisAttemptRuntime, runDeepTurnResponseAttemptRuntime }) ); expect(callOrder).toEqual(["normalization", "analysis", "response"]); expect(runtime.response).toEqual({ ok: true }); expect(runtime.debug).toEqual({ trace_id: "trace-1" }); }); it("computes followupApplied=false when followup usage is absent", async () => { const runDeepTurnNormalizationRuntime = vi.fn(async () => ({ followupBinding: { normalizedQuestion: "NQ", mergedContext: {}, usage: null }, normalizePayload: {}, normalized: { trace_id: "trace-1", prompt_version: "normalizer_v2_0_2", schema_version: "normalized_query_v2_0_2", normalized: { fragments: [] }, route_hint_summary: null } })); const runDeepTurnResponseAttemptRuntime = vi.fn(() => ({ response: { ok: true }, debug: {} })); const investigationState = { session_id: "asst-1" } as any; await runAssistantDeepTurnAttemptRuntime( buildInput({ sessionInvestigationState: investigationState, runDeepTurnNormalizationRuntime, runDeepTurnAnalysisAttemptRuntime: async () => ({ resolvedRouteSummary: null, requirementExtraction: { requirements: [], byFragment: new Map() }, coverageEvaluation: { requirements: [], coverage: {} }, groundingCheck: { status: "no_grounded_answer", reasons: [] }, executionPlan: [], retrievalCalls: [], retrievalResultsRaw: [], retrievalResults: [], questionTypeClass: "single_fact_lookup", companyAnchors: {}, businessScopeResolution: {}, temporalGuard: {}, polarityGuardResult: { audit: {} }, claimAnchorAudit: { claim_type: "unknown" }, targetedEvidenceResult: { audit: {} }, evidenceGateResult: { audit: {} }, rbpLiveRouteAudit: {}, faLiveRouteAudit: {}, groundedAnswerEligibilityGuard: {}, composition: { reply_type: "partial_coverage" } }) as any, runDeepTurnResponseAttemptRuntime }) ); expect(runDeepTurnResponseAttemptRuntime).toHaveBeenCalledWith( expect.objectContaining({ followupApplied: false, previousInvestigationState: investigationState }) ); }); });