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

133 lines
5.2 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: "raw question",
addressInputMessage: "normalized question",
carryover,
shouldPreferContextualLane: true,
canRetryWithRawUserMessage: true,
runAddressLaneAttempt,
isRetryableAddressLimitedResult: (lane) => Boolean(lane?.debug?.limited_reason_category)
});
expect(result.handled).toBe(true);
expect(result.selection?.messageUsed).toBe("normalized question");
expect(result.selection?.carryMeta).toBe(carryover);
expect(result.retryAudit.attempted).toBe(false);
expect(runAddressLaneAttempt).toHaveBeenCalledTimes(1);
expect(runAddressLaneAttempt).toHaveBeenCalledWith("normalized question", carryover, null);
});
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"))
.mockResolvedValueOnce(limitedLane("empty_match"))
.mockResolvedValueOnce(factualLane());
const result = await runAssistantAddressLaneRuntime({
userMessage: "raw question",
addressInputMessage: "normalized question",
carryover,
shouldPreferContextualLane: false,
canRetryWithRawUserMessage: true,
runAddressLaneAttempt,
isRetryableAddressLimitedResult: (lane) => Boolean(lane?.debug?.limited_reason_category)
});
expect(result.handled).toBe(true);
expect(result.selection?.messageUsed).toBe("raw question");
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("can retry with raw message after unsupported limited result", async () => {
const carryover: AssistantAddressFollowupCarryoverLike = { followupContext: { scope: "ctx" } };
const runAddressLaneAttempt = vi
.fn()
.mockResolvedValueOnce(limitedLane("unsupported"))
.mockResolvedValueOnce(limitedLane("unsupported"))
.mockResolvedValueOnce(factualLane());
const result = await runAssistantAddressLaneRuntime({
userMessage: "что нам отгружал чепурнов? какой товар или услугу?",
addressInputMessage: "Определить, что необходимо отгрузить контрагенту Чепурнов",
carryover,
shouldPreferContextualLane: false,
canRetryWithRawUserMessage: true,
runAddressLaneAttempt,
isRetryableAddressLimitedResult: (lane) =>
["missing_anchor", "empty_match", "unsupported"].includes(
String(lane?.debug?.limited_reason_category ?? "").trim()
)
});
expect(result.handled).toBe(true);
expect(result.selection?.messageUsed).toBe("что нам отгружал чепурнов? какой товар или услугу?");
expect(result.retryAudit.attempted).toBe(true);
expect(result.retryAudit.initial_limited_category).toBe("unsupported");
expect(runAddressLaneAttempt).toHaveBeenCalledTimes(3);
});
it("returns pending limited result when retry is disabled", async () => {
const runAddressLaneAttempt = vi
.fn()
.mockResolvedValueOnce(limitedLane("missing_anchor"))
.mockResolvedValueOnce(unhandledLane());
const result = await runAssistantAddressLaneRuntime({
userMessage: "raw question",
addressInputMessage: "normalized question",
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("normalized question");
expect(result.selection?.addressLane.debug?.limited_reason_category).toBe("missing_anchor");
expect(result.retryAudit.attempted).toBe(false);
});
});