135 lines
4.4 KiB
TypeScript
135 lines
4.4 KiB
TypeScript
import type { AssistantDebugRouteRecord } from "../types/assistant";
|
|
import type { RouteHintSummary } from "../types/normalizer";
|
|
|
|
interface FragmentLike {
|
|
fragment_id?: string;
|
|
raw_fragment_text?: string;
|
|
normalized_fragment_text?: string;
|
|
account_hints?: unknown;
|
|
}
|
|
|
|
export interface AssistantExecutionPlanItem {
|
|
fragment_id: string;
|
|
requirement_ids: string[];
|
|
route: string;
|
|
should_execute: boolean;
|
|
fragment_text: string;
|
|
no_route_reason: string | null;
|
|
clarification_reason: string | null;
|
|
}
|
|
|
|
function escapeRegex(value: string): string {
|
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
}
|
|
|
|
function enrichFragmentTextWithHints(fragment: FragmentLike, text: string): string {
|
|
const baseText = String(text ?? "").trim();
|
|
const accountHints = Array.isArray(fragment.account_hints)
|
|
? Array.from(new Set(fragment.account_hints.map((item) => String(item ?? "").trim()).filter((item) => item.length > 0)))
|
|
: [];
|
|
if (accountHints.length === 0) {
|
|
return baseText;
|
|
}
|
|
const hasAccountInText = accountHints.some((account) => new RegExp(`\\b${escapeRegex(account)}\\b`, "i").test(baseText));
|
|
if (hasAccountInText) {
|
|
return baseText;
|
|
}
|
|
return `${baseText}, по счету ${accountHints.join(", ")}`;
|
|
}
|
|
|
|
export function buildFragmentTextById(fragments: FragmentLike[]): Map<string, string> {
|
|
const result = new Map<string, string>();
|
|
for (const item of fragments) {
|
|
if (!item || typeof item !== "object") {
|
|
continue;
|
|
}
|
|
const fragment = item as FragmentLike;
|
|
const fragmentId = typeof fragment.fragment_id === "string" ? fragment.fragment_id : "";
|
|
if (!fragmentId) {
|
|
continue;
|
|
}
|
|
const text =
|
|
(typeof fragment.raw_fragment_text === "string" && fragment.raw_fragment_text.trim()) ||
|
|
(typeof fragment.normalized_fragment_text === "string" && fragment.normalized_fragment_text.trim()) ||
|
|
"";
|
|
result.set(fragmentId, enrichFragmentTextWithHints(fragment, text));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function buildExecutionPlanFromRoute(input: {
|
|
routeSummary: RouteHintSummary | null;
|
|
userMessage: string;
|
|
fragmentTextById: Map<string, string>;
|
|
requirementByFragment: Map<string, string[]>;
|
|
}): AssistantExecutionPlanItem[] {
|
|
if (!input.routeSummary) {
|
|
return [];
|
|
}
|
|
if (input.routeSummary.mode === "legacy_v1") {
|
|
return [
|
|
{
|
|
fragment_id: "F1",
|
|
requirement_ids: input.requirementByFragment.get("F1") ?? ["R1"],
|
|
route: input.routeSummary.route_hint,
|
|
should_execute: true,
|
|
fragment_text: input.userMessage,
|
|
no_route_reason: null,
|
|
clarification_reason: null
|
|
}
|
|
];
|
|
}
|
|
return input.routeSummary.decisions.map((decision) => {
|
|
const text = input.fragmentTextById.get(decision.fragment_id) ?? input.userMessage;
|
|
if (decision.route === "no_route") {
|
|
return {
|
|
fragment_id: decision.fragment_id,
|
|
requirement_ids: input.requirementByFragment.get(decision.fragment_id) ?? [],
|
|
route: "no_route",
|
|
should_execute: false,
|
|
fragment_text: text,
|
|
no_route_reason: decision.no_route_reason ?? null,
|
|
clarification_reason: decision.clarification_reason ?? null
|
|
};
|
|
}
|
|
return {
|
|
fragment_id: decision.fragment_id,
|
|
requirement_ids: input.requirementByFragment.get(decision.fragment_id) ?? [],
|
|
route: decision.route,
|
|
should_execute: true,
|
|
fragment_text: text,
|
|
no_route_reason: null,
|
|
clarification_reason: decision.clarification_reason ?? null
|
|
};
|
|
});
|
|
}
|
|
|
|
export function buildDebugRoutesFromRoute(input: {
|
|
routeSummary: RouteHintSummary | null;
|
|
resolveLegacyRouteReason: (route: string) => string;
|
|
}): AssistantDebugRouteRecord[] {
|
|
if (!input.routeSummary) {
|
|
return [];
|
|
}
|
|
if (input.routeSummary.mode === "legacy_v1") {
|
|
return [
|
|
{
|
|
fragment_id: "F1",
|
|
route: input.routeSummary.route_hint,
|
|
reason: input.resolveLegacyRouteReason(input.routeSummary.route_hint),
|
|
confidence: input.routeSummary.confidence,
|
|
intent_class: input.routeSummary.intent_class
|
|
}
|
|
];
|
|
}
|
|
return input.routeSummary.decisions.map((decision) => ({
|
|
fragment_id: decision.fragment_id,
|
|
route: decision.route,
|
|
reason: decision.reason,
|
|
route_status: decision.route_status ?? null,
|
|
no_route_reason: decision.no_route_reason ?? null,
|
|
clarification_reason: decision.clarification_reason ?? null,
|
|
execution_readiness: decision.execution_readiness ?? null
|
|
}));
|
|
}
|