import type { AssistantReplyType } from "../types/assistant"; import type { AssistantCapabilityBindingAction, AssistantCapabilityRuntimeBindingContract } from "../types/assistantRuntimeContracts"; export interface ApplyAssistantCapabilityBindingResponseGuardInput { assistantReply: string; replyType: AssistantReplyType; capabilityBinding: AssistantCapabilityRuntimeBindingContract | null | undefined; } export interface AssistantCapabilityBindingResponseGuardAudit { schema_version: "assistant_capability_binding_response_guard_v1"; guard_owner: "assistantCapabilityBindingResponseGuard"; applied: boolean; action: AssistantCapabilityBindingAction | "none"; original_reply_type: AssistantReplyType; guarded_reply_type: AssistantReplyType; reason_codes: string[]; } export interface ApplyAssistantCapabilityBindingResponseGuardOutput { assistantReply: string; replyType: AssistantReplyType; audit: AssistantCapabilityBindingResponseGuardAudit; } function formatMissingAnchors(anchors: string[]): string { if (anchors.length === 0) { return "нужный объект, период или организацию"; } return anchors.join(", "); } function buildClarificationReply(binding: AssistantCapabilityRuntimeBindingContract): string { return [ "Нужно уточнение, чтобы не подставить неподтвержденный объект в расчет.", `Не хватает: ${formatMissingAnchors(binding.missing_anchors)}.`, "Уточните это, и я продолжу тот же сценарий." ].join("\n"); } function buildBlockedReply(binding: AssistantCapabilityRuntimeBindingContract): string { const reasons = binding.violations.length > 0 ? binding.violations.join(", ") : "runtime_binding_blocked"; return [ "Не могу надежно подтвердить ответ в текущем контексте.", `Проверка сценария остановила ответ: ${reasons}.`, "Лучше уточнить объект или перезапустить вопрос от корневого запроса, чем выдавать это как подтвержденный факт." ].join("\n"); } export function applyAssistantCapabilityBindingResponseGuard( input: ApplyAssistantCapabilityBindingResponseGuardInput ): ApplyAssistantCapabilityBindingResponseGuardOutput { const binding = input.capabilityBinding; const baseAudit: AssistantCapabilityBindingResponseGuardAudit = { schema_version: "assistant_capability_binding_response_guard_v1", guard_owner: "assistantCapabilityBindingResponseGuard", applied: false, action: binding?.binding_action ?? "none", original_reply_type: input.replyType, guarded_reply_type: input.replyType, reason_codes: binding?.reason_codes ?? [] }; if (!binding || binding.binding_action === "allow" || binding.binding_action === "observe_only") { return { assistantReply: input.assistantReply, replyType: input.replyType, audit: baseAudit }; } if (binding.binding_action === "clarify") { return { assistantReply: buildClarificationReply(binding), replyType: "partial_coverage", audit: { ...baseAudit, applied: true, guarded_reply_type: "partial_coverage", reason_codes: [...baseAudit.reason_codes, "capability_binding_guard_clarification_reply"] } }; } if (binding.binding_action === "block") { return { assistantReply: buildBlockedReply(binding), replyType: "partial_coverage", audit: { ...baseAudit, applied: true, guarded_reply_type: "partial_coverage", reason_codes: [...baseAudit.reason_codes, "capability_binding_guard_blocked_reply"] } }; } return { assistantReply: input.assistantReply, replyType: input.replyType === "factual" ? "partial_coverage" : input.replyType, audit: { ...baseAudit, applied: input.replyType === "factual", guarded_reply_type: input.replyType === "factual" ? "partial_coverage" : input.replyType, reason_codes: [...baseAudit.reason_codes, "capability_binding_guard_limited_reply_type"] } }; }