import { describe, expect, it } from "vitest"; import { AddressQueryService } from "../src/services/addressQueryService"; import { resolveAssistantOrchestrationDecision, resolveLivingAssistantModeDecision } from "../src/services/assistantService"; describe("wave17 run regressions (2026-04-11 real runs)", () => { it("keeps real run 17:51 data-heavy prompts in address lane", () => { const realRunPrompts = [ "Где у нас накопились авансы к отгрузкам, которые уже давно пора закрыть или хотя бы перепроверить, чтобы не подозревать худшее?", "Какие контрагенты у нас на этом моменте могут быть причислены к тем, кто вообще не платит уже несколько месяцев?", "В каких случаях мы видим зависшие отгрузки, которые уже давно пора закрыть - это грозит проблемами в отчетности." ]; for (const prompt of realRunPrompts) { const decision = resolveAssistantOrchestrationDecision({ rawUserMessage: prompt, effectiveAddressUserMessage: prompt, followupContext: null, llmPreDecomposeMeta: null, useMock: false }); expect(decision.runAddressLane).toBe(true); expect(["address_mode_classifier_detected", "address_signal_detected", "address_intent_resolver_detected"]).toContain( decision.toolGateReason ); expect(decision.livingMode).toBe("address_data"); expect(decision.livingReason).toBe("address_lane_triggered"); } }); it("keeps short follow-up style prompts out of chat drift when predecompose says unsupported", () => { const shortFollowups = ["без воды?", "и коротко?", "прям сейчас?"]; for (const prompt of shortFollowups) { const decision = resolveLivingAssistantModeDecision({ userMessage: prompt, addressLaneTriggered: false, useMock: false, predecomposeMode: "unsupported", predecomposeModeConfidence: "low" }); expect(decision.mode).toBe("deep_analysis"); expect(decision.reason).toBe("predecompose_unsupported_mode_fallback_to_deep"); } }); it("routes data-scope slang wording to chat mode", () => { const decision = resolveLivingAssistantModeDecision({ userMessage: "по каким конторам можем общаться?", addressLaneTriggered: false, useMock: false, predecomposeMode: "unsupported", predecomposeModeConfidence: "low" }); expect(decision.mode).toBe("chat"); expect(decision.reason).toBe("assistant_data_scope_query_detected"); }); it("keeps open-contracts request in address lane even with stale deep followup context", () => { const decision = resolveAssistantOrchestrationDecision({ rawUserMessage: "Покажи незакрытые договоры на 2020-12-31", effectiveAddressUserMessage: "Покажи незакрытые договоры на 2020-12-31", followupContext: { previous_question_id: "msg-prev", last_user_message: "почему так по закрытию месяца", active_domain: "month_close_costs_20_44", active_requirement_ids: ["R1"], uncovered_requirement_ids: ["R1"], referenced_requirement_ids: ["R1"] } as any, llmPreDecomposeMeta: { applied: true, llmCanonicalCandidateDetected: true, reason: "normalized_fragment_applied", predecomposeContract: { mode: "address_query", mode_confidence: "high", intent: "list_open_contracts", intent_confidence: "medium" } } as any, useMock: false }); expect(decision.runAddressLane).toBe(true); expect(decision.livingMode).toBe("address_data"); expect(decision.livingReason).toBe("address_lane_triggered"); expect(decision.orchestrationContract?.deep_analysis_signal_fallback_to_deep).toBe(false); }); it("uses soft unsupported aggregate replies instead of rigid old template", async () => { const service = new AddressQueryService(); const prompts = ["какой самый доходный год?", "какие обороты по альтернативе за 2020 год"]; for (const prompt of prompts) { const result = await service.tryHandle(prompt); const reply = String(result?.reply_text ?? ""); expect(result?.handled).toBe(true); expect(result?.reply_type).toBe("partial_coverage"); expect(result?.debug.limited_reason_category).toBe("unsupported"); expect(reply).toContain("Что могу сделать сейчас:"); expect(reply).not.toMatch(/Сейчас этот тип вопроса вне поддерживаемого контура адресного режима/iu); } }); });