АРЧ - Добавить поддержку агентных смысловых прогонов в автопрогоны и правило AGENT в AGENTS.md
This commit is contained in:
parent
dc8dfcf237
commit
1484c2375f
10
AGENTS.md
10
AGENTS.md
|
|
@ -32,3 +32,13 @@ Rules:
|
||||||
- If a case falls outside the current routed contour because the route/intent/capability is not wired yet, treat it as domain enablement work for this project, not as automatic out-of-scope rejection.
|
- If a case falls outside the current routed contour because the route/intent/capability is not wired yet, treat it as domain enablement work for this project, not as automatic out-of-scope rejection.
|
||||||
- For new unmarked domains, `needs_exact_capability` means "bootstrap or extend the contour" rather than "close the case as unsupported".
|
- For new unmarked domains, `needs_exact_capability` means "bootstrap or extend the contour" rather than "close the case as unsupported".
|
||||||
- A case can be marked `accepted` only when analyst verdict is at least `80/100`, no unresolved `P0` remains, and the rerun does not mask heuristic output as confirmed.
|
- A case can be marked `accepted` only when analyst verdict is at least `80/100`, no unresolved `P0` remains, and the rerun does not mask heuristic output as confirmed.
|
||||||
|
|
||||||
|
## agent_semantic_runs
|
||||||
|
- `АГЕНТНЫЙ ПРОГОН` is a targeted full semantic replay for the current architecture fix, not a generic smoke test.
|
||||||
|
- Use it to validate human user questions, human model answers, technical chats, business logic, and system routing together.
|
||||||
|
- Build question lists around the active fix: mix direct domain questions with contextual chains, meta interruptions, cross-domain pivots, and follow-up edges that specifically hit the architecture change under validation.
|
||||||
|
- Save agent-built question packs into autoruns under `Пользовательские сессии` with title prefix `AGENT | ...`.
|
||||||
|
- Preferred repo-native save path: `python scripts/save_agent_semantic_run.py --spec <truth_harness_or_question_spec.json>`.
|
||||||
|
- Agent semantic runs must remain runnable by the user from the autoruns UI like any other saved user session.
|
||||||
|
- Do not run or save an `АГЕНТНЫЙ ПРОГОН` on every turn by default.
|
||||||
|
- Run it when the user explicitly asks for it, or when a substantial architecture/domain fix needs critical semantic proof beyond unit tests and narrow synthetic checks.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
{
|
||||||
|
"schema_version": "domain_truth_harness_spec_v1",
|
||||||
|
"scenario_id": "address_truth_harness_inventory_provenance_restore",
|
||||||
|
"domain": "inventory_selected_object_provenance_restore",
|
||||||
|
"title": "Targeted live replay for selected-object supplier provenance and root restore",
|
||||||
|
"description": "Strict short replay for the March 2021 stock snapshot, selected-item supplier question, item documents, and same-date root restore.",
|
||||||
|
"bindings": {},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step_id": "step_01_inventory_march_2021",
|
||||||
|
"title": "Inventory stock snapshot at March 2021",
|
||||||
|
"question": "какие остатки на складе на март 2021",
|
||||||
|
"allowed_reply_types": [
|
||||||
|
"factual"
|
||||||
|
],
|
||||||
|
"expected_intents": [
|
||||||
|
"inventory_on_hand_as_of_date"
|
||||||
|
],
|
||||||
|
"required_filters": {
|
||||||
|
"as_of_date": "2021-03-31",
|
||||||
|
"period_from": "2021-03-01",
|
||||||
|
"period_to": "2021-03-31"
|
||||||
|
},
|
||||||
|
"required_direct_answer_patterns_any": [
|
||||||
|
"31\\.03\\.2021",
|
||||||
|
"(?i)на складе",
|
||||||
|
"(?i)столешница 600\\*3050\\*26 альмандин"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_02_selected_item_supplier",
|
||||||
|
"title": "Selected-object supplier provenance",
|
||||||
|
"question": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?",
|
||||||
|
"allowed_reply_types": [
|
||||||
|
"factual"
|
||||||
|
],
|
||||||
|
"expected_intents": [
|
||||||
|
"inventory_purchase_provenance_for_item"
|
||||||
|
],
|
||||||
|
"required_direct_answer_patterns_any": [
|
||||||
|
"(?i)столешница 600\\*3050\\*26 альмандин",
|
||||||
|
"(?i)поставщик|поставил|куплен",
|
||||||
|
"(?i)союз|торговый дом"
|
||||||
|
],
|
||||||
|
"forbidden_direct_answer_patterns": [
|
||||||
|
"(?i)^на 31\\.03\\.2021 на складе",
|
||||||
|
"(?i)^не могу надежно подтвердить ответ"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_03_selected_item_documents",
|
||||||
|
"title": "Selected-object purchase documents",
|
||||||
|
"question": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": покажи документы по этой позиции",
|
||||||
|
"allowed_reply_types": [
|
||||||
|
"factual"
|
||||||
|
],
|
||||||
|
"expected_intents": [
|
||||||
|
"inventory_purchase_documents_for_item"
|
||||||
|
],
|
||||||
|
"required_direct_answer_patterns_any": [
|
||||||
|
"(?i)столешница 600\\*3050\\*26 альмандин",
|
||||||
|
"(?i)документ",
|
||||||
|
"(?i)союз|торговый дом"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_04_inventory_same_date_restore",
|
||||||
|
"title": "Restore root inventory snapshot on the same date",
|
||||||
|
"question": "покажи еще раз остатки на эту же дату",
|
||||||
|
"allowed_reply_types": [
|
||||||
|
"factual"
|
||||||
|
],
|
||||||
|
"expected_intents": [
|
||||||
|
"inventory_on_hand_as_of_date"
|
||||||
|
],
|
||||||
|
"required_filters": {
|
||||||
|
"as_of_date": "2021-03-31",
|
||||||
|
"period_from": "2021-03-01",
|
||||||
|
"period_to": "2021-03-31"
|
||||||
|
},
|
||||||
|
"required_direct_answer_patterns_any": [
|
||||||
|
"31\\.03\\.2021",
|
||||||
|
"(?i)на складе"
|
||||||
|
],
|
||||||
|
"forbidden_direct_answer_patterns": [
|
||||||
|
"(?i)^не могу надежно подтвердить ответ",
|
||||||
|
"(?i)transition_not_supported_by_capability"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -183,7 +183,13 @@ function readAutoGenHistory() {
|
||||||
: null,
|
: null,
|
||||||
source_session_id: toStringSafe(toRecord(item.context)?.source_session_id),
|
source_session_id: toStringSafe(toRecord(item.context)?.source_session_id),
|
||||||
saved_session_file: toStringSafe(toRecord(item.context)?.saved_session_file),
|
saved_session_file: toStringSafe(toRecord(item.context)?.saved_session_file),
|
||||||
saved_case_set_kind: toStringSafe(toRecord(item.context)?.saved_case_set_kind)
|
saved_case_set_kind: toStringSafe(toRecord(item.context)?.saved_case_set_kind),
|
||||||
|
agent_run: toBooleanSafe(toRecord(item.context)?.agent_run),
|
||||||
|
agent_focus: toStringSafe(toRecord(item.context)?.agent_focus)
|
||||||
|
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
|
||||||
|
: null,
|
||||||
|
architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase),
|
||||||
|
source_spec_file: toStringSafe(toRecord(item.context)?.source_spec_file)
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}))
|
}))
|
||||||
|
|
@ -1486,7 +1492,7 @@ function buildSavedSessionCaseSetPayload(input) {
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
case_id: caseId,
|
case_id: caseId,
|
||||||
scenario_tag: "saved_user_sessions",
|
scenario_tag: toStringSafe(input.scenarioTag) ?? "saved_user_sessions",
|
||||||
title: input.title,
|
title: input.title,
|
||||||
question_type: turns.length > 1 ? "followup" : "direct",
|
question_type: turns.length > 1 ? "followup" : "direct",
|
||||||
broadness_level: "medium",
|
broadness_level: "medium",
|
||||||
|
|
@ -1515,7 +1521,10 @@ function rewriteAutoGenCaseSetFile(record) {
|
||||||
? buildSavedSessionCaseSetPayload({
|
? buildSavedSessionCaseSetPayload({
|
||||||
generationId: record.generation_id,
|
generationId: record.generation_id,
|
||||||
title: record.title,
|
title: record.title,
|
||||||
questions: record.questions
|
questions: record.questions,
|
||||||
|
scenarioTag: record.context?.saved_case_set_kind === "agent_semantic_scenario"
|
||||||
|
? "agent_saved_user_sessions"
|
||||||
|
: "saved_user_sessions"
|
||||||
})
|
})
|
||||||
: buildAutogenCaseSetPayload({
|
: buildAutogenCaseSetPayload({
|
||||||
generationId: record.generation_id,
|
generationId: record.generation_id,
|
||||||
|
|
@ -2064,7 +2073,8 @@ function buildAutoRunsRouter(services, openaiClient = new openaiResponsesClient_
|
||||||
writeJsonFile(caseSetPath, buildSavedSessionCaseSetPayload({
|
writeJsonFile(caseSetPath, buildSavedSessionCaseSetPayload({
|
||||||
generationId,
|
generationId,
|
||||||
title,
|
title,
|
||||||
questions
|
questions,
|
||||||
|
scenarioTag: "saved_user_sessions"
|
||||||
}));
|
}));
|
||||||
const snapshotFile = writeSavedAssistantSessionSnapshot({
|
const snapshotFile = writeSavedAssistantSessionSnapshot({
|
||||||
generationId,
|
generationId,
|
||||||
|
|
|
||||||
|
|
@ -3430,6 +3430,107 @@ class AddressQueryService {
|
||||||
debug: debugPayload
|
debug: debugPayload
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
const buildFactualExecutionResult = (input) => {
|
||||||
|
const resultSemantics = mergeAddressResultSemantics(deriveAddressResultSemantics({
|
||||||
|
intent: intent.intent,
|
||||||
|
selectedRecipe: input.selectedRecipe,
|
||||||
|
filters: input.extractedFilters ?? filters.extracted_filters,
|
||||||
|
semanticFrame: input.semanticFrame ?? semanticFrame,
|
||||||
|
responseType: input.responseType,
|
||||||
|
rowsMatched: input.rowsMatched
|
||||||
|
}), input.responseSemantics);
|
||||||
|
const routeExpectationAudit = input.routeExpectationAudit ??
|
||||||
|
buildRouteExpectationAudit({
|
||||||
|
intent: routeExpectationIntent,
|
||||||
|
selectedRecipe: input.selectedRecipe,
|
||||||
|
requestedResultMode,
|
||||||
|
resultMode: resultSemantics.result_mode
|
||||||
|
});
|
||||||
|
const debugPayload = (0, addressTruthGatePolicy_1.attachAddressTruthGate)({
|
||||||
|
detected_mode: mode.mode,
|
||||||
|
detected_mode_confidence: mode.confidence,
|
||||||
|
query_shape: shape.shape,
|
||||||
|
query_shape_confidence: shape.confidence,
|
||||||
|
detected_intent: intent.intent,
|
||||||
|
detected_intent_confidence: intent.confidence,
|
||||||
|
extracted_filters: input.extractedFilters ?? filters.extracted_filters,
|
||||||
|
missing_required_filters: [],
|
||||||
|
selected_recipe: input.selectedRecipe,
|
||||||
|
mcp_call_status_legacy: toLegacyMcpStatus(input.mcpCallStatus),
|
||||||
|
account_scope_mode: input.accountScopeMode,
|
||||||
|
account_scope_fallback_applied: input.accountScopeFallbackApplied,
|
||||||
|
anchor_type: input.anchor.anchor_type,
|
||||||
|
anchor_value_raw: input.anchor.anchor_value_raw,
|
||||||
|
anchor_value_resolved: input.anchor.anchor_value_resolved,
|
||||||
|
resolver_confidence: input.anchor.resolver_confidence,
|
||||||
|
ambiguity_count: input.anchor.ambiguity_count,
|
||||||
|
match_failure_stage: input.matchFailureStage,
|
||||||
|
match_failure_reason: input.matchFailureReason,
|
||||||
|
mcp_call_status: input.mcpCallStatus,
|
||||||
|
rows_fetched: input.rowsFetched,
|
||||||
|
raw_rows_received: input.rawRowsReceived,
|
||||||
|
rows_after_account_scope: input.rowsAfterAccountScope,
|
||||||
|
rows_after_recipe_filter: input.rowsAfterRecipeFilter,
|
||||||
|
rows_materialized: input.rowsMaterialized,
|
||||||
|
rows_matched: input.rowsMatched,
|
||||||
|
raw_row_keys_sample: input.rawRowKeysSample,
|
||||||
|
materialization_drop_reason: input.materializationDropReason,
|
||||||
|
account_token_raw: input.accountScopeAudit.accountTokenRaw,
|
||||||
|
account_token_normalized: input.accountScopeAudit.accountTokenNormalized,
|
||||||
|
account_scope_fields_checked: input.accountScopeAudit.accountScopeFieldsChecked,
|
||||||
|
account_scope_match_strategy: input.accountScopeAudit.accountScopeMatchStrategy,
|
||||||
|
account_scope_drop_reason: input.accountScopeAudit.accountScopeDropReason,
|
||||||
|
runtime_readiness: input.runtimeReadiness ?? "LIVE_QUERYABLE_WITH_LIMITS",
|
||||||
|
limited_reason_category: input.limitedReasonCategory ?? null,
|
||||||
|
response_type: input.responseType,
|
||||||
|
route_expectation_status: routeExpectationAudit.status,
|
||||||
|
route_expectation_reason: routeExpectationAudit.reason,
|
||||||
|
route_expectation_expected_selected_recipes: routeExpectationAudit.expectedSelectedRecipes,
|
||||||
|
route_expectation_expected_requested_result_modes: routeExpectationAudit.expectedRequestedResultModes,
|
||||||
|
route_expectation_expected_result_modes: routeExpectationAudit.expectedResultModes,
|
||||||
|
semantic_frame: input.semanticFrame ?? semanticFrame,
|
||||||
|
...resultSemantics,
|
||||||
|
limitations: input.limitations,
|
||||||
|
reasons: input.reasons,
|
||||||
|
...(input.capabilityAudit
|
||||||
|
? {
|
||||||
|
capability_id: input.capabilityAudit.capabilityId,
|
||||||
|
capability_layer: input.capabilityAudit.layer,
|
||||||
|
capability_route_mode: input.capabilityAudit.routeMode,
|
||||||
|
capability_route_enabled: input.capabilityAudit.enabled,
|
||||||
|
capability_route_reason: input.capabilityAudit.reason
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(input.shadowRouteAudit
|
||||||
|
? {
|
||||||
|
shadow_route_intent: input.shadowRouteAudit.intent,
|
||||||
|
shadow_route_selected_recipe: input.shadowRouteAudit.selectedRecipe,
|
||||||
|
shadow_route_status: input.shadowRouteAudit.status
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
}, {
|
||||||
|
intent: intent.intent,
|
||||||
|
filters: input.extractedFilters ?? filters.extracted_filters,
|
||||||
|
semanticFrame: input.semanticFrame ?? semanticFrame,
|
||||||
|
selectedRecipe: input.selectedRecipe,
|
||||||
|
truthGateStatusHint: input.truthGateStatusHint ?? null,
|
||||||
|
rowsMatched: input.rowsMatched,
|
||||||
|
limitedReasonCategory: input.limitedReasonCategory ?? null,
|
||||||
|
runtimeReadiness: input.runtimeReadiness ?? "LIVE_QUERYABLE_WITH_LIMITS",
|
||||||
|
limitations: input.limitations,
|
||||||
|
reasons: input.reasons,
|
||||||
|
routeExpectationStatus: routeExpectationAudit.status,
|
||||||
|
routeExpectationReason: routeExpectationAudit.reason,
|
||||||
|
replyType: (0, composeStage_1.inferReplyType)(input.responseType)
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
handled: true,
|
||||||
|
reply_text: input.replyText,
|
||||||
|
reply_type: (0, composeStage_1.inferReplyType)(input.responseType),
|
||||||
|
response_type: input.responseType,
|
||||||
|
debug: debugPayload
|
||||||
|
};
|
||||||
|
};
|
||||||
if (organizationWarehouseRecoveryApplied) {
|
if (organizationWarehouseRecoveryApplied) {
|
||||||
if (!baseReasons.includes("organization_scope_live_grounding_recovered_rows")) {
|
if (!baseReasons.includes("organization_scope_live_grounding_recovered_rows")) {
|
||||||
baseReasons.push("organization_scope_live_grounding_recovered_rows");
|
baseReasons.push("organization_scope_live_grounding_recovered_rows");
|
||||||
|
|
@ -3481,60 +3582,33 @@ class AddressQueryService {
|
||||||
const replyPrefix = recoveredBankRows.length > 0
|
const replyPrefix = recoveredBankRows.length > 0
|
||||||
? "Документный фильтр в live дал пустой набор; показываю связанные банковские операции по договору."
|
? "Документный фильтр в live дал пустой набор; показываю связанные банковские операции по договору."
|
||||||
: "Документный фильтр в live дал пустой набор; показываю найденные строки по договорному якорю.";
|
: "Документный фильтр в live дал пустой набор; показываю найденные строки по договорному якорю.";
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: `${replyPrefix}\n${factual.text}`,
|
||||||
reply_text: `${replyPrefix}\n${factual.text}`,
|
|
||||||
reply_type: (0, composeStage_1.inferReplyType)(factual.responseType),
|
|
||||||
response_type: factual.responseType,
|
|
||||||
debug: {
|
|
||||||
detected_mode: mode.mode,
|
|
||||||
detected_mode_confidence: mode.confidence,
|
|
||||||
query_shape: shape.shape,
|
|
||||||
query_shape_confidence: shape.confidence,
|
|
||||||
detected_intent: intent.intent,
|
|
||||||
detected_intent_confidence: intent.confidence,
|
|
||||||
extracted_filters: filters.extracted_filters,
|
|
||||||
missing_required_filters: [],
|
|
||||||
selected_recipe: effectiveRecipeId,
|
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus("matched_non_empty"),
|
|
||||||
account_scope_mode: plan.account_scope_mode,
|
|
||||||
account_scope_fallback_applied: accountScopeFallbackApplied,
|
|
||||||
anchor_type: anchor.anchor_type,
|
|
||||||
anchor_value_raw: anchor.anchor_value_raw,
|
|
||||||
anchor_value_resolved: anchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: anchor.resolver_confidence,
|
|
||||||
ambiguity_count: anchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: "matched_non_empty",
|
|
||||||
rows_fetched: mcp.fetched_rows,
|
|
||||||
raw_rows_received: mcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: normalizedRows.length,
|
|
||||||
rows_after_recipe_filter: filterByAnchors.length,
|
|
||||||
rows_materialized: normalizedRows.length,
|
|
||||||
rows_matched: recoveredRows.length,
|
|
||||||
raw_row_keys_sample: rowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: rowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: accountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: accountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: accountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: accountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: accountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: factual.responseType,
|
|
||||||
...mergeAddressResultSemantics(deriveAddressResultSemantics({
|
|
||||||
intent: intent.intent,
|
|
||||||
selectedRecipe: effectiveRecipeId,
|
|
||||||
filters: filters.extracted_filters,
|
|
||||||
semanticFrame,
|
|
||||||
responseType: factual.responseType,
|
responseType: factual.responseType,
|
||||||
rowsMatched: recoveredRows.length
|
responseSemantics: factual.semantics,
|
||||||
}), factual.semantics),
|
selectedRecipe: effectiveRecipeId,
|
||||||
|
mcpCallStatus: "matched_non_empty",
|
||||||
|
rowsFetched: mcp.fetched_rows,
|
||||||
|
rawRowsReceived: mcp.raw_rows.length,
|
||||||
|
rowsAfterAccountScope: normalizedRows.length,
|
||||||
|
rowsAfterRecipeFilter: filterByAnchors.length,
|
||||||
|
rowsMaterialized: normalizedRows.length,
|
||||||
|
rowsMatched: recoveredRows.length,
|
||||||
|
rawRowKeysSample: rowDiagnostics.rawRowKeysSample,
|
||||||
|
materializationDropReason: rowDiagnostics.materializationDropReason,
|
||||||
|
accountScopeMode: plan.account_scope_mode,
|
||||||
|
accountScopeFallbackApplied,
|
||||||
|
accountScopeAudit,
|
||||||
|
anchor,
|
||||||
|
matchFailureStage: "none",
|
||||||
|
matchFailureReason: null,
|
||||||
limitations: [...filters.warnings, recoveryReason],
|
limitations: [...filters.warnings, recoveryReason],
|
||||||
reasons: withConfirmedBalanceFallbackReason([...baseReasons, recoveryReason], requestedResultMode, factual.semantics)
|
reasons: withConfirmedBalanceFallbackReason([...baseReasons, recoveryReason], requestedResultMode, factual.semantics),
|
||||||
}
|
limitedReasonCategory: "recipe_visibility_gap",
|
||||||
};
|
capabilityAudit,
|
||||||
|
shadowRouteAudit,
|
||||||
|
semanticFrame
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filteredRows.length === 0 &&
|
if (filteredRows.length === 0 &&
|
||||||
|
|
@ -3605,60 +3679,32 @@ class AddressQueryService {
|
||||||
const expandedPrefix = `Период сохранен. Глубина live-выборки автоматически расширена до ${expandedPlan.limit} строк.`;
|
const expandedPrefix = `Период сохранен. Глубина live-выборки автоматически расширена до ${expandedPlan.limit} строк.`;
|
||||||
const expandedLimitations = [...filters.warnings, "query_limit_auto_expanded_for_anchor_recovery"];
|
const expandedLimitations = [...filters.warnings, "query_limit_auto_expanded_for_anchor_recovery"];
|
||||||
const expandedReasons = [...baseReasons, "query_limit_auto_expanded_for_anchor_recovery"];
|
const expandedReasons = [...baseReasons, "query_limit_auto_expanded_for_anchor_recovery"];
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: `${expandedPrefix}\n${expandedFactual.text}`,
|
||||||
reply_text: `${expandedPrefix}\n${expandedFactual.text}`,
|
|
||||||
reply_type: (0, composeStage_1.inferReplyType)(expandedFactual.responseType),
|
|
||||||
response_type: expandedFactual.responseType,
|
|
||||||
debug: {
|
|
||||||
detected_mode: mode.mode,
|
|
||||||
detected_mode_confidence: mode.confidence,
|
|
||||||
query_shape: shape.shape,
|
|
||||||
query_shape_confidence: shape.confidence,
|
|
||||||
detected_intent: intent.intent,
|
|
||||||
detected_intent_confidence: intent.confidence,
|
|
||||||
extracted_filters: filters.extracted_filters,
|
|
||||||
missing_required_filters: [],
|
|
||||||
selected_recipe: expandedSelection.selected_recipe.recipe_id,
|
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus(expandedStageStatus),
|
|
||||||
account_scope_mode: expandedPlan.account_scope_mode,
|
|
||||||
account_scope_fallback_applied: expandedAccountScopeFallbackApplied,
|
|
||||||
anchor_type: expandedAnchor.anchor_type,
|
|
||||||
anchor_value_raw: expandedAnchor.anchor_value_raw,
|
|
||||||
anchor_value_resolved: expandedAnchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: expandedAnchor.resolver_confidence,
|
|
||||||
ambiguity_count: expandedAnchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: expandedStageStatus,
|
|
||||||
rows_fetched: expandedMcp.fetched_rows,
|
|
||||||
raw_rows_received: expandedMcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: expandedNormalizedRows.length,
|
|
||||||
rows_after_recipe_filter: expandedRowsByAnchor.length,
|
|
||||||
rows_materialized: expandedNormalizedRows.length,
|
|
||||||
rows_matched: expandedFilteredRows.length,
|
|
||||||
raw_row_keys_sample: expandedRowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: expandedRowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: expandedAccountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: expandedAccountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: expandedAccountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: expandedAccountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: expandedAccountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: expandedFactual.responseType,
|
|
||||||
...mergeAddressResultSemantics(deriveAddressResultSemantics({
|
|
||||||
intent: intent.intent,
|
|
||||||
selectedRecipe: expandedSelection.selected_recipe.recipe_id,
|
|
||||||
filters: filters.extracted_filters,
|
|
||||||
semanticFrame,
|
|
||||||
responseType: expandedFactual.responseType,
|
responseType: expandedFactual.responseType,
|
||||||
rowsMatched: expandedFilteredRows.length
|
responseSemantics: expandedFactual.semantics,
|
||||||
}), expandedFactual.semantics),
|
selectedRecipe: expandedSelection.selected_recipe.recipe_id,
|
||||||
|
mcpCallStatus: expandedStageStatus,
|
||||||
|
rowsFetched: expandedMcp.fetched_rows,
|
||||||
|
rawRowsReceived: expandedMcp.raw_rows.length,
|
||||||
|
rowsAfterAccountScope: expandedNormalizedRows.length,
|
||||||
|
rowsAfterRecipeFilter: expandedRowsByAnchor.length,
|
||||||
|
rowsMaterialized: expandedNormalizedRows.length,
|
||||||
|
rowsMatched: expandedFilteredRows.length,
|
||||||
|
rawRowKeysSample: expandedRowDiagnostics.rawRowKeysSample,
|
||||||
|
materializationDropReason: expandedRowDiagnostics.materializationDropReason,
|
||||||
|
accountScopeMode: expandedPlan.account_scope_mode,
|
||||||
|
accountScopeFallbackApplied: expandedAccountScopeFallbackApplied,
|
||||||
|
accountScopeAudit: expandedAccountScopeAudit,
|
||||||
|
anchor: expandedAnchor,
|
||||||
|
matchFailureStage: "none",
|
||||||
|
matchFailureReason: null,
|
||||||
limitations: expandedLimitations,
|
limitations: expandedLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(expandedReasons, requestedResultMode, expandedFactual.semantics)
|
reasons: withConfirmedBalanceFallbackReason(expandedReasons, requestedResultMode, expandedFactual.semantics),
|
||||||
}
|
capabilityAudit,
|
||||||
};
|
shadowRouteAudit,
|
||||||
|
semanticFrame
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3752,67 +3798,34 @@ class AddressQueryService {
|
||||||
requestedResultMode,
|
requestedResultMode,
|
||||||
resultMode: broadenedResultSemantics.result_mode
|
resultMode: broadenedResultSemantics.result_mode
|
||||||
});
|
});
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: injectNoticeAfterLeadLine(broadenedFactual.text, broadenedPrefix),
|
||||||
reply_text: injectNoticeAfterLeadLine(broadenedFactual.text, broadenedPrefix),
|
responseType: broadenedFactual.responseType,
|
||||||
reply_type: (0, composeStage_1.inferReplyType)(broadenedFactual.responseType),
|
responseSemantics: broadenedFactual.semantics,
|
||||||
response_type: broadenedFactual.responseType,
|
selectedRecipe: broadenedSelection.selected_recipe.recipe_id,
|
||||||
debug: {
|
mcpCallStatus: broadenedStageStatus,
|
||||||
detected_mode: mode.mode,
|
rowsFetched: broadenedMcp.fetched_rows,
|
||||||
detected_mode_confidence: mode.confidence,
|
rawRowsReceived: broadenedMcp.raw_rows.length,
|
||||||
query_shape: shape.shape,
|
rowsAfterAccountScope: broadenedNormalizedRows.length,
|
||||||
query_shape_confidence: shape.confidence,
|
rowsAfterRecipeFilter: broadenedRowsByAnchor.length,
|
||||||
detected_intent: intent.intent,
|
rowsMaterialized: broadenedNormalizedRows.length,
|
||||||
detected_intent_confidence: intent.confidence,
|
rowsMatched: broadenedFilteredRows.length,
|
||||||
extracted_filters: filters.extracted_filters,
|
rawRowKeysSample: broadenedRowDiagnostics.rawRowKeysSample,
|
||||||
missing_required_filters: [],
|
materializationDropReason: broadenedRowDiagnostics.materializationDropReason,
|
||||||
selected_recipe: broadenedSelection.selected_recipe.recipe_id,
|
accountScopeMode: broadenedPlan.account_scope_mode,
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus(broadenedStageStatus),
|
accountScopeFallbackApplied: broadenedAccountScopeFallbackApplied,
|
||||||
account_scope_mode: broadenedPlan.account_scope_mode,
|
accountScopeAudit: broadenedAccountScopeAudit,
|
||||||
account_scope_fallback_applied: broadenedAccountScopeFallbackApplied,
|
anchor: broadenedAnchor,
|
||||||
anchor_type: broadenedAnchor.anchor_type,
|
matchFailureStage: "none",
|
||||||
anchor_value_raw: broadenedAnchor.anchor_value_raw,
|
matchFailureReason: null,
|
||||||
anchor_value_resolved: broadenedAnchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: broadenedAnchor.resolver_confidence,
|
|
||||||
ambiguity_count: broadenedAnchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: broadenedStageStatus,
|
|
||||||
rows_fetched: broadenedMcp.fetched_rows,
|
|
||||||
raw_rows_received: broadenedMcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: broadenedNormalizedRows.length,
|
|
||||||
rows_after_recipe_filter: broadenedRowsByAnchor.length,
|
|
||||||
rows_materialized: broadenedNormalizedRows.length,
|
|
||||||
rows_matched: broadenedFilteredRows.length,
|
|
||||||
raw_row_keys_sample: broadenedRowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: broadenedRowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: broadenedAccountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: broadenedAccountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: broadenedAccountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: broadenedAccountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: broadenedAccountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: broadenedFactual.responseType,
|
|
||||||
capability_id: capabilityAudit.capabilityId,
|
|
||||||
capability_layer: capabilityAudit.layer,
|
|
||||||
capability_route_mode: capabilityAudit.routeMode,
|
|
||||||
capability_route_enabled: capabilityAudit.enabled,
|
|
||||||
capability_route_reason: capabilityAudit.reason,
|
|
||||||
shadow_route_intent: shadowRouteAudit.intent,
|
|
||||||
shadow_route_selected_recipe: shadowRouteAudit.selectedRecipe,
|
|
||||||
shadow_route_status: shadowRouteAudit.status,
|
|
||||||
route_expectation_status: broadenedRouteExpectationAudit.status,
|
|
||||||
route_expectation_reason: broadenedRouteExpectationAudit.reason,
|
|
||||||
route_expectation_expected_selected_recipes: broadenedRouteExpectationAudit.expectedSelectedRecipes,
|
|
||||||
route_expectation_expected_requested_result_modes: broadenedRouteExpectationAudit.expectedRequestedResultModes,
|
|
||||||
route_expectation_expected_result_modes: broadenedRouteExpectationAudit.expectedResultModes,
|
|
||||||
semantic_frame: semanticFrame,
|
|
||||||
...broadenedResultSemantics,
|
|
||||||
limitations: broadenedLimitations,
|
limitations: broadenedLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(broadenedReasons, requestedResultMode, broadenedFactual.semantics)
|
reasons: withConfirmedBalanceFallbackReason(broadenedReasons, requestedResultMode, broadenedFactual.semantics),
|
||||||
}
|
routeExpectationAudit: broadenedRouteExpectationAudit,
|
||||||
};
|
capabilityAudit,
|
||||||
|
shadowRouteAudit,
|
||||||
|
semanticFrame,
|
||||||
|
truthGateStatusHint: "limited_temporal_or_contextual"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3887,60 +3900,33 @@ class AddressQueryService {
|
||||||
: "";
|
: "";
|
||||||
const historicalLimitations = [...filters.warnings, "historical_window_sort_recovery_applied"];
|
const historicalLimitations = [...filters.warnings, "historical_window_sort_recovery_applied"];
|
||||||
const historicalReasons = [...baseReasons, "historical_window_sort_recovery_applied"];
|
const historicalReasons = [...baseReasons, "historical_window_sort_recovery_applied"];
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: `${historicalPrefix}\n${historicalFactual.text}${historicalSuggestion}`,
|
||||||
reply_text: `${historicalPrefix}\n${historicalFactual.text}${historicalSuggestion}`,
|
|
||||||
reply_type: (0, composeStage_1.inferReplyType)(historicalFactual.responseType),
|
|
||||||
response_type: historicalFactual.responseType,
|
|
||||||
debug: {
|
|
||||||
detected_mode: mode.mode,
|
|
||||||
detected_mode_confidence: mode.confidence,
|
|
||||||
query_shape: shape.shape,
|
|
||||||
query_shape_confidence: shape.confidence,
|
|
||||||
detected_intent: intent.intent,
|
|
||||||
detected_intent_confidence: intent.confidence,
|
|
||||||
extracted_filters: filters.extracted_filters,
|
|
||||||
missing_required_filters: [],
|
|
||||||
selected_recipe: historicalSelection.selected_recipe.recipe_id,
|
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus(historicalStageStatus),
|
|
||||||
account_scope_mode: historicalPlan.account_scope_mode,
|
|
||||||
account_scope_fallback_applied: historicalAccountScopeFallbackApplied,
|
|
||||||
anchor_type: historicalAnchor.anchor_type,
|
|
||||||
anchor_value_raw: historicalAnchor.anchor_value_raw,
|
|
||||||
anchor_value_resolved: historicalAnchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: historicalAnchor.resolver_confidence,
|
|
||||||
ambiguity_count: historicalAnchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: historicalStageStatus,
|
|
||||||
rows_fetched: historicalMcp.fetched_rows,
|
|
||||||
raw_rows_received: historicalMcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: historicalNormalizedRows.length,
|
|
||||||
rows_after_recipe_filter: historicalRowsByAnchor.length,
|
|
||||||
rows_materialized: historicalNormalizedRows.length,
|
|
||||||
rows_matched: historicalFilteredRows.length,
|
|
||||||
raw_row_keys_sample: historicalRowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: historicalRowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: historicalAccountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: historicalAccountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: historicalAccountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: historicalAccountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: historicalAccountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: historicalFactual.responseType,
|
|
||||||
...mergeAddressResultSemantics(deriveAddressResultSemantics({
|
|
||||||
intent: intent.intent,
|
|
||||||
selectedRecipe: historicalSelection.selected_recipe.recipe_id,
|
|
||||||
filters: filters.extracted_filters,
|
|
||||||
semanticFrame,
|
|
||||||
responseType: historicalFactual.responseType,
|
responseType: historicalFactual.responseType,
|
||||||
rowsMatched: historicalFilteredRows.length
|
responseSemantics: historicalFactual.semantics,
|
||||||
}), historicalFactual.semantics),
|
selectedRecipe: historicalSelection.selected_recipe.recipe_id,
|
||||||
|
mcpCallStatus: historicalStageStatus,
|
||||||
|
rowsFetched: historicalMcp.fetched_rows,
|
||||||
|
rawRowsReceived: historicalMcp.raw_rows.length,
|
||||||
|
rowsAfterAccountScope: historicalNormalizedRows.length,
|
||||||
|
rowsAfterRecipeFilter: historicalRowsByAnchor.length,
|
||||||
|
rowsMaterialized: historicalNormalizedRows.length,
|
||||||
|
rowsMatched: historicalFilteredRows.length,
|
||||||
|
rawRowKeysSample: historicalRowDiagnostics.rawRowKeysSample,
|
||||||
|
materializationDropReason: historicalRowDiagnostics.materializationDropReason,
|
||||||
|
accountScopeMode: historicalPlan.account_scope_mode,
|
||||||
|
accountScopeFallbackApplied: historicalAccountScopeFallbackApplied,
|
||||||
|
accountScopeAudit: historicalAccountScopeAudit,
|
||||||
|
anchor: historicalAnchor,
|
||||||
|
matchFailureStage: "none",
|
||||||
|
matchFailureReason: null,
|
||||||
limitations: historicalLimitations,
|
limitations: historicalLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(historicalReasons, requestedResultMode, historicalFactual.semantics)
|
reasons: withConfirmedBalanceFallbackReason(historicalReasons, requestedResultMode, historicalFactual.semantics),
|
||||||
}
|
capabilityAudit,
|
||||||
};
|
shadowRouteAudit,
|
||||||
|
semanticFrame,
|
||||||
|
truthGateStatusHint: "limited_temporal_or_contextual"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3959,60 +3945,33 @@ class AddressQueryService {
|
||||||
: "";
|
: "";
|
||||||
const fallbackLimitations = [...filters.warnings, "anchor_not_matched_fallback_rows"];
|
const fallbackLimitations = [...filters.warnings, "anchor_not_matched_fallback_rows"];
|
||||||
const fallbackReasons = [...baseReasons, "anchor_not_matched_fallback_rows"];
|
const fallbackReasons = [...baseReasons, "anchor_not_matched_fallback_rows"];
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: `${fallbackPrefix}\n${fallbackFactual.text}${fallbackSuggestion}`,
|
||||||
reply_text: `${fallbackPrefix}\n${fallbackFactual.text}${fallbackSuggestion}`,
|
|
||||||
reply_type: (0, composeStage_1.inferReplyType)(fallbackFactual.responseType),
|
|
||||||
response_type: fallbackFactual.responseType,
|
|
||||||
debug: {
|
|
||||||
detected_mode: mode.mode,
|
|
||||||
detected_mode_confidence: mode.confidence,
|
|
||||||
query_shape: shape.shape,
|
|
||||||
query_shape_confidence: shape.confidence,
|
|
||||||
detected_intent: intent.intent,
|
|
||||||
detected_intent_confidence: intent.confidence,
|
|
||||||
extracted_filters: filters.extracted_filters,
|
|
||||||
missing_required_filters: [],
|
|
||||||
selected_recipe: effectiveRecipeId,
|
|
||||||
mcp_call_status_legacy: "matched_non_empty",
|
|
||||||
account_scope_mode: plan.account_scope_mode,
|
|
||||||
account_scope_fallback_applied: accountScopeFallbackApplied,
|
|
||||||
anchor_type: anchor.anchor_type,
|
|
||||||
anchor_value_raw: anchor.anchor_value_raw,
|
|
||||||
anchor_value_resolved: anchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: anchor.resolver_confidence,
|
|
||||||
ambiguity_count: anchor.ambiguity_count,
|
|
||||||
match_failure_stage: matchFailureStage,
|
|
||||||
match_failure_reason: matchFailureReason,
|
|
||||||
mcp_call_status: "matched_non_empty",
|
|
||||||
rows_fetched: mcp.fetched_rows,
|
|
||||||
raw_rows_received: mcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: normalizedRows.length,
|
|
||||||
rows_after_recipe_filter: filterByAnchors.length,
|
|
||||||
rows_materialized: normalizedRows.length,
|
|
||||||
rows_matched: documentBankFallbackRows.length,
|
|
||||||
raw_row_keys_sample: rowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: rowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: accountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: accountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: accountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: accountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: accountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: fallbackFactual.responseType,
|
|
||||||
...mergeAddressResultSemantics(deriveAddressResultSemantics({
|
|
||||||
intent: intent.intent,
|
|
||||||
selectedRecipe: effectiveRecipeId,
|
|
||||||
filters: filters.extracted_filters,
|
|
||||||
semanticFrame,
|
|
||||||
responseType: fallbackFactual.responseType,
|
responseType: fallbackFactual.responseType,
|
||||||
rowsMatched: documentBankFallbackRows.length
|
responseSemantics: fallbackFactual.semantics,
|
||||||
}), fallbackFactual.semantics),
|
selectedRecipe: effectiveRecipeId,
|
||||||
|
mcpCallStatus: "matched_non_empty",
|
||||||
|
rowsFetched: mcp.fetched_rows,
|
||||||
|
rawRowsReceived: mcp.raw_rows.length,
|
||||||
|
rowsAfterAccountScope: normalizedRows.length,
|
||||||
|
rowsAfterRecipeFilter: filterByAnchors.length,
|
||||||
|
rowsMaterialized: normalizedRows.length,
|
||||||
|
rowsMatched: documentBankFallbackRows.length,
|
||||||
|
rawRowKeysSample: rowDiagnostics.rawRowKeysSample,
|
||||||
|
materializationDropReason: rowDiagnostics.materializationDropReason,
|
||||||
|
accountScopeMode: plan.account_scope_mode,
|
||||||
|
accountScopeFallbackApplied,
|
||||||
|
accountScopeAudit,
|
||||||
|
anchor,
|
||||||
|
matchFailureStage,
|
||||||
|
matchFailureReason,
|
||||||
limitations: fallbackLimitations,
|
limitations: fallbackLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(fallbackReasons, requestedResultMode, fallbackFactual.semantics)
|
reasons: withConfirmedBalanceFallbackReason(fallbackReasons, requestedResultMode, fallbackFactual.semantics),
|
||||||
}
|
capabilityAudit,
|
||||||
};
|
shadowRouteAudit,
|
||||||
|
limitedReasonCategory: "recipe_visibility_gap",
|
||||||
|
semanticFrame
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const allowConfirmedAsOfZeroSnapshot = filteredRows.length === 0 &&
|
const allowConfirmedAsOfZeroSnapshot = filteredRows.length === 0 &&
|
||||||
|
|
@ -4297,67 +4256,33 @@ class AddressQueryService {
|
||||||
const reasonsWithRouteExpectation = finalRouteExpectationAudit.status === "mismatch"
|
const reasonsWithRouteExpectation = finalRouteExpectationAudit.status === "mismatch"
|
||||||
? [...baseReasons, `route_expectation_mismatch:${finalRouteExpectationAudit.reason}`]
|
? [...baseReasons, `route_expectation_mismatch:${finalRouteExpectationAudit.reason}`]
|
||||||
: baseReasons;
|
: baseReasons;
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: factual.text,
|
||||||
reply_text: factual.text,
|
responseType: factual.responseType,
|
||||||
reply_type: (0, composeStage_1.inferReplyType)(factual.responseType),
|
responseSemantics: factual.semantics,
|
||||||
response_type: factual.responseType,
|
selectedRecipe: effectiveRecipeId,
|
||||||
debug: {
|
mcpCallStatus: stageStatus,
|
||||||
detected_mode: mode.mode,
|
rowsFetched: mcp.fetched_rows,
|
||||||
detected_mode_confidence: mode.confidence,
|
rawRowsReceived: mcp.raw_rows.length,
|
||||||
query_shape: shape.shape,
|
rowsAfterAccountScope: normalizedRows.length,
|
||||||
query_shape_confidence: shape.confidence,
|
rowsAfterRecipeFilter: filterByAnchors.length,
|
||||||
detected_intent: intent.intent,
|
rowsMaterialized: normalizedRows.length,
|
||||||
detected_intent_confidence: intent.confidence,
|
rowsMatched: filteredRows.length,
|
||||||
extracted_filters: filters.extracted_filters,
|
rawRowKeysSample: rowDiagnostics.rawRowKeysSample,
|
||||||
missing_required_filters: [],
|
materializationDropReason: rowDiagnostics.materializationDropReason,
|
||||||
selected_recipe: effectiveRecipeId,
|
accountScopeMode: plan.account_scope_mode,
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus(stageStatus),
|
accountScopeFallbackApplied,
|
||||||
account_scope_mode: plan.account_scope_mode,
|
accountScopeAudit,
|
||||||
account_scope_fallback_applied: accountScopeFallbackApplied,
|
anchor,
|
||||||
anchor_type: anchor.anchor_type,
|
matchFailureStage: "none",
|
||||||
anchor_value_raw: anchor.anchor_value_raw,
|
matchFailureReason: null,
|
||||||
anchor_value_resolved: anchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: anchor.resolver_confidence,
|
|
||||||
ambiguity_count: anchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: stageStatus,
|
|
||||||
rows_fetched: mcp.fetched_rows,
|
|
||||||
raw_rows_received: mcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: normalizedRows.length,
|
|
||||||
rows_after_recipe_filter: filterByAnchors.length,
|
|
||||||
rows_materialized: normalizedRows.length,
|
|
||||||
rows_matched: filteredRows.length,
|
|
||||||
raw_row_keys_sample: rowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: rowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: accountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: accountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: accountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: accountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: accountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: factual.responseType,
|
|
||||||
capability_id: capabilityAudit.capabilityId,
|
|
||||||
capability_layer: capabilityAudit.layer,
|
|
||||||
capability_route_mode: capabilityAudit.routeMode,
|
|
||||||
capability_route_enabled: capabilityAudit.enabled,
|
|
||||||
capability_route_reason: capabilityAudit.reason,
|
|
||||||
shadow_route_intent: shadowRouteAudit.intent,
|
|
||||||
shadow_route_selected_recipe: shadowRouteAudit.selectedRecipe,
|
|
||||||
shadow_route_status: shadowRouteAudit.status,
|
|
||||||
route_expectation_status: finalRouteExpectationAudit.status,
|
|
||||||
route_expectation_reason: finalRouteExpectationAudit.reason,
|
|
||||||
route_expectation_expected_selected_recipes: finalRouteExpectationAudit.expectedSelectedRecipes,
|
|
||||||
route_expectation_expected_requested_result_modes: finalRouteExpectationAudit.expectedRequestedResultModes,
|
|
||||||
route_expectation_expected_result_modes: finalRouteExpectationAudit.expectedResultModes,
|
|
||||||
semantic_frame: semanticFrame,
|
|
||||||
...factualResultSemantics,
|
|
||||||
limitations: factualLimitations,
|
limitations: factualLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(reasonsWithRouteExpectation, requestedResultMode, factual.semantics, factualResultSemantics.result_mode)
|
reasons: withConfirmedBalanceFallbackReason(reasonsWithRouteExpectation, requestedResultMode, factual.semantics, factualResultSemantics.result_mode),
|
||||||
}
|
routeExpectationAudit: finalRouteExpectationAudit,
|
||||||
};
|
capabilityAudit,
|
||||||
|
shadowRouteAudit,
|
||||||
|
semanticFrame
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.AddressQueryService = AddressQueryService;
|
exports.AddressQueryService = AddressQueryService;
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,9 @@ function hasReusableRootScope(input) {
|
||||||
return Boolean(input.semanticFrame && input.semanticFrame.scope_kind !== "none");
|
return Boolean(input.semanticFrame && input.semanticFrame.scope_kind !== "none");
|
||||||
}
|
}
|
||||||
function truthGateStatusFrom(input) {
|
function truthGateStatusFrom(input) {
|
||||||
|
if (input.truthGateStatusHint) {
|
||||||
|
return input.truthGateStatusHint;
|
||||||
|
}
|
||||||
const missingRequiredFilters = input.missingRequiredFilters ?? [];
|
const missingRequiredFilters = input.missingRequiredFilters ?? [];
|
||||||
if (input.routeExpectationStatus === "mismatch") {
|
if (input.routeExpectationStatus === "mismatch") {
|
||||||
return "blocked_route_expectation_failure";
|
return "blocked_route_expectation_failure";
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ exports.INVENTORY_CAPABILITY_CONTRACTS = [
|
||||||
capability_id: "confirmed_inventory_on_hand_as_of_date",
|
capability_id: "confirmed_inventory_on_hand_as_of_date",
|
||||||
intent_ids: ["inventory_on_hand_as_of_date"],
|
intent_ids: ["inventory_on_hand_as_of_date"],
|
||||||
entry_modes: ["root_entry", "root_followup", "clarification_resume"],
|
entry_modes: ["root_entry", "root_followup", "clarification_resume"],
|
||||||
transitions: ["T1", "T2", "T7"],
|
transitions: ["T1", "T2", "T6", "T7"],
|
||||||
requiresFocusObject: false,
|
requiresFocusObject: false,
|
||||||
requiredAnchors: [],
|
requiredAnchors: [],
|
||||||
resultShape: "item_list_with_quantity_cost_warehouse_organization",
|
resultShape: "item_list_with_quantity_cost_warehouse_organization",
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ function hasInventorySupplierCue(text) {
|
||||||
if (/(?:кто\s+(?:был\s+)?продавец|кто\s+нам\s+продал|кто\s+продал\s+нам|(?:^|[\s,.;:!?()\-])(?:продавец|seller)(?=$|[\s,.;:!?()\-]))/iu.test(value)) {
|
if (/(?:кто\s+(?:был\s+)?продавец|кто\s+нам\s+продал|кто\s+продал\s+нам|(?:^|[\s,.;:!?()\-])(?:продавец|seller)(?=$|[\s,.;:!?()\-]))/iu.test(value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (/(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|откуда\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(value)) {
|
if (/(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?(?:это|этот\s+товар|эту\s+позицию)\s+поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|откуда\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return hasInventoryPurchaseStem(value) && /(?:у\s+кого|от\s+кого|где)/iu.test(value);
|
return hasInventoryPurchaseStem(value) && /(?:у\s+кого|от\s+кого|где)/iu.test(value);
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,10 @@ interface AutoGenHistoryRecord {
|
||||||
source_session_id?: string | null;
|
source_session_id?: string | null;
|
||||||
saved_session_file?: string | null;
|
saved_session_file?: string | null;
|
||||||
saved_case_set_kind?: string | null;
|
saved_case_set_kind?: string | null;
|
||||||
|
agent_run?: boolean | null;
|
||||||
|
agent_focus?: string | null;
|
||||||
|
architecture_phase?: string | null;
|
||||||
|
source_spec_file?: string | null;
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -365,7 +369,13 @@ function readAutoGenHistory(): AutoGenHistoryRecord[] {
|
||||||
: null,
|
: null,
|
||||||
source_session_id: toStringSafe(toRecord(item.context)?.source_session_id),
|
source_session_id: toStringSafe(toRecord(item.context)?.source_session_id),
|
||||||
saved_session_file: toStringSafe(toRecord(item.context)?.saved_session_file),
|
saved_session_file: toStringSafe(toRecord(item.context)?.saved_session_file),
|
||||||
saved_case_set_kind: toStringSafe(toRecord(item.context)?.saved_case_set_kind)
|
saved_case_set_kind: toStringSafe(toRecord(item.context)?.saved_case_set_kind),
|
||||||
|
agent_run: toBooleanSafe(toRecord(item.context)?.agent_run),
|
||||||
|
agent_focus: toStringSafe(toRecord(item.context)?.agent_focus)
|
||||||
|
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
|
||||||
|
: null,
|
||||||
|
architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase),
|
||||||
|
source_spec_file: toStringSafe(toRecord(item.context)?.source_spec_file)
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}))
|
}))
|
||||||
|
|
@ -1797,6 +1807,7 @@ function buildSavedSessionCaseSetPayload(input: {
|
||||||
generationId: string;
|
generationId: string;
|
||||||
title: string | null;
|
title: string | null;
|
||||||
questions: string[];
|
questions: string[];
|
||||||
|
scenarioTag?: string | null;
|
||||||
}): Record<string, unknown> {
|
}): Record<string, unknown> {
|
||||||
const questions = parseAssistantSessionQuestions(input.questions);
|
const questions = parseAssistantSessionQuestions(input.questions);
|
||||||
const turns = questions.map((question) => ({
|
const turns = questions.map((question) => ({
|
||||||
|
|
@ -1818,7 +1829,7 @@ function buildSavedSessionCaseSetPayload(input: {
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
case_id: caseId,
|
case_id: caseId,
|
||||||
scenario_tag: "saved_user_sessions",
|
scenario_tag: toStringSafe(input.scenarioTag) ?? "saved_user_sessions",
|
||||||
title: input.title,
|
title: input.title,
|
||||||
question_type: turns.length > 1 ? "followup" : "direct",
|
question_type: turns.length > 1 ? "followup" : "direct",
|
||||||
broadness_level: "medium",
|
broadness_level: "medium",
|
||||||
|
|
@ -1851,7 +1862,11 @@ function rewriteAutoGenCaseSetFile(record: AutoGenHistoryRecord): string | null
|
||||||
? buildSavedSessionCaseSetPayload({
|
? buildSavedSessionCaseSetPayload({
|
||||||
generationId: record.generation_id,
|
generationId: record.generation_id,
|
||||||
title: record.title,
|
title: record.title,
|
||||||
questions: record.questions
|
questions: record.questions,
|
||||||
|
scenarioTag:
|
||||||
|
record.context?.saved_case_set_kind === "agent_semantic_scenario"
|
||||||
|
? "agent_saved_user_sessions"
|
||||||
|
: "saved_user_sessions"
|
||||||
})
|
})
|
||||||
: buildAutogenCaseSetPayload({
|
: buildAutogenCaseSetPayload({
|
||||||
generationId: record.generation_id,
|
generationId: record.generation_id,
|
||||||
|
|
@ -2468,7 +2483,8 @@ export function buildAutoRunsRouter(services: AppServices, openaiClient = new Op
|
||||||
buildSavedSessionCaseSetPayload({
|
buildSavedSessionCaseSetPayload({
|
||||||
generationId,
|
generationId,
|
||||||
title,
|
title,
|
||||||
questions
|
questions,
|
||||||
|
scenarioTag: "saved_user_sessions"
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
FEATURE_ASSISTANT_ADDRESS_QUERY_LIVE_V1,
|
FEATURE_ASSISTANT_ADDRESS_QUERY_LIVE_V1,
|
||||||
SHARED_LLM_CONNECTION_FILE
|
SHARED_LLM_CONNECTION_FILE
|
||||||
} from "../config";
|
} from "../config";
|
||||||
|
import type { AssistantTruthGateContractStatus } from "../types/assistantRuntimeContracts";
|
||||||
import type {
|
import type {
|
||||||
AddressCapabilityLayer,
|
AddressCapabilityLayer,
|
||||||
AddressCapabilityRouteMode,
|
AddressCapabilityRouteMode,
|
||||||
|
|
@ -4207,6 +4208,150 @@ export class AddressQueryService {
|
||||||
debug: debugPayload
|
debug: debugPayload
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
const buildFactualExecutionResult = (input: {
|
||||||
|
replyText: string;
|
||||||
|
responseType: AddressResponseType;
|
||||||
|
responseSemantics?: ComposeReplySemantics;
|
||||||
|
selectedRecipe: string | null;
|
||||||
|
mcpCallStatus: AddressMcpCallStatus;
|
||||||
|
rowsFetched: number;
|
||||||
|
rawRowsReceived: number;
|
||||||
|
rowsAfterAccountScope: number;
|
||||||
|
rowsAfterRecipeFilter: number;
|
||||||
|
rowsMaterialized: number;
|
||||||
|
rowsMatched: number;
|
||||||
|
rawRowKeysSample: string[];
|
||||||
|
materializationDropReason:
|
||||||
|
| "none"
|
||||||
|
| "dropped_by_account_scope_filter"
|
||||||
|
| "missing_period_and_registrator_fields"
|
||||||
|
| "missing_period_field"
|
||||||
|
| "missing_registrator_field"
|
||||||
|
| "unknown_row_shape";
|
||||||
|
accountScopeMode: "strict" | "preferred";
|
||||||
|
accountScopeFallbackApplied: boolean;
|
||||||
|
accountScopeAudit: AccountScopeAuditDebug;
|
||||||
|
anchor: AnchorResolutionDebug;
|
||||||
|
matchFailureStage: AddressMatchFailureStage;
|
||||||
|
matchFailureReason: string | null;
|
||||||
|
limitations: string[];
|
||||||
|
reasons: string[];
|
||||||
|
routeExpectationAudit?: AddressRouteExpectationAuditState;
|
||||||
|
capabilityAudit?: AddressCapabilityAudit;
|
||||||
|
shadowRouteAudit?: AddressShadowRouteAudit;
|
||||||
|
semanticFrame?: AddressSemanticFrame | null;
|
||||||
|
runtimeReadiness?: AddressRuntimeReadiness;
|
||||||
|
limitedReasonCategory?: AddressLimitedReasonCategory | null;
|
||||||
|
truthGateStatusHint?: AssistantTruthGateContractStatus | null;
|
||||||
|
extractedFilters?: AddressFilterSet;
|
||||||
|
}): AddressExecutionResult => {
|
||||||
|
const resultSemantics = mergeAddressResultSemantics(
|
||||||
|
deriveAddressResultSemantics({
|
||||||
|
intent: intent.intent,
|
||||||
|
selectedRecipe: input.selectedRecipe,
|
||||||
|
filters: input.extractedFilters ?? filters.extracted_filters,
|
||||||
|
semanticFrame: input.semanticFrame ?? semanticFrame,
|
||||||
|
responseType: input.responseType,
|
||||||
|
rowsMatched: input.rowsMatched
|
||||||
|
}),
|
||||||
|
input.responseSemantics
|
||||||
|
);
|
||||||
|
const routeExpectationAudit =
|
||||||
|
input.routeExpectationAudit ??
|
||||||
|
buildRouteExpectationAudit({
|
||||||
|
intent: routeExpectationIntent,
|
||||||
|
selectedRecipe: input.selectedRecipe,
|
||||||
|
requestedResultMode,
|
||||||
|
resultMode: resultSemantics.result_mode
|
||||||
|
});
|
||||||
|
const debugPayload = attachAddressTruthGate(
|
||||||
|
{
|
||||||
|
detected_mode: mode.mode,
|
||||||
|
detected_mode_confidence: mode.confidence,
|
||||||
|
query_shape: shape.shape,
|
||||||
|
query_shape_confidence: shape.confidence,
|
||||||
|
detected_intent: intent.intent,
|
||||||
|
detected_intent_confidence: intent.confidence,
|
||||||
|
extracted_filters: input.extractedFilters ?? filters.extracted_filters,
|
||||||
|
missing_required_filters: [],
|
||||||
|
selected_recipe: input.selectedRecipe,
|
||||||
|
mcp_call_status_legacy: toLegacyMcpStatus(input.mcpCallStatus),
|
||||||
|
account_scope_mode: input.accountScopeMode,
|
||||||
|
account_scope_fallback_applied: input.accountScopeFallbackApplied,
|
||||||
|
anchor_type: input.anchor.anchor_type,
|
||||||
|
anchor_value_raw: input.anchor.anchor_value_raw,
|
||||||
|
anchor_value_resolved: input.anchor.anchor_value_resolved,
|
||||||
|
resolver_confidence: input.anchor.resolver_confidence,
|
||||||
|
ambiguity_count: input.anchor.ambiguity_count,
|
||||||
|
match_failure_stage: input.matchFailureStage,
|
||||||
|
match_failure_reason: input.matchFailureReason,
|
||||||
|
mcp_call_status: input.mcpCallStatus,
|
||||||
|
rows_fetched: input.rowsFetched,
|
||||||
|
raw_rows_received: input.rawRowsReceived,
|
||||||
|
rows_after_account_scope: input.rowsAfterAccountScope,
|
||||||
|
rows_after_recipe_filter: input.rowsAfterRecipeFilter,
|
||||||
|
rows_materialized: input.rowsMaterialized,
|
||||||
|
rows_matched: input.rowsMatched,
|
||||||
|
raw_row_keys_sample: input.rawRowKeysSample,
|
||||||
|
materialization_drop_reason: input.materializationDropReason,
|
||||||
|
account_token_raw: input.accountScopeAudit.accountTokenRaw,
|
||||||
|
account_token_normalized: input.accountScopeAudit.accountTokenNormalized,
|
||||||
|
account_scope_fields_checked: input.accountScopeAudit.accountScopeFieldsChecked,
|
||||||
|
account_scope_match_strategy: input.accountScopeAudit.accountScopeMatchStrategy,
|
||||||
|
account_scope_drop_reason: input.accountScopeAudit.accountScopeDropReason,
|
||||||
|
runtime_readiness: input.runtimeReadiness ?? "LIVE_QUERYABLE_WITH_LIMITS",
|
||||||
|
limited_reason_category: input.limitedReasonCategory ?? null,
|
||||||
|
response_type: input.responseType,
|
||||||
|
route_expectation_status: routeExpectationAudit.status,
|
||||||
|
route_expectation_reason: routeExpectationAudit.reason,
|
||||||
|
route_expectation_expected_selected_recipes: routeExpectationAudit.expectedSelectedRecipes,
|
||||||
|
route_expectation_expected_requested_result_modes: routeExpectationAudit.expectedRequestedResultModes,
|
||||||
|
route_expectation_expected_result_modes: routeExpectationAudit.expectedResultModes,
|
||||||
|
semantic_frame: input.semanticFrame ?? semanticFrame,
|
||||||
|
...resultSemantics,
|
||||||
|
limitations: input.limitations,
|
||||||
|
reasons: input.reasons,
|
||||||
|
...(input.capabilityAudit
|
||||||
|
? {
|
||||||
|
capability_id: input.capabilityAudit.capabilityId,
|
||||||
|
capability_layer: input.capabilityAudit.layer,
|
||||||
|
capability_route_mode: input.capabilityAudit.routeMode,
|
||||||
|
capability_route_enabled: input.capabilityAudit.enabled,
|
||||||
|
capability_route_reason: input.capabilityAudit.reason
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(input.shadowRouteAudit
|
||||||
|
? {
|
||||||
|
shadow_route_intent: input.shadowRouteAudit.intent,
|
||||||
|
shadow_route_selected_recipe: input.shadowRouteAudit.selectedRecipe,
|
||||||
|
shadow_route_status: input.shadowRouteAudit.status
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
intent: intent.intent,
|
||||||
|
filters: input.extractedFilters ?? filters.extracted_filters,
|
||||||
|
semanticFrame: input.semanticFrame ?? semanticFrame,
|
||||||
|
selectedRecipe: input.selectedRecipe,
|
||||||
|
truthGateStatusHint: input.truthGateStatusHint ?? null,
|
||||||
|
rowsMatched: input.rowsMatched,
|
||||||
|
limitedReasonCategory: input.limitedReasonCategory ?? null,
|
||||||
|
runtimeReadiness: input.runtimeReadiness ?? "LIVE_QUERYABLE_WITH_LIMITS",
|
||||||
|
limitations: input.limitations,
|
||||||
|
reasons: input.reasons,
|
||||||
|
routeExpectationStatus: routeExpectationAudit.status,
|
||||||
|
routeExpectationReason: routeExpectationAudit.reason,
|
||||||
|
replyType: inferReplyType(input.responseType)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
handled: true,
|
||||||
|
reply_text: input.replyText,
|
||||||
|
reply_type: inferReplyType(input.responseType),
|
||||||
|
response_type: input.responseType,
|
||||||
|
debug: debugPayload
|
||||||
|
};
|
||||||
|
};
|
||||||
if (organizationWarehouseRecoveryApplied) {
|
if (organizationWarehouseRecoveryApplied) {
|
||||||
if (!baseReasons.includes("organization_scope_live_grounding_recovered_rows")) {
|
if (!baseReasons.includes("organization_scope_live_grounding_recovered_rows")) {
|
||||||
baseReasons.push("organization_scope_live_grounding_recovered_rows");
|
baseReasons.push("organization_scope_live_grounding_recovered_rows");
|
||||||
|
|
@ -4262,67 +4407,33 @@ export class AddressQueryService {
|
||||||
recoveredBankRows.length > 0
|
recoveredBankRows.length > 0
|
||||||
? "Документный фильтр в live дал пустой набор; показываю связанные банковские операции по договору."
|
? "Документный фильтр в live дал пустой набор; показываю связанные банковские операции по договору."
|
||||||
: "Документный фильтр в live дал пустой набор; показываю найденные строки по договорному якорю.";
|
: "Документный фильтр в live дал пустой набор; показываю найденные строки по договорному якорю.";
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: `${replyPrefix}\n${factual.text}`,
|
||||||
reply_text: `${replyPrefix}\n${factual.text}`,
|
|
||||||
reply_type: inferReplyType(factual.responseType),
|
|
||||||
response_type: factual.responseType,
|
|
||||||
debug: {
|
|
||||||
detected_mode: mode.mode,
|
|
||||||
detected_mode_confidence: mode.confidence,
|
|
||||||
query_shape: shape.shape,
|
|
||||||
query_shape_confidence: shape.confidence,
|
|
||||||
detected_intent: intent.intent,
|
|
||||||
detected_intent_confidence: intent.confidence,
|
|
||||||
extracted_filters: filters.extracted_filters,
|
|
||||||
missing_required_filters: [],
|
|
||||||
selected_recipe: effectiveRecipeId,
|
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus("matched_non_empty"),
|
|
||||||
account_scope_mode: plan.account_scope_mode,
|
|
||||||
account_scope_fallback_applied: accountScopeFallbackApplied,
|
|
||||||
anchor_type: anchor.anchor_type,
|
|
||||||
anchor_value_raw: anchor.anchor_value_raw,
|
|
||||||
anchor_value_resolved: anchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: anchor.resolver_confidence,
|
|
||||||
ambiguity_count: anchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: "matched_non_empty",
|
|
||||||
rows_fetched: mcp.fetched_rows,
|
|
||||||
raw_rows_received: mcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: normalizedRows.length,
|
|
||||||
rows_after_recipe_filter: filterByAnchors.length,
|
|
||||||
rows_materialized: normalizedRows.length,
|
|
||||||
rows_matched: recoveredRows.length,
|
|
||||||
raw_row_keys_sample: rowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: rowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: accountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: accountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: accountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: accountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: accountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: factual.responseType,
|
|
||||||
...mergeAddressResultSemantics(
|
|
||||||
deriveAddressResultSemantics({
|
|
||||||
intent: intent.intent,
|
|
||||||
selectedRecipe: effectiveRecipeId,
|
|
||||||
filters: filters.extracted_filters,
|
|
||||||
semanticFrame,
|
|
||||||
responseType: factual.responseType,
|
responseType: factual.responseType,
|
||||||
rowsMatched: recoveredRows.length
|
responseSemantics: factual.semantics,
|
||||||
}),
|
selectedRecipe: effectiveRecipeId,
|
||||||
factual.semantics
|
mcpCallStatus: "matched_non_empty",
|
||||||
),
|
rowsFetched: mcp.fetched_rows,
|
||||||
|
rawRowsReceived: mcp.raw_rows.length,
|
||||||
|
rowsAfterAccountScope: normalizedRows.length,
|
||||||
|
rowsAfterRecipeFilter: filterByAnchors.length,
|
||||||
|
rowsMaterialized: normalizedRows.length,
|
||||||
|
rowsMatched: recoveredRows.length,
|
||||||
|
rawRowKeysSample: rowDiagnostics.rawRowKeysSample,
|
||||||
|
materializationDropReason: rowDiagnostics.materializationDropReason,
|
||||||
|
accountScopeMode: plan.account_scope_mode,
|
||||||
|
accountScopeFallbackApplied,
|
||||||
|
accountScopeAudit,
|
||||||
|
anchor,
|
||||||
|
matchFailureStage: "none",
|
||||||
|
matchFailureReason: null,
|
||||||
limitations: [...filters.warnings, recoveryReason],
|
limitations: [...filters.warnings, recoveryReason],
|
||||||
reasons: withConfirmedBalanceFallbackReason(
|
reasons: withConfirmedBalanceFallbackReason([...baseReasons, recoveryReason], requestedResultMode, factual.semantics),
|
||||||
[...baseReasons, recoveryReason],
|
limitedReasonCategory: "recipe_visibility_gap",
|
||||||
requestedResultMode,
|
capabilityAudit,
|
||||||
factual.semantics
|
shadowRouteAudit,
|
||||||
)
|
semanticFrame
|
||||||
}
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4411,67 +4522,32 @@ export class AddressQueryService {
|
||||||
const expandedPrefix = `Период сохранен. Глубина live-выборки автоматически расширена до ${expandedPlan.limit} строк.`;
|
const expandedPrefix = `Период сохранен. Глубина live-выборки автоматически расширена до ${expandedPlan.limit} строк.`;
|
||||||
const expandedLimitations = [...filters.warnings, "query_limit_auto_expanded_for_anchor_recovery"];
|
const expandedLimitations = [...filters.warnings, "query_limit_auto_expanded_for_anchor_recovery"];
|
||||||
const expandedReasons = [...baseReasons, "query_limit_auto_expanded_for_anchor_recovery"];
|
const expandedReasons = [...baseReasons, "query_limit_auto_expanded_for_anchor_recovery"];
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: `${expandedPrefix}\n${expandedFactual.text}`,
|
||||||
reply_text: `${expandedPrefix}\n${expandedFactual.text}`,
|
|
||||||
reply_type: inferReplyType(expandedFactual.responseType),
|
|
||||||
response_type: expandedFactual.responseType,
|
|
||||||
debug: {
|
|
||||||
detected_mode: mode.mode,
|
|
||||||
detected_mode_confidence: mode.confidence,
|
|
||||||
query_shape: shape.shape,
|
|
||||||
query_shape_confidence: shape.confidence,
|
|
||||||
detected_intent: intent.intent,
|
|
||||||
detected_intent_confidence: intent.confidence,
|
|
||||||
extracted_filters: filters.extracted_filters,
|
|
||||||
missing_required_filters: [],
|
|
||||||
selected_recipe: expandedSelection.selected_recipe.recipe_id,
|
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus(expandedStageStatus),
|
|
||||||
account_scope_mode: expandedPlan.account_scope_mode,
|
|
||||||
account_scope_fallback_applied: expandedAccountScopeFallbackApplied,
|
|
||||||
anchor_type: expandedAnchor.anchor_type,
|
|
||||||
anchor_value_raw: expandedAnchor.anchor_value_raw,
|
|
||||||
anchor_value_resolved: expandedAnchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: expandedAnchor.resolver_confidence,
|
|
||||||
ambiguity_count: expandedAnchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: expandedStageStatus,
|
|
||||||
rows_fetched: expandedMcp.fetched_rows,
|
|
||||||
raw_rows_received: expandedMcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: expandedNormalizedRows.length,
|
|
||||||
rows_after_recipe_filter: expandedRowsByAnchor.length,
|
|
||||||
rows_materialized: expandedNormalizedRows.length,
|
|
||||||
rows_matched: expandedFilteredRows.length,
|
|
||||||
raw_row_keys_sample: expandedRowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: expandedRowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: expandedAccountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: expandedAccountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: expandedAccountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: expandedAccountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: expandedAccountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: expandedFactual.responseType,
|
|
||||||
...mergeAddressResultSemantics(
|
|
||||||
deriveAddressResultSemantics({
|
|
||||||
intent: intent.intent,
|
|
||||||
selectedRecipe: expandedSelection.selected_recipe.recipe_id,
|
|
||||||
filters: filters.extracted_filters,
|
|
||||||
semanticFrame,
|
|
||||||
responseType: expandedFactual.responseType,
|
responseType: expandedFactual.responseType,
|
||||||
rowsMatched: expandedFilteredRows.length
|
responseSemantics: expandedFactual.semantics,
|
||||||
}),
|
selectedRecipe: expandedSelection.selected_recipe.recipe_id,
|
||||||
expandedFactual.semantics
|
mcpCallStatus: expandedStageStatus,
|
||||||
),
|
rowsFetched: expandedMcp.fetched_rows,
|
||||||
|
rawRowsReceived: expandedMcp.raw_rows.length,
|
||||||
|
rowsAfterAccountScope: expandedNormalizedRows.length,
|
||||||
|
rowsAfterRecipeFilter: expandedRowsByAnchor.length,
|
||||||
|
rowsMaterialized: expandedNormalizedRows.length,
|
||||||
|
rowsMatched: expandedFilteredRows.length,
|
||||||
|
rawRowKeysSample: expandedRowDiagnostics.rawRowKeysSample,
|
||||||
|
materializationDropReason: expandedRowDiagnostics.materializationDropReason,
|
||||||
|
accountScopeMode: expandedPlan.account_scope_mode,
|
||||||
|
accountScopeFallbackApplied: expandedAccountScopeFallbackApplied,
|
||||||
|
accountScopeAudit: expandedAccountScopeAudit,
|
||||||
|
anchor: expandedAnchor,
|
||||||
|
matchFailureStage: "none",
|
||||||
|
matchFailureReason: null,
|
||||||
limitations: expandedLimitations,
|
limitations: expandedLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(
|
reasons: withConfirmedBalanceFallbackReason(expandedReasons, requestedResultMode, expandedFactual.semantics),
|
||||||
expandedReasons,
|
capabilityAudit,
|
||||||
requestedResultMode,
|
shadowRouteAudit,
|
||||||
expandedFactual.semantics
|
semanticFrame
|
||||||
)
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4586,72 +4662,34 @@ export class AddressQueryService {
|
||||||
requestedResultMode,
|
requestedResultMode,
|
||||||
resultMode: broadenedResultSemantics.result_mode
|
resultMode: broadenedResultSemantics.result_mode
|
||||||
});
|
});
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: injectNoticeAfterLeadLine(broadenedFactual.text, broadenedPrefix),
|
||||||
reply_text: injectNoticeAfterLeadLine(broadenedFactual.text, broadenedPrefix),
|
responseType: broadenedFactual.responseType,
|
||||||
reply_type: inferReplyType(broadenedFactual.responseType),
|
responseSemantics: broadenedFactual.semantics,
|
||||||
response_type: broadenedFactual.responseType,
|
selectedRecipe: broadenedSelection.selected_recipe.recipe_id,
|
||||||
debug: {
|
mcpCallStatus: broadenedStageStatus,
|
||||||
detected_mode: mode.mode,
|
rowsFetched: broadenedMcp.fetched_rows,
|
||||||
detected_mode_confidence: mode.confidence,
|
rawRowsReceived: broadenedMcp.raw_rows.length,
|
||||||
query_shape: shape.shape,
|
rowsAfterAccountScope: broadenedNormalizedRows.length,
|
||||||
query_shape_confidence: shape.confidence,
|
rowsAfterRecipeFilter: broadenedRowsByAnchor.length,
|
||||||
detected_intent: intent.intent,
|
rowsMaterialized: broadenedNormalizedRows.length,
|
||||||
detected_intent_confidence: intent.confidence,
|
rowsMatched: broadenedFilteredRows.length,
|
||||||
extracted_filters: filters.extracted_filters,
|
rawRowKeysSample: broadenedRowDiagnostics.rawRowKeysSample,
|
||||||
missing_required_filters: [],
|
materializationDropReason: broadenedRowDiagnostics.materializationDropReason,
|
||||||
selected_recipe: broadenedSelection.selected_recipe.recipe_id,
|
accountScopeMode: broadenedPlan.account_scope_mode,
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus(broadenedStageStatus),
|
accountScopeFallbackApplied: broadenedAccountScopeFallbackApplied,
|
||||||
account_scope_mode: broadenedPlan.account_scope_mode,
|
accountScopeAudit: broadenedAccountScopeAudit,
|
||||||
account_scope_fallback_applied: broadenedAccountScopeFallbackApplied,
|
anchor: broadenedAnchor,
|
||||||
anchor_type: broadenedAnchor.anchor_type,
|
matchFailureStage: "none",
|
||||||
anchor_value_raw: broadenedAnchor.anchor_value_raw,
|
matchFailureReason: null,
|
||||||
anchor_value_resolved: broadenedAnchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: broadenedAnchor.resolver_confidence,
|
|
||||||
ambiguity_count: broadenedAnchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: broadenedStageStatus,
|
|
||||||
rows_fetched: broadenedMcp.fetched_rows,
|
|
||||||
raw_rows_received: broadenedMcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: broadenedNormalizedRows.length,
|
|
||||||
rows_after_recipe_filter: broadenedRowsByAnchor.length,
|
|
||||||
rows_materialized: broadenedNormalizedRows.length,
|
|
||||||
rows_matched: broadenedFilteredRows.length,
|
|
||||||
raw_row_keys_sample: broadenedRowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: broadenedRowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: broadenedAccountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: broadenedAccountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: broadenedAccountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: broadenedAccountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: broadenedAccountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: broadenedFactual.responseType,
|
|
||||||
capability_id: capabilityAudit.capabilityId,
|
|
||||||
capability_layer: capabilityAudit.layer,
|
|
||||||
capability_route_mode: capabilityAudit.routeMode,
|
|
||||||
capability_route_enabled: capabilityAudit.enabled,
|
|
||||||
capability_route_reason: capabilityAudit.reason,
|
|
||||||
shadow_route_intent: shadowRouteAudit.intent,
|
|
||||||
shadow_route_selected_recipe: shadowRouteAudit.selectedRecipe,
|
|
||||||
shadow_route_status: shadowRouteAudit.status,
|
|
||||||
route_expectation_status: broadenedRouteExpectationAudit.status,
|
|
||||||
route_expectation_reason: broadenedRouteExpectationAudit.reason,
|
|
||||||
route_expectation_expected_selected_recipes: broadenedRouteExpectationAudit.expectedSelectedRecipes,
|
|
||||||
route_expectation_expected_requested_result_modes:
|
|
||||||
broadenedRouteExpectationAudit.expectedRequestedResultModes,
|
|
||||||
route_expectation_expected_result_modes: broadenedRouteExpectationAudit.expectedResultModes,
|
|
||||||
semantic_frame: semanticFrame,
|
|
||||||
...broadenedResultSemantics,
|
|
||||||
limitations: broadenedLimitations,
|
limitations: broadenedLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(
|
reasons: withConfirmedBalanceFallbackReason(broadenedReasons, requestedResultMode, broadenedFactual.semantics),
|
||||||
broadenedReasons,
|
routeExpectationAudit: broadenedRouteExpectationAudit,
|
||||||
requestedResultMode,
|
capabilityAudit,
|
||||||
broadenedFactual.semantics
|
shadowRouteAudit,
|
||||||
)
|
semanticFrame,
|
||||||
}
|
truthGateStatusHint: "limited_temporal_or_contextual"
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4745,67 +4783,33 @@ export class AddressQueryService {
|
||||||
: "";
|
: "";
|
||||||
const historicalLimitations = [...filters.warnings, "historical_window_sort_recovery_applied"];
|
const historicalLimitations = [...filters.warnings, "historical_window_sort_recovery_applied"];
|
||||||
const historicalReasons = [...baseReasons, "historical_window_sort_recovery_applied"];
|
const historicalReasons = [...baseReasons, "historical_window_sort_recovery_applied"];
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: `${historicalPrefix}\n${historicalFactual.text}${historicalSuggestion}`,
|
||||||
reply_text: `${historicalPrefix}\n${historicalFactual.text}${historicalSuggestion}`,
|
|
||||||
reply_type: inferReplyType(historicalFactual.responseType),
|
|
||||||
response_type: historicalFactual.responseType,
|
|
||||||
debug: {
|
|
||||||
detected_mode: mode.mode,
|
|
||||||
detected_mode_confidence: mode.confidence,
|
|
||||||
query_shape: shape.shape,
|
|
||||||
query_shape_confidence: shape.confidence,
|
|
||||||
detected_intent: intent.intent,
|
|
||||||
detected_intent_confidence: intent.confidence,
|
|
||||||
extracted_filters: filters.extracted_filters,
|
|
||||||
missing_required_filters: [],
|
|
||||||
selected_recipe: historicalSelection.selected_recipe.recipe_id,
|
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus(historicalStageStatus),
|
|
||||||
account_scope_mode: historicalPlan.account_scope_mode,
|
|
||||||
account_scope_fallback_applied: historicalAccountScopeFallbackApplied,
|
|
||||||
anchor_type: historicalAnchor.anchor_type,
|
|
||||||
anchor_value_raw: historicalAnchor.anchor_value_raw,
|
|
||||||
anchor_value_resolved: historicalAnchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: historicalAnchor.resolver_confidence,
|
|
||||||
ambiguity_count: historicalAnchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: historicalStageStatus,
|
|
||||||
rows_fetched: historicalMcp.fetched_rows,
|
|
||||||
raw_rows_received: historicalMcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: historicalNormalizedRows.length,
|
|
||||||
rows_after_recipe_filter: historicalRowsByAnchor.length,
|
|
||||||
rows_materialized: historicalNormalizedRows.length,
|
|
||||||
rows_matched: historicalFilteredRows.length,
|
|
||||||
raw_row_keys_sample: historicalRowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: historicalRowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: historicalAccountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: historicalAccountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: historicalAccountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: historicalAccountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: historicalAccountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: historicalFactual.responseType,
|
|
||||||
...mergeAddressResultSemantics(
|
|
||||||
deriveAddressResultSemantics({
|
|
||||||
intent: intent.intent,
|
|
||||||
selectedRecipe: historicalSelection.selected_recipe.recipe_id,
|
|
||||||
filters: filters.extracted_filters,
|
|
||||||
semanticFrame,
|
|
||||||
responseType: historicalFactual.responseType,
|
responseType: historicalFactual.responseType,
|
||||||
rowsMatched: historicalFilteredRows.length
|
responseSemantics: historicalFactual.semantics,
|
||||||
}),
|
selectedRecipe: historicalSelection.selected_recipe.recipe_id,
|
||||||
historicalFactual.semantics
|
mcpCallStatus: historicalStageStatus,
|
||||||
),
|
rowsFetched: historicalMcp.fetched_rows,
|
||||||
|
rawRowsReceived: historicalMcp.raw_rows.length,
|
||||||
|
rowsAfterAccountScope: historicalNormalizedRows.length,
|
||||||
|
rowsAfterRecipeFilter: historicalRowsByAnchor.length,
|
||||||
|
rowsMaterialized: historicalNormalizedRows.length,
|
||||||
|
rowsMatched: historicalFilteredRows.length,
|
||||||
|
rawRowKeysSample: historicalRowDiagnostics.rawRowKeysSample,
|
||||||
|
materializationDropReason: historicalRowDiagnostics.materializationDropReason,
|
||||||
|
accountScopeMode: historicalPlan.account_scope_mode,
|
||||||
|
accountScopeFallbackApplied: historicalAccountScopeFallbackApplied,
|
||||||
|
accountScopeAudit: historicalAccountScopeAudit,
|
||||||
|
anchor: historicalAnchor,
|
||||||
|
matchFailureStage: "none",
|
||||||
|
matchFailureReason: null,
|
||||||
limitations: historicalLimitations,
|
limitations: historicalLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(
|
reasons: withConfirmedBalanceFallbackReason(historicalReasons, requestedResultMode, historicalFactual.semantics),
|
||||||
historicalReasons,
|
capabilityAudit,
|
||||||
requestedResultMode,
|
shadowRouteAudit,
|
||||||
historicalFactual.semantics
|
semanticFrame,
|
||||||
)
|
truthGateStatusHint: "limited_temporal_or_contextual"
|
||||||
}
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4832,67 +4836,33 @@ export class AddressQueryService {
|
||||||
: "";
|
: "";
|
||||||
const fallbackLimitations = [...filters.warnings, "anchor_not_matched_fallback_rows"];
|
const fallbackLimitations = [...filters.warnings, "anchor_not_matched_fallback_rows"];
|
||||||
const fallbackReasons = [...baseReasons, "anchor_not_matched_fallback_rows"];
|
const fallbackReasons = [...baseReasons, "anchor_not_matched_fallback_rows"];
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: `${fallbackPrefix}\n${fallbackFactual.text}${fallbackSuggestion}`,
|
||||||
reply_text: `${fallbackPrefix}\n${fallbackFactual.text}${fallbackSuggestion}`,
|
|
||||||
reply_type: inferReplyType(fallbackFactual.responseType),
|
|
||||||
response_type: fallbackFactual.responseType,
|
|
||||||
debug: {
|
|
||||||
detected_mode: mode.mode,
|
|
||||||
detected_mode_confidence: mode.confidence,
|
|
||||||
query_shape: shape.shape,
|
|
||||||
query_shape_confidence: shape.confidence,
|
|
||||||
detected_intent: intent.intent,
|
|
||||||
detected_intent_confidence: intent.confidence,
|
|
||||||
extracted_filters: filters.extracted_filters,
|
|
||||||
missing_required_filters: [],
|
|
||||||
selected_recipe: effectiveRecipeId,
|
|
||||||
mcp_call_status_legacy: "matched_non_empty",
|
|
||||||
account_scope_mode: plan.account_scope_mode,
|
|
||||||
account_scope_fallback_applied: accountScopeFallbackApplied,
|
|
||||||
anchor_type: anchor.anchor_type,
|
|
||||||
anchor_value_raw: anchor.anchor_value_raw,
|
|
||||||
anchor_value_resolved: anchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: anchor.resolver_confidence,
|
|
||||||
ambiguity_count: anchor.ambiguity_count,
|
|
||||||
match_failure_stage: matchFailureStage,
|
|
||||||
match_failure_reason: matchFailureReason,
|
|
||||||
mcp_call_status: "matched_non_empty",
|
|
||||||
rows_fetched: mcp.fetched_rows,
|
|
||||||
raw_rows_received: mcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: normalizedRows.length,
|
|
||||||
rows_after_recipe_filter: filterByAnchors.length,
|
|
||||||
rows_materialized: normalizedRows.length,
|
|
||||||
rows_matched: documentBankFallbackRows.length,
|
|
||||||
raw_row_keys_sample: rowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: rowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: accountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: accountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: accountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: accountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: accountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: fallbackFactual.responseType,
|
|
||||||
...mergeAddressResultSemantics(
|
|
||||||
deriveAddressResultSemantics({
|
|
||||||
intent: intent.intent,
|
|
||||||
selectedRecipe: effectiveRecipeId,
|
|
||||||
filters: filters.extracted_filters,
|
|
||||||
semanticFrame,
|
|
||||||
responseType: fallbackFactual.responseType,
|
responseType: fallbackFactual.responseType,
|
||||||
rowsMatched: documentBankFallbackRows.length
|
responseSemantics: fallbackFactual.semantics,
|
||||||
}),
|
selectedRecipe: effectiveRecipeId,
|
||||||
fallbackFactual.semantics
|
mcpCallStatus: "matched_non_empty",
|
||||||
),
|
rowsFetched: mcp.fetched_rows,
|
||||||
|
rawRowsReceived: mcp.raw_rows.length,
|
||||||
|
rowsAfterAccountScope: normalizedRows.length,
|
||||||
|
rowsAfterRecipeFilter: filterByAnchors.length,
|
||||||
|
rowsMaterialized: normalizedRows.length,
|
||||||
|
rowsMatched: documentBankFallbackRows.length,
|
||||||
|
rawRowKeysSample: rowDiagnostics.rawRowKeysSample,
|
||||||
|
materializationDropReason: rowDiagnostics.materializationDropReason,
|
||||||
|
accountScopeMode: plan.account_scope_mode,
|
||||||
|
accountScopeFallbackApplied,
|
||||||
|
accountScopeAudit,
|
||||||
|
anchor,
|
||||||
|
matchFailureStage,
|
||||||
|
matchFailureReason,
|
||||||
limitations: fallbackLimitations,
|
limitations: fallbackLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(
|
reasons: withConfirmedBalanceFallbackReason(fallbackReasons, requestedResultMode, fallbackFactual.semantics),
|
||||||
fallbackReasons,
|
capabilityAudit,
|
||||||
requestedResultMode,
|
shadowRouteAudit,
|
||||||
fallbackFactual.semantics
|
limitedReasonCategory: "recipe_visibility_gap",
|
||||||
)
|
semanticFrame
|
||||||
}
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5223,71 +5193,37 @@ export class AddressQueryService {
|
||||||
finalRouteExpectationAudit.status === "mismatch"
|
finalRouteExpectationAudit.status === "mismatch"
|
||||||
? [...baseReasons, `route_expectation_mismatch:${finalRouteExpectationAudit.reason}`]
|
? [...baseReasons, `route_expectation_mismatch:${finalRouteExpectationAudit.reason}`]
|
||||||
: baseReasons;
|
: baseReasons;
|
||||||
return {
|
return buildFactualExecutionResult({
|
||||||
handled: true,
|
replyText: factual.text,
|
||||||
reply_text: factual.text,
|
responseType: factual.responseType,
|
||||||
reply_type: inferReplyType(factual.responseType),
|
responseSemantics: factual.semantics,
|
||||||
response_type: factual.responseType,
|
selectedRecipe: effectiveRecipeId,
|
||||||
debug: {
|
mcpCallStatus: stageStatus,
|
||||||
detected_mode: mode.mode,
|
rowsFetched: mcp.fetched_rows,
|
||||||
detected_mode_confidence: mode.confidence,
|
rawRowsReceived: mcp.raw_rows.length,
|
||||||
query_shape: shape.shape,
|
rowsAfterAccountScope: normalizedRows.length,
|
||||||
query_shape_confidence: shape.confidence,
|
rowsAfterRecipeFilter: filterByAnchors.length,
|
||||||
detected_intent: intent.intent,
|
rowsMaterialized: normalizedRows.length,
|
||||||
detected_intent_confidence: intent.confidence,
|
rowsMatched: filteredRows.length,
|
||||||
extracted_filters: filters.extracted_filters,
|
rawRowKeysSample: rowDiagnostics.rawRowKeysSample,
|
||||||
missing_required_filters: [],
|
materializationDropReason: rowDiagnostics.materializationDropReason,
|
||||||
selected_recipe: effectiveRecipeId,
|
accountScopeMode: plan.account_scope_mode,
|
||||||
mcp_call_status_legacy: toLegacyMcpStatus(stageStatus),
|
accountScopeFallbackApplied,
|
||||||
account_scope_mode: plan.account_scope_mode,
|
accountScopeAudit,
|
||||||
account_scope_fallback_applied: accountScopeFallbackApplied,
|
anchor,
|
||||||
anchor_type: anchor.anchor_type,
|
matchFailureStage: "none",
|
||||||
anchor_value_raw: anchor.anchor_value_raw,
|
matchFailureReason: null,
|
||||||
anchor_value_resolved: anchor.anchor_value_resolved,
|
|
||||||
resolver_confidence: anchor.resolver_confidence,
|
|
||||||
ambiguity_count: anchor.ambiguity_count,
|
|
||||||
match_failure_stage: "none",
|
|
||||||
match_failure_reason: null,
|
|
||||||
mcp_call_status: stageStatus,
|
|
||||||
rows_fetched: mcp.fetched_rows,
|
|
||||||
raw_rows_received: mcp.raw_rows.length,
|
|
||||||
rows_after_account_scope: normalizedRows.length,
|
|
||||||
rows_after_recipe_filter: filterByAnchors.length,
|
|
||||||
rows_materialized: normalizedRows.length,
|
|
||||||
rows_matched: filteredRows.length,
|
|
||||||
raw_row_keys_sample: rowDiagnostics.rawRowKeysSample,
|
|
||||||
materialization_drop_reason: rowDiagnostics.materializationDropReason,
|
|
||||||
account_token_raw: accountScopeAudit.accountTokenRaw,
|
|
||||||
account_token_normalized: accountScopeAudit.accountTokenNormalized,
|
|
||||||
account_scope_fields_checked: accountScopeAudit.accountScopeFieldsChecked,
|
|
||||||
account_scope_match_strategy: accountScopeAudit.accountScopeMatchStrategy,
|
|
||||||
account_scope_drop_reason: accountScopeAudit.accountScopeDropReason,
|
|
||||||
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
|
||||||
limited_reason_category: null,
|
|
||||||
response_type: factual.responseType,
|
|
||||||
capability_id: capabilityAudit.capabilityId,
|
|
||||||
capability_layer: capabilityAudit.layer,
|
|
||||||
capability_route_mode: capabilityAudit.routeMode,
|
|
||||||
capability_route_enabled: capabilityAudit.enabled,
|
|
||||||
capability_route_reason: capabilityAudit.reason,
|
|
||||||
shadow_route_intent: shadowRouteAudit.intent,
|
|
||||||
shadow_route_selected_recipe: shadowRouteAudit.selectedRecipe,
|
|
||||||
shadow_route_status: shadowRouteAudit.status,
|
|
||||||
route_expectation_status: finalRouteExpectationAudit.status,
|
|
||||||
route_expectation_reason: finalRouteExpectationAudit.reason,
|
|
||||||
route_expectation_expected_selected_recipes: finalRouteExpectationAudit.expectedSelectedRecipes,
|
|
||||||
route_expectation_expected_requested_result_modes: finalRouteExpectationAudit.expectedRequestedResultModes,
|
|
||||||
route_expectation_expected_result_modes: finalRouteExpectationAudit.expectedResultModes,
|
|
||||||
semantic_frame: semanticFrame,
|
|
||||||
...factualResultSemantics,
|
|
||||||
limitations: factualLimitations,
|
limitations: factualLimitations,
|
||||||
reasons: withConfirmedBalanceFallbackReason(
|
reasons: withConfirmedBalanceFallbackReason(
|
||||||
reasonsWithRouteExpectation,
|
reasonsWithRouteExpectation,
|
||||||
requestedResultMode,
|
requestedResultMode,
|
||||||
factual.semantics,
|
factual.semantics,
|
||||||
factualResultSemantics.result_mode
|
factualResultSemantics.result_mode
|
||||||
)
|
),
|
||||||
}
|
routeExpectationAudit: finalRouteExpectationAudit,
|
||||||
};
|
capabilityAudit,
|
||||||
|
shadowRouteAudit,
|
||||||
|
semanticFrame
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export interface ResolveAddressTruthGateInput {
|
||||||
filters?: AddressFilterSet | null;
|
filters?: AddressFilterSet | null;
|
||||||
semanticFrame?: AddressSemanticFrame | null;
|
semanticFrame?: AddressSemanticFrame | null;
|
||||||
selectedRecipe?: string | null;
|
selectedRecipe?: string | null;
|
||||||
|
truthGateStatusHint?: AssistantTruthGateContractStatus | null;
|
||||||
rowsMatched?: number;
|
rowsMatched?: number;
|
||||||
limitedReasonCategory?: AddressLimitedReasonCategory | null;
|
limitedReasonCategory?: AddressLimitedReasonCategory | null;
|
||||||
runtimeReadiness?: AddressRuntimeReadiness | null;
|
runtimeReadiness?: AddressRuntimeReadiness | null;
|
||||||
|
|
@ -170,6 +171,9 @@ function hasReusableRootScope(input: ResolveAddressTruthGateInput): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
function truthGateStatusFrom(input: ResolveAddressTruthGateInput): AssistantTruthGateContractStatus {
|
function truthGateStatusFrom(input: ResolveAddressTruthGateInput): AssistantTruthGateContractStatus {
|
||||||
|
if (input.truthGateStatusHint) {
|
||||||
|
return input.truthGateStatusHint;
|
||||||
|
}
|
||||||
const missingRequiredFilters = input.missingRequiredFilters ?? [];
|
const missingRequiredFilters = input.missingRequiredFilters ?? [];
|
||||||
if (input.routeExpectationStatus === "mismatch") {
|
if (input.routeExpectationStatus === "mismatch") {
|
||||||
return "blocked_route_expectation_failure";
|
return "blocked_route_expectation_failure";
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ export const INVENTORY_CAPABILITY_CONTRACTS: readonly AssistantCapabilityContrac
|
||||||
capability_id: "confirmed_inventory_on_hand_as_of_date",
|
capability_id: "confirmed_inventory_on_hand_as_of_date",
|
||||||
intent_ids: ["inventory_on_hand_as_of_date"],
|
intent_ids: ["inventory_on_hand_as_of_date"],
|
||||||
entry_modes: ["root_entry", "root_followup", "clarification_resume"],
|
entry_modes: ["root_entry", "root_followup", "clarification_resume"],
|
||||||
transitions: ["T1", "T2", "T7"],
|
transitions: ["T1", "T2", "T6", "T7"],
|
||||||
requiresFocusObject: false,
|
requiresFocusObject: false,
|
||||||
requiredAnchors: [],
|
requiredAnchors: [],
|
||||||
resultShape: "item_list_with_quantity_cost_warehouse_organization",
|
resultShape: "item_list_with_quantity_cost_warehouse_organization",
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export function hasInventorySupplierCue(text: string): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
/(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|откуда\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(
|
/(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?(?:это|этот\s+товар|эту\s+позицию)\s+поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|откуда\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(
|
||||||
value
|
value
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ describe("counterparty shipment item flow and open-items routing", () => {
|
||||||
expect(result?.handled).toBe(true);
|
expect(result?.handled).toBe(true);
|
||||||
expect(result?.response_type).toBe("FACTUAL_LIST");
|
expect(result?.response_type).toBe("FACTUAL_LIST");
|
||||||
expect(result?.debug.detected_intent).toBe("list_documents_by_counterparty");
|
expect(result?.debug.detected_intent).toBe("list_documents_by_counterparty");
|
||||||
|
expect(result?.debug.address_truth_gate_v1?.truth_gate_status).toBe("full_confirmed");
|
||||||
expect(String(result?.reply_text ?? "")).toContain("Контрагент: Чепурнов П.Д.");
|
expect(String(result?.reply_text ?? "")).toContain("Контрагент: Чепурнов П.Д.");
|
||||||
expect(String(result?.reply_text ?? "")).toContain("Позиции:");
|
expect(String(result?.reply_text ?? "")).toContain("Позиции:");
|
||||||
expect(String(result?.reply_text ?? "")).toContain("Кабель силовой");
|
expect(String(result?.reply_text ?? "")).toContain("Кабель силовой");
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,38 @@ describe("inventory root frame regressions", () => {
|
||||||
expect(result?.baseReasons).toContain("address_followup_context_applied");
|
expect(result?.baseReasons).toContain("address_followup_context_applied");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("restores inventory root frame for 'покажи еще раз остатки на эту же дату' after item documents drilldown", () => {
|
||||||
|
const result = runAddressDecomposeStage("покажи еще раз остатки на эту же дату", {
|
||||||
|
previous_intent: "inventory_purchase_documents_for_item",
|
||||||
|
previous_filters: {
|
||||||
|
item: "Столешница 600*3050*26 альмандин",
|
||||||
|
organization: 'ООО "Альтернатива Плюс"',
|
||||||
|
as_of_date: "2021-03-31"
|
||||||
|
},
|
||||||
|
previous_anchor_type: "item",
|
||||||
|
previous_anchor_value: "Столешница 600*3050*26 альмандин",
|
||||||
|
root_intent: "inventory_on_hand_as_of_date",
|
||||||
|
root_filters: {
|
||||||
|
organization: 'ООО "Альтернатива Плюс"',
|
||||||
|
period_from: "2021-03-01",
|
||||||
|
period_to: "2021-03-31",
|
||||||
|
as_of_date: "2021-03-31"
|
||||||
|
},
|
||||||
|
root_anchor_type: "organization",
|
||||||
|
root_anchor_value: 'ООО "Альтернатива Плюс"',
|
||||||
|
current_frame_kind: "inventory_drilldown"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result?.intent.intent).toBe("inventory_on_hand_as_of_date");
|
||||||
|
expect(result?.intent.reasons).toContain("intent_restored_to_inventory_root_frame");
|
||||||
|
expect(result?.filters.extracted_filters.organization).toBe('ООО "Альтернатива Плюс"');
|
||||||
|
expect(result?.filters.extracted_filters.as_of_date).toBe("2021-03-31");
|
||||||
|
expect(result?.filters.extracted_filters.period_from).toBe("2021-03-01");
|
||||||
|
expect(result?.filters.extracted_filters.period_to).toBe("2021-03-31");
|
||||||
|
expect(result?.filters.extracted_filters.item).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it("restores inventory root frame for root-context-only month follow-up after drilldown", () => {
|
it("restores inventory root frame for root-context-only month follow-up after drilldown", () => {
|
||||||
const result = runAddressDecomposeStage("остатки на июль 2019", {
|
const result = runAddressDecomposeStage("остатки на июль 2019", {
|
||||||
previous_filters: {
|
previous_filters: {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ describe("inventory selected-object follow-up", () => {
|
||||||
expect(result?.debug.capability_route_mode).toBe("exact");
|
expect(result?.debug.capability_route_mode).toBe("exact");
|
||||||
expect(result?.debug.reasons).toContain("period_window_auto_broadened_to_available_data");
|
expect(result?.debug.reasons).toContain("period_window_auto_broadened_to_available_data");
|
||||||
expect(result?.debug.limitations).toContain("period_window_auto_broadened_to_available_data");
|
expect(result?.debug.limitations).toContain("period_window_auto_broadened_to_available_data");
|
||||||
|
expect(result?.debug.address_truth_gate_v1?.truth_gate_status).toBe("limited_temporal_or_contextual");
|
||||||
|
expect(result?.debug.address_truth_gate_v1?.carryover_eligibility).toBe("object_only");
|
||||||
const replyLines = String(result?.reply_text ?? "").split("\n");
|
const replyLines = String(result?.reply_text ?? "").split("\n");
|
||||||
expect(replyLines[0]).toContain("По позиции Кромка с клеем 33 альмандин 137 м");
|
expect(replyLines[0]).toContain("По позиции Кромка с клеем 33 альмандин 137 м");
|
||||||
expect(replyLines[0]).toContain("до 31.03.2021 подтвержден поставщик");
|
expect(replyLines[0]).toContain("до 31.03.2021 подтвержден поставщик");
|
||||||
|
|
@ -197,6 +199,56 @@ describe("inventory selected-object follow-up", () => {
|
||||||
expect(String(result?.reply_text ?? "")).toContain("Торговый дом \\Союз МСК\\");
|
expect(String(result?.reply_text ?? "")).toContain("Торговый дом \\Союз МСК\\");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("handles selected-object supplier wording 'кто нам это поставил' as provenance follow-up", async () => {
|
||||||
|
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||||||
|
fetched_rows: 1,
|
||||||
|
matched_rows: 1,
|
||||||
|
raw_rows: [
|
||||||
|
{
|
||||||
|
Period: "2019-02-12T00:00:00Z",
|
||||||
|
Registrator: "Поступление товаров и услуг 00000000003 от 12.02.2019 0:00:00",
|
||||||
|
AccountDt: "41.01",
|
||||||
|
AccountKt: "60.01",
|
||||||
|
Amount: 3690,
|
||||||
|
SubcontoDt1: "Столешница 600*3050*26 альмандин",
|
||||||
|
SubcontoDt3: "Основной склад",
|
||||||
|
SubcontoKt1: "Торговый дом \\Союз",
|
||||||
|
SubcontoKt2: "Договор поставки № 12 от 01.02.2019",
|
||||||
|
Organization: "ООО \\Альтернатива Плюс\\"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
rows: [],
|
||||||
|
error: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const service = new AddressQueryService();
|
||||||
|
const result = await service.tryHandle(
|
||||||
|
'По выбранному объекту "Столешница 600*3050*26 альмандин": кто нам это поставил?',
|
||||||
|
{
|
||||||
|
followupContext: {
|
||||||
|
previous_intent: "inventory_on_hand_as_of_date",
|
||||||
|
previous_filters: {
|
||||||
|
as_of_date: "2021-03-31",
|
||||||
|
period_from: "2021-03-01",
|
||||||
|
period_to: "2021-03-31",
|
||||||
|
warehouse: "Основной склад",
|
||||||
|
organization: "ООО \\Альтернатива Плюс\\"
|
||||||
|
},
|
||||||
|
previous_anchor_type: "unknown",
|
||||||
|
previous_anchor_value: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result?.handled).toBe(true);
|
||||||
|
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
|
||||||
|
expect(result?.debug.detected_intent).toBe("inventory_purchase_provenance_for_item");
|
||||||
|
expect(result?.debug.extracted_filters?.item).toBe("Столешница 600*3050*26 альмандин");
|
||||||
|
expect(result?.debug.extracted_filters?.as_of_date).toBeUndefined();
|
||||||
|
expect(result?.debug.capability_id).toBe("inventory_inventory_purchase_provenance_for_item");
|
||||||
|
expect(String(result?.reply_text ?? "")).toContain("Торговый дом \\Союз");
|
||||||
|
});
|
||||||
|
|
||||||
it("handles selected-object colloquial supplier wording 'у кого купили' as provenance follow-up", async () => {
|
it("handles selected-object colloquial supplier wording 'у кого купили' as provenance follow-up", async () => {
|
||||||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||||||
fetched_rows: 1,
|
fetched_rows: 1,
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,38 @@ describe("assistant capability runtime binding adapter", () => {
|
||||||
expect(binding.violations).toContain("transition_not_supported_by_capability");
|
expect(binding.violations).toContain("transition_not_supported_by_capability");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows inventory root capability to return through T6 after a drilldown pivot", () => {
|
||||||
|
const binding = resolveAssistantCapabilityRuntimeBinding({
|
||||||
|
addressDebug: {
|
||||||
|
capability_id: "confirmed_inventory_on_hand_as_of_date",
|
||||||
|
detected_intent: "inventory_on_hand_as_of_date",
|
||||||
|
detected_mode: "address_query",
|
||||||
|
capability_layer: "compute",
|
||||||
|
capability_route_mode: "exact",
|
||||||
|
rows_matched: 11,
|
||||||
|
route_expectation_status: "matched"
|
||||||
|
},
|
||||||
|
runtimeContractShadow: {
|
||||||
|
schema_version: "assistant_runtime_contracts_v1",
|
||||||
|
transition_contract_id: "T6",
|
||||||
|
transition_contract_title: "Domain Pivot With Root-Only Carryover",
|
||||||
|
transition_contract_reason: ["root_context_reused_after_drilldown_context"],
|
||||||
|
capability_contract_id: "confirmed_inventory_on_hand_as_of_date",
|
||||||
|
capability_contract_reason: ["debug_capability_id_matched_contract"],
|
||||||
|
truth_gate_contract_status: "full_confirmed",
|
||||||
|
carryover_eligibility: "root_only"
|
||||||
|
},
|
||||||
|
groundingStatus: "grounded",
|
||||||
|
replyType: "factual"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(binding.binding_status).toBe("bound");
|
||||||
|
expect(binding.binding_action).toBe("allow");
|
||||||
|
expect(binding.transition_id).toBe("T6");
|
||||||
|
expect(binding.transition_allowed).toBe(true);
|
||||||
|
expect(binding.violations).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
it("attaches compact debug fields and preserves the binding contract", () => {
|
it("attaches compact debug fields and preserves the binding contract", () => {
|
||||||
const debug = attachAssistantCapabilityRuntimeBinding(
|
const debug = attachAssistantCapabilityRuntimeBinding(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,33 @@ describe("assistant truth answer policy runtime adapter", () => {
|
||||||
expect(policy.answer_shape.may_power_followup).toBe(true);
|
expect(policy.answer_shape.may_power_followup).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps explicit temporal-limited factual answers limited in the truth contract", () => {
|
||||||
|
const policy = resolveAssistantTruthAnswerPolicyRuntime({
|
||||||
|
addressDebug: {
|
||||||
|
capability_id: "inventory_inventory_purchase_provenance_for_item",
|
||||||
|
rows_matched: 2,
|
||||||
|
address_truth_gate_v1: {
|
||||||
|
schema_version: "address_truth_gate_v1",
|
||||||
|
policy_owner: "addressTruthGatePolicy",
|
||||||
|
truth_gate_status: "limited_temporal_or_contextual",
|
||||||
|
carryover_eligibility: "object_only",
|
||||||
|
limited_reason_category: null,
|
||||||
|
runtime_readiness: "LIVE_QUERYABLE_WITH_LIMITS",
|
||||||
|
reason_codes: ["period_window_auto_broadened_to_available_data"],
|
||||||
|
blocked_or_limited_explanation: "temporal_or_contextual_limit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
replyType: "factual"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(policy.truth_gate.coverage_status).toBe("partial");
|
||||||
|
expect(policy.truth_gate.truth_mode).toBe("limited");
|
||||||
|
expect(policy.truth_gate.carryover_eligibility).toBe("object_only");
|
||||||
|
expect(policy.truth_gate.blocked_or_limited_explanation).toBe("temporal_or_contextual_limit");
|
||||||
|
expect(policy.answer_shape.answer_shape).toBe("limited_with_reason");
|
||||||
|
expect(policy.answer_shape.must_include_limitation).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("attaches top-level debug fields without hiding the nested contract", () => {
|
it("attaches top-level debug fields without hiding the nested contract", () => {
|
||||||
const debug = attachAssistantTruthAnswerPolicy(
|
const debug = attachAssistantTruthAnswerPolicy(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"suite_id": "assistant_saved_session_gen-mo2kcds2-tlqmvng",
|
||||||
|
"suite_version": "0.1.0",
|
||||||
|
"schema_version": "assistant_saved_session_suite_v0_1",
|
||||||
|
"generated_at": "2026-04-17T07:04:48.578Z",
|
||||||
|
"generation_id": "gen-mo2kcds2-tlqmvng",
|
||||||
|
"mode": "saved_user_sessions",
|
||||||
|
"title": "Ручная сессия 17.04.2026, 10:04:19 ТЕМП",
|
||||||
|
"scenario_count": 1,
|
||||||
|
"case_ids": [
|
||||||
|
"SAVED-001"
|
||||||
|
],
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"case_id": "SAVED-001",
|
||||||
|
"scenario_tag": "saved_user_sessions",
|
||||||
|
"title": "Ручная сессия 17.04.2026, 10:04:19 ТЕМП",
|
||||||
|
"question_type": "followup",
|
||||||
|
"broadness_level": "medium",
|
||||||
|
"turns": [
|
||||||
|
{
|
||||||
|
"user_message": "покажи все документы по чепурнову"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "что нам отгружал чепурнов, какой товар или услугу?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "какие остатки на складе на сегодня?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "хвосты по счету 60 на август 2022"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "какие остатки на складе на март 2021"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": покажи документы по этой позиции"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "покажи еще раз остатки на эту же дату"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "какие остатки на складе на март 2016"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "на июль 2019"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "на сентябрь"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "а на март"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "это по общей базе"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>NDC AI Normalizer Playground</title>
|
<title>NDC AI Normalizer Playground</title>
|
||||||
<script type="module" crossorigin src="/assets/index-Bw40I8e3.js"></script>
|
<script type="module" crossorigin src="/assets/index-BIzNO_Mb.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DkWsdP2H.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DkWsdP2H.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,27 @@ function formatAutoGenModeLabel(mode: AutoGenMode): string {
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAgentSemanticGeneration(item: AutoGenHistoryRecord | null | undefined): boolean {
|
||||||
|
if (!item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (item.context?.agent_run === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (item.context?.saved_case_set_kind === "agent_semantic_scenario") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return typeof item.title === "string" && item.title.trim().toUpperCase().startsWith("AGENT");
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatAutoGenGenerationTitle(item: AutoGenHistoryRecord): string {
|
||||||
|
const fallback = item.title ?? formatDateTime(item.created_at);
|
||||||
|
if (isAgentSemanticGeneration(item) && !fallback.trim().toUpperCase().startsWith("AGENT")) {
|
||||||
|
return `AGENT | ${fallback}`;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
function buildSavedSessionDefaultTitle(items: AssistantConversationItem[]): string {
|
function buildSavedSessionDefaultTitle(items: AssistantConversationItem[]): string {
|
||||||
const lastMessage = items[items.length - 1];
|
const lastMessage = items[items.length - 1];
|
||||||
const timestamp = formatDateTime(lastMessage?.created_at ?? new Date().toISOString());
|
const timestamp = formatDateTime(lastMessage?.created_at ?? new Date().toISOString());
|
||||||
|
|
@ -2375,7 +2396,7 @@ export function AutoRunsHistoryPanel({
|
||||||
) : null}
|
) : null}
|
||||||
{visibleAutoGenHistory.map((item) => (
|
{visibleAutoGenHistory.map((item) => (
|
||||||
<option key={item.generation_id} value={item.generation_id}>
|
<option key={item.generation_id} value={item.generation_id}>
|
||||||
{formatDateTime(item.created_at)} | {item.title ?? formatAutoGenModeLabel(item.mode)} | {item.count}
|
{formatDateTime(item.created_at)} | {formatAutoGenGenerationTitle(item) ?? formatAutoGenModeLabel(item.mode)} | {item.count}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -2448,7 +2469,7 @@ export function AutoRunsHistoryPanel({
|
||||||
onClick={() => setSelectedAutogenGenerationId(item.generation_id)}
|
onClick={() => setSelectedAutogenGenerationId(item.generation_id)}
|
||||||
>
|
>
|
||||||
<header>
|
<header>
|
||||||
<strong>{item.title ?? formatDateTime(item.created_at)}</strong>
|
<strong>{formatAutoGenGenerationTitle(item)}</strong>
|
||||||
<div className="autoruns-autogen-card-actions">
|
<div className="autoruns-autogen-card-actions">
|
||||||
<span>{formatDateTime(item.created_at)}</span>
|
<span>{formatDateTime(item.created_at)}</span>
|
||||||
<button
|
<button
|
||||||
|
|
@ -2488,6 +2509,13 @@ export function AutoRunsHistoryPanel({
|
||||||
<div className="autoruns-run-meta">
|
<div className="autoruns-run-meta">
|
||||||
режим={formatAutoGenModeLabel(item.mode)} | count={item.count}
|
режим={formatAutoGenModeLabel(item.mode)} | count={item.count}
|
||||||
</div>
|
</div>
|
||||||
|
{isAgentSemanticGeneration(item) ? (
|
||||||
|
<div className="autoruns-run-meta">
|
||||||
|
тип=АГЕНТНЫЙ ПРОГОН
|
||||||
|
{item.context?.architecture_phase ? ` | этап=${item.context.architecture_phase}` : ""}
|
||||||
|
{item.context?.agent_focus ? ` | фокус=${item.context.agent_focus}` : ""}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
{item.domain || item.generated_by ? (
|
{item.domain || item.generated_by ? (
|
||||||
<div className="autoruns-run-meta">
|
<div className="autoruns-run-meta">
|
||||||
{item.domain ? `домен=${item.domain}` : "домен=общий"}
|
{item.domain ? `домен=${item.domain}` : "домен=общий"}
|
||||||
|
|
|
||||||
|
|
@ -334,6 +334,10 @@ export interface AutoGenHistoryRecord {
|
||||||
source_session_id?: string | null;
|
source_session_id?: string | null;
|
||||||
saved_session_file?: string | null;
|
saved_session_file?: string | null;
|
||||||
saved_case_set_kind?: string | null;
|
saved_case_set_kind?: string | null;
|
||||||
|
agent_run?: boolean | null;
|
||||||
|
agent_focus?: string | null;
|
||||||
|
architecture_phase?: string | null;
|
||||||
|
source_spec_file?: string | null;
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,318 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import secrets
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
HISTORY_FILE = REPO_ROOT / "llm_normalizer" / "data" / "autorun_generators" / "history.json"
|
||||||
|
SAVED_SESSIONS_DIR = REPO_ROOT / "llm_normalizer" / "data" / "autorun_generators" / "saved_sessions"
|
||||||
|
EVAL_CASES_DIR = REPO_ROOT / "llm_normalizer" / "data" / "eval_cases"
|
||||||
|
|
||||||
|
|
||||||
|
def now_utc() -> datetime:
|
||||||
|
return datetime.now(timezone.utc).replace(microsecond=0)
|
||||||
|
|
||||||
|
|
||||||
|
def utc_stamp(dt: datetime) -> str:
|
||||||
|
return (
|
||||||
|
f"{dt.year:04d}{dt.month:02d}{dt.day:02d}"
|
||||||
|
f"{dt.hour:02d}{dt.minute:02d}{dt.second:02d}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_id(dt: datetime) -> str:
|
||||||
|
return f"gen-ag{dt.strftime('%m%d%H%M')}-{secrets.token_hex(3)}"
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_question(value: Any) -> str:
|
||||||
|
text = str(value or "").replace("\r\n", "\n").replace("\r", "\n")
|
||||||
|
text = "\n".join(line.strip() for line in text.split("\n"))
|
||||||
|
text = re.sub(r"[ \t]+", " ", text).strip()
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_agent_title(title: str) -> str:
|
||||||
|
normalized = title.strip()
|
||||||
|
if not normalized:
|
||||||
|
raise RuntimeError("Agent semantic run title must not be empty")
|
||||||
|
return normalized if normalized.upper().startswith("AGENT") else f"AGENT | {normalized}"
|
||||||
|
|
||||||
|
|
||||||
|
def load_json(path: Path) -> Any:
|
||||||
|
return json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def write_json(path: Path, payload: Any) -> None:
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_questions(raw_questions: list[Any]) -> list[str]:
|
||||||
|
result: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for item in raw_questions:
|
||||||
|
question = sanitize_question(item)
|
||||||
|
if not question or question in seen:
|
||||||
|
continue
|
||||||
|
seen.add(question)
|
||||||
|
result.append(question)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def extract_questions_from_spec(spec: dict[str, Any]) -> list[str]:
|
||||||
|
if isinstance(spec.get("questions"), list):
|
||||||
|
return normalize_questions(list(spec["questions"]))
|
||||||
|
steps = spec.get("steps")
|
||||||
|
if isinstance(steps, list):
|
||||||
|
return normalize_questions(
|
||||||
|
[step.get("question") for step in steps if isinstance(step, dict) and step.get("question")]
|
||||||
|
)
|
||||||
|
raise RuntimeError("Spec must define either `questions[]` or `steps[].question`")
|
||||||
|
|
||||||
|
|
||||||
|
def build_case_set_payload(
|
||||||
|
generation_id: str,
|
||||||
|
title: str,
|
||||||
|
questions: list[str],
|
||||||
|
domain: str | None,
|
||||||
|
scenario_tag: str,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
turns = [{"user_message": question} for question in questions]
|
||||||
|
case_id = "SAVED-001"
|
||||||
|
return {
|
||||||
|
"suite_id": f"assistant_saved_session_{generation_id}",
|
||||||
|
"suite_version": "0.1.0",
|
||||||
|
"schema_version": "assistant_saved_session_suite_v0_1",
|
||||||
|
"generated_at": now_utc().isoformat(),
|
||||||
|
"generation_id": generation_id,
|
||||||
|
"mode": "saved_user_sessions",
|
||||||
|
"title": title,
|
||||||
|
"domain": domain,
|
||||||
|
"scenario_count": 1 if turns else 0,
|
||||||
|
"case_ids": [case_id] if turns else [],
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"case_id": case_id,
|
||||||
|
"scenario_tag": scenario_tag,
|
||||||
|
"title": title,
|
||||||
|
"question_type": "followup" if len(turns) > 1 else "direct",
|
||||||
|
"broadness_level": "medium",
|
||||||
|
"turns": turns,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
if turns
|
||||||
|
else [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_snapshot_payload(
|
||||||
|
generation_id: str,
|
||||||
|
title: str,
|
||||||
|
questions: list[str],
|
||||||
|
metadata: dict[str, Any],
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
created_at = now_utc().isoformat()
|
||||||
|
items: list[dict[str, Any]] = []
|
||||||
|
for index, question in enumerate(questions, start=1):
|
||||||
|
items.append(
|
||||||
|
{
|
||||||
|
"message_id": f"agent-user-{index:03d}",
|
||||||
|
"role": "user",
|
||||||
|
"text": question,
|
||||||
|
"created_at": created_at,
|
||||||
|
"reply_type": None,
|
||||||
|
"trace_id": None,
|
||||||
|
"debug": None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"saved_at": created_at,
|
||||||
|
"generation_id": generation_id,
|
||||||
|
"mode": "saved_user_sessions",
|
||||||
|
"title": title,
|
||||||
|
"agent_run": True,
|
||||||
|
"questions": questions,
|
||||||
|
"metadata": metadata,
|
||||||
|
"source_session_id": None,
|
||||||
|
"session": {
|
||||||
|
"session_id": None,
|
||||||
|
"mode": "agent_semantic_run",
|
||||||
|
"items": items,
|
||||||
|
"agent_run": True,
|
||||||
|
"metadata": metadata,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def read_history() -> list[dict[str, Any]]:
|
||||||
|
if not HISTORY_FILE.exists():
|
||||||
|
return []
|
||||||
|
parsed = load_json(HISTORY_FILE)
|
||||||
|
return parsed if isinstance(parsed, list) else []
|
||||||
|
|
||||||
|
|
||||||
|
def build_history_record(
|
||||||
|
generation_id: str,
|
||||||
|
title: str,
|
||||||
|
questions: list[str],
|
||||||
|
case_set_file: str,
|
||||||
|
saved_session_file: str,
|
||||||
|
domain: str | None,
|
||||||
|
generated_by: str,
|
||||||
|
metadata: dict[str, Any],
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
context = {
|
||||||
|
"llm_provider": None,
|
||||||
|
"model": None,
|
||||||
|
"assistant_prompt_version": metadata.get("assistant_prompt_version"),
|
||||||
|
"decomposition_prompt_version": metadata.get("decomposition_prompt_version"),
|
||||||
|
"prompt_fingerprint": metadata.get("prompt_fingerprint"),
|
||||||
|
"autogen_personality_id": None,
|
||||||
|
"autogen_personality_prompt": None,
|
||||||
|
"source_session_id": None,
|
||||||
|
"saved_session_file": saved_session_file,
|
||||||
|
"saved_case_set_kind": "agent_semantic_scenario",
|
||||||
|
"agent_run": True,
|
||||||
|
"agent_focus": metadata.get("agent_focus"),
|
||||||
|
"architecture_phase": metadata.get("architecture_phase"),
|
||||||
|
"source_spec_file": metadata.get("source_spec_file"),
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"generation_id": generation_id,
|
||||||
|
"created_at": now_utc().isoformat(),
|
||||||
|
"mode": "saved_user_sessions",
|
||||||
|
"title": title,
|
||||||
|
"count": len(questions),
|
||||||
|
"domain": domain,
|
||||||
|
"questions": questions,
|
||||||
|
"generated_by": generated_by,
|
||||||
|
"saved_case_set_file": case_set_file,
|
||||||
|
"context": context,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_metadata(args: argparse.Namespace, spec: dict[str, Any], spec_path: Path | None) -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"assistant_prompt_version": args.assistant_prompt_version,
|
||||||
|
"decomposition_prompt_version": args.decomposition_prompt_version,
|
||||||
|
"prompt_fingerprint": args.prompt_fingerprint,
|
||||||
|
"agent_focus": args.agent_focus or spec.get("description") or spec.get("title"),
|
||||||
|
"architecture_phase": args.architecture_phase,
|
||||||
|
"source_spec_file": str(spec_path.resolve()) if spec_path else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description="Save a targeted AGENT semantic run into autoruns user sessions.")
|
||||||
|
parser.add_argument("--spec", required=True, help="Path to a truth-harness spec or simple questions spec JSON.")
|
||||||
|
parser.add_argument("--title", help="Override title for the AGENT run.")
|
||||||
|
parser.add_argument("--generated-by", default="codex_agent", help="Author label for the generated run.")
|
||||||
|
parser.add_argument("--architecture-phase", default="turnaround_11", help="Architecture phase / slice being validated.")
|
||||||
|
parser.add_argument("--agent-focus", help="Short focus label for the targeted fix.")
|
||||||
|
parser.add_argument("--assistant-prompt-version", help="Optional assistant prompt version metadata.")
|
||||||
|
parser.add_argument("--decomposition-prompt-version", help="Optional decomposition prompt version metadata.")
|
||||||
|
parser.add_argument("--prompt-fingerprint", help="Optional prompt fingerprint metadata.")
|
||||||
|
parser.add_argument("--dry-run", action="store_true", help="Print resulting record metadata without writing files.")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
spec_path = Path(args.spec)
|
||||||
|
if not spec_path.is_absolute():
|
||||||
|
spec_path = (REPO_ROOT / spec_path).resolve()
|
||||||
|
if not spec_path.exists():
|
||||||
|
raise RuntimeError(f"Spec file not found: {spec_path}")
|
||||||
|
|
||||||
|
spec_raw = load_json(spec_path)
|
||||||
|
if not isinstance(spec_raw, dict):
|
||||||
|
raise RuntimeError("Spec JSON must be an object")
|
||||||
|
|
||||||
|
questions = extract_questions_from_spec(spec_raw)
|
||||||
|
if not questions:
|
||||||
|
raise RuntimeError("Agent semantic run must contain at least one question")
|
||||||
|
|
||||||
|
domain = str(spec_raw.get("domain") or "").strip() or None
|
||||||
|
source_title = str(args.title or spec_raw.get("title") or spec_path.stem).strip()
|
||||||
|
title = ensure_agent_title(source_title)
|
||||||
|
metadata = build_metadata(args, spec_raw, spec_path)
|
||||||
|
|
||||||
|
timestamp = now_utc()
|
||||||
|
generation_id = generate_id(timestamp)
|
||||||
|
case_set_file = f"assistant_autogen_saved_user_sessions_{utc_stamp(timestamp)}_{generation_id}.json"
|
||||||
|
saved_session_file = f"assistant_saved_session_{utc_stamp(timestamp)}_{generation_id}.json"
|
||||||
|
case_set_payload = build_case_set_payload(
|
||||||
|
generation_id=generation_id,
|
||||||
|
title=title,
|
||||||
|
questions=questions,
|
||||||
|
domain=domain,
|
||||||
|
scenario_tag="agent_saved_user_sessions",
|
||||||
|
)
|
||||||
|
snapshot_payload = build_snapshot_payload(
|
||||||
|
generation_id=generation_id,
|
||||||
|
title=title,
|
||||||
|
questions=questions,
|
||||||
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
record = build_history_record(
|
||||||
|
generation_id=generation_id,
|
||||||
|
title=title,
|
||||||
|
questions=questions,
|
||||||
|
case_set_file=case_set_file,
|
||||||
|
saved_session_file=saved_session_file,
|
||||||
|
domain=domain,
|
||||||
|
generated_by=str(args.generated_by or "codex_agent").strip() or "codex_agent",
|
||||||
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.dry_run:
|
||||||
|
print(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"ok": True,
|
||||||
|
"dry_run": True,
|
||||||
|
"generation_id": generation_id,
|
||||||
|
"title": title,
|
||||||
|
"questions_total": len(questions),
|
||||||
|
"case_set_file": case_set_file,
|
||||||
|
"saved_session_file": saved_session_file,
|
||||||
|
"domain": domain,
|
||||||
|
},
|
||||||
|
ensure_ascii=False,
|
||||||
|
indent=2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
write_json(EVAL_CASES_DIR / case_set_file, case_set_payload)
|
||||||
|
write_json(SAVED_SESSIONS_DIR / saved_session_file, snapshot_payload)
|
||||||
|
history = read_history()
|
||||||
|
history = [record, *[item for item in history if item.get("generation_id") != generation_id]]
|
||||||
|
write_json(HISTORY_FILE, history[:500])
|
||||||
|
|
||||||
|
print(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"ok": True,
|
||||||
|
"generation_id": generation_id,
|
||||||
|
"title": title,
|
||||||
|
"questions_total": len(questions),
|
||||||
|
"case_set_file": case_set_file,
|
||||||
|
"saved_session_file": saved_session_file,
|
||||||
|
},
|
||||||
|
ensure_ascii=False,
|
||||||
|
indent=2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Loading…
Reference in New Issue