ДОМЕНЫ - ВОПРОСЫ - Усилить НДС forecast: сумма в начале ответа, расширенный MCP probe источников и форматирование чисел

This commit is contained in:
2026-04-12 23:39:05 +03:00
parent 98872c2f11
commit f1ef5f9d3c
11 changed files with 1483 additions and 101 deletions
@@ -1,4 +1,4 @@
import {
import {
ASSISTANT_MCP_CHANNEL,
ASSISTANT_MCP_PROXY_URL,
ASSISTANT_MCP_TIMEOUT_MS
@@ -17,6 +17,13 @@ export interface AddressMcpQueryResult {
error: string | null;
}
export interface AddressMcpMetadataRowsResult {
fetched_rows: number;
raw_rows: Array<Record<string, unknown>>;
rows: Array<Record<string, unknown>>;
error: string | null;
}
function toStringValue(value: unknown): string {
if (value === null || value === undefined) {
return "";
@@ -188,7 +195,12 @@ function parseRowsFromTextTable(source: string): Array<Record<string, unknown>>
return normalizeMojibakeRows(rows);
}
function parseExecutePayload(payload: unknown): AddressMcpQueryResult {
function parseRowsPayload(
payload: unknown,
options: {
allowSingleObjectRow?: boolean;
} = {}
): AddressMcpQueryResult {
if (!payload || typeof payload !== "object") {
return {
ok: false,
@@ -240,6 +252,14 @@ function parseExecutePayload(payload: unknown): AddressMcpQueryResult {
};
}
if (source.data && typeof source.data === "object" && options.allowSingleObjectRow) {
return {
ok: true,
rows: [normalizeMojibakeValue(source.data) as Record<string, unknown>],
error: null
};
}
return {
ok: true,
rows: [],
@@ -312,7 +332,7 @@ export async function executeAddressMcpQuery(input: {
}
const payload = responseText.trim() ? (JSON.parse(responseText) as unknown) : {};
const parsed = parseExecutePayload(payload);
const parsed = parseRowsPayload(payload);
if (!parsed.ok) {
return {
fetched_rows: 0,
@@ -344,3 +364,100 @@ export async function executeAddressMcpQuery(input: {
clearTimeout(timeout);
}
}
export async function executeAddressMcpMetadata(input: {
filter?: string;
meta_type?: string | string[];
name_mask?: string;
limit?: number;
offset?: number;
sections?: string[];
extension_name?: string | null;
}): Promise<AddressMcpMetadataRowsResult> {
const endpoint = buildMcpUrl("/api/get_metadata");
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), Math.max(300, ASSISTANT_MCP_TIMEOUT_MS));
try {
const body: Record<string, unknown> = {};
if (typeof input.filter === "string" && input.filter.trim().length > 0) {
body.filter = input.filter.trim();
}
if (typeof input.meta_type === "string" && input.meta_type.trim().length > 0) {
body.meta_type = input.meta_type.trim();
} else if (Array.isArray(input.meta_type) && input.meta_type.length > 0) {
const values = input.meta_type
.map((item) => String(item ?? "").trim())
.filter((item) => item.length > 0);
if (values.length > 0) {
body.meta_type = values;
}
}
if (typeof input.name_mask === "string" && input.name_mask.trim().length > 0) {
body.name_mask = input.name_mask.trim();
}
if (typeof input.limit === "number" && Number.isFinite(input.limit)) {
body.limit = Math.max(1, Math.min(1000, Math.trunc(input.limit)));
}
if (typeof input.offset === "number" && Number.isFinite(input.offset)) {
body.offset = Math.max(0, Math.min(1_000_000, Math.trunc(input.offset)));
}
if (Array.isArray(input.sections) && input.sections.length > 0) {
const sections = input.sections
.map((item) => String(item ?? "").trim())
.filter((item) => item.length > 0);
if (sections.length > 0) {
body.sections = sections;
}
}
if (input.extension_name !== undefined) {
body.extension_name = input.extension_name;
}
const response = await fetch(endpoint, {
method: "POST",
headers: {
"content-type": "application/json; charset=utf-8"
},
body: JSON.stringify(body),
signal: controller.signal
});
const responseText = await response.text();
if (!response.ok) {
return {
fetched_rows: 0,
raw_rows: [],
rows: [],
error: `MCP HTTP ${response.status}: ${responseText.slice(0, 240)}`
};
}
const payload = responseText.trim() ? (JSON.parse(responseText) as unknown) : {};
const parsed = parseRowsPayload(payload, { allowSingleObjectRow: true });
if (!parsed.ok) {
return {
fetched_rows: 0,
raw_rows: [],
rows: [],
error: parsed.error
};
}
return {
fetched_rows: parsed.rows.length,
raw_rows: parsed.rows,
rows: parsed.rows,
error: null
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return {
fetched_rows: 0,
raw_rows: [],
rows: [],
error: `MCP fetch failed: ${message}`
};
} finally {
clearTimeout(timeout);
}
}