ДОМЕНЫ - ВОПРОСЫ - НДС: Стабилизировать VAT MCP probe: добавить задержки, увеличить таймауты и retry на aborted
This commit is contained in:
parent
c4f87222a8
commit
4205c6b3e6
|
|
@ -16,15 +16,20 @@ const ADDRESS_CONFIRMED_PAYABLES_MIN_LIMIT = 200;
|
|||
const COUNTERPARTY_CATALOG_LOOKUP_LIMIT = 1000;
|
||||
const COUNTERPARTY_CATALOG_CACHE_TTL_MS = 120_000;
|
||||
const VAT_METADATA_PROBE_LIMIT = 100;
|
||||
const VAT_SOURCE_PROBE_MAX_OBJECTS = 6;
|
||||
const VAT_METADATA_PROBE_TYPES = ["РегистрНакопления", "Документ"];
|
||||
const VAT_SOURCE_PROBE_MAX_OBJECTS = 4;
|
||||
const VAT_METADATA_PROBE_TYPES = ["РегистрНакопления"];
|
||||
const VAT_METADATA_PROBE_MASKS = ["ндс", "книгапродаж", "книгапокупок", "счетфактур"];
|
||||
const VAT_METADATA_PROBE_CONCURRENCY = 4;
|
||||
const VAT_METADATA_PROBE_TIMEOUT_MS = 800;
|
||||
const VAT_METADATA_PROBE_RETRY_TIMEOUT_MS = 1_200;
|
||||
const VAT_OBJECT_PROBE_CONCURRENCY = 4;
|
||||
const VAT_OBJECT_PROBE_TIMEOUT_MS = 800;
|
||||
const VAT_OBJECT_PROBE_FALLBACK_TIMEOUT_MS = 800;
|
||||
const VAT_METADATA_PROBE_CONCURRENCY = 2;
|
||||
const VAT_METADATA_PROBE_STAGGER_MS = 90;
|
||||
const VAT_METADATA_PROBE_TIMEOUT_MS = 1_200;
|
||||
const VAT_METADATA_PROBE_RETRY_TIMEOUT_MS = 1_800;
|
||||
const VAT_METADATA_PROBE_RETRY_DELAY_MS = 140;
|
||||
const VAT_OBJECT_PROBE_CONCURRENCY = 1;
|
||||
const VAT_OBJECT_PROBE_STAGGER_MS = 120;
|
||||
const VAT_OBJECT_PROBE_TIMEOUT_MS = 1_500;
|
||||
const VAT_OBJECT_PROBE_ABORT_RETRY_TIMEOUT_MS = 2_200;
|
||||
const VAT_OBJECT_PROBE_ABORT_RETRY_DELAY_MS = 180;
|
||||
const VAT_OBJECT_PROBE_FALLBACK_TIMEOUT_MS = 1_500;
|
||||
const PARTY_ANCHOR_STOPWORDS = new Set([
|
||||
"ооо",
|
||||
"ао",
|
||||
|
|
@ -75,6 +80,28 @@ const ACCOUNT_ALIAS_MAP = {
|
|||
"62": ["покупатель", "покупателями", "расчеты с покупателями"],
|
||||
"76": ["прочие расчеты", "прочими дебиторами и кредиторами"]
|
||||
};
|
||||
const VAT_FALLBACK_METADATA_OBJECTS = [
|
||||
{
|
||||
fullName: "РегистрНакопления.НДСЗаписиКнигиПродаж",
|
||||
synonym: "НДС Продажи",
|
||||
objectType: "register"
|
||||
},
|
||||
{
|
||||
fullName: "РегистрНакопления.НДСЗаписиКнигиПокупок",
|
||||
synonym: "НДС Покупки",
|
||||
objectType: "register"
|
||||
},
|
||||
{
|
||||
fullName: "РегистрНакопления.НДСПредъявленный",
|
||||
synonym: "НДС предъявленный",
|
||||
objectType: "register"
|
||||
},
|
||||
{
|
||||
fullName: "РегистрНакопления.НДСВключенныйВСтоимость",
|
||||
synonym: "НДС, включенный в стоимость",
|
||||
objectType: "register"
|
||||
}
|
||||
];
|
||||
const COUNTERPARTY_CATALOG_LOOKUP_QUERY_TEMPLATE = `
|
||||
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
|
||||
ДАТАВРЕМЯ(2000, 1, 1, 0, 0, 0) КАК Период,
|
||||
|
|
@ -252,6 +279,13 @@ async function mapWithConcurrency(items, concurrency, worker) {
|
|||
await Promise.all(runners);
|
||||
return results;
|
||||
}
|
||||
function sleepMs(ms) {
|
||||
const delayMs = Number.isFinite(ms) ? Math.max(0, Math.trunc(ms)) : 0;
|
||||
if (delayMs <= 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
async function executeVatMetadataProbeRequest(request) {
|
||||
const firstAttempt = await (0, addressMcpClient_1.executeAddressMcpMetadata)({
|
||||
...request,
|
||||
|
|
@ -260,6 +294,7 @@ async function executeVatMetadataProbeRequest(request) {
|
|||
if (!firstAttempt.error || !isAbortErrorMessage(firstAttempt.error)) {
|
||||
return firstAttempt;
|
||||
}
|
||||
await sleepMs(VAT_METADATA_PROBE_RETRY_DELAY_MS);
|
||||
const retryLimit = Math.max(20, Math.min(request.limit, Math.trunc(request.limit / 2)));
|
||||
const retryAttempt = await (0, addressMcpClient_1.executeAddressMcpMetadata)({
|
||||
...request,
|
||||
|
|
@ -301,6 +336,10 @@ function scoreVatMetadataObject(item) {
|
|||
}
|
||||
return score;
|
||||
}
|
||||
function vatMetadataObjectPriority(item) {
|
||||
const idx = VAT_FALLBACK_METADATA_OBJECTS.findIndex((fallback) => fallback.fullName === item.fullName);
|
||||
return idx >= 0 ? idx : VAT_FALLBACK_METADATA_OBJECTS.length + 1;
|
||||
}
|
||||
function buildVatObjectProbeQuery(object, asOfExpr, mode = "latest") {
|
||||
const orderClause = mode === "latest" ? "\nУПОРЯДОЧИТЬ ПО\n Движения.Период УБЫВ" : "";
|
||||
if (object.objectType === "document") {
|
||||
|
|
@ -363,7 +402,12 @@ async function probeVatDirectSources(filters) {
|
|||
name_mask: nameMask,
|
||||
limit: VAT_METADATA_PROBE_LIMIT
|
||||
})));
|
||||
const metadataResponses = await mapWithConcurrency(metadataRequests, VAT_METADATA_PROBE_CONCURRENCY, (request) => executeVatMetadataProbeRequest(request));
|
||||
const metadataResponses = await mapWithConcurrency(metadataRequests, VAT_METADATA_PROBE_CONCURRENCY, async (request, index) => {
|
||||
if (index > 0 && VAT_METADATA_PROBE_STAGGER_MS > 0) {
|
||||
await sleepMs(index * VAT_METADATA_PROBE_STAGGER_MS);
|
||||
}
|
||||
return executeVatMetadataProbeRequest(request);
|
||||
});
|
||||
const metadataOutcomes = metadataResponses.map((response, index) => ({
|
||||
request: metadataRequests[index],
|
||||
response
|
||||
|
|
@ -403,9 +447,27 @@ async function probeVatDirectSources(filters) {
|
|||
}
|
||||
const discoveredMetadataObjects = Array.from(deduplicatedObjects.values())
|
||||
.filter((item) => isVatMetadataObject(item))
|
||||
.sort((a, b) => scoreVatMetadataObject(b) - scoreVatMetadataObject(a) || a.fullName.localeCompare(b.fullName, "ru"));
|
||||
const metadataObjects = discoveredMetadataObjects.slice(0, VAT_SOURCE_PROBE_MAX_OBJECTS);
|
||||
const probeRows = await mapWithConcurrency(metadataObjects, VAT_OBJECT_PROBE_CONCURRENCY, async (object) => {
|
||||
.sort((a, b) => vatMetadataObjectPriority(a) - vatMetadataObjectPriority(b) ||
|
||||
scoreVatMetadataObject(b) - scoreVatMetadataObject(a) ||
|
||||
a.fullName.localeCompare(b.fullName, "ru"));
|
||||
const mergedMetadataObjectsMap = new Map();
|
||||
for (const item of discoveredMetadataObjects) {
|
||||
mergedMetadataObjectsMap.set(item.fullName, item);
|
||||
}
|
||||
for (const fallbackObject of VAT_FALLBACK_METADATA_OBJECTS) {
|
||||
if (!mergedMetadataObjectsMap.has(fallbackObject.fullName)) {
|
||||
mergedMetadataObjectsMap.set(fallbackObject.fullName, fallbackObject);
|
||||
}
|
||||
}
|
||||
const metadataObjects = Array.from(mergedMetadataObjectsMap.values())
|
||||
.sort((a, b) => vatMetadataObjectPriority(a) - vatMetadataObjectPriority(b) ||
|
||||
scoreVatMetadataObject(b) - scoreVatMetadataObject(a) ||
|
||||
a.fullName.localeCompare(b.fullName, "ru"))
|
||||
.slice(0, VAT_SOURCE_PROBE_MAX_OBJECTS);
|
||||
const probeRows = await mapWithConcurrency(metadataObjects, VAT_OBJECT_PROBE_CONCURRENCY, async (object, index) => {
|
||||
if (index > 0 && VAT_OBJECT_PROBE_STAGGER_MS > 0) {
|
||||
await sleepMs(index * VAT_OBJECT_PROBE_STAGGER_MS);
|
||||
}
|
||||
let probeResult = await (0, addressMcpClient_1.executeAddressMcpQuery)({
|
||||
query: buildVatObjectProbeQuery(object, asOfExpr, "latest"),
|
||||
limit: 1,
|
||||
|
|
@ -413,34 +475,43 @@ async function probeVatDirectSources(filters) {
|
|||
});
|
||||
let fallbackUsed = false;
|
||||
if (probeResult.error) {
|
||||
let latestError = probeResult.error;
|
||||
if (isAbortErrorMessage(probeResult.error)) {
|
||||
return {
|
||||
fullName: object.fullName,
|
||||
synonym: object.synonym,
|
||||
objectType: object.objectType,
|
||||
status: "error",
|
||||
rowsFetched: probeResult.fetched_rows,
|
||||
error: probeResult.error
|
||||
};
|
||||
await sleepMs(VAT_OBJECT_PROBE_ABORT_RETRY_DELAY_MS);
|
||||
const retryLatestResult = await (0, addressMcpClient_1.executeAddressMcpQuery)({
|
||||
query: buildVatObjectProbeQuery(object, asOfExpr, "latest"),
|
||||
limit: 1,
|
||||
timeout_ms: VAT_OBJECT_PROBE_ABORT_RETRY_TIMEOUT_MS
|
||||
});
|
||||
if (!retryLatestResult.error) {
|
||||
probeResult = retryLatestResult;
|
||||
latestError = null;
|
||||
}
|
||||
else {
|
||||
probeResult = retryLatestResult;
|
||||
latestError = `${latestError}; retry_latest: ${retryLatestResult.error}`;
|
||||
}
|
||||
}
|
||||
const fallbackResult = await (0, addressMcpClient_1.executeAddressMcpQuery)({
|
||||
query: buildVatObjectProbeQuery(object, asOfExpr, "exists"),
|
||||
limit: 1,
|
||||
timeout_ms: VAT_OBJECT_PROBE_FALLBACK_TIMEOUT_MS
|
||||
});
|
||||
if (!fallbackResult.error) {
|
||||
probeResult = fallbackResult;
|
||||
fallbackUsed = true;
|
||||
}
|
||||
else {
|
||||
return {
|
||||
fullName: object.fullName,
|
||||
synonym: object.synonym,
|
||||
objectType: object.objectType,
|
||||
status: "error",
|
||||
rowsFetched: probeResult.fetched_rows,
|
||||
error: `${probeResult.error}; fallback: ${fallbackResult.error}`
|
||||
};
|
||||
if (probeResult.error) {
|
||||
const fallbackResult = await (0, addressMcpClient_1.executeAddressMcpQuery)({
|
||||
query: buildVatObjectProbeQuery(object, asOfExpr, "exists"),
|
||||
limit: 1,
|
||||
timeout_ms: VAT_OBJECT_PROBE_FALLBACK_TIMEOUT_MS
|
||||
});
|
||||
if (!fallbackResult.error) {
|
||||
probeResult = fallbackResult;
|
||||
fallbackUsed = true;
|
||||
}
|
||||
else {
|
||||
return {
|
||||
fullName: object.fullName,
|
||||
synonym: object.synonym,
|
||||
objectType: object.objectType,
|
||||
status: "error",
|
||||
rowsFetched: probeResult.fetched_rows,
|
||||
error: `${latestError ?? probeResult.error}; fallback: ${fallbackResult.error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
const firstRow = probeResult.raw_rows[0] ?? null;
|
||||
|
|
@ -462,7 +533,13 @@ async function probeVatDirectSources(filters) {
|
|||
sampleRegistrator
|
||||
};
|
||||
});
|
||||
const status = metadataResponses.every((item) => item.error) ? "error" : "ok";
|
||||
const hasProbeAttempts = probeRows.length > 0;
|
||||
const hasNonErrorProbeResult = probeRows.some((item) => item.status !== "error");
|
||||
const status = hasProbeAttempts && hasNonErrorProbeResult
|
||||
? "ok"
|
||||
: metadataResponses.every((item) => item.error) && !hasProbeAttempts
|
||||
? "error"
|
||||
: "ok";
|
||||
const allErrors = [
|
||||
...metadataErrors,
|
||||
...probeRows
|
||||
|
|
|
|||
|
|
@ -2114,7 +2114,10 @@ function composeFactualReply(intent, rows, options = {}) {
|
|||
lines.push("- Сумма расчета выше получена по книгам продаж/покупок; probe использован для контроля полноты VAT-источников.");
|
||||
}
|
||||
else if (vatProbe && vatProbe.status === "error") {
|
||||
lines.push("", "Покрытие VAT-источников через MCP: probe завершился ошибкой, проверьте доступность регистров книг продаж/покупок.");
|
||||
lines.push("", "Покрытие VAT-источников через MCP: дополнительный probe недоступен (например, timeout metadata).", "Итоговая сумма НДС выше рассчитана по основному маршруту книг продаж/покупок; probe влияет только на диагностику покрытия.");
|
||||
if (vatProbe.errors.length > 0) {
|
||||
lines.push(`- Детали probe: ${vatProbe.errors.slice(0, 2).join("; ")}.`);
|
||||
}
|
||||
}
|
||||
if (rows.length === 0) {
|
||||
lines.push("", "За выбранный налоговый период не найдены строки книг продаж/покупок, поэтому подтвержденная сумма к уплате равна 0.");
|
||||
|
|
|
|||
|
|
@ -91,15 +91,20 @@ const ADDRESS_CONFIRMED_PAYABLES_MIN_LIMIT = 200;
|
|||
const COUNTERPARTY_CATALOG_LOOKUP_LIMIT = 1000;
|
||||
const COUNTERPARTY_CATALOG_CACHE_TTL_MS = 120_000;
|
||||
const VAT_METADATA_PROBE_LIMIT = 100;
|
||||
const VAT_SOURCE_PROBE_MAX_OBJECTS = 6;
|
||||
const VAT_METADATA_PROBE_TYPES = ["РегистрНакопления", "Документ"] as const;
|
||||
const VAT_SOURCE_PROBE_MAX_OBJECTS = 4;
|
||||
const VAT_METADATA_PROBE_TYPES = ["РегистрНакопления"] as const;
|
||||
const VAT_METADATA_PROBE_MASKS = ["ндс", "книгапродаж", "книгапокупок", "счетфактур"] as const;
|
||||
const VAT_METADATA_PROBE_CONCURRENCY = 4;
|
||||
const VAT_METADATA_PROBE_TIMEOUT_MS = 800;
|
||||
const VAT_METADATA_PROBE_RETRY_TIMEOUT_MS = 1_200;
|
||||
const VAT_OBJECT_PROBE_CONCURRENCY = 4;
|
||||
const VAT_OBJECT_PROBE_TIMEOUT_MS = 800;
|
||||
const VAT_OBJECT_PROBE_FALLBACK_TIMEOUT_MS = 800;
|
||||
const VAT_METADATA_PROBE_CONCURRENCY = 2;
|
||||
const VAT_METADATA_PROBE_STAGGER_MS = 90;
|
||||
const VAT_METADATA_PROBE_TIMEOUT_MS = 1_200;
|
||||
const VAT_METADATA_PROBE_RETRY_TIMEOUT_MS = 1_800;
|
||||
const VAT_METADATA_PROBE_RETRY_DELAY_MS = 140;
|
||||
const VAT_OBJECT_PROBE_CONCURRENCY = 1;
|
||||
const VAT_OBJECT_PROBE_STAGGER_MS = 120;
|
||||
const VAT_OBJECT_PROBE_TIMEOUT_MS = 1_500;
|
||||
const VAT_OBJECT_PROBE_ABORT_RETRY_TIMEOUT_MS = 2_200;
|
||||
const VAT_OBJECT_PROBE_ABORT_RETRY_DELAY_MS = 180;
|
||||
const VAT_OBJECT_PROBE_FALLBACK_TIMEOUT_MS = 1_500;
|
||||
const PARTY_ANCHOR_STOPWORDS = new Set([
|
||||
"ооо",
|
||||
"ао",
|
||||
|
|
@ -156,6 +161,28 @@ interface VatMetadataObject {
|
|||
synonym: string | null;
|
||||
objectType: "document" | "register";
|
||||
}
|
||||
const VAT_FALLBACK_METADATA_OBJECTS: VatMetadataObject[] = [
|
||||
{
|
||||
fullName: "РегистрНакопления.НДСЗаписиКнигиПродаж",
|
||||
synonym: "НДС Продажи",
|
||||
objectType: "register"
|
||||
},
|
||||
{
|
||||
fullName: "РегистрНакопления.НДСЗаписиКнигиПокупок",
|
||||
synonym: "НДС Покупки",
|
||||
objectType: "register"
|
||||
},
|
||||
{
|
||||
fullName: "РегистрНакопления.НДСПредъявленный",
|
||||
synonym: "НДС предъявленный",
|
||||
objectType: "register"
|
||||
},
|
||||
{
|
||||
fullName: "РегистрНакопления.НДСВключенныйВСтоимость",
|
||||
synonym: "НДС, включенный в стоимость",
|
||||
objectType: "register"
|
||||
}
|
||||
];
|
||||
const COUNTERPARTY_CATALOG_LOOKUP_QUERY_TEMPLATE = `
|
||||
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
|
||||
ДАТАВРЕМЯ(2000, 1, 1, 0, 0, 0) КАК Период,
|
||||
|
|
@ -363,6 +390,14 @@ async function mapWithConcurrency<T, R>(
|
|||
return results;
|
||||
}
|
||||
|
||||
function sleepMs(ms: number): Promise<void> {
|
||||
const delayMs = Number.isFinite(ms) ? Math.max(0, Math.trunc(ms)) : 0;
|
||||
if (delayMs <= 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
|
||||
async function executeVatMetadataProbeRequest(request: {
|
||||
meta_type: string;
|
||||
name_mask: string;
|
||||
|
|
@ -376,6 +411,7 @@ async function executeVatMetadataProbeRequest(request: {
|
|||
return firstAttempt;
|
||||
}
|
||||
|
||||
await sleepMs(VAT_METADATA_PROBE_RETRY_DELAY_MS);
|
||||
const retryLimit = Math.max(20, Math.min(request.limit, Math.trunc(request.limit / 2)));
|
||||
const retryAttempt = await executeAddressMcpMetadata({
|
||||
...request,
|
||||
|
|
@ -420,6 +456,11 @@ function scoreVatMetadataObject(item: VatMetadataObject): number {
|
|||
return score;
|
||||
}
|
||||
|
||||
function vatMetadataObjectPriority(item: VatMetadataObject): number {
|
||||
const idx = VAT_FALLBACK_METADATA_OBJECTS.findIndex((fallback) => fallback.fullName === item.fullName);
|
||||
return idx >= 0 ? idx : VAT_FALLBACK_METADATA_OBJECTS.length + 1;
|
||||
}
|
||||
|
||||
type VatObjectProbeMode = "latest" | "exists";
|
||||
|
||||
function buildVatObjectProbeQuery(object: VatMetadataObject, asOfExpr: string, mode: VatObjectProbeMode = "latest"): string {
|
||||
|
|
@ -494,7 +535,12 @@ async function probeVatDirectSources(filters: AddressFilterSet): Promise<VatDire
|
|||
const metadataResponses = await mapWithConcurrency(
|
||||
metadataRequests,
|
||||
VAT_METADATA_PROBE_CONCURRENCY,
|
||||
(request) => executeVatMetadataProbeRequest(request)
|
||||
async (request, index) => {
|
||||
if (index > 0 && VAT_METADATA_PROBE_STAGGER_MS > 0) {
|
||||
await sleepMs(index * VAT_METADATA_PROBE_STAGGER_MS);
|
||||
}
|
||||
return executeVatMetadataProbeRequest(request);
|
||||
}
|
||||
);
|
||||
|
||||
const metadataOutcomes = metadataResponses.map((response, index) => ({
|
||||
|
|
@ -539,14 +585,36 @@ async function probeVatDirectSources(filters: AddressFilterSet): Promise<VatDire
|
|||
const discoveredMetadataObjects = Array.from(deduplicatedObjects.values())
|
||||
.filter((item) => isVatMetadataObject(item))
|
||||
.sort(
|
||||
(a, b) => scoreVatMetadataObject(b) - scoreVatMetadataObject(a) || a.fullName.localeCompare(b.fullName, "ru")
|
||||
(a, b) =>
|
||||
vatMetadataObjectPriority(a) - vatMetadataObjectPriority(b) ||
|
||||
scoreVatMetadataObject(b) - scoreVatMetadataObject(a) ||
|
||||
a.fullName.localeCompare(b.fullName, "ru")
|
||||
);
|
||||
const metadataObjects = discoveredMetadataObjects.slice(0, VAT_SOURCE_PROBE_MAX_OBJECTS);
|
||||
const mergedMetadataObjectsMap = new Map<string, VatMetadataObject>();
|
||||
for (const item of discoveredMetadataObjects) {
|
||||
mergedMetadataObjectsMap.set(item.fullName, item);
|
||||
}
|
||||
for (const fallbackObject of VAT_FALLBACK_METADATA_OBJECTS) {
|
||||
if (!mergedMetadataObjectsMap.has(fallbackObject.fullName)) {
|
||||
mergedMetadataObjectsMap.set(fallbackObject.fullName, fallbackObject);
|
||||
}
|
||||
}
|
||||
const metadataObjects = Array.from(mergedMetadataObjectsMap.values())
|
||||
.sort(
|
||||
(a, b) =>
|
||||
vatMetadataObjectPriority(a) - vatMetadataObjectPriority(b) ||
|
||||
scoreVatMetadataObject(b) - scoreVatMetadataObject(a) ||
|
||||
a.fullName.localeCompare(b.fullName, "ru")
|
||||
)
|
||||
.slice(0, VAT_SOURCE_PROBE_MAX_OBJECTS);
|
||||
|
||||
const probeRows = await mapWithConcurrency(
|
||||
metadataObjects,
|
||||
VAT_OBJECT_PROBE_CONCURRENCY,
|
||||
async (object): Promise<VatDirectSourceProbeItem> => {
|
||||
async (object, index): Promise<VatDirectSourceProbeItem> => {
|
||||
if (index > 0 && VAT_OBJECT_PROBE_STAGGER_MS > 0) {
|
||||
await sleepMs(index * VAT_OBJECT_PROBE_STAGGER_MS);
|
||||
}
|
||||
let probeResult = await executeAddressMcpQuery({
|
||||
query: buildVatObjectProbeQuery(object, asOfExpr, "latest"),
|
||||
limit: 1,
|
||||
|
|
@ -554,33 +622,41 @@ async function probeVatDirectSources(filters: AddressFilterSet): Promise<VatDire
|
|||
});
|
||||
let fallbackUsed = false;
|
||||
if (probeResult.error) {
|
||||
let latestError: string | null = probeResult.error;
|
||||
if (isAbortErrorMessage(probeResult.error)) {
|
||||
return {
|
||||
fullName: object.fullName,
|
||||
synonym: object.synonym,
|
||||
objectType: object.objectType,
|
||||
status: "error",
|
||||
rowsFetched: probeResult.fetched_rows,
|
||||
error: probeResult.error
|
||||
};
|
||||
await sleepMs(VAT_OBJECT_PROBE_ABORT_RETRY_DELAY_MS);
|
||||
const retryLatestResult = await executeAddressMcpQuery({
|
||||
query: buildVatObjectProbeQuery(object, asOfExpr, "latest"),
|
||||
limit: 1,
|
||||
timeout_ms: VAT_OBJECT_PROBE_ABORT_RETRY_TIMEOUT_MS
|
||||
});
|
||||
if (!retryLatestResult.error) {
|
||||
probeResult = retryLatestResult;
|
||||
latestError = null;
|
||||
} else {
|
||||
probeResult = retryLatestResult;
|
||||
latestError = `${latestError}; retry_latest: ${retryLatestResult.error}`;
|
||||
}
|
||||
}
|
||||
const fallbackResult = await executeAddressMcpQuery({
|
||||
query: buildVatObjectProbeQuery(object, asOfExpr, "exists"),
|
||||
limit: 1,
|
||||
timeout_ms: VAT_OBJECT_PROBE_FALLBACK_TIMEOUT_MS
|
||||
});
|
||||
if (!fallbackResult.error) {
|
||||
probeResult = fallbackResult;
|
||||
fallbackUsed = true;
|
||||
} else {
|
||||
return {
|
||||
fullName: object.fullName,
|
||||
synonym: object.synonym,
|
||||
objectType: object.objectType,
|
||||
status: "error",
|
||||
rowsFetched: probeResult.fetched_rows,
|
||||
error: `${probeResult.error}; fallback: ${fallbackResult.error}`
|
||||
};
|
||||
if (probeResult.error) {
|
||||
const fallbackResult = await executeAddressMcpQuery({
|
||||
query: buildVatObjectProbeQuery(object, asOfExpr, "exists"),
|
||||
limit: 1,
|
||||
timeout_ms: VAT_OBJECT_PROBE_FALLBACK_TIMEOUT_MS
|
||||
});
|
||||
if (!fallbackResult.error) {
|
||||
probeResult = fallbackResult;
|
||||
fallbackUsed = true;
|
||||
} else {
|
||||
return {
|
||||
fullName: object.fullName,
|
||||
synonym: object.synonym,
|
||||
objectType: object.objectType,
|
||||
status: "error",
|
||||
rowsFetched: probeResult.fetched_rows,
|
||||
error: `${latestError ?? probeResult.error}; fallback: ${fallbackResult.error}`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
const firstRow = probeResult.raw_rows[0] ?? null;
|
||||
|
|
@ -610,7 +686,14 @@ async function probeVatDirectSources(filters: AddressFilterSet): Promise<VatDire
|
|||
}
|
||||
);
|
||||
|
||||
const status: VatDirectSourceProbeSummary["status"] = metadataResponses.every((item) => item.error) ? "error" : "ok";
|
||||
const hasProbeAttempts = probeRows.length > 0;
|
||||
const hasNonErrorProbeResult = probeRows.some((item) => item.status !== "error");
|
||||
const status: VatDirectSourceProbeSummary["status"] =
|
||||
hasProbeAttempts && hasNonErrorProbeResult
|
||||
? "ok"
|
||||
: metadataResponses.every((item) => item.error) && !hasProbeAttempts
|
||||
? "error"
|
||||
: "ok";
|
||||
const allErrors = [
|
||||
...metadataErrors,
|
||||
...probeRows
|
||||
|
|
|
|||
|
|
@ -2718,7 +2718,14 @@ export function composeFactualReply(
|
|||
}
|
||||
lines.push("- Сумма расчета выше получена по книгам продаж/покупок; probe использован для контроля полноты VAT-источников.");
|
||||
} else if (vatProbe && vatProbe.status === "error") {
|
||||
lines.push("", "Покрытие VAT-источников через MCP: probe завершился ошибкой, проверьте доступность регистров книг продаж/покупок.");
|
||||
lines.push(
|
||||
"",
|
||||
"Покрытие VAT-источников через MCP: дополнительный probe недоступен (например, timeout metadata).",
|
||||
"Итоговая сумма НДС выше рассчитана по основному маршруту книг продаж/покупок; probe влияет только на диагностику покрытия."
|
||||
);
|
||||
if (vatProbe.errors.length > 0) {
|
||||
lines.push(`- Детали probe: ${vatProbe.errors.slice(0, 2).join("; ")}.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (rows.length === 0) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue