Skip to content

Possible regression: registered edge types retain names despite disallowed endpoint signatures after #1191 #1830

Description

@meetwudi

Question / suspected regression

Should a registered custom relationship type still be checked against edge_type_map before persistence, or is that mapping now intentionally only an extraction/attribute-schema hint?

At commit 9debb86a5523a3d702252090d39751f04a9b830d (0.30.1), a registered MAKES relationship with Product -> Company endpoints survives resolve_extracted_edges even when the only allowed signature is Company -> Product. This is not a request to prohibit newly discovered, unregistered relationship names.

History

I understand avoiding a second LLM classification. Is removing the deterministic signature check also intended? The custom-types documentation still describes endpoint constraints and generic relationships for unmatched pairs. Related broader question: #1789.

Offline reproduction

This invokes the real resolution function with synthetic nodes and an intentionally reversed extraction. Database/search/embedding calls are mocked; no LLM call, credentials, or database are needed. It is a function-level reproduction, not an end-to-end model benchmark.

import asyncio
from datetime import datetime, timezone
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch

from pydantic import BaseModel
from graphiti_core.edges import EntityEdge
from graphiti_core.nodes import EntityNode, EpisodeType, EpisodicNode
from graphiti_core.search.search_config import SearchResults
from graphiti_core.utils.maintenance import edge_operations as ops


class Makes(BaseModel):
    """A Company makes a Product."""


async def main():
    company = EntityNode(name="ExampleCo", group_id="repro", labels=["Company"])
    product = EntityNode(name="ExampleApp", group_id="repro", labels=["Product"])
    episode = EpisodicNode(
        name="example", group_id="repro", source=EpisodeType.text,
        source_description="synthetic reproduction", content="ExampleCo makes ExampleApp.",
        valid_at=datetime.now(timezone.utc),
    )
    edge = EntityEdge(
        source_node_uuid=product.uuid, target_node_uuid=company.uuid,
        name="MAKES", fact="ExampleApp makes ExampleCo.", group_id="repro",
        valid_at=episode.valid_at, created_at=episode.valid_at,
    )
    clients = SimpleNamespace(driver=object(), embedder=object(), llm_client=object())
    with (
        patch.object(ops, "create_entity_edge_embeddings", AsyncMock()),
        patch.object(EntityEdge, "get_between_nodes", AsyncMock(return_value=[])),
        patch.object(ops, "search", AsyncMock(return_value=SearchResults())),
    ):
        resolved, _, _ = await ops.resolve_extracted_edges(
            clients, [edge], episode, [company, product],
            {"MAKES": Makes}, {("Company", "Product"): ["MAKES"]},
        )
    print([(item.name, item.source_node_uuid == product.uuid,
            item.target_node_uuid == company.uuid) for item in resolved])


asyncio.run(main())

Observed (Python 3.10): [('MAKES', True, True)].

Expected under the policy in #950: this edge must not retain the registered MAKES name with disallowed endpoints; the historical behavior was generic RELATES_TO, not dropping the fact or failing ingestion.

If the current behavior is intentional, is there a supported strict-validation hook or option we should use? If it is a regression, would restoring the narrow registered-type check be appropriate?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions