АРЧ АП11 - Архитектура: вынести counterparty intent-family из resolveAddressIntent и обновить pre-expansion cut
This commit is contained in:
parent
50c66e71a8
commit
8f9364e7c9
|
|
@ -25,9 +25,9 @@ This snapshot is based on:
|
||||||
|
|
||||||
Latest graph rebuild:
|
Latest graph rebuild:
|
||||||
|
|
||||||
- `5284 nodes`
|
- `5287 nodes`
|
||||||
- `11402 edges`
|
- `11407 edges`
|
||||||
- `138 communities`
|
- `135 communities`
|
||||||
|
|
||||||
Most relevant current god nodes for turnaround `11`:
|
Most relevant current god nodes for turnaround `11`:
|
||||||
|
|
||||||
|
|
@ -126,7 +126,7 @@ This is enough to build targeted semantic packs that are not single-domain toy s
|
||||||
|
|
||||||
## Honest Phase Status
|
## Honest Phase Status
|
||||||
|
|
||||||
Estimated overall turnaround completion: `~91%`
|
Estimated overall turnaround completion: `~92%`
|
||||||
|
|
||||||
### Phase 0. Shared Baseline
|
### Phase 0. Shared Baseline
|
||||||
|
|
||||||
|
|
@ -163,17 +163,19 @@ Remaining debt:
|
||||||
|
|
||||||
### Phase 3. Capability Contracts
|
### Phase 3. Capability Contracts
|
||||||
|
|
||||||
Status: `88%`
|
Status: `89%`
|
||||||
|
|
||||||
Reason:
|
Reason:
|
||||||
|
|
||||||
- critical inventory/address capabilities are materially contract-driven;
|
- critical inventory/address capabilities are materially contract-driven;
|
||||||
- selected-object and root capability behavior is much more explicit than before.
|
- selected-object and root capability behavior is much more explicit than before.
|
||||||
- inventory intent-family now has an explicit owner in `addressInventoryIntentSignals.ts` instead of staying only as inline signal pressure inside `resolveAddressIntent()`.
|
- inventory intent-family now has an explicit owner in `addressInventoryIntentSignals.ts` instead of staying only as inline signal pressure inside `resolveAddressIntent()`.
|
||||||
|
- counterparty / documents / contracts / open-items intent-family now also has an explicit owner in `addressCounterpartyIntentSignals.ts`, even though legacy inline branches still remain as cleanup debt inside the central resolver.
|
||||||
|
|
||||||
Remaining debt:
|
Remaining debt:
|
||||||
|
|
||||||
- `resolveAddressIntent()` is still too central;
|
- `resolveAddressIntent()` is still too central;
|
||||||
|
- extracted owner seams now exist for the hottest inventory and counterparty families, but the legacy inline body still keeps graph pressure high until the final cleanup pass is done;
|
||||||
- some business contours outside the most exercised inventory/address scenarios remain less explicit.
|
- some business contours outside the most exercised inventory/address scenarios remain less explicit.
|
||||||
|
|
||||||
### Phase 4. Coverage / Evidence / Truth Gate Isolation
|
### Phase 4. Coverage / Evidence / Truth Gate Isolation
|
||||||
|
|
@ -253,6 +255,7 @@ Compared with the pre-turnaround baseline, the system is now materially better i
|
||||||
- confirmed-balance and heuristic-candidate reply contracts now have explicit builders instead of repeated inline `semantics` objects in major compose branches;
|
- confirmed-balance and heuristic-candidate reply contracts now have explicit builders instead of repeated inline `semantics` objects in major compose branches;
|
||||||
- inventory factual replies are now owned by a dedicated module rather than embedded directly in the central compose body;
|
- inventory factual replies are now owned by a dedicated module rather than embedded directly in the central compose body;
|
||||||
- inventory intent classification now has a dedicated owner instead of being only an inline segment inside the central address intent resolver;
|
- inventory intent classification now has a dedicated owner instead of being only an inline segment inside the central address intent resolver;
|
||||||
|
- counterparty / contract / documents intent classification now also has a dedicated owner instead of being only an inline segment inside the central address intent resolver;
|
||||||
- architecture regressions can now be localized to route, transition, truth gate, coverage/evidence, boundary, or meta/memory layers.
|
- architecture regressions can now be localized to route, transition, truth gate, coverage/evidence, boundary, or meta/memory layers.
|
||||||
|
|
||||||
## What Still Remains The Main Architectural Debt
|
## What Still Remains The Main Architectural Debt
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ What "done enough" means:
|
||||||
Current status:
|
Current status:
|
||||||
|
|
||||||
- inventory signal-family is now delegated to `addressInventoryIntentSignals.ts`;
|
- inventory signal-family is now delegated to `addressInventoryIntentSignals.ts`;
|
||||||
|
- counterparty / documents / contracts / open-items signal-family is now also delegated to `addressCounterpartyIntentSignals.ts`;
|
||||||
- this reduces ownership pressure, even though the old inline bodies still remain as cleanup debt.
|
- this reduces ownership pressure, even though the old inline bodies still remain as cleanup debt.
|
||||||
|
|
||||||
### 2. Answer semantics pressure in `composeFactualReplyBody()`
|
### 2. Answer semantics pressure in `composeFactualReplyBody()`
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.resolveCounterpartyAddressIntent = resolveCounterpartyAddressIntent;
|
||||||
|
function resolveCounterpartyAddressIntent(text, deps) {
|
||||||
|
if (deps.hasOpenContractsListSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "open_contracts_confirmed_as_of_date",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["open_contract_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasAny(text, deps.openItemsHints) &&
|
||||||
|
!deps.hasCounterpartyDebtLongevitySignal(text) &&
|
||||||
|
!deps.hasInventoryAgingSignal(text) &&
|
||||||
|
!deps.hasInventoryProvenanceSignalV2(text) &&
|
||||||
|
!deps.hasInventoryPurchaseDocumentsSignalV2(text) &&
|
||||||
|
!deps.hasInventorySaleTraceSignalV2(text) &&
|
||||||
|
(/(?:контраг|договор|контракт|counterparty|contract|покупател|клиент|заказчик|customer|client|buyer|supplier|поставщик)/iu.test(text) ||
|
||||||
|
deps.hasAccountNumberAnchor(text) ||
|
||||||
|
deps.hasCompactAccountCodeToken(text))) {
|
||||||
|
return {
|
||||||
|
intent: "open_items_by_counterparty_or_contract",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["open_items_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasPeriodCoverageProfileSignal(text) &&
|
||||||
|
!deps.hasPartyAnchorMention(text) &&
|
||||||
|
!deps.hasContractAnchorSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "period_coverage_profile",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["period_coverage_profile_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasDocumentTypeAndAccountSectionProfileSignal(text) &&
|
||||||
|
!deps.hasPartyAnchorMention(text) &&
|
||||||
|
!deps.hasContractAnchorSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "document_type_and_account_section_profile",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["document_type_and_account_section_profile_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasCounterpartyPopulationAndRolesSignal(text) &&
|
||||||
|
!deps.hasContractAnchorSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "counterparty_population_and_roles",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["counterparty_population_and_roles_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasCounterpartyActivityLifecycleSignal(text) &&
|
||||||
|
!deps.hasContractAnchorSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "counterparty_activity_lifecycle",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["counterparty_activity_lifecycle_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasContractUsageOverviewSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text) &&
|
||||||
|
!deps.hasOpenContractsListSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "contract_usage_overview",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["contract_usage_overview_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasCustomerRevenueAndPaymentsSignal(text) && !deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "customer_revenue_and_payments",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["customer_revenue_and_payments_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasSupplierPayoutsProfileSignal(text) && !deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "supplier_payouts_profile",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["supplier_payouts_profile_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasContractUsageAndValueSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text) &&
|
||||||
|
!deps.hasOpenContractsListSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "contract_usage_and_value",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["contract_usage_and_value_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasContractListByCounterpartySignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "list_contracts_by_counterparty",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["contracts_by_counterparty_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasContractAnchorSignal(text) && deps.hasBankOperationSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "bank_operations_by_contract",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["bank_ops_by_contract_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasContractAnchorSignal(text) &&
|
||||||
|
(deps.hasAny(text, deps.documentsByContractHints) || deps.hasDocumentSignal(text))) {
|
||||||
|
return {
|
||||||
|
intent: "list_documents_by_contract",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["documents_by_contract_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasAny(text, deps.bankOperationsByCounterpartyHints) &&
|
||||||
|
(deps.hasPartyAnchorMention(text) || deps.hasLooseByAnchorMention(text) || deps.hasHeuristicCounterpartyAnchor(text))) {
|
||||||
|
return {
|
||||||
|
intent: "bank_operations_by_counterparty",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["bank_ops_by_counterparty_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ((deps.hasAny(text, deps.documentsByCounterpartyHints) || deps.hasCounterpartyShipmentItemFlowSignal(text)) &&
|
||||||
|
(deps.hasPartyAnchorMention(text) ||
|
||||||
|
deps.hasLooseByAnchorMention(text) ||
|
||||||
|
deps.hasImplicitCounterpartyAnchorAroundDocs(text) ||
|
||||||
|
deps.hasHeuristicCounterpartyAnchor(text) ||
|
||||||
|
deps.hasCounterpartyShipmentItemFlowSignal(text))) {
|
||||||
|
return {
|
||||||
|
intent: "list_documents_by_counterparty",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: [
|
||||||
|
deps.hasCounterpartyShipmentItemFlowSignal(text)
|
||||||
|
? "counterparty_item_flow_signal_detected"
|
||||||
|
: "documents_by_counterparty_signal_detected"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "account_balance_snapshot",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["account_balance_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasLooseByAnchorMention(text) && deps.hasGenericAddressLookupSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "list_documents_by_counterparty",
|
||||||
|
confidence: "low",
|
||||||
|
reasons: ["generic_lookup_with_loose_anchor_fallback"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (deps.hasAny(text, deps.openContractsHints) &&
|
||||||
|
(text.includes("договор") || text.includes("контракт") || text.includes("contract"))) {
|
||||||
|
return {
|
||||||
|
intent: "open_contracts_confirmed_as_of_date",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["open_contract_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.resolveAddressIntent = resolveAddressIntent;
|
exports.resolveAddressIntent = resolveAddressIntent;
|
||||||
|
const addressCounterpartyIntentSignals_1 = require("./addressCounterpartyIntentSignals");
|
||||||
const addressInventoryIntentSignals_1 = require("./addressInventoryIntentSignals");
|
const addressInventoryIntentSignals_1 = require("./addressInventoryIntentSignals");
|
||||||
const inventoryLifecycleCueHelpers_1 = require("./inventoryLifecycleCueHelpers");
|
const inventoryLifecycleCueHelpers_1 = require("./inventoryLifecycleCueHelpers");
|
||||||
const RECEIVABLES_STRONG = [
|
const RECEIVABLES_STRONG = [
|
||||||
|
|
@ -1679,6 +1680,44 @@ function resolveAddressIntent(userMessage) {
|
||||||
reasons: ["inventory_on_hand_signal_detected"]
|
reasons: ["inventory_on_hand_signal_detected"]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const counterpartyIntent = (0, addressCounterpartyIntentSignals_1.resolveCounterpartyAddressIntent)(text, {
|
||||||
|
hasAny,
|
||||||
|
openItemsHints: OPEN_ITEMS_HINTS,
|
||||||
|
openContractsHints: OPEN_CONTRACTS_HINTS,
|
||||||
|
documentsByCounterpartyHints: DOCUMENTS_BY_COUNTERPARTY_HINTS,
|
||||||
|
bankOperationsByCounterpartyHints: BANK_OPERATIONS_BY_COUNTERPARTY_HINTS,
|
||||||
|
documentsByContractHints: DOCUMENTS_BY_CONTRACT_HINTS,
|
||||||
|
hasCounterpartyDebtLongevitySignal,
|
||||||
|
hasInventoryAgingSignal,
|
||||||
|
hasInventoryProvenanceSignalV2,
|
||||||
|
hasInventoryPurchaseDocumentsSignalV2,
|
||||||
|
hasInventorySaleTraceSignalV2,
|
||||||
|
hasAccountNumberAnchor,
|
||||||
|
hasCompactAccountCodeToken,
|
||||||
|
hasPeriodCoverageProfileSignal,
|
||||||
|
hasPartyAnchorMention,
|
||||||
|
hasContractAnchorSignal,
|
||||||
|
hasAccountBalanceSignal,
|
||||||
|
hasDocumentTypeAndAccountSectionProfileSignal,
|
||||||
|
hasCounterpartyPopulationAndRolesSignal,
|
||||||
|
hasCounterpartyActivityLifecycleSignal,
|
||||||
|
hasContractUsageOverviewSignal,
|
||||||
|
hasOpenContractsListSignal,
|
||||||
|
hasCustomerRevenueAndPaymentsSignal,
|
||||||
|
hasSupplierPayoutsProfileSignal,
|
||||||
|
hasContractUsageAndValueSignal,
|
||||||
|
hasContractListByCounterpartySignal,
|
||||||
|
hasBankOperationSignal,
|
||||||
|
hasDocumentSignal,
|
||||||
|
hasLooseByAnchorMention,
|
||||||
|
hasHeuristicCounterpartyAnchor,
|
||||||
|
hasCounterpartyShipmentItemFlowSignal,
|
||||||
|
hasImplicitCounterpartyAnchorAroundDocs,
|
||||||
|
hasGenericAddressLookupSignal
|
||||||
|
});
|
||||||
|
if (counterpartyIntent) {
|
||||||
|
return counterpartyIntent;
|
||||||
|
}
|
||||||
if (hasOpenContractsListSignal(text)) {
|
if (hasOpenContractsListSignal(text)) {
|
||||||
return {
|
return {
|
||||||
intent: "open_contracts_confirmed_as_of_date",
|
intent: "open_contracts_confirmed_as_of_date",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
import type { AddressIntentResolution } from "../types/addressQuery";
|
||||||
|
|
||||||
|
type HintList = string[];
|
||||||
|
|
||||||
|
type CounterpartyIntentDeps = {
|
||||||
|
hasAny: (text: string, hints: string[]) => boolean;
|
||||||
|
openItemsHints: HintList;
|
||||||
|
openContractsHints: HintList;
|
||||||
|
documentsByCounterpartyHints: HintList;
|
||||||
|
bankOperationsByCounterpartyHints: HintList;
|
||||||
|
documentsByContractHints: HintList;
|
||||||
|
hasCounterpartyDebtLongevitySignal: (text: string) => boolean;
|
||||||
|
hasInventoryAgingSignal: (text: string) => boolean;
|
||||||
|
hasInventoryProvenanceSignalV2: (text: string) => boolean;
|
||||||
|
hasInventoryPurchaseDocumentsSignalV2: (text: string) => boolean;
|
||||||
|
hasInventorySaleTraceSignalV2: (text: string) => boolean;
|
||||||
|
hasAccountNumberAnchor: (text: string) => boolean;
|
||||||
|
hasCompactAccountCodeToken: (text: string) => boolean;
|
||||||
|
hasPeriodCoverageProfileSignal: (text: string) => boolean;
|
||||||
|
hasPartyAnchorMention: (text: string) => boolean;
|
||||||
|
hasContractAnchorSignal: (text: string) => boolean;
|
||||||
|
hasAccountBalanceSignal: (text: string) => boolean;
|
||||||
|
hasDocumentTypeAndAccountSectionProfileSignal: (text: string) => boolean;
|
||||||
|
hasCounterpartyPopulationAndRolesSignal: (text: string) => boolean;
|
||||||
|
hasCounterpartyActivityLifecycleSignal: (text: string) => boolean;
|
||||||
|
hasContractUsageOverviewSignal: (text: string) => boolean;
|
||||||
|
hasOpenContractsListSignal: (text: string) => boolean;
|
||||||
|
hasCustomerRevenueAndPaymentsSignal: (text: string) => boolean;
|
||||||
|
hasSupplierPayoutsProfileSignal: (text: string) => boolean;
|
||||||
|
hasContractUsageAndValueSignal: (text: string) => boolean;
|
||||||
|
hasContractListByCounterpartySignal: (text: string) => boolean;
|
||||||
|
hasBankOperationSignal: (text: string) => boolean;
|
||||||
|
hasDocumentSignal: (text: string) => boolean;
|
||||||
|
hasLooseByAnchorMention: (text: string) => boolean;
|
||||||
|
hasHeuristicCounterpartyAnchor: (text: string) => boolean;
|
||||||
|
hasCounterpartyShipmentItemFlowSignal: (text: string) => boolean;
|
||||||
|
hasImplicitCounterpartyAnchorAroundDocs: (text: string) => boolean;
|
||||||
|
hasGenericAddressLookupSignal: (text: string) => boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function resolveCounterpartyAddressIntent(
|
||||||
|
text: string,
|
||||||
|
deps: CounterpartyIntentDeps
|
||||||
|
): AddressIntentResolution | null {
|
||||||
|
if (deps.hasOpenContractsListSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "open_contracts_confirmed_as_of_date",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["open_contract_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasAny(text, deps.openItemsHints) &&
|
||||||
|
!deps.hasCounterpartyDebtLongevitySignal(text) &&
|
||||||
|
!deps.hasInventoryAgingSignal(text) &&
|
||||||
|
!deps.hasInventoryProvenanceSignalV2(text) &&
|
||||||
|
!deps.hasInventoryPurchaseDocumentsSignalV2(text) &&
|
||||||
|
!deps.hasInventorySaleTraceSignalV2(text) &&
|
||||||
|
(
|
||||||
|
/(?:контраг|договор|контракт|counterparty|contract|покупател|клиент|заказчик|customer|client|buyer|supplier|поставщик)/iu.test(
|
||||||
|
text
|
||||||
|
) ||
|
||||||
|
deps.hasAccountNumberAnchor(text) ||
|
||||||
|
deps.hasCompactAccountCodeToken(text)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "open_items_by_counterparty_or_contract",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["open_items_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasPeriodCoverageProfileSignal(text) &&
|
||||||
|
!deps.hasPartyAnchorMention(text) &&
|
||||||
|
!deps.hasContractAnchorSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "period_coverage_profile",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["period_coverage_profile_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasDocumentTypeAndAccountSectionProfileSignal(text) &&
|
||||||
|
!deps.hasPartyAnchorMention(text) &&
|
||||||
|
!deps.hasContractAnchorSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "document_type_and_account_section_profile",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["document_type_and_account_section_profile_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasCounterpartyPopulationAndRolesSignal(text) &&
|
||||||
|
!deps.hasContractAnchorSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "counterparty_population_and_roles",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["counterparty_population_and_roles_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasCounterpartyActivityLifecycleSignal(text) &&
|
||||||
|
!deps.hasContractAnchorSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "counterparty_activity_lifecycle",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["counterparty_activity_lifecycle_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasContractUsageOverviewSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text) &&
|
||||||
|
!deps.hasOpenContractsListSignal(text)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "contract_usage_overview",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["contract_usage_overview_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deps.hasCustomerRevenueAndPaymentsSignal(text) && !deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "customer_revenue_and_payments",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["customer_revenue_and_payments_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deps.hasSupplierPayoutsProfileSignal(text) && !deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "supplier_payouts_profile",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["supplier_payouts_profile_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasContractUsageAndValueSignal(text) &&
|
||||||
|
!deps.hasAccountBalanceSignal(text) &&
|
||||||
|
!deps.hasOpenContractsListSignal(text)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "contract_usage_and_value",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["contract_usage_and_value_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deps.hasContractListByCounterpartySignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "list_contracts_by_counterparty",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["contracts_by_counterparty_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deps.hasContractAnchorSignal(text) && deps.hasBankOperationSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "bank_operations_by_contract",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["bank_ops_by_contract_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasContractAnchorSignal(text) &&
|
||||||
|
(deps.hasAny(text, deps.documentsByContractHints) || deps.hasDocumentSignal(text))
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "list_documents_by_contract",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["documents_by_contract_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasAny(text, deps.bankOperationsByCounterpartyHints) &&
|
||||||
|
(deps.hasPartyAnchorMention(text) || deps.hasLooseByAnchorMention(text) || deps.hasHeuristicCounterpartyAnchor(text))
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "bank_operations_by_counterparty",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["bank_ops_by_counterparty_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(deps.hasAny(text, deps.documentsByCounterpartyHints) || deps.hasCounterpartyShipmentItemFlowSignal(text)) &&
|
||||||
|
(deps.hasPartyAnchorMention(text) ||
|
||||||
|
deps.hasLooseByAnchorMention(text) ||
|
||||||
|
deps.hasImplicitCounterpartyAnchorAroundDocs(text) ||
|
||||||
|
deps.hasHeuristicCounterpartyAnchor(text) ||
|
||||||
|
deps.hasCounterpartyShipmentItemFlowSignal(text))
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "list_documents_by_counterparty",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: [
|
||||||
|
deps.hasCounterpartyShipmentItemFlowSignal(text)
|
||||||
|
? "counterparty_item_flow_signal_detected"
|
||||||
|
: "documents_by_counterparty_signal_detected"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deps.hasAccountBalanceSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "account_balance_snapshot",
|
||||||
|
confidence: "high",
|
||||||
|
reasons: ["account_balance_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deps.hasLooseByAnchorMention(text) && deps.hasGenericAddressLookupSignal(text)) {
|
||||||
|
return {
|
||||||
|
intent: "list_documents_by_counterparty",
|
||||||
|
confidence: "low",
|
||||||
|
reasons: ["generic_lookup_with_loose_anchor_fallback"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
deps.hasAny(text, deps.openContractsHints) &&
|
||||||
|
(text.includes("договор") || text.includes("контракт") || text.includes("contract"))
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
intent: "open_contracts_confirmed_as_of_date",
|
||||||
|
confidence: "medium",
|
||||||
|
reasons: ["open_contract_signal_detected"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { AddressIntentResolution } from "../types/addressQuery";
|
import type { AddressIntentResolution } from "../types/addressQuery";
|
||||||
|
import { resolveCounterpartyAddressIntent } from "./addressCounterpartyIntentSignals";
|
||||||
import { resolveInventoryAddressIntent } from "./addressInventoryIntentSignals";
|
import { resolveInventoryAddressIntent } from "./addressInventoryIntentSignals";
|
||||||
import {
|
import {
|
||||||
hasInventoryProfitabilityCue,
|
hasInventoryProfitabilityCue,
|
||||||
|
|
@ -2068,6 +2069,45 @@ export function resolveAddressIntent(userMessage: string): AddressIntentResoluti
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const counterpartyIntent = resolveCounterpartyAddressIntent(text, {
|
||||||
|
hasAny,
|
||||||
|
openItemsHints: OPEN_ITEMS_HINTS,
|
||||||
|
openContractsHints: OPEN_CONTRACTS_HINTS,
|
||||||
|
documentsByCounterpartyHints: DOCUMENTS_BY_COUNTERPARTY_HINTS,
|
||||||
|
bankOperationsByCounterpartyHints: BANK_OPERATIONS_BY_COUNTERPARTY_HINTS,
|
||||||
|
documentsByContractHints: DOCUMENTS_BY_CONTRACT_HINTS,
|
||||||
|
hasCounterpartyDebtLongevitySignal,
|
||||||
|
hasInventoryAgingSignal,
|
||||||
|
hasInventoryProvenanceSignalV2,
|
||||||
|
hasInventoryPurchaseDocumentsSignalV2,
|
||||||
|
hasInventorySaleTraceSignalV2,
|
||||||
|
hasAccountNumberAnchor,
|
||||||
|
hasCompactAccountCodeToken,
|
||||||
|
hasPeriodCoverageProfileSignal,
|
||||||
|
hasPartyAnchorMention,
|
||||||
|
hasContractAnchorSignal,
|
||||||
|
hasAccountBalanceSignal,
|
||||||
|
hasDocumentTypeAndAccountSectionProfileSignal,
|
||||||
|
hasCounterpartyPopulationAndRolesSignal,
|
||||||
|
hasCounterpartyActivityLifecycleSignal,
|
||||||
|
hasContractUsageOverviewSignal,
|
||||||
|
hasOpenContractsListSignal,
|
||||||
|
hasCustomerRevenueAndPaymentsSignal,
|
||||||
|
hasSupplierPayoutsProfileSignal,
|
||||||
|
hasContractUsageAndValueSignal,
|
||||||
|
hasContractListByCounterpartySignal,
|
||||||
|
hasBankOperationSignal,
|
||||||
|
hasDocumentSignal,
|
||||||
|
hasLooseByAnchorMention,
|
||||||
|
hasHeuristicCounterpartyAnchor,
|
||||||
|
hasCounterpartyShipmentItemFlowSignal,
|
||||||
|
hasImplicitCounterpartyAnchorAroundDocs,
|
||||||
|
hasGenericAddressLookupSignal
|
||||||
|
});
|
||||||
|
if (counterpartyIntent) {
|
||||||
|
return counterpartyIntent;
|
||||||
|
}
|
||||||
|
|
||||||
if (hasOpenContractsListSignal(text)) {
|
if (hasOpenContractsListSignal(text)) {
|
||||||
return {
|
return {
|
||||||
intent: "open_contracts_confirmed_as_of_date",
|
intent: "open_contracts_confirmed_as_of_date",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { resolveCounterpartyAddressIntent } from "../src/services/addressCounterpartyIntentSignals";
|
||||||
|
import { resolveAddressIntent } from "../src/services/addressIntentResolver";
|
||||||
|
|
||||||
|
const defaultDeps = {
|
||||||
|
hasAny: (text: string, hints: readonly string[]) => hints.some((hint) => text.includes(hint)),
|
||||||
|
openItemsHints: ["хвост", "долг", "open items"],
|
||||||
|
openContractsHints: ["договор", "контракт", "open contracts"],
|
||||||
|
documentsByCounterpartyHints: ["документы по", "documents by counterparty"],
|
||||||
|
bankOperationsByCounterpartyHints: ["банк", "выписка", "bank operations by counterparty"],
|
||||||
|
documentsByContractHints: ["документы по договору", "documents by contract"],
|
||||||
|
hasCounterpartyDebtLongevitySignal: () => false,
|
||||||
|
hasInventoryAgingSignal: () => false,
|
||||||
|
hasInventoryProvenanceSignalV2: () => false,
|
||||||
|
hasInventoryPurchaseDocumentsSignalV2: () => false,
|
||||||
|
hasInventorySaleTraceSignalV2: () => false,
|
||||||
|
hasAccountNumberAnchor: (text: string) => /60/.test(text),
|
||||||
|
hasCompactAccountCodeToken: () => false,
|
||||||
|
hasPeriodCoverageProfileSignal: (text: string) => text.includes("year coverage"),
|
||||||
|
hasPartyAnchorMention: (text: string) => text.includes("чепурнов"),
|
||||||
|
hasContractAnchorSignal: (text: string) => text.includes("договор"),
|
||||||
|
hasAccountBalanceSignal: (text: string) => text.includes("balance by account"),
|
||||||
|
hasDocumentTypeAndAccountSectionProfileSignal: (text: string) => text.includes("document profile"),
|
||||||
|
hasCounterpartyPopulationAndRolesSignal: (text: string) => text.includes("population and roles"),
|
||||||
|
hasCounterpartyActivityLifecycleSignal: (text: string) => text.includes("activity lifecycle"),
|
||||||
|
hasContractUsageOverviewSignal: (text: string) => text.includes("usage overview"),
|
||||||
|
hasOpenContractsListSignal: (text: string) => text.includes("open contracts"),
|
||||||
|
hasCustomerRevenueAndPaymentsSignal: (text: string) => text.includes("revenue and payments"),
|
||||||
|
hasSupplierPayoutsProfileSignal: (text: string) => text.includes("supplier payouts"),
|
||||||
|
hasContractUsageAndValueSignal: (text: string) => text.includes("usage and value"),
|
||||||
|
hasContractListByCounterpartySignal: (text: string) => text.includes("contracts by counterparty"),
|
||||||
|
hasBankOperationSignal: (text: string) => text.includes("банк"),
|
||||||
|
hasDocumentSignal: (text: string) => text.includes("документ"),
|
||||||
|
hasLooseByAnchorMention: (text: string) => text.includes("по нему"),
|
||||||
|
hasHeuristicCounterpartyAnchor: (text: string) => text.includes("чепурнов"),
|
||||||
|
hasCounterpartyShipmentItemFlowSignal: (text: string) => text.includes("отгружал"),
|
||||||
|
hasImplicitCounterpartyAnchorAroundDocs: () => false,
|
||||||
|
hasGenericAddressLookupSignal: (text: string) => text.includes("покажи")
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("addressCounterpartyIntentSignals", () => {
|
||||||
|
it("classifies open-items wording through the extracted counterparty owner", () => {
|
||||||
|
const result = resolveCounterpartyAddressIntent("хвосты покажи по счету 60 на август 2022", defaultDeps);
|
||||||
|
|
||||||
|
expect(result?.intent).toBe("open_items_by_counterparty_or_contract");
|
||||||
|
expect(result?.reasons).toContain("open_items_signal_detected");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("classifies counterparty document-flow wording through the extracted counterparty owner", () => {
|
||||||
|
const result = resolveCounterpartyAddressIntent("что нам отгружал чепурнов", defaultDeps);
|
||||||
|
|
||||||
|
expect(result?.intent).toBe("list_documents_by_counterparty");
|
||||||
|
expect(result?.reasons).toContain("counterparty_item_flow_signal_detected");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the main resolver behavior stable through counterparty-owner delegation", () => {
|
||||||
|
const result = resolveAddressIntent("хвосты покажи по счету 60 на август 2022");
|
||||||
|
|
||||||
|
expect(result.intent).toBe("open_items_by_counterparty_or_contract");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not steal inventory wording into the counterparty owner", () => {
|
||||||
|
const result = resolveCounterpartyAddressIntent("show inventory on hand as of 2020-03-15", defaultDeps);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue