Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- `[profile.ci]` build profile with thin LTO and 16 codegen-units for faster CI release builds (#878)
- `schema` feature flag in `zeph-llm` gating `schemars` dependency and typed output API (#879)

### Performance
- Added composite covering index `(conversation_id, id)` on `messages` table (migration 015); replaces single-column index for filter+order access patterns in `oldest_message_ids` and `load_history_filtered` (#895)
- Replaced double-sort subquery in `load_history_filtered` with a CTE — eliminates redundant `ORDER BY` on the derived table (#896)

### Changed
- Split 3177-line `src/main.rs` into focused modules: `runner.rs` (dispatch), `agent_setup.rs` (tool/MCP/feature setup), `tracing_init.rs`, `tui_bridge.rs`, `channel.rs`, `tests.rs` — `main.rs` reduced to 26 LOC (#839)
- Split 1791-line `crates/zeph-core/src/bootstrap.rs` into submodule directory: `config.rs`, `health.rs`, `mcp.rs`, `provider.rs`, `skills.rs`, `tests.rs` — `bootstrap/mod.rs` reduced to 278 LOC (#840)
Expand Down
2 changes: 1 addition & 1 deletion crates/zeph-memory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Includes a document ingestion subsystem for loading, chunking, and storing user

| Module | Description |
|--------|-------------|
| `sqlite` | SQLite storage for conversations and messages; visibility-aware queries (`load_history_filtered`, `messages_by_ids`, `keyword_search`); durable compaction via `replace_conversation()` |
| `sqlite` | SQLite storage for conversations and messages; visibility-aware queries (`load_history_filtered` via CTE, `messages_by_ids`, `keyword_search`); durable compaction via `replace_conversation()`; composite covering index `(conversation_id, id)` on messages for efficient history reads |
| `sqlite::history` | Input history persistence for CLI channel |
| `sqlite::acp_sessions` | ACP session and event persistence for session resume and lifecycle tracking |
| `qdrant` | Qdrant client for vector upsert and search |
Expand Down
3 changes: 3 additions & 0 deletions crates/zeph-memory/migrations/015_messages_covering_index.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- Covering index for queries that filter by conversation_id and order/limit by id.
-- Replaces the single-column idx_messages_conversation_id for these access patterns.
CREATE INDEX IF NOT EXISTS idx_messages_conversation_id_id ON messages(conversation_id, id);
4 changes: 2 additions & 2 deletions crates/zeph-memory/src/sqlite/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,14 @@ impl SqliteStore {
let uv = user_visible.map(i64::from);

let rows: Vec<(String, String, String, i64, i64)> = sqlx::query_as(
"SELECT role, content, parts, agent_visible, user_visible FROM (\
"WITH recent AS (\
SELECT role, content, parts, agent_visible, user_visible, id FROM messages \
WHERE conversation_id = ? \
AND (? IS NULL OR agent_visible = ?) \
AND (? IS NULL OR user_visible = ?) \
ORDER BY id DESC \
LIMIT ?\
) ORDER BY id ASC",
) SELECT role, content, parts, agent_visible, user_visible FROM recent ORDER BY id ASC",
)
.bind(conversation_id)
.bind(av)
Expand Down
6 changes: 6 additions & 0 deletions docs/src/architecture/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ The TUI applies two optimizations to maintain responsive input during heavy stre
- **Event loop batching**: `biased` `tokio::select!` prioritizes keyboard/mouse input over agent events. Agent events are drained via `try_recv` loop, coalescing multiple streaming chunks into a single frame redraw.
- **Per-message render cache**: Syntax highlighting and markdown parsing results are cached with content-hash keys. Only messages with changed content are re-parsed. Cache invalidation triggers: content mutation, terminal resize, and view mode toggle.

## SQLite Message Index

Migration `015_messages_covering_index.sql` replaces the single-column `conversation_id` index on the `messages` table with a composite covering index on `(conversation_id, id)`. History queries filter by `conversation_id` and order by `id`, so the covering index satisfies both clauses from the index alone, eliminating the post-filter sort step.

The `load_history_filtered` query uses a CTE to express the base filter before applying ordering and limit, replacing the previous double-sort subquery pattern.

## SQLite WAL Mode

SQLite is opened with WAL (Write-Ahead Logging) mode, enabling concurrent reads during writes and improving throughput for the message persistence hot path.
Expand Down
Loading