NODEDC_1C/llm_normalizer/backend/src/services/assistantAddressToolGateRun...

133 lines
4.6 KiB
TypeScript

export interface AssistantAddressToolGateRuntimeInput<ResponseType = unknown> {
sessionId: string;
userMessage: string;
addressInputMessage: string;
orchestrationDecision: {
runAddressLane?: boolean;
[key: string]: unknown;
};
livingModeDecision: {
mode?: unknown;
[key: string]: unknown;
};
addressRuntimeMeta: {
attempted?: unknown;
applied?: unknown;
reason?: unknown;
fallbackRuleHit?: unknown;
sanitizedUserMessage?: unknown;
orchestrationContract?: unknown;
toolGateDecision?: unknown;
toolGateReason?: unknown;
predecomposeContract?: {
intent?: unknown;
aggregation_profile?: unknown;
period?: {
scope?: unknown;
} | null;
} | null;
semanticExtractionContract?: {
valid?: unknown;
quality?: unknown;
apply_canonical_recommended?: unknown;
reason_codes?: unknown;
} | null;
[key: string]: unknown;
} | null;
logEvent: (payload: Record<string, unknown>) => void;
tryHandleLivingChat: (
modeDecision: { mode?: unknown; reason?: unknown },
addressRuntimeMeta: Record<string, unknown> | null
) => Promise<ResponseType | null>;
nowIso: () => string;
}
export interface AssistantAddressToolGateRuntimeOutput<ResponseType = unknown> {
handled: boolean;
response: ResponseType | null;
}
export async function runAssistantAddressToolGateRuntime<ResponseType = unknown>(
input: AssistantAddressToolGateRuntimeInput<ResponseType>
): Promise<AssistantAddressToolGateRuntimeOutput<ResponseType>> {
if (Boolean(input.orchestrationDecision?.runAddressLane)) {
return {
handled: false,
response: null
};
}
const runtimeMeta =
input.addressRuntimeMeta && typeof input.addressRuntimeMeta === "object"
? input.addressRuntimeMeta
: ({} as Record<string, unknown>);
const predecomposeContract =
runtimeMeta.predecomposeContract && typeof runtimeMeta.predecomposeContract === "object"
? (runtimeMeta.predecomposeContract as {
intent?: unknown;
aggregation_profile?: unknown;
period?: { scope?: unknown } | null;
})
: null;
const predecomposePeriod =
predecomposeContract?.period && typeof predecomposeContract.period === "object"
? predecomposeContract.period
: null;
const semanticExtractionContract =
runtimeMeta.semanticExtractionContract && typeof runtimeMeta.semanticExtractionContract === "object"
? (runtimeMeta.semanticExtractionContract as {
valid?: unknown;
quality?: unknown;
apply_canonical_recommended?: unknown;
reason_codes?: unknown;
})
: null;
const semanticReasonCodes = Array.isArray(semanticExtractionContract?.reason_codes)
? semanticExtractionContract?.reason_codes
: [];
input.logEvent({
timestamp: input.nowIso(),
level: "info",
service: "assistant_loop",
message: "assistant_address_tool_gate_skip",
sessionId: input.sessionId,
details: {
session_id: input.sessionId,
user_message: input.userMessage,
effective_address_user_message: input.addressInputMessage,
address_llm_predecompose_attempted: Boolean(runtimeMeta.attempted),
address_llm_predecompose_applied: Boolean(runtimeMeta.applied),
address_llm_predecompose_reason: runtimeMeta.reason ?? null,
address_fallback_rule_hit: runtimeMeta.fallbackRuleHit ?? null,
address_sanitized_user_message: runtimeMeta.sanitizedUserMessage ?? null,
assistant_orchestration_contract_v1: runtimeMeta.orchestrationContract ?? null,
address_tool_gate_decision: runtimeMeta.toolGateDecision ?? null,
address_tool_gate_reason: runtimeMeta.toolGateReason ?? null,
address_llm_predecompose_contract_intent: predecomposeContract?.intent ?? null,
address_llm_predecompose_contract_aggregation_profile: predecomposeContract?.aggregation_profile ?? null,
address_llm_predecompose_contract_period_scope: predecomposePeriod?.scope ?? null,
address_semantic_contract_valid: semanticExtractionContract?.valid ?? null,
address_semantic_contract_quality: semanticExtractionContract?.quality ?? null,
address_semantic_apply_canonical_recommended:
semanticExtractionContract?.apply_canonical_recommended ?? null,
address_semantic_reason_codes: semanticReasonCodes
}
});
if (input.livingModeDecision?.mode === "chat") {
const chatHandled = await input.tryHandleLivingChat(input.livingModeDecision, runtimeMeta);
if (chatHandled) {
return {
handled: true,
response: chatHandled
};
}
}
return {
handled: false,
response: null
};
}