- Graph reverse indexes (slice 4.0): edges_by_object (O(1) reverse lookups)
and entities_by_type (type-filtered queries) added to lore_engine_poc/tools.py
- responses.py (slice 4.1): shared edge_to_fact / entity_summary / envelope
helpers — single source of truth for tool response shape
- read_tools.py (slice 4.2-4.6): 16 read tools across 5 groups
- Group 1: lookup, entity_context
- Group 2: true_during, entities_present, timeline (state_at deferred to 4.6+)
- Group 3: list_lineage, list_offspring, ancestors_of, descendants_of,
location_hierarchy
- Group 4: event_chain, events_during
- Group 5: lore_about (cite deferred to 4.7+)
- write_tools.py (slice 4.7): 3 minimal world-builder tools
(add_entity, add_relation, add_lore_source) with allowlist + envelope
- scripts/02_demo.py: now exercises every read tool + write/read round-trip
- 92 new tests (7 graph_indexes + 7 responses + 10 group1 + 15 group2 +
19 group3 + 11 group4 + 7 group5 + 16 write_tools). 341/341 green.
Excluded: state_at (composes 4 tools + consistency engine), summarize_chain
+ narrate_arc (LLM-required), cite (vector store), and Group 8 write tools
beyond the 3 minimal — all deferred per slice 4 plan.
89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
"""Lore Engine POC — shared response helpers (slice 4.1).
|
|
|
|
The 16 read tools in slice 4 all return dicts with a small,
|
|
consistent shape. Schema drift across tools is the most common
|
|
tool-bug source (slice 4 plan risk #3), so the helpers below
|
|
centralise the contract:
|
|
|
|
* :func:`edge_to_fact` — flat dict for one fact. Every tool
|
|
that returns a single Edge (or a list of Edges) builds its
|
|
response through this helper, so ``valid_from``/``valid_until``,
|
|
``sources``, and ``confidence`` are always in the same place.
|
|
* :func:`entity_summary` — compact ``{name, type}`` for tools
|
|
that surface many entities (``lookup``, ``events_during``,
|
|
``entities_present``).
|
|
* :func:`envelope` — ``{"ok": ..., "data": ...}`` (or
|
|
``{"ok": False, "error": str}``) for tools that can fail
|
|
(the slice-4.7 write tools).
|
|
|
|
The slice-0 ``was_true_at`` tool predates this module and keeps
|
|
its inline response shape for backward compat. New read tools
|
|
should call these helpers instead.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Optional
|
|
|
|
from .tools import Edge
|
|
|
|
|
|
def edge_to_fact(edge: Edge) -> dict:
|
|
"""Convert an :class:`Edge` to the canonical fact dict.
|
|
|
|
The shape is the union of ``was_true_at``'s inline dict
|
|
and ``explain_violation``'s fact-shaped fields::
|
|
|
|
{
|
|
"subject": str,
|
|
"relation": str,
|
|
"object": str,
|
|
"valid_from": str | None,
|
|
"valid_until": str | None,
|
|
"sources": list[str],
|
|
"confidence": float,
|
|
}
|
|
|
|
``confidence`` is the edge's :pyattr:`Edge.aggregate_confidence`
|
|
— the worst-case per-source product of extraction and source
|
|
confidence across all sources. Read tools that want the
|
|
*best*-case confidence should consult the edge's
|
|
``extraction_confidences`` / ``source_confidences`` arrays
|
|
directly, but most callers want the floor.
|
|
"""
|
|
return {
|
|
"subject": edge.subject,
|
|
"relation": edge.relation,
|
|
"object": edge.object,
|
|
"valid_from": edge.valid_from,
|
|
"valid_until": edge.valid_until,
|
|
"sources": list(edge.sources),
|
|
"confidence": edge.aggregate_confidence,
|
|
}
|
|
|
|
|
|
def entity_summary(name: str, type_: Optional[str] = None) -> dict:
|
|
"""Compact ``{name, type}`` triple used by lookup-style tools.
|
|
|
|
``type_`` is optional because the in-memory graph doesn't
|
|
always carry type metadata (markdown-only entities without
|
|
structured-YAML types, for example). When ``None``, the
|
|
summary just carries the name.
|
|
"""
|
|
return {"name": name, "type": type_ or ""}
|
|
|
|
|
|
def envelope(ok: bool, data: Any = None, error: Optional[str] = None) -> dict:
|
|
"""Wrap a tool result in the standard ``{ok, data}`` envelope.
|
|
|
|
Used by the slice-4.7 write tools (which can fail) and by any
|
|
read tool that wants to surface an error rather than raising.
|
|
Successful reads don't need the envelope — they return plain
|
|
dicts / lists.
|
|
"""
|
|
if ok:
|
|
return {"ok": True, "data": data}
|
|
return {"ok": False, "error": error or "unknown error"}
|
|
|
|
|
|
__all__ = ["edge_to_fact", "entity_summary", "envelope"] |