АРЧ АП11 - Выделить route policy adapter для архитектуры assistant runtime
This commit is contained in:
parent
1d95ed9b60
commit
9d5928125d
|
|
@ -1,6 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.buildAssistantAddressOrchestrationRuntime = buildAssistantAddressOrchestrationRuntime;
|
exports.buildAssistantAddressOrchestrationRuntime = buildAssistantAddressOrchestrationRuntime;
|
||||||
|
const assistantRoutePolicyRuntimeAdapter_1 = require("./assistantRoutePolicyRuntimeAdapter");
|
||||||
function hasSelectedObjectInventorySignal(text) {
|
function hasSelectedObjectInventorySignal(text) {
|
||||||
return /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(String(text ?? ""));
|
return /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(String(text ?? ""));
|
||||||
}
|
}
|
||||||
|
|
@ -78,26 +79,25 @@ async function buildAssistantAddressOrchestrationRuntime(input) {
|
||||||
carryover = input.resolveAddressFollowupCarryoverContext(input.userMessage, input.sessionItems, addressInputMessage, addressPreDecompose, input.sessionAddressNavigationState);
|
carryover = input.resolveAddressFollowupCarryoverContext(input.userMessage, input.sessionItems, addressInputMessage, addressPreDecompose, input.sessionAddressNavigationState);
|
||||||
}
|
}
|
||||||
const followupContext = carryover?.followupContext ?? null;
|
const followupContext = carryover?.followupContext ?? null;
|
||||||
const orchestrationDecision = input.resolveAssistantOrchestrationDecision({
|
const routePolicyRuntime = (0, assistantRoutePolicyRuntimeAdapter_1.runAssistantRoutePolicyRuntime)({
|
||||||
rawUserMessage: input.userMessage,
|
rawUserMessage: input.userMessage,
|
||||||
effectiveAddressUserMessage: addressInputMessage,
|
effectiveAddressUserMessage: addressInputMessage,
|
||||||
followupContext,
|
followupContext,
|
||||||
llmPreDecomposeMeta: addressPreDecompose,
|
llmPreDecomposeMeta: addressPreDecompose,
|
||||||
sessionItems: input.sessionItems,
|
sessionItems: input.sessionItems,
|
||||||
sessionOrganizationScope: input.sessionOrganizationScope ?? null,
|
sessionOrganizationScope: input.sessionOrganizationScope ?? null,
|
||||||
useMock: input.useMock
|
useMock: input.useMock,
|
||||||
|
resolveAssistantOrchestrationDecision: input.resolveAssistantOrchestrationDecision
|
||||||
});
|
});
|
||||||
|
const orchestrationDecision = routePolicyRuntime.orchestrationDecision;
|
||||||
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(input.userMessage, addressInputMessage, carryover, addressPreDecompose);
|
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(input.userMessage, addressInputMessage, carryover, addressPreDecompose);
|
||||||
const addressRuntimeMeta = {
|
const addressRuntimeMeta = {
|
||||||
...addressPreDecompose,
|
...addressPreDecompose,
|
||||||
toolGateDecision: orchestrationDecision.toolGateDecision ?? null,
|
toolGateDecision: orchestrationDecision.toolGateDecision ?? null,
|
||||||
toolGateReason: orchestrationDecision.toolGateReason ?? null,
|
toolGateReason: orchestrationDecision.toolGateReason ?? null,
|
||||||
dialogContinuationContract,
|
dialogContinuationContract,
|
||||||
orchestrationContract: orchestrationDecision.orchestrationContract ?? null
|
orchestrationContract: orchestrationDecision.orchestrationContract ?? null,
|
||||||
};
|
routePolicyContract: routePolicyRuntime.routePolicyContract
|
||||||
const livingModeDecision = {
|
|
||||||
mode: orchestrationDecision.livingMode,
|
|
||||||
reason: orchestrationDecision.livingReason
|
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
addressPreDecompose,
|
addressPreDecompose,
|
||||||
|
|
@ -105,6 +105,6 @@ async function buildAssistantAddressOrchestrationRuntime(input) {
|
||||||
carryover,
|
carryover,
|
||||||
orchestrationDecision,
|
orchestrationDecision,
|
||||||
addressRuntimeMeta,
|
addressRuntimeMeta,
|
||||||
livingModeDecision
|
livingModeDecision: routePolicyRuntime.livingModeDecision
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.ASSISTANT_ROUTE_POLICY_RUNTIME_SCHEMA_VERSION = void 0;
|
||||||
|
exports.runAssistantRoutePolicyRuntime = runAssistantRoutePolicyRuntime;
|
||||||
|
exports.ASSISTANT_ROUTE_POLICY_RUNTIME_SCHEMA_VERSION = "assistant_route_policy_runtime_v1";
|
||||||
|
function hasObject(value) {
|
||||||
|
return Boolean(value && typeof value === "object");
|
||||||
|
}
|
||||||
|
function runAssistantRoutePolicyRuntime(input) {
|
||||||
|
const orchestrationDecision = input.resolveAssistantOrchestrationDecision({
|
||||||
|
rawUserMessage: input.rawUserMessage,
|
||||||
|
effectiveAddressUserMessage: input.effectiveAddressUserMessage,
|
||||||
|
followupContext: input.followupContext,
|
||||||
|
llmPreDecomposeMeta: input.llmPreDecomposeMeta,
|
||||||
|
sessionItems: input.sessionItems,
|
||||||
|
sessionOrganizationScope: input.sessionOrganizationScope,
|
||||||
|
useMock: input.useMock
|
||||||
|
});
|
||||||
|
const livingModeDecision = {
|
||||||
|
mode: orchestrationDecision.livingMode,
|
||||||
|
reason: orchestrationDecision.livingReason
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
orchestrationDecision,
|
||||||
|
livingModeDecision,
|
||||||
|
routePolicyContract: {
|
||||||
|
schema_version: exports.ASSISTANT_ROUTE_POLICY_RUNTIME_SCHEMA_VERSION,
|
||||||
|
policy_owner: "assistantRoutePolicyRuntimeAdapter",
|
||||||
|
decision_source: "resolveAssistantOrchestrationDecision",
|
||||||
|
living_mode: livingModeDecision.mode,
|
||||||
|
living_reason: livingModeDecision.reason,
|
||||||
|
tool_gate_decision: orchestrationDecision.toolGateDecision ?? null,
|
||||||
|
tool_gate_reason: orchestrationDecision.toolGateReason ?? null,
|
||||||
|
has_followup_context: hasObject(input.followupContext),
|
||||||
|
has_orchestration_contract: hasObject(orchestrationDecision.orchestrationContract)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { runAssistantRoutePolicyRuntime } from "./assistantRoutePolicyRuntimeAdapter";
|
||||||
|
|
||||||
export interface BuildAssistantAddressOrchestrationRuntimeInput {
|
export interface BuildAssistantAddressOrchestrationRuntimeInput {
|
||||||
userMessage: string;
|
userMessage: string;
|
||||||
sessionItems: unknown[];
|
sessionItems: unknown[];
|
||||||
|
|
@ -195,15 +197,17 @@ export async function buildAssistantAddressOrchestrationRuntime(
|
||||||
}
|
}
|
||||||
|
|
||||||
const followupContext = carryover?.followupContext ?? null;
|
const followupContext = carryover?.followupContext ?? null;
|
||||||
const orchestrationDecision = input.resolveAssistantOrchestrationDecision({
|
const routePolicyRuntime = runAssistantRoutePolicyRuntime({
|
||||||
rawUserMessage: input.userMessage,
|
rawUserMessage: input.userMessage,
|
||||||
effectiveAddressUserMessage: addressInputMessage,
|
effectiveAddressUserMessage: addressInputMessage,
|
||||||
followupContext,
|
followupContext,
|
||||||
llmPreDecomposeMeta: addressPreDecompose,
|
llmPreDecomposeMeta: addressPreDecompose,
|
||||||
sessionItems: input.sessionItems,
|
sessionItems: input.sessionItems,
|
||||||
sessionOrganizationScope: input.sessionOrganizationScope ?? null,
|
sessionOrganizationScope: input.sessionOrganizationScope ?? null,
|
||||||
useMock: input.useMock
|
useMock: input.useMock,
|
||||||
|
resolveAssistantOrchestrationDecision: input.resolveAssistantOrchestrationDecision
|
||||||
});
|
});
|
||||||
|
const orchestrationDecision = routePolicyRuntime.orchestrationDecision;
|
||||||
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(
|
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(
|
||||||
input.userMessage,
|
input.userMessage,
|
||||||
addressInputMessage,
|
addressInputMessage,
|
||||||
|
|
@ -215,11 +219,8 @@ export async function buildAssistantAddressOrchestrationRuntime(
|
||||||
toolGateDecision: orchestrationDecision.toolGateDecision ?? null,
|
toolGateDecision: orchestrationDecision.toolGateDecision ?? null,
|
||||||
toolGateReason: orchestrationDecision.toolGateReason ?? null,
|
toolGateReason: orchestrationDecision.toolGateReason ?? null,
|
||||||
dialogContinuationContract,
|
dialogContinuationContract,
|
||||||
orchestrationContract: orchestrationDecision.orchestrationContract ?? null
|
orchestrationContract: orchestrationDecision.orchestrationContract ?? null,
|
||||||
};
|
routePolicyContract: routePolicyRuntime.routePolicyContract
|
||||||
const livingModeDecision = {
|
|
||||||
mode: orchestrationDecision.livingMode,
|
|
||||||
reason: orchestrationDecision.livingReason
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -228,6 +229,6 @@ export async function buildAssistantAddressOrchestrationRuntime(
|
||||||
carryover,
|
carryover,
|
||||||
orchestrationDecision,
|
orchestrationDecision,
|
||||||
addressRuntimeMeta,
|
addressRuntimeMeta,
|
||||||
livingModeDecision
|
livingModeDecision: routePolicyRuntime.livingModeDecision
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
export const ASSISTANT_ROUTE_POLICY_RUNTIME_SCHEMA_VERSION = "assistant_route_policy_runtime_v1" as const;
|
||||||
|
|
||||||
|
export interface RunAssistantRoutePolicyRuntimeInput {
|
||||||
|
rawUserMessage: string;
|
||||||
|
effectiveAddressUserMessage: string;
|
||||||
|
followupContext: unknown;
|
||||||
|
llmPreDecomposeMeta: Record<string, unknown>;
|
||||||
|
sessionItems?: unknown[];
|
||||||
|
sessionOrganizationScope?: unknown;
|
||||||
|
useMock: boolean;
|
||||||
|
resolveAssistantOrchestrationDecision: (input: {
|
||||||
|
rawUserMessage: string;
|
||||||
|
effectiveAddressUserMessage: string;
|
||||||
|
followupContext: unknown;
|
||||||
|
llmPreDecomposeMeta: Record<string, unknown>;
|
||||||
|
sessionItems?: unknown[];
|
||||||
|
sessionOrganizationScope?: unknown;
|
||||||
|
useMock: boolean;
|
||||||
|
}) => Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssistantRoutePolicyRuntimeContract {
|
||||||
|
schema_version: typeof ASSISTANT_ROUTE_POLICY_RUNTIME_SCHEMA_VERSION;
|
||||||
|
policy_owner: "assistantRoutePolicyRuntimeAdapter";
|
||||||
|
decision_source: "resolveAssistantOrchestrationDecision";
|
||||||
|
living_mode: unknown;
|
||||||
|
living_reason: unknown;
|
||||||
|
tool_gate_decision: unknown;
|
||||||
|
tool_gate_reason: unknown;
|
||||||
|
has_followup_context: boolean;
|
||||||
|
has_orchestration_contract: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RunAssistantRoutePolicyRuntimeOutput {
|
||||||
|
orchestrationDecision: Record<string, unknown>;
|
||||||
|
livingModeDecision: {
|
||||||
|
mode: unknown;
|
||||||
|
reason: unknown;
|
||||||
|
};
|
||||||
|
routePolicyContract: AssistantRoutePolicyRuntimeContract;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasObject(value: unknown): boolean {
|
||||||
|
return Boolean(value && typeof value === "object");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runAssistantRoutePolicyRuntime(
|
||||||
|
input: RunAssistantRoutePolicyRuntimeInput
|
||||||
|
): RunAssistantRoutePolicyRuntimeOutput {
|
||||||
|
const orchestrationDecision = input.resolveAssistantOrchestrationDecision({
|
||||||
|
rawUserMessage: input.rawUserMessage,
|
||||||
|
effectiveAddressUserMessage: input.effectiveAddressUserMessage,
|
||||||
|
followupContext: input.followupContext,
|
||||||
|
llmPreDecomposeMeta: input.llmPreDecomposeMeta,
|
||||||
|
sessionItems: input.sessionItems,
|
||||||
|
sessionOrganizationScope: input.sessionOrganizationScope,
|
||||||
|
useMock: input.useMock
|
||||||
|
});
|
||||||
|
const livingModeDecision = {
|
||||||
|
mode: orchestrationDecision.livingMode,
|
||||||
|
reason: orchestrationDecision.livingReason
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
orchestrationDecision,
|
||||||
|
livingModeDecision,
|
||||||
|
routePolicyContract: {
|
||||||
|
schema_version: ASSISTANT_ROUTE_POLICY_RUNTIME_SCHEMA_VERSION,
|
||||||
|
policy_owner: "assistantRoutePolicyRuntimeAdapter",
|
||||||
|
decision_source: "resolveAssistantOrchestrationDecision",
|
||||||
|
living_mode: livingModeDecision.mode,
|
||||||
|
living_reason: livingModeDecision.reason,
|
||||||
|
tool_gate_decision: orchestrationDecision.toolGateDecision ?? null,
|
||||||
|
tool_gate_reason: orchestrationDecision.toolGateReason ?? null,
|
||||||
|
has_followup_context: hasObject(input.followupContext),
|
||||||
|
has_orchestration_contract: hasObject(orchestrationDecision.orchestrationContract)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -63,6 +63,16 @@ describe("assistant address orchestration runtime adapter", () => {
|
||||||
expect(output.orchestrationDecision.runAddressLane).toBe(true);
|
expect(output.orchestrationDecision.runAddressLane).toBe(true);
|
||||||
expect(output.livingModeDecision.mode).toBe("deep_analysis");
|
expect(output.livingModeDecision.mode).toBe("deep_analysis");
|
||||||
expect(output.addressRuntimeMeta.toolGateDecision).toBe("run_address_lane");
|
expect(output.addressRuntimeMeta.toolGateDecision).toBe("run_address_lane");
|
||||||
|
expect(output.addressRuntimeMeta.routePolicyContract).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
schema_version: "assistant_route_policy_runtime_v1",
|
||||||
|
policy_owner: "assistantRoutePolicyRuntimeAdapter",
|
||||||
|
living_mode: "deep_analysis",
|
||||||
|
tool_gate_decision: "run_address_lane",
|
||||||
|
has_followup_context: true,
|
||||||
|
has_orchestration_contract: true
|
||||||
|
})
|
||||||
|
);
|
||||||
expect(output.addressRuntimeMeta.dialogContinuationContract).toEqual({
|
expect(output.addressRuntimeMeta.dialogContinuationContract).toEqual({
|
||||||
schema_version: "address_dialog_continuation_contract_v2"
|
schema_version: "address_dialog_continuation_contract_v2"
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { runAssistantRoutePolicyRuntime } from "../src/services/assistantRoutePolicyRuntimeAdapter";
|
||||||
|
|
||||||
|
describe("assistant route policy runtime adapter", () => {
|
||||||
|
it("delegates to the current orchestration decision function and emits a route policy contract", () => {
|
||||||
|
const resolveAssistantOrchestrationDecision = vi.fn(() => ({
|
||||||
|
runAddressLane: true,
|
||||||
|
livingMode: "address_data",
|
||||||
|
livingReason: "address_lane_triggered",
|
||||||
|
toolGateDecision: "run_address_lane",
|
||||||
|
toolGateReason: "followup_context_detected",
|
||||||
|
orchestrationContract: {
|
||||||
|
schema_version: "assistant_orchestration_contract_v1"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
const followupContext = { previous_intent: "inventory_on_hand_as_of_date" };
|
||||||
|
|
||||||
|
const output = runAssistantRoutePolicyRuntime({
|
||||||
|
rawUserMessage: "кто это поставил нам",
|
||||||
|
effectiveAddressUserMessage: "кто это поставил нам",
|
||||||
|
followupContext,
|
||||||
|
llmPreDecomposeMeta: {
|
||||||
|
attempted: true
|
||||||
|
},
|
||||||
|
sessionItems: [{ role: "assistant" }],
|
||||||
|
sessionOrganizationScope: {
|
||||||
|
selectedOrganization: "ООО Ромашка"
|
||||||
|
},
|
||||||
|
useMock: false,
|
||||||
|
resolveAssistantOrchestrationDecision
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolveAssistantOrchestrationDecision).toHaveBeenCalledWith({
|
||||||
|
rawUserMessage: "кто это поставил нам",
|
||||||
|
effectiveAddressUserMessage: "кто это поставил нам",
|
||||||
|
followupContext,
|
||||||
|
llmPreDecomposeMeta: {
|
||||||
|
attempted: true
|
||||||
|
},
|
||||||
|
sessionItems: [{ role: "assistant" }],
|
||||||
|
sessionOrganizationScope: {
|
||||||
|
selectedOrganization: "ООО Ромашка"
|
||||||
|
},
|
||||||
|
useMock: false
|
||||||
|
});
|
||||||
|
expect(output.orchestrationDecision.runAddressLane).toBe(true);
|
||||||
|
expect(output.livingModeDecision).toEqual({
|
||||||
|
mode: "address_data",
|
||||||
|
reason: "address_lane_triggered"
|
||||||
|
});
|
||||||
|
expect(output.routePolicyContract).toEqual({
|
||||||
|
schema_version: "assistant_route_policy_runtime_v1",
|
||||||
|
policy_owner: "assistantRoutePolicyRuntimeAdapter",
|
||||||
|
decision_source: "resolveAssistantOrchestrationDecision",
|
||||||
|
living_mode: "address_data",
|
||||||
|
living_reason: "address_lane_triggered",
|
||||||
|
tool_gate_decision: "run_address_lane",
|
||||||
|
tool_gate_reason: "followup_context_detected",
|
||||||
|
has_followup_context: true,
|
||||||
|
has_orchestration_contract: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue