222 lines
7.8 KiB
TypeScript
222 lines
7.8 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { runAssistantDeepTurnAttemptRuntime } from "../src/services/assistantDeepTurnAttemptRuntimeAdapter";
|
|
|
|
function buildInput(overrides: Record<string, unknown> = {}) {
|
|
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
|
|
})
|
|
);
|
|
});
|
|
});
|