From 801851d174bd4528b63a22e5396e92d894fded21 Mon Sep 17 00:00:00 2001 From: Feng Zhang Date: Mon, 11 Aug 2025 14:43:00 -0700 Subject: [PATCH 1/6] Cleanup for v0.3.0 Release --- Cargo.toml | 2 +- pyproject.toml | 2 +- python/docs/conf.py | 4 ++-- python/prollytree/__init__.py | 2 +- src/agent/embedding_search.rs | 2 ++ src/bin/prolly-ui.rs | 2 +- src/diff.rs | 3 +++ src/git/versioned_store.rs | 9 +++++++-- src/lib.rs | 2 +- src/tree.rs | 16 ++++++++++++++-- 10 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c49fb4e..e58c1d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "prollytree" description = "A prolly (probabilistic) tree for efficient storage, retrieval, and modification of ordered data." authors = ["Feng Zhang "] -version = "0.2.1-beta" +version = "0.3.0" edition = "2021" license = "Apache-2.0" diff --git a/pyproject.toml b/pyproject.toml index 0248617..f8a1fb2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "prollytree" -version = "0.2.1" +version = "0.3.0" description = "Python bindings for ProllyTree - a probabilistic tree for efficient storage and retrieval" readme = "python/README.md" requires-python = ">=3.8" diff --git a/python/docs/conf.py b/python/docs/conf.py index 726908d..ce5be81 100644 --- a/python/docs/conf.py +++ b/python/docs/conf.py @@ -26,8 +26,8 @@ author = 'ProllyTree Contributors' # The full version, including alpha/beta/rc tags -release = '0.2.1' -version = '0.2.1' +release = '0.3.0' +version = '0.3.0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/python/prollytree/__init__.py b/python/prollytree/__init__.py index 8847548..1a22b3d 100644 --- a/python/prollytree/__init__.py +++ b/python/prollytree/__init__.py @@ -62,4 +62,4 @@ if git_available: __all__.extend(["WorktreeManager", "WorktreeVersionedKvStore"]) -__version__ = "0.2.1" +__version__ = "0.3.0" diff --git a/src/agent/embedding_search.rs b/src/agent/embedding_search.rs index a3ca944..bbb8ad9 100644 --- a/src/agent/embedding_search.rs +++ b/src/agent/embedding_search.rs @@ -84,6 +84,8 @@ impl MockEmbeddingGenerator { /// Advanced search functionality for memory stores pub struct MemorySearchEngine { store: T, + /// Cache for embeddings to reduce API calls + /// Note: Reserved for future optimization features #[allow(dead_code)] embedding_cache: HashMap>, } diff --git a/src/bin/prolly-ui.rs b/src/bin/prolly-ui.rs index 12d9355..0c09022 100644 --- a/src/bin/prolly-ui.rs +++ b/src/bin/prolly-ui.rs @@ -22,7 +22,7 @@ use std::path::{Path, PathBuf}; #[derive(Parser)] #[command(name = "prolly-ui")] #[command(about = "Generate static HTML visualization for git-prolly repositories")] -#[command(version = "0.1.0")] +#[command(version = "0.3.0")] struct Cli { /// Path to the main git repository containing datasets as subdirectories #[arg(help = "Repository path (defaults to current directory)")] diff --git a/src/diff.rs b/src/diff.rs index 194eb58..358f97b 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -124,6 +124,7 @@ impl AgentPriorityResolver { /// Extract agent ID from key (assumes key format includes agent info) /// This is a simple implementation - in practice you might have more sophisticated key parsing + /// Note: Reserved for future agent-based conflict resolution features #[allow(dead_code)] fn extract_agent_id(&self, key: &[u8]) -> Option { let key_str = String::from_utf8_lossy(key); @@ -136,6 +137,8 @@ impl AgentPriorityResolver { None } + /// Get priority for a given key based on its agent ID + /// Note: Reserved for future agent-based conflict resolution features #[allow(dead_code)] fn get_priority_for_key(&self, key: &[u8]) -> u32 { self.extract_agent_id(key) diff --git a/src/git/versioned_store.rs b/src/git/versioned_store.rs index 2b2019c..20d7263 100644 --- a/src/git/versioned_store.rs +++ b/src/git/versioned_store.rs @@ -250,7 +250,12 @@ where // Create tree object in Git using git commands // Get the git root directory - let git_root = Self::find_git_root(self.git_repo.path().parent().unwrap()).unwrap(); + let parent_path = + self.git_repo.path().parent().ok_or_else(|| { + GitKvError::GitObjectError("Repository path has no parent".into()) + })?; + let git_root = Self::find_git_root(parent_path) + .ok_or_else(|| GitKvError::GitObjectError("Could not find git root".into()))?; // Stage all files in the current directory recursively let add_cmd = std::process::Command::new("git") @@ -476,7 +481,7 @@ where // Get the current time let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) - .unwrap() + .map_err(|e| GitKvError::GitObjectError(format!("System time error: {e}")))? .as_secs() as i64; // Get git user configuration diff --git a/src/lib.rs b/src/lib.rs index a7045ee..20f7f76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ limitations under the License. //! //! ```toml //! [dependencies] -//! prolly = "0.1.0" +//! prollytree = "0.3.0" //! ``` //! //! Follow examples in the github repository to get started. diff --git a/src/tree.rs b/src/tree.rs index 5a085e5..840e0e3 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -607,12 +607,24 @@ impl> Tree for ProllyTree { let is_valid = self.verify(proof.clone(), key, None); // Print the tree structure with proof path highlighted + #[cfg(feature = "tracing")] + tracing::debug!("root:"); + #[cfg(not(feature = "tracing"))] println!("root:"); + self.root.print_tree_with_proof(&self.storage, &proof, key); // Print proof information - println!("\nProof for key {key:?} is valid: {is_valid}"); - println!("Proof: {proof:#?}"); + #[cfg(feature = "tracing")] + { + tracing::debug!("Proof for key {:?} is valid: {}", key, is_valid); + tracing::debug!("Proof: {:#?}", proof); + } + #[cfg(not(feature = "tracing"))] + { + println!("\nProof for key {key:?} is valid: {is_valid}"); + println!("Proof: {proof:#?}"); + } is_valid } From 59b0673567f4a8e61cbe427c27fcc09453189ace Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 11 Aug 2025 15:06:51 -0700 Subject: [PATCH 2/6] fix examples on readme --- README.md | 70 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 191ede5..1a7c768 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,10 @@ Add to your `Cargo.toml`: ```toml [dependencies] -prollytree = "0.2.0" +prollytree = "0.3.0" # Optional features -prollytree = { version = "0.2.0", features = ["git", "sql", "rig"] } +prollytree = { version = "0.3.0", features = ["git", "sql", "rig"] } ``` ## Examples @@ -34,7 +34,7 @@ prollytree = { version = "0.2.0", features = ["git", "sql", "rig"] } ### Basic Tree Operations ```rust -use prollytree::tree::ProllyTree; +use prollytree::tree::{ProllyTree, Tree}; use prollytree::storage::InMemoryNodeStorage; let storage = InMemoryNodeStorage::<32>::new(); @@ -44,56 +44,62 @@ let mut tree = ProllyTree::new(storage, Default::default()); tree.insert(b"user:alice".to_vec(), b"Alice Johnson".to_vec()); tree.insert(b"config:timeout".to_vec(), b"30".to_vec()); -// Query data -let value = tree.find(b"user:alice")?; -println!("Found: {}", String::from_utf8(value)?); +// Query data - find returns a node, extract the value +if let Some(node) = tree.find(b"user:alice") { + for (i, key) in node.keys.iter().enumerate() { + if key == b"user:alice" { + let value = &node.values[i]; + println!("Found: {}", String::from_utf8(value.clone())?); + break; + } + } +} // Generate cryptographic proof -let proof = tree.generate_proof(b"user:alice")?; -let is_valid = tree.verify_proof(&proof, b"user:alice", b"Alice Johnson"); +let proof = tree.generate_proof(b"user:alice"); +let is_valid = tree.verify(proof, b"user:alice", Some(b"Alice Johnson")); ``` ### Git-backed Versioned Storage ```rust use prollytree::git::GitVersionedKvStore; +use prollytree::diff::IgnoreConflictsResolver; -let mut store = GitVersionedKvStore::init("./data")?; +let mut store = GitVersionedKvStore::<32>::init("./data")?; // Version your data like Git -store.set(b"config/api_key", b"secret123")?; +store.insert(b"config/api_key".to_vec(), b"secret123".to_vec())?; store.commit("Initial config")?; -// Branch for experiments -store.checkout_new_branch("feature/optimization")?; -store.set(b"config/timeout", b"60")?; +// Create and switch to a new branch for experiments +store.create_branch("feature/optimization")?; +// Use regular git to switch branches: git checkout feature/optimization +store.insert(b"config/timeout".to_vec(), b"60".to_vec())?; store.commit("Increase timeout")?; -// Three-way merge back to main -store.checkout("main")?; -store.merge("feature/optimization")?; +// Switch back to main and merge +// Use regular git to switch: git checkout main +store.merge("feature/optimization", &IgnoreConflictsResolver)?; ``` -### SQL Interface with Time Travel +### SQL Interface with ProllyTree ```rust use prollytree::sql::ProllyStorage; use gluesql_core::prelude::Glue; -let storage = ProllyStorage::<32>::init("./data")?; +// Create ProllyTree storage backend +let storage = ProllyStorage::<32>::new("./data")?; let mut glue = Glue::new(storage); -// Standard SQL operations +// Execute SQL operations glue.execute("CREATE TABLE users (id INTEGER, name TEXT)").await?; -glue.execute("INSERT INTO users VALUES (1, 'Alice')").await?; +glue.execute("INSERT INTO users VALUES (1, 'Alice Johnson')").await?; -// Time travel queries -glue.storage.commit("v1.0").await?; -glue.execute("UPDATE users SET name = 'Alice Smith' WHERE id = 1").await?; - -// Query historical data -let v1_data = glue.storage.query_at_commit("v1.0", - "SELECT * FROM users WHERE id = 1").await?; +// Query the data +let result = glue.execute("SELECT * FROM users WHERE id = 1").await?; +println!("Query result: {:?}", result); ``` ### AI Agent Memory @@ -126,7 +132,7 @@ let checkpoint = memory.checkpoint("Weather conversation").await?; ```toml [dependencies.prollytree] -version = "0.2.0" +version = "0.3.0" features = [ "git", # Git-backed versioned storage "sql", # SQL interface via GlueSQL @@ -159,11 +165,13 @@ Run benchmarks: `cargo bench` # Install git-prolly CLI cargo install prollytree --features git -# Use like Git for key-value data -git-prolly init my-data +# First setup a git repository and then create a dataset +git init my-repo && cd my-repo +git-prolly init my-data # Creates subdirectory for dataset +cd my-data git-prolly set "user:alice" "Alice Johnson" git-prolly commit -m "Add user" -git-prolly checkout -b feature/updates +git checkout -b feature/updates # Use regular git for branching git-prolly merge main ``` From 9c35e4c76571fd01de57cbb84902f985b2db3e5b Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 11 Aug 2025 15:20:08 -0700 Subject: [PATCH 3/6] re check the reame.examples --- README.md | 106 +++++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 1a7c768..530d2de 100644 --- a/README.md +++ b/README.md @@ -64,68 +64,70 @@ let is_valid = tree.verify(proof, b"user:alice", Some(b"Alice Johnson")); ```rust use prollytree::git::GitVersionedKvStore; -use prollytree::diff::IgnoreConflictsResolver; - -let mut store = GitVersionedKvStore::<32>::init("./data")?; - -// Version your data like Git +use std::process::Command; +use std::fs; + +// Setup: Create a temporary Git repository (in real use, you'd have an existing repo) +let repo_path = "/tmp/demo_git_repo"; +fs::remove_dir_all(repo_path).ok(); // Clean up +fs::create_dir_all(repo_path)?; + +// Initialize Git repository +Command::new("git").args(&["init"]).current_dir(repo_path).output()?; +Command::new("git").args(&["config", "user.name", "Demo"]).current_dir(repo_path).output()?; +Command::new("git").args(&["config", "user.email", "demo@example.com"]).current_dir(repo_path).output()?; + +// Initial git commit +fs::write(format!("{}/README.md", repo_path), "# Demo")?; +Command::new("git").args(&["add", "."]).current_dir(repo_path).output()?; +Command::new("git").args(&["commit", "-m", "Initial"]).current_dir(repo_path).output()?; + +// Switch to repo directory and create dataset +std::env::set_current_dir(repo_path)?; +fs::create_dir_all("data")?; +let mut store = GitVersionedKvStore::<32>::init("data")?; + +// Now use Git-backed versioned storage store.insert(b"config/api_key".to_vec(), b"secret123".to_vec())?; store.commit("Initial config")?; -// Create and switch to a new branch for experiments -store.create_branch("feature/optimization")?; -// Use regular git to switch branches: git checkout feature/optimization -store.insert(b"config/timeout".to_vec(), b"60".to_vec())?; -store.commit("Increase timeout")?; +// Retrieve data +if let Some(value) = store.get(b"config/api_key") { + println!("Retrieved: {}", String::from_utf8(value)?); +} -// Switch back to main and merge -// Use regular git to switch: git checkout main -store.merge("feature/optimization", &IgnoreConflictsResolver)?; +// Add more data and commit +store.insert(b"config/timeout".to_vec(), b"30".to_vec())?; +store.commit("Add timeout config")?; + +// Create branches for parallel development +store.create_branch("experimental")?; +println!("Git-backed storage with full version control!"); ``` -### SQL Interface with ProllyTree +### Multiple Storage Backends ```rust -use prollytree::sql::ProllyStorage; -use gluesql_core::prelude::Glue; - -// Create ProllyTree storage backend -let storage = ProllyStorage::<32>::new("./data")?; -let mut glue = Glue::new(storage); +use prollytree::tree::{ProllyTree, Tree}; +use prollytree::storage::{InMemoryNodeStorage, FileNodeStorage}; -// Execute SQL operations -glue.execute("CREATE TABLE users (id INTEGER, name TEXT)").await?; -glue.execute("INSERT INTO users VALUES (1, 'Alice Johnson')").await?; +// In-memory storage (fast, temporary) +let mem_storage = InMemoryNodeStorage::<32>::new(); +let mut mem_tree = ProllyTree::new(mem_storage, Default::default()); +mem_tree.insert(b"session:abc123".to_vec(), b"active".to_vec()); -// Query the data -let result = glue.execute("SELECT * FROM users WHERE id = 1").await?; -println!("Query result: {:?}", result); -``` +// File-based storage (persistent) +let file_storage = FileNodeStorage::<32>::new("./tree_data".into()); +let mut file_tree = ProllyTree::new(file_storage, Default::default()); +file_tree.insert(b"user:alice".to_vec(), b"Alice Johnson".to_vec()); -### AI Agent Memory +// Both trees support the same operations +if let Some(node) = mem_tree.find(b"session:abc123") { + println!("Session found in memory storage"); +} -```rust -use prollytree::agent::{AgentMemorySystem, MemoryQuery}; - -let mut memory = AgentMemorySystem::init_with_thread_safe_git( - "./agent_memory", "agent_001".to_string(), None -)?; - -// Store conversation context -memory.short_term.store_conversation_turn( - "session_123", "user", "What's the weather today?", None -).await?; - -// Store persistent knowledge -memory.semantic.store_fact( - "weather", "temperature", - json!({"location": "Tokyo", "temp": "22°C"}), - 0.9, "weather_api" -).await?; - -// Query and checkpoint -let memories = memory.semantic.query(MemoryQuery::text("Tokyo")).await?; -let checkpoint = memory.checkpoint("Weather conversation").await?; +// For SQL functionality, see examples/sql.rs +println!("Multiple storage backends working!"); ``` ## Feature Flags @@ -165,9 +167,9 @@ Run benchmarks: `cargo bench` # Install git-prolly CLI cargo install prollytree --features git -# First setup a git repository and then create a dataset +# Setup git repository and create dataset git init my-repo && cd my-repo -git-prolly init my-data # Creates subdirectory for dataset +mkdir my-data && git-prolly init my-data # Create dataset directory cd my-data git-prolly set "user:alice" "Alice Johnson" git-prolly commit -m "Add user" From 1d77d2b7f5a5a797d7ec469e66c79c73c89b876e Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 11 Aug 2025 15:29:49 -0700 Subject: [PATCH 4/6] minor --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 530d2de..8dcfedf 100644 --- a/README.md +++ b/README.md @@ -156,10 +156,10 @@ Run benchmarks: `cargo bench` ## Documentation & Examples -- **[📖 Full API Documentation](https://docs.rs/prollytree)** -- **[💡 Use Cases & Examples](examples/README.md)** - AI agents, version control, distributed systems -- **[🐍 Python Bindings](python/README.md)** - Complete Python API -- **[⚡ Performance Guide](docs/performance.md)** - Optimization tips +- **[Full API Documentation](https://docs.rs/prollytree)** +- **[Use Cases & Examples](examples/README.md)** - AI agents, version control, distributed systems +- **[Python Bindings](python/README.md)** - Complete Python API +- **[Performance Guide](docs/performance.md)** - Optimization tips ## CLI Tool From cb1e2e8ccd2ab7541645f25698116e188fafb5f7 Mon Sep 17 00:00:00 2001 From: zhangfengcdt Date: Mon, 11 Aug 2025 15:39:02 -0700 Subject: [PATCH 5/6] remove unecessary git set --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index 8dcfedf..8a388a4 100644 --- a/README.md +++ b/README.md @@ -74,13 +74,6 @@ fs::create_dir_all(repo_path)?; // Initialize Git repository Command::new("git").args(&["init"]).current_dir(repo_path).output()?; -Command::new("git").args(&["config", "user.name", "Demo"]).current_dir(repo_path).output()?; -Command::new("git").args(&["config", "user.email", "demo@example.com"]).current_dir(repo_path).output()?; - -// Initial git commit -fs::write(format!("{}/README.md", repo_path), "# Demo")?; -Command::new("git").args(&["add", "."]).current_dir(repo_path).output()?; -Command::new("git").args(&["commit", "-m", "Initial"]).current_dir(repo_path).output()?; // Switch to repo directory and create dataset std::env::set_current_dir(repo_path)?; From bed1a52d8dc38766be3793f6642352262ac0a005 Mon Sep 17 00:00:00 2001 From: zhangfengcdt Date: Mon, 11 Aug 2025 15:48:32 -0700 Subject: [PATCH 6/6] more fix --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 8a388a4..369991c 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,7 @@ use std::fs; // Setup: Create a temporary Git repository (in real use, you'd have an existing repo) let repo_path = "/tmp/demo_git_repo"; -fs::remove_dir_all(repo_path).ok(); // Clean up fs::create_dir_all(repo_path)?; - -// Initialize Git repository Command::new("git").args(&["init"]).current_dir(repo_path).output()?; // Switch to repo directory and create dataset