105 lines
4.2 KiB
TypeScript
105 lines
4.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { applyAssistantCapabilityBindingResponseGuard } from "../src/services/assistantCapabilityBindingResponseGuard";
|
||
import type { AssistantCapabilityRuntimeBindingContract } from "../src/types/assistantRuntimeContracts";
|
||
|
||
function binding(overrides: Partial<AssistantCapabilityRuntimeBindingContract>): AssistantCapabilityRuntimeBindingContract {
|
||
return {
|
||
schema_version: "assistant_capability_runtime_binding_v1",
|
||
binding_owner: "assistantCapabilityRuntimeBindingAdapter",
|
||
capability_id: "inventory_inventory_purchase_provenance_for_item",
|
||
capability_contract_id: "inventory_inventory_purchase_provenance_for_item",
|
||
binding_status: "bound",
|
||
binding_action: "allow",
|
||
runtime_lane_expected: "address_exact",
|
||
runtime_lane_observed: "address_exact",
|
||
execution_adapter: "AddressQueryService",
|
||
transition_id: "T4",
|
||
transition_allowed: true,
|
||
required_anchors: ["item"],
|
||
provided_anchors: ["item"],
|
||
missing_anchors: [],
|
||
requires_focus_object: true,
|
||
focus_object_binding_status: "inferred_from_anchor",
|
||
result_shape: "supplier_purchase_provenance_trace",
|
||
answer_object_shape: "inventory_provenance_bundle",
|
||
truth_gate_behavior: "partial_or_blocked_if_evidence_insufficient",
|
||
truth_fallback_allowed: true,
|
||
answer_shape_compatible: true,
|
||
violations: [],
|
||
reason_codes: ["binding_status_bound"],
|
||
...overrides
|
||
};
|
||
}
|
||
|
||
describe("assistant capability binding response guard", () => {
|
||
it("leaves allowed answers unchanged", () => {
|
||
const output = applyAssistantCapabilityBindingResponseGuard({
|
||
assistantReply: "answer",
|
||
replyType: "factual",
|
||
capabilityBinding: binding({})
|
||
});
|
||
|
||
expect(output.assistantReply).toBe("answer");
|
||
expect(output.replyType).toBe("factual");
|
||
expect(output.audit.applied).toBe(false);
|
||
expect(output.audit.guarded_reply_type).toBe("factual");
|
||
});
|
||
|
||
it("turns missing required anchors into clarification replies", () => {
|
||
const output = applyAssistantCapabilityBindingResponseGuard({
|
||
assistantReply: "unsafe answer",
|
||
replyType: "factual",
|
||
capabilityBinding: binding({
|
||
binding_status: "blocked",
|
||
binding_action: "clarify",
|
||
missing_anchors: ["item"],
|
||
focus_object_binding_status: "missing",
|
||
violations: ["required_anchor_missing", "focus_object_required_but_unbound"],
|
||
reason_codes: ["required_anchor_missing"]
|
||
})
|
||
});
|
||
|
||
expect(output.replyType).toBe("partial_coverage");
|
||
expect(output.assistantReply).toContain("Нужно уточнение");
|
||
expect(output.assistantReply).toContain("item");
|
||
expect(output.audit.applied).toBe(true);
|
||
expect(output.audit.reason_codes).toContain("capability_binding_guard_clarification_reply");
|
||
});
|
||
|
||
it("turns blocked incompatible transitions into bounded replies", () => {
|
||
const output = applyAssistantCapabilityBindingResponseGuard({
|
||
assistantReply: "unsafe answer",
|
||
replyType: "factual",
|
||
capabilityBinding: binding({
|
||
binding_status: "blocked",
|
||
binding_action: "block",
|
||
violations: ["transition_not_supported_by_capability"],
|
||
reason_codes: ["transition_not_supported_by_capability"]
|
||
})
|
||
});
|
||
|
||
expect(output.replyType).toBe("partial_coverage");
|
||
expect(output.assistantReply).toContain("Не могу надежно подтвердить");
|
||
expect(output.assistantReply).toContain("transition_not_supported_by_capability");
|
||
expect(output.audit.applied).toBe(true);
|
||
expect(output.audit.reason_codes).toContain("capability_binding_guard_blocked_reply");
|
||
});
|
||
|
||
it("downgrades factual reply type for limited bound answers without rewriting text", () => {
|
||
const output = applyAssistantCapabilityBindingResponseGuard({
|
||
assistantReply: "limited answer",
|
||
replyType: "factual",
|
||
capabilityBinding: binding({
|
||
binding_status: "bound_with_limits",
|
||
binding_action: "limit",
|
||
reason_codes: ["truth_mode_limited"]
|
||
})
|
||
});
|
||
|
||
expect(output.assistantReply).toBe("limited answer");
|
||
expect(output.replyType).toBe("partial_coverage");
|
||
expect(output.audit.applied).toBe(true);
|
||
expect(output.audit.reason_codes).toContain("capability_binding_guard_limited_reply_type");
|
||
});
|
||
});
|