NODEDC_1C/llm_normalizer/backend/tests/assistantAddressLaneRuntime...

106 lines
4.1 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
runAssistantAddressLaneRuntime,
type AssistantAddressFollowupCarryoverLike,
type AssistantAddressLaneLike
} from "../src/services/assistantAddressLaneRuntimeAdapter";
function limitedLane(category: string): AssistantAddressLaneLike {
return {
handled: true,
debug: {
limited_reason_category: category
}
};
}
function factualLane(): AssistantAddressLaneLike {
return {
handled: true,
debug: {}
};
}
function unhandledLane(): AssistantAddressLaneLike {
return {
handled: false,
debug: {}
};
}
describe("assistant address lane runtime adapter", () => {
it("returns contextual lane immediately when preferred contextual attempt is factual", async () => {
const carryover: AssistantAddressFollowupCarryoverLike = { followupContext: { scope: "ctx" } };
const runAddressLaneAttempt = vi.fn(async () => factualLane());
const result = await runAssistantAddressLaneRuntime({
userMessage: "сырой вопрос",
addressInputMessage: "нормализованный вопрос",
carryover,
shouldPreferContextualLane: true,
canRetryWithRawUserMessage: true,
runAddressLaneAttempt,
isRetryableAddressLimitedResult: (lane) => Boolean(lane?.debug?.limited_reason_category)
});
expect(result.handled).toBe(true);
expect(result.selection?.messageUsed).toBe("нормализованный вопрос");
expect(result.selection?.carryMeta).toBe(carryover);
expect(result.retryAudit.attempted).toBe(false);
expect(runAddressLaneAttempt).toHaveBeenCalledTimes(1);
expect(runAddressLaneAttempt).toHaveBeenCalledWith("нормализованный вопрос", carryover);
});
it("retries with raw message after limited result and returns factual retry", async () => {
const carryover: AssistantAddressFollowupCarryoverLike = { followupContext: { scope: "ctx" } };
const runAddressLaneAttempt = vi
.fn()
.mockResolvedValueOnce(limitedLane("empty_match")) // primary
.mockResolvedValueOnce(limitedLane("empty_match")) // contextual
.mockResolvedValueOnce(factualLane()); // raw contextual retry
const result = await runAssistantAddressLaneRuntime({
userMessage: "сырой вопрос",
addressInputMessage: "нормализованный вопрос",
carryover,
shouldPreferContextualLane: false,
canRetryWithRawUserMessage: true,
runAddressLaneAttempt,
isRetryableAddressLimitedResult: (lane) => Boolean(lane?.debug?.limited_reason_category)
});
expect(result.handled).toBe(true);
expect(result.selection?.messageUsed).toBe("сырой вопрос");
expect(result.selection?.carryMeta).toBe(carryover);
expect(result.retryAudit.attempted).toBe(true);
expect(result.retryAudit.reason).toBe("limited_result_retry_with_raw_message");
expect(result.retryAudit.initial_limited_category).toBe("empty_match");
expect(result.retryAudit.retry_used_followup_context).toBe(true);
expect(result.retryAudit.retry_result_category).toBe(null);
expect(runAddressLaneAttempt).toHaveBeenCalledTimes(3);
});
it("returns pending limited result when retry is disabled", async () => {
const runAddressLaneAttempt = vi
.fn()
.mockResolvedValueOnce(limitedLane("missing_anchor")) // primary
.mockResolvedValueOnce(unhandledLane()); // contextual fallback
const result = await runAssistantAddressLaneRuntime({
userMessage: "сырой вопрос",
addressInputMessage: "нормализованный вопрос",
carryover: { followupContext: { scope: "ctx" } },
shouldPreferContextualLane: false,
canRetryWithRawUserMessage: false,
runAddressLaneAttempt,
isRetryableAddressLimitedResult: (lane) => Boolean(lane?.debug?.limited_reason_category)
});
expect(result.handled).toBe(true);
expect(result.selection?.messageUsed).toBe("нормализованный вопрос");
expect(result.selection?.addressLane.debug?.limited_reason_category).toBe("missing_anchor");
expect(result.retryAudit.attempted).toBe(false);
});
});