Skip to content

symfonic.core.learning.entity_extractor

entity_extractor

Moved to :mod:symfonic.capabilities.memory.phases.entities.

The extractor protocol and the entity value objects are the seam phase 12.5 consumes, so they live with the phase in the capability and are imported back here for the shipped extractors and their tests to keep using. The concrete spaCy and LLM adapters stay in this package: they are integrations, and nothing on a roster imports them.

EntityCandidate dataclass

EntityCandidate(surface_form: str, kind: EntityKind, confidence: float, start: int | None = None, end: int | None = None)

A single extracted entity mention.

Attributes:

Name Type Description
surface_form str

Verbatim text as observed in the episodic.

kind EntityKind

One of the five canonical entity kinds.

confidence float

Extractor-reported probability in [0.0, 1.0].

start int | None

Optional character offset of the mention in the source text.

end int | None

Optional character offset (exclusive) of the mention.

EntityExtractor

Bases: Protocol

Async pure-function protocol: text -> list[EntityCandidate].

Implementations MUST be side-effect free with respect to the framework state and SHOULD be deterministic so the consolidation phase honours its idempotency contract (design §9). The LLM extractor is the documented exception (design §9.3).

canonicalise_surface

canonicalise_surface(surface: str, kind: EntityKind, *, lemmatise: bool = False) -> str

Decision 2 canonical-form helper.

Proper-noun buckets (person / place / event) collapse via case-insensitive exact match: the canonical form is the surface lowercased with internal whitespace normalised to single underscores ("Cafe Milano" -> "cafe_milano").

Common-noun buckets (object / other) use the same whitespace normalisation plus lemma+lowercase when lemmatise=True (regex strategy: rule-based; spaCy strategy: real lemmatiser). When lemmatise=False the bucket falls back to lowercase-only.

Returns the empty string when surface is blank.

Source code in src/symfonic/capabilities/memory/phases/entities.py
def canonicalise_surface(
    surface: str,
    kind: EntityKind,
    *,
    lemmatise: bool = False,
) -> str:
    """Decision 2 canonical-form helper.

    Proper-noun buckets (``person`` / ``place`` / ``event``) collapse via
    case-insensitive exact match: the canonical form is the surface
    lowercased with internal whitespace normalised to single underscores
    ("Cafe Milano" -> "cafe_milano").

    Common-noun buckets (``object`` / ``other``) use the same whitespace
    normalisation plus lemma+lowercase when ``lemmatise=True`` (regex
    strategy: rule-based; spaCy strategy: real lemmatiser). When
    ``lemmatise=False`` the bucket falls back to lowercase-only.

    Returns the empty string when ``surface`` is blank.
    """
    if not surface or not surface.strip():
        return ""
    # Normalise internal whitespace so "Cafe   Milano" and "Cafe Milano"
    # collapse to the same canonical token. Underscore mirrors the
    # existing seed-label convention (``cafe_milano``).
    cleaned = "_".join(surface.split())
    if kind in ("person", "place", "event"):
        return cleaned.lower()
    # Common-noun bucket
    if lemmatise:
        return _lemmatise_common_noun(cleaned)
    return cleaned.lower()

strip_phase11_markers

strip_phase11_markers(text: str) -> str

Remove [semantic:...] and [SOUL: ...] markers from text.

These markers are Phase 11's territory; the entity_links phase must not double-count them as entity surfaces (design §13.6).

Source code in src/symfonic/capabilities/memory/phases/entities.py
def strip_phase11_markers(text: str) -> str:
    """Remove ``[semantic:...]`` and ``[SOUL: ...]`` markers from text.

    These markers are Phase 11's territory; the entity_links phase
    must not double-count them as entity surfaces (design §13.6).
    """
    if not text:
        return ""
    return _PHASE11_MARKER_RE.sub("", text)