Skip to content

Improve trajectory storage and fix schema handling - #30

Merged
vinodmut merged 1 commit into
AgentToolkit:mainfrom
vinodmut:demo
Jan 22, 2026
Merged

Improve trajectory storage and fix schema handling#30
vinodmut merged 1 commit into
AgentToolkit:mainfrom
vinodmut:demo

Conversation

@vinodmut

@vinodmut vinodmut commented Jan 20, 2026

Copy link
Copy Markdown
Contributor
  • Add architecture diagram to README
  • Store trajectory as single entity per span/trace ID
  • Support list and dict content types (not just strings)
  • Fix Milvus backend to handle non-string content
  • Fix SQLite schema for new content format

For #28

Summary by CodeRabbit

  • Documentation

    • Added an Architecture section to the README with a visual diagram.
  • Improvements

    • Search now works with structured and non-text content (lists/dicts) for more comprehensive results.
    • Content handling and storage enhanced to preserve richer payloads and increase capacity.
    • Message storage consolidated into single trajectory entities for more coherent session records.
  • Tests

    • Updated tests to reflect tips as structured objects.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR enables non-string entity content by serializing/deserializable payloads in Milvus and filesystem search, expands schema types to allow str|list|dict, consolidates per-message Phoenix sync into a single trajectory entity, and adds an Architecture section to the README.

Changes

Cohort / File(s) Summary
Schema Extensions
kaizen/schema/core.py, kaizen/schema/conflict_resolution.py
content type expanded from str to str | list | dict for Entity, SimpleEntity, and EntityUpdate.
Milvus Backend
kaizen/backend/milvus.py
Added serialize_content() / deserialize_content(); store/search serialized content; increased content VARCHAR max_length 512→65535; deserialize on read.
Filesystem Search
kaizen/backend/filesystem.py
Search normalizes non-string entity content via json.dumps() before case-insensitive substring match.
Phoenix Sync
kaizen/sync/phoenix_sync.py
Replaced per-message entities with a single trajectory entity containing messages list and trajectory-level metadata; update_entities called with one trajectory entity.
Tests
tests/unit/test_phoenix_sync.py
Tests updated to use mock Tip objects (with content, category, rationale, trigger) instead of plain strings.
DB / Config / Docs
kaizen/db/sqlite_manager.py, pyproject.toml, README.md
Parameterized LIMIT in search_namespaces; added arize-phoenix>=12.30.0 dependency; added ## Architecture section referencing docs/assets/architecture.png.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Phoenix as PhoenixSync
    participant Serializer as Serializer
    participant Milvus as MilvusBackend
    participant DB as SQLite/Storage

    Client->>Phoenix: send messages / trajectory
    Phoenix->>Serializer: prepare trajectory entity (messages list, metadata)
    Serializer->>Milvus: serialize content -> compute embeddings -> upsert
    Milvus->>DB: persist metadata/index (storage layer)
    Client->>Milvus: search query
    Milvus->>Serializer: serialize query / match against serialized content
    Milvus->>DB: retrieve matching entity ids/records
    DB->>Serializer: return stored serialized content
    Serializer->>Client: deserialize and return original content shapes
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • visahak

Poem

🐰
I hop through bytes and tidy trails,
Lists and dicts now tell their tales.
I serialize, then store with care,
One trajectory — consolidated there.
Tip-tap, I nibble bugs away! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main objectives: improving trajectory storage (consolidating to single entity) and fixing schema handling (supporting non-string content types).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@kaizen/backend/milvus.py`:
- Around line 130-137: The int(update.id) cast in the milvus.upsert call can
raise ValueError for non-numeric IDs; wrap the conversion in defensive
validation inside the function that calls milvus.upsert (the code block invoking
self.milvus.upsert) by attempting to convert update.id with a try/except (or
using str.isdigit/explicit validation) and on failure log the invalid ID and
skip or map it to a safe fallback id rather than letting the exception
propagate; ensure the logged message includes update.id, namespace_id, and the
event type so you can trace which record was rejected and keep the upsert
payload shape (keys like 'id', 'type', 'content', 'embedding', 'metadata')
unchanged when a valid id is present.
🧹 Nitpick comments (2)
kaizen/backend/milvus.py (2)

113-114: Verify semantic search quality with serialized structured content.

When entity.content is a dict/list, the serialized JSON string (e.g., '{"key": "value"}') is used for embedding-based search. This may reduce search quality since JSON syntax tokens don't carry semantic meaning.

Consider whether a dedicated text representation (e.g., extracting key text fields) would improve search relevance for structured content.


198-198: Schema change requires migration for existing collections.

Increasing max_length from 512 to 65535 only affects newly created collections. Existing Milvus collections will retain the old schema and may reject content exceeding 512 characters.

Consider adding a migration strategy or documenting the upgrade path for existing deployments.

Comment thread kaizen/backend/milvus.py
Comment thread kaizen/backend/milvus.py
Comment on lines 130 to 137
self.milvus.upsert(collection_name=namespace_id, data={
'type': entity_type,
'id': update.id,
'content': update.content,
'id': int(update.id),
'content': content_str,
'created_at': int(now.timestamp()),
'embedding': self.embedding_model.encode(update.content),
'embedding': self.embedding_model.encode(content_str),
'metadata': update.metadata
}, partial_update=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential ValueError if update.id is non-numeric.

The int(update.id) conversion on line 132 will raise ValueError if the ID is not a valid integer string. While the conflict resolution logic should only return UPDATE events for existing entities with numeric IDs, consider adding defensive error handling.

Suggested defensive handling
                     case 'UPDATE':
+                        try:
+                            entity_id = int(update.id)
+                        except ValueError:
+                            logger.error(f"Invalid entity ID for UPDATE: {update.id}")
+                            continue
                         self.milvus.upsert(collection_name=namespace_id, data={
                             'type': entity_type,
-                            'id': int(update.id),
+                            'id': entity_id,
                             'content': content_str,
🤖 Prompt for AI Agents
In `@kaizen/backend/milvus.py` around lines 130 - 137, The int(update.id) cast in
the milvus.upsert call can raise ValueError for non-numeric IDs; wrap the
conversion in defensive validation inside the function that calls milvus.upsert
(the code block invoking self.milvus.upsert) by attempting to convert update.id
with a try/except (or using str.isdigit/explicit validation) and on failure log
the invalid ID and skip or map it to a safe fallback id rather than letting the
exception propagate; ensure the logged message includes update.id, namespace_id,
and the event type so you can trace which record was rejected and keep the
upsert payload shape (keys like 'id', 'type', 'content', 'embedding',
'metadata') unchanged when a valid id is present.

- Add architecture diagram to README
- Store trajectory as single entity per span/trace ID
- Support list and dict content types (not just strings)
- Fix Milvus backend to handle non-string content
- Fix SQLite schema for new content format

@illeatmyhat illeatmyhat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems ok, as long as we can handle updating incomplete trajectories
i wonder if it would be practical to let people define a callback for the embedding string
as it is, making an embedding of an entire trajectory is probably meaningless for search purposes.
we would likely prefer it to be the user’s first utterance or some summary of the whole conversation, and need some mechanism to allow people to do this.

@vinodmut

Copy link
Copy Markdown
Contributor Author

@illeatmyhat I agree we should index by the utterance or sub-task. Will work on it next.

@vinodmut
vinodmut merged commit 6d486cd into AgentToolkit:main Jan 22, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants