Metadata-Version: 2.4
Name: generic-llm-memorizer
Version: 0.2.0
Summary: A library for managing LLM conversations and context
Author-email: Your Name <your.email@example.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/yourusername/generic-llm-memorizer
Project-URL: Documentation, https://github.com/yourusername/generic-llm-memorizer#readme
Project-URL: Repository, https://github.com/yourusername/generic-llm-memorizer
Keywords: llm,conversation,chat,ai,prompt,context-management
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: dotenv>=0.9.9
Requires-Dist: pydantic>=2.12.5
Requires-Dist: scikit-learn>=1.3.0
Provides-Extra: test
Requires-Dist: pydantic-ai>=1.76.0; extra == "test"
Requires-Dist: pytest>=9.0.2; extra == "test"
Requires-Dist: pytest-asyncio>=1.3.0; extra == "test"
Requires-Dist: google-adk; extra == "test"
Requires-Dist: ollama; extra == "test"
Requires-Dist: litellm; extra == "test"
Provides-Extra: rag
Requires-Dist: sentence-transformers>=3.0.0; extra == "rag"
Dynamic: license-file

# Generic LLM Memorizer

A library for AI agent long-term memory. Provides two tools — `memorize` and `search` — that any agent (pydantic-ai, Google ADK, etc.) can use to store and retrieve conversational knowledge.

## Features

- **Fact Extraction** — Automatically extracts relevant facts from conversations (preferences, skills, context, events, demographics)
- **User Profile** — Builds and maintains a user profile (name, age, hobbies, occupation)
- **Conversation Summary** — Generates concise summaries of each conversation
- **SKOS Knowledge Base** — Stores ontological concepts following the SKOS standard
- **Semantic Search** — Hybrid search combining:
  - **Dense vector search** (cosine similarity on embeddings) — requires optional `rag` install
  - **Keyword search** (TF-IDF) — built-in
  - **LLM reranking** — re-ranks top results by semantic relevance
- **Persistence** — JSON files, one directory per user (pluggable `StorageBackend`)

## Installation

```bash
uv sync
# Optional: enable RAG with local sentence-transformers
uv sync --group rag
```

## Quick Start

```python
import asyncio
from pydantic import BaseModel
from generic_llm_memorizer import Memorizer

# Your LLM call function (OpenAI, Ollama, Anthropic, etc.)
async def my_llm_call(prompt: str, system_prompt: str, response_model: type[BaseModel]):
    # Call your LLM API and return an instance of response_model
    ...

async def main():
    mem = Memorizer(user_id="user_123", llm_call=my_llm_call)

    # Tool 1: memorize a conversation
    result = await mem.memorize(
        conversation=[
            "User: Bonjour, je m'appelle Marie et je suis développeuse Python",
            "Assistant: Enchantée Marie !",
        ],
        session_id="session_1",
    )
    print(f"Facts added: {result.facts_added}")
    print(f"Profile updated: {result.profile_updated}")
    print(f"Summary: {result.summary}")

    # Tool 2: search stored memory
    search_result = await mem.search("What does the user do?")
    print(search_result.to_context_string())

asyncio.run(main())
```

### With RAG (Dense Vector Search)

```bash
pip install generic-llm-memorizer[rag]
```

```python
from generic_llm_memorizer import Memorizer
from generic_llm_memorizer.embeddings import SentenceTransformerEmbeddings

mem = Memorizer(
    user_id="user_123",
    llm_call=my_llm_call,
    embedder=SentenceTransformerEmbeddings(),
)
# memorize() will now also build a vector index
# search() will use hybrid vector + keyword matching
```

### With a custom embedding API

```python
from generic_llm_memorizer.embeddings import OpenAIEmbeddings

async def my_embed_fn(texts: list[str]) -> list[list[float]]:
    # call your embedding API
    ...

mem = Memorizer(
    user_id="user_123",
    llm_call=my_llm_call,
    embedder=OpenAIEmbeddings(embed_fn=my_embed_fn),
)
```

## API

### `Memorizer`

```python
Memorizer(
    user_id: str,
    llm_call: LLMCall,
    store: StorageBackend | None = None,       # defaults to FileStorageBackend
    system_prompt: str | None = None,
    batch_size: int = 10,
    max_batches: int = 3,
    embedder: EmbeddingProvider | None = None,  # enables RAG vector search
)
```

#### `memorize(conversation, session_id) -> MemorizeResult`

Extract facts, profile, summary, and SKOS concepts from a conversation.

- `conversation: list[str]` — Messages in `"role: content"` format
- `session_id: str` — Session identifier for persistence
- Returns `MemorizeResult(facts_added, profile_updated, summary, skos_concepts_added)`

#### `search(query, scope=ALL, top_k=10, use_llm_rerank=True) -> SearchResult`

Search stored memory. Uses hybrid vector + keyword + optional LLM rerank.

- `query: str` — Natural language query
- `scope: SearchScope` — `FACTS`, `SKOS`, `PROFILE`, or `ALL`
- `top_k: int` — Max results to return
- `use_llm_rerank: bool` — Whether to rerank with the LLM
- Returns `SearchResult(facts, skos_concepts, user_profile)` with `to_context_string()`

### Storage

```python
from generic_llm_memorizer import FileStorageBackend

store = FileStorageBackend(base_path="./my_memories")
mem = Memorizer(user_id="user_123", llm_call=my_llm_call, store=store)
```

Persisted to `./my_memories/{user_id}/`:
- `memory.json` — User profile and facts
- `skos_knowledge_db.json` — SKOS ontology
- `{session_id}/conversation.json` — Conversation history

### RAG (Optional)

| Provider | Install | Class |
|---|---|---|
| Sentence Transformers (local) | `pip install generic-llm-memorizer[rag]` | `SentenceTransformerEmbeddings` |
| OpenAI / any API | (included) | `OpenAIEmbeddings` |

## Architecture

```
Memorizer
├── memorize()
│   ├── _extract_facts()      → LLM → Facts
│   ├── _extract_user_profile() → LLM → User
│   ├── _summarize()          → LLM → Summary
│   ├── _build_skos()         → LLM → SkosKnowledgeDatabase
│   └── persist + rebuild indices
│
├── search()
│   ├── VectorIndex.search()  → cosine similarity on embeddings (RAG)
│   ├── MemoryIndex.search()  → TF-IDF keyword matching
│   ├── _merge_search_results() → dedup + merge
│   └── LLM rerank (optional)
│
├── FileStorageBackend        → JSON files per user
└── EmbeddingProvider (RAG)   → SentenceTransformer / OpenAI / custom
```

## Development

```bash
# Run all unit tests
pytest

# Run with coverage
pytest --cov=generic_llm_memorizer

# Run integration tests (requires Ollama with gemma4:26b)
pytest tests/test_with_agent.py -v
```

## Dependencies

- Python 3.12+
- pydantic
- scikit-learn
- sentence-transformers (optional, for RAG)
