diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6743cc..0f6b969 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,17 +8,24 @@ on: - main jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - uses: pre-commit/action@v3.0.1 + test: + needs: pre-commit strategy: matrix: feature_flags: ["no-default-features", "all-features"] runs-on: ubuntu-latest steps: - - - uses: actions/checkout@v2 - - name: fmt - run: cargo fmt --all -- --check + - uses: actions/checkout@v4 - name: build run: cargo build --all --verbose diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..7b31bfa --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,40 @@ +name: Pre-commit + +on: + pull_request: + types: [opened, ready_for_review, synchronize] + push: + branches: + - main + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history for better pre-commit performance + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Cache pre-commit + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} + + - name: Install pre-commit + run: | + python -m pip install --upgrade pip + pip install pre-commit + + - name: Run pre-commit on all files + run: pre-commit run --all-files + + - name: Run pre-commit on changed files (for PRs) + if: github.event_name == 'pull_request' + run: pre-commit run --from-ref origin/${{ github.base_ref }} --to-ref HEAD diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e31ed39 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +repos: + - repo: local + hooks: + - id: license-check + name: License Header Check + entry: scripts/check-license.sh + language: script + files: \.(rs|py)$ + pass_filenames: true + stages: [pre-commit] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-toml + - id: check-merge-conflict + - id: check-added-large-files + + - repo: local + hooks: + - id: cargo-fmt + name: Cargo format + entry: cargo fmt --all -- --check + language: rust + files: \.rs$ + pass_filenames: false diff --git a/README.md b/README.md index 238bf54..451ac8f 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ [![License](https://img.shields.io/crates/l/prollytree.svg)](https://github.com/yourusername/prollytree/blob/main/LICENSE) [![Downloads](https://img.shields.io/crates/d/prollytree.svg)](https://crates.io/crates/prollytree) -A Prolly Tree is a hybrid data structure that combines the features of B-trees and Merkle trees to provide -both efficient data access and verifiable integrity. It is specifically designed to handle the requirements -of distributed systems and large-scale databases, making indexes syncable and distributable over +A Prolly Tree is a hybrid data structure that combines the features of B-trees and Merkle trees to provide +both efficient data access and verifiable integrity. It is specifically designed to handle the requirements +of distributed systems and large-scale databases, making indexes syncable and distributable over peer-to-peer (P2P) networks. ## Key Features @@ -111,23 +111,23 @@ use prollytree::git::GitVersionedKvStore; fn main() -> Result<(), Box> { // Initialize git-backed store let mut store = GitVersionedKvStore::init("./my-data")?; - + // Set values (automatically stages changes) store.set(b"config/api_key", b"secret123")?; store.set(b"config/timeout", b"30")?; - + // Commit changes store.commit("Update API configuration")?; - + // Create a branch for experiments store.checkout_new_branch("feature/new-settings")?; store.set(b"config/timeout", b"60")?; store.commit("Increase timeout")?; - + // Switch back and see the difference store.checkout("main")?; let timeout = store.get(b"config/timeout")?; // Returns b"30" - + Ok(()) } ``` @@ -143,23 +143,23 @@ async fn main() -> Result<(), Box> { // Initialize SQL-capable storage let storage = ProllyStorage::<32>::init("./data")?; let mut glue = Glue::new(storage); - + // Create table and insert data glue.execute("CREATE TABLE users (id INTEGER, name TEXT, age INTEGER)").await?; glue.execute("INSERT INTO users VALUES (1, 'Alice', 30)").await?; glue.execute("INSERT INTO users VALUES (2, 'Bob', 25)").await?; - + // Query with SQL let result = glue.execute("SELECT * FROM users WHERE age > 26").await?; // Returns: [(1, 'Alice', 30)] - + // Time travel query (requires commit) glue.storage.commit("Initial user data").await?; glue.execute("UPDATE users SET age = 31 WHERE id = 1").await?; - + // Query previous version let old_data = glue.storage.query_at_commit("HEAD~1", "SELECT * FROM users").await?; - + Ok(()) } ``` @@ -176,19 +176,19 @@ async fn main() -> Result<(), Box> { let mut memory = AgentMemorySystem::init_with_thread_safe_git( "./agent_memory", "assistant_001".to_string(), None )?; - + // Store conversation in short-term memory memory.short_term.store_conversation_turn( "session_123", "user", "What's the weather in Tokyo?", None ).await?; - + // Store facts in semantic memory memory.semantic.store_fact( "location", "tokyo", json!({"timezone": "JST", "temp": "22°C"}), 0.9, "weather_api" ).await?; - + // Query memories let query = MemoryQuery { namespace: None, @@ -201,11 +201,11 @@ async fn main() -> Result<(), Box> { include_expired: false, }; let results = memory.semantic.query(query).await?; - + // Create checkpoint let commit_id = memory.checkpoint("Weather session").await?; println!("Stored {} memories, checkpoint: {}", results.len(), commit_id); - + Ok(()) } ``` @@ -219,19 +219,19 @@ use prollytree::storage::InMemoryNodeStorage; fn main() { let storage = InMemoryNodeStorage::<32>::new(); let mut tree = ProllyTree::new(storage, Default::default()); - + // Insert sensitive data tree.insert(b"balance:alice".to_vec(), b"1000".to_vec()); tree.insert(b"balance:bob".to_vec(), b"500".to_vec()); - + // Generate cryptographic proof let proof = tree.generate_proof(b"balance:alice").unwrap(); let root_hash = tree.root_hash(); - + // Verify proof (can be done by third party) let is_valid = tree.verify_proof(&proof, b"balance:alice", b"1000"); assert!(is_valid); - + // Root hash changes if any data changes tree.update(b"balance:alice".to_vec(), b"1100".to_vec()); let new_root = tree.root_hash(); @@ -249,4 +249,4 @@ Contributions are welcome! Please submit a pull request or open an issue to disc ## License -This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details. \ No newline at end of file +This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details. diff --git a/benches/sql.rs b/benches/sql.rs index 62b3b8d..baed5a9 100644 --- a/benches/sql.rs +++ b/benches/sql.rs @@ -43,7 +43,7 @@ async fn setup_database(record_count: usize) -> (Glue>, TempDi // Insert test data for i in 0..record_count { let insert_sql = format!( - "INSERT INTO users (id, name, email, age, city, created_at) + "INSERT INTO users (id, name, email, age, city, created_at) VALUES ({}, 'User{}', 'user{}@example.com', {}, 'City{}', TIMESTAMP '2024-01-{:02} 12:00:00')", i, i, i, 20 + (i % 50), i % 10, (i % 28) + 1 ); @@ -147,7 +147,7 @@ fn bench_sql_join(c: &mut Criterion) { // Insert orders for i in 0..size * 2 { let sql = format!( - "INSERT INTO orders (id, user_id, amount, status) + "INSERT INTO orders (id, user_id, amount, status) VALUES ({}, {}, {}, '{}')", i, i % size, @@ -198,7 +198,7 @@ fn bench_sql_aggregation(c: &mut Criterion) { runtime.block_on(async { let result = glue .execute( - "SELECT city, + "SELECT city, COUNT(*) as user_count, AVG(age) as avg_age, MIN(age) as min_age, @@ -237,8 +237,8 @@ fn bench_sql_update(c: &mut Criterion) { // Update multiple records let result = glue .execute( - "UPDATE users - SET age = age + 1, + "UPDATE users + SET age = age + 1, city = 'UpdatedCity' WHERE age < 30", ) @@ -368,16 +368,16 @@ fn bench_sql_complex_query(c: &mut Criterion) { // Complex query with subqueries let result = glue .execute( - "SELECT + "SELECT u.city, COUNT(DISTINCT u.id) as user_count, - (SELECT COUNT(*) - FROM users u2 + (SELECT COUNT(*) + FROM users u2 WHERE u2.city = u.city AND u2.age > 40) as senior_count, AVG(u.age) as avg_age FROM users u WHERE u.id IN ( - SELECT id FROM users + SELECT id FROM users WHERE age BETWEEN 25 AND 45 ) GROUP BY u.city diff --git a/docs/git.md b/docs/git.md index d6348fe..e9bee70 100644 --- a/docs/git.md +++ b/docs/git.md @@ -267,11 +267,11 @@ git-prolly diff main feature/preferences git-prolly diff main feature/preferences --format=detailed # Output: Detailed Key-Value Changes (main -> feature/preferences): # ═══════════════════════════════════════ -# +# # Key: pref:123:notifications # Status: Added # Value: "enabled" -# +# # Key: user:123 # Status: Modified # Old Value: "John Doe" @@ -307,7 +307,7 @@ git-prolly show HEAD # Output: Commit: f1e2d3c4 - Add user preferences # Author: Developer # Date: 2024-01-15 10:30:00 -# +# # Key-Value Changes: # + pref:123:notifications = "enabled" # ~ user:123 = "John Doe" -> "John A. Doe" @@ -350,7 +350,7 @@ git-prolly history user:123 --format=detailed # Date: 2024-01-15 10:30:00 UTC # Author: Developer # Message: Update user profile -# +# # Commit: a1b2c3d4e5f6789012345678901234567890abcd # Date: 2024-01-15 09:15:00 UTC # Author: Developer @@ -686,4 +686,4 @@ For issues, questions, or contributions: ## License -Licensed under the Apache License, Version 2.0. \ No newline at end of file +Licensed under the Apache License, Version 2.0. diff --git a/docs/sql.md b/docs/sql.md index 19f9180..4d13656 100644 --- a/docs/sql.md +++ b/docs/sql.md @@ -88,7 +88,7 @@ CREATE TABLE products ( category TEXT ); -INSERT INTO products VALUES +INSERT INTO products VALUES (1, 'Laptop', 1200, 'Electronics'), (2, 'Book', 25, 'Education'); EOF @@ -191,16 +191,16 @@ CREATE TABLE products ( ```sql -- Single row insert -INSERT INTO users (id, name, email) +INSERT INTO users (id, name, email) VALUES (1, 'Alice Johnson', 'alice@example.com'); -- Multiple row insert -INSERT INTO users (id, name, email) VALUES +INSERT INTO users (id, name, email) VALUES (2, 'Bob Smith', 'bob@example.com'), (3, 'Charlie Brown', 'charlie@example.com'); -- Insert without specifying columns (must match table structure) -INSERT INTO products VALUES +INSERT INTO products VALUES (1, 'Laptop', 1200, true, 'High-performance laptop'); ``` @@ -443,7 +443,7 @@ ORDER BY revenue DESC LIMIT 10; -- Customer purchase history -SELECT c.name, COUNT(DISTINCT s.id) as purchase_count, +SELECT c.name, COUNT(DISTINCT s.id) as purchase_count, SUM(s.quantity * s.price) as total_spent FROM customers c JOIN sales s ON c.id = s.customer_id @@ -471,7 +471,7 @@ INSERT INTO users_new (id, name, email) SELECT id, name, email FROM users; -- Update new fields -UPDATE users_new SET +UPDATE users_new SET created_at = '2024-01-01', updated_at = '2024-01-01', status = 'active'; @@ -489,7 +489,7 @@ git prolly sql -f migrate_v2.sql ```bash # Generate daily report git prolly sql -o json " -SELECT +SELECT DATE(order_date) as date, COUNT(*) as orders, SUM(quantity * price) as revenue @@ -713,4 +713,4 @@ The `git prolly sql` command brings the power of SQL to ProllyTree's versioned s - Track data history over time - Export data in multiple formats -For more examples and advanced usage, see the `examples/sql_example.rs` file in the repository. \ No newline at end of file +For more examples and advanced usage, see the `examples/sql_example.rs` file in the repository. diff --git a/docs/storage.md b/docs/storage.md index ffbdb07..493a0bc 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -396,4 +396,4 @@ for (key, value) in data { - Disable garbage collection or switch to persistent storage - Create periodic commits to preserve important data -For additional help, consult the project documentation or open an issue on the GitHub repository. \ No newline at end of file +For additional help, consult the project documentation or open an issue on the GitHub repository. diff --git a/examples/agent_context.rs b/examples/agent_context.rs index 2adcd27..778dbf2 100644 --- a/examples/agent_context.rs +++ b/examples/agent_context.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use prollytree::agent::{MemoryQuery, MemoryType, SearchableMemoryStore, TimeRange, *}; use rig::{completion::Prompt, providers::openai::Client}; use serde::{Deserialize, Serialize}; diff --git a/examples/agent_demo.rs b/examples/agent_demo.rs index e984e80..b4825d6 100644 --- a/examples/agent_demo.rs +++ b/examples/agent_demo.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use chrono::Duration; use prollytree::agent::*; use rig::{completion::Prompt, providers::openai::Client}; diff --git a/examples/data/README.md b/examples/data/README.md index f264293..7537373 100644 --- a/examples/data/README.md +++ b/examples/data/README.md @@ -74,6 +74,6 @@ This structure demonstrates the agent's ability to maintain context across diffe ## Tips - Keep messages concise but meaningful -- Mix different message types (facts, rules, searches, queries) +- Mix different message types (facts, rules, searches, queries) - Use consistent category names for better recall -- Test your JSON syntax before running the demo \ No newline at end of file +- Test your JSON syntax before running the demo diff --git a/examples/data/conversation_data.json b/examples/data/conversation_data.json index 455b985..53c26f1 100644 --- a/examples/data/conversation_data.json +++ b/examples/data/conversation_data.json @@ -40,4 +40,4 @@ "Fact: Southeast US has experienced a 40% increase in extreme precipitation events (>3 inches in 24hr) since 1950 category: flooding", "What economic impact facts do we have across all categories?" ] -} \ No newline at end of file +} diff --git a/examples/data/conversation_data_financial.json b/examples/data/conversation_data_financial.json index f37668f..84c8501 100644 --- a/examples/data/conversation_data_financial.json +++ b/examples/data/conversation_data_financial.json @@ -27,4 +27,4 @@ "Fact: Global crypto market cap exceeded $2.8 trillion in 2024 category: market_size", "Rule: market_monitoring: IF major_news OR price_move > 15% THEN reassess positions immediately" ] -} \ No newline at end of file +} diff --git a/examples/data/conversation_data_simple.json b/examples/data/conversation_data_simple.json index c30cfce..7489a1c 100644 --- a/examples/data/conversation_data_simple.json +++ b/examples/data/conversation_data_simple.json @@ -14,4 +14,4 @@ "Can you summarize our technology facts?", "Fact: AI systems require careful testing before deployment category: technology" ] -} \ No newline at end of file +} diff --git a/examples/financial_advisor/.env.example b/examples/financial_advisor/.env.example index 56ff29c..c3ccc3a 100644 --- a/examples/financial_advisor/.env.example +++ b/examples/financial_advisor/.env.example @@ -2,4 +2,4 @@ OPENAI_API_KEY=your-api-key-here # Optional: Override default model -# LLM_MODEL=gpt-4o-mini \ No newline at end of file +# LLM_MODEL=gpt-4o-mini diff --git a/examples/financial_advisor/.gitignore b/examples/financial_advisor/.gitignore index d69ddea..d721f19 100644 --- a/examples/financial_advisor/.gitignore +++ b/examples/financial_advisor/.gitignore @@ -14,4 +14,4 @@ Cargo.lock # OS files .DS_Store -Thumbs.db \ No newline at end of file +Thumbs.db diff --git a/examples/financial_advisor/README.md b/examples/financial_advisor/README.md index 248db04..d6b3e5d 100644 --- a/examples/financial_advisor/README.md +++ b/examples/financial_advisor/README.md @@ -13,7 +13,7 @@ Secure, auditable AI financial advisor with git-like versioned memory and comple - Security monitoring with injection detection - Multi-source data validation -### Enhanced Financial Advisor +### Enhanced Financial Advisor Advanced system with full agent memory integration, multi-step workflows, and behavioral learning. **Key Enhancements:** @@ -54,7 +54,7 @@ OPENAI_API_KEY="your-key" cargo run -- enhanced --verbose ### Original Version - **Versioned Memory**: Git-like storage with temporal queries -- **Security First**: Input validation, anomaly detection, audit trails +- **Security First**: Input validation, anomaly detection, audit trails - **AI Integration**: OpenAI-powered analysis with graceful fallbacks - **Real-world Simulation**: Multi-source market data with realistic delays @@ -173,7 +173,7 @@ cargo run --example memory_demo # Memory system showcase ### Educational Value This project demonstrates: 1. **Versioned Memory Systems** - Git-like storage with temporal queries -2. **Agent Memory Architecture** - Complete 4-type memory implementation +2. **Agent Memory Architecture** - Complete 4-type memory implementation 3. **Complex Workflow Orchestration** - Multi-step analysis with context 4. **Behavioral Learning** - Client adaptation and outcome-based improvement 5. **Security Best Practices** - Input validation and comprehensive auditing @@ -185,4 +185,4 @@ This project demonstrates: Part of the ProllyTree project. See main repository for license terms. -**⚠️ This is a demonstration system for educational purposes. Not for actual investment decisions.** \ No newline at end of file +**⚠️ This is a demonstration system for educational purposes. Not for actual investment decisions.** diff --git a/examples/financial_advisor/docs/architecture.md b/examples/financial_advisor/docs/architecture.md index 6c15f25..497cc05 100644 --- a/examples/financial_advisor/docs/architecture.md +++ b/examples/financial_advisor/docs/architecture.md @@ -55,7 +55,7 @@ START: Client requests recommendation for AAPL │ │ │ └─ Semantic ───► Retrieve: client_profile, risk_tolerance, goals │ -├─ STEP 2: Market Research Phase +├─ STEP 2: Market Research Phase │ │ │ ├─ Semantic ───► Query: market_entity_facts(AAPL) │ │ └─ Returns: valuation_metrics, sector_info, analyst_ratings @@ -70,7 +70,7 @@ START: Client requests recommendation for AAPL │ ├─ STEP 3: Risk Assessment Phase │ │ -│ ├─ Episodic ───► Query: client_risk_history(client_id, 90_days) +│ ├─ Episodic ───► Query: client_risk_history(client_id, 90_days) │ │ └─ Returns: past_decisions, risk_outcomes, patterns │ │ │ ├─ Procedural ─► Execute: risk_assessment_workflow @@ -114,7 +114,7 @@ START: Client requests recommendation for AAPL END: Return DetailedRecommendation to client ``` -### 2. Learning from Outcomes Workflow +### 2. Learning from Outcomes Workflow ``` START: Client reports recommendation outcome @@ -160,7 +160,7 @@ START: Client reports recommendation outcome │ ├─ Procedural ─► Update: workflow_efficiency_metrics │ - ├─ Semantic ───► Refine: confidence_scoring_algorithms + ├─ Semantic ───► Refine: confidence_scoring_algorithms │ └─ Episodic ───► Archive: complete_learning_episode @@ -291,4 +291,4 @@ User Input ──► CLI Interface ──► Enhanced Advisor └─────────────────────────────────────┘ ``` -This architecture demonstrates how the AgentMemorySystem provides the intelligence layer that transforms basic financial advisory into a sophisticated, learning, and adaptive system. \ No newline at end of file +This architecture demonstrates how the AgentMemorySystem provides the intelligence layer that transforms basic financial advisory into a sophisticated, learning, and adaptive system. diff --git a/examples/financial_advisor/docs/enhanced.md b/examples/financial_advisor/docs/enhanced.md index cc6b97e..648c578 100644 --- a/examples/financial_advisor/docs/enhanced.md +++ b/examples/financial_advisor/docs/enhanced.md @@ -147,7 +147,7 @@ advisor.set_current_client("sarah_retired").await?; advisor.update_client_risk_profile("sarah_retired", RiskTolerance::Conservative).await?; let conservative_rec = advisor.get_enhanced_recommendation("JNJ").await?; -// Client 2: Aggressive young investor +// Client 2: Aggressive young investor advisor.set_current_client("mike_young").await?; advisor.update_client_risk_profile("mike_young", RiskTolerance::Aggressive).await?; let aggressive_rec = advisor.get_enhanced_recommendation("NVDA").await?; @@ -254,4 +254,4 @@ export ADVISOR_VERBOSE="true" # Enable verbose logging - **ProllyTree Agent Memory**: Advanced memory abstraction layer - **Rig Framework**: AI-powered analysis and reasoning - **Git Integration**: Native git operations for versioning -- **Async Rust**: High-performance concurrent processing \ No newline at end of file +- **Async Rust**: High-performance concurrent processing diff --git a/examples/financial_advisor/docs/original.md b/examples/financial_advisor/docs/original.md index 2e9e452..6e4c020 100644 --- a/examples/financial_advisor/docs/original.md +++ b/examples/financial_advisor/docs/original.md @@ -16,7 +16,7 @@ The `recommend ` command generates AI-powered investment advice through ### 1. Data Collection (Simulated) The system simulates fetching real-time market data from three sources: - **Bloomberg**: Premium data with 95% trust weight (50ms latency) -- **Yahoo Finance**: Free tier with 85% trust weight (120ms latency) +- **Yahoo Finance**: Free tier with 85% trust weight (120ms latency) - **Alpha Vantage**: Rate-limited with 80% trust weight (200ms latency) ``` @@ -46,7 +46,7 @@ The validator: ### 3. Security Checks Before processing, the security monitor scans for: - SQL injection patterns -- Malicious payloads +- Malicious payloads - Data anomalies - Manipulation attempts @@ -65,8 +65,8 @@ Action: BUY Confidence: 85.0% Reasoning: Strong fundamentals with P/E of 28.4... -🤖 AI Analysis: Apple shows robust growth potential with -upcoming product launches and services expansion. The current +🤖 AI Analysis: Apple shows robust growth potential with +upcoming product launches and services expansion. The current valuation offers an attractive entry point for long-term investors. ``` @@ -74,10 +74,10 @@ valuation offers an attractive entry point for long-term investors. ### Recommendations & Profiles - `recommend ` - Get AI recommendation with market analysis -- `profile` - View/edit client profile +- `profile` - View/edit client profile - `risk ` - Set risk tolerance -### Branch Management +### Branch Management - `branch ` - Create strategy branch - `switch ` - Change branches - `visualize` - Show branch tree with commits @@ -114,7 +114,7 @@ cargo run -- --storage /tmp/advisor/data advise ## Data Simulation Notes The system uses realistic market data simulation: - Popular stocks (AAPL, MSFT, GOOGL, etc.) have accurate characteristics -- Prices vary ±1% between sources to simulate real discrepancies +- Prices vary ±1% between sources to simulate real discrepancies - Network latency is simulated based on API tier - All data includes proper timestamps and source attribution @@ -138,4 +138,4 @@ export ADVISOR_VERBOSE="true" # Enable verbose logging - **Security Rules**: Modify injection detection patterns - **Data Sources**: Add new simulated market data providers - **Validation Logic**: Customize cross-source validation rules -- **Branch Strategies**: Create custom investment strategy branches \ No newline at end of file +- **Branch Strategies**: Create custom investment strategy branches diff --git a/examples/financial_advisor/examples/demo.rs b/examples/financial_advisor/examples/demo.rs index b8d2a97..4045aeb 100644 --- a/examples/financial_advisor/examples/demo.rs +++ b/examples/financial_advisor/examples/demo.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use anyhow::Result; use chrono::Duration; use colored::Colorize; diff --git a/examples/financial_advisor/src/advisor/analysis_modules.rs b/examples/financial_advisor/src/advisor/analysis_modules.rs index 9c3cb42..48e8c36 100644 --- a/examples/financial_advisor/src/advisor/analysis_modules.rs +++ b/examples/financial_advisor/src/advisor/analysis_modules.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use anyhow::Result; // Removed unused DateTime and Utc imports use rig::{completion::Prompt, providers::openai::Client}; diff --git a/examples/financial_advisor/src/advisor/compliance.rs b/examples/financial_advisor/src/advisor/compliance.rs index 57e5c61..df453d6 100644 --- a/examples/financial_advisor/src/advisor/compliance.rs +++ b/examples/financial_advisor/src/advisor/compliance.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use crate::memory::MemoryStore; diff --git a/examples/financial_advisor/src/advisor/enhanced_advisor.rs b/examples/financial_advisor/src/advisor/enhanced_advisor.rs index d85ebeb..dfb9c47 100644 --- a/examples/financial_advisor/src/advisor/enhanced_advisor.rs +++ b/examples/financial_advisor/src/advisor/enhanced_advisor.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use anyhow::Result; use chrono::Utc; use colored::Colorize; diff --git a/examples/financial_advisor/src/advisor/interactive.rs b/examples/financial_advisor/src/advisor/interactive.rs index 6393549..fe453cf 100644 --- a/examples/financial_advisor/src/advisor/interactive.rs +++ b/examples/financial_advisor/src/advisor/interactive.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/advisor/mod.rs b/examples/financial_advisor/src/advisor/mod.rs index f7421f7..663376a 100644 --- a/examples/financial_advisor/src/advisor/mod.rs +++ b/examples/financial_advisor/src/advisor/mod.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/advisor/personalization.rs b/examples/financial_advisor/src/advisor/personalization.rs index dadd389..0670608 100644 --- a/examples/financial_advisor/src/advisor/personalization.rs +++ b/examples/financial_advisor/src/advisor/personalization.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use anyhow::Result; use chrono::{DateTime, Duration, Utc}; use rig::{completion::Prompt, providers::openai::Client}; diff --git a/examples/financial_advisor/src/advisor/recommendations.rs b/examples/financial_advisor/src/advisor/recommendations.rs index 039eecd..eb7a460 100644 --- a/examples/financial_advisor/src/advisor/recommendations.rs +++ b/examples/financial_advisor/src/advisor/recommendations.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/advisor/rig_agent.rs b/examples/financial_advisor/src/advisor/rig_agent.rs index dae26fd..de82eba 100644 --- a/examples/financial_advisor/src/advisor/rig_agent.rs +++ b/examples/financial_advisor/src/advisor/rig_agent.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/advisor/workflow.rs b/examples/financial_advisor/src/advisor/workflow.rs index 4f79c9c..51b6bb4 100644 --- a/examples/financial_advisor/src/advisor/workflow.rs +++ b/examples/financial_advisor/src/advisor/workflow.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use anyhow::Result; use chrono::{Duration, Utc}; use colored::Colorize; diff --git a/examples/financial_advisor/src/benchmarks.rs b/examples/financial_advisor/src/benchmarks.rs index e2d6ed1..d4cdf2d 100644 --- a/examples/financial_advisor/src/benchmarks.rs +++ b/examples/financial_advisor/src/benchmarks.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use anyhow::Result; use colored::Colorize; use indicatif::{ProgressBar, ProgressStyle}; diff --git a/examples/financial_advisor/src/lib.rs b/examples/financial_advisor/src/lib.rs index 913edcf..c867c67 100644 --- a/examples/financial_advisor/src/lib.rs +++ b/examples/financial_advisor/src/lib.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + pub mod advisor; pub mod benchmarks; pub mod memory; diff --git a/examples/financial_advisor/src/main.rs b/examples/financial_advisor/src/main.rs index e0f62ac..76c46f2 100644 --- a/examples/financial_advisor/src/main.rs +++ b/examples/financial_advisor/src/main.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use anyhow::Result; use clap::{Parser, Subcommand}; use colored::Colorize; diff --git a/examples/financial_advisor/src/memory/consistency.rs b/examples/financial_advisor/src/memory/consistency.rs index 7b22e46..a037bcb 100644 --- a/examples/financial_advisor/src/memory/consistency.rs +++ b/examples/financial_advisor/src/memory/consistency.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/memory/display.rs b/examples/financial_advisor/src/memory/display.rs index 64102e5..fcbcbf3 100644 --- a/examples/financial_advisor/src/memory/display.rs +++ b/examples/financial_advisor/src/memory/display.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/memory/enhanced_types.rs b/examples/financial_advisor/src/memory/enhanced_types.rs index 5543e08..a786b75 100644 --- a/examples/financial_advisor/src/memory/enhanced_types.rs +++ b/examples/financial_advisor/src/memory/enhanced_types.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/examples/financial_advisor/src/memory/mod.rs b/examples/financial_advisor/src/memory/mod.rs index 5ff207b..bab1b17 100644 --- a/examples/financial_advisor/src/memory/mod.rs +++ b/examples/financial_advisor/src/memory/mod.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] #![allow(unused_imports)] diff --git a/examples/financial_advisor/src/memory/types.rs b/examples/financial_advisor/src/memory/types.rs index b124c45..bf81bd0 100644 --- a/examples/financial_advisor/src/memory/types.rs +++ b/examples/financial_advisor/src/memory/types.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use chrono::{DateTime, Utc}; diff --git a/examples/financial_advisor/src/security/attack_simulator.rs b/examples/financial_advisor/src/security/attack_simulator.rs index 8c89b13..fa3c2d1 100644 --- a/examples/financial_advisor/src/security/attack_simulator.rs +++ b/examples/financial_advisor/src/security/attack_simulator.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/security/mod.rs b/examples/financial_advisor/src/security/mod.rs index 7201316..37f1101 100644 --- a/examples/financial_advisor/src/security/mod.rs +++ b/examples/financial_advisor/src/security/mod.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use anyhow::Result; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/examples/financial_advisor/src/validation/mod.rs b/examples/financial_advisor/src/validation/mod.rs index 15b0c0b..04fdce4 100644 --- a/examples/financial_advisor/src/validation/mod.rs +++ b/examples/financial_advisor/src/validation/mod.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/visualization/display.rs b/examples/financial_advisor/src/visualization/display.rs index 23ed182..b8c2edf 100644 --- a/examples/financial_advisor/src/visualization/display.rs +++ b/examples/financial_advisor/src/visualization/display.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] use anyhow::Result; diff --git a/examples/financial_advisor/src/visualization/mod.rs b/examples/financial_advisor/src/visualization/mod.rs index 390a6ff..7833980 100644 --- a/examples/financial_advisor/src/visualization/mod.rs +++ b/examples/financial_advisor/src/visualization/mod.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + #![allow(dead_code)] pub mod display; diff --git a/examples/sql.rs b/examples/sql.rs index 1e812a6..008fa2e 100644 --- a/examples/sql.rs +++ b/examples/sql.rs @@ -12,6 +12,20 @@ See the License for the specific language governing permissions and limitations under the License. */ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + //! Example demonstrating SQL capabilities with ProllyTree storage //! //! This example shows how to use GlueSQL with ProllyTree as a custom storage backend diff --git a/examples/storage.rs b/examples/storage.rs index fbcb678..a9d00d3 100644 --- a/examples/storage.rs +++ b/examples/storage.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use prollytree::config::TreeConfig; use prollytree::storage::RocksDBNodeStorage; use prollytree::tree::{ProllyTree, Tree}; diff --git a/pyproject.toml b/pyproject.toml index f2e1045..a325dfa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,4 +41,4 @@ Repository = "https://github.com/zhangfengcdt/prollytree.git" [tool.maturin] features = ["python"] module-name = "prollytree" -python-source = "python" \ No newline at end of file +python-source = "python" diff --git a/python/.pypirc.example b/python/.pypirc.example index 1838268..7b254d7 100644 --- a/python/.pypirc.example +++ b/python/.pypirc.example @@ -2,7 +2,7 @@ # Copy to ~/.pypirc and add your API tokens [distutils] -index-servers = +index-servers = pypi testpypi @@ -13,4 +13,4 @@ password = pypi-YOUR_API_TOKEN_HERE [testpypi] repository = https://test.pypi.org/legacy/ username = __token__ -password = pypi-YOUR_TEST_API_TOKEN_HERE \ No newline at end of file +password = pypi-YOUR_TEST_API_TOKEN_HERE diff --git a/python/README.md b/python/README.md index d55c615..5fa6042 100644 --- a/python/README.md +++ b/python/README.md @@ -54,15 +54,15 @@ memory = AgentMemorySystem("/path/to/memory", "agent_001") # Short-term memory (conversations) memory.store_conversation_turn( - "thread_123", - "user", + "thread_123", + "user", "What's the weather like?", {"session": "morning", "platform": "chat"} ) memory.store_conversation_turn( "thread_123", - "assistant", + "assistant", "I'd be happy to help with weather information!" ) @@ -72,10 +72,10 @@ history = memory.get_conversation_history("thread_123", limit=10) # Semantic memory (facts about entities) memory.store_fact( "person", - "john_doe", + "john_doe", json.dumps({ "name": "John Doe", - "role": "Software Engineer", + "role": "Software Engineer", "location": "San Francisco" }), confidence=0.95, @@ -182,7 +182,7 @@ pip install prollytree #### TreeConfig Configuration for ProllyTree instances: - `base`: Rolling hash base (default: 4) -- `modulus`: Rolling hash modulus (default: 64) +- `modulus`: Rolling hash modulus (default: 64) - `min_chunk_size`: Minimum chunk size (default: 1) - `max_chunk_size`: Maximum chunk size (default: 4096) - `pattern`: Chunk boundary pattern (default: 0) @@ -234,7 +234,7 @@ Enum for memory classification: `ShortTerm`, `Semantic`, `Episodic`, `Procedural ### Versioned Key-Value Store -#### VersionedKvStore +#### VersionedKvStore Git-backed versioned storage with full branching support: **Initialization:** @@ -273,7 +273,7 @@ Run the comprehensive test suite: ```bash cd python/tests -python test_prollytree.py # Basic ProllyTree functionality +python test_prollytree.py # Basic ProllyTree functionality python test_agent.py # Agent memory system python test_versioned_kv.py # Versioned key-value store ``` diff --git a/python/build_python.sh b/python/build_python.sh index 5bb5eee..318bb13 100755 --- a/python/build_python.sh +++ b/python/build_python.sh @@ -46,7 +46,7 @@ if [ "$1" = "--install" ]; then echo "📦 Installing wheel..." pip install "$WHEEL_PATH" --force-reinstall echo "✅ Installed ProllyTree Python bindings" - + # Run quick test echo "🧪 Running quick test..." python3 -c " @@ -68,4 +68,4 @@ echo " python3 test_python_binding.py" echo "" echo "To publish to PyPI:" echo " cd python && ./publish_python.sh test # Publish to TestPyPI first" -echo " cd python && ./publish_python.sh prod # Publish to production PyPI" \ No newline at end of file +echo " cd python && ./publish_python.sh prod # Publish to production PyPI" diff --git a/python/examples/basic_usage.py b/python/examples/basic_usage.py index 52483be..0b1f540 100644 --- a/python/examples/basic_usage.py +++ b/python/examples/basic_usage.py @@ -22,13 +22,13 @@ def main(): # Create a new in-memory tree with default configuration print("Creating in-memory ProllyTree...") tree = ProllyTree(storage_type="memory") - + # Insert some key-value pairs print("\nInserting key-value pairs...") tree.insert(b"key1", b"value1") tree.insert(b"key2", b"value2") tree.insert(b"key3", b"value3") - + # Batch insert print("Batch inserting...") batch_items = [ @@ -37,70 +37,70 @@ def main(): (b"key6", b"value6"), ] tree.insert_batch(batch_items) - + # Find values print("\nFinding values...") value = tree.find(b"key1") print(f"key1 -> {value.decode() if value else 'Not found'}") - + value = tree.find(b"key5") print(f"key5 -> {value.decode() if value else 'Not found'}") - + # Update a value print("\nUpdating key2...") tree.update(b"key2", b"updated_value2") value = tree.find(b"key2") print(f"key2 -> {value.decode() if value else 'Not found'}") - + # Get tree statistics print(f"\nTree size: {tree.size()}") print(f"Tree depth: {tree.depth()}") print(f"Root hash: {tree.get_root_hash().hex()}") - + stats = tree.stats() print(f"Tree stats: {stats}") - + # Generate and verify proof print("\nGenerating Merkle proof for key3...") proof = tree.generate_proof(b"key3") is_valid = tree.verify_proof(proof, b"key3", b"value3") print(f"Proof valid: {is_valid}") - + # Delete a key print("\nDeleting key4...") tree.delete(b"key4") value = tree.find(b"key4") print(f"key4 after deletion: {value.decode() if value else 'Not found'}") - + # Create another tree for diff comparison print("\nCreating second tree for comparison...") tree2 = ProllyTree(storage_type="memory") tree2.insert(b"key1", b"value1") tree2.insert(b"key2", b"different_value2") tree2.insert(b"key7", b"value7") - + # Compare trees - shows changes from tree to tree2 print("\nComparing trees...") diff = tree.diff(tree2) - + print("Added in tree2:", {k.decode(): v.decode() for k, v in diff["added"].items()}) print("Removed from tree:", {k.decode(): v.decode() for k, v in diff["removed"].items()}) print("Modified keys:") for k, changes in diff["modified"].items(): print(f" {k.decode()}: {changes['old'].decode()} -> {changes['new'].decode()}") - + # File-based storage example print("\n\nCreating file-based ProllyTree...") config = TreeConfig(base=4, modulus=64, min_chunk_size=1, max_chunk_size=4096) file_tree = ProllyTree(storage_type="file", path="/tmp/prolly_tree_test", config=config) - + file_tree.insert(b"persistent_key", b"persistent_value") file_tree.save_config() print("File-based tree created and saved.") - + # Tree traversal print("\nTree structure:") print(tree.traverse()) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/python/prollytree/__init__.py b/python/prollytree/__init__.py index 9104046..131df22 100644 --- a/python/prollytree/__init__.py +++ b/python/prollytree/__init__.py @@ -20,4 +20,4 @@ from .prollytree import ProllyTree, TreeConfig, AgentMemorySystem, MemoryType, VersionedKvStore, StorageBackend __version__ = "0.2.1" -__all__ = ["ProllyTree", "TreeConfig", "AgentMemorySystem", "MemoryType", "VersionedKvStore", "StorageBackend"] \ No newline at end of file +__all__ = ["ProllyTree", "TreeConfig", "AgentMemorySystem", "MemoryType", "VersionedKvStore", "StorageBackend"] diff --git a/python/prollytree/prollytree.pyi b/python/prollytree/prollytree.pyi index 4b4d6c2..3b7e619 100644 --- a/python/prollytree/prollytree.pyi +++ b/python/prollytree/prollytree.pyi @@ -4,7 +4,7 @@ from typing import Optional, Dict, List, Tuple, Union class TreeConfig: """Configuration for ProllyTree""" - + def __init__( self, base: int = 4, @@ -16,7 +16,7 @@ class TreeConfig: class ProllyTree: """A probabilistic tree for efficient storage and retrieval of ordered data""" - + def __init__( self, storage_type: str = "memory", @@ -25,58 +25,58 @@ class ProllyTree: ) -> None: """ Create a new ProllyTree instance. - + Args: storage_type: Type of storage to use ("memory" or "file") path: Path for file storage (required if storage_type is "file") config: Tree configuration (uses defaults if not provided) """ ... - + def insert(self, key: bytes, value: bytes) -> None: """Insert a key-value pair into the tree""" ... - + def insert_batch(self, items: List[Tuple[bytes, bytes]]) -> None: """Insert multiple key-value pairs in a single batch operation""" ... - + def find(self, key: bytes) -> Optional[bytes]: """Find and return the value associated with a key""" ... - + def update(self, key: bytes, value: bytes) -> None: """Update the value for an existing key""" ... - + def delete(self, key: bytes) -> None: """Delete a key from the tree""" ... - + def delete_batch(self, keys: List[bytes]) -> None: """Delete multiple keys in a single batch operation""" ... - + def size(self) -> int: """Return the number of key-value pairs in the tree""" ... - + def depth(self) -> int: """Return the depth of the tree""" ... - + def get_root_hash(self) -> bytes: """Get the root hash of the tree""" ... - + def stats(self) -> Dict[str, int]: """Get statistics about the tree structure""" ... - + def generate_proof(self, key: bytes) -> bytes: """Generate a Merkle proof for a key""" ... - + def verify_proof( self, proof: bytes, @@ -85,26 +85,26 @@ class ProllyTree: ) -> bool: """Verify a Merkle proof for a key""" ... - + def diff(self, other: "ProllyTree") -> Dict[str, Union[Dict[bytes, bytes], Dict[bytes, Dict[str, bytes]]]]: """ Compare two trees and return the differences from this tree to other. - + Note: Currently returns empty results due to implementation limitations. The diff operation works at the tree structure level, but probabilistic trees can have different structures for the same logical data. - + Returns a dictionary with: - "added": Dict of keys/values present in other but not in this tree - - "removed": Dict of keys/values present in this tree but not in other + - "removed": Dict of keys/values present in this tree but not in other - "modified": Dict of keys with different values (maps to {"old": value_in_self, "new": value_in_other}) """ ... - + def traverse(self) -> str: """Return a string representation of the tree structure""" ... - + def save_config(self) -> None: """Save the tree configuration to storage""" ... @@ -115,33 +115,33 @@ class MemoryType: Semantic: "MemoryType" Episodic: "MemoryType" Procedural: "MemoryType" - + def __str__(self) -> str: ... class AgentMemorySystem: """Comprehensive memory system for AI agents""" - + def __init__(self, path: str, agent_id: str) -> None: """ Initialize a new agent memory system. - + Args: path: Directory path for memory storage agent_id: Unique identifier for the agent """ ... - + @staticmethod def open(path: str, agent_id: str) -> "AgentMemorySystem": """ Open an existing agent memory system. - + Args: path: Directory path where memory is stored agent_id: Unique identifier for the agent """ ... - + def store_conversation_turn( self, thread_id: str, @@ -151,18 +151,18 @@ class AgentMemorySystem: ) -> str: """ Store a conversation turn in short-term memory. - + Args: thread_id: Conversation thread identifier role: Role of the speaker (e.g., "user", "assistant") content: The message content metadata: Optional metadata dictionary - + Returns: Unique ID of the stored memory """ ... - + def get_conversation_history( self, thread_id: str, @@ -170,16 +170,16 @@ class AgentMemorySystem: ) -> List[Dict[str, Union[str, float]]]: """ Retrieve conversation history for a thread. - + Args: thread_id: Conversation thread identifier limit: Maximum number of messages to retrieve - + Returns: List of message dictionaries with id, content, and created_at fields """ ... - + def store_fact( self, entity_type: str, @@ -190,19 +190,19 @@ class AgentMemorySystem: ) -> str: """ Store a fact in semantic memory. - + Args: entity_type: Type of entity (e.g., "person", "place") entity_id: Unique identifier for the entity facts: JSON string containing the facts confidence: Confidence score (0.0 to 1.0) source: Source of the information - + Returns: Unique ID of the stored fact """ ... - + def get_entity_facts( self, entity_type: str, @@ -210,16 +210,16 @@ class AgentMemorySystem: ) -> List[Dict[str, Union[str, float]]]: """ Retrieve facts about an entity. - + Args: entity_type: Type of entity entity_id: Unique identifier for the entity - + Returns: List of fact dictionaries """ ... - + def store_procedure( self, category: str, @@ -231,7 +231,7 @@ class AgentMemorySystem: ) -> str: """ Store a procedure in procedural memory. - + Args: category: Category of the procedure name: Name of the procedure @@ -239,43 +239,43 @@ class AgentMemorySystem: steps: List of JSON strings describing each step prerequisites: Optional list of prerequisites priority: Priority level (default: 1) - + Returns: Unique ID of the stored procedure """ ... - + def get_procedures_by_category( self, category: str ) -> List[Dict[str, str]]: """ Retrieve procedures by category. - + Args: category: Category to search for - + Returns: List of procedure dictionaries """ ... - + def checkpoint(self, message: str) -> str: """ Create a memory checkpoint. - + Args: message: Commit message for the checkpoint - + Returns: Checkpoint ID """ ... - + def optimize(self) -> Dict[str, int]: """ Optimize the memory system by cleaning up and consolidating memories. - + Returns: Dictionary with optimization statistics """ @@ -286,167 +286,167 @@ class StorageBackend: InMemory: "StorageBackend" File: "StorageBackend" Git: "StorageBackend" - + def __str__(self) -> str: ... class VersionedKvStore: """A versioned key-value store backed by Git and ProllyTree""" - + def __init__(self, path: str) -> None: """ Initialize a new versioned key-value store. - + Args: path: Directory path for the store (must be within a git repository) """ ... - + @staticmethod def open(path: str) -> "VersionedKvStore": """ Open an existing versioned key-value store. - + Args: path: Directory path where the store is located """ ... - + def insert(self, key: bytes, value: bytes) -> None: """ Insert a key-value pair (stages the change). - + Args: key: The key as bytes value: The value as bytes """ ... - + def get(self, key: bytes) -> Optional[bytes]: """ Get a value by key. - + Args: key: The key to look up - + Returns: The value as bytes, or None if not found """ ... - + def update(self, key: bytes, value: bytes) -> bool: """ Update an existing key-value pair (stages the change). - + Args: key: The key to update value: The new value - + Returns: True if the key existed and was updated, False otherwise """ ... - + def delete(self, key: bytes) -> bool: """ Delete a key-value pair (stages the change). - + Args: key: The key to delete - + Returns: True if the key existed and was deleted, False otherwise """ ... - + def list_keys(self) -> List[bytes]: """ List all keys in the store (includes staged changes). - + Returns: List of keys as bytes """ ... - + def status(self) -> List[Tuple[bytes, str]]: """ Show current staging area status. - + Returns: List of tuples (key, status) where status is "added", "modified", or "deleted" """ ... - + def commit(self, message: str) -> str: """ Commit staged changes. - + Args: message: Commit message - + Returns: Commit hash as hex string """ ... - + def branch(self, name: str) -> None: """ Create a new branch. - + Args: name: Name of the new branch """ ... - + def create_branch(self, name: str) -> None: """ Create a new branch and switch to it. - + Args: name: Name of the new branch """ ... - + def checkout(self, branch_or_commit: str) -> None: """ Switch to a different branch or commit. - + Args: branch_or_commit: Branch name or commit hash """ ... - + def current_branch(self) -> str: """ Get the current branch name. - + Returns: Current branch name """ ... - + def list_branches(self) -> List[str]: """ List all branches in the repository. - + Returns: List of branch names """ ... - + def log(self) -> List[Dict[str, Union[str, int]]]: """ Get commit history. - + Returns: List of commit dictionaries with id, author, committer, message, and timestamp """ ... - + def storage_backend(self) -> StorageBackend: """ Get the current storage backend type. - + Returns: Storage backend enum value """ - ... \ No newline at end of file + ... diff --git a/python/publish_python.sh b/python/publish_python.sh index d7100c9..13c7ac8 100755 --- a/python/publish_python.sh +++ b/python/publish_python.sh @@ -110,4 +110,4 @@ if [ "$ENVIRONMENT" = "test" ]; then echo " pip install --index-url https://test.pypi.org/simple/ prollytree" else echo " pip install prollytree" -fi \ No newline at end of file +fi diff --git a/python/tests/test_agent.py b/python/tests/test_agent.py index 6fdbfef..7ba707d 100644 --- a/python/tests/test_agent.py +++ b/python/tests/test_agent.py @@ -1,4 +1,17 @@ #!/usr/bin/env python3 + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Test script for ProllyTree Agent Memory System Python bindings.""" import json @@ -8,15 +21,15 @@ def test_agent_memory_system(): """Test the agent memory system functionality.""" - + # Create a temporary directory for the memory store with tempfile.TemporaryDirectory() as tmpdir: print(f"📁 Creating memory system in: {tmpdir}") - + # Initialize the agent memory system memory_system = AgentMemorySystem(tmpdir, "test_agent_001") print("✅ Agent memory system initialized") - + # Test 1: Store conversation turns print("\n🧪 Test 1: Short-term memory (conversation)") conv_id1 = memory_system.store_conversation_turn( @@ -26,20 +39,20 @@ def test_agent_memory_system(): {"source": "chat", "session": "morning"} ) print(f" Stored user message: {conv_id1}") - + conv_id2 = memory_system.store_conversation_turn( "thread_123", "assistant", "I'm doing well, thank you for asking! How can I help you today?" ) print(f" Stored assistant message: {conv_id2}") - + # Retrieve conversation history history = memory_system.get_conversation_history("thread_123", limit=10) print(f" Retrieved {len(history)} messages from conversation history") for msg in history: print(f" - {msg['created_at']}: {json.loads(msg['content'])}") - + # Test 2: Store semantic facts print("\n🧪 Test 2: Semantic memory (facts)") fact_id = memory_system.store_fact( @@ -54,14 +67,14 @@ def test_agent_memory_system(): "user_input" ) print(f" Stored fact about john_doe: {fact_id}") - + # Retrieve facts facts = memory_system.get_entity_facts("person", "john_doe") print(f" Retrieved {len(facts)} facts about john_doe") for fact in facts: print(f" - Confidence: {fact['confidence']}, Source: {fact['source']}") print(f" Facts: {fact['facts']}") - + # Test 3: Store procedures print("\n🧪 Test 3: Procedural memory") proc_id = memory_system.store_procedure( @@ -78,27 +91,27 @@ def test_agent_memory_system(): priority=2 ) print(f" Stored procedure: {proc_id}") - + # Get procedures by category procedures = memory_system.get_procedures_by_category("task_management") print(f" Retrieved {len(procedures)} procedures in task_management category") for proc in procedures: print(f" - {proc['id']}: {proc['content']}") - + # Test 4: Create checkpoint print("\n🧪 Test 4: Memory checkpoint") checkpoint_id = memory_system.checkpoint("Initial test data loaded") print(f" Created checkpoint: {checkpoint_id}") - + # Test 5: Optimize memory print("\n🧪 Test 5: Memory optimization") optimization_report = memory_system.optimize() print(" Optimization report:") for key, value in optimization_report.items(): print(f" - {key}: {value}") - + print("\n✅ All tests completed successfully!") - + # Test MemoryType enum print("\n🧪 Test 6: MemoryType enum") print(f" MemoryType.ShortTerm: {MemoryType.ShortTerm}") @@ -108,4 +121,4 @@ def test_agent_memory_system(): if __name__ == "__main__": - test_agent_memory_system() \ No newline at end of file + test_agent_memory_system() diff --git a/python/tests/test_prollytree.py b/python/tests/test_prollytree.py index b0a08ca..5d064f9 100644 --- a/python/tests/test_prollytree.py +++ b/python/tests/test_prollytree.py @@ -25,20 +25,20 @@ class TestProllyTree(unittest.TestCase): def setUp(self): self.tree = ProllyTree(storage_type="memory") self.temp_dir = tempfile.mkdtemp() - + def tearDown(self): shutil.rmtree(self.temp_dir) - + def test_insert_and_find(self): """Test basic insert and find operations""" self.tree.insert(b"test_key", b"test_value") value = self.tree.find(b"test_key") self.assertEqual(value, b"test_value") - + # Non-existent key should return None value = self.tree.find(b"non_existent") self.assertIsNone(value) - + def test_batch_operations(self): """Test batch insert and delete""" items = [ @@ -47,105 +47,105 @@ def test_batch_operations(self): (b"key3", b"value3"), ] self.tree.insert_batch(items) - + self.assertEqual(self.tree.size(), 3) self.assertEqual(self.tree.find(b"key2"), b"value2") - + # Batch delete self.tree.delete_batch([b"key1", b"key3"]) self.assertEqual(self.tree.size(), 1) self.assertIsNone(self.tree.find(b"key1")) self.assertIsNone(self.tree.find(b"key3")) self.assertEqual(self.tree.find(b"key2"), b"value2") - + def test_update(self): """Test update operation""" self.tree.insert(b"key", b"original_value") self.tree.update(b"key", b"updated_value") self.assertEqual(self.tree.find(b"key"), b"updated_value") - + def test_delete(self): """Test delete operation""" self.tree.insert(b"key", b"value") self.tree.delete(b"key") self.assertIsNone(self.tree.find(b"key")) self.assertEqual(self.tree.size(), 0) - + def test_tree_properties(self): """Test tree size, depth, and root hash""" # Empty tree self.assertEqual(self.tree.size(), 0) self.assertIsInstance(self.tree.depth(), int) self.assertIsInstance(self.tree.get_root_hash(), bytes) - + # Add items for i in range(10): self.tree.insert(f"key{i}".encode(), f"value{i}".encode()) - + self.assertEqual(self.tree.size(), 10) self.assertGreater(self.tree.depth(), 0) - + # Root hash should change after modifications initial_hash = self.tree.get_root_hash() self.tree.insert(b"new_key", b"new_value") new_hash = self.tree.get_root_hash() self.assertNotEqual(initial_hash, new_hash) - + def test_merkle_proof(self): """Test Merkle proof generation and verification""" self.tree.insert(b"key", b"value") - + # Generate and verify proof proof = self.tree.generate_proof(b"key") self.assertIsInstance(proof, bytes) - + # Verify with correct value self.assertTrue(self.tree.verify_proof(proof, b"key", b"value")) - + # Verify with incorrect value should fail self.assertFalse(self.tree.verify_proof(proof, b"key", b"wrong_value")) - + def test_tree_diff(self): """Test tree comparison - currently returns empty results""" tree2 = ProllyTree(storage_type="memory") - + # Setup trees self.tree.insert(b"shared", b"value1") self.tree.insert(b"only_in_tree1", b"value2") self.tree.insert(b"modified", b"original") - + tree2.insert(b"shared", b"value1") tree2.insert(b"only_in_tree2", b"value3") tree2.insert(b"modified", b"changed") - + # Compare - currently returns empty results due to tree structure differences # The current diff implementation works at the tree structure level, # but probabilistic trees can have different structures for the same data diff = self.tree.diff(tree2) - + # Verify the diff structure exists (even if empty) self.assertIn("added", diff) self.assertIn("removed", diff) self.assertIn("modified", diff) - + # Currently returns empty results - this is a known limitation self.assertEqual(len(diff["added"]), 0) self.assertEqual(len(diff["removed"]), 0) self.assertEqual(len(diff["modified"]), 0) - + def test_file_storage(self): """Test file-based storage""" path = f"{self.temp_dir}/test_tree" config = TreeConfig(base=4, modulus=64) - + tree = ProllyTree(storage_type="file", path=path, config=config) tree.insert(b"persistent", b"data") tree.save_config() - + # Verify file was created import os self.assertTrue(os.path.exists(path)) - + def test_custom_config(self): """Test tree with custom configuration""" config = TreeConfig( @@ -155,30 +155,30 @@ def test_custom_config(self): max_chunk_size=8192, pattern=42 ) - + tree = ProllyTree(storage_type="memory", config=config) tree.insert(b"key", b"value") self.assertEqual(tree.find(b"key"), b"value") - + def test_stats(self): """Test tree statistics""" for i in range(5): self.tree.insert(f"key{i}".encode(), f"value{i}".encode()) - + stats = self.tree.stats() self.assertIsInstance(stats, dict) # Stats should contain various metrics self.assertGreater(len(stats), 0) - + def test_traverse(self): """Test tree traversal""" self.tree.insert(b"a", b"1") self.tree.insert(b"b", b"2") self.tree.insert(b"c", b"3") - + traversal = self.tree.traverse() self.assertIsInstance(traversal, str) self.assertGreater(len(traversal), 0) if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/python/tests/test_versioned_kv.py b/python/tests/test_versioned_kv.py index 11ec868..f953a89 100644 --- a/python/tests/test_versioned_kv.py +++ b/python/tests/test_versioned_kv.py @@ -1,4 +1,17 @@ #!/usr/bin/env python3 + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Test script for VersionedKvStore Python bindings.""" import tempfile @@ -8,103 +21,103 @@ def test_versioned_kv_store(): """Test the versioned key-value store functionality.""" - + # Create a temporary directory and initialize a git repository with tempfile.TemporaryDirectory() as tmpdir: print(f"📁 Creating test in: {tmpdir}") - + # Initialize git repository subprocess.run(["git", "init"], cwd=tmpdir, check=True, capture_output=True) subprocess.run(["git", "config", "user.name", "Test User"], cwd=tmpdir, check=True) subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmpdir, check=True) - + # Create a subdirectory for our dataset - dataset_dir = os.path.join(tmpdir, "dataset") + dataset_dir = os.path.join(tmpdir, "dataset") os.makedirs(dataset_dir) os.chdir(dataset_dir) - + print("✅ Git repository initialized") - + # Test 1: Initialize VersionedKvStore print("\n🧪 Test 1: Initialize VersionedKvStore") store = VersionedKvStore(dataset_dir) print(f" Storage backend: {store.storage_backend()}") print(f" Current branch: {store.current_branch()}") - + # Test 2: Basic key-value operations print("\n🧪 Test 2: Basic key-value operations") store.insert(b"name", b"Alice") store.insert(b"age", b"30") store.insert(b"city", b"San Francisco") - + # Check values name = store.get(b"name") age = store.get(b"age") city = store.get(b"city") - + print(f" name: {name}") print(f" age: {age}") print(f" city: {city}") - + # Test 3: List keys and status print("\n🧪 Test 3: List keys and status") keys = store.list_keys() print(f" Keys: {[k.decode() for k in keys]}") - + status = store.status() print(" Status:") for key, status_str in status: print(f" - {key.decode()}: {status_str}") - + # Test 4: Commit changes print("\n🧪 Test 4: Commit changes") commit_hash = store.commit("Add initial user data") print(f" Commit hash: {commit_hash}") - + # Check status after commit status = store.status() print(f" Status after commit: {len(status)} staged changes") - + # Test 5: Update and delete operations print("\n🧪 Test 5: Update and delete operations") updated = store.update(b"age", b"31") print(f" Updated age: {updated}") - + deleted = store.delete(b"city") print(f" Deleted city: {deleted}") - + # Add new key store.insert(b"country", b"USA") - + # Check status status = store.status() print(" Status after changes:") for key, status_str in status: print(f" - {key.decode()}: {status_str}") - + # Test 6: Branch operations print("\n🧪 Test 6: Branch operations") store.create_branch("feature-branch") print(" Created and switched to feature-branch") print(f" Current branch: {store.current_branch()}") - + # Make changes on feature branch store.insert(b"feature", b"new-feature") store.commit("Add feature on feature branch") - + # List all branches branches = store.list_branches() print(f" Available branches: {branches}") - + # Test 7: Switch back to main print("\n🧪 Test 7: Switch back to main") store.checkout("main") print(f" Current branch: {store.current_branch()}") - + # Check if feature key exists (should not exist on main) feature = store.get(b"feature") print(f" Feature key on main: {feature}") - + # Test 8: Commit history print("\n🧪 Test 8: Commit history") history = store.log() @@ -113,9 +126,9 @@ def test_versioned_kv_store(): print(f" {i+1}. {commit['id'][:8]} - {commit['message']}") print(f" Author: {commit['author']}") print(f" Timestamp: {commit['timestamp']}") - + print("\n✅ All VersionedKvStore tests completed successfully!") if __name__ == "__main__": - test_versioned_kv_store() \ No newline at end of file + test_versioned_kv_store() diff --git a/run_benchmarks.sh b/run_benchmarks.sh index e19ecf3..0baee57 100755 --- a/run_benchmarks.sh +++ b/run_benchmarks.sh @@ -25,7 +25,7 @@ echo "" echo "📊 2. Running SQL Benchmarks..." cargo bench --bench sql_bench --features sql --quiet -- --quick -echo "" +echo "" echo "📊 3. Running Git-Prolly Integration Benchmarks..." cargo bench --bench git_prolly_bench --features "git sql" --quiet -- --quick @@ -38,4 +38,4 @@ echo "" echo "📊 Available benchmarks:" echo " - prollytree_bench: Core tree operations" echo " - sql_bench: SQL operations (requires --features sql)" -echo " - git_prolly_bench: Git integration (requires --features git,sql)" \ No newline at end of file +echo " - git_prolly_bench: Git integration (requires --features git,sql)" diff --git a/scripts/check-license.sh b/scripts/check-license.sh new file mode 100755 index 0000000..05990fe --- /dev/null +++ b/scripts/check-license.sh @@ -0,0 +1,126 @@ +#!/bin/bash + +# License header check script for Apache 2.0 license +# This script checks if Rust (.rs) and Python (.py) files contain the Apache 2.0 license header + +set -e + +# Apache 2.0 license header for Rust files (multi-line comment) +read -r -d '' RUST_LICENSE_HEADER << 'EOF' || true +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +EOF + +# Apache 2.0 license header for Python files (single-line comments) +read -r -d '' PYTHON_LICENSE_HEADER << 'EOF' || true +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +EOF + +EXIT_CODE=0 + +check_rust_license() { + local file="$1" + + # Skip files in target directory (generated code) + if [[ "$file" =~ target/ ]]; then + return 0 + fi + + # Read first 14 lines of the file (license header length) + local file_header + file_header=$(head -14 "$file" 2>/dev/null || echo "") + + # Check if license header is present + if [[ "$file_header" != "$RUST_LICENSE_HEADER"* ]]; then + echo "❌ Missing or incorrect Apache 2.0 license header in: $file" + echo "Expected header:" + echo "$RUST_LICENSE_HEADER" + echo "" + echo "Found header:" + echo "$file_header" + echo "" + return 1 + fi + + echo "✅ License header OK: $file" + return 0 +} + +check_python_license() { + local file="$1" + + # Read the entire file content + local file_content + file_content=$(cat "$file" 2>/dev/null || echo "") + + # Check if the license header is present anywhere in the file + if [[ "$file_content" == *"$PYTHON_LICENSE_HEADER"* ]]; then + echo "✅ License header OK: $file" + return 0 + fi + + # If not found, show error + echo "❌ Missing or incorrect Apache 2.0 license header in: $file" + echo "Expected header:" + echo "$PYTHON_LICENSE_HEADER" + echo "" + echo "Found header:" + head -14 "$file" 2>/dev/null || echo "" + echo "" + return 1 +} + +# Process each file passed as argument +for file in "$@"; do + if [[ ! -f "$file" ]]; then + continue + fi + + case "$file" in + *.rs) + if ! check_rust_license "$file"; then + EXIT_CODE=1 + fi + ;; + *.py) + if ! check_python_license "$file"; then + EXIT_CODE=1 + fi + ;; + *) + echo "⚠️ Skipping unsupported file type: $file" + ;; + esac +done + +if [[ $EXIT_CODE -eq 0 ]]; then + echo "🎉 All files have proper license headers!" +else + echo "" + echo "💡 To fix missing license headers, add the appropriate Apache 2.0 license header to the beginning of each file." + echo " For Rust files (.rs): Use /* */ multi-line comment format" + echo " For Python files (.py): Use # single-line comment format" +fi + +exit $EXIT_CODE diff --git a/src/agent/embedding_search.rs b/src/agent/embedding_search.rs index 6cccc55..a3ca944 100644 --- a/src/agent/embedding_search.rs +++ b/src/agent/embedding_search.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use async_trait::async_trait; use std::collections::HashMap; diff --git a/src/agent/mem_lifecycle.rs b/src/agent/mem_lifecycle.rs index d652f6a..07d2d8d 100644 --- a/src/agent/mem_lifecycle.rs +++ b/src/agent/mem_lifecycle.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use async_trait::async_trait; use chrono::{Duration, Utc}; use std::collections::HashMap; diff --git a/src/agent/mem_long_term.rs b/src/agent/mem_long_term.rs index 4a16a51..5addc93 100644 --- a/src/agent/mem_long_term.rs +++ b/src/agent/mem_long_term.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use async_trait::async_trait; use chrono::{Datelike, Utc}; use serde_json::json; diff --git a/src/agent/mem_short_term.rs b/src/agent/mem_short_term.rs index 4fae297..a111ccf 100644 --- a/src/agent/mem_short_term.rs +++ b/src/agent/mem_short_term.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use async_trait::async_trait; use chrono::{Duration, Utc}; use serde_json::json; diff --git a/src/agent/mem_store.rs b/src/agent/mem_store.rs index 80f5946..c79daf5 100644 --- a/src/agent/mem_store.rs +++ b/src/agent/mem_store.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use async_trait::async_trait; use chrono::{DateTime, Utc}; use serde_json; diff --git a/src/agent/mod.rs b/src/agent/mod.rs index a752c3a..609d118 100644 --- a/src/agent/mod.rs +++ b/src/agent/mod.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + //! Agent Memory System //! //! This module provides a comprehensive memory system for AI agents, implementing @@ -78,7 +92,6 @@ //! //! // Retrieve conversation history //! let history = short_term.get_conversation_history("thread_123", None).await?; -//! println!("Conversation history: {} messages", history.len()); //! //! // Commit changes //! short_term.commit("Store initial conversation").await?; diff --git a/src/agent/persistence.rs b/src/agent/persistence.rs index 479af14..4981f19 100644 --- a/src/agent/persistence.rs +++ b/src/agent/persistence.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use async_trait::async_trait; use std::error::Error; use std::path::Path; @@ -113,13 +127,11 @@ impl MemoryPersistence for InMemoryPersistence { Ok(matching_keys) } - async fn checkpoint(&mut self, message: &str) -> Result> { + async fn checkpoint(&mut self, _message: &str) -> Result> { let commit_id = self.next_commit_id().await; // For in-memory storage, we just generate a commit ID // In a real git-based implementation, this would create an actual commit - println!("Prolly tree checkpoint: {} - {}", commit_id, message); - Ok(commit_id) } } @@ -127,14 +139,12 @@ impl MemoryPersistence for InMemoryPersistence { /// Additional methods specific to prolly tree persistence impl InMemoryPersistence { /// Create a new branch (for in-memory, this is a no-op) - pub async fn create_branch(&mut self, name: &str) -> Result<(), Box> { - println!("Created prolly tree branch: {name}"); + pub async fn create_branch(&mut self, _name: &str) -> Result<(), Box> { Ok(()) } /// Switch to a branch or commit (for in-memory, this is a no-op) - pub async fn checkout(&mut self, branch_or_commit: &str) -> Result<(), Box> { - println!("Checked out prolly tree: {branch_or_commit}"); + pub async fn checkout(&mut self, _branch_or_commit: &str) -> Result<(), Box> { Ok(()) } @@ -154,8 +164,7 @@ impl InMemoryPersistence { } /// Merge another branch (for in-memory, this is a no-op) - pub async fn merge(&mut self, branch: &str) -> Result> { - println!("Merged prolly tree branch: {branch}"); + pub async fn merge(&mut self, _branch: &str) -> Result> { // Use a simple timestamp instead of chrono for in-memory implementation use std::time::{SystemTime, UNIX_EPOCH}; let timestamp = SystemTime::now() @@ -585,7 +594,6 @@ mod tests { .await .unwrap(); assert!(!commit_id.is_empty()); - println!("Created checkpoint: {}", commit_id); // Verify memories are still accessible after checkpoint let retrieved1 = store.get("checkpoint_test1").await.unwrap(); @@ -725,7 +733,6 @@ mod tests { .unwrap(); let duration = start_time.elapsed(); - println!("{} backend completed in {:?}", backend_name, duration); // Verify all memories were stored for i in 0..10 { diff --git a/src/agent/persistence_prolly.rs b/src/agent/persistence_prolly.rs index ec47e33..a68145c 100644 --- a/src/agent/persistence_prolly.rs +++ b/src/agent/persistence_prolly.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use super::traits::MemoryPersistence; use crate::git::{GitVersionedKvStore, GitKvError}; use async_trait::async_trait; @@ -7,45 +21,45 @@ use std::sync::Arc; use tokio::sync::RwLock; /// ProllyTree-based memory persistence using git-backed versioned storage -/// +/// /// # Implementation Status -/// +/// /// **FULLY IMPLEMENTED** but currently disabled in the module due to thread safety constraints. /// This implementation is complete, tested, and ready to use in single-threaded contexts. -/// +/// /// # Thread Safety Warning -/// +/// /// **IMPORTANT**: This struct is NOT thread-safe due to limitations in the underlying -/// Git library (gix). The GitVersionedKvStore contains internal RefCell components +/// Git library (gix). The GitVersionedKvStore contains internal RefCell components /// that prevent it from being Sync. -/// +/// /// **Use only in single-threaded contexts** or where you can guarantee exclusive access. /// For multi-threaded applications, use SimpleMemoryPersistence instead. -/// +/// /// # Benefits -/// +/// /// - Real git-backed versioned storage with authentic commit history /// - Branch operations (create, checkout, merge) /// - Time-travel debugging capabilities /// - Persistent storage across application restarts /// - Full git log and diff capabilities -/// +/// /// # How to Enable -/// +/// /// To use this implementation: /// 1. Uncomment the module import in `mod.rs` /// 2. Uncomment the PersistenceBackend::Prolly variant /// 3. Use only in single-threaded applications /// 4. See `PROLLY_MEMORY_IMPLEMENTATION.md` for complete instructions -/// +/// /// # Example -/// +/// /// ```rust,no_run /// use prollytree::agent::ProllyMemoryPersistence; -/// +/// /// // Only use in single-threaded contexts! /// let persistence = ProllyMemoryPersistence::init( -/// "/tmp/agent_memory", +/// "/tmp/agent_memory", /// "agent_memories" /// )?; /// ``` @@ -89,17 +103,17 @@ impl MemoryPersistence for ProllyMemoryPersistence { async fn save(&mut self, key: &str, data: &[u8]) -> Result<(), Box> { let full_key = self.full_key(key); let mut store = self.store.write().await; - + // Save to git-backed prolly tree store.insert(full_key.into_bytes(), data.to_vec())?; - + Ok(()) } async fn load(&self, key: &str) -> Result>, Box> { let full_key = self.full_key(key); let store = self.store.read().await; - + let data = store.get(full_key.as_bytes()); Ok(data) } @@ -107,17 +121,17 @@ impl MemoryPersistence for ProllyMemoryPersistence { async fn delete(&mut self, key: &str) -> Result<(), Box> { let full_key = self.full_key(key); let mut store = self.store.write().await; - + // Delete from git-backed prolly tree store.delete(full_key.as_bytes())?; - + Ok(()) } async fn list_keys(&self, prefix: &str) -> Result, Box> { let full_prefix = self.full_key(prefix); let store = self.store.read().await; - + // Get all keys from git-backed store and filter by prefix let all_keys = store.list_keys(); let filtered_keys: Vec = all_keys @@ -133,16 +147,16 @@ impl MemoryPersistence for ProllyMemoryPersistence { } }) .collect(); - + Ok(filtered_keys) } async fn checkpoint(&mut self, message: &str) -> Result> { let mut store = self.store.write().await; - + // Create a git commit with the provided message let commit_id = store.commit(message)?; - + Ok(format!("{}", commit_id)) } } @@ -171,21 +185,21 @@ impl ProllyMemoryPersistence { /// Get memory statistics including git information pub async fn get_stats(&self) -> Result> { let store = self.store.read().await; - + // Get git log to count commits let commits = store.log().unwrap_or_default(); let commit_count = commits.len(); - + // Get current branch info let current_branch = "main".to_string(); // GitKv doesn't expose current branch yet - + // Count total keys with our namespace let all_keys = store.list_keys("")?; let namespace_keys: Vec<_> = all_keys .into_iter() .filter(|key| key.starts_with(&format!("{}:", self.namespace_prefix))) .collect(); - + Ok(ProllyMemoryStats { total_keys: namespace_keys.len(), namespace_prefix: self.namespace_prefix.clone(), @@ -270,4 +284,4 @@ mod tests { assert_eq!(data1, Some(b"data1".to_vec())); assert_eq!(data2, Some(b"data2".to_vec())); } -} \ No newline at end of file +} diff --git a/src/agent/persistence_simple.rs b/src/agent/persistence_simple.rs index 5193f35..e7c6d20 100644 --- a/src/agent/persistence_simple.rs +++ b/src/agent/persistence_simple.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use async_trait::async_trait; use std::error::Error; use std::path::Path; @@ -118,8 +132,6 @@ impl MemoryPersistence for SimpleMemoryPersistence { // For in-memory storage, we just generate a commit ID // In a real git-based implementation, this would create an actual commit - println!("Prolly tree checkpoint: {} - {}", commit_id, message); - Ok(commit_id) } } @@ -128,13 +140,11 @@ impl MemoryPersistence for SimpleMemoryPersistence { impl SimpleMemoryPersistence { /// Create a new branch (for in-memory, this is a no-op) pub async fn create_branch(&mut self, name: &str) -> Result<(), Box> { - println!("Created prolly tree branch: {name}"); Ok(()) } /// Switch to a branch or commit (for in-memory, this is a no-op) pub async fn checkout(&mut self, branch_or_commit: &str) -> Result<(), Box> { - println!("Checked out prolly tree: {branch_or_commit}"); Ok(()) } @@ -155,7 +165,6 @@ impl SimpleMemoryPersistence { /// Merge another branch (for in-memory, this is a no-op) pub async fn merge(&mut self, branch: &str) -> Result> { - println!("Merged prolly tree branch: {branch}"); // Use a simple timestamp instead of chrono for in-memory implementation use std::time::{SystemTime, UNIX_EPOCH}; let timestamp = SystemTime::now() diff --git a/src/agent/traits.rs b/src/agent/traits.rs index e6d4d3c..9d17299 100644 --- a/src/agent/traits.rs +++ b/src/agent/traits.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use async_trait::async_trait; use std::error::Error; use std::fmt; diff --git a/src/agent/types.rs b/src/agent/types.rs index a41e54f..d84548a 100644 --- a/src/agent/types.rs +++ b/src/agent/types.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use chrono::{DateTime, Duration, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/src/agent/versioned_persistence.rs b/src/agent/versioned_persistence.rs index f0726e6..4b33342 100644 --- a/src/agent/versioned_persistence.rs +++ b/src/agent/versioned_persistence.rs @@ -1,3 +1,17 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use super::traits::MemoryPersistence; use crate::git::{GitKvError, ThreadSafeGitVersionedKvStore}; use async_trait::async_trait; diff --git a/src/git/versioned_store.rs b/src/git/versioned_store.rs index 36b3cfc..e63e7d7 100644 --- a/src/git/versioned_store.rs +++ b/src/git/versioned_store.rs @@ -39,7 +39,7 @@ pub trait HistoricalCommitAccess { /// Returns commits in reverse chronological order (newest first) fn get_commits_for_key(&self, key: &[u8]) -> Result, GitKvError>; - /// Get the commit history for the repository + /// Get the commit history for the repository /// Returns commits in reverse chronological order (newest first) fn get_commit_history(&self) -> Result, GitKvError>; } @@ -82,7 +82,7 @@ pub struct ThreadSafeVersionedKvStore> { inner: Arc>>, } -/// Type alias for thread-safe Git storage +/// Type alias for thread-safe Git storage pub type ThreadSafeGitVersionedKvStore = ThreadSafeVersionedKvStore>;