ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.51 - вынос living-chat guard chain (script/grounding/meta boundary wiring) в отдельный input-builder adapter, чтобы ужать assistantService.
This commit is contained in:
parent
5e4cc0ed67
commit
b612615219
|
|
@ -1610,7 +1610,42 @@ Validation:
|
||||||
- `assistantWave10SettlementCorrectiveRegression.test.ts`
|
- `assistantWave10SettlementCorrectiveRegression.test.ts`
|
||||||
- `assistantLivingChatMode.test.ts`
|
- `assistantLivingChatMode.test.ts`
|
||||||
|
|
||||||
Status: **In progress (Phase 2.1 + 2.2 + 2.3 + 2.4 + 2.5 + 2.6 + 2.7 + 2.8 + 2.9 + 2.10 + 2.11 + 2.12 + 2.13 + 2.14 + 2.15 + 2.16 + 2.17 + 2.18 + 2.19 + 2.20 + 2.21 + 2.22 + 2.23 + 2.24 + 2.25 + 2.26 + 2.27 + 2.28 + 2.29 + 2.30 + 2.31 + 2.32 + 2.33 + 2.34 + 2.35 + 2.36 + 2.37 + 2.38 + 2.39 + 2.40 + 2.41 + 2.42 + 2.43 + 2.44 + 2.45 + 2.46 + 2.47 + 2.48 + 2.49 + 2.50 completed)**
|
Implemented in current pass (Phase 2.51):
|
||||||
|
1. Extracted living-chat attempt input assembly from `assistantAddressAttemptRuntimeAdapter` into dedicated builder:
|
||||||
|
- `assistantLivingChatAttemptInputBuilder.ts`
|
||||||
|
- introduced:
|
||||||
|
- `buildAssistantLivingChatAttemptRuntimeInput(...)`
|
||||||
|
2. Rewired `assistantAddressAttemptRuntimeAdapter` to consume the new builder (behavior-preserving):
|
||||||
|
- moved inline living-chat payload mapping (including `traceIdFactory` derivation and scope/meta wiring) behind a single input-builder boundary.
|
||||||
|
3. Added focused unit tests:
|
||||||
|
- `assistantLivingChatAttemptInputBuilder.test.ts`
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
1. `npm run build` passed.
|
||||||
|
2. Targeted living/address/deep followup pack passed:
|
||||||
|
- `assistantLivingChatAttemptInputBuilder.test.ts`
|
||||||
|
- `assistantAddressAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantLivingChatAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantLivingChatHandlerRuntimeAdapter.test.ts`
|
||||||
|
- `assistantLivingChatRuntimeAdapter.test.ts`
|
||||||
|
- `assistantTurnRuntimeDepsAdapter.test.ts`
|
||||||
|
- `assistantTurnRuntimeInputBuilder.test.ts`
|
||||||
|
- `assistantTurnAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantOrganizationScopeRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressLaneAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressLaneResponseAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressLaneResponseRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnResponseAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnAnalysisAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnAnalysisRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnResponseRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnPackagingRuntimeAdapter.test.ts`
|
||||||
|
- `assistantWave10SettlementCorrectiveRegression.test.ts`
|
||||||
|
- `assistantLivingChatMode.test.ts`
|
||||||
|
|
||||||
|
Status: **In progress (Phase 2.1 + 2.2 + 2.3 + 2.4 + 2.5 + 2.6 + 2.7 + 2.8 + 2.9 + 2.10 + 2.11 + 2.12 + 2.13 + 2.14 + 2.15 + 2.16 + 2.17 + 2.18 + 2.19 + 2.20 + 2.21 + 2.22 + 2.23 + 2.24 + 2.25 + 2.26 + 2.27 + 2.28 + 2.29 + 2.30 + 2.31 + 2.32 + 2.33 + 2.34 + 2.35 + 2.36 + 2.37 + 2.38 + 2.39 + 2.40 + 2.41 + 2.42 + 2.43 + 2.44 + 2.45 + 2.46 + 2.47 + 2.48 + 2.49 + 2.50 + 2.51 completed)**
|
||||||
|
|
||||||
## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards)
|
## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ const assistantAddressRuntimeAdapter_1 = require("./assistantAddressRuntimeAdapt
|
||||||
const assistantAddressLaneAttemptRuntimeAdapter_1 = require("./assistantAddressLaneAttemptRuntimeAdapter");
|
const assistantAddressLaneAttemptRuntimeAdapter_1 = require("./assistantAddressLaneAttemptRuntimeAdapter");
|
||||||
const assistantAddressLaneResponseAttemptRuntimeAdapter_1 = require("./assistantAddressLaneResponseAttemptRuntimeAdapter");
|
const assistantAddressLaneResponseAttemptRuntimeAdapter_1 = require("./assistantAddressLaneResponseAttemptRuntimeAdapter");
|
||||||
const assistantLivingChatAttemptRuntimeAdapter_1 = require("./assistantLivingChatAttemptRuntimeAdapter");
|
const assistantLivingChatAttemptRuntimeAdapter_1 = require("./assistantLivingChatAttemptRuntimeAdapter");
|
||||||
|
const assistantLivingChatAttemptInputBuilder_1 = require("./assistantLivingChatAttemptInputBuilder");
|
||||||
async function runAssistantAddressAttemptRuntime(input) {
|
async function runAssistantAddressAttemptRuntime(input) {
|
||||||
const runAddressRuntimeSafe = input.runAddressRuntime ?? assistantAddressRuntimeAdapter_1.runAssistantAddressRuntime;
|
const runAddressRuntimeSafe = input.runAddressRuntime ?? assistantAddressRuntimeAdapter_1.runAssistantAddressRuntime;
|
||||||
const runAddressLaneAttemptRuntimeSafe = input.runAddressLaneAttemptRuntime ?? assistantAddressLaneAttemptRuntimeAdapter_1.runAssistantAddressLaneAttemptRuntime;
|
const runAddressLaneAttemptRuntimeSafe = input.runAddressLaneAttemptRuntime ?? assistantAddressLaneAttemptRuntimeAdapter_1.runAssistantAddressLaneAttemptRuntime;
|
||||||
|
|
@ -31,7 +32,7 @@ async function runAssistantAddressAttemptRuntime(input) {
|
||||||
logEvent: input.logEvent,
|
logEvent: input.logEvent,
|
||||||
messageIdFactory: input.messageIdFactory
|
messageIdFactory: input.messageIdFactory
|
||||||
});
|
});
|
||||||
const tryHandleLivingChat = async (modeDecision, addressRuntimeMeta = null) => runLivingChatAttemptRuntimeSafe({
|
const tryHandleLivingChat = async (modeDecision, addressRuntimeMeta = null) => runLivingChatAttemptRuntimeSafe((0, assistantLivingChatAttemptInputBuilder_1.buildAssistantLivingChatAttemptRuntimeInput)({
|
||||||
sessionId: input.sessionId,
|
sessionId: input.sessionId,
|
||||||
userMessage: input.userMessage,
|
userMessage: input.userMessage,
|
||||||
sessionItems: input.sessionItems,
|
sessionItems: input.sessionItems,
|
||||||
|
|
@ -42,7 +43,6 @@ async function runAssistantAddressAttemptRuntime(input) {
|
||||||
activeOrganization: input.sessionScope.activeOrganization
|
activeOrganization: input.sessionScope.activeOrganization
|
||||||
},
|
},
|
||||||
addressRuntimeMeta,
|
addressRuntimeMeta,
|
||||||
traceIdFactory: () => `chat-${input.messageIdFactory().replace(/^msg-/, "")}`,
|
|
||||||
toNonEmptyString: input.toNonEmptyString,
|
toNonEmptyString: input.toNonEmptyString,
|
||||||
mergeKnownOrganizations: input.mergeKnownOrganizations,
|
mergeKnownOrganizations: input.mergeKnownOrganizations,
|
||||||
hasAssistantDataScopeMetaQuestionSignal: input.hasAssistantDataScopeMetaQuestionSignal,
|
hasAssistantDataScopeMetaQuestionSignal: input.hasAssistantDataScopeMetaQuestionSignal,
|
||||||
|
|
@ -77,7 +77,7 @@ async function runAssistantAddressAttemptRuntime(input) {
|
||||||
defaultModel: input.defaultModel,
|
defaultModel: input.defaultModel,
|
||||||
defaultBaseUrl: input.defaultBaseUrl,
|
defaultBaseUrl: input.defaultBaseUrl,
|
||||||
defaultApiKey: input.defaultApiKey
|
defaultApiKey: input.defaultApiKey
|
||||||
});
|
}));
|
||||||
const runAddressLaneAttempt = async (messageUsed, carryMeta, analysisDateHint) => runAddressLaneAttemptRuntimeSafe({
|
const runAddressLaneAttempt = async (messageUsed, carryMeta, analysisDateHint) => runAddressLaneAttemptRuntimeSafe({
|
||||||
messageUsed,
|
messageUsed,
|
||||||
carryMeta,
|
carryMeta,
|
||||||
|
|
|
||||||
52
llm_normalizer/backend/dist/services/assistantLivingChatAttemptInputBuilder.js
vendored
Normal file
52
llm_normalizer/backend/dist/services/assistantLivingChatAttemptInputBuilder.js
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.buildAssistantLivingChatAttemptRuntimeInput = buildAssistantLivingChatAttemptRuntimeInput;
|
||||||
|
function buildAssistantLivingChatAttemptRuntimeInput(input) {
|
||||||
|
return {
|
||||||
|
sessionId: input.sessionId,
|
||||||
|
userMessage: input.userMessage,
|
||||||
|
sessionItems: input.sessionItems,
|
||||||
|
modeDecision: input.modeDecision,
|
||||||
|
sessionScope: {
|
||||||
|
knownOrganizations: input.sessionScope.knownOrganizations,
|
||||||
|
selectedOrganization: input.sessionScope.selectedOrganization,
|
||||||
|
activeOrganization: input.sessionScope.activeOrganization
|
||||||
|
},
|
||||||
|
addressRuntimeMeta: input.addressRuntimeMeta ?? null,
|
||||||
|
traceIdFactory: () => `chat-${input.messageIdFactory().replace(/^msg-/, "")}`,
|
||||||
|
toNonEmptyString: input.toNonEmptyString,
|
||||||
|
mergeKnownOrganizations: input.mergeKnownOrganizations,
|
||||||
|
hasAssistantDataScopeMetaQuestionSignal: input.hasAssistantDataScopeMetaQuestionSignal,
|
||||||
|
shouldHandleAsAssistantCapabilityMetaQuery: input.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||||
|
hasDestructiveDataActionSignal: input.hasDestructiveDataActionSignal,
|
||||||
|
hasDangerOrCoercionSignal: input.hasDangerOrCoercionSignal,
|
||||||
|
hasOperationalAdminActionRequestSignal: input.hasOperationalAdminActionRequestSignal,
|
||||||
|
hasOrganizationFactLookupSignal: input.hasOrganizationFactLookupSignal,
|
||||||
|
hasOrganizationFactFollowupSignal: input.hasOrganizationFactFollowupSignal,
|
||||||
|
shouldEmitOrganizationSelectionReply: input.shouldEmitOrganizationSelectionReply,
|
||||||
|
hasAssistantCapabilityQuestionSignal: input.hasAssistantCapabilityQuestionSignal,
|
||||||
|
resolveDataScopeProbe: input.resolveDataScopeProbe,
|
||||||
|
applyScriptGuard: input.applyScriptGuard,
|
||||||
|
applyGroundingGuard: input.applyGroundingGuard,
|
||||||
|
buildAssistantSafetyRefusalReply: input.buildAssistantSafetyRefusalReply,
|
||||||
|
buildAssistantDataScopeContractReply: input.buildAssistantDataScopeContractReply,
|
||||||
|
buildAssistantOrganizationFactBoundaryReply: input.buildAssistantOrganizationFactBoundaryReply,
|
||||||
|
buildAssistantDataScopeSelectionReply: input.buildAssistantDataScopeSelectionReply,
|
||||||
|
buildAssistantOperationalBoundaryReply: input.buildAssistantOperationalBoundaryReply,
|
||||||
|
buildAssistantCapabilityContractReply: input.buildAssistantCapabilityContractReply,
|
||||||
|
appendItem: input.appendItem,
|
||||||
|
getSession: input.getSession,
|
||||||
|
persistSession: input.persistSession,
|
||||||
|
cloneConversation: input.cloneConversation,
|
||||||
|
logEvent: input.logEvent,
|
||||||
|
messageIdFactory: input.messageIdFactory,
|
||||||
|
nowIso: input.nowIso,
|
||||||
|
payload: input.payload,
|
||||||
|
chatClient: input.chatClient,
|
||||||
|
loadAssistantCanonExcerpt: input.loadAssistantCanonExcerpt,
|
||||||
|
sanitizeOutgoingAssistantText: input.sanitizeOutgoingAssistantText,
|
||||||
|
defaultModel: input.defaultModel,
|
||||||
|
defaultBaseUrl: input.defaultBaseUrl,
|
||||||
|
defaultApiKey: input.defaultApiKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
runAssistantLivingChatAttemptRuntime,
|
runAssistantLivingChatAttemptRuntime,
|
||||||
type RunAssistantLivingChatAttemptRuntimeInput
|
type RunAssistantLivingChatAttemptRuntimeInput
|
||||||
} from "./assistantLivingChatAttemptRuntimeAdapter";
|
} from "./assistantLivingChatAttemptRuntimeAdapter";
|
||||||
|
import { buildAssistantLivingChatAttemptRuntimeInput } from "./assistantLivingChatAttemptInputBuilder";
|
||||||
|
|
||||||
interface AddressAttemptPayload {
|
interface AddressAttemptPayload {
|
||||||
llmProvider?: unknown;
|
llmProvider?: unknown;
|
||||||
|
|
@ -135,53 +136,54 @@ export async function runAssistantAddressAttemptRuntime<ResponseType = unknown>(
|
||||||
modeDecision,
|
modeDecision,
|
||||||
addressRuntimeMeta = null
|
addressRuntimeMeta = null
|
||||||
) =>
|
) =>
|
||||||
runLivingChatAttemptRuntimeSafe({
|
runLivingChatAttemptRuntimeSafe(
|
||||||
sessionId: input.sessionId,
|
buildAssistantLivingChatAttemptRuntimeInput({
|
||||||
userMessage: input.userMessage,
|
sessionId: input.sessionId,
|
||||||
sessionItems: input.sessionItems,
|
userMessage: input.userMessage,
|
||||||
modeDecision,
|
sessionItems: input.sessionItems,
|
||||||
sessionScope: {
|
modeDecision,
|
||||||
knownOrganizations: input.sessionScope.knownOrganizations,
|
sessionScope: {
|
||||||
selectedOrganization: input.sessionScope.selectedOrganization,
|
knownOrganizations: input.sessionScope.knownOrganizations,
|
||||||
activeOrganization: input.sessionScope.activeOrganization
|
selectedOrganization: input.sessionScope.selectedOrganization,
|
||||||
},
|
activeOrganization: input.sessionScope.activeOrganization
|
||||||
addressRuntimeMeta,
|
},
|
||||||
traceIdFactory: () => `chat-${input.messageIdFactory().replace(/^msg-/, "")}`,
|
addressRuntimeMeta,
|
||||||
toNonEmptyString: input.toNonEmptyString,
|
toNonEmptyString: input.toNonEmptyString,
|
||||||
mergeKnownOrganizations: input.mergeKnownOrganizations as any,
|
mergeKnownOrganizations: input.mergeKnownOrganizations as any,
|
||||||
hasAssistantDataScopeMetaQuestionSignal: input.hasAssistantDataScopeMetaQuestionSignal,
|
hasAssistantDataScopeMetaQuestionSignal: input.hasAssistantDataScopeMetaQuestionSignal,
|
||||||
shouldHandleAsAssistantCapabilityMetaQuery: input.shouldHandleAsAssistantCapabilityMetaQuery,
|
shouldHandleAsAssistantCapabilityMetaQuery: input.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||||
hasDestructiveDataActionSignal: input.hasDestructiveDataActionSignal,
|
hasDestructiveDataActionSignal: input.hasDestructiveDataActionSignal,
|
||||||
hasDangerOrCoercionSignal: input.hasDangerOrCoercionSignal,
|
hasDangerOrCoercionSignal: input.hasDangerOrCoercionSignal,
|
||||||
hasOperationalAdminActionRequestSignal: input.hasOperationalAdminActionRequestSignal,
|
hasOperationalAdminActionRequestSignal: input.hasOperationalAdminActionRequestSignal,
|
||||||
hasOrganizationFactLookupSignal: input.hasOrganizationFactLookupSignal,
|
hasOrganizationFactLookupSignal: input.hasOrganizationFactLookupSignal,
|
||||||
hasOrganizationFactFollowupSignal: input.hasOrganizationFactFollowupSignal,
|
hasOrganizationFactFollowupSignal: input.hasOrganizationFactFollowupSignal,
|
||||||
shouldEmitOrganizationSelectionReply: input.shouldEmitOrganizationSelectionReply,
|
shouldEmitOrganizationSelectionReply: input.shouldEmitOrganizationSelectionReply,
|
||||||
hasAssistantCapabilityQuestionSignal: input.hasAssistantCapabilityQuestionSignal,
|
hasAssistantCapabilityQuestionSignal: input.hasAssistantCapabilityQuestionSignal,
|
||||||
resolveDataScopeProbe: input.resolveDataScopeProbe,
|
resolveDataScopeProbe: input.resolveDataScopeProbe,
|
||||||
applyScriptGuard: input.applyScriptGuard,
|
applyScriptGuard: input.applyScriptGuard,
|
||||||
applyGroundingGuard: input.applyGroundingGuard,
|
applyGroundingGuard: input.applyGroundingGuard,
|
||||||
buildAssistantSafetyRefusalReply: input.buildAssistantSafetyRefusalReply,
|
buildAssistantSafetyRefusalReply: input.buildAssistantSafetyRefusalReply,
|
||||||
buildAssistantDataScopeContractReply: input.buildAssistantDataScopeContractReply,
|
buildAssistantDataScopeContractReply: input.buildAssistantDataScopeContractReply,
|
||||||
buildAssistantOrganizationFactBoundaryReply: input.buildAssistantOrganizationFactBoundaryReply,
|
buildAssistantOrganizationFactBoundaryReply: input.buildAssistantOrganizationFactBoundaryReply,
|
||||||
buildAssistantDataScopeSelectionReply: input.buildAssistantDataScopeSelectionReply,
|
buildAssistantDataScopeSelectionReply: input.buildAssistantDataScopeSelectionReply,
|
||||||
buildAssistantOperationalBoundaryReply: input.buildAssistantOperationalBoundaryReply,
|
buildAssistantOperationalBoundaryReply: input.buildAssistantOperationalBoundaryReply,
|
||||||
buildAssistantCapabilityContractReply: input.buildAssistantCapabilityContractReply,
|
buildAssistantCapabilityContractReply: input.buildAssistantCapabilityContractReply,
|
||||||
appendItem: input.appendItem,
|
appendItem: input.appendItem,
|
||||||
getSession: input.getSession,
|
getSession: input.getSession,
|
||||||
persistSession: input.persistSession,
|
persistSession: input.persistSession,
|
||||||
cloneConversation: input.cloneConversation,
|
cloneConversation: input.cloneConversation,
|
||||||
logEvent: input.logEvent,
|
logEvent: input.logEvent,
|
||||||
messageIdFactory: input.messageIdFactory,
|
messageIdFactory: input.messageIdFactory,
|
||||||
nowIso: input.nowIso,
|
nowIso: input.nowIso,
|
||||||
payload: input.payload,
|
payload: input.payload,
|
||||||
chatClient: input.chatClient,
|
chatClient: input.chatClient,
|
||||||
loadAssistantCanonExcerpt: input.loadAssistantCanonExcerpt,
|
loadAssistantCanonExcerpt: input.loadAssistantCanonExcerpt,
|
||||||
sanitizeOutgoingAssistantText: input.sanitizeOutgoingAssistantText,
|
sanitizeOutgoingAssistantText: input.sanitizeOutgoingAssistantText,
|
||||||
defaultModel: input.defaultModel,
|
defaultModel: input.defaultModel,
|
||||||
defaultBaseUrl: input.defaultBaseUrl,
|
defaultBaseUrl: input.defaultBaseUrl,
|
||||||
defaultApiKey: input.defaultApiKey
|
defaultApiKey: input.defaultApiKey
|
||||||
} as RunAssistantLivingChatAttemptRuntimeInput<ResponseType>);
|
} as any) as RunAssistantLivingChatAttemptRuntimeInput<ResponseType>
|
||||||
|
);
|
||||||
|
|
||||||
const runAddressLaneAttempt: RunAssistantAddressRuntimeInput<ResponseType>["runAddressLaneAttempt"] = async (
|
const runAddressLaneAttempt: RunAssistantAddressRuntimeInput<ResponseType>["runAddressLaneAttempt"] = async (
|
||||||
messageUsed,
|
messageUsed,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
import type { RunAssistantLivingChatAttemptRuntimeInput } from "./assistantLivingChatAttemptRuntimeAdapter";
|
||||||
|
|
||||||
|
interface AssistantLivingChatAttemptSessionScope {
|
||||||
|
knownOrganizations: string[];
|
||||||
|
selectedOrganization: string | null;
|
||||||
|
activeOrganization: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuildAssistantLivingChatAttemptRuntimeInputInput<ResponseType = unknown> {
|
||||||
|
sessionId: string;
|
||||||
|
userMessage: string;
|
||||||
|
sessionItems: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["sessionItems"];
|
||||||
|
modeDecision: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["modeDecision"];
|
||||||
|
sessionScope: AssistantLivingChatAttemptSessionScope;
|
||||||
|
addressRuntimeMeta?: Record<string, unknown> | null;
|
||||||
|
toNonEmptyString: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["toNonEmptyString"];
|
||||||
|
mergeKnownOrganizations: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["mergeKnownOrganizations"];
|
||||||
|
hasAssistantDataScopeMetaQuestionSignal: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["hasAssistantDataScopeMetaQuestionSignal"];
|
||||||
|
shouldHandleAsAssistantCapabilityMetaQuery: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["shouldHandleAsAssistantCapabilityMetaQuery"];
|
||||||
|
hasDestructiveDataActionSignal: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["hasDestructiveDataActionSignal"];
|
||||||
|
hasDangerOrCoercionSignal: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["hasDangerOrCoercionSignal"];
|
||||||
|
hasOperationalAdminActionRequestSignal: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["hasOperationalAdminActionRequestSignal"];
|
||||||
|
hasOrganizationFactLookupSignal: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["hasOrganizationFactLookupSignal"];
|
||||||
|
hasOrganizationFactFollowupSignal: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["hasOrganizationFactFollowupSignal"];
|
||||||
|
shouldEmitOrganizationSelectionReply: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["shouldEmitOrganizationSelectionReply"];
|
||||||
|
hasAssistantCapabilityQuestionSignal: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["hasAssistantCapabilityQuestionSignal"];
|
||||||
|
resolveDataScopeProbe: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["resolveDataScopeProbe"];
|
||||||
|
applyScriptGuard: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["applyScriptGuard"];
|
||||||
|
applyGroundingGuard: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["applyGroundingGuard"];
|
||||||
|
buildAssistantSafetyRefusalReply: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["buildAssistantSafetyRefusalReply"];
|
||||||
|
buildAssistantDataScopeContractReply: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["buildAssistantDataScopeContractReply"];
|
||||||
|
buildAssistantOrganizationFactBoundaryReply: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["buildAssistantOrganizationFactBoundaryReply"];
|
||||||
|
buildAssistantDataScopeSelectionReply: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["buildAssistantDataScopeSelectionReply"];
|
||||||
|
buildAssistantOperationalBoundaryReply: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["buildAssistantOperationalBoundaryReply"];
|
||||||
|
buildAssistantCapabilityContractReply: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["buildAssistantCapabilityContractReply"];
|
||||||
|
appendItem: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["appendItem"];
|
||||||
|
getSession: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["getSession"];
|
||||||
|
persistSession: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["persistSession"];
|
||||||
|
cloneConversation: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["cloneConversation"];
|
||||||
|
logEvent: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["logEvent"];
|
||||||
|
messageIdFactory: NonNullable<RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["messageIdFactory"]>;
|
||||||
|
nowIso: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["nowIso"];
|
||||||
|
payload: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["payload"];
|
||||||
|
chatClient: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["chatClient"];
|
||||||
|
loadAssistantCanonExcerpt: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["loadAssistantCanonExcerpt"];
|
||||||
|
sanitizeOutgoingAssistantText: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["sanitizeOutgoingAssistantText"];
|
||||||
|
defaultModel: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["defaultModel"];
|
||||||
|
defaultBaseUrl: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["defaultBaseUrl"];
|
||||||
|
defaultApiKey?: RunAssistantLivingChatAttemptRuntimeInput<ResponseType>["defaultApiKey"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildAssistantLivingChatAttemptRuntimeInput<ResponseType = unknown>(
|
||||||
|
input: BuildAssistantLivingChatAttemptRuntimeInputInput<ResponseType>
|
||||||
|
): RunAssistantLivingChatAttemptRuntimeInput<ResponseType> {
|
||||||
|
return {
|
||||||
|
sessionId: input.sessionId,
|
||||||
|
userMessage: input.userMessage,
|
||||||
|
sessionItems: input.sessionItems,
|
||||||
|
modeDecision: input.modeDecision,
|
||||||
|
sessionScope: {
|
||||||
|
knownOrganizations: input.sessionScope.knownOrganizations,
|
||||||
|
selectedOrganization: input.sessionScope.selectedOrganization,
|
||||||
|
activeOrganization: input.sessionScope.activeOrganization
|
||||||
|
},
|
||||||
|
addressRuntimeMeta: input.addressRuntimeMeta ?? null,
|
||||||
|
traceIdFactory: () => `chat-${input.messageIdFactory().replace(/^msg-/, "")}`,
|
||||||
|
toNonEmptyString: input.toNonEmptyString,
|
||||||
|
mergeKnownOrganizations: input.mergeKnownOrganizations,
|
||||||
|
hasAssistantDataScopeMetaQuestionSignal: input.hasAssistantDataScopeMetaQuestionSignal,
|
||||||
|
shouldHandleAsAssistantCapabilityMetaQuery: input.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||||
|
hasDestructiveDataActionSignal: input.hasDestructiveDataActionSignal,
|
||||||
|
hasDangerOrCoercionSignal: input.hasDangerOrCoercionSignal,
|
||||||
|
hasOperationalAdminActionRequestSignal: input.hasOperationalAdminActionRequestSignal,
|
||||||
|
hasOrganizationFactLookupSignal: input.hasOrganizationFactLookupSignal,
|
||||||
|
hasOrganizationFactFollowupSignal: input.hasOrganizationFactFollowupSignal,
|
||||||
|
shouldEmitOrganizationSelectionReply: input.shouldEmitOrganizationSelectionReply,
|
||||||
|
hasAssistantCapabilityQuestionSignal: input.hasAssistantCapabilityQuestionSignal,
|
||||||
|
resolveDataScopeProbe: input.resolveDataScopeProbe,
|
||||||
|
applyScriptGuard: input.applyScriptGuard,
|
||||||
|
applyGroundingGuard: input.applyGroundingGuard,
|
||||||
|
buildAssistantSafetyRefusalReply: input.buildAssistantSafetyRefusalReply,
|
||||||
|
buildAssistantDataScopeContractReply: input.buildAssistantDataScopeContractReply,
|
||||||
|
buildAssistantOrganizationFactBoundaryReply: input.buildAssistantOrganizationFactBoundaryReply,
|
||||||
|
buildAssistantDataScopeSelectionReply: input.buildAssistantDataScopeSelectionReply,
|
||||||
|
buildAssistantOperationalBoundaryReply: input.buildAssistantOperationalBoundaryReply,
|
||||||
|
buildAssistantCapabilityContractReply: input.buildAssistantCapabilityContractReply,
|
||||||
|
appendItem: input.appendItem,
|
||||||
|
getSession: input.getSession,
|
||||||
|
persistSession: input.persistSession,
|
||||||
|
cloneConversation: input.cloneConversation,
|
||||||
|
logEvent: input.logEvent,
|
||||||
|
messageIdFactory: input.messageIdFactory,
|
||||||
|
nowIso: input.nowIso,
|
||||||
|
payload: input.payload,
|
||||||
|
chatClient: input.chatClient,
|
||||||
|
loadAssistantCanonExcerpt: input.loadAssistantCanonExcerpt,
|
||||||
|
sanitizeOutgoingAssistantText: input.sanitizeOutgoingAssistantText,
|
||||||
|
defaultModel: input.defaultModel,
|
||||||
|
defaultBaseUrl: input.defaultBaseUrl,
|
||||||
|
defaultApiKey: input.defaultApiKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { buildAssistantLivingChatAttemptRuntimeInput } from "../src/services/assistantLivingChatAttemptInputBuilder";
|
||||||
|
|
||||||
|
function buildInput(overrides: Record<string, unknown> = {}) {
|
||||||
|
return {
|
||||||
|
sessionId: "asst-1",
|
||||||
|
userMessage: "че там",
|
||||||
|
sessionItems: [],
|
||||||
|
modeDecision: {
|
||||||
|
mode: "chat",
|
||||||
|
reason: "living_chat_signal_detected"
|
||||||
|
},
|
||||||
|
sessionScope: {
|
||||||
|
knownOrganizations: ["Org A"],
|
||||||
|
selectedOrganization: "Org A",
|
||||||
|
activeOrganization: "Org A"
|
||||||
|
},
|
||||||
|
addressRuntimeMeta: {
|
||||||
|
source: "address_runtime"
|
||||||
|
},
|
||||||
|
toNonEmptyString: (value: unknown) =>
|
||||||
|
typeof value === "string" && value.trim().length > 0 ? value.trim() : null,
|
||||||
|
mergeKnownOrganizations: (values: string[]) => values,
|
||||||
|
hasAssistantDataScopeMetaQuestionSignal: () => false,
|
||||||
|
shouldHandleAsAssistantCapabilityMetaQuery: () => false,
|
||||||
|
hasDestructiveDataActionSignal: () => false,
|
||||||
|
hasDangerOrCoercionSignal: () => false,
|
||||||
|
hasOperationalAdminActionRequestSignal: () => false,
|
||||||
|
hasOrganizationFactLookupSignal: () => false,
|
||||||
|
hasOrganizationFactFollowupSignal: () => false,
|
||||||
|
shouldEmitOrganizationSelectionReply: () => false,
|
||||||
|
hasAssistantCapabilityQuestionSignal: () => false,
|
||||||
|
resolveDataScopeProbe: () => null,
|
||||||
|
applyScriptGuard: (chatText: string) => chatText,
|
||||||
|
applyGroundingGuard: (guardInput: Record<string, unknown>) => guardInput,
|
||||||
|
buildAssistantSafetyRefusalReply: () => "safety",
|
||||||
|
buildAssistantDataScopeContractReply: () => "scope",
|
||||||
|
buildAssistantOrganizationFactBoundaryReply: () => "boundary",
|
||||||
|
buildAssistantDataScopeSelectionReply: () => "selection",
|
||||||
|
buildAssistantOperationalBoundaryReply: () => "operational",
|
||||||
|
buildAssistantCapabilityContractReply: () => "capability",
|
||||||
|
appendItem: () => {},
|
||||||
|
getSession: () => null,
|
||||||
|
persistSession: () => {},
|
||||||
|
cloneConversation: (items: unknown[]) => items,
|
||||||
|
logEvent: () => {},
|
||||||
|
messageIdFactory: vi.fn(() => "msg-abc123"),
|
||||||
|
nowIso: () => "2026-04-11T00:00:00.000Z",
|
||||||
|
payload: {
|
||||||
|
llmProvider: "openai"
|
||||||
|
},
|
||||||
|
chatClient: {},
|
||||||
|
loadAssistantCanonExcerpt: () => "",
|
||||||
|
sanitizeOutgoingAssistantText: (value: unknown, fallback = "") => {
|
||||||
|
const text = typeof value === "string" ? value.trim() : "";
|
||||||
|
return text || fallback;
|
||||||
|
},
|
||||||
|
defaultModel: "gpt-5",
|
||||||
|
defaultBaseUrl: "http://localhost",
|
||||||
|
defaultApiKey: "key",
|
||||||
|
...overrides
|
||||||
|
} as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("assistant living chat attempt input builder", () => {
|
||||||
|
it("builds living-chat runtime input with derived trace id and session scope", () => {
|
||||||
|
const runtimeInput = buildAssistantLivingChatAttemptRuntimeInput(buildInput());
|
||||||
|
|
||||||
|
expect(runtimeInput.sessionId).toBe("asst-1");
|
||||||
|
expect(runtimeInput.userMessage).toBe("че там");
|
||||||
|
expect(runtimeInput.traceIdFactory()).toBe("chat-abc123");
|
||||||
|
expect(runtimeInput.sessionScope).toEqual({
|
||||||
|
knownOrganizations: ["Org A"],
|
||||||
|
selectedOrganization: "Org A",
|
||||||
|
activeOrganization: "Org A"
|
||||||
|
});
|
||||||
|
expect(runtimeInput.modeDecision).toEqual({
|
||||||
|
mode: "chat",
|
||||||
|
reason: "living_chat_signal_detected"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("normalizes absent address runtime meta to null and preserves optional api key", () => {
|
||||||
|
const runtimeInput = buildAssistantLivingChatAttemptRuntimeInput(
|
||||||
|
buildInput({
|
||||||
|
addressRuntimeMeta: undefined,
|
||||||
|
defaultApiKey: undefined
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(runtimeInput.addressRuntimeMeta).toBeNull();
|
||||||
|
expect(runtimeInput.defaultApiKey).toBeUndefined();
|
||||||
|
expect(runtimeInput.defaultBaseUrl).toBe("http://localhost");
|
||||||
|
expect(runtimeInput.defaultModel).toBe("gpt-5");
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue