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
1,127 changes: 6 additions & 1,121 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Persisting provides a **unified tiered storage** for AI workloads — agent traj
├─────────────────────────────────────────────────────────────────┤
│ Storage Engine │
│ │
│ Lance (columnar) · Vortex (trajectory) · Numpy (memory) │
│ Lance (columnar) · Lance (trajectory) · Numpy (memory) │
└─────────────────────────────────────────────────────────────────┘
```

Expand All @@ -53,6 +53,7 @@ Record every LLM call your agent makes:

```bash
persisting traj capture -o ./store -c proxy.toml -f md -- claude
# or: -f lance → only events.lance/ dataset; then traj materialize for Markdown
```

Trajectories are stored as `(agent_id, run_id, time)` — the same TTAS model used for KV cache and parameters.
Expand Down Expand Up @@ -109,7 +110,7 @@ arr = kv["s1", 0, 2, 0:512].tensor()

| Capability | Status | Description |
|------------|--------|-------------|
| **Agent Trajectory Capture** | ✅ Stable | Proxy + record LLM traffic as Vortex + Markdown |
| **Agent Trajectory Capture** | ✅ Stable | Proxy + record LLM traffic as Lance + Markdown |
| **Streaming Queue** | ✅ Stable | Lance-backed append/consume, KV API, samplers |
| **Compute Orchestration** | ✅ Stable | `plan()` + `execute()`, local/torchrun |
| **Agent Search** | ✅ Stable | Document indexing, IVF-PQ, hybrid search |
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/conversion/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ impl StreamTranslator {
pub fn streaming_capture_snapshot(&self) -> Option<String> {
let text = match self {
Self::ToMessages(t) => t.accumulated_assistant_text().to_string(),
// Markdown + turn indexing: narrative only; tools stay in Vortex payload.
// Markdown + turn indexing: narrative only; tools stay in Lance payload.
Self::ToResponses(t) => t.narrative_for_capture(),
};
if text.trim().is_empty() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl CompletionsToResponsesStreamTranslator {

/// Visible assistant narrative for live markdown / `assistant_content` (no tool fences).
///
/// Tool calls remain in Vortex via full SSE `body`. Some Codex / reasoning models stream the
/// Tool calls remain in Lance via full SSE `body`. Some Codex / reasoning models stream the
/// user-visible answer in `reasoning_content` while `content` stays empty during tool turns.
pub fn narrative_for_capture(&self) -> String {
if !self.accumulated_text.trim().is_empty() {
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/engine/egress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn rebuild_story_from_records(
TurnMachine::replay_records(story_id, agent_id, run_id, records).snapshot()
}

/// Rebuild story for one session markdown stem from Vortex records.
/// Rebuild story for one session markdown stem from Lance records.
pub fn rebuild_session_story(
session_id: &str,
root_session: &str,
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/engine/prepare.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Prepare phase — pure capture logic before story-local I/O.
//!
//! Runs on the runtime thread; may `ask` the run actor. Vortex/markdown I/O only via [`StoryCommand`].
//! Runs on the runtime thread; may `ask` the run actor. Lance/markdown I/O only via [`StoryCommand`].

use anyhow::Result;
use pulsing_actor::ActorRef;
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/engine/wire/story.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl StoryScope {
/// first reconstructing a `String`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) enum StoryCommand {
/// Append one record to Vortex/sink and sync live markdown.
/// Append one record to Lance/sink and sync live markdown.
PersistRecord {
scope: StoryScope,
/// JSON-encoded [`crate::record::CaptureRecord`].
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/reconcile.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Lightweight post-run reconcile: compare live markdown call_ids vs Vortex dialogue records.
//! Lightweight post-run reconcile: compare live markdown call_ids vs Lance dialogue records.

use std::collections::{BTreeMap, BTreeSet};
use std::path::{Path, PathBuf};
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/session/index.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Session index on disk for `capture list` without full Vortex replay.
//! Session index on disk for `capture list` without full Lance replay.

use std::collections::HashMap;
use std::fs;
Expand Down
18 changes: 9 additions & 9 deletions crates/persisting-capture/src/storage/convert.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Bidirectional conversion between Vortex raw event log and TLV Markdown.
//! Bidirectional conversion between Lance raw event log and TLV Markdown.
//!
//! - **Vortex → Markdown** (`materialize`): full scan, overwrite document.
//! - **Vortex → Markdown** (`stream`): incremental append per batch (capture `-f md`).
//! - **Markdown → Vortex** (`compact`): reconstructs [`CaptureRecord`] rows.
//! - **Lance → Markdown** (`materialize`): full scan, overwrite document.
//! - **Lance → Markdown** (`stream`): incremental append per batch (capture `-f md`).
//! - **Markdown → Lance** (`compact`): reconstructs [`CaptureRecord`] rows.

use std::path::Path;

Expand All @@ -17,23 +17,23 @@ use super::markdown::{
use super::record::{record_to_engine_line, CaptureRecord};
use super::session_client::{resolve_client_meta_for_run_dir, SessionClientMeta};

/// Outcome of Vortex → Markdown materialization.
/// Outcome of Lance → Markdown materialization.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaterializeStats {
pub source_events: usize,
pub markdown_blocks: usize,
pub skipped_events: usize,
}

/// Outcome of streaming Vortex → Markdown (incremental block append).
/// Outcome of streaming Lance → Markdown (incremental block append).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StreamMaterializeStats {
pub events_seen: usize,
pub blocks_appended: usize,
pub skipped_events: usize,
}

/// Outcome of Markdown → Vortex compaction.
/// Outcome of Markdown → Lance compaction.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompactStats {
pub source_blocks: usize,
Expand Down Expand Up @@ -131,7 +131,7 @@ fn enrich_record_from_block(mut rec: CaptureRecord, block: &MarkdownBlock) -> Ca
rec
}

/// Parse a TLV Markdown document into capture records (for Vortex compaction).
/// Parse a TLV Markdown document into capture records (for Lance compaction).
pub fn markdown_document_to_capture_records(doc: &str) -> Result<Vec<CaptureRecord>> {
parse_document(doc)?
.iter()
Expand Down Expand Up @@ -168,7 +168,7 @@ pub fn compact_stats_note(
overwrite: bool,
) -> String {
format!(
"Compacted Markdown→Vortex: {} block(s) → {} row(s) ({}) at {} → {}",
"Compacted Markdown→Lance: {} block(s) → {} row(s) ({}) at {} → {}",
stats.source_blocks,
stats.event_rows,
if overwrite { "overwrite" } else { "append" },
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/storage/dialogue/draft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::skip_markdown_block;
use crate::record::CaptureRecord;
use crate::storage::markdown::BlockHeader;

/// Build a streaming draft assistant block (markdown view only; not written to Vortex).
/// Build a streaming draft assistant block (markdown view only; not written to Lance).
pub fn draft_stream_assistant_block(
rec: &CaptureRecord,
assistant_content: &str,
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/storage/dialogue/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::fields::role_and_body;
use super::skip_markdown_block;
use crate::record::CaptureRecord;

/// Queryable columns extracted from a capture record (shared by Vortex + markdown block headers).
/// Queryable columns extracted from a capture record (shared by Lance + markdown block headers).
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CaptureRecordView {
pub role: Option<String>,
Expand Down
4 changes: 2 additions & 2 deletions crates/persisting-capture/src/storage/event_row.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! Vortex event row ↔ [`CaptureRecord`].
//! Lance event row ↔ [`CaptureRecord`].

use anyhow::{Context, Result};

use super::record::{engine_line_to_record, CaptureRecord};

/// One row in the Vortex event log (canonical trajectory store).
/// One row in the Lance event log (canonical trajectory store).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventRow {
pub seq: i64,
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/storage/lifecycle.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Session lifecycle events for the raw Vortex event log.
//! Session lifecycle events for the raw Lance event log.

use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/storage/markdown_policy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Markdown / Vortex eligibility rules for [`CaptureRecord`] (storage layer only).
//! Markdown / Lance eligibility rules for [`CaptureRecord`] (storage layer only).

use super::dialogue_extract::is_subagent_shape_payload;
use super::record::CaptureRecord;
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/storage/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Trajectory storage: Vortex event log, TLV Markdown, session routing, capture sink.
//! Trajectory storage: Lance event log, TLV Markdown, session routing, capture sink.

pub mod convert;
pub mod dialogue;
Expand Down
26 changes: 13 additions & 13 deletions crates/persisting-capture/src/storage/path_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ fn infer_traj_location_from_path(path_arg: &str) -> Option<ParsedTrajPath> {
fn looks_like_session_dir(dir: &Path) -> bool {
dir.is_dir()
&& (locate_session_markdown(dir).is_some()
|| dir.join("events.vortex").is_file()
|| dir.join("events.lance").is_dir()
|| dir.join("_versions").is_dir())
}

/// Capture run layout: `{storage}/{agent}/{run_id}/events.vortex`.
/// Flat layout: `{storage}/{agent}/{session_id}/events.vortex`.
/// Capture run layout: `{storage}/{agent}/{run_id}/events.lance/` (dataset directory).
/// Flat layout: `{storage}/{agent}/{session_id}/events.lance/`.
fn infer_root_session_id(session_dir: &Path, session_id: &str) -> Option<String> {
let is_run_dir = session_id.starts_with("run-");
if session_dir.join("events.vortex").is_file()
if session_dir.join("events.lance").is_dir()
|| (is_run_dir && session_dir.join("_versions").is_dir())
{
Some(session_id.to_string())
Expand Down Expand Up @@ -222,7 +222,7 @@ pub fn merge_story_location(
}
}

/// When a capture run uses a header session id (UUID) for Vortex/md but the run folder is `run-*`,
/// When a capture run uses a header session id (UUID) for Lance/md but the run folder is `run-*`,
/// remap the story storage key from the primary markdown stem under the run directory.
fn remap_capture_run_story_session_id(
storage: &str,
Expand Down Expand Up @@ -392,7 +392,7 @@ pub fn list_story_read_locations(
let sessions = list_sessions_under_agent(&stor, &agent)?;
if sessions.is_empty() {
anyhow::bail!(
"trajectory stats: no sessions under {stor}/{agent}/ (expected run dirs with events.vortex or markdown)"
"trajectory stats: no sessions under {stor}/{agent}/ (expected run dirs with events.lance or markdown)"
);
}
return Ok(sessions);
Expand All @@ -402,7 +402,7 @@ pub fn list_story_read_locations(
let sessions = list_sessions_under_agent(&storage, &agent)?;
if sessions.is_empty() {
anyhow::bail!(
"trajectory stats: no sessions under {storage}/{agent}/ (expected run dirs with events.vortex or markdown)"
"trajectory stats: no sessions under {storage}/{agent}/ (expected run dirs with events.lance or markdown)"
);
}
return Ok(sessions);
Expand Down Expand Up @@ -546,7 +546,7 @@ mod tests {
let _ = std::fs::remove_dir_all(&base);
let session = base.join("store").join("agent").join("run-1");
std::fs::create_dir_all(&session).unwrap();
std::fs::write(session.join("events.vortex"), b"vortex").unwrap();
std::fs::create_dir_all(session.join("events.lance")).unwrap();
std::fs::write(session.join("run-1.md"), "# test\n").unwrap();

let loc = resolve_traj_read_location(
Expand Down Expand Up @@ -582,7 +582,7 @@ mod tests {
let _ = std::fs::remove_dir_all(&base);
let session = base.join("store").join("agent").join("run-1");
std::fs::create_dir_all(&session).unwrap();
std::fs::write(session.join("events.vortex"), b"vortex").unwrap();
std::fs::create_dir_all(session.join("events.lance")).unwrap();
std::fs::write(session.join("uuid-story.md"), "# test\n").unwrap();

let loc = resolve_traj_read_location(
Expand Down Expand Up @@ -647,7 +647,7 @@ mod tests {
std::fs::write(store.join(".capture/run_session"), "run-20260101-000000").unwrap();
let session = store.join("agent-a").join("run-20260101-000000");
std::fs::create_dir_all(&session).unwrap();
std::fs::write(session.join("events.vortex"), b"v").unwrap();
std::fs::create_dir_all(session.join("events.lance")).unwrap();

let loc = resolve_traj_read_location(
"trajectory stats",
Expand All @@ -673,7 +673,7 @@ mod tests {
for run in ["run-001", "run-002"] {
let session = agent.join(run);
std::fs::create_dir_all(&session).unwrap();
std::fs::write(session.join("events.vortex"), b"v").unwrap();
std::fs::create_dir_all(session.join("events.lance")).unwrap();
}

let locs =
Expand Down Expand Up @@ -705,7 +705,7 @@ mod tests {
.join("deepseek-proxy")
.join("run-with-md");
std::fs::create_dir_all(&run).unwrap();
std::fs::write(run.join("events.vortex"), b"v").unwrap();
std::fs::create_dir_all(run.join("events.lance")).unwrap();
std::fs::write(run.join("header-session-uuid.md"), "# story\n").unwrap();

let resolved = resolve_traj_read_location(
Expand All @@ -732,7 +732,7 @@ mod tests {
let agent = base.join("store").join("deepseek-proxy");
let run = agent.join("run-only");
std::fs::create_dir_all(&run).unwrap();
std::fs::write(run.join("events.vortex"), b"v").unwrap();
std::fs::create_dir_all(run.join("events.lance")).unwrap();

let locs =
list_story_read_locations(agent.to_str().unwrap().into(), None, None, None).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion crates/persisting-capture/src/storage/record.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `CaptureRecord` and Vortex append wire encoding (RON lines between engine components).
//! `CaptureRecord` and Lance append wire encoding (RON lines between engine components).

use anyhow::Context;
use serde::{Deserialize, Serialize};
Expand Down
32 changes: 16 additions & 16 deletions crates/persisting-capture/src/storage/story_coords.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Run / Story coordinates for offline trajectory egress (aligned with [`CaptureRoute`]).
//!
//! - **Run** → `root_session_id` → `{storage}/{agent_id}/{run_id}/`
//! - **Story** → `session_id` → `{session_id}.md` under the run directory; Vortex rows are filtered by session_id
//! - **Story** → `session_id` → `{session_id}.md` under the run directory; Lance rows are filtered by session_id

use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -51,8 +51,8 @@ impl StoryCoords {
)
}

pub fn vortex_event_path(&self) -> Result<PathBuf> {
story_vortex_event_path(
pub fn lance_event_path(&self) -> Result<PathBuf> {
story_lance_event_path(
&self.storage,
&self.agent_id,
&self.session_id,
Expand Down Expand Up @@ -103,41 +103,41 @@ pub fn story_run_dir(
}
}

/// Vortex event log file at `{run}/events.vortex`.
pub fn story_vortex_event_path(
/// Lance event log dataset directory at `{run}/events.lance/`.
pub fn story_lance_event_path(
storage: &str,
agent_id: &str,
session_id: &str,
root_session_id: Option<&str>,
) -> Result<PathBuf> {
let run = story_run_dir(storage, agent_id, session_id, root_session_id)?;
Ok(run.join("events.vortex"))
Ok(run.join("events.lance"))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn nested_sessions_share_run_level_vortex_path() {
let root = story_vortex_event_path("/store", "agent", "run-001", Some("run-001")).unwrap();
let sub = story_vortex_event_path("/store", "agent", "agent-sub", Some("run-001")).unwrap();
fn nested_sessions_share_run_level_lance_path() {
let root = story_lance_event_path("/store", "agent", "run-001", Some("run-001")).unwrap();
let sub = story_lance_event_path("/store", "agent", "agent-sub", Some("run-001")).unwrap();
assert_eq!(root, sub);
assert!(root.ends_with("agent/run-001/events.vortex"));
assert!(root.ends_with("agent/run-001/events.lance"));
}

#[test]
fn flat_session_vortex_path_is_session_scoped() {
let path = story_vortex_event_path("/store", "agent", "sess-flat", None).unwrap();
assert!(path.ends_with("agent/sess-flat/events.vortex"));
fn flat_session_lance_path_is_session_scoped() {
let path = story_lance_event_path("/store", "agent", "sess-flat", None).unwrap();
assert!(path.ends_with("agent/sess-flat/events.lance"));
}

#[test]
fn story_coords_vortex_event_path_matches_helper() {
fn story_coords_lance_event_path_matches_helper() {
let coords = StoryCoords::new("/store", "agent", "run-x", Some("run-x".into()));
assert_eq!(
coords.vortex_event_path().unwrap(),
story_vortex_event_path("/store", "agent", "run-x", Some("run-x")).unwrap()
coords.lance_event_path().unwrap(),
story_lance_event_path("/store", "agent", "run-x", Some("run-x")).unwrap()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ lazy_static! {
Regex::new(r"(?m)(?:^|\n)\s*agentId:\s*([a-zA-Z0-9_-]{4,})").expect("agentId tool result");
static ref TOOL_AGENT_BLOCK: Regex =
Regex::new(r"```tool:(?:Agent|Task)\s*\n([\s\S]*?)```").expect("tool agent block");
/// Design-doc paths in prompts (`*.md` or legacy `*.zh.md` under docs/src/design/).
static ref DOC_TARGET: Regex =
Regex::new(r"docs/src/design/[^\s\)\]]+\.zh\.md").expect("doc target path");
Regex::new(r"docs/src/design/[^\s\)\]]+\.md").expect("doc target path");
static ref TASK_ID_XML: Regex =
Regex::new(r"(?i)<task-id>\s*([a-zA-Z0-9_-]{4,})\s*</task-id>").expect("task-id xml");
}
Expand Down
Loading
Loading