warm-rust 0.6.5

Local-first search engine and agent memory: fast approximate nearest-neighbor and BM25 full-text search, encrypted stores, provable deletion, built on vector-symbolic architectures.
Documentation
# Pydantic AI

`writerslogic_warm_integrations.pydantic_ai` provides a memory toolset and a persistent message-history store.

Tested against `pydantic-ai-slim==2.58.0`, Python 3.12.

## Install

```sh
pip install "writerslogic-warm-integrations[pydantic-ai]"
```

Needs a running `warm-server` whose `--input-dim` matches the embedder (default embedder: 384).

## Toolset

```python
from pydantic_ai import Agent
from writerslogic_warm_integrations.core import WarmConfig
from writerslogic_warm_integrations.pydantic_ai import memory_toolset

config = WarmConfig(url="http://127.0.0.1:8080", tenant_id="acme")
agent = Agent("openai:gpt-5", toolsets=[memory_toolset(config, user_id="alice")])
```

`memory_toolset` returns a `FunctionToolset` with `remember(text, tags?)`, `recall(query, k?)` and `forget(memory_id)`. Schemas are generated by Pydantic AI from the function signatures. `user_id` is stored as metadata, restricts `recall` (service-side equality filter) and makes `forget` refuse other users' ids. `sync=True` builds blocking tools on `WarmBackend` (Pydantic AI runs them in a worker thread); the default is async on `AsyncWarmBackend`. Extra keyword arguments (`max_retries`, `timeout`, `requires_approval`, ...) go to `FunctionToolset`; `toolset_id` defaults to `"writerslogic-warm"`.

## Message history

```python
from writerslogic_warm_integrations.pydantic_ai import MessageStore  # AsyncMessageStore for async

store = MessageStore(config)
result = agent.run_sync("hi", message_history=store.load("chat-1"))
store.save("chat-1", result.all_messages())
```

Each `ModelMessage` is stored as one JSON item through `ModelMessagesTypeAdapter`, so every part type round-trips (tested with text, tool calls and returns, and a 30 000-character tool return). `load(id, limit=None)`, `append`, `save`, `replace`, `delete`.

- `save` appends only the tail beyond what is stored, assuming `messages` extends the stored history. If `messages` is shorter (a history processor compacted it) the conversation is rewritten. If a processor rewrote messages in place without changing the count, call `replace`.
- Storage is a `core.paged` log: unbounded message count, page size `min(50, max_fetch)`, an item up to `chunk_chars * max_parts` characters (default 8000 x 100), no concurrent writers to one conversation id across processes. See `openai-agents.md` for the layout.
- `Conversation` objects (usage, conversation id, deferred requests) are not stored; only messages, as with `message_history=`.

## Knobs

All on `WarmConfig`: `url`, `api_key`, `tenant_id`, `collection`, `embedder`, `text_queries`, `zero_trust_key`, `max_fetch`, `overfetch`. BM25 and title weight are native-engine options and not available over the HTTP service.

## Filters

No filter argument is exposed; `user_id` (tools) and conversation id (history) are top-level equality filters evaluated by the service.

## Conformance

Verified through `Agent.run` with `FunctionModel` (scripted tool calls, schema inspection through `AgentInfo.function_tools`) and `TestModel` (which calls every tool with generated arguments), against a real `warm-server`: `pytest tests/pydantic_ai`. Boundaries: 49/50/51/120 messages across pages.

## Limitations

Retrieval is by similarity over the configured embedder; the default `HashingEmbedder` is lexical. Supply an `embedder` for semantic recall.