feat(api): add taosmd.ingest()/search() to back the agent-rules contract - #60
Conversation
The per-turn rules block in `taosmd/docs/agent-rules.md` told agents to
call `taosmd.ingest(transcript, agent="x")` and
`taosmd.search(query, agent="x")`, but neither function existed at the
module level — agents that followed the contract verbatim hit
AttributeError, including the README's verify-install step
(`taosmd.search('hello', agent='<name>')`). The compact rules-block API
also drifted from the long-form AGENTS.md guide which used the
underlying `archive.record()` / `vmem.search()` calls directly.
Adds `taosmd/api.py` with two zero-config entry points that match the
contract:
- `ingest(transcript, *, agent, data_dir=None)` — shelves verbatim text
into the zero-loss archive AND embeds it into vector memory so a
later `search()` can reach it. Accepts a string, a `{role, content,
timestamp}` dict, or any iterable of either.
- `search(query, *, agent, limit=5, data_dir=None)` — calls retrieve()
across {vector, kg, archive} and reshapes the hits into the rules-
block contract: `{text, source, timestamp, confidence, metadata}`.
Confidence is taken from the underlying source score (cosine for
vector, KG confidence, etc) so the documented `< 0.6` threshold
reads in the right range.
Both auto-discover `~/.taosmd` (override via `TAOSMD_DATA_DIR` or
explicit `data_dir=`), honour the `config.json` written by
`python -m taosmd.auto_setup`, and find the MiniLM ONNX model under
`<data_dir>/models/minilm-onnx`, `$TAOSMD_DIR/models/minilm-onnx`
(set by `scripts/setup.sh`), or `$TAOSMD_ONNX_PATH`. Stores are
lazy-initialised on first call and cached per data_dir, so concurrent
calls don't double-init.
Re-exports both from `taosmd/__init__.py`.
13 new tests cover: top-level export presence (regression for the
agent-rules contract), ingest of str/dict/iterable, empty-content
skipping, agent-required validation, search hit shape, empty query,
data_dir resolution (explicit / env / default), config.json honoured,
and the format_hit confidence-source preference. 123 tests pass.
📝 WalkthroughWalkthroughThe PR introduces a public API layer with async Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant ingest as ingest()
participant Cache as Store Cache
participant Config as Config
participant Archive as ArchiveStore
participant Vector as VectorMemory
participant KG as TemporalKnowledgeGraph
Client->>ingest: ingest(transcript, agent, data_dir)
ingest->>ingest: Resolve data directory
ingest->>Config: Load config.json
Config-->>ingest: embed_mode, model path
ingest->>Cache: Check if stores cached
alt Stores not cached
ingest->>Archive: Initialize
ingest->>Vector: Initialize (with embed mode)
ingest->>KG: Initialize
ingest->>Cache: Cache stores
end
ingest->>ingest: Normalize transcript
ingest->>Archive: Record turns
ingest->>Vector: Add text with metadata
ingest->>ingest: Update agent stats
ingest-->>Client: Return {archived, agent, data_dir}
sequenceDiagram
participant Client
participant search as search()
participant Cache as Store Cache
participant Vector as VectorMemory
participant KG as TemporalKnowledgeGraph
participant Archive as ArchiveStore
participant Formatter as Hit Formatter
Client->>search: search(query, agent, limit, data_dir)
search->>search: Resolve data directory
search->>Cache: Get cached stores
alt Stores not cached
search->>Vector: Initialize
search->>KG: Initialize
search->>Archive: Initialize
search->>Cache: Cache stores
end
par Parallel retrieval
search->>Vector: Query vector memory
search->>KG: Query knowledge graph
search->>Archive: Query archive
end
search->>Formatter: Format raw hits
Formatter->>Formatter: Extract confidence & timestamp
Formatter-->>search: Formatted hits
search-->>Client: Return list of {text, source, timestamp, confidence, metadata}
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Review rate limit: 9/10 reviews remaining, refill in 6 minutes. Comment |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (3 files)
Reviewed by grok-code-fast-1:optimized:free · 99,107 tokens |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/test_api.py (1)
40-47: 💤 Low valueConsider logging cleanup exceptions for debugging.
Static analysis flags the bare
except Exception: passpattern. While acceptable in test cleanup (you want tests to complete regardless), logging at debug level would aid troubleshooting if cleanup silently fails.♻️ Optional improvement
if store and hasattr(store, "close"): try: asyncio.run(store.close()) - except Exception: - pass + except Exception as exc: + # Don't fail teardown, but log for debugging + import logging + logging.getLogger(__name__).debug("cleanup error: %s", exc)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_api.py` around lines 40 - 47, The cleanup loop in tests/test_api.py silently swallows exceptions (bare except Exception: pass) when calling store.close on entries from taosmd_api._stores_cache; change it to capture the exception object and log it at debug level instead of passing silently—import or use the test module logger (e.g., logging.getLogger(__name__)) and call logger.debug with a message that includes the store identity and the exception (and optionally use logger.exception or exc_info=True) when catching exceptions from store.close to aid debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/test_api.py`:
- Around line 40-47: The cleanup loop in tests/test_api.py silently swallows
exceptions (bare except Exception: pass) when calling store.close on entries
from taosmd_api._stores_cache; change it to capture the exception object and log
it at debug level instead of passing silently—import or use the test module
logger (e.g., logging.getLogger(__name__)) and call logger.debug with a message
that includes the store identity and the exception (and optionally use
logger.exception or exc_info=True) when catching exceptions from store.close to
aid debugging.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 7767886d-738b-4d1a-96ee-390eeafc4cad
📒 Files selected for processing (3)
taosmd/__init__.pytaosmd/api.pytests/test_api.py
Summary
The per-turn rules block in `taosmd/docs/agent-rules.md` (which agents copy verbatim into their CLAUDE.md / AGENTS.md / system prompt at install time) told agents to call:
```python
await taosmd.ingest(transcript, agent="")
hits = await taosmd.search(query, agent="")
```
Neither function existed at the module level. An agent that followed the contract verbatim hit `AttributeError`, including the README's verify-install step (`taosmd.search('hello', agent='')`). The compact rules-block API also drifted from the long-form `AGENTS.md` integration guide which uses the underlying `archive.record()` / `vmem.search()` calls directly — agents reading both got two different stories.
This PR backs the contract with real implementations. No doc churn — the rules block, AGENTS.md, and the README install ritual all stay as-is.
What changes
What does NOT change
Test plan
Summary by CodeRabbit
Release Notes
ingest()function to archive and index transcripts by agentsearch()function to query stored data with confidence scores and metadata