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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "prollytree"
description = "A prolly (probabilistic) tree for efficient storage, retrieval, and modification of ordered data."
authors = ["Feng Zhang <f.feng.zhang@gmail.com>"]
version = "0.2.1-beta"
version = "0.3.0"
edition = "2021"

license = "Apache-2.0"
Expand Down
138 changes: 69 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ 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

### Basic Tree Operations

```rust
use prollytree::tree::ProllyTree;
use prollytree::tree::{ProllyTree, Tree};
use prollytree::storage::InMemoryNodeStorage;

let storage = InMemoryNodeStorage::<32>::new();
Expand All @@ -44,89 +44,87 @@ 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 std::process::Command;
use std::fs;

let mut store = GitVersionedKvStore::init("./data")?;
// Setup: Create a temporary Git repository (in real use, you'd have an existing repo)
let repo_path = "/tmp/demo_git_repo";
fs::create_dir_all(repo_path)?;
Command::new("git").args(&["init"]).current_dir(repo_path).output()?;

// Version your data like Git
store.set(b"config/api_key", b"secret123")?;
store.commit("Initial config")?;

// Branch for experiments
store.checkout_new_branch("feature/optimization")?;
store.set(b"config/timeout", b"60")?;
store.commit("Increase timeout")?;

// Three-way merge back to main
store.checkout("main")?;
store.merge("feature/optimization")?;
```
// 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")?;

### SQL Interface with Time Travel

```rust
use prollytree::sql::ProllyStorage;
use gluesql_core::prelude::Glue;

let storage = ProllyStorage::<32>::init("./data")?;
let mut glue = Glue::new(storage);
// Now use Git-backed versioned storage
store.insert(b"config/api_key".to_vec(), b"secret123".to_vec())?;
store.commit("Initial config")?;

// Standard SQL operations
glue.execute("CREATE TABLE users (id INTEGER, name TEXT)").await?;
glue.execute("INSERT INTO users VALUES (1, 'Alice')").await?;
// Retrieve data
if let Some(value) = store.get(b"config/api_key") {
println!("Retrieved: {}", String::from_utf8(value)?);
}

// Time travel queries
glue.storage.commit("v1.0").await?;
glue.execute("UPDATE users SET name = 'Alice Smith' WHERE id = 1").await?;
// Add more data and commit
store.insert(b"config/timeout".to_vec(), b"30".to_vec())?;
store.commit("Add timeout config")?;

// Query historical data
let v1_data = glue.storage.query_at_commit("v1.0",
"SELECT * FROM users WHERE id = 1").await?;
// Create branches for parallel development
store.create_branch("experimental")?;
println!("Git-backed storage with full version control!");
```

### AI Agent Memory
### Multiple Storage Backends

```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?;
use prollytree::tree::{ProllyTree, Tree};
use prollytree::storage::{InMemoryNodeStorage, FileNodeStorage};

// 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());

// 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());

// Both trees support the same operations
if let Some(node) = mem_tree.find(b"session:abc123") {
println!("Session found in memory storage");
}

// For SQL functionality, see examples/sql.rs
println!("Multiple storage backends working!");
```

## Feature Flags

```toml
[dependencies.prollytree]
version = "0.2.0"
version = "0.3.0"
features = [
"git", # Git-backed versioned storage
"sql", # SQL interface via GlueSQL
Expand All @@ -148,22 +146,24 @@ 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

```bash
# Install git-prolly CLI
cargo install prollytree --features git

# Use like Git for key-value data
git-prolly init my-data
# Setup git repository and create dataset
git init my-repo && cd my-repo
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"
git-prolly checkout -b feature/updates
git checkout -b feature/updates # Use regular git for branching
git-prolly merge main
```

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions python/docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion python/prollytree/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@
if git_available:
__all__.extend(["WorktreeManager", "WorktreeVersionedKvStore"])

__version__ = "0.2.1"
__version__ = "0.3.0"
2 changes: 2 additions & 0 deletions src/agent/embedding_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ impl MockEmbeddingGenerator {
/// Advanced search functionality for memory stores
pub struct MemorySearchEngine<T: SearchableMemoryStore> {
store: T,
/// Cache for embeddings to reduce API calls
/// Note: Reserved for future optimization features
#[allow(dead_code)]
embedding_cache: HashMap<String, Vec<f32>>,
}
Expand Down
2 changes: 1 addition & 1 deletion src/bin/prolly-ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)")]
Expand Down
3 changes: 3 additions & 0 deletions src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
let key_str = String::from_utf8_lossy(key);
Expand All @@ -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)
Expand Down
9 changes: 7 additions & 2 deletions src/git/versioned_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 14 additions & 2 deletions src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,12 +607,24 @@ impl<const N: usize, S: NodeStorage<N>> Tree<N, S> for ProllyTree<N, S> {
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
}
Expand Down