breathe-memory/breathe/interfaces.py
mvyshhnyvetska 4e671a0a83 Initial release: breathe-memory v0.1.0
Context optimization and associative memory for LLM applications.
Two-phase system: SYNAPSE (pre-generation memory injection) +
GraphCompactor (structured context compression).

- Interface-based, storage-agnostic, LLM-agnostic
- Memory Nexus: PostgreSQL + pgvector reference backend
- Zero mandatory dependencies beyond stdlib
- 28 tests passing, clean install verified

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 13:50:00 +01:00

147 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Interfaces — abstract contracts for external dependencies.
BREATHE is storage-agnostic and LLM-agnostic by design.
Implement these interfaces to integrate with any backend.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Optional
@dataclass
class RetrievedNode:
"""A node retrieved from graph traversal or memory search."""
node_id: str
concept: str
node_type: str # entity, theme, insight, event, memory_vector, memory_keyword
summary: Optional[str] = None
importance: float = 0.5
depth: int = 0 # BFS depth from anchor
relation: Optional[str] = None # edge relation that led here
memory_content: Optional[str] = None # raw text from memory store
class MemoryRepository(ABC):
"""
Abstract storage backend for BREATHE.
Implement this to connect BREATHE to your own database.
See ``breathe.backends.postgres`` for the asyncpg/PostgreSQL reference
implementation.
The interface deliberately stays minimal — you only need what SYNAPSE
actually calls. Graph BFS and keyword search are optional; implement the
ones you need and raise ``NotImplementedError`` for the rest.
"""
@abstractmethod
async def get_concepts(self) -> dict[str, str]:
"""
Return all active known concepts as ``{concept_text: node_uuid}``.
Called once at initialization to build the concept regex.
An empty dict is valid — SYNAPSE falls back to regex-only extraction.
"""
@abstractmethod
async def graph_bfs(
self,
start_ids: list[str],
max_depth: int = 2,
min_strength: float = 0.2,
limit: int = 20,
) -> list[RetrievedNode]:
"""
BFS traversal from ``start_ids`` through the concept graph.
Args:
start_ids: UUIDs of matched memory_nodes.
max_depth: Maximum edge hops to follow.
min_strength: Minimum edge strength to follow (01).
limit: Maximum nodes to return.
Returns:
List of RetrievedNode, sorted by importance descending.
"""
@abstractmethod
async def keyword_search(
self, keywords: list[str], limit: int = 5
) -> list[RetrievedNode]:
"""
Full-text keyword search over memory content.
Called for anchors that didn't match any known concept (no node_id).
ILIKE or equivalent is fine — precision matters less than recall here.
Args:
keywords: Words to search for (case-insensitive).
limit: Maximum memories to return.
"""
async def flush_edges(self, edges: list) -> int:
"""
Persist new session graph edges to long-term storage.
Called at session end by SessionGraph.flush(). Optional — if you don't
need cross-session graph persistence, leave this as a no-op.
Returns number of edges flushed.
"""
return 0
class VectorSearchClient(ABC):
"""
Abstract client for semantic / vector search.
Wraps any dense-embedding search backend (pgvector, Pinecone, Weaviate, etc.).
BREATHE uses this for Strategy 2 in SYNAPSE traversal — the highest-quality
but most expensive retrieval path.
"""
@abstractmethod
async def search(self, query: str, limit: int = 5) -> list[RetrievedNode]:
"""
Return the most semantically similar memories for ``query``.
Args:
query: Short anchor phrase (NOT the full user message).
limit: Max results.
Returns:
List of RetrievedNode sorted by similarity descending.
Set ``importance`` to the similarity score (01).
"""
class LLMClient(ABC):
"""
Abstract LLM client for GraphCompactor.
GraphCompactor needs a single call: given a long prompt, return text.
Implement this to use any LLM (Anthropic, OpenAI, local, etc.).
"""
@abstractmethod
async def complete(
self,
prompt: str,
max_tokens: int = 4000,
temperature: float = 0.2,
) -> Optional[str]:
"""
Generate a completion for ``prompt``.
Args:
prompt: The full extraction prompt (can be long).
max_tokens: Max tokens to generate.
temperature: Low values (0.10.3) work best for structured extraction.
Returns:
Generated text, or None on failure.
"""