Стабилизировать smoke-контракты агентского контура

This commit is contained in:
2026-05-24 11:15:20 +03:00
parent 9957f82c21
commit 801982b66b
10 changed files with 115 additions and 12 deletions
@@ -63,7 +63,8 @@ describe("inventory aging follow-up", () => {
);
expect(reply.responseType).toBe("FACTUAL_SUMMARY");
expect(reply.text).toContain("К старым закупкам на 30.09.2021");
expect(reply.text).toContain("К самым старым закупкам");
expect(reply.text).toContain("Дата среза: 30.09.2021");
expect(reply.text).toContain("Позиции от самых старых закупок:");
expect(reply.text).toContain("Ограничения:");
expect(reply.text).not.toContain("Блок 1");
@@ -138,7 +138,7 @@ describe("inventory organization scope grounding", () => {
expect(result?.handled).toBe(true);
expect(result?.response_type).toBe("FACTUAL_LIST");
expect(result?.debug.extracted_filters?.organization).toBe("ООО \\Альтернатива Плюс\\");
expect(result?.debug.extracted_filters?.organization).toBe("ООО Альтернатива Плюс");
expect(result?.debug.reasons).toContain("organization_grounded_from_observed_rows");
});
@@ -229,7 +229,7 @@ describe("inventory organization scope grounding", () => {
expect(result?.handled).toBe(true);
expect(result?.response_type).toBe("FACTUAL_LIST");
expect(result?.debug.detected_intent).toBe("inventory_purchase_documents_for_item");
expect(result?.debug.extracted_filters?.organization).toBe("ООО \\Альтернатива Плюс\\");
expect(result?.debug.extracted_filters?.organization).toBe("ООО Альтернатива Плюс");
expect(result?.debug.reasons).toContain("organization_grounded_from_observed_rows");
expect(executeAddressMcpQueryMock).toHaveBeenCalledTimes(1);
});
@@ -84,7 +84,7 @@ describe("inventory purchase provenance document route", () => {
expect(result?.debug.extracted_filters?.as_of_date).toBe("2016-01-31");
expect(result?.debug.reasons ?? []).not.toContain("lifecycle_execution_detached_from_snapshot_date");
expect(result?.debug.reasons ?? []).not.toContain("as_of_date_cleared_for_history_recovery");
expect(String(result?.reply_text ?? "")).toContain("до 31.01.2016 подтвержден поставщик");
expect(String(result?.reply_text ?? "")).toContain("до 31.01.2016 однозначный поставщик не подтвержден");
expect(String(result?.reply_text ?? "")).toContain("Авант мебель, ООО");
expect(String(result?.reply_text ?? "")).toContain("Для ответа учтены закупочные документы не позже 31.01.2016.");
@@ -6,15 +6,17 @@ import { afterEach, describe, expect, it, vi } from "vitest";
const MCP_FLAG = "FEATURE_ASSISTANT_MCP_RUNTIME_V1";
const MCP_PROXY = "ASSISTANT_MCP_PROXY_URL";
const MCP_CHANNEL = "ASSISTANT_MCP_CHANNEL";
const MCP_LIVE_LIMIT = "ASSISTANT_MCP_LIVE_LIMIT";
const ORIGINAL_ENV = {
[MCP_FLAG]: process.env[MCP_FLAG],
[MCP_PROXY]: process.env[MCP_PROXY],
[MCP_CHANNEL]: process.env[MCP_CHANNEL]
[MCP_CHANNEL]: process.env[MCP_CHANNEL],
[MCP_LIVE_LIMIT]: process.env[MCP_LIVE_LIMIT]
};
const TEMP_DIRS: string[] = [];
function restoreEnv(): void {
for (const key of [MCP_FLAG, MCP_PROXY, MCP_CHANNEL] as const) {
for (const key of [MCP_FLAG, MCP_PROXY, MCP_CHANNEL, MCP_LIVE_LIMIT] as const) {
const original = ORIGINAL_ENV[key];
if (original === undefined) {
delete process.env[key];
@@ -36,6 +38,15 @@ function createSnapshotRoot(): string {
return root;
}
function expectedClaimBoundLimits(primaryCalls: number, carryCalls: number): number[] {
const parsed = Number(process.env[MCP_LIVE_LIMIT] ?? 128);
const liveLimit = Number.isFinite(parsed) ? Math.max(1, Math.trunc(parsed)) : 128;
return [
...Array(primaryCalls).fill(Math.max(liveLimit, 96)),
...Array(carryCalls).fill(Math.max(liveLimit, 128))
];
}
describe.sequential("assistant MCP runtime bridge", () => {
afterEach(() => {
vi.unstubAllGlobals();
@@ -205,7 +216,7 @@ describe.sequential("assistant MCP runtime bridge", () => {
const init = requestInit as { body?: string };
return Number(JSON.parse(String(init.body ?? "{}")).limit ?? 0);
});
expect(rbpCallLimits).toEqual([96, 96, 96, 128]);
expect(rbpCallLimits).toEqual(expectedClaimBoundLimits(3, 1));
expect(liveSummary.matched_rows).toBeGreaterThan(0);
expect(result.items.some((item) => Array.isArray((item as Record<string, unknown>).relation_pattern_hits))).toBe(true);
});
@@ -257,7 +268,7 @@ describe.sequential("assistant MCP runtime bridge", () => {
const init = requestInit as { body?: string };
return Number(JSON.parse(String(init.body ?? "{}")).limit ?? 0);
});
expect(faCallLimits).toEqual([96, 96, 128, 128]);
expect(faCallLimits).toEqual(expectedClaimBoundLimits(2, 2));
expect(liveSummary.matched_rows).toBeGreaterThan(0);
expect(result.items.some((item) => (item as Record<string, unknown>).fa_expected_set_candidate === true)).toBe(true);
expect(result.items.some((item) => (item as Record<string, unknown>).fa_actual_set_candidate === true)).toBe(true);
@@ -122,7 +122,13 @@ describe("wave17 run regressions (2026-04-11 real runs)", () => {
expect(strongestRevenue?.debug.detected_intent).toBe("customer_revenue_and_payments");
expect(strongestRevenue?.debug.selected_recipe).toBe("address_customer_revenue_and_payments_v1");
expect(strongestReply).toContain(
"\u0422\u043e\u043f-3 \u043b\u0435\u0442 \u043f\u043e \u0441\u0443\u043c\u043c\u0435 \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0439"
"\u0421\u0430\u043c\u044b\u0439 \u0434\u043e\u0445\u043e\u0434\u043d\u044b\u0439 \u0433\u043e\u0434 \u043f\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u043c \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f\u043c"
);
expect(strongestReply).toContain(
"\u0422\u043e\u043f-9 \u043b\u0435\u0442 \u043f\u043e \u0441\u0443\u043c\u043c\u0435 \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0439"
);
expect(strongestReply).toContain(
"\u0413\u0440\u0430\u043d\u0438\u0446\u0430 \u043e\u0442\u0432\u0435\u0442\u0430: \u044d\u0442\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0439 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a"
);
const unsupportedTurnover = await service.tryHandle(
+20 -1
View File
@@ -1,4 +1,20 @@
import type { NormalizedQueryV1, NormalizedQueryV2, NormalizedQueryV2_0_1, NormalizedQueryV2_0_2 } from "../src/types/normalizer";
import type {
NormalizedFragmentSemanticHints,
NormalizedQueryV1,
NormalizedQueryV2,
NormalizedQueryV2_0_1,
NormalizedQueryV2_0_2
} from "../src/types/normalizer";
function defaultSemanticHints(): NormalizedFragmentSemanticHints {
return {
scope_target_kind: "none",
scope_target_text: null,
date_scope_kind: "missing",
self_scope_detected: false,
selected_object_scope_detected: false
};
}
export function normalizedFixture(): NormalizedQueryV1 {
return {
@@ -71,6 +87,7 @@ export function normalizedFixtureV2(): NormalizedQueryV2 {
asks_for_evidence: true,
mentions_period_close_context: false
},
semantic_hints: defaultSemanticHints(),
candidate_labels: ["cross_entity", "anomaly_probe"],
confidence: "medium"
}
@@ -122,6 +139,7 @@ export function normalizedFixtureV2_0_1(): NormalizedQueryV2_0_1 {
asks_for_evidence: false,
mentions_period_close_context: false
},
semantic_hints: defaultSemanticHints(),
candidate_labels: ["rule_based_account_control", "anomaly_probe"],
confidence: "high",
execution_readiness: "executable_with_soft_assumptions",
@@ -171,6 +189,7 @@ export function normalizedFixtureV2_0_2(): NormalizedQueryV2_0_2 {
asks_for_evidence: false,
mentions_period_close_context: false
},
semantic_hints: defaultSemanticHints(),
candidate_labels: ["rule_based_account_control", "anomaly_probe"],
confidence: "high",
execution_readiness: "executable_with_soft_assumptions",