using Foundgine.Core.Semantic.Resolution; using Foundgine.SupplyChain.Advanced.Semantics; using Xunit; namespace Foundgine.SupplyChain.Advanced.Tests.Grounding; /// /// Case study for the README / walkthrough headline example — "show me overdue /// purchase orders from our top supplier in Texas" — run against the real /// generated Supply Chain semantic contract, the same contract /// find_top_supplier_overdue_orders resolves against in /// Semantic/Api/Mcp/Program.cs. /// /// Unlike and /// , which hand-write fake /// candidates, this suite proves the alias declarations actually live in /// Semantic/Domain/Domain.cs: /// - [FoundgineEntity("Supplier", ...)] [FoundgineAlias("Vendor")] /// [FoundgineAlias("Seller")] /// - [FoundgineEntity("PurchaseOrder", ...)] [FoundgineAlias(["PO", "POs"])] /// [FoundgineAlias(["Buy", "Buys"])] /// and that they survive AOT metadata generation into /// output, and from there into /// committing "seller"/"buys" to /// the exact same interpretation as "supplier"/"PurchaseOrder" — a paraphrase /// like "show me overdue buys from our top seller in Texas" is not a /// coincidentally-similar second meaning, it is the declared alias path to /// the one meaning "purchase orders"/"supplier" already commit to. /// public sealed class SupplyChainGroundingAliasTests { [Fact] public void Domain_alias_attributes_are_projected_into_the_real_generated_lexicon() { var contract = SupplyChainSemanticModel.Build().Freeze().CreateSnapshot(); var lexicon = SemanticLexiconProjection.Build(contract); var supplierEntry = Assert.Single(lexicon, x => x.Kind == SemanticLexicalCandidateKind.Entity && x.CanonicalName == "Supplier"); Assert.Contains("Vendor", supplierEntry.EffectiveAliases); Assert.Contains("Seller", supplierEntry.EffectiveAliases); var purchaseOrderEntry = Assert.Single(lexicon, x => x.Kind == SemanticLexicalCandidateKind.Entity && x.CanonicalName == "PurchaseOrder"); Assert.Contains("PO", purchaseOrderEntry.EffectiveAliases); Assert.Contains("Buys", purchaseOrderEntry.EffectiveAliases); } [Fact] public void Retrieval_representations_for_an_entity_are_collapsed_before_resolution() { var contract = SupplyChainSemanticModel.Build().Freeze().CreateSnapshot(); var resolver = new SemanticLexicalResolver( contract, new AliasAwareLexicalSource(SemanticLexiconProjection.Build(contract))); // The projection intentionally contains both an Entity and a Node // document for each entity. They are retrieval representations of the // same semantic identity and must not become competing interpretations. var sellerCandidates = resolver.GetCandidates("seller")["seller"]; var buysCandidates = resolver.GetCandidates("buys")["buys"]; Assert.Single(sellerCandidates); Assert.Equal(SemanticLexicalCandidateKind.Entity, sellerCandidates[0].Kind); Assert.Equal(SupplyChainSemanticModel.Supplier, sellerCandidates[0].EntityId); Assert.Single(buysCandidates); Assert.Equal(SemanticLexicalCandidateKind.Entity, buysCandidates[0].Kind); Assert.Equal(SupplyChainSemanticModel.PurchaseOrder, buysCandidates[0].EntityId); } [Fact] public void Exact_canonical_entity_names_win_over_same_named_relationship_roots() { var contract = SupplyChainSemanticModel.Build().Freeze().CreateSnapshot(); var resolver = new SemanticLexicalResolver( contract, new AliasAwareLexicalSource(SemanticLexiconProjection.Build(contract))); var supplier = resolver.Ground("Supplier"); var purchaseOrder = resolver.Ground("PurchaseOrder"); Assert.Equal(GroundingOutcome.Committed, supplier.Outcome); Assert.Equal(SupplyChainSemanticModel.Supplier, supplier.Committed!.RootEntity); Assert.Equal("Supplier", supplier.Committed.Steps.Single().Candidate.CanonicalName); Assert.Equal(GroundingOutcome.Committed, purchaseOrder.Outcome); Assert.Equal(SupplyChainSemanticModel.PurchaseOrder, purchaseOrder.Committed!.RootEntity); Assert.Equal("PurchaseOrder", purchaseOrder.Committed.Steps.Single().Candidate.CanonicalName); } [Fact] public void Seller_grounds_to_the_same_interpretation_as_supplier() { var contract = SupplyChainSemanticModel.Build().Freeze().CreateSnapshot(); var resolver = new SemanticLexicalResolver( contract, new AliasAwareLexicalSource(SemanticLexiconProjection.Build(contract))); var canonical = resolver.Ground("Supplier"); var alias = resolver.Ground("seller"); Assert.Equal(GroundingOutcome.Committed, canonical.Outcome); Assert.Equal(GroundingOutcome.Committed, alias.Outcome); Assert.Equal(SupplyChainSemanticModel.Supplier, canonical.Committed!.RootEntity); Assert.Equal(SupplyChainSemanticModel.Supplier, alias.Committed!.RootEntity); Assert.Equal(canonical.Committed.Signature, alias.Committed.Signature); } [Fact] public void Readme_paraphrase_grounds_the_same_semantic_identities_as_the_canonical_request() { var contract = SupplyChainSemanticModel.Build().Freeze().CreateSnapshot(); var resolver = new SemanticLexicalResolver( contract, new AliasAwareLexicalSource(SemanticLexiconProjection.Build(contract))); // README canonical request: // "show me overdue purchase orders from our top supplier in Texas" // README paraphrase: // "show me the overdue buys from our top seller in Texas" // // Grounding owns the vocabulary-to-meaning step. The other words are // handled by the operation graph / retrieval stages, so this test // deliberately proves the two content-bearing aliases against their // canonical semantic identities. var canonicalPurchaseOrder = resolver.Ground("purchase order"); var aliasPurchaseOrder = resolver.Ground("buys"); var canonicalSupplier = resolver.Ground("supplier"); var aliasSupplier = resolver.Ground("seller"); Assert.Equal(GroundingOutcome.Committed, canonicalPurchaseOrder.Outcome); Assert.Equal(GroundingOutcome.Committed, aliasPurchaseOrder.Outcome); Assert.Equal(GroundingOutcome.Committed, canonicalSupplier.Outcome); Assert.Equal(GroundingOutcome.Committed, aliasSupplier.Outcome); Assert.Equal(SupplyChainSemanticModel.PurchaseOrder, canonicalPurchaseOrder.Committed!.RootEntity); Assert.Equal(SupplyChainSemanticModel.PurchaseOrder, aliasPurchaseOrder.Committed!.RootEntity); Assert.Equal(canonicalPurchaseOrder.Committed.Signature, aliasPurchaseOrder.Committed.Signature); Assert.Equal(SupplyChainSemanticModel.Supplier, canonicalSupplier.Committed!.RootEntity); Assert.Equal(SupplyChainSemanticModel.Supplier, aliasSupplier.Committed!.RootEntity); Assert.Equal(canonicalSupplier.Committed.Signature, aliasSupplier.Committed.Signature); } [Fact] public void Buys_grounds_to_the_same_interpretation_as_purchase_order() { var contract = SupplyChainSemanticModel.Build().Freeze().CreateSnapshot(); var resolver = new SemanticLexicalResolver( contract, new AliasAwareLexicalSource(SemanticLexiconProjection.Build(contract))); var canonical = resolver.Ground("PurchaseOrder"); var alias = resolver.Ground("buys"); Assert.Equal(GroundingOutcome.Committed, canonical.Outcome); Assert.Equal(GroundingOutcome.Committed, alias.Outcome); Assert.Equal(SupplyChainSemanticModel.PurchaseOrder, canonical.Committed!.RootEntity); Assert.Equal(SupplyChainSemanticModel.PurchaseOrder, alias.Committed!.RootEntity); Assert.Equal(canonical.Committed.Signature, alias.Committed.Signature); } /// /// Stand-in retrieval provider that matches a token against either an /// entry's canonical name or any of its declared aliases — the same /// lookup an Elasticsearch/pgvector index built from /// output performs. Not a set of /// hand-picked fake candidates: it reads only what /// actually derived from /// the real contract, so "seller"/"buys" only resolve here because /// Domain.cs declared them. /// private sealed class AliasAwareLexicalSource(IReadOnlyList lexicon) : ISemanticLexicalCandidateSource { public IReadOnlyList Retrieve(SemanticLexicalRequest request) => lexicon .Where(entry => request.EffectiveKinds.Contains(entry.Kind)) .Where(entry => string.Equals(entry.CanonicalName, request.Token, StringComparison.OrdinalIgnoreCase) || entry.EffectiveAliases.Any(a => string.Equals(a, request.Token, StringComparison.OrdinalIgnoreCase))) .Select(entry => new SemanticLexicalCandidate( request.Token, entry.Kind, entry.CanonicalName, Score: .95, EntityId: entry.EntityId, RelationshipId: entry.RelationshipId, FieldId: entry.FieldId, SourceEntityId: entry.SourceEntityId, TargetEntityId: entry.TargetEntityId, Value: entry.Value)) .ToArray(); } }