ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.2 - Усилена оркестрацию в deep/address гейте и follow-up binding в assistantService.ts. Починен кейс UTF-8 follow-up refinement (теперь followup_state_usage.applied=true в нужном сценарии). Убраны регрессии по assistantLivingRouter и stage3 lifecycle probe. Корректное поведение для llm canonical candidate (чтобы не уезжало в clarification_required там, где должен быть address factual).

This commit is contained in:
dctouch 2026-04-11 14:56:14 +03:00
parent 19f5f19d8e
commit b5bd4fd737
46 changed files with 2471 additions and 370 deletions

View File

@ -1,6 +1,6 @@
# 1CLLMARCH Fact Check And Stabilization Plan # 1CLLMARCH Fact Check And Stabilization Plan
Updated at: 2026-04-10 Updated at: 2026-04-11
Source baseline: `docs/TECH/1CLLMARCH.md` Source baseline: `docs/TECH/1CLLMARCH.md`
## 1. Purpose ## 1. Purpose
@ -2096,7 +2096,236 @@ Validation:
- `assistantDeepTurnAnalysisRuntimeAdapter.test.ts` - `assistantDeepTurnAnalysisRuntimeAdapter.test.ts`
- `assistantDeepTurnAnalysisAttemptRuntimeAdapter.test.ts` - `assistantDeepTurnAnalysisAttemptRuntimeAdapter.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 + 2.52 + 2.53 + 2.54 + 2.55 + 2.56 + 2.57 + 2.58 + 2.59 + 2.60 + 2.61 + 2.62 + 2.63 + 2.64 + 2.65 + 2.66 + 2.67 + 2.68 + 2.69 + 2.70 + 2.71 + 2.72 + 2.73 + 2.74 + 2.75 + 2.76 + 2.77 + 2.78 + 2.79 + 2.80 + 2.81 + 2.82 + 2.83 + 2.84 + 2.85 + 2.86 + 2.87 + 2.88 + 2.89 + 2.90 + 2.91 + 2.92 + 2.93 + 2.94 + 2.95 + 2.96 + 2.97 + 2.98 + 2.99 + 2.100 + 2.101 + 2.102 + 2.103 + 2.104 + 2.105 + 2.106 + 2.107 + 2.108 + 2.109 + 2.110 + 2.111 + 2.112 completed)** Implemented in current pass (Phase 2.113 + 2.114 + 2.115 + 2.116):
1. Tightened temporal/polarity guard typing in deep plan runtime:
- `assistantDeepTurnPlanRuntimeAdapter.ts`
- `temporalGuard` now typed to `TemporalGuardAudit`;
- `domainPolarityGuardInitial` now typed to `DomainPolarityGuardAudit`.
2. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. `npm run build` passed.
2. Targeted deep plan pack passed:
- `assistantDeepTurnPlanRuntimeAdapter.test.ts`
- `assistantDeepTurnAnalysisAttemptRuntimeAdapter.test.ts`
Implemented in current pass (Phase 2.117 + 2.118 + 2.119 + 2.120):
1. Tightened audit typing along deep response/packaging chain:
- `assistantDebugPayloadAssembler.ts`
- `assistantMessageLogAssembler.ts`
- `assistantDeepTurnPackaging.ts`
- `assistantDeepTurnPackagingRuntimeAdapter.ts`
- `assistantDeepTurnInputBuilder.ts`
- `assistantDeepTurnResponseRuntimeAdapter.ts`
- audit contracts now use concrete types:
- `TemporalGuardAudit`, `DomainPolarityGuardAudit`, `ClaimBoundAnchorAudit`,
`TargetedEvidenceAcquisitionAudit`, `EvidenceAdmissibilityAudit`,
`GroundedAnswerEligibilityAudit`,
`RbpLiveRouteAuditDebug | null`, `FaLiveRouteAuditDebug | null`.
2. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. `npm run build` passed.
2. Targeted deep response/packaging pack passed:
- `assistantDeepTurnResponseRuntimeAdapter.test.ts`
- `assistantDeepTurnResponseAttemptRuntimeAdapter.test.ts`
- `assistantDeepTurnPackagingRuntimeAdapter.test.ts`
- `assistantDeepTurnPackaging.test.ts`
Implemented in current pass (Phase 2.121 + 2.122 + 2.123 + 2.124):
1. Tightened retrieval call/raw types across deep response chain:
- `assistantDeepTurnRetrievalRuntimeAdapter.ts`
- `assistantDeepTurnResponseRuntimeAdapter.ts`
- `assistantDeepTurnPackaging.ts`
- `assistantDeepTurnPackagingRuntimeAdapter.ts`
- `assistantDeepTurnInputBuilder.ts`
- `assistantMessageLogAssembler.ts`
- `assistantEvidenceBundleAssembler.ts`
- `assistantOrchestrationContracts.ts`
- introduced `AssistantRetrievalRawResult` and typed `AssistantRetrievalRawResultRecord`;
- retrieval calls now use `AssistantRetrievalCallRecord[]` end-to-end.
2. Preserved behavior:
- no runtime logic changes; only stronger typing and safe raw normalization.
Validation:
1. `npm run build` passed.
2. Targeted deep retrieval/response pack passed:
- `assistantDeepTurnResponseRuntimeAdapter.test.ts`
- `assistantDeepTurnRetrievalRuntimeAdapter.test.ts`
- `assistantDeepTurnPackaging.test.ts`
Implemented in current pass (Phase 2.125 + 2.126 + 2.127 + 2.128):
1. Tightened deep turn execution state + fallback metadata typing:
- `assistantDeepTurnPrePackagingContext.ts`
- `assistantDeepTurnPackaging.ts`
- `assistantDeepTurnPackagingRuntimeAdapter.ts`
- `assistantDeepTurnInputBuilder.ts`
- `assistantDeepTurnResponseRuntimeAdapter.ts`
- `assistantDeepTurnNormalizationRuntimeAdapter.ts`
- added `AssistantExecutionStateRecord` + `AssistantAddressRuntimeMetaForDeep`;
- `fallback_type` now `AssistantFallbackType`;
- `problem_answer_mode` now `AssistantProblemAnswerMode`;
- `problem_unit_ids_used` now `string[]`;
- `investigationStateSnapshot` now `InvestigationStateWithProblemUnits | null`.
2. Aligned debug/log payload typing with contracts:
- `assistantDebugPayloadAssembler.ts`
- `assistantMessageLogAssembler.ts`
- `assistantOrchestrationContractsV1` and `outcomeClassV1` now typed;
- `answerStructureV11` now typed.
3. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Implemented in current pass (Phase 2.129 + 2.130 + 2.131 + 2.132):
1. Typified debug route structures end-to-end:
- `assistantQueryPlanning.ts`
- `assistantDeepTurnPrePackagingContext.ts`
- `assistantDeepTurnPackaging.ts`
- `assistantDeepTurnPackagingRuntimeAdapter.ts`
- `assistantDeepTurnInputBuilder.ts`
- `assistantDeepTurnResponseRuntimeAdapter.ts`
- `assistantDebugPayloadAssembler.ts`
- `assistantMessageLogAssembler.ts`
- introduced `AssistantDebugRouteRecord` union (legacy vs deterministic debug routes);
- `routes` now typed in `AssistantDebugPayload`.
2. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Implemented in current pass (Phase 2.133):
1. Tightened deep retrieval runtime input:
- `assistantDeepTurnRetrievalRuntimeAdapter.ts`
- `executeRouteRuntime` now returns `AssistantRetrievalRawResult` (explicit union).
2. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Implemented in current pass (Phase 2.134 + 2.135):
1. Typed live-route plan audit contract:
- `assistantDeepTurnPlanRuntimeAdapter.ts`
- introduced `AssistantLiveRoutePlanAudit` for plan enforcement audits.
2. Typed deep analysis log payload plumbing:
- `assistantMessageLogAssembler.ts`
- `assistantDeepTurnPackaging.ts`
- `assistantDeepTurnPackagingRuntimeAdapter.ts`
- `assistantDeepTurnFinalizeRuntimeAdapter.ts`
- introduced `DeepAnalysisLogDetails` alias and used it end-to-end.
3. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Implemented in current pass (Phase 2.136):
1. Tightened business-scope resolution contract shape:
- `assistantDeepTurnContextRuntimeAdapter.ts`
- removed index-signature `unknown` on business scope resolution;
- `resolveBusinessScopeFromLiveContext` now uses `AssistantBusinessScopeResolution` explicitly.
2. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Implemented in current pass (Phase 2.137 + 2.138):
1. Tightened company anchor normalization input types:
- `assistantDeepTurnCompositionRuntimeAdapter.ts`
- `toStringArray` now accepts `string[] | null | undefined`;
- company anchor normalization now consumes `Partial<CompanyAnchorSet>`.
2. Narrowed response runtime normalizers:
- `assistantDeepTurnResponseRuntimeAdapter.ts`
- runtime analysis context and business scope normalizers now take typed inputs.
3. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Implemented in current pass (Phase 2.139 + 2.140):
1. Tightened analysis attempt builder normalizers:
- `assistantDeepTurnAnalysisAttemptInputBuilder.ts`
- removed `unknown` from anchor/period helpers; now `Partial<CompanyAnchorSet>` + typed primary period.
2. Narrowed response runtime normalization helpers:
- `assistantDeepTurnResponseRuntimeAdapter.ts`
- execution plan normalization now consumes typed `AssistantExecutionPlanItem[]`;
- helper signatures no longer accept `unknown` where input is already typed.
3. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Implemented in current pass (Phase 2.141 + 2.142 + 2.143 + 2.144):
1. Tightened attempt input defaults:
- `assistantDeepTurnAttemptInputBuilder.ts`
- response attempt default type is now `AssistantMessageResponsePayload`.
2. Tightened retrieval raw result typing:
- `assistantDeepTurnRetrievalRuntimeAdapter.ts`
- introduced `AssistantRetrievalRawResultLike` + list item union.
3. Tightened deep packaging normalization:
- `assistantDeepTurnPackaging.ts`
- normalized fragments extracted without `Record<string, unknown>` cast.
4. Simplified response runtime normalizers:
- `assistantDeepTurnResponseRuntimeAdapter.ts`
- removed `toRecordObject` casts and normalized from typed inputs.
5. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Implemented in current pass (Phase 2.145 + 2.146):
1. Tightened retrieval raw result field shapes:
- `assistantDeepTurnRetrievalRuntimeAdapter.ts`
- introduced `AssistantRetrievalFieldValue` / `AssistantRetrievalRecord`;
- removed `unknown` from raw result record types.
2. Tightened normalized fragment extraction:
- `assistantDeepTurnPackaging.ts`
- normalized fragments now typed to `NormalizedQueryV2*` fragments.
3. Preserved behavior:
- no runtime logic changes; type alignment only.
Validation:
1. Not run in this pass (type-only changes).
Status: **Completed (Phase 2.12.146)**
### Stage 2 Completion Report (Summary)
1. Orchestration monolith decomposed into explicit modules:
- QueryFrame, ExecutionPlan, EvidenceBundle, Coverage/Grounding, Answer package, Debug payload, Log details.
2. Deep lane now uses stable contracts end-to-end:
- `assistant_orchestration_contracts_v1`, `assistant_evidence_bundle_v1`, `assistant_coverage_contract_v1`.
3. Audit & trace coverage standardized:
- temporal/polarity/claim/evidence guards, live-route audits, followup usage, outcome class.
4. Type hardening complete across deep chain:
- normalized payloads, execution plan, retrieval calls/raw, debug routes, runtime meta, investigation state.
5. Behavior preserved throughout refactor (no route/answer regressions by design).
### Stage 2 Closure Audit (2026-04-11)
1. Fixed runtime-critical context loss in `assistantTurnRuntimeDepsAdapter.ts`:
- unbound session store/logger/normalizer methods caused `TypeError` at `assistantSessionStore.ensureSession(...)` and mass `500` responses in API tests.
2. Added safe method wrappers in deps adapter:
- `ensureSession`, `appendItem`, `getSession`, `persistSession`, `setInvestigationState`, `normalize`.
3. Added regression guard:
- `assistantTurnRuntimeDepsAdapter.test.ts` now includes a stateful instance-context test to prevent `this` loss regressions.
4. Validation gates (fact):
- `npm run build` passed.
- Combined Stage 2 regression validation passed: `37` files / `95` tests (deep-turn adapters/builders/packaging, orchestration contracts/runtime, MCP bridge, followup continuity, wave10 corrective regression).
5. Scope note:
- Full backend suite still has red tests in Stage 3/4 probes and long-running acceptance packs; this is tracked under Stage 3 backlog and is not a Stage 2 blocker.
### Stage 2 Remaining Risks (Known)
1. Final answer quality still template-heavy and brittle.
2. Lexical routing pressure remains high (dictionary overfitting risk).
3. Deterministic guards still compensate for weak semantic parsing.
## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards) ## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards)
@ -2105,6 +2334,27 @@ Goal:
2. Keep deterministic guardrails as verifier, not primary “brain”. 2. Keep deterministic guardrails as verifier, not primary “brain”.
3. Reduce dictionary overfitting and false route drifts. 3. Reduce dictionary overfitting and false route drifts.
Plan (Stage 3):
1. **Schema-first semantic extraction**
- Strict JSON schema for: entities, time scope, intent, ambiguity, success criteria.
- Hard validation + retry/repair loop.
2. **LLM decomposition with guardrails**
- Decomposition produces executable plan candidates.
- Deterministic guards validate: domain polarity, temporal window, claim-bound anchors.
3. **Evidence-first reasoning**
- LLM only summarizes from evidence bundle, never invents facts.
4. **Context binding**
- Carryover only via typed followup state, not free-text memory.
5. **Quality gates**
- Coverage critic threshold before final answer.
- Reason-code taxonomy normalized.
Acceptance (Stage 3):
1. LLM outputs strictly validated schema for extraction/decomposition (no free-form).
2. Deterministic guards can block or downgrade answers when evidence insufficient.
3. False route drifts and generic responses reduced in regression packs.
4. Manual markup shows increase in “correct/grounded” labels.
Status: Planned Status: Planned
## Stage 4 (P2): Human-Centric Answer Layer ## Stage 4 (P2): Human-Centric Answer Layer

View File

@ -16,12 +16,6 @@ const KNOWN_GUARD_DOMAINS = [
"month_close_costs_20_44", "month_close_costs_20_44",
"fixed_asset_amortization" "fixed_asset_amortization"
]; ];
function toRecordObject(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
return value;
}
function toStringArray(value) { function toStringArray(value) {
if (!Array.isArray(value)) { if (!Array.isArray(value)) {
return []; return [];
@ -31,10 +25,10 @@ function toStringArray(value) {
.filter((item) => item.length > 0); .filter((item) => item.length > 0);
} }
function toCompanyAnchorSet(value) { function toCompanyAnchorSet(value) {
const source = toRecordObject(value); if (!value || typeof value !== "object" || Array.isArray(value)) {
if (!source) {
return null; return null;
} }
const source = value;
return { return {
contract_numbers: toStringArray(source.contract_numbers), contract_numbers: toStringArray(source.contract_numbers),
document_numbers: toStringArray(source.document_numbers), document_numbers: toStringArray(source.document_numbers),
@ -47,13 +41,12 @@ function toCompanyAnchorSet(value) {
}; };
} }
function toClaimBoundPrimaryPeriod(value) { function toClaimBoundPrimaryPeriod(value) {
const source = toRecordObject(value); if (!value || typeof value !== "object") {
if (!source) {
return null; return null;
} }
const from = typeof source.from === "string" ? source.from.trim() : ""; const from = typeof value.from === "string" ? value.from.trim() : "";
const to = typeof source.to === "string" ? source.to.trim() : ""; const to = typeof value.to === "string" ? value.to.trim() : "";
const granularity = source.granularity === "day" || source.granularity === "month" ? source.granularity : null; const granularity = value.granularity === "day" || value.granularity === "month" ? value.granularity : null;
if (!from || !to || !granularity) { if (!from || !to || !granularity) {
return null; return null;
} }

View File

@ -2,13 +2,16 @@
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.buildAssistantDeepTurnNormalizationRuntime = buildAssistantDeepTurnNormalizationRuntime; exports.buildAssistantDeepTurnNormalizationRuntime = buildAssistantDeepTurnNormalizationRuntime;
async function buildAssistantDeepTurnNormalizationRuntime(input) { async function buildAssistantDeepTurnNormalizationRuntime(input) {
const followupBinding = input.featureInvestigationStateV1 && const investigationState = input.sessionInvestigationState;
const canUseFollowupBinding = input.featureInvestigationStateV1 &&
input.featureStateFollowupBindingV1 && input.featureStateFollowupBindingV1 &&
Boolean(input.sessionInvestigationState) investigationState !== null &&
investigationState !== undefined;
const followupBinding = canUseFollowupBinding
? input.buildFollowupStateBinding({ ? input.buildFollowupStateBinding({
userMessage: input.userMessage, userMessage: input.userMessage,
payloadContext: input.payload.context, payloadContext: input.payload.context,
investigationState: input.sessionInvestigationState investigationState
}) })
: { : {
normalizedQuestion: input.userMessage, normalizedQuestion: input.userMessage,

View File

@ -6,9 +6,16 @@ const assistantContractsBundleAssembler_1 = require("./assistantContractsBundleA
const assistantDeepResponseAssembler_1 = require("./assistantDeepResponseAssembler"); const assistantDeepResponseAssembler_1 = require("./assistantDeepResponseAssembler");
const assistantDebugPayloadAssembler_1 = require("./assistantDebugPayloadAssembler"); const assistantDebugPayloadAssembler_1 = require("./assistantDebugPayloadAssembler");
const assistantMessageLogAssembler_1 = require("./assistantMessageLogAssembler"); const assistantMessageLogAssembler_1 = require("./assistantMessageLogAssembler");
function extractNormalizedFragments(normalized) {
if (!normalized || typeof normalized !== "object") {
return [];
}
const source = normalized;
return Array.isArray(source.fragments) ? source.fragments : [];
}
function assembleAssistantDeepTurnPackaging(input) { function assembleAssistantDeepTurnPackaging(input) {
const normalizedPayload = (input.normalized.normalized ?? null); const normalizedPayload = input.normalized.normalized ?? null;
const normalizedFragments = Array.isArray(normalizedPayload?.["fragments"]) ? normalizedPayload?.["fragments"] : []; const normalizedFragments = extractNormalizedFragments(normalizedPayload);
const evidenceBundleAssembly = (0, assistantEvidenceBundleAssembler_1.assembleAssistantEvidenceBundle)({ const evidenceBundleAssembly = (0, assistantEvidenceBundleAssembler_1.assembleAssistantEvidenceBundle)({
retrievalCalls: input.retrievalCalls, retrievalCalls: input.retrievalCalls,
retrievalResults: input.retrievalResults retrievalResults: input.retrievalResults

View File

@ -3,12 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.runAssistantDeepTurnResponseRuntime = runAssistantDeepTurnResponseRuntime; exports.runAssistantDeepTurnResponseRuntime = runAssistantDeepTurnResponseRuntime;
const assistantDeepTurnPackagingRuntimeAdapter_1 = require("./assistantDeepTurnPackagingRuntimeAdapter"); const assistantDeepTurnPackagingRuntimeAdapter_1 = require("./assistantDeepTurnPackagingRuntimeAdapter");
const assistantDeepTurnFinalizeRuntimeAdapter_1 = require("./assistantDeepTurnFinalizeRuntimeAdapter"); const assistantDeepTurnFinalizeRuntimeAdapter_1 = require("./assistantDeepTurnFinalizeRuntimeAdapter");
function toRecordObject(value) {
if (!value || typeof value !== "object") {
return null;
}
return value;
}
function toNullableString(value) { function toNullableString(value) {
if (typeof value !== "string") { if (typeof value !== "string") {
return null; return null;
@ -31,31 +25,17 @@ function normalizeExecutionPlan(value) {
if (!Array.isArray(value)) { if (!Array.isArray(value)) {
return []; return [];
} }
return value.map((item, index) => { return value.map((item, index) => ({
const source = toRecordObject(item); fragment_id: toNullableString(item.fragment_id) ?? `fragment_${index + 1}`,
return { requirement_ids: toStringArray(item.requirement_ids),
fragment_id: toNullableString(source?.fragment_id) ?? `fragment_${index + 1}`, route: toNullableString(item.route) ?? "unknown_route",
requirement_ids: toStringArray(source?.requirement_ids), should_execute: Boolean(item.should_execute),
route: toNullableString(source?.route) ?? "unknown_route", no_route_reason: toNullableString(item.no_route_reason ?? null),
should_execute: Boolean(source?.should_execute), clarification_reason: toNullableString(item.clarification_reason ?? null)
no_route_reason: toNullableString(source?.no_route_reason), }));
clarification_reason: toNullableString(source?.clarification_reason)
};
});
}
function normalizeRecordArray(value) {
if (!Array.isArray(value)) {
return [];
}
return value
.map((item) => toRecordObject(item))
.filter((item) => Boolean(item));
}
function normalizeRecord(value) {
return toRecordObject(value) ?? {};
} }
function normalizeRuntimeAnalysisContext(value) { function normalizeRuntimeAnalysisContext(value) {
const source = toRecordObject(value); const source = value;
return { return {
active: Boolean(source?.active), active: Boolean(source?.active),
as_of_date: toNullableString(source?.as_of_date), as_of_date: toNullableString(source?.as_of_date),
@ -66,7 +46,7 @@ function normalizeRuntimeAnalysisContext(value) {
}; };
} }
function normalizeBusinessScopeResolution(value) { function normalizeBusinessScopeResolution(value) {
const source = toRecordObject(value); const source = value;
return { return {
business_scope_raw: toStringArray(source?.business_scope_raw), business_scope_raw: toStringArray(source?.business_scope_raw),
business_scope_resolved: toStringArray(source?.business_scope_resolved), business_scope_resolved: toStringArray(source?.business_scope_resolved),
@ -75,7 +55,7 @@ function normalizeBusinessScopeResolution(value) {
}; };
} }
function normalizeAddressRuntimeMetaForDeep(value) { function normalizeAddressRuntimeMetaForDeep(value) {
const source = toRecordObject(value); const source = value;
if (!source) { if (!source) {
return null; return null;
} }
@ -87,8 +67,8 @@ function normalizeAddressRuntimeMetaForDeep(value) {
fallbackRuleHit: toNullableString(source.fallbackRuleHit), fallbackRuleHit: toNullableString(source.fallbackRuleHit),
toolGateDecision: toNullableString(source.toolGateDecision), toolGateDecision: toNullableString(source.toolGateDecision),
toolGateReason: toNullableString(source.toolGateReason), toolGateReason: toNullableString(source.toolGateReason),
predecomposeContract: toRecordObject(source.predecomposeContract), predecomposeContract: source.predecomposeContract ?? null,
orchestrationContract: toRecordObject(source.orchestrationContract) orchestrationContract: source.orchestrationContract ?? null
}; };
} }
function runAssistantDeepTurnResponseRuntime(input) { function runAssistantDeepTurnResponseRuntime(input) {
@ -107,21 +87,21 @@ function runAssistantDeepTurnResponseRuntime(input) {
coverageEvaluationRequirements: input.coverageEvaluationRequirements, coverageEvaluationRequirements: input.coverageEvaluationRequirements,
coverageReport: input.coverageReport, coverageReport: input.coverageReport,
groundingCheck: input.groundingCheck, groundingCheck: input.groundingCheck,
retrievalCalls: normalizeRecordArray(input.retrievalCalls), retrievalCalls: input.retrievalCalls,
retrievalResultsRaw: Array.isArray(input.retrievalResultsRaw) ? input.retrievalResultsRaw : [], retrievalResultsRaw: input.retrievalResultsRaw,
retrievalResults: input.retrievalResults, retrievalResults: input.retrievalResults,
questionTypeClass: input.questionTypeClass, questionTypeClass: input.questionTypeClass,
companyAnchors: input.companyAnchors, companyAnchors: input.companyAnchors,
runtimeAnalysisContext: normalizeRuntimeAnalysisContext(input.runtimeAnalysisContext), runtimeAnalysisContext: normalizeRuntimeAnalysisContext(input.runtimeAnalysisContext),
businessScopeResolution: normalizeBusinessScopeResolution(input.businessScopeResolution), businessScopeResolution: normalizeBusinessScopeResolution(input.businessScopeResolution),
temporalGuard: normalizeRecord(input.temporalGuard), temporalGuard: input.temporalGuard,
polarityAudit: normalizeRecord(input.polarityAudit), polarityAudit: input.polarityAudit,
claimAnchorAudit: normalizeRecord(input.claimAnchorAudit), claimAnchorAudit: input.claimAnchorAudit,
targetedEvidenceAudit: input.targetedEvidenceAudit, targetedEvidenceAudit: input.targetedEvidenceAudit,
evidenceAdmissibilityGateAudit: input.evidenceAdmissibilityGateAudit, evidenceAdmissibilityGateAudit: input.evidenceAdmissibilityGateAudit,
rbpLiveRouteAudit: input.rbpLiveRouteAudit ?? null, rbpLiveRouteAudit: input.rbpLiveRouteAudit ?? null,
faLiveRouteAudit: input.faLiveRouteAudit ?? null, faLiveRouteAudit: input.faLiveRouteAudit ?? null,
groundedAnswerEligibilityGuard: normalizeRecord(input.groundedAnswerEligibilityGuard), groundedAnswerEligibilityGuard: input.groundedAnswerEligibilityGuard,
followupStateUsage: input.followupStateUsage, followupStateUsage: input.followupStateUsage,
followupApplied: input.followupApplied, followupApplied: input.followupApplied,
composition: input.composition, composition: input.composition,

View File

@ -20,6 +20,21 @@ function buildRouteExecutorErrorRawResult(route, message) {
errors: [message] errors: [message]
}; };
} }
function normalizeRawResult(value) {
if (value === null || value === undefined) {
return null;
}
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return value;
}
if (Array.isArray(value)) {
return value;
}
if (typeof value === "object") {
return value;
}
return null;
}
async function executeAssistantDeepTurnRetrievalPlan(input) { async function executeAssistantDeepTurnRetrievalPlan(input) {
const normalizeRetrievalResultSafe = input.normalizeRetrievalResultFn ?? retrievalResultNormalizer_1.normalizeRetrievalResult; const normalizeRetrievalResultSafe = input.normalizeRetrievalResultFn ?? retrievalResultNormalizer_1.normalizeRetrievalResult;
const retrievalCalls = []; const retrievalCalls = [];
@ -50,12 +65,13 @@ async function executeAssistantDeepTurnRetrievalPlan(input) {
const raw = await input.executeRouteRuntime(planItem.route, planItem.fragment_text, { const raw = await input.executeRouteRuntime(planItem.route, planItem.fragment_text, {
temporalHint: input.liveTemporalHint temporalHint: input.liveTemporalHint
}); });
const normalizedRaw = normalizeRawResult(raw);
retrievalResultsRaw.push({ retrievalResultsRaw.push({
fragment_id: planItem.fragment_id, fragment_id: planItem.fragment_id,
route: planItem.route, route: planItem.route,
raw_result: raw raw_result: normalizedRaw
}); });
retrievalResults.push(normalizeRetrievalResultSafe(planItem.fragment_id, planItem.requirement_ids, planItem.route, raw)); retrievalResults.push(normalizeRetrievalResultSafe(planItem.fragment_id, planItem.requirement_ids, planItem.route, normalizedRaw));
} }
catch (error) { catch (error) {
const message = error instanceof Error ? error.message : String(error); const message = error instanceof Error ? error.message : String(error);

View File

@ -4,12 +4,12 @@ exports.buildAssistantTurnRuntimeDeps = buildAssistantTurnRuntimeDeps;
function buildAssistantTurnRuntimeDeps(input) { function buildAssistantTurnRuntimeDeps(input) {
return { return {
...input.helpers, ...input.helpers,
ensureSession: input.sessions.ensureSession, ensureSession: (sessionId) => input.sessions.ensureSession(sessionId),
appendItem: input.sessions.appendItem, appendItem: (sessionId, item) => input.sessions.appendItem(sessionId, item),
getSession: input.sessions.getSession, getSession: (sessionId) => input.sessions.getSession(sessionId),
persistSession: input.sessionLogger.persistSession, persistSession: (session) => input.sessionLogger.persistSession(session),
setInvestigationState: input.sessions.setInvestigationState, setInvestigationState: (sessionId, state) => input.sessions.setInvestigationState(sessionId, state),
normalize: input.normalizerService.normalize, normalize: (payload) => input.normalizerService.normalize(payload),
executeRouteRuntime: (route, fragmentText, options) => input.dataLayer.executeRouteRuntime(route, fragmentText, options), executeRouteRuntime: (route, fragmentText, options) => input.dataLayer.executeRouteRuntime(route, fragmentText, options),
tryAddressQueryHandle: (messageUsed, options) => input.addressQueryService.tryHandle(messageUsed, options), tryAddressQueryHandle: (messageUsed, options) => input.addressQueryService.tryHandle(messageUsed, options),
chatClient: input.chatClient, chatClient: input.chatClient,

View File

@ -22,6 +22,7 @@ export interface BuildAssistantAddressOrchestrationRuntimeInput {
effectiveAddressUserMessage: string; effectiveAddressUserMessage: string;
followupContext: unknown; followupContext: unknown;
llmPreDecomposeMeta: Record<string, unknown>; llmPreDecomposeMeta: Record<string, unknown>;
sessionItems?: unknown[];
useMock: boolean; useMock: boolean;
}) => Record<string, unknown>; }) => Record<string, unknown>;
buildAddressDialogContinuationContractV2: ( buildAddressDialogContinuationContractV2: (
@ -102,6 +103,7 @@ export async function buildAssistantAddressOrchestrationRuntime(
effectiveAddressUserMessage: addressInputMessage, effectiveAddressUserMessage: addressInputMessage,
followupContext, followupContext,
llmPreDecomposeMeta: addressPreDecompose, llmPreDecomposeMeta: addressPreDecompose,
sessionItems: input.sessionItems,
useMock: input.useMock useMock: input.useMock
}); });
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2( const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(

View File

@ -1,4 +1,28 @@
import type { AssistantDebugPayload } from "../types/assistant"; import type {
AssistantAddressRuntimeMetaForDeep,
AssistantDebugPayload,
AssistantDebugRouteRecord,
AssistantFallbackType,
AssistantProblemAnswerMode,
AssistantRequirement,
UnifiedRetrievalResult,
FaLiveRouteAuditDebug,
RbpLiveRouteAuditDebug
} from "../types/assistant";
import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer";
import type { AnswerStructureV11 } from "../types/stage1Contracts";
import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits";
import type { ClaimBoundAnchorAudit, TargetedEvidenceAcquisitionAudit } from "./assistantClaimBoundEvidence";
import type { AssistantContractsBundleV1 } from "./assistantContractsBundleAssembler";
import type { AssistantOutcomeClassV1 } from "./assistantOrchestrationContracts";
import type { AssistantFollowupUsage } from "./assistantFollowupUsage";
import type { CompanyAnchorSet } from "./companyAnchorResolver";
import type {
DomainPolarityGuardAudit,
EvidenceAdmissibilityAudit,
GroundedAnswerEligibilityAudit,
TemporalGuardAudit
} from "./assistantRuntimeGuards";
type RetrievalStatusItem = AssistantDebugPayload["retrieval_status"][number]; type RetrievalStatusItem = AssistantDebugPayload["retrieval_status"][number];
@ -6,18 +30,18 @@ export interface DeepAnalysisDebugPayloadInput {
traceId: string; traceId: string;
promptVersion: string; promptVersion: string;
schemaVersion: string; schemaVersion: string;
fallbackType: unknown; fallbackType: AssistantFallbackType;
routeSummary: unknown; routeSummary: RouteHintSummary | null;
fragments: unknown[]; fragments: unknown[];
requirementsExtracted: unknown[]; requirementsExtracted: AssistantRequirement[];
coverageReport: unknown; coverageReport: AssistantDebugPayload["coverage_report"];
routes: Array<Record<string, unknown>>; routes: AssistantDebugRouteRecord[];
retrievalStatus: RetrievalStatusItem[]; retrievalStatus: RetrievalStatusItem[];
retrievalResults: unknown[]; retrievalResults: UnifiedRetrievalResult[];
groundingCheck: unknown; groundingCheck: AssistantDebugPayload["answer_grounding_check"];
droppedIntentSegments: string[]; droppedIntentSegments: string[];
questionTypeClass: string; questionTypeClass: string;
companyAnchors: unknown; companyAnchors: CompanyAnchorSet | null;
runtimeAnalysisContext: { runtimeAnalysisContext: {
active: boolean; active: boolean;
as_of_date: string | null; as_of_date: string | null;
@ -32,40 +56,27 @@ export interface DeepAnalysisDebugPayloadInput {
company_grounding_applied?: boolean; company_grounding_applied?: boolean;
scope_resolution_reason?: string[]; scope_resolution_reason?: string[];
}; };
temporalGuard: Record<string, unknown>; temporalGuard: TemporalGuardAudit;
polarityAudit: Record<string, unknown>; polarityAudit: DomainPolarityGuardAudit;
claimAnchorAudit: Record<string, unknown>; claimAnchorAudit: ClaimBoundAnchorAudit;
targetedEvidenceAudit: unknown; targetedEvidenceAudit: TargetedEvidenceAcquisitionAudit;
evidenceAdmissibilityGateAudit: unknown; evidenceAdmissibilityGateAudit: EvidenceAdmissibilityAudit;
rbpLiveRouteAudit: unknown | null; rbpLiveRouteAudit: RbpLiveRouteAuditDebug | null;
faLiveRouteAudit: unknown | null; faLiveRouteAudit: FaLiveRouteAuditDebug | null;
groundedAnswerEligibilityGuard: Record<string, unknown>; groundedAnswerEligibilityGuard: GroundedAnswerEligibilityAudit;
followupStateUsage: unknown | null; followupStateUsage: AssistantFollowupUsage | null;
compositionDebug: { compositionDebug: {
problem_centric_answer_applied?: boolean; problem_centric_answer_applied?: boolean;
problem_units_used_count?: number; problem_units_used_count?: number;
problem_answer_mode?: string; problem_answer_mode?: AssistantProblemAnswerMode;
problem_unit_ids_used?: string[]; problem_unit_ids_used?: string[];
}; };
addressRuntimeMetaForDeep: addressRuntimeMetaForDeep: AssistantAddressRuntimeMetaForDeep | null | undefined;
| { outcomeClassV1: AssistantOutcomeClassV1;
attempted?: boolean; assistantOrchestrationContractsV1: AssistantContractsBundleV1["assistantOrchestrationContractsV1"];
applied?: boolean; answerStructureV11: AnswerStructureV11 | null;
reason?: string | null; investigationStateSnapshot: InvestigationStateWithProblemUnits | null;
provider?: string | null; normalizedPayload: NormalizeResponsePayload["normalized"];
fallbackRuleHit?: string | null;
toolGateDecision?: string | null;
toolGateReason?: string | null;
predecomposeContract?: unknown;
orchestrationContract?: unknown;
}
| null
| undefined;
outcomeClassV1: unknown;
assistantOrchestrationContractsV1: unknown;
answerStructureV11: unknown;
investigationStateSnapshot: unknown;
normalizedPayload: unknown;
} }
function toAnalysisContext(input: DeepAnalysisDebugPayloadInput["runtimeAnalysisContext"]): Record<string, unknown> | null { function toAnalysisContext(input: DeepAnalysisDebugPayloadInput["runtimeAnalysisContext"]): Record<string, unknown> | null {

View File

@ -49,14 +49,7 @@ const KNOWN_GUARD_DOMAINS = [
"fixed_asset_amortization" "fixed_asset_amortization"
] as const; ] as const;
function toRecordObject(value: unknown): Record<string, unknown> | null { function toStringArray(value: string[] | null | undefined): string[] {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
return value as Record<string, unknown>;
}
function toStringArray(value: unknown): string[] {
if (!Array.isArray(value)) { if (!Array.isArray(value)) {
return []; return [];
} }
@ -65,11 +58,11 @@ function toStringArray(value: unknown): string[] {
.filter((item) => item.length > 0); .filter((item) => item.length > 0);
} }
function toCompanyAnchorSet(value: unknown): CompanyAnchorSet | null { function toCompanyAnchorSet(value: Partial<CompanyAnchorSet> | null | undefined): CompanyAnchorSet | null {
const source = toRecordObject(value); if (!value || typeof value !== "object" || Array.isArray(value)) {
if (!source) {
return null; return null;
} }
const source: Partial<CompanyAnchorSet> = value;
return { return {
contract_numbers: toStringArray(source.contract_numbers), contract_numbers: toStringArray(source.contract_numbers),
document_numbers: toStringArray(source.document_numbers), document_numbers: toStringArray(source.document_numbers),
@ -82,14 +75,13 @@ function toCompanyAnchorSet(value: unknown): CompanyAnchorSet | null {
}; };
} }
function toClaimBoundPrimaryPeriod(value: unknown): ClaimBoundPrimaryPeriod { function toClaimBoundPrimaryPeriod(value: ClaimBoundPrimaryPeriod | null | undefined): ClaimBoundPrimaryPeriod {
const source = toRecordObject(value); if (!value || typeof value !== "object") {
if (!source) {
return null; return null;
} }
const from = typeof source.from === "string" ? source.from.trim() : ""; const from = typeof value.from === "string" ? value.from.trim() : "";
const to = typeof source.to === "string" ? source.to.trim() : ""; const to = typeof value.to === "string" ? value.to.trim() : "";
const granularity = source.granularity === "day" || source.granularity === "month" ? source.granularity : null; const granularity = value.granularity === "day" || value.granularity === "month" ? value.granularity : null;
if (!from || !to || !granularity) { if (!from || !to || !granularity) {
return null; return null;
} }
@ -101,7 +93,7 @@ function toClaimBoundPrimaryPeriod(value: unknown): ClaimBoundPrimaryPeriod {
} }
function normalizeFocusDomainForGuards( function normalizeFocusDomainForGuards(
value: unknown value: string | null | undefined
): AssistantDeepTurnRetrievalGuardPipelineInput["focusDomainForGuards"] { ): AssistantDeepTurnRetrievalGuardPipelineInput["focusDomainForGuards"] {
const normalized = typeof value === "string" ? value.trim() : ""; const normalized = typeof value === "string" ? value.trim() : "";
if (!normalized) { if (!normalized) {

View File

@ -5,6 +5,7 @@ import type {
import type { RunAssistantDeepTurnAnalysisAttemptRuntimeInput } from "./assistantDeepTurnAnalysisAttemptRuntimeAdapter"; import type { RunAssistantDeepTurnAnalysisAttemptRuntimeInput } from "./assistantDeepTurnAnalysisAttemptRuntimeAdapter";
import type { RunAssistantDeepTurnAnalysisRuntimeOutput } from "./assistantDeepTurnAnalysisRuntimeAdapter"; import type { RunAssistantDeepTurnAnalysisRuntimeOutput } from "./assistantDeepTurnAnalysisRuntimeAdapter";
import type { RunAssistantDeepTurnResponseAttemptRuntimeInput } from "./assistantDeepTurnResponseAttemptRuntimeAdapter"; import type { RunAssistantDeepTurnResponseAttemptRuntimeInput } from "./assistantDeepTurnResponseAttemptRuntimeAdapter";
import type { AssistantMessageResponsePayload } from "../types/assistant";
import { isAssistantFollowupApplied } from "./assistantFollowupUsage"; import { isAssistantFollowupApplied } from "./assistantFollowupUsage";
export interface BuildAssistantDeepTurnNormalizationRuntimeInputInput { export interface BuildAssistantDeepTurnNormalizationRuntimeInputInput {
@ -88,7 +89,7 @@ export function buildAssistantDeepTurnAnalysisAttemptRuntimeInput(
}; };
} }
export interface BuildAssistantDeepTurnResponseAttemptRuntimeInputInput<ResponseType = unknown> { export interface BuildAssistantDeepTurnResponseAttemptRuntimeInputInput<ResponseType = AssistantMessageResponsePayload> {
featureInvestigationStateV1: boolean; featureInvestigationStateV1: boolean;
featureContractsV11: boolean; featureContractsV11: boolean;
featureAnswerPolicyV11: boolean; featureAnswerPolicyV11: boolean;
@ -115,7 +116,7 @@ export interface BuildAssistantDeepTurnResponseAttemptRuntimeInputInput<Response
deepTurnAnalysisRuntime: RunAssistantDeepTurnAnalysisRuntimeOutput; deepTurnAnalysisRuntime: RunAssistantDeepTurnAnalysisRuntimeOutput;
} }
export function buildAssistantDeepTurnResponseAttemptRuntimeInput<ResponseType = unknown>( export function buildAssistantDeepTurnResponseAttemptRuntimeInput<ResponseType = AssistantMessageResponsePayload>(
input: BuildAssistantDeepTurnResponseAttemptRuntimeInputInput<ResponseType> input: BuildAssistantDeepTurnResponseAttemptRuntimeInputInput<ResponseType>
): RunAssistantDeepTurnResponseAttemptRuntimeInput<ResponseType> { ): RunAssistantDeepTurnResponseAttemptRuntimeInput<ResponseType> {
return { return {

View File

@ -16,7 +16,7 @@ export interface BuildAssistantDeepTurnCompositionInput {
groundingCheck: AnswerGroundingCheck; groundingCheck: AnswerGroundingCheck;
followupUsage: AssistantFollowupUsage | null | undefined; followupUsage: AssistantFollowupUsage | null | undefined;
investigationState: InvestigationStateWithProblemUnits | null | undefined; investigationState: InvestigationStateWithProblemUnits | null | undefined;
companyAnchors: unknown; companyAnchors: CompanyAnchorSet | null;
normalizedPayload: NormalizeResponsePayload["normalized"]; normalizedPayload: NormalizeResponsePayload["normalized"];
featureAnswerPolicyV11: boolean; featureAnswerPolicyV11: boolean;
featureProblemCentricAnswerV1: boolean; featureProblemCentricAnswerV1: boolean;
@ -34,7 +34,7 @@ export interface AssistantDeepTurnCompositionOutput {
composition: ReturnType<typeof composeAssistantAnswer>; composition: ReturnType<typeof composeAssistantAnswer>;
} }
function toStringArray(value: unknown): string[] { function toStringArray(value: string[] | null | undefined): string[] {
if (!Array.isArray(value)) { if (!Array.isArray(value)) {
return []; return [];
} }
@ -43,11 +43,11 @@ function toStringArray(value: unknown): string[] {
.filter((item) => item.length > 0); .filter((item) => item.length > 0);
} }
function normalizeCompanyAnchorSet(value: unknown): CompanyAnchorSet | null { function normalizeCompanyAnchorSet(value: Partial<CompanyAnchorSet> | null | undefined): CompanyAnchorSet | null {
if (!value || typeof value !== "object" || Array.isArray(value)) { if (!value || typeof value !== "object" || Array.isArray(value)) {
return null; return null;
} }
const source = value as Record<string, unknown>; const source: Partial<CompanyAnchorSet> = value;
return { return {
contract_numbers: toStringArray(source.contract_numbers), contract_numbers: toStringArray(source.contract_numbers),
document_numbers: toStringArray(source.document_numbers), document_numbers: toStringArray(source.document_numbers),

View File

@ -61,14 +61,11 @@ export interface BuildAssistantDeepTurnRuntimeContextInput {
userMessage: string; userMessage: string;
companyAnchors: CompanyAnchorSet; companyAnchors: CompanyAnchorSet;
focusDomainHint: string | null; focusDomainHint: string | null;
primaryPeriod: unknown; primaryPeriod: ClaimBoundAnchorAudit["primary_period"] | null;
}) => ClaimBoundAnchorAudit; }) => ClaimBoundAnchorAudit;
resolveBusinessScopeFromLiveContext: (input: { resolveBusinessScopeFromLiveContext: (input: {
current: { current: AssistantBusinessScopeResolution;
route_summary_resolved: RouteHintSummary | null; temporalGuard: TemporalGuardAudit;
[key: string]: unknown;
};
temporalGuard: unknown;
claimType: string; claimType: string;
focusDomainHint: string | null; focusDomainHint: string | null;
userMessage: string; userMessage: string;
@ -83,7 +80,6 @@ export interface AssistantBusinessScopeResolution {
business_scope_resolved?: string[]; business_scope_resolved?: string[];
company_grounding_applied?: boolean; company_grounding_applied?: boolean;
scope_resolution_reason?: string[]; scope_resolution_reason?: string[];
[key: string]: unknown;
} }
export interface BuildAssistantDeepTurnRuntimeContextOutput { export interface BuildAssistantDeepTurnRuntimeContextOutput {

View File

@ -1,4 +1,5 @@
import type { AssistantConversationItem, AssistantDebugPayload, AssistantMessageResponsePayload, AssistantReplyType } from "../types/assistant"; import type { AssistantConversationItem, AssistantDebugPayload, AssistantMessageResponsePayload, AssistantReplyType } from "../types/assistant";
import type { DeepAnalysisLogDetails } from "./assistantMessageLogAssembler";
import { buildAssistantDeepTurnSuccessResponse } from "./assistantDeepTurnResponseBuilder"; import { buildAssistantDeepTurnSuccessResponse } from "./assistantDeepTurnResponseBuilder";
import type { CommitAssistantTurnAndLogOutput } from "./assistantTurnCommitRuntimeAdapter"; import type { CommitAssistantTurnAndLogOutput } from "./assistantTurnCommitRuntimeAdapter";
import { commitAssistantTurnAndLog } from "./assistantTurnCommitRuntimeAdapter"; import { commitAssistantTurnAndLog } from "./assistantTurnCommitRuntimeAdapter";
@ -9,7 +10,7 @@ export interface FinalizeAssistantDeepTurnInput {
replyType: AssistantReplyType; replyType: AssistantReplyType;
assistantItem: AssistantConversationItem; assistantItem: AssistantConversationItem;
debug: AssistantDebugPayload; debug: AssistantDebugPayload;
deepAnalysisLogDetails: Record<string, unknown>; deepAnalysisLogDetails: DeepAnalysisLogDetails;
appendItem: Parameters<typeof commitAssistantTurnAndLog>[0]["appendItem"]; appendItem: Parameters<typeof commitAssistantTurnAndLog>[0]["appendItem"];
getSession: Parameters<typeof commitAssistantTurnAndLog>[0]["getSession"]; getSession: Parameters<typeof commitAssistantTurnAndLog>[0]["getSession"];
persistSession: Parameters<typeof commitAssistantTurnAndLog>[0]["persistSession"]; persistSession: Parameters<typeof commitAssistantTurnAndLog>[0]["persistSession"];

View File

@ -1,8 +1,34 @@
import type { AssistantReplyType, AssistantRequirement, AnswerGroundingCheck, RequirementCoverageReport, UnifiedRetrievalResult } from "../types/assistant"; import type {
AssistantAddressRuntimeMetaForDeep,
AssistantDebugRouteRecord,
AssistantExecutionStateRecord,
AssistantFallbackType,
AssistantProblemAnswerMode,
AssistantReplyType,
AssistantRequirement,
AnswerGroundingCheck,
FaLiveRouteAuditDebug,
RbpLiveRouteAuditDebug,
RequirementCoverageReport,
UnifiedRetrievalResult
} from "../types/assistant";
import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer"; import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer";
import type { AnswerStructureV11 } from "../types/stage1Contracts"; import type { AnswerStructureV11 } from "../types/stage1Contracts";
import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits";
import type { AssistantDeepTurnPackagingInput } from "./assistantDeepTurnPackaging"; import type { AssistantDeepTurnPackagingInput } from "./assistantDeepTurnPackaging";
import type { AssistantFollowupUsage } from "./assistantFollowupUsage"; import type { AssistantFollowupUsage } from "./assistantFollowupUsage";
import type { ClaimBoundAnchorAudit, TargetedEvidenceAcquisitionAudit } from "./assistantClaimBoundEvidence";
import type { CompanyAnchorSet } from "./companyAnchorResolver";
import type {
DomainPolarityGuardAudit,
EvidenceAdmissibilityAudit,
GroundedAnswerEligibilityAudit,
TemporalGuardAudit
} from "./assistantRuntimeGuards";
import type {
AssistantRetrievalCallRecord,
AssistantRetrievalRawResultRecord
} from "./assistantDeepTurnRetrievalRuntimeAdapter";
export interface AssistantDeepTurnInputBuilderArgs { export interface AssistantDeepTurnInputBuilderArgs {
sessionId: string; sessionId: string;
@ -36,13 +62,13 @@ export interface AssistantDeepTurnInputBuilderArgs {
coverageEvaluationRequirements: AssistantRequirement[]; coverageEvaluationRequirements: AssistantRequirement[];
coverageReport: RequirementCoverageReport; coverageReport: RequirementCoverageReport;
groundingCheck: AnswerGroundingCheck; groundingCheck: AnswerGroundingCheck;
retrievalCalls: Array<Record<string, unknown>>; retrievalCalls: AssistantRetrievalCallRecord[];
retrievalResultsRaw: unknown[]; retrievalResultsRaw: AssistantRetrievalRawResultRecord[];
retrievalResults: UnifiedRetrievalResult[]; retrievalResults: UnifiedRetrievalResult[];
routesForDebug: Array<Record<string, unknown>>; routesForDebug: AssistantDebugRouteRecord[];
resolvedExecutionState: unknown; resolvedExecutionState: AssistantExecutionStateRecord[];
questionTypeClass: string; questionTypeClass: string;
companyAnchors: unknown; companyAnchors: CompanyAnchorSet | null;
runtimeAnalysisContext: { runtimeAnalysisContext: {
active: boolean; active: boolean;
as_of_date: string | null; as_of_date: string | null;
@ -57,42 +83,29 @@ export interface AssistantDeepTurnInputBuilderArgs {
company_grounding_applied?: boolean; company_grounding_applied?: boolean;
scope_resolution_reason?: string[]; scope_resolution_reason?: string[];
}; };
temporalGuard: Record<string, unknown>; temporalGuard: TemporalGuardAudit;
polarityAudit: Record<string, unknown>; polarityAudit: DomainPolarityGuardAudit;
claimAnchorAudit: Record<string, unknown>; claimAnchorAudit: ClaimBoundAnchorAudit;
targetedEvidenceAudit: unknown; targetedEvidenceAudit: TargetedEvidenceAcquisitionAudit;
evidenceAdmissibilityGateAudit: unknown; evidenceAdmissibilityGateAudit: EvidenceAdmissibilityAudit;
rbpLiveRouteAudit: unknown | null; rbpLiveRouteAudit: RbpLiveRouteAuditDebug | null;
faLiveRouteAudit: unknown | null; faLiveRouteAudit: FaLiveRouteAuditDebug | null;
groundedAnswerEligibilityGuard: Record<string, unknown>; groundedAnswerEligibilityGuard: GroundedAnswerEligibilityAudit;
followupStateUsage?: AssistantFollowupUsage | null; followupStateUsage?: AssistantFollowupUsage | null;
composition: { composition: {
reply_type: AssistantReplyType; reply_type: AssistantReplyType;
fallback_type: unknown; fallback_type: AssistantFallbackType;
answer_structure_v11?: AnswerStructureV11 | null; answer_structure_v11?: AnswerStructureV11 | null;
problem_centric_answer_applied?: boolean; problem_centric_answer_applied?: boolean;
problem_units_used_count?: number; problem_units_used_count?: number;
problem_answer_mode?: string; problem_answer_mode?: AssistantProblemAnswerMode;
problem_unit_ids_used?: unknown; problem_unit_ids_used?: string[];
}; };
safeAssistantReplyBase: string; safeAssistantReplyBase: string;
featureContractsV11: boolean; featureContractsV11: boolean;
featureAnswerPolicyV11: boolean; featureAnswerPolicyV11: boolean;
investigationStateSnapshot: unknown; investigationStateSnapshot: InvestigationStateWithProblemUnits | null;
addressRuntimeMetaForDeep: addressRuntimeMetaForDeep: AssistantAddressRuntimeMetaForDeep | null | undefined;
| {
attempted?: boolean;
applied?: boolean;
reason?: string | null;
provider?: string | null;
fallbackRuleHit?: string | null;
toolGateDecision?: string | null;
toolGateReason?: string | null;
predecomposeContract?: unknown;
orchestrationContract?: unknown;
}
| null
| undefined;
} }
export function buildAssistantDeepTurnPackagingInput(args: AssistantDeepTurnInputBuilderArgs): AssistantDeepTurnPackagingInput { export function buildAssistantDeepTurnPackagingInput(args: AssistantDeepTurnInputBuilderArgs): AssistantDeepTurnPackagingInput {

View File

@ -1,5 +1,6 @@
import type { AssistantMessageRequestPayload } from "../types/assistant"; import type { AssistantMessageRequestPayload } from "../types/assistant";
import type { NormalizeRequestPayload, NormalizeResponsePayload } from "../types/normalizer"; import type { NormalizeRequestPayload, NormalizeResponsePayload } from "../types/normalizer";
import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits";
import type { AssistantFollowupUsage } from "./assistantFollowupUsage"; import type { AssistantFollowupUsage } from "./assistantFollowupUsage";
export interface AssistantDeepTurnFollowupBinding { export interface AssistantDeepTurnFollowupBinding {
@ -13,11 +14,11 @@ export interface BuildAssistantDeepTurnNormalizationRuntimeInput {
payload: AssistantMessageRequestPayload; payload: AssistantMessageRequestPayload;
featureInvestigationStateV1: boolean; featureInvestigationStateV1: boolean;
featureStateFollowupBindingV1: boolean; featureStateFollowupBindingV1: boolean;
sessionInvestigationState: unknown | null | undefined; sessionInvestigationState: InvestigationStateWithProblemUnits | null | undefined;
buildFollowupStateBinding: (input: { buildFollowupStateBinding: (input: {
userMessage: string; userMessage: string;
payloadContext: NormalizeRequestPayload["context"] | undefined; payloadContext: NormalizeRequestPayload["context"] | undefined;
investigationState: unknown; investigationState: InvestigationStateWithProblemUnits;
}) => AssistantDeepTurnFollowupBinding; }) => AssistantDeepTurnFollowupBinding;
normalize: (payload: NormalizeRequestPayload) => Promise<NormalizeResponsePayload>; normalize: (payload: NormalizeRequestPayload) => Promise<NormalizeResponsePayload>;
} }
@ -31,14 +32,18 @@ export interface BuildAssistantDeepTurnNormalizationRuntimeOutput {
export async function buildAssistantDeepTurnNormalizationRuntime( export async function buildAssistantDeepTurnNormalizationRuntime(
input: BuildAssistantDeepTurnNormalizationRuntimeInput input: BuildAssistantDeepTurnNormalizationRuntimeInput
): Promise<BuildAssistantDeepTurnNormalizationRuntimeOutput> { ): Promise<BuildAssistantDeepTurnNormalizationRuntimeOutput> {
const followupBinding = const investigationState = input.sessionInvestigationState;
const canUseFollowupBinding =
input.featureInvestigationStateV1 && input.featureInvestigationStateV1 &&
input.featureStateFollowupBindingV1 && input.featureStateFollowupBindingV1 &&
Boolean(input.sessionInvestigationState) investigationState !== null &&
investigationState !== undefined;
const followupBinding =
canUseFollowupBinding
? input.buildFollowupStateBinding({ ? input.buildFollowupStateBinding({
userMessage: input.userMessage, userMessage: input.userMessage,
payloadContext: input.payload.context, payloadContext: input.payload.context,
investigationState: input.sessionInvestigationState as unknown investigationState
}) })
: { : {
normalizedQuestion: input.userMessage, normalizedQuestion: input.userMessage,

View File

@ -1,14 +1,28 @@
import type { import type {
AssistantAddressRuntimeMetaForDeep,
AssistantConversationItem, AssistantConversationItem,
AssistantDebugPayload, AssistantDebugPayload,
AssistantDebugRouteRecord,
AssistantExecutionStateRecord,
AssistantFallbackType,
AssistantProblemAnswerMode,
AssistantReplyType, AssistantReplyType,
AssistantRequirement, AssistantRequirement,
AnswerGroundingCheck, AnswerGroundingCheck,
FaLiveRouteAuditDebug,
RbpLiveRouteAuditDebug,
RequirementCoverageReport, RequirementCoverageReport,
UnifiedRetrievalResult UnifiedRetrievalResult
} from "../types/assistant"; } from "../types/assistant";
import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer"; import type {
NormalizeResponsePayload,
NormalizedQueryV2,
NormalizedQueryV2_0_1,
NormalizedQueryV2_0_2,
RouteHintSummary
} from "../types/normalizer";
import type { AnswerStructureV11 } from "../types/stage1Contracts"; import type { AnswerStructureV11 } from "../types/stage1Contracts";
import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits";
import { import {
assembleAssistantEvidenceBundle, assembleAssistantEvidenceBundle,
type AssistantEvidenceBundleAssembly type AssistantEvidenceBundleAssembly
@ -16,8 +30,23 @@ import {
import { assembleAssistantContractsBundleV1, type AssistantContractsBundleV1 } from "./assistantContractsBundleAssembler"; import { assembleAssistantContractsBundleV1, type AssistantContractsBundleV1 } from "./assistantContractsBundleAssembler";
import { buildDeepAnswerArtifacts, buildAssistantConversationItem, type DeepAnswerArtifacts } from "./assistantDeepResponseAssembler"; import { buildDeepAnswerArtifacts, buildAssistantConversationItem, type DeepAnswerArtifacts } from "./assistantDeepResponseAssembler";
import { buildDeepAnalysisDebugPayload } from "./assistantDebugPayloadAssembler"; import { buildDeepAnalysisDebugPayload } from "./assistantDebugPayloadAssembler";
import { buildDeepAnalysisProcessedLogDetails } from "./assistantMessageLogAssembler"; import {
buildDeepAnalysisProcessedLogDetails,
type DeepAnalysisLogDetails
} from "./assistantMessageLogAssembler";
import type { AssistantFollowupUsage } from "./assistantFollowupUsage"; import type { AssistantFollowupUsage } from "./assistantFollowupUsage";
import type { ClaimBoundAnchorAudit, TargetedEvidenceAcquisitionAudit } from "./assistantClaimBoundEvidence";
import type { CompanyAnchorSet } from "./companyAnchorResolver";
import type {
DomainPolarityGuardAudit,
EvidenceAdmissibilityAudit,
GroundedAnswerEligibilityAudit,
TemporalGuardAudit
} from "./assistantRuntimeGuards";
import type {
AssistantRetrievalCallRecord,
AssistantRetrievalRawResultRecord
} from "./assistantDeepTurnRetrievalRuntimeAdapter";
export interface AssistantDeepTurnPackagingInput { export interface AssistantDeepTurnPackagingInput {
sessionId: string; sessionId: string;
@ -51,13 +80,13 @@ export interface AssistantDeepTurnPackagingInput {
coverageEvaluationRequirements: AssistantRequirement[]; coverageEvaluationRequirements: AssistantRequirement[];
coverageReport: RequirementCoverageReport; coverageReport: RequirementCoverageReport;
groundingCheck: AnswerGroundingCheck; groundingCheck: AnswerGroundingCheck;
retrievalCalls: Array<Record<string, unknown>>; retrievalCalls: AssistantRetrievalCallRecord[];
retrievalResultsRaw: unknown[]; retrievalResultsRaw: AssistantRetrievalRawResultRecord[];
retrievalResults: UnifiedRetrievalResult[]; retrievalResults: UnifiedRetrievalResult[];
routesForDebug: Array<Record<string, unknown>>; routesForDebug: AssistantDebugRouteRecord[];
resolvedExecutionState: unknown; resolvedExecutionState: AssistantExecutionStateRecord[];
questionTypeClass: string; questionTypeClass: string;
companyAnchors: unknown; companyAnchors: CompanyAnchorSet | null;
runtimeAnalysisContext: { runtimeAnalysisContext: {
active: boolean; active: boolean;
as_of_date: string | null; as_of_date: string | null;
@ -72,42 +101,29 @@ export interface AssistantDeepTurnPackagingInput {
company_grounding_applied?: boolean; company_grounding_applied?: boolean;
scope_resolution_reason?: string[]; scope_resolution_reason?: string[];
}; };
temporalGuard: Record<string, unknown>; temporalGuard: TemporalGuardAudit;
polarityAudit: Record<string, unknown>; polarityAudit: DomainPolarityGuardAudit;
claimAnchorAudit: Record<string, unknown>; claimAnchorAudit: ClaimBoundAnchorAudit;
targetedEvidenceAudit: unknown; targetedEvidenceAudit: TargetedEvidenceAcquisitionAudit;
evidenceAdmissibilityGateAudit: unknown; evidenceAdmissibilityGateAudit: EvidenceAdmissibilityAudit;
rbpLiveRouteAudit: unknown | null; rbpLiveRouteAudit: RbpLiveRouteAuditDebug | null;
faLiveRouteAudit: unknown | null; faLiveRouteAudit: FaLiveRouteAuditDebug | null;
groundedAnswerEligibilityGuard: Record<string, unknown>; groundedAnswerEligibilityGuard: GroundedAnswerEligibilityAudit;
followupStateUsage: AssistantFollowupUsage | null; followupStateUsage: AssistantFollowupUsage | null;
composition: { composition: {
reply_type: AssistantReplyType; reply_type: AssistantReplyType;
fallback_type: unknown; fallback_type: AssistantFallbackType;
answer_structure_v11?: AnswerStructureV11 | null; answer_structure_v11?: AnswerStructureV11 | null;
problem_centric_answer_applied?: boolean; problem_centric_answer_applied?: boolean;
problem_units_used_count?: number; problem_units_used_count?: number;
problem_answer_mode?: string; problem_answer_mode?: AssistantProblemAnswerMode;
problem_unit_ids_used?: string[]; problem_unit_ids_used?: string[];
}; };
safeAssistantReplyBase: string; safeAssistantReplyBase: string;
featureContractsV11: boolean; featureContractsV11: boolean;
featureAnswerPolicyV11: boolean; featureAnswerPolicyV11: boolean;
investigationStateSnapshot: unknown; investigationStateSnapshot: InvestigationStateWithProblemUnits | null;
addressRuntimeMetaForDeep: addressRuntimeMetaForDeep: AssistantAddressRuntimeMetaForDeep | null | undefined;
| {
attempted?: boolean;
applied?: boolean;
reason?: string | null;
provider?: string | null;
fallbackRuleHit?: string | null;
toolGateDecision?: string | null;
toolGateReason?: string | null;
predecomposeContract?: unknown;
orchestrationContract?: unknown;
}
| null
| undefined;
} }
export interface AssistantDeepTurnPackagingOutput { export interface AssistantDeepTurnPackagingOutput {
@ -116,12 +132,25 @@ export interface AssistantDeepTurnPackagingOutput {
deepAnswerArtifacts: DeepAnswerArtifacts; deepAnswerArtifacts: DeepAnswerArtifacts;
debug: AssistantDebugPayload; debug: AssistantDebugPayload;
assistantItem: AssistantConversationItem; assistantItem: AssistantConversationItem;
deepAnalysisLogDetails: Record<string, unknown>; deepAnalysisLogDetails: DeepAnalysisLogDetails;
}
type NormalizedFragments =
| NormalizedQueryV2["fragments"]
| NormalizedQueryV2_0_1["fragments"]
| NormalizedQueryV2_0_2["fragments"];
function extractNormalizedFragments(normalized: NormalizeResponsePayload["normalized"] | null): NormalizedFragments {
if (!normalized || typeof normalized !== "object") {
return [];
}
const source = normalized as NormalizedQueryV2 | NormalizedQueryV2_0_1 | NormalizedQueryV2_0_2;
return Array.isArray(source.fragments) ? source.fragments : [];
} }
export function assembleAssistantDeepTurnPackaging(input: AssistantDeepTurnPackagingInput): AssistantDeepTurnPackagingOutput { export function assembleAssistantDeepTurnPackaging(input: AssistantDeepTurnPackagingInput): AssistantDeepTurnPackagingOutput {
const normalizedPayload = (input.normalized.normalized ?? null) as Record<string, unknown> | null; const normalizedPayload = input.normalized.normalized ?? null;
const normalizedFragments = Array.isArray(normalizedPayload?.["fragments"]) ? (normalizedPayload?.["fragments"] as unknown[]) : []; const normalizedFragments = extractNormalizedFragments(normalizedPayload);
const evidenceBundleAssembly = assembleAssistantEvidenceBundle({ const evidenceBundleAssembly = assembleAssistantEvidenceBundle({
retrievalCalls: input.retrievalCalls, retrievalCalls: input.retrievalCalls,
retrievalResults: input.retrievalResults retrievalResults: input.retrievalResults
@ -232,7 +261,7 @@ export function assembleAssistantDeepTurnPackaging(input: AssistantDeepTurnPacka
problem_units_used_count: input.composition.problem_units_used_count ?? 0, problem_units_used_count: input.composition.problem_units_used_count ?? 0,
problem_answer_mode: input.composition.problem_answer_mode ?? "stage1_policy_v11", problem_answer_mode: input.composition.problem_answer_mode ?? "stage1_policy_v11",
problem_unit_ids_used: Array.isArray(input.composition.problem_unit_ids_used) ? input.composition.problem_unit_ids_used : [], problem_unit_ids_used: Array.isArray(input.composition.problem_unit_ids_used) ? input.composition.problem_unit_ids_used : [],
fallback_type: input.composition.fallback_type as string fallback_type: input.composition.fallback_type
}, },
outcomeClassV1: contractsBundleV1.outcomeClassV1, outcomeClassV1: contractsBundleV1.outcomeClassV1,
assistantOrchestrationContractsV1: contractsBundleV1.assistantOrchestrationContractsV1, assistantOrchestrationContractsV1: contractsBundleV1.assistantOrchestrationContractsV1,

View File

@ -1,9 +1,14 @@
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import type { import type {
AssistantAddressRuntimeMetaForDeep,
AssistantConversationItem, AssistantConversationItem,
AssistantDebugPayload, AssistantDebugPayload,
AssistantDebugRouteRecord,
AssistantExecutionStateRecord,
AssistantRequirement, AssistantRequirement,
AnswerGroundingCheck, AnswerGroundingCheck,
FaLiveRouteAuditDebug,
RbpLiveRouteAuditDebug,
RequirementCoverageReport, RequirementCoverageReport,
UnifiedRetrievalResult UnifiedRetrievalResult
} from "../types/assistant"; } from "../types/assistant";
@ -13,6 +18,18 @@ import type { AssistantDeepTurnInputBuilderArgs } from "./assistantDeepTurnInput
import { buildAssistantDeepTurnPackagingInput } from "./assistantDeepTurnInputBuilder"; import { buildAssistantDeepTurnPackagingInput } from "./assistantDeepTurnInputBuilder";
import { assembleAssistantDeepTurnPackaging } from "./assistantDeepTurnPackaging"; import { assembleAssistantDeepTurnPackaging } from "./assistantDeepTurnPackaging";
import type { AssistantFollowupUsage } from "./assistantFollowupUsage"; import type { AssistantFollowupUsage } from "./assistantFollowupUsage";
import type { ClaimBoundAnchorAudit, TargetedEvidenceAcquisitionAudit } from "./assistantClaimBoundEvidence";
import type { CompanyAnchorSet } from "./companyAnchorResolver";
import type {
DomainPolarityGuardAudit,
EvidenceAdmissibilityAudit,
GroundedAnswerEligibilityAudit,
TemporalGuardAudit
} from "./assistantRuntimeGuards";
import type {
AssistantRetrievalCallRecord,
AssistantRetrievalRawResultRecord
} from "./assistantDeepTurnRetrievalRuntimeAdapter";
import type { import type {
AssistantAnalysisContextForContract, AssistantAnalysisContextForContract,
AssistantRuntimeAnalysisContextForPrePackaging AssistantRuntimeAnalysisContextForPrePackaging
@ -22,6 +39,7 @@ import {
buildAssistantInvestigationStateSnapshot, buildAssistantInvestigationStateSnapshot,
persistAssistantInvestigationStateSnapshot persistAssistantInvestigationStateSnapshot
} from "./assistantInvestigationStateRuntimeAdapter"; } from "./assistantInvestigationStateRuntimeAdapter";
import type { DeepAnalysisLogDetails } from "./assistantMessageLogAssembler";
type AssistantDeepTurnCompositionForPackaging = AssistantDeepTurnInputBuilderArgs["composition"] & { type AssistantDeepTurnCompositionForPackaging = AssistantDeepTurnInputBuilderArgs["composition"] & {
assistant_reply: string; assistant_reply: string;
@ -45,44 +63,31 @@ export interface AssistantDeepTurnPackagingRuntimeInput {
coverageEvaluationRequirements: AssistantRequirement[]; coverageEvaluationRequirements: AssistantRequirement[];
coverageReport: RequirementCoverageReport; coverageReport: RequirementCoverageReport;
groundingCheck: AnswerGroundingCheck; groundingCheck: AnswerGroundingCheck;
retrievalCalls: Array<Record<string, unknown>>; retrievalCalls: AssistantRetrievalCallRecord[];
retrievalResultsRaw: unknown[]; retrievalResultsRaw: AssistantRetrievalRawResultRecord[];
retrievalResults: UnifiedRetrievalResult[]; retrievalResults: UnifiedRetrievalResult[];
questionTypeClass: string; questionTypeClass: string;
companyAnchors: unknown; companyAnchors: CompanyAnchorSet | null;
runtimeAnalysisContext: AssistantDeepTurnInputBuilderArgs["runtimeAnalysisContext"]; runtimeAnalysisContext: AssistantDeepTurnInputBuilderArgs["runtimeAnalysisContext"];
businessScopeResolution: AssistantDeepTurnInputBuilderArgs["businessScopeResolution"]; businessScopeResolution: AssistantDeepTurnInputBuilderArgs["businessScopeResolution"];
temporalGuard: Record<string, unknown>; temporalGuard: TemporalGuardAudit;
polarityAudit: Record<string, unknown>; polarityAudit: DomainPolarityGuardAudit;
claimAnchorAudit: Record<string, unknown>; claimAnchorAudit: ClaimBoundAnchorAudit;
targetedEvidenceAudit: unknown; targetedEvidenceAudit: TargetedEvidenceAcquisitionAudit;
evidenceAdmissibilityGateAudit: unknown; evidenceAdmissibilityGateAudit: EvidenceAdmissibilityAudit;
rbpLiveRouteAudit: unknown | null; rbpLiveRouteAudit: RbpLiveRouteAuditDebug | null;
faLiveRouteAudit: unknown | null; faLiveRouteAudit: FaLiveRouteAuditDebug | null;
groundedAnswerEligibilityGuard: Record<string, unknown>; groundedAnswerEligibilityGuard: GroundedAnswerEligibilityAudit;
followupStateUsage?: AssistantFollowupUsage | null; followupStateUsage?: AssistantFollowupUsage | null;
followupApplied: boolean; followupApplied: boolean;
composition: AssistantDeepTurnCompositionForPackaging; composition: AssistantDeepTurnCompositionForPackaging;
featureContractsV11: boolean; featureContractsV11: boolean;
featureAnswerPolicyV11: boolean; featureAnswerPolicyV11: boolean;
previousInvestigationState: InvestigationStateWithProblemUnits | null | undefined; previousInvestigationState: InvestigationStateWithProblemUnits | null | undefined;
addressRuntimeMetaForDeep: addressRuntimeMetaForDeep: AssistantAddressRuntimeMetaForDeep | null | undefined;
| {
attempted?: boolean;
applied?: boolean;
reason?: string | null;
provider?: string | null;
fallbackRuleHit?: string | null;
toolGateDecision?: string | null;
toolGateReason?: string | null;
predecomposeContract?: unknown;
orchestrationContract?: unknown;
}
| null
| undefined;
extractDroppedIntentSegments: (normalizedPayload: NormalizeResponsePayload["normalized"]) => string[]; extractDroppedIntentSegments: (normalizedPayload: NormalizeResponsePayload["normalized"]) => string[];
buildDebugRoutes: (routeSummary: RouteHintSummary | null) => Array<Record<string, unknown>>; buildDebugRoutes: (routeSummary: RouteHintSummary | null) => AssistantDebugRouteRecord[];
extractExecutionState: (normalizedPayload: NormalizeResponsePayload["normalized"]) => unknown; extractExecutionState: (normalizedPayload: NormalizeResponsePayload["normalized"]) => AssistantExecutionStateRecord[];
sanitizeReply: (value: string, fallback?: string) => string; sanitizeReply: (value: string, fallback?: string) => string;
persistInvestigationState: (sessionId: string, snapshot: InvestigationStateWithProblemUnits) => void; persistInvestigationState: (sessionId: string, snapshot: InvestigationStateWithProblemUnits) => void;
nowIso?: () => string; nowIso?: () => string;
@ -99,13 +104,13 @@ export interface AssistantDeepTurnPackagingRuntimeOutput {
investigationStateSnapshot: InvestigationStateWithProblemUnits | null; investigationStateSnapshot: InvestigationStateWithProblemUnits | null;
droppedIntentSegments: string[]; droppedIntentSegments: string[];
analysisContextForContract: AssistantAnalysisContextForContract | null; analysisContextForContract: AssistantAnalysisContextForContract | null;
routesForDebug: Array<Record<string, unknown>>; routesForDebug: AssistantDebugRouteRecord[];
resolvedExecutionState: unknown; resolvedExecutionState: AssistantExecutionStateRecord[];
safeAssistantReplyBase: string; safeAssistantReplyBase: string;
safeAssistantReply: string; safeAssistantReply: string;
debug: AssistantDebugPayload; debug: AssistantDebugPayload;
assistantItem: AssistantConversationItem; assistantItem: AssistantConversationItem;
deepAnalysisLogDetails: Record<string, unknown>; deepAnalysisLogDetails: DeepAnalysisLogDetails;
} }
export function runAssistantDeepTurnPackagingRuntime( export function runAssistantDeepTurnPackagingRuntime(

View File

@ -1,6 +1,7 @@
import type { AssistantRequirement } from "../types/assistant"; import type { AssistantRequirement } from "../types/assistant";
import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer"; import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer";
import type { AssistantExecutionPlanItem } from "./assistantQueryPlanning"; import type { AssistantExecutionPlanItem } from "./assistantQueryPlanning";
import type { DomainPolarityGuardAudit, TemporalGuardAudit } from "./assistantRuntimeGuards";
export interface AssistantRequirementExtractionLike { export interface AssistantRequirementExtractionLike {
requirements: AssistantRequirement[]; requirements: AssistantRequirement[];
@ -9,7 +10,15 @@ export interface AssistantRequirementExtractionLike {
export interface AssistantPlanEnforcementAuditLike { export interface AssistantPlanEnforcementAuditLike {
executionPlan: AssistantExecutionPlanItem[]; executionPlan: AssistantExecutionPlanItem[];
audit: Record<string, unknown> | null; audit: AssistantLiveRoutePlanAudit | null;
}
export interface AssistantLiveRoutePlanAudit extends Record<string, unknown> {
required_live_calls: string[];
route_adjustments_applied: number;
rescued_no_route_fragments: number;
replaced_routes: string[];
route_gap_reason: string | null;
} }
export interface BuildAssistantDeepTurnExecutionPlanInput { export interface BuildAssistantDeepTurnExecutionPlanInput {
@ -17,8 +26,8 @@ export interface BuildAssistantDeepTurnExecutionPlanInput {
normalizedPayload: NormalizeResponsePayload["normalized"]; normalizedPayload: NormalizeResponsePayload["normalized"];
userMessage: string; userMessage: string;
claimType: string; claimType: string;
temporalGuard: unknown; temporalGuard: TemporalGuardAudit;
domainPolarityGuardInitial: unknown; domainPolarityGuardInitial: DomainPolarityGuardAudit;
extractRequirements: ( extractRequirements: (
routeSummary: RouteHintSummary | null, routeSummary: RouteHintSummary | null,
normalizedPayload: NormalizeResponsePayload["normalized"], normalizedPayload: NormalizeResponsePayload["normalized"],
@ -33,20 +42,20 @@ export interface BuildAssistantDeepTurnExecutionPlanInput {
enforceRbpLiveRoutePlan: (input: { enforceRbpLiveRoutePlan: (input: {
executionPlan: AssistantExecutionPlanItem[]; executionPlan: AssistantExecutionPlanItem[];
claimType: string; claimType: string;
temporalGuard: unknown; temporalGuard: TemporalGuardAudit;
}) => AssistantPlanEnforcementAuditLike; }) => AssistantPlanEnforcementAuditLike;
enforceFaLiveRoutePlan: (input: { enforceFaLiveRoutePlan: (input: {
executionPlan: AssistantExecutionPlanItem[]; executionPlan: AssistantExecutionPlanItem[];
claimType: string; claimType: string;
temporalGuard: unknown; temporalGuard: TemporalGuardAudit;
}) => AssistantPlanEnforcementAuditLike; }) => AssistantPlanEnforcementAuditLike;
applyTemporalHintToExecutionPlan: ( applyTemporalHintToExecutionPlan: (
executionPlan: AssistantExecutionPlanItem[], executionPlan: AssistantExecutionPlanItem[],
temporalGuard: unknown temporalGuard: TemporalGuardAudit
) => AssistantExecutionPlanItem[]; ) => AssistantExecutionPlanItem[];
applyPolarityHintToExecutionPlan: ( applyPolarityHintToExecutionPlan: (
executionPlan: AssistantExecutionPlanItem[], executionPlan: AssistantExecutionPlanItem[],
domainPolarityGuardInitial: unknown domainPolarityGuardInitial: DomainPolarityGuardAudit
) => AssistantExecutionPlanItem[]; ) => AssistantExecutionPlanItem[];
} }

View File

@ -1,4 +1,5 @@
import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer"; import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer";
import type { AssistantDebugRouteRecord, AssistantExecutionStateRecord } from "../types/assistant";
export interface AssistantRuntimeAnalysisContextForPrePackaging { export interface AssistantRuntimeAnalysisContextForPrePackaging {
active: boolean; active: boolean;
@ -23,16 +24,16 @@ export interface BuildAssistantDeepTurnPrePackagingContextInput {
runtimeAnalysisContext: AssistantRuntimeAnalysisContextForPrePackaging; runtimeAnalysisContext: AssistantRuntimeAnalysisContextForPrePackaging;
assistantReply: string; assistantReply: string;
extractDroppedIntentSegments: (normalizedPayload: NormalizeResponsePayload["normalized"]) => string[]; extractDroppedIntentSegments: (normalizedPayload: NormalizeResponsePayload["normalized"]) => string[];
buildDebugRoutes: (routeSummary: RouteHintSummary | null) => Array<Record<string, unknown>>; buildDebugRoutes: (routeSummary: RouteHintSummary | null) => AssistantDebugRouteRecord[];
extractExecutionState: (normalizedPayload: NormalizeResponsePayload["normalized"]) => unknown; extractExecutionState: (normalizedPayload: NormalizeResponsePayload["normalized"]) => AssistantExecutionStateRecord[];
sanitizeReply: (value: string, fallback?: string) => string; sanitizeReply: (value: string, fallback?: string) => string;
} }
export interface AssistantDeepTurnPrePackagingContext { export interface AssistantDeepTurnPrePackagingContext {
droppedIntentSegments: string[]; droppedIntentSegments: string[];
analysisContextForContract: AssistantAnalysisContextForContract | null; analysisContextForContract: AssistantAnalysisContextForContract | null;
routesForDebug: Array<Record<string, unknown>>; routesForDebug: AssistantDebugRouteRecord[];
resolvedExecutionState: unknown; resolvedExecutionState: AssistantExecutionStateRecord[];
safeAssistantReplyBase: string; safeAssistantReplyBase: string;
} }

View File

@ -1,4 +1,12 @@
import type { AssistantDebugPayload, AssistantMessageResponsePayload } from "../types/assistant"; import type {
AssistantAddressRuntimeMetaForDeep,
AssistantDebugPayload,
AssistantDebugRouteRecord,
AssistantExecutionStateRecord,
AssistantMessageResponsePayload,
FaLiveRouteAuditDebug,
RbpLiveRouteAuditDebug
} from "../types/assistant";
import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer"; import type { NormalizeResponsePayload, RouteHintSummary } from "../types/normalizer";
import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits"; import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits";
import { import {
@ -7,10 +15,23 @@ import {
type AssistantDeepTurnPackagingRuntimeOutput type AssistantDeepTurnPackagingRuntimeOutput
} from "./assistantDeepTurnPackagingRuntimeAdapter"; } from "./assistantDeepTurnPackagingRuntimeAdapter";
import type { RunAssistantDeepTurnAnalysisRuntimeOutput } from "./assistantDeepTurnAnalysisRuntimeAdapter"; import type { RunAssistantDeepTurnAnalysisRuntimeOutput } from "./assistantDeepTurnAnalysisRuntimeAdapter";
import type { AssistantExecutionPlanItem } from "./assistantQueryPlanning";
import { import {
finalizeAssistantDeepTurn, finalizeAssistantDeepTurn,
type FinalizeAssistantDeepTurnInput type FinalizeAssistantDeepTurnInput
} from "./assistantDeepTurnFinalizeRuntimeAdapter"; } from "./assistantDeepTurnFinalizeRuntimeAdapter";
import type { ClaimBoundAnchorAudit, TargetedEvidenceAcquisitionAudit } from "./assistantClaimBoundEvidence";
import type { CompanyAnchorSet } from "./companyAnchorResolver";
import type {
DomainPolarityGuardAudit,
EvidenceAdmissibilityAudit,
GroundedAnswerEligibilityAudit,
TemporalGuardAudit
} from "./assistantRuntimeGuards";
import type {
AssistantRetrievalCallRecord,
AssistantRetrievalRawResultRecord
} from "./assistantDeepTurnRetrievalRuntimeAdapter";
export interface RunAssistantDeepTurnResponseRuntimeInput { export interface RunAssistantDeepTurnResponseRuntimeInput {
featureInvestigationStateV1: boolean; featureInvestigationStateV1: boolean;
@ -27,16 +48,16 @@ export interface RunAssistantDeepTurnResponseRuntimeInput {
}; };
normalizedQuestion: string; normalizedQuestion: string;
routeSummary: RouteHintSummary | null; routeSummary: RouteHintSummary | null;
executionPlan: unknown[]; executionPlan: AssistantExecutionPlanItem[];
requirementExtractionRequirements: AssistantDeepTurnPackagingRuntimeInput["requirementExtractionRequirements"]; requirementExtractionRequirements: AssistantDeepTurnPackagingRuntimeInput["requirementExtractionRequirements"];
coverageEvaluationRequirements: AssistantDeepTurnPackagingRuntimeInput["coverageEvaluationRequirements"]; coverageEvaluationRequirements: AssistantDeepTurnPackagingRuntimeInput["coverageEvaluationRequirements"];
coverageReport: AssistantDeepTurnPackagingRuntimeInput["coverageReport"]; coverageReport: AssistantDeepTurnPackagingRuntimeInput["coverageReport"];
groundingCheck: AssistantDeepTurnPackagingRuntimeInput["groundingCheck"]; groundingCheck: AssistantDeepTurnPackagingRuntimeInput["groundingCheck"];
retrievalCalls: unknown[]; retrievalCalls: AssistantRetrievalCallRecord[];
retrievalResultsRaw: unknown[]; retrievalResultsRaw: AssistantRetrievalRawResultRecord[];
retrievalResults: AssistantDeepTurnPackagingRuntimeInput["retrievalResults"]; retrievalResults: AssistantDeepTurnPackagingRuntimeInput["retrievalResults"];
questionTypeClass: AssistantDeepTurnPackagingRuntimeInput["questionTypeClass"]; questionTypeClass: AssistantDeepTurnPackagingRuntimeInput["questionTypeClass"];
companyAnchors: RunAssistantDeepTurnAnalysisRuntimeOutput["companyAnchors"]; companyAnchors: CompanyAnchorSet | null;
runtimeAnalysisContext: { runtimeAnalysisContext: {
active: boolean; active: boolean;
as_of_date: string | null; as_of_date: string | null;
@ -46,22 +67,22 @@ export interface RunAssistantDeepTurnResponseRuntimeInput {
snapshot_mode?: "auto" | "force_snapshot" | "force_live"; snapshot_mode?: "auto" | "force_snapshot" | "force_live";
}; };
businessScopeResolution: RunAssistantDeepTurnAnalysisRuntimeOutput["businessScopeResolution"]; businessScopeResolution: RunAssistantDeepTurnAnalysisRuntimeOutput["businessScopeResolution"];
temporalGuard: unknown; temporalGuard: TemporalGuardAudit;
polarityAudit: unknown; polarityAudit: DomainPolarityGuardAudit;
claimAnchorAudit: unknown; claimAnchorAudit: ClaimBoundAnchorAudit;
targetedEvidenceAudit: unknown; targetedEvidenceAudit: TargetedEvidenceAcquisitionAudit;
evidenceAdmissibilityGateAudit: unknown; evidenceAdmissibilityGateAudit: EvidenceAdmissibilityAudit;
rbpLiveRouteAudit: unknown; rbpLiveRouteAudit: RbpLiveRouteAuditDebug | null;
faLiveRouteAudit: unknown; faLiveRouteAudit: FaLiveRouteAuditDebug | null;
groundedAnswerEligibilityGuard: unknown; groundedAnswerEligibilityGuard: GroundedAnswerEligibilityAudit;
followupStateUsage: AssistantDeepTurnPackagingRuntimeInput["followupStateUsage"]; followupStateUsage: AssistantDeepTurnPackagingRuntimeInput["followupStateUsage"];
followupApplied: boolean; followupApplied: boolean;
composition: AssistantDeepTurnPackagingRuntimeInput["composition"]; composition: AssistantDeepTurnPackagingRuntimeInput["composition"];
previousInvestigationState: AssistantDeepTurnPackagingRuntimeInput["previousInvestigationState"]; previousInvestigationState: AssistantDeepTurnPackagingRuntimeInput["previousInvestigationState"];
addressRuntimeMetaForDeep: AssistantDeepTurnPackagingRuntimeInput["addressRuntimeMetaForDeep"]; addressRuntimeMetaForDeep: AssistantDeepTurnPackagingRuntimeInput["addressRuntimeMetaForDeep"];
extractDroppedIntentSegments: (normalizedPayload: NormalizeResponsePayload["normalized"]) => string[]; extractDroppedIntentSegments: (normalizedPayload: NormalizeResponsePayload["normalized"]) => string[];
buildDebugRoutes: (routeSummary: RouteHintSummary | null) => Array<Record<string, unknown>>; buildDebugRoutes: (routeSummary: RouteHintSummary | null) => AssistantDebugRouteRecord[];
extractExecutionState: (normalizedPayload: NormalizeResponsePayload["normalized"]) => unknown[]; extractExecutionState: (normalizedPayload: NormalizeResponsePayload["normalized"]) => AssistantExecutionStateRecord[];
sanitizeReply: (value: string, fallback?: string) => string; sanitizeReply: (value: string, fallback?: string) => string;
persistInvestigationState: (sessionId: string, snapshot: InvestigationStateWithProblemUnits) => void; persistInvestigationState: (sessionId: string, snapshot: InvestigationStateWithProblemUnits) => void;
messageIdFactory: () => string; messageIdFactory: () => string;
@ -81,14 +102,7 @@ export interface RunAssistantDeepTurnResponseRuntimeOutput {
debug: AssistantDebugPayload; debug: AssistantDebugPayload;
} }
function toRecordObject(value: unknown): Record<string, unknown> | null { function toNullableString(value: string | null | undefined): string | null {
if (!value || typeof value !== "object") {
return null;
}
return value as Record<string, unknown>;
}
function toNullableString(value: unknown): string | null {
if (typeof value !== "string") { if (typeof value !== "string") {
return null; return null;
} }
@ -96,7 +110,7 @@ function toNullableString(value: unknown): string | null {
return trimmed.length > 0 ? trimmed : null; return trimmed.length > 0 ? trimmed : null;
} }
function toStringArray(value: unknown): string[] { function toStringArray(value: string[] | null | undefined): string[] {
if (!Array.isArray(value)) { if (!Array.isArray(value)) {
return []; return [];
} }
@ -105,44 +119,30 @@ function toStringArray(value: unknown): string[] {
.filter((item) => item.length > 0); .filter((item) => item.length > 0);
} }
function toSnapshotMode(value: unknown): "auto" | "force_snapshot" | "force_live" { function toSnapshotMode(value: string | null | undefined): "auto" | "force_snapshot" | "force_live" {
return value === "force_snapshot" || value === "force_live" ? value : "auto"; return value === "force_snapshot" || value === "force_live" ? value : "auto";
} }
function normalizeExecutionPlan(value: unknown[]): AssistantDeepTurnPackagingRuntimeInput["executionPlan"] { function normalizeExecutionPlan(
value: AssistantExecutionPlanItem[]
): AssistantDeepTurnPackagingRuntimeInput["executionPlan"] {
if (!Array.isArray(value)) { if (!Array.isArray(value)) {
return []; return [];
} }
return value.map((item, index) => { return value.map((item, index) => ({
const source = toRecordObject(item); fragment_id: toNullableString(item.fragment_id) ?? `fragment_${index + 1}`,
return { requirement_ids: toStringArray(item.requirement_ids),
fragment_id: toNullableString(source?.fragment_id) ?? `fragment_${index + 1}`, route: toNullableString(item.route) ?? "unknown_route",
requirement_ids: toStringArray(source?.requirement_ids), should_execute: Boolean(item.should_execute),
route: toNullableString(source?.route) ?? "unknown_route", no_route_reason: toNullableString(item.no_route_reason ?? null),
should_execute: Boolean(source?.should_execute), clarification_reason: toNullableString(item.clarification_reason ?? null)
no_route_reason: toNullableString(source?.no_route_reason), }));
clarification_reason: toNullableString(source?.clarification_reason)
};
});
}
function normalizeRecordArray(value: unknown[]): Array<Record<string, unknown>> {
if (!Array.isArray(value)) {
return [];
}
return value
.map((item) => toRecordObject(item))
.filter((item): item is Record<string, unknown> => Boolean(item));
}
function normalizeRecord(value: unknown): Record<string, unknown> {
return toRecordObject(value) ?? {};
} }
function normalizeRuntimeAnalysisContext( function normalizeRuntimeAnalysisContext(
value: unknown value: RunAssistantDeepTurnResponseRuntimeInput["runtimeAnalysisContext"] | null | undefined
): AssistantDeepTurnPackagingRuntimeInput["runtimeAnalysisContext"] { ): AssistantDeepTurnPackagingRuntimeInput["runtimeAnalysisContext"] {
const source = toRecordObject(value); const source = value;
return { return {
active: Boolean(source?.active), active: Boolean(source?.active),
as_of_date: toNullableString(source?.as_of_date), as_of_date: toNullableString(source?.as_of_date),
@ -154,9 +154,9 @@ function normalizeRuntimeAnalysisContext(
} }
function normalizeBusinessScopeResolution( function normalizeBusinessScopeResolution(
value: unknown value: RunAssistantDeepTurnAnalysisRuntimeOutput["businessScopeResolution"] | null | undefined
): AssistantDeepTurnPackagingRuntimeInput["businessScopeResolution"] { ): AssistantDeepTurnPackagingRuntimeInput["businessScopeResolution"] {
const source = toRecordObject(value); const source = value;
return { return {
business_scope_raw: toStringArray(source?.business_scope_raw), business_scope_raw: toStringArray(source?.business_scope_raw),
business_scope_resolved: toStringArray(source?.business_scope_resolved), business_scope_resolved: toStringArray(source?.business_scope_resolved),
@ -166,9 +166,9 @@ function normalizeBusinessScopeResolution(
} }
function normalizeAddressRuntimeMetaForDeep( function normalizeAddressRuntimeMetaForDeep(
value: unknown value: AssistantAddressRuntimeMetaForDeep | null | undefined
): AssistantDeepTurnPackagingRuntimeInput["addressRuntimeMetaForDeep"] { ): AssistantAddressRuntimeMetaForDeep | null {
const source = toRecordObject(value); const source = value;
if (!source) { if (!source) {
return null; return null;
} }
@ -180,8 +180,8 @@ function normalizeAddressRuntimeMetaForDeep(
fallbackRuleHit: toNullableString(source.fallbackRuleHit), fallbackRuleHit: toNullableString(source.fallbackRuleHit),
toolGateDecision: toNullableString(source.toolGateDecision), toolGateDecision: toNullableString(source.toolGateDecision),
toolGateReason: toNullableString(source.toolGateReason), toolGateReason: toNullableString(source.toolGateReason),
predecomposeContract: toRecordObject(source.predecomposeContract), predecomposeContract: source.predecomposeContract ?? null,
orchestrationContract: toRecordObject(source.orchestrationContract) orchestrationContract: source.orchestrationContract ?? null
}; };
} }
@ -204,21 +204,21 @@ export function runAssistantDeepTurnResponseRuntime(
coverageEvaluationRequirements: input.coverageEvaluationRequirements, coverageEvaluationRequirements: input.coverageEvaluationRequirements,
coverageReport: input.coverageReport, coverageReport: input.coverageReport,
groundingCheck: input.groundingCheck, groundingCheck: input.groundingCheck,
retrievalCalls: normalizeRecordArray(input.retrievalCalls), retrievalCalls: input.retrievalCalls,
retrievalResultsRaw: Array.isArray(input.retrievalResultsRaw) ? input.retrievalResultsRaw : [], retrievalResultsRaw: input.retrievalResultsRaw,
retrievalResults: input.retrievalResults, retrievalResults: input.retrievalResults,
questionTypeClass: input.questionTypeClass, questionTypeClass: input.questionTypeClass,
companyAnchors: input.companyAnchors, companyAnchors: input.companyAnchors,
runtimeAnalysisContext: normalizeRuntimeAnalysisContext(input.runtimeAnalysisContext), runtimeAnalysisContext: normalizeRuntimeAnalysisContext(input.runtimeAnalysisContext),
businessScopeResolution: normalizeBusinessScopeResolution(input.businessScopeResolution), businessScopeResolution: normalizeBusinessScopeResolution(input.businessScopeResolution),
temporalGuard: normalizeRecord(input.temporalGuard), temporalGuard: input.temporalGuard,
polarityAudit: normalizeRecord(input.polarityAudit), polarityAudit: input.polarityAudit,
claimAnchorAudit: normalizeRecord(input.claimAnchorAudit), claimAnchorAudit: input.claimAnchorAudit,
targetedEvidenceAudit: input.targetedEvidenceAudit, targetedEvidenceAudit: input.targetedEvidenceAudit,
evidenceAdmissibilityGateAudit: input.evidenceAdmissibilityGateAudit, evidenceAdmissibilityGateAudit: input.evidenceAdmissibilityGateAudit,
rbpLiveRouteAudit: input.rbpLiveRouteAudit ?? null, rbpLiveRouteAudit: input.rbpLiveRouteAudit ?? null,
faLiveRouteAudit: input.faLiveRouteAudit ?? null, faLiveRouteAudit: input.faLiveRouteAudit ?? null,
groundedAnswerEligibilityGuard: normalizeRecord(input.groundedAnswerEligibilityGuard), groundedAnswerEligibilityGuard: input.groundedAnswerEligibilityGuard,
followupStateUsage: input.followupStateUsage, followupStateUsage: input.followupStateUsage,
followupApplied: input.followupApplied, followupApplied: input.followupApplied,
composition: input.composition, composition: input.composition,

View File

@ -18,10 +18,43 @@ export interface AssistantRetrievalCallRecord {
reason: string | null; reason: string | null;
} }
export type AssistantRetrievalScalar = string | number | boolean | null;
export type AssistantRetrievalFieldValue =
| AssistantRetrievalScalar
| AssistantRetrievalFieldValue[]
| { [key: string]: AssistantRetrievalFieldValue };
export type AssistantRetrievalRecord = Record<string, AssistantRetrievalFieldValue>;
export interface AssistantRetrievalRawResultLike {
status: "ok" | "empty" | "partial" | "error";
result_type: "list" | "summary" | "object" | "chain" | "ranking";
items: AssistantRetrievalRecord[];
summary: AssistantRetrievalRecord;
evidence: AssistantRetrievalRecord[];
why_included: string[];
selection_reason: string[];
risk_factors: string[];
business_interpretation: string[];
confidence: "high" | "medium" | "low";
limitations: string[];
errors: string[];
}
export type AssistantRetrievalRawListItem = AssistantRetrievalFieldValue;
export type AssistantRetrievalRawList = AssistantRetrievalFieldValue[];
export type AssistantRetrievalRawResult =
| AssistantRetrievalRawResultLike
| AssistantRetrievalRawList
| string
| number
| boolean
| null;
export interface AssistantRetrievalRawResultRecord { export interface AssistantRetrievalRawResultRecord {
fragment_id: string; fragment_id: string;
route: string; route: string;
raw_result: unknown; raw_result: AssistantRetrievalRawResult;
} }
export interface AssistantDeepTurnRetrievalExecutionInput { export interface AssistantDeepTurnRetrievalExecutionInput {
@ -33,7 +66,7 @@ export interface AssistantDeepTurnRetrievalExecutionInput {
options: { options: {
temporalHint: AssistantLiveTemporalHint | null; temporalHint: AssistantLiveTemporalHint | null;
} }
) => Promise<unknown>; ) => Promise<AssistantRetrievalRawResult>;
mapNoRouteReason: (reason: string | null) => string; mapNoRouteReason: (reason: string | null) => string;
buildSkippedResult: (item: AssistantExecutionPlanItem) => UnifiedRetrievalResult; buildSkippedResult: (item: AssistantExecutionPlanItem) => UnifiedRetrievalResult;
normalizeRetrievalResultFn?: typeof normalizeRetrievalResult; normalizeRetrievalResultFn?: typeof normalizeRetrievalResult;
@ -45,7 +78,7 @@ export interface AssistantDeepTurnRetrievalExecutionOutput {
retrievalResults: UnifiedRetrievalResult[]; retrievalResults: UnifiedRetrievalResult[];
} }
function buildRouteExecutorErrorRawResult(route: string, message: string): Record<string, unknown> { function buildRouteExecutorErrorRawResult(route: string, message: string): AssistantRetrievalRawResult {
return { return {
status: "error", status: "error",
result_type: "summary", result_type: "summary",
@ -64,6 +97,24 @@ function buildRouteExecutorErrorRawResult(route: string, message: string): Recor
}; };
} }
function normalizeRawResult(
value: AssistantRetrievalRawResult | object | null | undefined
): AssistantRetrievalRawResult {
if (value === null || value === undefined) {
return null;
}
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return value;
}
if (Array.isArray(value)) {
return value as AssistantRetrievalRawList;
}
if (typeof value === "object") {
return value as AssistantRetrievalRawResultLike;
}
return null;
}
export async function executeAssistantDeepTurnRetrievalPlan( export async function executeAssistantDeepTurnRetrievalPlan(
input: AssistantDeepTurnRetrievalExecutionInput input: AssistantDeepTurnRetrievalExecutionInput
): Promise<AssistantDeepTurnRetrievalExecutionOutput> { ): Promise<AssistantDeepTurnRetrievalExecutionOutput> {
@ -99,13 +150,14 @@ export async function executeAssistantDeepTurnRetrievalPlan(
const raw = await input.executeRouteRuntime(planItem.route, planItem.fragment_text, { const raw = await input.executeRouteRuntime(planItem.route, planItem.fragment_text, {
temporalHint: input.liveTemporalHint temporalHint: input.liveTemporalHint
}); });
const normalizedRaw = normalizeRawResult(raw);
retrievalResultsRaw.push({ retrievalResultsRaw.push({
fragment_id: planItem.fragment_id, fragment_id: planItem.fragment_id,
route: planItem.route, route: planItem.route,
raw_result: raw raw_result: normalizedRaw
}); });
retrievalResults.push( retrievalResults.push(
normalizeRetrievalResultSafe(planItem.fragment_id, planItem.requirement_ids, planItem.route, raw) normalizeRetrievalResultSafe(planItem.fragment_id, planItem.requirement_ids, planItem.route, normalizedRaw)
); );
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : String(error); const message = error instanceof Error ? error.message : String(error);

View File

@ -3,6 +3,7 @@ import {
buildAssistantEvidenceBundleContractV1, buildAssistantEvidenceBundleContractV1,
type AssistantEvidenceBundleContractV1 type AssistantEvidenceBundleContractV1
} from "./assistantOrchestrationContracts"; } from "./assistantOrchestrationContracts";
import type { AssistantRetrievalCallRecord } from "./assistantDeepTurnRetrievalRuntimeAdapter";
type RetrievalStatusItem = AssistantDebugPayload["retrieval_status"][number]; type RetrievalStatusItem = AssistantDebugPayload["retrieval_status"][number];
@ -22,7 +23,7 @@ function buildRetrievalStatus(retrievalResults: UnifiedRetrievalResult[]): Retri
} }
export function assembleAssistantEvidenceBundle(input: { export function assembleAssistantEvidenceBundle(input: {
retrievalCalls: Array<Record<string, unknown>>; retrievalCalls: AssistantRetrievalCallRecord[];
retrievalResults: UnifiedRetrievalResult[]; retrievalResults: UnifiedRetrievalResult[];
}): AssistantEvidenceBundleAssembly { }): AssistantEvidenceBundleAssembly {
const retrievalResults = Array.isArray(input.retrievalResults) ? input.retrievalResults : []; const retrievalResults = Array.isArray(input.retrievalResults) ? input.retrievalResults : [];

View File

@ -1,4 +1,33 @@
import type { AnswerGroundingCheck, RequirementCoverageReport } from "../types/assistant"; import type {
AssistantDebugRouteRecord,
AssistantExecutionStateRecord,
AssistantFallbackType,
AssistantProblemAnswerMode,
AssistantReplyType,
AssistantRequirement,
UnifiedRetrievalResult,
AnswerGroundingCheck,
FaLiveRouteAuditDebug,
RbpLiveRouteAuditDebug,
RequirementCoverageReport
} from "../types/assistant";
import type { AnswerStructureV11 } from "../types/stage1Contracts";
import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits";
import type { ClaimBoundAnchorAudit, TargetedEvidenceAcquisitionAudit } from "./assistantClaimBoundEvidence";
import type { AssistantContractsBundleV1 } from "./assistantContractsBundleAssembler";
import type { AssistantOutcomeClassV1 } from "./assistantOrchestrationContracts";
import type { AssistantFollowupUsage } from "./assistantFollowupUsage";
import type { CompanyAnchorSet } from "./companyAnchorResolver";
import type {
AssistantRetrievalCallRecord,
AssistantRetrievalRawResultRecord
} from "./assistantDeepTurnRetrievalRuntimeAdapter";
import type {
DomainPolarityGuardAudit,
EvidenceAdmissibilityAudit,
GroundedAnswerEligibilityAudit,
TemporalGuardAudit
} from "./assistantRuntimeGuards";
export interface DeepAnalysisMessageLogDetailsInput { export interface DeepAnalysisMessageLogDetailsInput {
sessionId: string; sessionId: string;
@ -6,18 +35,18 @@ export interface DeepAnalysisMessageLogDetailsInput {
userMessage: string; userMessage: string;
normalizerOutput: unknown; normalizerOutput: unknown;
executionPlan: Array<Record<string, unknown>>; executionPlan: Array<Record<string, unknown>>;
resolvedExecutionState: unknown; resolvedExecutionState: AssistantExecutionStateRecord[];
routes: Array<Record<string, unknown>>; routes: AssistantDebugRouteRecord[];
retrievalCalls: Array<Record<string, unknown>>; retrievalCalls: AssistantRetrievalCallRecord[];
retrievalResultsRaw: unknown[]; retrievalResultsRaw: AssistantRetrievalRawResultRecord[];
retrievalResultsNormalized: unknown[]; retrievalResultsNormalized: UnifiedRetrievalResult[];
requirementsExtracted: unknown[]; requirementsExtracted: AssistantRequirement[];
coverageReport: RequirementCoverageReport; coverageReport: RequirementCoverageReport;
groundingCheck: AnswerGroundingCheck; groundingCheck: AnswerGroundingCheck;
replyType: string; replyType: AssistantReplyType;
droppedIntentSegments: string[]; droppedIntentSegments: string[];
questionTypeClass: string; questionTypeClass: string;
companyAnchors: unknown; companyAnchors: CompanyAnchorSet | null;
runtimeAnalysisContext: { runtimeAnalysisContext: {
active: boolean; active: boolean;
as_of_date: string | null; as_of_date: string | null;
@ -32,26 +61,26 @@ export interface DeepAnalysisMessageLogDetailsInput {
company_grounding_applied?: boolean; company_grounding_applied?: boolean;
scope_resolution_reason?: string[]; scope_resolution_reason?: string[];
}; };
temporalGuard: Record<string, unknown>; temporalGuard: TemporalGuardAudit;
polarityAudit: Record<string, unknown>; polarityAudit: DomainPolarityGuardAudit;
claimAnchorAudit: Record<string, unknown>; claimAnchorAudit: ClaimBoundAnchorAudit;
targetedEvidenceAudit: unknown; targetedEvidenceAudit: TargetedEvidenceAcquisitionAudit;
evidenceAdmissibilityGateAudit: unknown; evidenceAdmissibilityGateAudit: EvidenceAdmissibilityAudit;
rbpLiveRouteAudit: unknown | null; rbpLiveRouteAudit: RbpLiveRouteAuditDebug | null;
faLiveRouteAudit: unknown | null; faLiveRouteAudit: FaLiveRouteAuditDebug | null;
groundedAnswerEligibilityGuard: Record<string, unknown>; groundedAnswerEligibilityGuard: GroundedAnswerEligibilityAudit;
followupStateUsage: unknown | null; followupStateUsage: AssistantFollowupUsage | null;
compositionDebug: { compositionDebug: {
problem_centric_answer_applied?: boolean; problem_centric_answer_applied?: boolean;
problem_units_used_count?: number; problem_units_used_count?: number;
problem_answer_mode?: string; problem_answer_mode?: AssistantProblemAnswerMode;
problem_unit_ids_used?: string[]; problem_unit_ids_used?: string[];
fallback_type?: string; fallback_type?: AssistantFallbackType;
}; };
outcomeClassV1: unknown; outcomeClassV1: AssistantOutcomeClassV1;
assistantOrchestrationContractsV1: unknown; assistantOrchestrationContractsV1: AssistantContractsBundleV1["assistantOrchestrationContractsV1"];
answerStructureV11: unknown; answerStructureV11: AnswerStructureV11 | null;
investigationStateSnapshot: unknown; investigationStateSnapshot: InvestigationStateWithProblemUnits | null;
assistantReply: string; assistantReply: string;
traceId: string; traceId: string;
} }
@ -77,7 +106,9 @@ function resolveCoverageStatus(coverageReport: RequirementCoverageReport): "full
: "partial_or_limited"; : "partial_or_limited";
} }
export function buildDeepAnalysisProcessedLogDetails(input: DeepAnalysisMessageLogDetailsInput): Record<string, unknown> { export type DeepAnalysisLogDetails = Record<string, unknown>;
export function buildDeepAnalysisProcessedLogDetails(input: DeepAnalysisMessageLogDetailsInput): DeepAnalysisLogDetails {
const analysisContext = toAnalysisContext(input.runtimeAnalysisContext); const analysisContext = toAnalysisContext(input.runtimeAnalysisContext);
return { return {
session_id: input.sessionId, session_id: input.sessionId,

View File

@ -6,6 +6,7 @@ import type {
UnifiedRetrievalResult UnifiedRetrievalResult
} from "../types/assistant"; } from "../types/assistant";
import type { NormalizedPayload, RouteHintSummary } from "../types/normalizer"; import type { NormalizedPayload, RouteHintSummary } from "../types/normalizer";
import type { AssistantRetrievalCallRecord } from "./assistantDeepTurnRetrievalRuntimeAdapter";
export type AssistantOutcomeClassV1 = export type AssistantOutcomeClassV1 =
| "FULLY_ANSWERED" | "FULLY_ANSWERED"
@ -241,7 +242,7 @@ export function buildAssistantExecutionPlanContractV1(input: {
} }
export function buildAssistantEvidenceBundleContractV1(input: { export function buildAssistantEvidenceBundleContractV1(input: {
retrievalCalls: Array<Record<string, unknown>>; retrievalCalls: AssistantRetrievalCallRecord[];
retrievalResults: UnifiedRetrievalResult[]; retrievalResults: UnifiedRetrievalResult[];
}): AssistantEvidenceBundleContractV1 { }): AssistantEvidenceBundleContractV1 {
const retrievalResults = Array.isArray(input.retrievalResults) ? input.retrievalResults : []; const retrievalResults = Array.isArray(input.retrievalResults) ? input.retrievalResults : [];

View File

@ -1,3 +1,4 @@
import type { AssistantDebugRouteRecord } from "../types/assistant";
import type { RouteHintSummary } from "../types/normalizer"; import type { RouteHintSummary } from "../types/normalizer";
interface FragmentLike { interface FragmentLike {
@ -106,7 +107,7 @@ export function buildExecutionPlanFromRoute(input: {
export function buildDebugRoutesFromRoute(input: { export function buildDebugRoutesFromRoute(input: {
routeSummary: RouteHintSummary | null; routeSummary: RouteHintSummary | null;
resolveLegacyRouteReason: (route: string) => string; resolveLegacyRouteReason: (route: string) => string;
}): Array<Record<string, unknown>> { }): AssistantDebugRouteRecord[] {
if (!input.routeSummary) { if (!input.routeSummary) {
return []; return [];
} }

View File

@ -4,6 +4,7 @@ import type { CompanyAnchorSet } from "./companyAnchorResolver";
import type { EvidenceItem } from "../types/stage1Contracts"; import type { EvidenceItem } from "../types/stage1Contracts";
import type { ProblemUnit } from "../types/stage2ProblemUnits"; import type { ProblemUnit } from "../types/stage2ProblemUnits";
import type { ClaimBoundAnchorAudit } from "./assistantClaimBoundEvidence"; import type { ClaimBoundAnchorAudit } from "./assistantClaimBoundEvidence";
import iconv from "iconv-lite";
type P0DomainHint = type P0DomainHint =
| "settlements_60_62" | "settlements_60_62"
@ -895,12 +896,70 @@ export interface DomainPolarityGuardAudit {
reason_codes: string[]; reason_codes: string[];
} }
function mojibakeScoreForRuntimeGuards(value: string): number {
const source = String(value ?? "");
const cyrillic = (source.match(/[А-Яа-яЁё]/g) ?? []).length;
const latin = (source.match(/[A-Za-z]/g) ?? []).length;
const hardMarkers = (source.match(/[ѓ“‚„…†‡€‰‹‰ЉЊ‹Џ‘’“”•–—™љ›њћџ]/g) ?? []).length;
const pairMarkers = (source.match(/(?:Р.|С.|Гђ.|Г‘.)/g) ?? []).length;
const doubleEncodedMarkers = (source.match(/(?:Р.|Р.|Гѓ.|Г.)/gu) ?? []).length;
return cyrillic + latin - hardMarkers * 3 - pairMarkers * 2 - doubleEncodedMarkers * 2;
}
function looksLikeMojibakeForRuntimeGuards(value: string): boolean {
const source = String(value ?? "");
if (!source.trim()) {
return false;
}
if (/[ѓ“‚„…†‡€‰‹‰ЉЊ‹Џ‘’“”•–—™љ›њћџ]/.test(source)) {
return true;
}
if ((source.match(/(?:Р.|С.|Гђ.|Г‘.)/g) ?? []).length >= 2) {
return true;
}
return (source.match(/(?:Р.|Р.|Гѓ.|Г.)/gu) ?? []).length >= 2;
}
function repairRuntimeGuardsMojibake(value: string): string {
const source = String(value ?? "");
if (!looksLikeMojibakeForRuntimeGuards(source)) {
return source;
}
let candidate = source;
for (let pass = 0; pass < 3; pass += 1) {
let improved = false;
try {
const fromWin1251 = iconv.encode(candidate, "win1251").toString("utf8");
if (mojibakeScoreForRuntimeGuards(fromWin1251) > mojibakeScoreForRuntimeGuards(candidate)) {
candidate = fromWin1251;
improved = true;
}
} catch (_error) {
// noop
}
try {
const fromLatin1 = Buffer.from(candidate, "latin1").toString("utf8");
if (mojibakeScoreForRuntimeGuards(fromLatin1) > mojibakeScoreForRuntimeGuards(candidate)) {
candidate = fromLatin1;
improved = true;
}
} catch (_error) {
// noop
}
if (!improved) {
break;
}
}
return candidate;
}
export function resolveDomainPolarityGuard(input: { export function resolveDomainPolarityGuard(input: {
userMessage: string; userMessage: string;
companyAnchors?: CompanyAnchorSet | null; companyAnchors?: CompanyAnchorSet | null;
focusDomainHint?: string | null; focusDomainHint?: string | null;
}): DomainPolarityGuardAudit { }): DomainPolarityGuardAudit {
const lower = String(input.userMessage ?? "").toLowerCase(); const repairedMessage = repairRuntimeGuardsMojibake(String(input.userMessage ?? ""));
const lower = repairedMessage.toLowerCase();
const accountExtraction = extractAccountsFromTextDetailed(lower); const accountExtraction = extractAccountsFromTextDetailed(lower);
const accounts = uniqueStrings([...(input.companyAnchors?.accounts ?? []), ...accountExtraction.resolved_account_anchors]); const accounts = uniqueStrings([...(input.companyAnchors?.accounts ?? []), ...accountExtraction.resolved_account_anchors]);
const prefixes = new Set(accounts.map((item) => accountPrefix(item)).filter((item): item is string => Boolean(item))); const prefixes = new Set(accounts.map((item) => accountPrefix(item)).filter((item): item is string => Boolean(item)));
@ -1683,7 +1742,8 @@ export function applyEligibilityToGroundingCheck<T extends { status: string; rea
const reasonMap: Record<string, string> = { const reasonMap: Record<string, string> = {
admissible_evidence_count_zero: "Недостаточно подтвержденных данных для уверенного ответа.", admissible_evidence_count_zero: "Недостаточно подтвержденных данных для уверенного ответа.",
critical_domain_or_account_contradiction: "Есть противоречие по выбранному домену или контуру счета.", critical_domain_or_account_contradiction: "Есть противоречие по выбранному домену или контуру счета.",
temporal_guard_failed_out_of_snapshot_window: "Запрошенный период выходит за доступный срез данных.", temporal_guard_failed_out_of_snapshot_window:
"Запрошенный период выходит за доступный срез данных. Temporal anchor outside snapshot window.",
temporal_guard_ambiguous_limited: "Период в вопросе определен недостаточно точно.", temporal_guard_ambiguous_limited: "Период в вопросе определен недостаточно точно.",
business_scope_generic_unresolved: "Не удалось надежно привязать вопрос к конкретному бизнес-контексту.", business_scope_generic_unresolved: "Не удалось надежно привязать вопрос к конкретному бизнес-контексту.",
polarity_guard_limited_unresolved_polarity: "Не удалось однозначно определить сторону расчета (нам должны или мы должны).", polarity_guard_limited_unresolved_polarity: "Не удалось однозначно определить сторону расчета (нам должны или мы должны).",

View File

@ -1040,7 +1040,12 @@ function hasCrossScopeConflictWithState(userMessage, state) {
const inferredDomain = inferP0DomainFromMessage(userMessage); const inferredDomain = inferP0DomainFromMessage(userMessage);
const stateDomain = compactWhitespace(state.followup_context?.active_domain ?? state.focus.domain ?? ""); const stateDomain = compactWhitespace(state.followup_context?.active_domain ?? state.focus.domain ?? "");
if (inferredDomain && stateDomain && inferredDomain !== stateDomain) { if (inferredDomain && stateDomain && inferredDomain !== stateDomain) {
return true; const followupDomainRefinement = hasFollowupMarker(userMessage) ||
hasReferentialPointer(userMessage) ||
hasPeriodLiteral(userMessage);
if (!followupDomainRefinement) {
return true;
}
} }
const explicitAccounts = extractAccountTokens(userMessage); const explicitAccounts = extractAccountTokens(userMessage);
const fallbackAccounts = explicitAccounts.length > 0 ? explicitAccounts : extractFollowupAccountAnchorsLoose(userMessage); const fallbackAccounts = explicitAccounts.length > 0 ? explicitAccounts : extractFollowupAccountAnchorsLoose(userMessage);
@ -1070,9 +1075,11 @@ function inferP0DomainFromMessage(text) {
return null; return null;
} }
function hasStrongFollowupAnchors(userMessage, state) { function hasStrongFollowupAnchors(userMessage, state) {
const normalizedMessage = compactWhitespace(repairAddressMojibake(String(userMessage ?? "")).toLowerCase());
const periodRefinementCue = /(?:^(?:\u0430\s+)?\u0435\u0441\u043b\u0438|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0437\u0430|\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c|\u043f\u043e\s+\u043f\u0435\u0440\u0438\u043e\u0434\u0443|\u0437\u0430\s+\u0438\u044e\u043d\u044c|\u0437\u0430\s+\u0438\u044e\u043b\u044c)/iu.test(normalizedMessage);
const explicitPeriod = extractNormalizedPeriodLiteral(userMessage); const explicitPeriod = extractNormalizedPeriodLiteral(userMessage);
if (explicitPeriod && state.focus.period && explicitPeriod !== state.focus.period) { if (explicitPeriod && state.focus.period && explicitPeriod !== state.focus.period) {
const periodLooksLikeFollowupRefinement = hasFollowupMarker(userMessage) || hasReferentialPointer(userMessage); const periodLooksLikeFollowupRefinement = hasFollowupMarker(userMessage) || hasReferentialPointer(userMessage) || periodRefinementCue;
if (!periodLooksLikeFollowupRefinement) { if (!periodLooksLikeFollowupRefinement) {
return true; return true;
} }
@ -2969,26 +2976,33 @@ function resolveAddressToolGateDecision(addressInputMessage, followupContext, ll
reason: dataScopeMetaQuery ? "assistant_data_scope_query_detected" : "assistant_capability_query_detected" reason: dataScopeMetaQuery ? "assistant_data_scope_query_detected" : "assistant_capability_query_detected"
}; };
} }
const directDeepAnalysisSignal = hasDirectDeepAnalysisSignal(rawMessageForGate) ||
hasDirectDeepAnalysisSignal(repairedInputMessage);
const deepAnalysisPreferenceSignal = directDeepAnalysisSignal ||
hasDeepAnalysisPreferenceSignal(rawMessageForGate) ||
hasDeepAnalysisPreferenceSignal(repairedInputMessage);
const modeDetection = (0, addressQueryClassifier_1.detectAddressQuestionMode)(repairedInputMessage || addressInputMessage); const modeDetection = (0, addressQueryClassifier_1.detectAddressQuestionMode)(repairedInputMessage || addressInputMessage);
const hasClassifierSignal = modeDetection.mode === "address_query"; const hasClassifierSignal = modeDetection.mode === "address_query";
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode); const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
const llmContractModeConfidence = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode_confidence); const llmContractModeConfidence = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode_confidence);
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent); const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
const llmContractIntentConfidence = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent_confidence); const llmCanonicalEntitySignal = /(?:\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043a\u043e\u043c\u043f\u0430\u043d|customer|supplier|counterparty|company|vendor|client|\b[a-z]{2,}\b)/iu.test(compactWhitespace(repairedInputMessage.toLowerCase()));
const llmCanonicalAppliedSignal = Boolean(llmPreDecomposeMeta?.applied) && llmContractMode !== "deep_analysis";
const hasLlmCanonicalSignal = Boolean(llmPreDecomposeMeta?.llmCanonicalCandidateDetected) && const hasLlmCanonicalSignal = Boolean(llmPreDecomposeMeta?.llmCanonicalCandidateDetected) &&
llmContractMode === "address_query" && ((llmContractMode === "address_query" && llmContractModeConfidence !== "low") ||
llmContractModeConfidence !== "low" && (llmCanonicalAppliedSignal &&
llmContractIntent !== null && (hasStrongDataIntentSignal(repairedInputMessage) || llmCanonicalEntitySignal || llmContractMode === "unsupported")));
llmContractIntent !== "unknown" &&
llmContractIntentConfidence !== "low";
const hasLlmCanonicalDataSignal = Boolean(llmPreDecomposeMeta?.llmCanonicalCandidateDetected) && const hasLlmCanonicalDataSignal = Boolean(llmPreDecomposeMeta?.llmCanonicalCandidateDetected) &&
Boolean(llmPreDecomposeMeta?.applied) && Boolean(llmPreDecomposeMeta?.applied) &&
llmContractMode === "address_query" && (llmContractMode === "address_query" || llmContractMode === "unsupported" || llmContractMode === null) &&
hasStrongDataIntentSignal(repairedInputMessage); hasStrongDataIntentSignal(repairedInputMessage);
const sameDateAccountFollowupSignal = hasSameDateAccountFollowupSignalForPredecompose(rawMessageForGate) ||
hasSameDateAccountFollowupSignalForPredecompose(repairedInputMessage);
const hasLexicalAddressSignal = isAddressLlmPreDecomposeCandidate(addressInputMessage) || const hasLexicalAddressSignal = isAddressLlmPreDecomposeCandidate(addressInputMessage) ||
isAddressLlmPreDecomposeCandidate(repairedInputMessage) || isAddressLlmPreDecomposeCandidate(repairedInputMessage) ||
hasAccountingSignal(addressInputMessage) || hasAccountingSignal(addressInputMessage) ||
hasAccountingSignal(repairedInputMessage); hasAccountingSignal(repairedInputMessage) ||
sameDateAccountFollowupSignal;
const hasUnsupportedLowConfidencePredecomposeSignal = llmContractMode === "unsupported" && const hasUnsupportedLowConfidencePredecomposeSignal = llmContractMode === "unsupported" &&
(llmContractModeConfidence === "low" || llmContractModeConfidence === "medium") && (llmContractModeConfidence === "low" || llmContractModeConfidence === "medium") &&
llmContractIntent === "unknown"; llmContractIntent === "unknown";
@ -3036,6 +3050,89 @@ function resolveAddressToolGateDecision(addressInputMessage, followupContext, ll
reason: "no_address_signal_after_l0" reason: "no_address_signal_after_l0"
}; };
} }
function hasLooseAllTimeAddressLookupSignal(text) {
const repaired = repairAddressMojibake(String(text ?? ""));
const normalized = compactWhitespace(repaired.toLowerCase());
if (!normalized) {
return false;
}
if (shouldHandleAsAssistantCapabilityMetaQuery(normalized) || hasAssistantDataScopeMetaQuestionSignal(normalized)) {
return false;
}
const hasAllTimeSignal = /(?:\u0437\u0430\s+\u0432\u0435\u0441\u044c\s+\u043f\u0435\u0440\u0438\u043e\u0434|\u0437\u0430\s+\u0432\u0441\u0435\s+\u0432\u0440\u0435\u043c\u044f|\u0437\u0430\s+\u0432\u0441\u044e\s+\u0438\u0441\u0442\u043e\u0440\u0438(?:\u044e|\u0438)|for\s+all\s+time|all\s+time|entire\s+period|full\s+period)/iu.test(normalized);
if (!hasAllTimeSignal) {
return false;
}
return /(?:\u0447\u0442\u043e\s+\u0435\u0441\u0442\u044c|\u0447[\u0435\u0451]\s+\u0435\u0441\u0442\u044c|\u043f\u043e\u043a\u0430\u0436\u0438|\u0432\u044b\u0432\u0435\u0434\u0438|\u0434\u0430\u0439|show|list|find)/iu.test(normalized);
}
function hasDeepSessionContinuationSignal(input) {
const sessionItems = Array.isArray(input?.sessionItems) ? input.sessionItems : [];
if (sessionItems.length === 0) {
return false;
}
const previousDebug = findLastAssistantLivingChatDebug(sessionItems);
if (!previousDebug || typeof previousDebug !== "object") {
return false;
}
const investigationState = previousDebug.investigation_state_snapshot;
if (!investigationState || typeof investigationState !== "object") {
return false;
}
const candidateTexts = [
input?.rawUserMessage,
input?.repairedRawUserMessage,
input?.effectiveAddressUserMessage,
input?.repairedEffectiveAddressUserMessage
]
.map((value) => compactWhitespace(repairAddressMojibake(String(value ?? "")).toLowerCase()))
.filter((value) => value.length > 0);
if (candidateTexts.length === 0) {
return false;
}
return candidateTexts.some((text) => {
const hasContinuationCue = /^(?:\u0438|\u0430|\u0442\u0430\u043a\u0436\u0435|\u0435\u0449[\u0435\u0451]|\u0434\u043e\u0431\u0430\u0432\u044c|\u0434\u043e\u043f\u043e\u043b\u043d\u0438|\u0443\u0442\u043e\u0447\u043d\u0438|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438|\u0442\u0435\u043f\u0435\u0440\u044c|then|also|and)\b/iu.test(text) ||
/(?:\u043f\u043e\s+\u0442\u043e\u043c\u0443\s+\u0436\u0435|\u043f\u043e\s+\u044d\u0442\u043e\u043c\u0443|\u0432\s+\u044d\u0442\u043e\u043c\s+\u0436\u0435|\u0438\s+\u043f\u043e\s+\u043f\u0435\u0440\u0438\u043e\u0434\u0443|\u0434\u043e\u0431\u0430\u0432\u044c\s+\u0443\u0442\u043e\u0447\u043d\u0435\u043d\u0438\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c|\u0430\s+\u0435\u0441\u043b\u0438|\u0435\u0441\u043b\u0438\s+\u0442\u043e\u043b\u044c\u043a\u043e|\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e)/iu.test(text);
const hasAccountOrPeriodCue = /(?:\u0441\u0447[\u0435\u0451]\u0442|account|\b\d{2}(?:[.,]\d{1,2})?\b|\b20\d{2}(?:[-/.]\d{1,2})?\b|\u043f\u0435\u0440\u0438\u043e\u0434|\u043c\u0435\u0441\u044f\u0446)/iu.test(text);
const hasDeepRebindCue = /(?:\u0430\u043c\u043e\u0440\u0442\u0438\u0437|fixed\s*asset|\u043e\u0441\b|\u043d\u0434\u0441|vat|\u0440\u0430\u0437\u0440\u044b\u0432|\u0446\u0435\u043f\u043e\u0447\u043a|\u0430\u043d\u043e\u043c\u0430\u043b|lifecycle|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447)/iu.test(text);
if (hasContinuationCue && (hasAccountOrPeriodCue || hasDeepRebindCue)) {
return true;
}
return hasDeepRebindCue && hasAccountOrPeriodCue;
});
}
function hasDeepAnalysisPreferenceSignal(text) {
const repaired = repairAddressMojibake(String(text ?? ""));
const lower = compactWhitespace(repaired.toLowerCase());
if (!lower) {
return false;
}
const riskOrAnomalySignal = /(?:\u0440\u0438\u0441\u043a|risk|\u0430\u043d\u043e\u043c\u0430\u043b|anomal|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442|conflict|deviation|\u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d|\u043d\u0435\u0441\u044b\u043a\u043e\u0432\u043a|\u043d\u0435\u0441\u0445\u043e\u0434|\u043e\u0448\u0438\u0431|error|issue|\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
const chainSignal = /(?:\u0446\u0435\u043f\u043e\u0447\u043a|chain|trace\s*chain|lifecycle|\u0436\u0438\u0437\u043d\u0435\u043d\u043d[\u0430-\u044f]+\s+\u0446\u0438\u043a\u043b|state\s+transition|\u0440\u0430\u0437\u0440\u044b\u0432[\u0430-\u044f]*)/iu.test(lower);
const diagnosticsSignal = /(?:\u0440\u0430\u0437\u043b\u043e\u0436\u0438|\u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437|\u0440\u0430\u0437\u0431\u0435\u0440\u0438|\u043f\u043e\u0447\u0435\u043c\u0443|why|\u043a\u043e\u0440\u043d\u0435\u0432[\u0430-\u044f]+\s+\u043f\u0440\u0438\u0447\u0438\u043d|root\s*cause|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c[\u0430-\u044f]*|\u0433\u0434\u0435\s+\u0440\u0430\u0437\u0440\u044b\u0432|\u0447\u0442\u043e\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
const closureSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442\u0438[\u0435\u044f]\s+\u043f\u0435\u0440\u0438\u043e\u0434|period\s*close|\u043d\u0435\s+\u0437\u0430\u043a\u0440\u044b\u043b[\u0430-\u044f]*|\u0445\u0432\u043e\u0441\u0442[\u0430-\u044f]*)/iu.test(lower);
const closureIntentSignal = /(?:\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|period\s*close|close\s+period)/iu.test(lower);
const closureDiagnosticPhraseSignal = /(?:\u0447\u0442\u043e(?:\s+\S+){0,8}\s+\u043c\u0435\u0448\u0430[\u0430-\u044f]+\s+\u0437\u0430\u043a\u0440\u044b\u0442)/iu.test(lower);
const signalVsNoiseDiagnostic = /(?:\u043d\u0435\s+\u043f\u0440\u043e\u0441\u0442\u043e\s+(?:\u043d\u0430\s+)?\u0448\u0443\u043c|\u043f\u043e\u0445\u043e\u0436[\u0438\u0435]\s+(?:\u0438\u043c\u0435\u043d\u043d\u043e\s+)?\u043d\u0430\s+\u043f\u0440\u043e\u0431\u043b\u0435\u043c)/iu.test(lower);
const lifecycleMismatchSignal = /(?:\u043d\u0435\s+\u0442\u0435\u043c\s+\u0442\u0438\u043f(?:\u043e\u043c)?\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|wrong\s+closing\s+document|expected\s+transition)/iu.test(lower);
const lifecycleTransitionGapSignal = /(?:\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u043f\u0435\u0440\u0435\u0445\u043e\u0434[\u0430-\u044f]*\s+\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432|\u0441\u0442\u0430\u0434\u0438[\u0438\u044f\u0435]\s+.*\u043f\u0440\u043e\u0439\u0434\u0435\u043d.*\u043f\u0435\u0440\u0435\u0445\u043e\u0434)/iu.test(lower);
const expectedActualMismatchSignal = /(?:\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a[\u0430-\u044f]+\s+\u0441\u043e\u0441\u0442\u043e\u044f\u043d[\u0438\u0435\u044f]+\s+.*\u0440\u0430\u0441\u0445\u043e\u0434[\u0430-\u044f]*\s+\u0441\s+\u043e\u0436\u0438\u0434\u0430\u0435\u043c|\u043e\u0436\u0438\u0434\u0430\u0435\u043c[\u0430-\u044f]+\s+\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d[\u0430-\u044f]*\s+\u0441\u043f\u0438\u0441\u0430\u043d)/iu.test(lower);
return riskOrAnomalySignal ||
lifecycleMismatchSignal ||
(chainSignal && lifecycleTransitionGapSignal) ||
expectedActualMismatchSignal ||
(chainSignal && diagnosticsSignal) ||
(riskOrAnomalySignal && (chainSignal || closureSignal || diagnosticsSignal || closureIntentSignal)) ||
(diagnosticsSignal && closureIntentSignal) ||
closureDiagnosticPhraseSignal ||
signalVsNoiseDiagnostic;
}
function hasDirectDeepAnalysisSignal(text) {
const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase());
if (!normalized) {
return false;
}
return /(?:\u0440\u0430\u0437\u043b\u043e\u0436|\u0446\u0435\u043f\u043e\u0447|lifecycle|\u0440\u0430\u0437\u0440\u044b\u0432|\u043f\u0440\u043e\u0442\u0438\u0432\u043e\u0440\u0435\u0447|\u0430\u043d\u043e\u043c\u0430\u043b|\u043f\u043e\u0447\u0435\u043c\u0443|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c|\u0437\u0430\u043a\u0440\u044b\u0442[\u0430-\u044f]*|state\s+transition|root\s*cause|trace\s*chain)/iu.test(normalized);
}
export function resolveAssistantOrchestrationDecision(input) { export function resolveAssistantOrchestrationDecision(input) {
const rawUserMessage = String(input?.rawUserMessage ?? input?.userMessage ?? ""); const rawUserMessage = String(input?.rawUserMessage ?? input?.userMessage ?? "");
const effectiveAddressUserMessage = String(input?.effectiveAddressUserMessage ?? rawUserMessage); const effectiveAddressUserMessage = String(input?.effectiveAddressUserMessage ?? rawUserMessage);
@ -3044,6 +3141,7 @@ export function resolveAssistantOrchestrationDecision(input) {
const followupContext = input?.followupContext ?? null; const followupContext = input?.followupContext ?? null;
const llmPreDecomposeMeta = input?.llmPreDecomposeMeta ?? null; const llmPreDecomposeMeta = input?.llmPreDecomposeMeta ?? null;
const useMock = Boolean(input?.useMock); const useMock = Boolean(input?.useMock);
const sessionItems = Array.isArray(input?.sessionItems) ? input.sessionItems : null;
const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawUserMessage) || const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawUserMessage) ||
hasAssistantDataScopeMetaQuestionSignal(repairedRawUserMessage) || hasAssistantDataScopeMetaQuestionSignal(repairedRawUserMessage) ||
hasAssistantDataScopeMetaQuestionSignal(effectiveAddressUserMessage) || hasAssistantDataScopeMetaQuestionSignal(effectiveAddressUserMessage) ||
@ -3131,11 +3229,52 @@ export function resolveAssistantOrchestrationDecision(input) {
}; };
} }
const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage); const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
const preserveAddressLaneSignal = Boolean((llmPreDecomposeMeta?.llmCanonicalCandidateDetected &&
llmPreDecomposeMeta?.applied &&
(llmContractMode === "address_query" || llmContractMode === "unsupported")) ||
hasSameDateAccountFollowupSignalForPredecompose(rawUserMessage) ||
hasSameDateAccountFollowupSignalForPredecompose(effectiveAddressUserMessage) ||
hasSameDateAccountFollowupSignalForPredecompose(repairedRawUserMessage) ||
hasSameDateAccountFollowupSignalForPredecompose(repairedEffectiveAddressUserMessage) ||
hasLooseAllTimeAddressLookupSignal(rawUserMessage) ||
hasLooseAllTimeAddressLookupSignal(effectiveAddressUserMessage) ||
hasLooseAllTimeAddressLookupSignal(repairedRawUserMessage) ||
hasLooseAllTimeAddressLookupSignal(repairedEffectiveAddressUserMessage) ||
hasAddressFollowupContextSignal(rawUserMessage) ||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage));
const unsupportedAddressIntentFallbackToDeep = Boolean(!followupContext && const unsupportedAddressIntentFallbackToDeep = Boolean(!followupContext &&
baseToolGate?.runAddressLane && baseToolGate?.runAddressLane &&
modeDetection.mode !== "address_query" && modeDetection.mode !== "address_query" &&
intentResolution.intent === "unknown" && intentResolution.intent === "unknown" &&
strongDataSignal); strongDataSignal &&
!preserveAddressLaneSignal);
const deepAnalysisPreferenceDetected = Boolean(hasDeepAnalysisPreferenceSignal(rawUserMessage) ||
hasDeepAnalysisPreferenceSignal(repairedRawUserMessage) ||
hasDeepAnalysisPreferenceSignal(effectiveAddressUserMessage) ||
hasDeepAnalysisPreferenceSignal(repairedEffectiveAddressUserMessage) ||
hasDirectDeepAnalysisSignal(rawUserMessage) ||
hasDirectDeepAnalysisSignal(repairedRawUserMessage) ||
hasDirectDeepAnalysisSignal(effectiveAddressUserMessage) ||
hasDirectDeepAnalysisSignal(repairedEffectiveAddressUserMessage));
const vatExplainFollowupSignal = Boolean(followupContext &&
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
/(?:\u043f\u043e\u0447\u0435\u043c\u0443|why).*(?:\u043f\u0440\u043e\u0433\u043d\u043e\u0437|forecast).*(?:\u0443\u043f\u043b\u0430\u0442|payable|\b0\b)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
const deepAnalysisSignalFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
deepAnalysisPreferenceDetected &&
!vatExplainFollowupSignal &&
(!followupContext || !dataRetrievalSignal));
const deepSessionContinuationFallbackToDeep = Boolean(!followupContext &&
baseToolGate?.runAddressLane &&
hasDeepSessionContinuationSignal({
rawUserMessage,
repairedRawUserMessage,
effectiveAddressUserMessage,
repairedEffectiveAddressUserMessage,
sessionItems
}));
let runAddressLane = Boolean(baseToolGate?.runAddressLane); let runAddressLane = Boolean(baseToolGate?.runAddressLane);
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane"); let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0"); let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
@ -3144,6 +3283,16 @@ export function resolveAssistantOrchestrationDecision(input) {
toolGateDecision = "skip_address_lane"; toolGateDecision = "skip_address_lane";
toolGateReason = "address_signal_unsupported_intent_fallback_to_deep"; toolGateReason = "address_signal_unsupported_intent_fallback_to_deep";
} }
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
runAddressLane = false;
toolGateDecision = "skip_address_lane";
toolGateReason = "deep_analysis_signal_fallback_to_deep";
}
if (deepSessionContinuationFallbackToDeep) {
runAddressLane = false;
toolGateDecision = "skip_address_lane";
toolGateReason = "deep_session_continuation_fallback_to_deep";
}
let livingDecision = resolveLivingAssistantModeDecision({ let livingDecision = resolveLivingAssistantModeDecision({
userMessage: rawUserMessage, userMessage: rawUserMessage,
addressLaneTriggered: runAddressLane, addressLaneTriggered: runAddressLane,
@ -3157,6 +3306,18 @@ export function resolveAssistantOrchestrationDecision(input) {
reason: "unsupported_address_intent_fallback_to_deep" reason: "unsupported_address_intent_fallback_to_deep"
}; };
} }
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
livingDecision = {
mode: "deep_analysis",
reason: "deep_analysis_signal_fallback_to_deep"
};
}
if (deepSessionContinuationFallbackToDeep) {
livingDecision = {
mode: "deep_analysis",
reason: "deep_session_continuation_fallback_to_deep"
};
}
return { return {
runAddressLane, runAddressLane,
toolGateDecision, toolGateDecision,
@ -3174,6 +3335,8 @@ export function resolveAssistantOrchestrationDecision(input) {
data_retrieval_signal_detected: dataRetrievalSignal, data_retrieval_signal_detected: dataRetrievalSignal,
followup_context_detected: Boolean(followupContext), followup_context_detected: Boolean(followupContext),
unsupported_address_intent_fallback_to_deep: unsupportedAddressIntentFallbackToDeep, unsupported_address_intent_fallback_to_deep: unsupportedAddressIntentFallbackToDeep,
deep_analysis_signal_fallback_to_deep: deepAnalysisSignalFallbackToDeep,
deep_session_continuation_fallback_to_deep: deepSessionContinuationFallbackToDeep,
final_decision: { final_decision: {
run_address_lane: runAddressLane, run_address_lane: runAddressLane,
tool_gate_decision: toolGateDecision, tool_gate_decision: toolGateDecision,

View File

@ -85,12 +85,12 @@ export function buildAssistantTurnRuntimeDeps(
): AssistantTurnRuntimeBuilderDeps { ): AssistantTurnRuntimeBuilderDeps {
return { return {
...input.helpers, ...input.helpers,
ensureSession: input.sessions.ensureSession, ensureSession: (sessionId) => input.sessions.ensureSession(sessionId),
appendItem: input.sessions.appendItem, appendItem: (sessionId, item) => input.sessions.appendItem(sessionId, item),
getSession: input.sessions.getSession, getSession: (sessionId) => input.sessions.getSession(sessionId),
persistSession: input.sessionLogger.persistSession, persistSession: (session) => input.sessionLogger.persistSession(session),
setInvestigationState: input.sessions.setInvestigationState, setInvestigationState: (sessionId, state) => input.sessions.setInvestigationState(sessionId, state),
normalize: input.normalizerService.normalize, normalize: (payload) => input.normalizerService.normalize(payload),
executeRouteRuntime: (route, fragmentText, options) => executeRouteRuntime: (route, fragmentText, options) =>
input.dataLayer.executeRouteRuntime(route, fragmentText, options), input.dataLayer.executeRouteRuntime(route, fragmentText, options),
tryAddressQueryHandle: (messageUsed, options) => input.addressQueryService.tryHandle(messageUsed, options), tryAddressQueryHandle: (messageUsed, options) => input.addressQueryService.tryHandle(messageUsed, options),

View File

@ -1,4 +1,13 @@
import type { NormalizeRequestPayload, NormalizeResponsePayload, RouteHintSummary } from "./normalizer"; import type {
NormalizeRequestPayload,
NormalizeResponsePayload,
NoRouteReason,
RouteHintSummary,
RouteStatus,
ExecutionReadiness,
ConfidenceLevel,
IntentClass
} from "./normalizer";
import type { AnswerStructureV11, EvidenceItem } from "./stage1Contracts"; import type { AnswerStructureV11, EvidenceItem } from "./stage1Contracts";
import type { import type {
CandidateEvidenceItem, CandidateEvidenceItem,
@ -27,6 +36,43 @@ export type AssistantProblemAnswerMode =
| "stage2_problem_centric_v1" | "stage2_problem_centric_v1"
| "stage3_lifecycle_aware_v1"; | "stage3_lifecycle_aware_v1";
export interface AssistantExecutionStateRecord {
fragment_id: string | null;
execution_readiness: ExecutionReadiness | null;
route_status: RouteStatus | null;
no_route_reason: NoRouteReason | null;
}
export type AssistantDebugRouteRecord =
| {
fragment_id: string;
route: string;
reason: string;
confidence: ConfidenceLevel;
intent_class: IntentClass;
}
| {
fragment_id: string;
route: string;
reason: string;
route_status: RouteStatus | null;
no_route_reason: NoRouteReason | null;
clarification_reason: string | null;
execution_readiness: ExecutionReadiness | null;
};
export interface AssistantAddressRuntimeMetaForDeep {
attempted?: boolean;
applied?: boolean;
reason?: string | null;
provider?: string | null;
fallbackRuleHit?: string | null;
toolGateDecision?: string | null;
toolGateReason?: string | null;
predecomposeContract?: Record<string, unknown> | null;
orchestrationContract?: Record<string, unknown> | null;
}
export interface AssistantRequirement { export interface AssistantRequirement {
requirement_id: string; requirement_id: string;
source_fragment_id: string | null; source_fragment_id: string | null;
@ -302,7 +348,7 @@ export interface AssistantDebugPayload {
fragments: unknown[]; fragments: unknown[];
requirements_extracted: AssistantRequirement[]; requirements_extracted: AssistantRequirement[];
coverage_report: RequirementCoverageReport; coverage_report: RequirementCoverageReport;
routes: Array<Record<string, unknown>>; routes: AssistantDebugRouteRecord[];
retrieval_status: Array<{ retrieval_status: Array<{
fragment_id: string; fragment_id: string;
requirement_ids: string[]; requirement_ids: string[];

View File

@ -32,6 +32,9 @@ function redactObject(value: unknown): unknown {
} }
export function logJson(entry: JsonLogEntry): void { export function logJson(entry: JsonLogEntry): void {
if (process.env.NODE_ENV === "test" && process.env.FEATURE_JSON_STDOUT_LOGS_IN_TESTS !== "1") {
return;
}
const safe = { const safe = {
...entry, ...entry,
details: redactObject(entry.details) details: redactObject(entry.details)

View File

@ -261,4 +261,40 @@ describe("assistant orchestration contract", () => {
decision.toolGateReason decision.toolGateReason
); );
}); });
it("routes risk/anomaly analytics wording to deep pipeline", () => {
const decision = resolveAssistantOrchestrationDecision({
rawUserMessage: "Проверь НДС по счету 19 за 2020-06 и рискованные записи по документам.",
effectiveAddressUserMessage: "Проверь НДС по счету 19 за 2020-06 и рискованные записи по документам.",
followupContext: null,
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateDecision).toBe("skip_address_lane");
expect(decision.toolGateReason).toBe("deep_analysis_signal_fallback_to_deep");
expect(decision.livingMode).toBe("deep_analysis");
expect(decision.livingReason).toBe("deep_analysis_signal_fallback_to_deep");
expect(decision.orchestrationContract?.deep_analysis_signal_fallback_to_deep).toBe(true);
});
it("routes settlement closure verification wording to deep pipeline", () => {
const decision = resolveAssistantOrchestrationDecision({
rawUserMessage:
"По оплате поставщику на счете 60 в июле 2020 остался хвост. Проверь закрытие по договору и объекту расчетов.",
effectiveAddressUserMessage:
"По оплате поставщику на счете 60 в июле 2020 остался хвост. Проверь закрытие по договору и объекту расчетов.",
followupContext: null,
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateDecision).toBe("skip_address_lane");
expect(decision.toolGateReason).toBe("deep_analysis_signal_fallback_to_deep");
expect(decision.livingMode).toBe("deep_analysis");
expect(decision.livingReason).toBe("deep_analysis_signal_fallback_to_deep");
expect(decision.orchestrationContract?.deep_analysis_signal_fallback_to_deep).toBe(true);
});
}); });

View File

@ -129,4 +129,89 @@ describe("assistant turn runtime deps adapter", () => {
expect(deps.compactWhitespace(" value ")).toBe("value"); expect(deps.compactWhitespace(" value ")).toBe("value");
expect(helperCompactWhitespace).toHaveBeenCalledWith(" value "); expect(helperCompactWhitespace).toHaveBeenCalledWith(" value ");
}); });
it("preserves method context for stateful service instances", async () => {
class SessionStoreLike {
public calls: string[] = [];
public ensureSession(sessionId: string) {
this.calls.push(`ensure:${sessionId}`);
return { session_id: sessionId } as any;
}
public appendItem(sessionId: string) {
this.calls.push(`append:${sessionId}`);
}
public getSession(sessionId: string) {
this.calls.push(`get:${sessionId}`);
return null;
}
public setInvestigationState(sessionId: string) {
this.calls.push(`state:${sessionId}`);
return null;
}
}
class SessionLoggerLike {
public persisted = 0;
public persistSession() {
this.persisted += 1;
}
}
class NormalizerLike {
public normalized = 0;
public async normalize() {
this.normalized += 1;
return {};
}
}
const sessions = new SessionStoreLike();
const sessionLogger = new SessionLoggerLike();
const normalizerService = new NormalizerLike();
const deps = buildAssistantTurnRuntimeDeps({
sessions,
sessionLogger,
normalizerService,
dataLayer: {
executeRouteRuntime: async () => ({})
},
addressQueryService: {
tryHandle: async () => null
},
chatClient: {},
messageIdFactory: () => "msg-ctx",
nowIso: () => "2026-04-11T00:00:00.000Z",
defaultApiKey: "",
logEvent: () => {},
flags: {
featureAssistantAddressQueryV1: false,
featureAddressLlmPredecomposeV1: false,
featureInvestigationStateV1: false,
featureStateFollowupBindingV1: false,
featureContractsV11: false,
featureAnswerPolicyV11: false,
featureProblemCentricAnswerV1: false,
featureLifecycleAnswerV1: false
},
defaults: {
defaultModel: "model",
defaultBaseUrl: "base"
},
helpers: {
compactWhitespace: (value: unknown) => String(value ?? "")
} as any
});
deps.ensureSession("asst-ctx");
deps.appendItem("asst-ctx", { role: "user" } as any);
deps.getSession("asst-ctx");
deps.setInvestigationState("asst-ctx", null);
deps.persistSession({ session_id: "asst-ctx" } as any);
await deps.normalize({ user_message: "ctx" } as any);
expect(sessions.calls).toEqual(["ensure:asst-ctx", "append:asst-ctx", "get:asst-ctx", "state:asst-ctx"]);
expect(sessionLogger.persisted).toBe(1);
expect(normalizerService.normalized).toBe(1);
});
}); });

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,112 @@
{
"run_id": "eval-5S6BYeWJWv",
"timestamp": "2026-04-11T10:38:19.513Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 2
},
"cases_total": 2,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 100,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 0,
"routed_fragment_rate": 100,
"no_route_fragment_rate": 0,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 100,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 2,
"checks_passed": 2
},
"route_distribution": {
"store_feature_risk": 1,
"hybrid_store_plus_live": 1
},
"fallback_distribution": {
"none": 2
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь счет 60 за июнь 2020",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "bi53megxPkVDpq",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Покажи риски по НДС и по закрытию",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "ew725J5aRLs1XU",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,112 @@
{
"run_id": "eval-D1gt1OyBqh",
"timestamp": "2026-04-11T10:35:37.557Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 2
},
"cases_total": 2,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 100,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 0,
"routed_fragment_rate": 100,
"no_route_fragment_rate": 0,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 100,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 2,
"checks_passed": 2
},
"route_distribution": {
"store_feature_risk": 1,
"hybrid_store_plus_live": 1
},
"fallback_distribution": {
"none": 2
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь счет 60 за июнь 2020",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "0XdpLLt2DVEC8b",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Покажи риски по НДС и по закрытию",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "TGXTmJEIbfRfxl",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,137 @@
{
"run_id": "eval-EYDYh4k78m",
"timestamp": "2026-04-11T10:35:23.070Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 3
},
"cases_total": 3,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 33.33,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 33.33,
"routed_fragment_rate": 66.67,
"no_route_fragment_rate": 33.33,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 66.67,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 3,
"checks_passed": 2
},
"route_distribution": {
"hybrid_store_plus_live": 1,
"no_route": 1,
"batch_refresh_then_store": 1
},
"fallback_distribution": {
"none": 1,
"out_of_scope": 1,
"clarification": 1
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь хвосты по поставщикам и разложи цепочку",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "XkM4LP-bXu4rf9",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Как вообще по ФСБУ",
"validation_passed": true,
"message_in_scope": false,
"scope_confidence": "low",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 0,
"out_of_scope_fragments": 1,
"unclear_fragments": 0,
"fallback_type": "out_of_scope",
"predicted_route_status": "no_route",
"expected_route_status": null,
"predicted_no_route_reason": "out_of_scope",
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 0,
"trace_id": "eDF6_4JOsguyhk",
"request_count_for_case": 0
},
{
"case_id": "BQ-003",
"raw_question": "Покажи топ рисков за июнь 2020",
"validation_passed": true,
"message_in_scope": false,
"scope_confidence": "low",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 0,
"out_of_scope_fragments": 0,
"unclear_fragments": 1,
"fallback_type": "clarification",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 0,
"trace_id": "dsCVkLkLYqwqHr",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,112 @@
{
"run_id": "eval-NeqPDTflc7",
"timestamp": "2026-04-11T10:16:53.909Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 2
},
"cases_total": 2,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 100,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 0,
"routed_fragment_rate": 100,
"no_route_fragment_rate": 0,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 100,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 2,
"checks_passed": 2
},
"route_distribution": {
"store_feature_risk": 1,
"hybrid_store_plus_live": 1
},
"fallback_distribution": {
"none": 2
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь счет 60 за июнь 2020",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "ynDJfmhtaBS6Jh",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Покажи риски по счету 97",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "BhT9NfO-40oigp",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,137 @@
{
"run_id": "eval-OSNnoiu9KQ",
"timestamp": "2026-04-11T10:16:34.741Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 3
},
"cases_total": 3,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 33.33,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 33.33,
"routed_fragment_rate": 66.67,
"no_route_fragment_rate": 33.33,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 66.67,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 3,
"checks_passed": 2
},
"route_distribution": {
"hybrid_store_plus_live": 1,
"no_route": 1,
"batch_refresh_then_store": 1
},
"fallback_distribution": {
"none": 1,
"out_of_scope": 1,
"clarification": 1
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь хвосты по поставщикам и разложи цепочку",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "YPmWhiLGA674vn",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Как вообще по ФСБУ",
"validation_passed": true,
"message_in_scope": false,
"scope_confidence": "low",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 0,
"out_of_scope_fragments": 1,
"unclear_fragments": 0,
"fallback_type": "out_of_scope",
"predicted_route_status": "no_route",
"expected_route_status": null,
"predicted_no_route_reason": "out_of_scope",
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 0,
"trace_id": "JAbihOWvYs9bYR",
"request_count_for_case": 0
},
{
"case_id": "BQ-003",
"raw_question": "Покажи топ рисков за июнь 2020",
"validation_passed": true,
"message_in_scope": false,
"scope_confidence": "low",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 0,
"out_of_scope_fragments": 0,
"unclear_fragments": 1,
"fallback_type": "clarification",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 0,
"trace_id": "WuI3XbextvaDxw",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,112 @@
{
"run_id": "eval-RVeDpGREWm",
"timestamp": "2026-04-11T10:14:44.794Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 2
},
"cases_total": 2,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 100,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 0,
"routed_fragment_rate": 100,
"no_route_fragment_rate": 0,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 100,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 2,
"checks_passed": 2
},
"route_distribution": {
"store_feature_risk": 1,
"hybrid_store_plus_live": 1
},
"fallback_distribution": {
"none": 2
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь счет 60 за июнь 2020",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "9WDoQo0uvKHsFh",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Покажи риски по НДС и по закрытию",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "YJ7f6z9_SS7ZgW",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,137 @@
{
"run_id": "eval-hhXvDmFzGJ",
"timestamp": "2026-04-11T10:14:25.501Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 3
},
"cases_total": 3,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 33.33,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 33.33,
"routed_fragment_rate": 66.67,
"no_route_fragment_rate": 33.33,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 66.67,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 3,
"checks_passed": 2
},
"route_distribution": {
"hybrid_store_plus_live": 1,
"no_route": 1,
"batch_refresh_then_store": 1
},
"fallback_distribution": {
"none": 1,
"out_of_scope": 1,
"clarification": 1
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь хвосты по поставщикам и разложи цепочку",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "5-MShsMwdLMm1O",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Как вообще по ФСБУ",
"validation_passed": true,
"message_in_scope": false,
"scope_confidence": "low",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 0,
"out_of_scope_fragments": 1,
"unclear_fragments": 0,
"fallback_type": "out_of_scope",
"predicted_route_status": "no_route",
"expected_route_status": null,
"predicted_no_route_reason": "out_of_scope",
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 0,
"trace_id": "pv-o-6Cock8Ve2",
"request_count_for_case": 0
},
{
"case_id": "BQ-003",
"raw_question": "Покажи топ рисков за июнь 2020",
"validation_passed": true,
"message_in_scope": false,
"scope_confidence": "low",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 0,
"out_of_scope_fragments": 0,
"unclear_fragments": 1,
"fallback_type": "clarification",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 0,
"trace_id": "K0zAFkzMAu2BqF",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,112 @@
{
"run_id": "eval-kBTYkPR4wq",
"timestamp": "2026-04-11T10:16:53.895Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 2
},
"cases_total": 2,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 100,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 0,
"routed_fragment_rate": 100,
"no_route_fragment_rate": 0,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 100,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 2,
"checks_passed": 2
},
"route_distribution": {
"store_feature_risk": 1,
"hybrid_store_plus_live": 1
},
"fallback_distribution": {
"none": 2
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь счет 60 за июнь 2020",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "ZQ4J6HgngY7JB4",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Покажи риски по НДС и по закрытию",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "Vk2rIlI-UGAuAE",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,112 @@
{
"run_id": "eval-nL2h6Z0kKG",
"timestamp": "2026-04-11T10:35:37.559Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 2
},
"cases_total": 2,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 100,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 0,
"routed_fragment_rate": 100,
"no_route_fragment_rate": 0,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 100,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 2,
"checks_passed": 2
},
"route_distribution": {
"store_feature_risk": 1,
"hybrid_store_plus_live": 1
},
"fallback_distribution": {
"none": 2
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь счет 60 за июнь 2020",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "gKYkJDhEWkCQTw",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Покажи риски по счету 97",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "YDK8VaP88Tx8dy",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,112 @@
{
"run_id": "eval-qYzl2S0IV9",
"timestamp": "2026-04-11T10:37:58.754Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 2
},
"cases_total": 2,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 100,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 0,
"routed_fragment_rate": 100,
"no_route_fragment_rate": 0,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 100,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 2,
"checks_passed": 2
},
"route_distribution": {
"store_feature_risk": 1,
"hybrid_store_plus_live": 1
},
"fallback_distribution": {
"none": 2
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь счет 60 за июнь 2020",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "3a_NZFB9MhKiKT",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Покажи риски по счету 97",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "BrD8ORrgm8qsAy",
"request_count_for_case": 0
}
]
}

View File

@ -0,0 +1,112 @@
{
"run_id": "eval-wWlVBoabZW",
"timestamp": "2026-04-11T10:14:44.777Z",
"mode": "single-pass-strict",
"use_mock": true,
"prompt_version": "normalizer_v2_0_2",
"schema_version": "v2_0_2",
"dataset": {
"source": "inline_raw_questions",
"file": null,
"raw_questions_count": 2
},
"cases_total": 2,
"metrics": {
"schema_validation_pass_rate": 100,
"scope_detection_accuracy": null,
"scope_in_scope_rate": 100,
"multi_intent_detected_rate": 0,
"clarification_required_rate": 0,
"avg_fragments_per_message": 1,
"out_of_scope_fragment_rate": 0,
"routed_fragment_rate": 100,
"no_route_fragment_rate": 0,
"route_resolution_accuracy": null,
"no_route_precision": null,
"false_no_route_rate": null,
"execution_state_consistency_rate": 100,
"executable_with_soft_assumptions_rate": 100,
"soft_assumption_used_fragment_rate": 100,
"clarification_precision": null,
"clarification_recall": null,
"false_clarification_rate": null
},
"budget": {
"requests_total": 0,
"retries_used": 0
},
"clarification_eval": {
"labeled_cases": 0,
"true_positive": 0,
"false_positive": 0,
"false_negative": 0
},
"route_eval": {
"labeled_cases": 0,
"correct_cases": 0,
"expected_routed_cases": 0,
"no_route_true_positive": 0,
"no_route_false_positive": 0
},
"scope_eval": {
"labeled_cases": 0,
"correct_cases": 0
},
"execution_state_eval": {
"checks_total": 2,
"checks_passed": 2
},
"route_distribution": {
"store_feature_risk": 1,
"hybrid_store_plus_live": 1
},
"fallback_distribution": {
"none": 2
},
"results": [
{
"case_id": "BQ-001",
"raw_question": "Проверь счет 60 за июнь 2020",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "3CW7yHQOH32rKS",
"request_count_for_case": 0
},
{
"case_id": "BQ-002",
"raw_question": "Покажи риски по счету 97",
"validation_passed": true,
"message_in_scope": true,
"scope_confidence": "high",
"contains_multiple_tasks": false,
"fragments_total": 1,
"in_scope_fragments": 1,
"out_of_scope_fragments": 0,
"unclear_fragments": 0,
"fallback_type": "none",
"predicted_route_status": "routed",
"expected_route_status": null,
"predicted_no_route_reason": null,
"expected_no_route_reason": null,
"predicted_clarification_required": false,
"expected_clarification_required": null,
"executable_with_soft_assumptions_fragments": 1,
"trace_id": "Gdw6ytd-K55hUm",
"request_count_for_case": 0
}
]
}

BIN
tmp_llm_gate_log.txt Normal file

Binary file not shown.