ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.2 - Усилена оркестрацию в deep/address гейте и follow-up binding в assistantService.ts. Починен кейс UTF-8 follow-up refinement (теперь followup_state_usage.applied=true в нужном сценарии). Убраны регрессии по assistantLivingRouter и stage3 lifecycle probe. Корректное поведение для llm canonical candidate (чтобы не уезжало в clarification_required там, где должен быть address factual).
This commit is contained in:
@@ -261,4 +261,40 @@ describe("assistant orchestration contract", () => {
|
||||
decision.toolGateReason
|
||||
);
|
||||
});
|
||||
|
||||
it("routes risk/anomaly analytics wording to deep pipeline", () => {
|
||||
const decision = resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage: "Проверь НДС по счету 19 за 2020-06 и рискованные записи по документам.",
|
||||
effectiveAddressUserMessage: "Проверь НДС по счету 19 за 2020-06 и рискованные записи по документам.",
|
||||
followupContext: null,
|
||||
llmPreDecomposeMeta: null,
|
||||
useMock: false
|
||||
});
|
||||
|
||||
expect(decision.runAddressLane).toBe(false);
|
||||
expect(decision.toolGateDecision).toBe("skip_address_lane");
|
||||
expect(decision.toolGateReason).toBe("deep_analysis_signal_fallback_to_deep");
|
||||
expect(decision.livingMode).toBe("deep_analysis");
|
||||
expect(decision.livingReason).toBe("deep_analysis_signal_fallback_to_deep");
|
||||
expect(decision.orchestrationContract?.deep_analysis_signal_fallback_to_deep).toBe(true);
|
||||
});
|
||||
|
||||
it("routes settlement closure verification wording to deep pipeline", () => {
|
||||
const decision = resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage:
|
||||
"По оплате поставщику на счете 60 в июле 2020 остался хвост. Проверь закрытие по договору и объекту расчетов.",
|
||||
effectiveAddressUserMessage:
|
||||
"По оплате поставщику на счете 60 в июле 2020 остался хвост. Проверь закрытие по договору и объекту расчетов.",
|
||||
followupContext: null,
|
||||
llmPreDecomposeMeta: null,
|
||||
useMock: false
|
||||
});
|
||||
|
||||
expect(decision.runAddressLane).toBe(false);
|
||||
expect(decision.toolGateDecision).toBe("skip_address_lane");
|
||||
expect(decision.toolGateReason).toBe("deep_analysis_signal_fallback_to_deep");
|
||||
expect(decision.livingMode).toBe("deep_analysis");
|
||||
expect(decision.livingReason).toBe("deep_analysis_signal_fallback_to_deep");
|
||||
expect(decision.orchestrationContract?.deep_analysis_signal_fallback_to_deep).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -129,4 +129,89 @@ describe("assistant turn runtime deps adapter", () => {
|
||||
expect(deps.compactWhitespace(" value ")).toBe("value");
|
||||
expect(helperCompactWhitespace).toHaveBeenCalledWith(" value ");
|
||||
});
|
||||
|
||||
it("preserves method context for stateful service instances", async () => {
|
||||
class SessionStoreLike {
|
||||
public calls: string[] = [];
|
||||
public ensureSession(sessionId: string) {
|
||||
this.calls.push(`ensure:${sessionId}`);
|
||||
return { session_id: sessionId } as any;
|
||||
}
|
||||
public appendItem(sessionId: string) {
|
||||
this.calls.push(`append:${sessionId}`);
|
||||
}
|
||||
public getSession(sessionId: string) {
|
||||
this.calls.push(`get:${sessionId}`);
|
||||
return null;
|
||||
}
|
||||
public setInvestigationState(sessionId: string) {
|
||||
this.calls.push(`state:${sessionId}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class SessionLoggerLike {
|
||||
public persisted = 0;
|
||||
public persistSession() {
|
||||
this.persisted += 1;
|
||||
}
|
||||
}
|
||||
|
||||
class NormalizerLike {
|
||||
public normalized = 0;
|
||||
public async normalize() {
|
||||
this.normalized += 1;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const sessions = new SessionStoreLike();
|
||||
const sessionLogger = new SessionLoggerLike();
|
||||
const normalizerService = new NormalizerLike();
|
||||
|
||||
const deps = buildAssistantTurnRuntimeDeps({
|
||||
sessions,
|
||||
sessionLogger,
|
||||
normalizerService,
|
||||
dataLayer: {
|
||||
executeRouteRuntime: async () => ({})
|
||||
},
|
||||
addressQueryService: {
|
||||
tryHandle: async () => null
|
||||
},
|
||||
chatClient: {},
|
||||
messageIdFactory: () => "msg-ctx",
|
||||
nowIso: () => "2026-04-11T00:00:00.000Z",
|
||||
defaultApiKey: "",
|
||||
logEvent: () => {},
|
||||
flags: {
|
||||
featureAssistantAddressQueryV1: false,
|
||||
featureAddressLlmPredecomposeV1: false,
|
||||
featureInvestigationStateV1: false,
|
||||
featureStateFollowupBindingV1: false,
|
||||
featureContractsV11: false,
|
||||
featureAnswerPolicyV11: false,
|
||||
featureProblemCentricAnswerV1: false,
|
||||
featureLifecycleAnswerV1: false
|
||||
},
|
||||
defaults: {
|
||||
defaultModel: "model",
|
||||
defaultBaseUrl: "base"
|
||||
},
|
||||
helpers: {
|
||||
compactWhitespace: (value: unknown) => String(value ?? "")
|
||||
} as any
|
||||
});
|
||||
|
||||
deps.ensureSession("asst-ctx");
|
||||
deps.appendItem("asst-ctx", { role: "user" } as any);
|
||||
deps.getSession("asst-ctx");
|
||||
deps.setInvestigationState("asst-ctx", null);
|
||||
deps.persistSession({ session_id: "asst-ctx" } as any);
|
||||
await deps.normalize({ user_message: "ctx" } as any);
|
||||
|
||||
expect(sessions.calls).toEqual(["ensure:asst-ctx", "append:asst-ctx", "get:asst-ctx", "state:asst-ctx"]);
|
||||
expect(sessionLogger.persisted).toBe(1);
|
||||
expect(normalizerService.normalized).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user