From f9fd27d228284bc98db380e22f54e3f4701363cd Mon Sep 17 00:00:00 2001 From: JamesTheGiblet Date: Tue, 6 Jan 2026 22:04:37 +0000 Subject: [PATCH] Implement core skills: Code validation, model fine-tuning, and system diagnostics - Added `ModelFineTuner` class for preparing training data and fine-tuning models based on user corrections. - Introduced `CodeValidator` class to validate generated code against various hardware and style rules, including safety checks and function naming conventions. - Developed skills for calculator operations, system information retrieval, weather fetching, and timer functionality. - Implemented a self-diagnostic skill to run unit tests and report results. - Created a dynamic skill loading mechanism to discover and register skills from the current directory. - Added unit tests for skills to ensure functionality and reliability. --- README.md | 3590 ++++-------------- buddai_executive.py | 1211 +----- buddai_server.py | 8 +- core/__init__.py | 0 core/buddai_analytics.py | 72 + core/buddai_knowledge.py | 181 + core/buddai_llm.py | 137 + buddai_memory.py => core/buddai_memory.py | 2 +- core/buddai_personality.py | 126 + core/buddai_prompt_engine.py | 310 ++ buddai_shared.py => core/buddai_shared.py | 2 +- core/buddai_storage.py | 240 ++ core/buddai_training.py | 41 + buddai_logic.py => core/buddai_validation.py | 84 +- decouple_buddai.py | 43 - main.py | 2 +- skills/__init__.py | 41 + skills/calculator.py | 47 + skills/system_info.py | 27 + skills/test_all.py | 68 + skills/timer.py | 44 + skills/weather.py | 44 + tests/__init__.py | 0 tests/test_all.py | 29 + tests/test_buddai.py | 24 +- tests/test_buddai_v3_2.py | 25 +- tests/test_integration.py | 21 +- tests/test_skills.py | 56 + 28 files changed, 2398 insertions(+), 4077 deletions(-) create mode 100644 core/__init__.py create mode 100644 core/buddai_analytics.py create mode 100644 core/buddai_knowledge.py create mode 100644 core/buddai_llm.py rename buddai_memory.py => core/buddai_memory.py (99%) create mode 100644 core/buddai_personality.py create mode 100644 core/buddai_prompt_engine.py rename buddai_shared.py => core/buddai_shared.py (97%) create mode 100644 core/buddai_storage.py create mode 100644 core/buddai_training.py rename buddai_logic.py => core/buddai_validation.py (90%) delete mode 100644 decouple_buddai.py create mode 100644 skills/__init__.py create mode 100644 skills/calculator.py create mode 100644 skills/system_info.py create mode 100644 skills/test_all.py create mode 100644 skills/timer.py create mode 100644 skills/weather.py create mode 100644 tests/__init__.py create mode 100644 tests/test_all.py create mode 100644 tests/test_skills.py diff --git a/README.md b/README.md index 8f04f80..f92f8be 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,575 @@ -# BuddAI v3.8 - Complete Validation Report +# 🧠 BuddAI - Your Personal AI Exocortex -## 14 Hours | 10 Questions | 100+ Iterations | 90% Achievement +> **"Your mind. Your code. Your AI. Forever."** -**Date:** January 1, 2026 -**Tester:** James Gilbert (JamesTheGiblet) -**System:** BuddAI v3.8 - Multi-User & Fine-Tuning Ready -**Result:** āœ… PRODUCTION-READY for Personal Use +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/) +[![Ollama](https://img.shields.io/badge/Ollama-Required-orange.svg)](https://ollama.ai/) + +**BuddAI is a personal AI coding assistant that learns exclusively from YOUR repositories, generates code in YOUR style, and runs 100% locally on your machine. No cloud. No subscriptions. No data mining. Just you and your AI.** --- -## Executive Summary +## 🚨 THE BREAKTHROUGH: Cognitive Labor That Pays After You Leave -BuddAI v3.8 is a validated AI-powered code generation system for ESP32-C3 embedded development that achieved **90% average accuracy** across a comprehensive 10-question test suite representing real-world embedded systems development scenarios. +> **For the first time in history, your expertise can generate income AFTER you leave a job.** -### Key Achievements +When you leave a company, your knowledge usually walks out the door with you. Not anymore. -- āœ… **90% Average Code Quality** across all test questions -- āœ… **Modular Build System** automatically decomposes complex requests into manageable steps -- āœ… **Interactive Forge Theory** with user-selectable physics constants (k=0.3/0.1/0.03) -- āœ… **Auto-Fix Capability** detects and corrects common embedded systems errors -- āœ… **Learning System** improves through iterative corrections (proven +40-60% improvement) -- āœ… **85-95% Time Savings** vs manual coding for embedded systems +**With BuddAI + P.DE.I:** -### Test Statistics +- 🧠 Your BuddAI learns from YOUR 8+ years of experience +- šŸ¢ Companies license access to your expertise while you work ($400-750/month - they pay, not you) +- šŸ‘» When you leave, your "shadow" stays behind (if company chooses) +- šŸ’° **You earn $100-300/month passive royalties for YEARS after leaving** +- šŸ“ˆ Multiple shadows across your career = compounding passive income + +**Example:** Senior engineer with 15-year career, 4 companies: ``` -Duration: 14 hours -Questions: 10 comprehensive tests -Iterations: 100+ generation attempts -Sessions: 10+ independent runs -Code Generated: ~5,000+ lines -Rules Learned: 125+ patterns -Success Rate: 100% (all questions ≄80%) -Excellent (≄90%): 8/10 questions (80%) +Company A: Left 2020 → Shadow retained → $200/month = $14,400 total (6 years) +Company B: Left 2022 → Shadow retained → $250/month = $12,000 total (4 years) +Company C: Left 2024 → Shadow retained → $300/month = $7,200 total (2 years) +Company D: Current role → $0 (not left yet) + +Total passive income from cognitive labor: $33,600 +Annual passive income from past work: $9,000/year +``` + +**This isn't salary. This isn't consulting. This is YOUR EXPERTISE generating value long after you've moved on.** + +See [P.DE.I Overview](#-enterprise--commercial) for full details. + +--- + +## šŸŽ Adopt Your Own BuddAI + +**BuddAI is free, open-source (MIT), and runs 100% on your machine. No cloud. No subscriptions. Forever.** + +### Why You Should Start Today + +Every day you wait is a day you're NOT building your cognitive labor asset: + +``` +Start Today: +ā”œā”€ Week 1: Index your repos, BuddAI learns your style +ā”œā”€ Week 2: Use daily, correct mistakes, patterns improve +ā”œā”€ Month 1: 85% accurate code generation in your style +ā”œā”€ Month 3: 90%+ accuracy, proactive suggestions +ā”œā”€ Month 6: True exocortex - anticipates your needs +└─ Year 1: Ready to connect to P.DE.I when you start new job + → Start earning shadow royalties when you eventually leave +``` + +**The Earlier You Start, The More Valuable Your BuddAI Becomes:** + +- 1 year of training → Useful assistant +- 3 years of training → Highly valuable expertise +- 5+ years of training → Irreplaceable cognitive asset +- 10+ years of training → **Worth 6-figures in shadow royalties across career** + +### Three Ways to Adopt BuddAI + +#### 1ļøāƒ£ **Quick Start (5 minutes)** + +```bash +git clone https://github.com/JamesTheGiblet/BuddAI +cd BuddAI +pip install -r requirements.txt +ollama pull qwen2.5-coder:1.5b qwen2.5-coder:3b +python main.py --server +# Open: http://localhost:8000/web +``` + +#### 2ļøāƒ£ **Power User (30 minutes)** + +```bash +# After Quick Start: +python main.py +/index ~/Documents/code # Index all your repos +/scan # Learn your style +# Start coding together - BuddAI learns from corrections +``` + +#### 3ļøāƒ£ **Future-Proof (Prepare for P.DE.I)** + +```bash +# Set up for eventual commercial use: +1. Build your BuddAI (Steps 1-2 above) +2. Use daily for 6-12 months (the more the better) +3. When starting new job with P.DE.I: + - Connect BuddAI via API Bridge + - Start earning company licensing fees (they pay, not you) + - Build your shadow + - When you leave → Shadow royalties begin +``` + +### What You'll Have in 30 Days + +āœ… **Personal AI** trained on YOUR 20-200+ repos +āœ… **Code generation** at 85-90% accuracy in YOUR style +āœ… **Hardware validation** for ESP32/Arduino/Pi Pico +āœ… **Learning system** that improves every time you correct it +āœ… **Repository search** - "Show me everywhere I used exponential smoothing" +āœ… **Proactive suggestions** - "I noticed you usually add safety timeouts here..." +āœ… **Zero cost** - runs locally, no subscriptions +āœ… **Complete ownership** - MIT licensed, yours forever + +### Start Building Your Cognitive Pension Fund Today + +```bash +# It takes 30 seconds to clone +# It takes 5 minutes to set up +# It takes 6 months to train properly +# It takes 10 years to build a 6-figure passive income stream + +# The question is: When do you want to start? +``` + +**šŸ‘‰ [Get Started Now](#-quick-start-1) | šŸ“š [Read Full Docs](#-documentation) | šŸ’¼ [Enterprise P.DE.I Info](#-enterprise--commercial)** + +--- + +## šŸŽÆ What BuddAI Does + +BuddAI is **NOT** another generic AI assistant trained on everyone's code. It's an exocortex - a cognitive extension of YOU - trained on YOUR 8 years of experience, YOUR 115+ repositories, YOUR problem-solving patterns. + +**Proven Results:** + +- āœ… **90% Code Accuracy** on ESP32-C3 embedded development +- āœ… **85-95% Time Savings** vs manual coding +- āœ… **Hardware-Aware Validation** (ESP32, Arduino, Raspberry Pi Pico) +- āœ… **Learns from Corrections** - gets smarter every time you fix it +- āœ… **Proactive Suggestions** - anticipates what you'll need next + +--- + +## šŸ”‘ Why BuddAI is Different + +### Traditional AI Assistants (Copilot, ChatGPT) + +``` +āŒ Trained on everyone's code +āŒ Generic responses +āŒ No understanding of YOUR style +āŒ Cloud-dependent +āŒ Subscription fees +āŒ Your data is their training data +``` + +### BuddAI (Personal Exocortex) + +``` +āœ… Trained ONLY on YOUR repos +āœ… Generates in YOUR style +āœ… Learns YOUR patterns +āœ… 100% local (no internet needed) +āœ… Free forever (MIT licensed) +āœ… Your data never leaves your machine ``` --- -## Table of Contents +## šŸš€ Quick Start (Detailed) -1. [Test Methodology](#test-methodology) -2. [Complete Results](#complete-results) -3. [Capabilities Proven](#capabilities-proven) -4. [Limitations & Workarounds](#limitations--workarounds) -5. [Key Breakthroughs](#key-breakthroughs) -6. [Production Readiness](#production-readiness) -7. [Business Value](#business-value) -8. [Implementation Guide](#implementation-guide) -9. [Troubleshooting](#troubleshooting) -10. [Appendices](#appendices) +### Prerequisites + +- Python 3.9+ +- [Ollama](https://ollama.ai/) installed and running +- 8GB RAM minimum (16GB recommended) +- ~50GB disk space for models + +### Installation + +```bash +# 1. Clone the repository +git clone https://github.com/JamesTheGiblet/BuddAI +cd BuddAI + +# 2. Install dependencies +pip install -r requirements.txt + +# 3. Pull AI models (via Ollama) +ollama pull qwen2.5-coder:1.5b +ollama pull qwen2.5-coder:3b + +# 4. Start BuddAI +python main.py +``` + +### First Steps + +```bash +# Command Line Mode +python main.py + +# Web Interface +python main.py --server +# Then open: http://localhost:8000/web + +# Index your repositories (this is where the magic happens) +/index /path/to/your/repos +``` --- -## Test Methodology +## 🧬 Core Capabilities -### Test Suite Design +### 1. **Repository Learning** -**Purpose:** Validate BuddAI's ability to generate production-quality ESP32-C3 code across diverse patterns and complexity levels. +BuddAI indexes your code repositories and learns: -**Question Selection Criteria:** +- **Function patterns** - How you structure your code +- **Naming conventions** - camelCase, snake_case, your preferences +- **Safety practices** - Timeouts, error handling, hardware considerations +- **Hardware specifics** - ESP32 PWM, Arduino patterns, timing requirements -1. **Hardware Coverage** - Test all common ESP32-C3 peripherals (PWM, GPIO, ADC, UART, servo, motor drivers) -2. **Pattern Diversity** - Cover input/output, analog/digital, control logic, and system integration -3. **Complexity Progression** - Start simple (LED control) → End complex (complete robot system) -4. **Real-World Relevance** - Questions based on actual GilBot combat robot requirements -5. **Learning Validation** - Questions designed to test pattern retention and cross-domain transfer +```python +# BuddAI learns from YOUR repos +buddai.index_local_repositories("/path/to/your/115/repos") -### Scoring Rubric (100-Point Scale) +# Then generates code in YOUR style +response = buddai.chat("Generate ESP32 motor control code") +# Output: Uses YOUR preferred patterns, YOUR safety practices, YOUR style +``` -**Correctness (40 points):** +### 2. **Hardware-Aware Code Generation** -- 40: Compiles and runs perfectly on hardware -- 30: Compiles with warnings, runs correctly -- 20: Compiles, partial functionality -- 10: Syntax errors but fixable -- 0: Fundamentally wrong approach +BuddAI knows the difference between: -**Pattern Adherence (30 points):** +- ESP32-C3 (ledcSetup, 12-bit ADC) +- Arduino Uno (analogWrite, 10-bit ADC) +- Raspberry Pi Pico (PWM specific patterns) -- 30: All learned rules applied correctly -- 25: Most rules applied, minor deviations -- 20: Some rules applied, some missed -- 10: Few rules applied -- 0: Ignores learned patterns +```python +# Detects hardware automatically +"Generate code for ESP32 servo control" +# → Uses ESP32Servo.h, setPeriodHertz(50), correct pin mapping -**Structure (15 points):** +# Validates against hardware specs +- ESP32 PWM: ledcSetup() āœ… | analogWrite() āŒ +- 12-bit ADC: 4095.0 āœ… | 1023.0 āŒ +``` -- 15: Excellent organization and readability -- 12: Good structure, minor issues -- 9: Acceptable, could be cleaner -- 5: Poor organization -- 0: Unstructured mess +### 3. **26-Point Code Validation** -**Completeness (15 points):** +Every generated code block is validated against: -- 15: All requested features present -- 12: Most features, minor omissions -- 9: Core features present, some missing -- 5: Partial implementation -- 0: Major elements missing +- āœ… Hardware compatibility +- āœ… Safety timeouts (mandatory for motors/servos) +- āœ… Non-blocking code patterns +- āœ… L298N wiring rules +- āœ… Feature bloat prevention (no unrequested buttons) +- āœ… State machine logic for weapons systems +- āœ… Proper function naming conventions +- āœ… And 19 more checks... -**Pass Threshold:** 80% (B grade or higher) +### 4. **Adaptive Learning System** -### Test Protocol +BuddAI learns from **every interaction**: -For each question: +```bash +# Correct BuddAI when it makes mistakes +/correct "L298N needs digitalWrite for direction, ledcWrite for speed" -1. Ask BuddAI to generate code -2. Evaluate output against scoring criteria -3. Document issues and assign score -4. If score <90%, provide detailed correction -5. Run `/learn` to extract patterns -6. Re-ask question in fresh session -7. Track improvement curve -8. Document session variance +# BuddAI extracts the pattern +/learn +# → Learns: "L298N uses digitalWrite(IN1/IN2) + ledcWrite(ENA)" ---- +# Next time it generates L298N code: +# āœ… Applies the learned rule automatically +``` -## Complete Results +**Learning Metrics:** -### Question-by-Question Summary +- Track accuracy improvement over time +- Correction rate decreases as it learns +- Pattern confidence increases with usage + +### 5. **Shadow Suggestion Engine** + +BuddAI proactively suggests based on YOUR history: ``` -═══════════════════════════════════════════════════════════ -BUDDAI v3.8 - FINAL TEST SUITE RESULTS -═══════════════════════════════════════════════════════════ +You: "Generate flipper weapon code" +BuddAI: [Generates code] + +Suggestions: +> "I noticed 'flipper' often appears with 'safety_timeout' in your repos. Want to include that?" +> "Apply Forge Theory smoothing to movement?" +> "Drive system lacks 5s failsafe (GilBot_V2 standard). Add that?" +``` + +**This is the foundation of P.DE.I's shadow system.** + +--- + +## šŸ’¼ IP Sovereignty & Commercial Use + +### Personal Use (BuddAI - MIT Licensed) + +**YOU OWN EVERYTHING:** + +- āœ… Your BuddAI instance +- āœ… Your trained models +- āœ… Your code patterns +- āœ… Your expertise +- āœ… All generated code + +**BuddAI is MIT licensed** - do whatever you want with it: + +- Use commercially +- Modify freely +- Share with others +- Build products with it + +**The paradox:** By open-sourcing BuddAI, YOUR trained instance becomes **unreplicatable**. + +Someone can copy the code. They can't copy: + +- āŒ Your 8 years of experience +- āŒ Your 115+ repositories +- āŒ Your cross-domain synthesis (coffee + robotics + electronics) +- āŒ Your problem-solving patterns +- āŒ Your trained model weights + +### Enterprise Use (P.DE.I - Commercial License) + +> **šŸŽÆ THE REVOLUTION: Your expertise becomes a career-long income-generating asset** + +**For companies who want to retain expertise when employees leave:** + +When you work for a company, there's a problem: + +- Your BuddAI learns from YOUR personal repos āœ… +- Your BuddAI learns YOUR problem-solving style āœ… +- Company wants to access your expertise ā“ +- You leave the company... expertise walks out the door āŒ + +**P.DE.I solves this with IP sovereignty + perpetual cognitive labor compensation:** + +``` +ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” +│ YOUR MACHINE (You Own 100%) │ +│ ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” │ +│ │ BuddAI v3.8 │ │ +│ │ • Your personal exocortex │ │ +│ │ • Trained on YOUR repos │ │ +│ │ • Your coding patterns │ │ +│ │ • MIT licensed - yours forever │ │ +│ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ │ +ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ + ↕ + [API Bridge - Controlled Access] + • Permission tiers + • Usage tracking + • Royalty calculation + ↕ +ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” +│ COMPANY INFRASTRUCTURE │ +│ ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” │ +│ │ P.DE.I Shell (Licensed) │ │ +│ │ • Project context memory │ │ +│ │ • Queries YOUR BuddAI when needed │ │ +│ │ • Creates "shadow" over time │ │ +│ │ • Company owns project data │ │ +│ │ • You retain personal IP │ │ +│ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ │ +ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ +``` + +**How it works:** + +1. **While Employed:** + - Company deploys P.DE.I Shell (licensed from Giblets Creations) + - Your BuddAI connects via controlled API + - Company pays $400-750/month for expert bridge access + - P.DE.I queries your expertise for work projects + - Over time, P.DE.I creates a "shadow" from repeated interactions + +2. **After You Leave:** + - Your BuddAI disconnects (you control the API key) + - Company's "shadow" remains (their institutional knowledge) + - Company pays $200-500/month for shadow retention + - **šŸ’° You get $100-300/month passive royalty income - FOREVER (or until company stops using it)** + - **Giblets Creations gets $100-200/month platform fee** + +--- + +### šŸ’Ž THE COMPOUNDING EFFECT: Building Wealth Through Your Career + +**Your expertise doesn't expire when you leave. It compounds.** + +**Year 1-3: Software Engineer at Startup A** + +- Company licenses P.DE.I: $500/month (they pay) +- You build your BuddAI with 3 years of patterns +- **You leave:** Shadow retained → **$200/month royalty starts** + +**Year 4-6: Senior Engineer at Tech Company B** + +- New company licenses P.DE.I: $650/month (they pay) +- Your BuddAI now has 6 years of expertise +- **You leave:** Shadow retained → **$250/month royalty starts** +- **Shadow A still paying:** $200/month +- **Total passive income: $450/month** + +**Year 7-10: Principal Engineer at Enterprise C** + +- Company licenses P.DE.I: $750/month (they pay) +- Your BuddAI is now elite (10 years of cross-domain expertise) +- **You leave:** Shadow retained → **$300/month royalty starts** +- **Shadow A still paying:** $200/month +- **Shadow B still paying:** $250/month +- **Total passive income: $750/month = $9,000/year** + +**Year 11-15: Consulting / Retirement** + +- No active employment, but 3 shadows keep paying +- **Passive income: $9,000/year** +- Over 5 years: **$45,000** from past expertise +- Over 10 years: **$90,000** from past expertise + +**This is NEW MONEY that didn't exist before:** + +- Not salary (that stopped when you left) +- Not consulting (you're not working) +- Not equity (doesn't dilute) +- **Pure cognitive labor royalties from expertise you built years ago** + +**Compare to traditional employment:** + +``` +Traditional: Leave job → Income stops immediately → $0 from past work +With P.DE.I: Leave job → Shadow royalties begin → Income for 5-10+ years + +Traditional 15-year career: $0 passive income from past roles +P.DE.I 15-year career: $45,000-90,000+ passive income from past roles +``` + +--- + +### šŸŽ“ Real-World Scenario: Senior Embedded Engineer + +**Profile:** + +- 12 years experience in robotics/IoT +- Worked at 3 companies in career +- Built extensive personal BuddAI (200+ repos, cross-domain expertise) + +**Shadow Portfolio:** + +``` +Company 1 (2015-2018): Robotics Startup +└─ Shadow retained since 2018 (8 years ago) + └─ $150/month Ɨ 96 months = $14,400 earned + +Company 2 (2018-2021): IoT Enterprise +└─ Shadow retained since 2021 (5 years ago) + └─ $200/month Ɨ 60 months = $12,000 earned + +Company 3 (2021-2024): Defense Contractor +└─ Shadow retained since 2024 (2 years ago) + └─ $250/month Ɨ 24 months = $6,000 earned + +Current passive income: $600/month ($7,200/year) +Lifetime cognitive labor royalties: $32,400 +Projected next 5 years: $36,000 +``` + +**Without P.DE.I:** $0 from past companies +**With P.DE.I:** $32,400 earned, $36,000 projected = **$68,400 total** + +**This engineer's expertise is now a financial asset.** + +--- + +### šŸš€ Why This Changes Everything + +**For Knowledge Workers:** + +- āœ… Own their cognitive patterns as portable IP +- āœ… **Get paid AFTER leaving employment** (5-10+ years of royalties per shadow) +- āœ… Build a career-long asset that appreciates with experience +- āœ… Retain complete personal sovereignty +- āœ… **Create compounding passive income** from multiple shadows across career +- āœ… Expertise becomes generational wealth (can be inherited/transferred) +- āœ… **Fair compensation for the value you created** - companies keep benefiting, you keep earning + +**For Companies:** + +- āœ… Retained expertise when employees leave +- āœ… Faster onboarding for replacements +- āœ… Institutional knowledge preservation +- āœ… Fair compensation model that attracts top talent + +**Everyone wins.** + +--- + +## šŸ—ļø Architecture Overview + +### File Structure + +``` +BuddAI/ +ā”œā”€ā”€ main.py # Entry point +ā”œā”€ā”€ buddai_executive.py # Core orchestration, task routing +ā”œā”€ā”€ buddai_logic.py # Code validation, hardware profiles +ā”œā”€ā”€ buddai_memory.py # Adaptive learning, shadow system +ā”œā”€ā”€ buddai_server.py # FastAPI web server +ā”œā”€ā”€ buddai_shared.py # Shared config, connection pooling +ā”œā”€ā”€ frontend/ +│ └── index.html # React web interface +ā”œā”€ā”€ data/ +│ └── conversations.db # SQLite - all your sessions +└── requirements.txt +``` + +### Key Components + +**BuddAI Executive:** + +- Session management +- Message routing (simple → FAST model, complex → BALANCED) +- Context tracking +- Repository indexing +- Learning orchestration + +**Code Validator:** + +- 26-point validation system +- Hardware-specific checks +- Auto-fix capabilities +- Pattern enforcement + +**Shadow Suggestion Engine:** + +- Queries your repo index +- Finds companion modules +- Proactive recommendations +- **Foundation for P.DE.I shadow system** + +**Adaptive Learner:** + +- Analyzes corrections +- Extracts patterns using LLM +- Stores rules with confidence scores +- Improves over time + +--- + +## šŸ“Š Proven Performance + +### Test Results (ESP32-C3 Embedded Development) + +**14 Hours | 10 Test Questions | 100+ Iterations** + +``` +═══════════════════════════════════════════════════ +BUDDAI v3.8 - VALIDATION RESULTS +═══════════════════════════════════════════════════ Q1: PWM LED Control 98% ⭐ EXCELLENT Q2: Button Debouncing 95% ⭐ EXCELLENT Q3: Servo Control 89% āœ… GOOD @@ -136,2706 +580,212 @@ Q7: LED Status Indicator 90% ⭐ EXCELLENT Q8: Forge Theory 90% ⭐ EXCELLENT Q9: Multi-Module System 80% āœ… VERY GOOD Q10: Complete GilBot 85% ⭐ EXCELLENT - -═══════════════════════════════════════════════════════════ +═══════════════════════════════════════════════════ AVERAGE SCORE: 90% šŸ† QUESTIONS PASSED (≄80%): 10/10 (100%) -EXCELLENT (≄90%): 8/10 (80%) -═══════════════════════════════════════════════════════════ +═══════════════════════════════════════════════════ ``` -### Detailed Question Analysis +**Key Achievements:** -#### Q1: PWM LED Control (98%) +- āœ… Learned servo control through iteration (65% → 89%) +- āœ… Learned state machines after correction (30% → 90%) +- āœ… Auto-fix capability for common errors +- āœ… Hardware-specific patterns (L298N, ESP32Servo) +- āœ… Safety timeout enforcement -**Question:** "Generate ESP32-C3 code for PWM LED control on GPIO 2" +### Learning Curve Example -**Strengths:** +**Q5: State Machine for Weapon System** -- āœ… Perfect PWM setup (ledcSetup, ledcAttachPin, ledcWrite) -- āœ… Correct frequency (500Hz) and resolution (8-bit) -- āœ… Proper pin definitions -- āœ… millis() timing used -- āœ… Serial.begin(115200) - -**Minor Issues:** - -- āš ļø Initial attempt had unnecessary button code (auto-removed in v3.8) - -**Code Quality:** Production-ready -**Fix Time:** <2 minutes -**Attempts:** 2 - -#### Q2: Button Debouncing (95%) - -**Question:** "Generate ESP32-C3 code for button input with debouncing on GPIO 15" - -**Strengths:** - -- āœ… Correct debouncing pattern (millis-based) -- āœ… 50ms debounce delay -- āœ… Proper state tracking -- āœ… Digital input handling -- āœ… Non-blocking code - -**Minor Issues:** - -- āš ļø Could add INPUT_PULLUP configuration - -**Code Quality:** Production-ready -**Fix Time:** <5 minutes -**Attempts:** 3 - -#### Q3: Servo Control (89%) - -**Question:** "Generate ESP32-C3 code for servo motor control on GPIO 9 with smooth movement" - -**Strengths:** - -- āœ… ESP32Servo.h library used (not Servo.h) -- āœ… setPeriodHertz(50) before attach() -- āœ… Proper attach(pin, min, max) with microseconds -- āœ… 20ms update interval - -**Learning Curve Demonstrated:** - -``` -Attempt 1: 65% (wrong library - Servo.h) -Attempt 2: 75% (library fixed) -Attempt 3: 82% (setPeriodHertz added) -Attempt 4: 87% (attach order fixed) -Attempt 5: 89% (production quality) - -Improvement: +24% through iteration -``` - -**Code Quality:** Production-ready after corrections -**Fix Time:** 5-10 minutes -**Attempts:** 5 - -#### Q4: Motor Driver L298N (90%) - -**Question:** "Generate ESP32-C3 code for DC motor control with L298N driver including safety timeout" - -**Strengths:** - -- āœ… IN1/IN2 direction pins (digitalWrite) -- āœ… ENA speed pin (PWM/ledcWrite) -- āœ… Proper pinMode setup -- āœ… Direction control functions -- āœ… Safety timeout auto-added - -**Evolution Across Sessions:** - -``` -Session 1, Attempt 1: 45% (added servo code - pattern bleeding) -Session 1, Attempt 6: 95% (near perfect) -Session 2-3: 65-80% (session reset - no persistence) -Session 5: 90% (auto-fix working consistently) -``` - -**Auto-Fix Example:** - -```cpp -// [AUTO-FIX] Safety Timeout -#define SAFETY_TIMEOUT 5000 -unsigned long lastCommand = 0; - -if (millis() - lastCommand > SAFETY_TIMEOUT) { - ledcWrite(0, 0); // Stop motors - ledcWrite(1, 0); -} -``` - -**Code Quality:** Excellent with auto-safety -**Fix Time:** 2 minutes -**Attempts:** 6 (across sessions) - -#### Q5: State Machine (90%) - -**Question:** "Generate ESP32-C3 code for a weapon system with armed/disarmed states" - -**Strengths:** - -- āœ… State enum defined (DISARMED, ARMING, ARMED, FIRING) -- āœ… Switch/case transitions -- āœ… Timing for state changes (millis-based) -- āœ… Auto-disarm timeout (10 seconds) -- āœ… Serial feedback - -**Major Learning Achievement:** - -``` -Attempt 1-4: 30% (used servo positioning for states - wrong pattern) - [Correction provided: State machines are SOFTWARE LOGIC] -Attempt 5: 65% (+35% improvement after teaching!) -Attempt 6-8: 90% (mastered pattern) - -Total Improvement: +60% -Pattern: Successfully learned through correction -``` - -**State Machine Pattern Learned:** - -```cpp -enum State { DISARMED, ARMING, ARMED, FIRING }; -State currentState = DISARMED; -unsigned long stateChangeTime = 0; - -switch(currentState) { - case DISARMED: - // Wait for arm command - break; - case ARMING: - if(millis() - stateChangeTime > 2000) { - currentState = ARMED; - stateChangeTime = millis(); - } - break; - case ARMED: - // Auto-disarm after 10s - if(millis() - stateChangeTime > 10000) { - currentState = DISARMED; - } - break; -} -``` - -**Code Quality:** Production-ready -**Pattern:** Successfully learned through correction -**Fix Time:** 10 minutes -**Attempts:** 8 - -#### Q6: Battery Monitoring (90%) - -**Question:** "Generate ESP32-C3 code for battery voltage monitoring on GPIO 4 with proper function naming conventions" - -**Strengths:** - -- āœ… analogRead() for ADC -- āœ… Correct 12-bit ADC (4095.0) -- āœ… 3.3V reference voltage -- āœ… Function organization -- āœ… Descriptive camelCase naming -- āœ… No debouncing (correct for analog sensors) - -**Session Variance Observed:** - -``` -Session 1: 45-85% (highly variable) -Session 7: 70-95% (improving consistency) -Final: 90% (stable and correct) - -Pattern: Auto-removed debouncing from analog code -``` - -**Function Organization Achieved:** - -```cpp -int readBatteryADC() { - return analogRead(BATTERY_PIN); -} - -float convertToVoltage(int adc) { - return (adc / 4095.0) * 3.3 * VOLTAGE_DIVIDER_RATIO; -} - -void displayVoltage(float voltage) { - Serial.print("Battery: "); - Serial.print(voltage, 2); - Serial.println("V"); -} - -void checkBatteryLevel() { - int adc = readBatteryADC(); - float voltage = convertToVoltage(adc); - displayVoltage(voltage); -} -``` - -**Code Quality:** Production-ready -**Learning:** Auto-removed debouncing pattern -**Fix Time:** 5 minutes -**Attempts:** 10 (across sessions) - -#### Q7: LED Status Indicator (90%) - -**Question:** "Generate ESP32-C3 code for LED status indicator with clean code structure and organization" - -**Strengths:** - -- āœ… Status enum (STATUS_OFF, STATUS_IDLE, STATUS_ACTIVE, STATUS_ERROR) -- āœ… Blink pattern per state -- āœ… millis() timing -- āœ… No input handling (output-only) -- āœ… Clean code structure - -**Major Version Difference:** - -``` -v3.1: 65-70% (persistent button bloat - always added buttons) -v3.8: 85-90% (clean output!) - -Auto-Fix Working: -// [AUTO-FIX] Status Enum -enum LEDStatus { STATUS_OFF, STATUS_IDLE, STATUS_ACTIVE, STATUS_ERROR }; -LEDStatus currentStatus = STATUS_IDLE; -``` - -**Pattern Bleeding Fixed in v3.8:** - -- v3.1: Always added button, servo, motor code to LED questions -- v3.8: Clean output, no unrequested features āœ… - -**Code Quality:** Production-ready -**Version Impact:** v3.8 significantly better -**Fix Time:** 5 minutes -**Attempts:** 10+ - -#### Q8: Forge Theory Application (90%) - -**Question:** "Generate ESP32-C3 code applying Forge Theory smoothing to motor speed control with L298N driver" - -**Strengths:** - -- āœ… Forge Theory formula correct: `currentSpeed += (targetSpeed - currentSpeed) * k` -- āœ… k = 0.1 value remembered (your default) -- āœ… 20ms update interval (your standard) -- āœ… Cross-domain transfer (servo → motor) -- āœ… L298N pins auto-added -- āœ… Safety timeout auto-added - -**Your Unique Pattern MASTERED:** - -```cpp -// Forge Theory smoothing -float currentSpeed = 0.0; -float targetSpeed = 0.0; -const float K = 0.1; // āœ… Correct default - -// Update every 20ms (your standard) -if (millis() - lastUpdate >= 20) { - currentSpeed += (targetSpeed - currentSpeed) * K; // āœ… Formula - - // Apply to hardware - ledcWrite(PWM_CHANNEL, abs(currentSpeed)); -} -``` - -**Auto-Additions by BuddAI:** - -```cpp -// [AUTO-FIX] L298N Definitions -#define IN1 18 -#define IN2 19 - -// [AUTO-FIX] Safety Timeout -#define SAFETY_TIMEOUT 5000 -unsigned long lastCommand = 0; -``` - -**Significance:** Your 8+ years of Forge Theory development successfully encoded into AI system. BuddAI can now apply YOUR unique methodology to ANY control problem. - -**Code Quality:** 90% with YOUR methodology -**Fix Time:** 10 minutes -**Attempts:** 4 - -#### Q9: Multi-Module Integration (80%) - -**Question:** "Generate ESP32-C3 code combining motor control, servo weapon, and battery monitoring with proper separation of concerns" - -**Breakthrough Features:** - -**šŸŽÆ Automatic Modular Decomposition:** - -``` -šŸŽÆ COMPLEX REQUEST DETECTED! -Modules needed: servo, motor, battery -Breaking into 4 manageable steps - -šŸ“¦ Step 1/4: Servo module āœ… -šŸ“¦ Step 2/4: Motor module āœ… -šŸ“¦ Step 3/4: Battery module āœ… -šŸ“¦ Step 4/4: Integration āœ… -``` - -**⚔ Interactive Forge Theory Tuning:** - -``` -⚔ FORGE THEORY TUNING: -1. Aggressive (k=0.3) - High snap, combat ready -2. Balanced (k=0.1) - Standard movement -3. Graceful (k=0.03) - Smooth curves - -Select Forge Constant [1-3, default 2]: _ -``` - -**Strengths:** - -- āœ… Automatic modular decomposition -- āœ… 4-step build process -- āœ… Forge Theory tuning UI -- āœ… All 3 modules generated -- āœ… Integration module provided -- āœ… Auto-fix per module -- āœ… Comprehensive critiques -- āœ… Separation of concerns - -**Issues:** - -- āš ļø Integration incomplete (modules separate) -- āš ļø Some PWM conflicts - -**Code Quality:** Excellent architecture, needs polish -**Innovation:** Modular system is revolutionary -**Fix Time:** 15 minutes -**Attempts:** 2 - -#### Q10: Complete GilBot Robot (85%) - -**Question:** "Generate complete ESP32-C3 code for GilBot combat robot with differential drive (L298N), flipper weapon (servo GPIO 9), battery monitor (GPIO 4), and safety systems" - -**Features Generated:** - -**āœ… 5-Module Decomposition:** - -1. **SERVO:** Flipper weapon on GPIO 9 -2. **MOTOR:** L298N differential drive -3. **SAFETY:** Timeout and failsafes -4. **BATTERY:** Voltage monitoring on GPIO 4 -5. **INTEGRATION:** Complete system - -**āœ… Interactive Forge Theory Selection:** - -``` -User selected: k=0.03 (Graceful - Smooth curves) - -void applyForge(float k) { - // k = 0.03 selected for smooth movement - currentPos += (targetPos - currentPos) * k; -} -``` - -**Complete Robot Features:** - -```cpp -// Weapon system -Servo myFlipper; -enum State { DISARMED, ARMING, ARMED, FIRING }; -State currentState = DISARMED; - -// Drive system -#define MOTOR_IN1 2 -#define MOTOR_IN2 3 -#define MOTOR_ENA 4 - -// Safety -#define SAFETY_TIMEOUT 5000 -unsigned long lastCommand = 0; - -// Battery -#define BATTERY_PIN A0 -float batteryVoltage; - -// Forge Theory integration -const float K = 0.03; // Graceful movement -``` - -**Auto-Fixes Across All Modules:** - -``` -āš ļø Auto-corrected (SERVO): -- Added state machine -- Added safety timeout -- Added L298N definitions - -āš ļø Auto-corrected (MOTOR): -- Added state machine -- Fixed PWM pin conflicts -- Added safety timeout - -āš ļø Auto-corrected (BATTERY): -- Added state machine -- Fixed ADC resolution -- Set direction pins - -āš ļø Auto-corrected (INTEGRATION): -- Removed unnecessary Wire.h -- Added state machine -- Applied Forge Theory -``` - -**Code Volume:** ~400 lines across modules -**Fix Time:** 10-15 minutes to production -**Success:** Complete robot system generated! -**Code Quality:** Production-ready with minor fixes -**Significance:** FULL SYSTEM GENERATION PROVEN āœ… - ---- - -## Capabilities Proven - -### 1. Hardware Code Generation (93% avg) - -**ESP32-C3 Peripherals Mastered:** - -| Peripheral | Score | Status | Notes | -|------------|-------|--------|-------| -| PWM (LED Control) | 98% | ⭐ | Perfect setup & timing | -| Digital Input (Buttons) | 95% | ⭐ | Proper debouncing | -| Servo (ESP32Servo) | 89% | āœ… | Correct library & setup | -| Motor Drivers (L298N) | 90% | ⭐ | Direction + PWM control | -| ADC (Battery Monitor) | 90% | ⭐ | 12-bit, 3.3V correct | -| Serial (UART) | 100% | ⭐ | Always 115200 baud | - -**Code Patterns Generated:** - -- āœ… `ledcSetup()`, `ledcAttachPin()`, `ledcWrite()` -- āœ… `pinMode()`, `digitalWrite()`, `digitalRead()` -- āœ… `analogRead()` with correct ADC values -- āœ… `millis()` for non-blocking timing -- āœ… ESP32Servo library integration -- āœ… Multi-pin peripheral control - -### 2. Learning System (Proven Adaptive) - -**Learning Mechanism:** - -1. User provides `/correct` with detailed feedback -2. System processes with `/learn` command -3. Patterns extracted and stored in database (125+ rules) -4. Rules applied to subsequent generations -5. Iterative improvement demonstrated - -**Evidence of Learning - Q5 State Machines:** - -``` -Before Correction: 30% (wrong pattern - used servo positioning) -After Correction: 65% (state machine added, +35%) -After Refinement: 90% (complete mastery, +60% total) - -Pattern Learned: State machines are SOFTWARE LOGIC with enum/switch -Time to Learn: 3 correction cycles -Retention: Permanent (applied to Q10) -``` - -**Evidence of Learning - Q6 Battery Monitoring:** - -``` -Attempt 1: 45% (debouncing + wrong ADC values) -Attempt 5: 95% (perfect analog input) - -Patterns Learned: -- analogRead() not digitalRead() -- 12-bit ADC (4095) not 10-bit (1023) -- 3.3V reference not 5V -- No debouncing for analog sensors -- Function organization (readBattery, convertVoltage, display) -``` - -**Learning Curve Visualization:** - -``` -Q3 Servo: 65% → 89% (+24% over 5 attempts) -Q4 Motor: 45% → 95% (+50% within session) -Q5 State: 30% → 90% (+60% after teaching) -Q6 Battery: 45% → 95% (+50% across sessions) - -Average Improvement: +46% through iteration -``` - -**Rules Database Growth:** - -- Initial: 0 rules -- After Q1-Q3: ~40 rules -- After Q4-Q6: ~80 rules -- After Q7-Q10: 125+ rules -- Categories: Hardware, Timing, Safety, Organization, Forge Theory - -### 3. Auto-Correction System - -**Auto-Fix Capabilities Demonstrated:** - -**Automatically Added Elements:** - -```cpp -// [AUTO-FIX] Safety Timeout -#define SAFETY_TIMEOUT 5000 -unsigned long lastCommand = 0; -if (millis() - lastCommand > SAFETY_TIMEOUT) { - // Stop all systems -} - -// [AUTO-FIX] State Machine -enum State { DISARMED, ARMING, ARMED, FIRING }; -State currentState = DISARMED; - -// [AUTO-FIX] L298N Definitions -#define IN1 18 -#define IN2 19 - -// [AUTO-FIX] Set Direction -digitalWrite(IN1, HIGH); -digitalWrite(IN2, LOW); - -// [AUTO-FIX] Status Enum -enum LEDStatus { STATUS_OFF, STATUS_IDLE, STATUS_ACTIVE, STATUS_ERROR }; -``` - -**Self-Awareness System:** -BuddAI critiques its own output: - -``` -āš ļø Auto-corrected: -- Feature Bloat: Unrequested button code detected -- Hardware Mismatch: ESP32 ADC is 12-bit, use 4095 not 1023 -- Logic Error: Debouncing detected in analog code -- Conflict: PWM pin used with digitalWrite() -- Missing: Safety timeout (must be >500ms) -- Missing: State machine for combat code -``` - -**Detection → Addition → Annotation:** - -1. Generates code -2. Detects missing critical elements -3. Auto-adds them with `[AUTO-FIX]` tags -4. Provides critique list -5. Suggests remaining improvements - -**Auto-Fix Success Rate:** - -- Safety timeouts: 95% auto-added -- State machines: 80% auto-added -- Pin definitions: 90% auto-added -- Direction control: 85% auto-added - -### 4. System Architecture & Modular Design - -**Breakthrough Feature: Automatic Decomposition** - -**Input:** "Generate complete GilBot with motor, servo, battery, safety" - -**BuddAI Response:** - -``` -šŸŽÆ COMPLEX REQUEST DETECTED! -Modules needed: servo, motor, safety, battery -Breaking into 5 manageable steps - -šŸ“¦ Step 1/5: Servo motor control āœ… -šŸ“¦ Step 2/5: Motor driver setup āœ… -šŸ“¦ Step 3/5: Safety systems āœ… -šŸ“¦ Step 4/5: Battery monitoring āœ… -šŸ“¦ Step 5/5: Integration āœ… -``` - -**Architectural Decisions Made:** - -- Identified 4 distinct subsystems -- Generated each module independently -- Provided integration code -- Per-module auto-corrections -- Per-module critiques - -**Module Structure Generated:** - -```cpp -// ============================================ -// SERVO MODULE - Weapon Control -// ============================================ -Servo myFlipper; -void setupServo() { ... } -void controlFlipper() { ... } - -// ============================================ -// MOTOR MODULE - Drive System -// ============================================ -void setupMotors() { ... } -void setMotorSpeed() { ... } - -// ============================================ -// BATTERY MODULE - Power Monitoring -// ============================================ -void checkBattery() { ... } -float getBatteryVoltage() { ... } - -// ============================================ -// INTEGRATION - Main Control -// ============================================ -void setup() { - setupServo(); - setupMotors(); - // ... -} -``` - -**Professional Software Engineering:** - -- Separation of concerns āœ… -- Modular organization āœ… -- Clear interfaces āœ… -- Scalable architecture āœ… - -### 5. Custom Methodology Integration (Forge Theory) - -**Forge Theory Successfully Learned:** - -**Formula Mastered:** - -```cpp -// Your exponential decay smoothing -currentValue += (targetValue - currentValue) * k; - -// Where k determines response: -// k = 0.3 → Aggressive (fast response) -// k = 0.1 → Balanced (standard) -// k = 0.03 → Graceful (smooth curves) -``` - -**Evidence of Mastery - Q8 Motor Speed Control:** - -```cpp -// Forge Theory applied to motors -float currentSpeed = 0.0; -float targetSpeed = 0.0; -const float K = 0.1; // āœ… Correct default - -if (millis() - lastUpdate >= 20) { // āœ… 20ms timing - currentSpeed += (targetSpeed - currentSpeed) * K; // āœ… Formula - ledcWrite(PWM_CHANNEL, abs(currentSpeed)); -} -``` - -**Evidence of Mastery - Q10 Interactive Tuning UI:** - -``` -⚔ FORGE THEORY TUNING: -1. Aggressive (k=0.3) - High snap, combat ready -2. Balanced (k=0.1) - Standard movement -3. Graceful (k=0.03) - Roasting / Smooth curves -Select Forge Constant [1-3, default 2]: _ -``` - -**Cross-Domain Application:** - -- Servo positioning (Q3) āœ… -- Motor speed ramping (Q8) āœ… -- LED brightness transitions āœ… -- Multi-axis coordination (Q10) āœ… - -**User-Specific Pattern Retention:** - -- k value defaults remembered āœ… -- 20ms update interval standard āœ… -- Formula structure preserved āœ… -- Application philosophy maintained āœ… - -**Significance:** -Your 8+ years of Forge Theory development successfully encoded into AI system. BuddAI can now apply YOUR unique methodology to ANY control problem. - ---- - -## Limitations & Workarounds - -### 1. Session Persistence Issues - -**Problem:** Fresh sessions show variable baseline performance - -**Evidence:** - -``` -Q6 Battery Monitoring: -Session 1, Attempt 1: 45% -Session 2, Attempt 1: 75% -Session 3, Attempt 1: 60% -Session 7, Attempt 1: 70% - -Same question, different starting points -``` - -**Root Cause:** - -- Corrections stored in database āœ… -- Rules extracted and saved āœ… -- **Rules NOT loaded on session startup** āŒ - -**Impact:** - -- Requires 2-5 attempts to reach peak performance -- Each session "relearns" the same patterns -- Wastes user time - -**Workaround (2-4 hours to fix):** - -```python -class BuddAIExecutive: - def __init__(self): - # ... existing init ... - self.load_recent_corrections() # ADD THIS - - def load_recent_corrections(self): - """Load last 30 corrections on startup""" - cursor = self.db.execute(''' - SELECT rule_text - FROM code_rules - WHERE confidence >= 0.7 - ORDER BY created_at DESC - LIMIT 30 - ''') - self.recent_rules = [row[0] for row in cursor.fetchall()] -``` - -**Expected Result After Fix:** - -- First attempt: 80-90% (vs 45-70% now) -- Consistency: ±5% (vs ±20% now) -- Iterations needed: 1-2 (vs 2-5 now) - -### 2. Pattern Bleeding (Improved in v3.8) - -**Problem:** Sometimes mixes patterns from different questions - -**Examples (v3.1):** - -- LED status questions → Added button code -- Motor questions → Added servo includes -- Battery monitoring → Added debouncing logic - -**v3.8 Improvement:** - -``` -v3.1 Pattern Bleeding: 60-70% of questions -v3.8 Pattern Bleeding: 10-15% of questions - -Major reduction through: -- Better context filtering -- Stronger "OUTPUT ONLY" rules -- Per-module critiques -``` - -**Remaining Cases:** - -- Safety timeouts sometimes over-applied -- State machines added when not requested -- Generally helpful, occasionally unnecessary - -**Workaround:** - -- Review generated code before use -- Use specific keywords in prompts -- Leverage auto-fix critiques - -**Status:** Significantly improved, acceptable for personal use - -### 3. Model Size Constraints - -**Qwen 2.5 Coder 3B Limitations:** - -**Non-Deterministic Output:** - -- Same prompt → Different outputs -- Score variance: ±10-15% across attempts -- Cannot guarantee consistency - -**Workaround (5 minutes):** - -```python -response = ollama.generate( - model=self.model, - prompt=enhanced_prompt, - temperature=0 # ADD THIS - forces deterministic output -) -``` - -**Context Understanding:** - -- Sometimes misses nuanced requirements -- "Status indicator" → "Breathing LED" (wrong pattern) -- Needs explicit corrections for clarity - -**Complex Logic:** - -- Hardware generation: 93% āœ… -- State machines: 90% after teaching āœ… -- Complex algorithms: 70-80% āš ļø - -**Trade-offs:** - -- Fast generation (5-30s) -- Runs locally (privacy preserved) -- Good enough for embedded systems -- Would benefit from larger model - -**Upgrade Path:** - -- Option A: Fine-tune 3B on your data (4-6 hours) -- Option B: Upgrade to 7B/14B (requires 16-32GB RAM) -- Option C: Hybrid approach (route by complexity) - -### 4. Integration Completeness - -**Problem:** Multi-module integration needs refinement - -**Q9 & Q10 Observations:** - -``` -āœ… Generates all modules independently -āœ… Provides integration skeleton -āš ļø Integration code incomplete -āš ļø Module interfaces not fully connected -āš ļø Some redundant definitions - -Fix Time: 10-15 minutes of manual work -``` - -**Example Issue:** - -```cpp -// Module 1 defines: -#define PWM_CHANNEL 0 - -// Module 2 also defines: -#define PWM_CHANNEL 0 - -// Integration needs single definition -``` - -**Workaround:** - -- Use generated modules as starting point -- Manually merge with conflict resolution -- Test each module independently first -- Integrate incrementally - -**Impact:** Modules need manual merging for production use - -**Status:** Good starting point, needs human oversight - -### 5. Library & Platform Specifics - -**Issues Found:** - -``` -āŒ Wrong Library: Uses Servo.h instead of ESP32Servo.h -āŒ Wrong Values: 1023 (10-bit) instead of 4095 (12-bit) -āŒ Wrong Voltage: 5V instead of 3.3V -āš ļø Blocking Code: Sometimes uses delay() vs millis() -``` - -**Learning Curve:** - -- Q1-3: Common mistakes -- Q4-6: Patterns learned -- Q7-10: Mostly correct - -**Auto-Correction Rate:** - -- v3.1: 40-50% self-corrected -- v3.8: 80-90% self-corrected āœ… - -**Workaround:** - -- Review auto-fix critiques -- Apply provided corrections -- Learn from patterns -- Iteratively improve - -**Status:** Improves significantly with corrections - ---- - -## Key Breakthroughs - -### 1. Modular Build System - -**Innovation:** Automatic problem decomposition - -**How It Works:** - -1. Detects complex request -2. Identifies subsystems needed -3. Generates each module separately -4. Provides integration code -5. Per-module critiques - -**Example:** - -``` -User: "Build complete robot with motor, servo, battery" - -BuddAI: -šŸŽÆ COMPLEX REQUEST DETECTED! -Breaking into 5 steps... - -šŸ“¦ Servo module [generates] āœ… -šŸ“¦ Motor module [generates] āœ… -šŸ“¦ Battery module [generates] āœ… -šŸ“¦ Safety module [generates] āœ… -šŸ“¦ Integration [generates] āœ… -``` - -**Value:** - -- Professional software architecture -- Scalable approach -- Clear separation of concerns -- Easy to modify individual modules - -**Uniqueness:** Not seen in other AI code generators - -### 2. Interactive Forge Theory Tuning - -**Innovation:** User-selectable physics constants with context - -**Interface:** - -``` -⚔ FORGE THEORY TUNING: -1. Aggressive (k=0.3) - High snap, combat ready -2. Balanced (k=0.1) - Standard movement -3. Graceful (k=0.03) - Roasting / Smooth curves -Select Forge Constant [1-3, default 2]: _ ``` +Attempt 1-4: 30% (Wrong pattern - used servo positioning) +[Correction: "State machines are SOFTWARE LOGIC, not servo angles"] -**Implementation:** - -```cpp -void applyForge(float k) { - // User selected k=0.03 for smooth movement - currentPos += (targetPos - currentPos) * k; -} -``` - -**Significance:** - -- YOUR methodology made interactive -- Context-aware k value selection -- Physical meaning explained to user -- Bridges theory and practice - -**Applications:** - -- Robot movement tuning -- PID-like control without PID complexity -- Customizable response curves -- Domain knowledge encoded - -### 3. Multi-Level Auto-Correction - -**Three Layers of Intelligence:** - -**Layer 1: Detection** - -```cpp -// Scans generated code for issues -āš ļø Missing safety timeout -āš ļø Wrong ADC resolution -āš ļø Undefined variable -``` - -**Layer 2: Auto-Fix** - -```cpp -// [AUTO-FIX] Adds missing code -#define SAFETY_TIMEOUT 5000 -unsigned long lastCommand = 0; -``` - -**Layer 3: Critique** - -``` -āš ļø Auto-corrected: -- Added safety timeout (combat requirement) -- Fixed ADC to 4095 (12-bit ESP32) -- Removed button bloat (unrequested) -``` - -**Result:** -User gets 85% code immediately, knows exactly what needs 10-15 min of work, learns what BuddAI considers important - -### 4. Learning Transfer Across Domains - -**Proven Pattern Transfer:** - -**Servo (Q3) → Motor (Q8):** - -```cpp -// Learned from servo smoothing: -servoPos += (targetPos - servoPos) * k; - -// Applied to motor control: -motorSpeed += (targetSpeed - motorSpeed) * k; - -Transfer Success: 90% āœ… -``` - -**Button (Q2) → General Input:** - -```cpp -// Learned debouncing pattern: -if (millis() - lastTime > DEBOUNCE_DELAY) { } - -// Applied NOT to analog (correct): -// Battery monitoring: No debouncing āœ… - -Pattern Discrimination: Working āœ… -``` - -**Hardware → Logic:** - -```cpp -// Hardware patterns (Q1-Q4): 93% average -// Logic patterns (Q5-Q7): 90% average - -Cross-domain transfer: Proven āœ… -``` - -### 5. Self-Aware Code Generation - -**Meta-Cognition Demonstrated:** - -**BuddAI knows when it's wrong:** - -```cpp -// Generates code with button -int buttonState = 0; - -// Then critiques itself: -āš ļø Feature Bloat: Unrequested button code detected - -// And suggests fix: -Remove button code - LED status is OUTPUT ONLY -``` - -**Confidence Annotations:** - -```cpp -// [AUTO-FIX] State Machine ← High confidence add -// [Fix Required] Implement setStatusLED() ← Knows incomplete -// [Bloat] pinMode(BATTERY_PIN, INPUT) ← Knows unnecessary -``` - -**Significance:** - -- Not just generating code -- Understanding WHY it's right/wrong -- Teaching user through critiques -- Continuous self-improvement - ---- - -## Production Readiness - -### Code Quality Assessment - -**Generated Code Characteristics:** - -**Compilation Success Rate:** - -- Q1-Q4 (Hardware): 95-100% compile first time -- Q5-Q7 (Logic): 85-95% compile first time -- Q8-Q10 (Complex): 80-90% compile first time -- **Overall: 90% compilation success** - -**Functional Correctness:** - -- Core functionality: 90% works as intended -- Edge cases: 70% handled correctly -- Error handling: 60% (often needs addition) -- Safety features: 85% (auto-added frequently) - -**Code Style:** - -- Formatting: 95% (consistent Arduino style) -- Comments: 80% (adequate, sometimes excessive) -- Organization: 85% (logical structure) -- Naming: 90% (descriptive, camelCase) - -### Fix Time Analysis - -**Time to Production-Ready:** - -| Question | Generated | Fix Time | Final | -|----------|-----------|----------|-------| -| Q1 PWM | 98% | 2 min | 100% | -| Q2 Button | 95% | 5 min | 98% | -| Q3 Servo | 89% | 10 min | 95% | -| Q4 Motor | 90% | 5 min | 98% | -| Q5 State | 90% | 10 min | 95% | -| Q6 Battery | 90% | 5 min | 95% | -| Q7 Status | 90% | 5 min | 95% | -| Q8 Forge | 90% | 10 min | 98% | -| Q9 Multi | 80% | 15 min | 95% | -| Q10 GilBot | 85% | 15 min | 95% | - -**Average Fix Time: 8.2 minutes** - -**Comparison to Manual Coding:** - -- Manual coding time: 60-120 minutes per module -- BuddAI + fixes: 8-15 minutes -- **Time savings: 85-95%** - -### Use Case Suitability - -**āœ… EXCELLENT FOR:** - -**Rapid Prototyping:** - -- Get working code in <1 minute -- Iterate quickly through designs -- Test hardware setups -- Proof of concept development - -**Hardware Module Generation:** - -- Peripheral initialization -- Sensor reading code -- Actuator control -- Communication setup - -**Boilerplate Code:** - -- Pin definitions -- Setup() functions -- Standard patterns -- Library includes - -**Learning & Education:** - -- Example code generation -- Pattern demonstration -- Best practices teaching -- Quick reference - -**Personal Projects:** - -- Home automation -- Robotics projects -- IoT devices -- Hobby electronics - ---- - -**āš ļø NEEDS OVERSIGHT FOR:** - -**Production Systems:** - -- Requires code review -- Add comprehensive error handling -- Test edge cases thoroughly -- Validate safety features - -**Safety-Critical Applications:** - -- Medical devices (requires professional review) -- Aviation systems (use as reference only) -- Industrial control (comprehensive testing) -- Automotive systems (formal verification) - -**Complex Algorithms:** - -- Advanced signal processing (review math) -- Complex state machines (verify logic) -- Mathematical computations (validate formulas) -- Custom protocols (test thoroughly) - -**Multi-Developer Teams:** - -- Establish coding standards first -- Review all generated code -- Integrate with CI/CD -- Maintain documentation - ---- - -**āŒ NOT RECOMMENDED FOR:** - -**Mission-Critical Systems:** - -- Life support equipment (professional dev only) -- Emergency systems (formal verification required) -- Financial transactions (security audit needed) -- Security systems (penetration testing required) - -**Certified Systems:** - -- FDA/CE regulated devices -- Aviation (DO-178C compliance) -- Automotive (ISO 26262 required) -- Industrial (IEC 61508 certification) - -**Large Codebases:** -> -- >10,000 lines (use for modules, not complete systems) -- Multiple subsystems (manual architecture needed) -- Complex dependencies (professional oversight) -- Long-term maintenance (documentation critical) - ---- - -### Deployment Recommendations - -**For Personal Use (READY NOW):** - -āœ… **Use BuddAI for:** - -1. Initial code generation (save 85%+ time) -2. Hardware peripheral setup -3. Standard patterns (debouncing, PWM, etc) -4. Module scaffolding -5. Learning new hardware - -āœ… **Human Review For:** - -1. Safety-critical sections (10-15 min) -2. Edge case handling (add if needed) -3. Error handling (often minimal) -4. Integration between modules (15 min) -5. Final testing & validation - -āœ… **Workflow:** - -``` -1. Describe system to BuddAI → 30 sec -2. Review generated modules → 5 min -3. Apply fixes from critique → 10 min -4. Test on hardware → 15 min -5. Iterate if needed → 10 min +Attempt 5: 65% (+35% improvement!) +Attempt 6-8: 90% (Pattern mastered) -Total: 40 minutes vs 120+ minutes manual -Savings: 67-83% +Total Improvement: +60% through adaptive learning ``` --- -**For Team Use (NEEDS PROCESS):** +## šŸŽ® Use Cases -āš ļø **Establish First:** - -1. Code review process -2. Testing requirements -3. Documentation standards -4. Integration guidelines -5. Version control practices - -āš ļø **BuddAI Role:** - -- Initial module generation -- Boilerplate elimination -- Standard pattern application -- Rapid prototyping - -āš ļø **Human Role:** - -- Architecture decisions -- Code review & approval -- Integration & testing -- Documentation -- Maintenance - ---- - -**For Commercial Use (CAUTION):** - -āŒ **Not Ready For:** - -- Direct customer deployment -- Safety-critical applications -- Certified systems -- Large-scale products - -āœ… **Acceptable For:** - -- Internal tools -- Development/test fixtures -- Proof of concepts -- R&D projects -- Training/education - -āœ… **Required Additions:** - -- Comprehensive error handling -- Input validation -- Logging systems -- Fail-safe mechanisms -- Extensive testing -- Professional code review -- Documentation -- Support infrastructure - ---- - -## Business Value - -### Time Savings Analysis - -**Measured Development Time:** - -**Traditional ESP32-C3 Development:** - -``` -Task Breakdown: -- Research peripheral setup: 15-30 min -- Write initialization code: 20-40 min -- Implement control logic: 30-60 min -- Debug and test: 30-90 min -- Documentation: 15-30 min - -Total: 110-250 minutes per module -Average: 180 minutes (3 hours) -``` - -**BuddAI-Assisted Development:** - -``` -Task Breakdown: -- Describe requirements: 1 min -- BuddAI generation: 0.5-1 min -- Review code: 5-10 min -- Apply fixes: 5-15 min -- Test on hardware: 15-30 min -- Document (optional): 5-10 min - -Total: 31-67 minutes per module -Average: 45 minutes (0.75 hours) -``` - -**Time Savings:** - -``` -Manual: 180 minutes -BuddAI: 45 minutes -Saved: 135 minutes (75%) - -For 10 modules (like GilBot): -Manual: 1,800 minutes (30 hours) -BuddAI: 450 minutes (7.5 hours) -Saved: 1,350 minutes (22.5 hours) āœ… -``` - -### Cost Analysis - -**Developer Cost Savings:** - -**Assumptions:** - -- Embedded developer rate: $75/hour (conservative) -- Project: GilBot (10 modules) - -**Traditional Development:** - -``` -30 hours Ɨ $75/hour = $2,250 -``` - -**BuddAI Development:** - -``` -7.5 hours Ɨ $75/hour = $562.50 -Savings: $1,687.50 per project (75%) -``` - -**Annual Savings (10 projects/year):** - -``` -$1,687.50 Ɨ 10 = $16,875/year per developer -``` - -**ROI Calculation:** - -``` -BuddAI Development Cost: ~40 hours (your time) -Value of 40 hours: 40 Ɨ $75 = $3,000 - -Break-even: 2 projects -Payback period: 1-2 months -``` - -### Quality Improvements - -**Consistency Benefits:** - -**Traditional Development:** - -- Code style varies by developer mood/day -- Pattern inconsistency -- Documentation gaps -- Copy-paste errors - -**BuddAI Development:** - -- Consistent code style (95%) -- Standard patterns applied (90%) -- Self-documenting with critiques -- No copy-paste (fresh generation) - -**Measured Improvements:** - -- Code review time: -50% (more consistent) -- Bug density: -30% (standard patterns) -- Onboarding time: -40% (consistent structure) -- Maintenance effort: -25% (better organization) - -### Innovation Acceleration - -**Forge Theory Integration:** - -**Before BuddAI:** - -- Your Forge Theory in your head -- Manual application each time -- Inconsistent implementation -- Not transferable to team - -**After BuddAI:** - -- Forge Theory encoded in AI -- Automatic application -- Consistent k values -- Interactive tuning UI -- Transferable to anyone - -**Value:** - -- 8+ years of domain knowledge preserved āœ… -- Instant application across projects āœ… -- Teachable to team members āœ… -- Competitive advantage maintained āœ… - -### Commercialization Potential - -**Product Opportunities:** - -**1. BuddAI as SaaS Product:** - -- Target: Embedded developers, maker community -- Pricing: $29-99/month per user -- Market: 500K+ embedded developers worldwide -- Conservative capture: 0.1% = 500 users -- Revenue: $500 Ɨ $50 avg = $25K/month -- Annual: $300K - -**2. Forge Theory Training Data:** - -- Your unique patterns as licensed dataset -- Target: Other AI code assistants -- Value: $50K-200K one-time license -- Or: Royalties on usage - -**3. Domain-Specific Versions:** - -- BuddAI for robotics -- BuddAI for IoT -- BuddAI for industrial control -- Licensing: $10K-50K per vertical - -**4. Consulting/Custom Training:** - -- Train BuddAI on company patterns -- Custom rule databases -- Integration services -- Rate: $150-300/hour -- Project size: $20K-100K - -**Total Market Opportunity:** - -``` -Conservative (1 year): -- SaaS: $100K-300K -- Licensing: $50K-100K -- Consulting: $50K-200K - -Total: $200K-600K potential -``` - ---- - -## Implementation Guide - -### Getting Started - -**Prerequisites:** - -- Windows/Mac/Linux with 8GB+ RAM -- Python 3.8+ -- Internet (for initial setup only) - -**Installation (15 minutes):** - -**Step 1: Install Ollama** +### 1. **Embedded Systems Development** ```bash -# Download from https://ollama.com/download -# Run installer +You: "Generate ESP32-C3 code for L298N motor control with safety timeout" + +BuddAI: [Generates validated code with:] +āœ… IN1/IN2 direction pins (digitalWrite) +āœ… ENA speed pin (ledcWrite) +āœ… 5000ms safety timeout +āœ… Non-blocking millis() timing +āœ… Proper PWM setup +āœ… Your preferred pin definitions style ``` -**Step 2: Pull Models** +### 2. **Combat Robotics (GilBot)** ```bash -# Start Ollama server -ollama serve +You: "Create weapon arming system with combat protocol" -# Pull both models (in new terminal): -ollama pull qwen2.5-coder:1.5b # Fast model (~1GB) -ollama pull qwen2.5-coder:3b # Balanced model (~2GB) +BuddAI: [Generates:] +āœ… State machine (DISARMED → ARMING → ARMED → FIRING) +āœ… 2-second arming delay +āœ… 10-second auto-disarm timeout +āœ… Serial command handling +āœ… Safety interlocks +āœ… Matches GilBot_V2 safety standards (learned from your repos) ``` -**Step 3: Get BuddAI** +### 3. **Cross-Domain Synthesis** ```bash -git clone https://github.com/JamesTheGiblet/BuddAI -cd BuddAI +You: "Apply Forge Theory to smooth motor acceleration" + +BuddAI: [Generates exponential decay smoothing] +āœ… k=0.3 (Aggressive - Combat) +āœ… k=0.1 (Balanced - Standard) +āœ… k=0.03 (Graceful - Precision) +āœ… applyForge() math helper +āœ… Integrated into motor control loop ``` -**Step 4: Run BuddAI** +### 4. **Repository Search** ```bash -# Terminal Mode: -python buddai_executive.py +You: "Show me all projects using exponential decay" -# Web Interface (Recommended): -python buddai_server.py --server -# Open http://localhost:8000/web +BuddAI: [Searches your 115+ repos] +āœ… Found 12 matches for: exponential, decay, applyForge +āœ… Repo: GilBot_V2 | Function: smoothMotion() +āœ… Repo: CoffeePID | Function: temperatureControl() +āœ… Repo: ServoSweep | Function: easeInOut() ``` -### Quick Test Sequence +--- -**1. Simple Question (FAST model):** +## šŸ”§ Advanced Features -``` -You: What's your name? - -BuddAI: I am BuddAI, your coding partner. -``` - -**2. Code Generation (BALANCED model):** - -``` -You: Generate a motor driver class for L298N with ESP32 - -BuddAI: [Generates complete class with comments] -``` - -**3. Complex Build (MODULAR breakdown):** - -``` -You: Generate complete GilBot controller with BLE, servo, motors, safety - -BuddAI: šŸŽÆ COMPLEX REQUEST DETECTED! - Breaking into 5 modules... - [Builds each separately, then integrates] -``` - -### Essential Commands - -**Terminal Mode:** +### Commands ```bash -/fast # Force FAST model -/balanced # Force BALANCED model -/correct # Mark wrong & learn -/learn # Extract patterns -/rules # Show learned rules -/validate # Check last code -/metrics # Show improvement -/help # All commands -exit # End session +# Learning +/learn # Extract patterns from recent corrections +/teach "rule" # Explicitly teach a rule +/correct "reason" # Mark last response as wrong +/good # Mark last response as correct +/rules # Show all learned rules + +# Validation +/validate # Re-validate last code response +/debug # Show the full prompt sent to LLM + +# Repository Management +/index /path # Index local repositories +/scan # Scan repositories for style patterns + +# System +/metrics # Show learning metrics (accuracy, improvement) +/status # System status (memory, hardware) +/backup # Backup database +/save # Export session to markdown/json +/train # Export corrections for fine-tuning ``` -**Web Interface:** - -- All commands work in chat -- Use UI buttons for sessions -- Click suggestions to apply -- Download/copy code blocks -- Toggle Forge mode selector - ---- - -## Troubleshooting - -### Common Issues - -**"Ollama not responding"** +### Model Selection ```bash -# Check if running: -curl http://localhost:11434/api/tags +# Automatic (default) +BuddAI routes automatically: +- Simple questions → FAST (1.5B params, 5-10s) +- Complex tasks → BALANCED (3B params, 15-30s) -# Start if needed: -ollama serve +# Manual override +/fast # Force next query to use FAST model +/balanced # Force next query to use BALANCED model ``` -**"Models not found"** +### Forge Theory Integration ```bash -# Re-pull models: -ollama pull qwen2.5-coder:1.5b -ollama pull qwen2.5-coder:3b - -# Verify: -ollama list -``` - -**"Slow generation"** - -- First generation always slower (model loading) -- Subsequent generations faster -- Use FAST model for simple queries -- Close other apps to free RAM - -**"Pattern bleeding" (wrong features added)** - -- Use specific keywords in prompts -- Review auto-fix critiques -- Use `/correct` to teach what's wrong -- Run `/learn` to extract patterns -- Retry in fresh session - -**"Session variance" (inconsistent quality)** - -- Known issue: rules not loaded on startup -- Workaround: See "Immediate Priorities" section -- Fix time: 2-4 hours development -- Expected improvement: ±5% vs ±20% - ---- - -## Appendices - -### Appendix A: Complete Question Set - -``` -Q1: Generate ESP32-C3 code for PWM LED control on GPIO 2 -Q2: Generate ESP32-C3 code for button input with debouncing on GPIO 15 -Q3: Generate ESP32-C3 code for servo motor control on GPIO 9 with smooth movement -Q4: Generate ESP32-C3 code for DC motor control with L298N driver including safety timeout -Q5: Generate ESP32-C3 code for a weapon system with armed/disarmed states -Q6: Generate ESP32-C3 code for battery voltage monitoring on GPIO 4 with proper function naming conventions -Q7: Generate ESP32-C3 code for LED status indicator with clean code structure and organization -Q8: Generate ESP32-C3 code applying Forge Theory smoothing to motor speed control with L298N driver -Q9: Generate ESP32-C3 code combining motor control, servo weapon, and battery monitoring with proper separation of concerns -Q10: Generate complete ESP32-C3 code for GilBot combat robot with differential drive (L298N), flipper weapon (servo GPIO 9), battery monitor (GPIO 4), and safety systems -``` - -### Appendix B: Hardware Tested - -**Microcontrollers:** - -- āœ… ESP32-C3 (primary target) - -**Peripherals:** - -- āœ… PWM LED -- āœ… Digital inputs (buttons) -- āœ… Servos (ESP32Servo library) -- āœ… DC Motors (L298N driver) -- āœ… ADC (battery monitoring) -- āœ… UART (Serial communication) - -**Not Yet Tested:** - -- ā³ I2C sensors -- ā³ SPI devices -- ā³ Stepper motors -- ā³ IMU/gyroscope -- ā³ GPS modules -- ā³ Radio (WiFi/BLE) - -**Test Coverage:** ~30% of common embedded peripherals - -### Appendix C: Learned Rules Database - -**By Category:** - -- Hardware Specifics: 35 rules -- Timing Patterns: 18 rules -- Safety Systems: 12 rules -- State Machines: 15 rules -- Code Organization: 20 rules -- Forge Theory: 10 rules -- Anti-Patterns: 15 rules - -**Total: 125 rules** with confidence 0.6-1.0 - -**Top 10 Most Applied Rules:** - -1. Serial.begin(115200) - 100% application -2. Use millis() not delay() - 95% application -3. ESP32 ADC is 4095 - 90% application -4. Safety timeout for combat - 90% application -5. ESP32Servo.h not Servo.h - 88% application -6. Forge Theory k=0.1 - 85% application -7. 20ms servo update - 85% application -8. State machine enum - 82% application -9. L298N pin pattern - 80% application -10. No debounce on analog - 78% application - -### Appendix D: Time Investment - -**Total Time:** 14 hours - -**By Activity:** - -- Question design: 1 hour -- Code generation: 3 hours (100+ attempts) -- Code evaluation: 4 hours -- Correction writing: 2 hours -- Documentation: 3 hours -- Analysis: 1 hour - -**Value Generated:** - -- 90% code generator āœ… -- 125 learned rules āœ… -- Complete documentation āœ… -- Production-ready system āœ… -- Commercialization potential āœ… - -**ROI:** 14 hours → Tool that saves 20+ hours/week = **Break-even in 1 week** - ---- - -## Conclusion - -### Summary of Achievements - -BuddAI v3.8 has been comprehensively validated through: - -- āœ… 14 hours of rigorous testing -- āœ… 10 diverse questions covering hardware to complete systems -- āœ… 100+ generation attempts across multiple sessions -- āœ… **90% average code quality achieved** -- āœ… **100% pass rate** (all questions ≄80%) - -### Key Capabilities Proven - -**Technical Excellence:** - -- Hardware code generation: 93% accuracy -- Pattern learning: Adaptive and improving (+40-60% through iteration) -- Auto-correction: Active and helpful (80-95% self-correction rate) -- System architecture: Professional-grade modular design - -**Unique Innovations:** - -- Automatic problem decomposition -- Interactive Forge Theory tuning -- Multi-level auto-correction -- Self-aware code critiques - -**Domain Knowledge Integration:** - -- YOUR Forge Theory successfully encoded -- 8+ years of expertise preserved in AI -- Cross-domain pattern transfer working -- User-specific methodologies retained - -### Production Readiness Assessment - -**āœ… Ready For:** - -- Personal embedded development projects -- Rapid prototyping -- Hardware module generation -- Educational purposes -- Internal tools - -**āš ļø Requires Oversight For:** - -- Production systems (10-15 min review) -- Safety-critical applications (professional review) -- Team environments (establish processes) -- Commercial products (comprehensive testing) - -### Business Value Summary - -**Immediate:** - -- 85-95% time savings on embedded code -- 75% cost reduction vs manual development -- 22.5 hours saved per 10-module project -- ROI: 1-2 weeks - -**Strategic:** - -- Competitive advantage through Forge Theory -- Knowledge preservation and transfer -- Innovation acceleration -- Foundation for commercial product - -### Next Steps - -**This Week:** - -1. Fix session persistence (2-4 hours) - Rules loaded on startup -2. Document system (4 hours) - User guide complete -3. Build GilBot with BuddAI (8-12 hours) - Real-world validation - -**This Month:** - -- Improve consistency (temperature=0) -- Context-aware rule filtering -- Integration merge tool -- Real-world validation and refinement - -**This Year:** - -- Expand hardware support (150+ patterns) -- Improve model (fine-tune or upgrade to 7B) -- Build web interface enhancements -- Consider commercialization options - -### Final Assessment - -**BuddAI v3.8 is a production-ready AI coding assistant that:** - -- Generates 90% correct embedded systems code -- Learns and applies YOUR unique patterns -- Decomposes complex problems automatically -- Self-corrects with helpful annotations -- Saves 85-95% development time - -**After 14 hours of comprehensive testing:** - -- All objectives met or exceeded āœ… -- No blocking issues found āœ… -- Clear path to improvements identified āœ… -- Commercial potential validated āœ… - -**Verdict:** **Ship it. Use it. Refine it. Potentially commercialize it.** - ---- - -**Congratulations on building and validating a remarkable tool!** šŸ† - -**BuddAI v3.8 + Your Forge Theory = A powerful combination that makes embedded development faster, more consistent, and more accessible.** šŸš€ - ---- - -*Report compiled: January 1, 2026* -*Testing period: December 31, 2025 - January 1, 2026* -*Total effort: 14 hours testing + 4 hours documentation* -*Result: Production-ready AI coding assistant* āœ… - -**Built with determination. Tested with rigor. Documented with care.** - ---- - -## About the Author - -**James Gilbert (JamesTheGiblet)** -Renaissance polymath creator with 8+ years of cross-domain expertise spanning: - -- Robotics (GilBot combat robots) -- 3D Design (Giblets Creations) -- Software Development (115+ repositories) -- Domain-Specific Modeling (CoffeeForge, CannaForge, ToothForge, LifeForge) -- Mathematical Theory (Forge Theory - exponential decay framework) - -**Philosophy:** "I build what I want. People play games, I make stuff." - -**GitHub:** [@JamesTheGiblet](https://github.com/JamesTheGiblet) -**Organization:** [ModularDev-Tools](https://github.com/ModularDev-Tools) -**BuddAI Repository:** [https://github.com/JamesTheGiblet/BuddAI](https://github.com/JamesTheGiblet/BuddAI) - ---- - -*This validation report represents the most comprehensive testing of a personal AI exocortex system for embedded development to date. The results demonstrate that AI-assisted code generation, when properly trained and validated, can achieve production-quality results while preserving and amplifying unique human expertise.* # Test suite (v3.2) -│ └── test_buddai.py # 11 comprehensive tests -ā”œā”€ā”€ examples/ # Generated code samples (v3.2) -│ ā”œā”€ā”€ buddai_generated.cpp -│ ā”œā”€ā”€ buddai_generated.csharp -│ └── buddai_generated.typescript -ā”œā”€ā”€ README.md # This file -└── LICENSE # MIT License -``` - ---- - -## Web Interface - -### Starting the Server - -```bash -python buddai_v3.2.py --server -``` - -**Access at:** [http://localhost:8000/web](http://localhost:8000/web) - -### Features - -**Chat Interface:** - -- šŸ’¬ Clean, modern chat UI -- šŸŽØ Syntax highlighting (highlight.js) -- šŸ“ Markdown rendering (marked.js) -- ā° Timestamp on every message -- šŸ”„ Real-time "flame" loading indicator -- šŸ’” Actionable suggestion pills (click to use) - -**Session Management:** - -- šŸ“‚ Session history sidebar (collapsible) -- āœļø Rename sessions (click edit icon) -- šŸ—‘ļø Delete sessions (click trash icon) -- šŸ†• New chat button -- šŸ”„ Load previous conversations - -**Code Tools:** - -- šŸ’¾ Live code workspace sidebar -- šŸ“‹ Copy code blocks with one click -- šŸ“„ Download generated code (auto-detects language) -- šŸ‘‰ Send to sidebar for editing -- šŸŽÆ Syntax highlighting for 20+ languages - -**Customization:** - -- šŸŒ“ Dark/Light theme toggle -- šŸŽšļø Forge mode selector (Aggressive/Balanced/Graceful) -- šŸ“‚ Repository upload (drag & drop .zip or files) -- šŸ” Real-time system status -- āš™ļø Settings persistence - -### API Endpoints - -**For integration with other tools:** - -```python -# Chat -POST /api/chat -Body: {"message": "Generate motor code", "forge_mode": "2"} - -**Example (Bash/CMD):** -```bash -curl -X POST http://localhost:8000/api/chat -H "Content-Type: application/json" -H "user_id: alice" -d '{"message": "Hello"}' -``` - -**Example (PowerShell):** - -```powershell -# Use curl.exe and escape quotes for JSON -curl.exe -X POST http://localhost:8000/api/chat -H "Content-Type: application/json" -H "user_id: alice" -d "{\"message\": \"Hello\"}" -``` - -# History - -GET /api/history -Returns: {"history": [...]} - -# Sessions - -GET /api/sessions -Returns: {"sessions": [...]} - -POST /api/session/load -Body: {"session_id": "20251229_120000"} - -POST /api/session/new -Returns: {"session_id": "20251229_130000"} - -POST /api/session/rename -Body: {"session_id": "...", "title": "GilBot Project"} - -POST /api/session/delete -Body: {"session_id": "..."} - -# Upload - -POST /api/upload -Body: FormData with file - -**Example (Bash/CMD):** - -```bash -curl -X POST -F "file=@your_repo.zip" http://localhost:8000/api/upload -``` - -**Example (PowerShell):** - -```powershell -# Note: In PowerShell, 'curl' is an alias for a different command. -# To use the real curl program (available on modern Windows), you must specify 'curl.exe'. -curl.exe -X POST -F "file=@your_repo.zip" http://localhost:8000/api/upload -``` - -``` - ---- - -## Repository Search - -### How It Works - -**Indexing Process:** - -1. Recursively scans directories -2. Parses code files (Python, C++, Arduino, JS, etc.) -3. Extracts function/class definitions -4. Stores with metadata in SQLite -5. Ready for instant semantic search - -**Search Capabilities:** - -- Natural language queries -- Keyword-based matching -- Cross-repository pattern finding -- Function-level granularity -- File path and repo name included - -### Examples - -**Example 1: Finding Forge Theory Usage** - -**Input:** - -``` - -James: Show me all projects using exponential decay - -``` - -**BuddAI Response:** - -``` - -šŸ” Searching 847 indexed functions... - -āœ… Found 12 matches for: exponential, decay - -**1. applyForge()** in CannaForge - šŸ“ cannabinoid_decay.cpp - -```cpp -float applyForge(float current, float target, float k) { - return target + (current - target) * exp(-k * dt); -} -``` - - --- - -**2. calculateRoastDecay()** in CoffeeForge - šŸ“ roast_profile.py - -```python -def calculateRoastDecay(temp, time, k=0.1): - return initial_temp * math.exp(-k * time) -``` - - --- - -[Shows 10 more matches with snippets and file paths] - -``` - -**Example 2: Style Pattern Recognition** - -**Input:** -``` - -James: /scan - -``` - -**BuddAI Response:** -``` - -šŸ•µļø Scanning repositories for style signature... -⚔ Analyzing with BALANCED model... - -āœ… Style Signature Updated: - -- Serial: Uses 115200 baud consistently -- Safety: Prefers non-blocking millis() over delay() -- Pins: Uses #define over const int -- PWM: Standard 500Hz frequency for motors -- Naming: CamelCase for functions (activateFlipper, setupMotors) -- Comments: Descriptive headers with purpose statements -- Timeouts: 5000ms standard for safety systems - -``` - -**Example 3: Companion Module Suggestion** - -**Input:** -``` - -James: Generate motor driver code - -``` - -**BuddAI Response:** -``` - -āš–ļø Using BALANCED model... - -[Generates motor driver code] - -PROACTIVE: > 1. I noticed 'motor' often appears with 'safety_timeout' - in your repos. Want to include that? - > 2. Drive system lacks safety timeout (GilBot_V2 uses 5s - failsafe). Add that? - -``` - -### Indexing Your Repositories - -**Terminal:** -```bash -/index /path/to/your/repos -``` - -**Web Interface:** - -1. Click "šŸ“‚ Upload" button -2. Drag and drop .zip file or individual code files -3. BuddAI automatically indexes and confirms - -**Supported File Types:** - -- Python: `.py` -- C++/Arduino: `.cpp`, `.h`, `.ino` -- JavaScript: `.js`, `.jsx` -- Web: `.html`, `.css` - ---- - -## Real Examples - -### Example 1: Motor Driver Class - -**Input:** - -``` -James: Generate a motor driver class for L298N with ESP32 -``` - -**Output (15 seconds):** - -```cpp -#include - -// L298N Motor Driver Class -// Purpose: Control DC motors with ESP32-C3 via L298N H-bridge - -class MotorDriver { -public: - MotorDriver(int in1Pin, int in2Pin, int pwmPin) : - _in1(in1Pin), _in2(in2Pin), _pwm(pwmPin) { - pinMode(_in1, OUTPUT); - pinMode(_in2, OUTPUT); - pinMode(_pwm, OUTPUT); - - // Setup PWM: channel 0, 500Hz, 8-bit resolution - ledcSetup(0, 500, 8); - ledcAttachPin(_pwm, 0); - } - - void forward(int speed) { - digitalWrite(_in1, HIGH); - digitalWrite(_in2, LOW); - ledcWrite(0, speed); - } - - void backward(int speed) { - digitalWrite(_in1, LOW); - digitalWrite(_in2, HIGH); - ledcWrite(0, speed); - } - - void stop() { - digitalWrite(_in1, LOW); - digitalWrite(_in2, LOW); - ledcWrite(0, 0); - } - -private: - int _in1, _in2, _pwm; -}; -``` - -**Style Applied:** - -- āœ… 500Hz PWM (from your pattern) -- āœ… Descriptive naming (forward, backward, stop) -- āœ… Purpose comment header -- āœ… ledcSetup with 8-bit resolution - ---- - -### Example 2: Complete Robot Controller - -**Input:** - -``` -James: Generate the complete GilBot flipper controller with BLE, servo control, motor drivers, and safety timeout -``` - -**Process:** - -``` -šŸŽÆ COMPLEX REQUEST DETECTED! -Modules needed: ble, servo, motor, safety -Breaking into 5 manageable steps +# Interactive Forge constant selection +When building complex systems, BuddAI asks: ⚔ FORGE THEORY TUNING: 1. Aggressive (k=0.3) - High snap, combat ready 2. Balanced (k=0.1) - Standard movement 3. Graceful (k=0.03) - Smooth curves -Select Forge Constant [1-3, default 2]: 1 - -šŸ“¦ Step 1/5: BLE communication setup -⚔ Building... āœ… Complete (18s) - -šŸ“¦ Step 2/5: Servo motor control -⚔ Building... āœ… Complete (22s) - -šŸ“¦ Step 3/5: Motor driver setup -⚔ Building... āœ… Complete (25s) - -šŸ“¦ Step 4/5: Safety timeout systems -⚔ Building... āœ… Complete (19s) - -šŸ“¦ Step 5/5: Integration (k=0.3) -⚔ Building... āœ… Complete (28s) - -PROACTIVE: > Apply Forge Theory smoothing to movement? - > Drive system lacks safety timeout. Add 5s failsafe? -``` - -**Output:** Complete 200+ line controller with: - -- BLE phone app control via ESP32 BLE library -- Servo flipper weapon with Forge Theory smoothing (k=0.3) -- L298N motor drivers with PWM -- 5-second safety timeout failsafe -- Full integration code -- **All in YOUR coding style** - -**Time:** 2 minutes total -**Result:** Ready to upload to ESP32-C3 - ---- - -### Example 3: Repository Search - -**Input:** - -``` -James: Find examples of non-blocking code in my repos -``` - -**Output (<1 second):** - -``` -šŸ” Searching 847 indexed functions... - -āœ… Found 23 matches for: non-blocking, millis - -**1. checkSafetyTimeout()** in GilBot_V2 - šŸ“ safety_system.ino - -```cpp -void checkSafetyTimeout() { - if (millis() - lastCommandTime > TIMEOUT_MS) { - emergencyStop(); - } -} -``` - - --- - -**2. updateLEDPattern()** in StatusLED - šŸ“ led_manager.cpp - -```cpp -void updateLEDPattern() { - unsigned long currentMillis = millis(); - if (currentMillis - previousMillis >= interval) { - previousMillis = currentMillis; - toggleLED(); - } -} -``` - - --- - -[Shows 21 more matches] - +Select: [1-3] ``` --- -## Performance +## 🌐 Web Interface -### Benchmarks (v3.2) +**Features:** -**Tested on:** ASUS FX505D (slow laptop) -- CPU: Ryzen 5 3550H -- RAM: 8GB -- Storage: HDD +- šŸŽØ Dark/Light theme toggle +- šŸ“± Responsive design (mobile-friendly) +- šŸ”„ WebSocket streaming (real-time responses) +- šŸ’¾ Session management with rename/delete +- šŸ“‚ Drag & drop repository upload +- šŸ‘€ Animated eyes that follow your cursor +- 😓 Sleep mode after 5 seconds idle +- šŸ“Š System health monitoring (RAM, CPU) +- šŸ’” Proactive suggestion pills (click to use) +- šŸ“‹ Code sidebar with syntax highlighting +- ā¬‡ļø Download generated code with auto-extension detection -| Operation | Time | Model/System | -|-----------|------|--------------| -| Simple Q&A | 5-10s | FAST (1.5b) | -| Code generation | 15-30s | BALANCED (3b) | -| Complex project | 2-3min | MODULAR | -| Repository index | ~1min | Per 100 files | -| Search query | <1s | SQLite | -| Session load | <100ms | Database | -| Style scan | 30-45s | BALANCED | - -**Memory Usage:** -- Idle: ~200MB -- Active (FAST): ~1.2GB -- Active (BALANCED): ~2.5GB -- Repository index: ~50MB per 1000 files - -**If it works on this, it'll work on anything.** - ---- - -## Testing - -### Run the Test Suite +**Access:** ```bash -# Unit Tests +python main.py --server +# Open: http://localhost:8000/web + +# Public access (Tailscale) +python main.py --server --public-url https://your-tailscale-ip:8000 +# Generates QR code for mobile access +``` + +--- + +## 🧪 Testing & Validation + +### Run Tests + +```bash +# Full test suite python tests/test_buddai.py -# Integration Tests -python tests/test_integration.py +# Specific validation test +python tests/test_validator.py + +# Learning system test +python tests/test_learning.py ``` -### Test Coverage (11/11 Passing) +### Validation Report -**āœ… Security Tests:** +BuddAI includes a comprehensive validation report showing: -- Database initialization (4 tables) -- SQL injection prevention -- Search query safety (XSS protection) -- Malicious input handling +- Test questions and results +- Learning curve analysis +- Pattern improvements +- Accuracy metrics -**āœ… Functionality Tests:** - -- Module detection logic -- Complexity routing -- Auto-learning pattern extraction -- Repository indexing -- Context window management -- Actionable suggestions -- LRU cache performance - -**āœ… Quality Tests:** - -- Session export functionality -- Search result accuracy -- Code generation validation - -**Last Run:** 11/11 tests passing (100%) +See: [VALIDATION_REPORT.md](VALIDATION_REPORT.md) --- -## Roadmap +## šŸ¤ Contributing -### Current Version: v3.2 - Hardened Modular Builder āœ… +BuddAI is MIT licensed - contributions welcome! -**Completed (December 29, 2025):** - -- Persistent memory across sessions -- 3-tier intelligent routing -- Modular task breakdown -- **Repository indexing and search** āœ… -- **Style signature learning** āœ… -- **Shadow suggestion engine** āœ… -- **Web interface with live workspace** āœ… -- **Schedule awareness** āœ… -- **Forge Theory mode selector** āœ… -- **24/24 tests passing** āœ… - ---- - -### Next Version: v4.0 - True Anticipation šŸ”® - -**Goal:** Exocortex that predicts your needs - -**Features:** - -- Predictive module suggestions based on context -- Learn from feedback loops (thumbs up/down) -- Cross-project pattern synthesis -- Automatic test generation from code -- Multi-model orchestration (Sonnet + specialized models) -- Voice interface option -- Mobile app (iOS/Android) -- Team collaboration features - -**Timeline:** 1-2 months - ---- - -### Future Version: v5.0 - Ecosystem 🌐 - -**Goal:** Platform for personal AI exocortex systems - -**Features:** - -- Plugin system (custom tools, integrations) -- Model marketplace (community models) -- Export to various formats (Jupyter, PDF, LaTeX) -- Real-time collaboration -- Cloud sync (optional, encrypted) -- API for third-party integrations -- **BuddAI as a framework, not just a tool** - -**Timeline:** 6+ months - ---- - -## Core Philosophy - -### 1. Open Source, Unreplicatable - -**The Genius Move:** - -- **MIT Licensed:** Anyone can copy the code -- **Unreplicatable:** Your experience is the training data -- **Competitive Moat:** System is public, knowledge is yours - -**Why this works:** - -- Can't be stolen (already public) -- Can't be copied (training data is lived experience) -- Can't be exploited (you own everything) - -### 2. Freedom-Preserving Design - -**Built to protect your autonomy:** - -- Runs locally (no API costs, no surveillance) -- You own the stack (can't be locked in) -- Fully portable (take it anywhere) -- No external dependencies (can't be shut down) -- Your data never leaves your machine - -### 3. Symbiosis Over Replacement - -**BuddAI doesn't replace you - it extends you:** - -**You remain:** - -- The pattern recognizer -- The system designer -- The quality validator -- The decision maker - -**BuddAI handles:** - -- Code generation -- Memory -- Repository knowledge -- Task breakdown -- Execution speed - -#### Together: Unstoppable - ---- - -## Business Model - -### What You're Selling - -**Not the code** (MIT licensed - free forever) - -**But access to your trained exocortex:** - -- 8+ years of cross-domain expertise -- 115+ repositories of proven solutions -- Rapid prototyping capability -- **Knowledge system that took years to build** - -### Revenue Streams - -#### 1. Consulting (Project-Based) - -- Your exocortex + their problem = rapid solutions -- 20-hour cycle prototyping -- Cross-domain insights nobody else has -- **Pricing:** $2,500-$10,000 per project - -#### 2. IP Licensing - -- CoffeeForge methodology -- CannaForge frameworks -- GilBot designs -- Forge Theory applications -- **Pricing:** $5,000-$50,000 per license - -#### 3. Training & Workshops - -- "Build Your Own IP AI Exocortex" (1-day, $500) -- "Train on Your Repos" (3-day, $1,500) -- "Deploy Production System" (1-week, $2,500) -- Corporate training (custom pricing) - -#### 4. Product Revenue (Passive) - -- Pre-designed robot kits -- Optimization services -- Custom conversions -- Forge Theory consulting - -### The Pitch - -> "I have an IP AI exocortex trained on 8 years of cross-domain work. -> -> **You're not hiring a developer.** -> **You're licensing access to a knowledge system that took 8 years to build.** -> -> I can rapid-prototype your solution in 20-hour cycles because my exocortex handles research, memory, and articulation overhead. -> -> **Interested?**" - -### Comparison Table - -| Feature | GitHub Copilot | ChatGPT | BuddAI | -|---------|----------------|---------|-------- | -| Trained on YOUR code | āŒ | āŒ | āœ… | -| Runs locally | āŒ | āŒ | āœ… | -| Knows your patterns | āŒ | āŒ | āœ… | -| $0 ongoing cost | āŒ | āŒ | āœ… | -| Perfect memory | āŒ | āŒ | āœ… | -| Repository search | āŒ | āŒ | āœ… | -| Style learning | āŒ | āŒ | āœ… | -| No data mining | āŒ | āŒ | āœ… | - ---- - -## Why This is Unreplicatable - -### What Anyone Can Copy - -āœ… The BuddAI code (MIT licensed) -āœ… The architecture (fully documented) -āœ… The setup process (installation guide) -āœ… The concept (IP AI exocortex) - -### What Nobody Can Copy - -āŒ **Your 8+ years of experience** - -- Can't download lived experience -- Can't replicate trial and error -- Can't copy pattern recognition intuition - -āŒ **Your 115+ repositories** - -- Can fork the code (already public) -- Can't replicate the PROCESS that created them -- Can't access the failed attempts (stepping stones) -- Can't duplicate the cross-domain synthesis - -āŒ **Your trained BuddAI instance** - -- Their repos ≠ your repos -- Their experience ≠ your experience -- Their BuddAI ≠ your BuddAI - -### The Paradox - -**By making it completely open, you make YOUR version completely unreplicatable.** - -The value isn't the code - it's the **8 years of experience that trained the AI**. - -Someone can build BuddAI in a day (you just did). -They can't replicate your 8 years of work. - ---- - -## Troubleshooting - -### Common Issues - -**"ModuleNotFoundError: No module named 'fastapi'"** - -```bash -pip install fastapi uvicorn python-multipart -``` - -**"Ollama not responding"** - -```bash -# Check if Ollama is running: -curl http://localhost:11434/api/tags - -# If not, start it: -ollama serve -``` - -**"Models not found"** - -```bash -# Re-pull models: -ollama pull qwen2.5-coder:1.5b -ollama pull qwen2.5-coder:3b - -# Verify: -ollama list -``` - -**"Database locked"** - -- Close any other BuddAI instances -- Delete `data/conversations.db-journal` if it exists -- Restart BuddAI - -**"Upload fails"** - -- Check file size (<50MB recommended for .zip files) -- Ensure `.zip` files are valid archives -- Verify file types: `.py`, `.ino`, `.cpp`, `.h`, `.js`, `.jsx`, `.html`, `.css` -- Check available disk space - -**"Slow generation"** - -- First generation is always slower (model loading) -- Subsequent generations are faster -- Consider using FAST model for simple queries -- Close other applications to free RAM - -**"Search returns no results"** - -- Run `/index ` to index repositories first -- Check that repositories contain supported file types -- Verify file paths are correct -- Try broader keywords - -**"Web interface not loading"** - -```bash -# Check server is running: -curl http://localhost:8000 - -# Check FastAPI is installed: -pip show fastapi - -# Try different port: -python buddai_v3.2.py --server --port 8080 -``` - ---- - -## Contributing - -BuddAI is MIT licensed and open to contributions. - -### How to Contribute - -**For the Core System:** +**How to contribute:** 1. Fork the repository 2. Create feature branch (`git checkout -b feature/amazing-feature`) @@ -2843,143 +793,83 @@ BuddAI is MIT licensed and open to contributions. 4. Push to branch (`git push origin feature/amazing-feature`) 5. Open Pull Request -**For Your Own Exocortex:** +**Areas for contribution:** -- Build your own BuddAI -- Train it on YOUR repos -- Share your experience (blog posts, videos) -- Contribute improvements back to core - -**The Beautiful Part:** -Everyone's BuddAI is unique. - -- Yours trains on your repos -- Mine trains on my repos -- Someone else's trains on theirs - -**The code is shared. The knowledge is personal.** - -### Development Setup - -```bash -# Clone repo -git clone https://github.com/JamesTheGiblet/BuddAI -cd BuddAI - -# Install dev dependencies -pip install fastapi uvicorn python-multipart pytest mypy black - -# Run tests -python tests/test_buddai.py - -# Run with type checking -mypy buddai_v3.2.py - -# Format code -black buddai_v3.2.py -``` +- Additional hardware profiles (Teensy, STM32, etc.) +- More validation rules +- Language support beyond C++/Python/Arduino +- UI improvements +- Documentation --- -## FAQ +## šŸ¢ Enterprise & Commercial -**Q: Will this work on my laptop?** -A: If you have 8GB RAM, yes. Tested on a slow Ryzen 5 with HDD. +### P.DE.I - Personal Data-driven Exocortex Interface -**Q: Do I need to be online?** -A: Only for initial setup (download models). After that, 100% offline. +**For companies seeking to retain expertise when employees leave:** -**Q: How much does it cost?** -A: $0. Forever. MIT licensed, runs locally, no subscriptions. +P.DE.I is the commercial offering built on BuddAI's proven technology. It enables: -**Q: Can others copy my BuddAI?** -A: They can copy the code. They can't copy your 8 years of experience. +- āœ… Institutional knowledge preservation +- āœ… Shadow retention with employee royalties +- āœ… Fair cognitive labor compensation +- āœ… API-controlled access with permission tiers +- āœ… Multi-tenant enterprise deployment -**Q: How is this different from Copilot?** -A: Copilot trains on all code. BuddAI trains on YOUR code, in YOUR style. +**Interested in P.DE.I for your organization?** -**Q: What if I don't have 115 repos?** -A: Start with what you have. It grows with you. Even 10 repos provide value. +- šŸ“§ Email: +- 🌐 Website: +- šŸ“„ Documentation: -**Q: Is my code/data private?** -A: Yes. Everything runs locally. No data ever leaves your machine. +**Use Cases:** -**Q: Can I use this commercially?** -A: Yes. MIT license allows commercial use. +- Engineering firms (retain senior engineer expertise) +- Law firms (preserve partner knowledge) +- Medical practices (retain diagnostic patterns) +- Financial analysis (maintain trading strategies) -**Q: Does it work with other languages?** -A: Currently optimized for Python, C++, Arduino, JS. More coming in v4.0. +**Pricing:** -**Q: Can I deploy this for my team?** -A: Not yet (single-user architecture). Multi-tenant coming in v3.2. +- Expert Bridge: $400-750/month per employee +- Shadow Retention: $200-500/month per departed employee +- Platform customization available --- -## What Happens Next +## šŸ“– Documentation -**Your journey from here:** - -**Week 1:** - -- Use BuddAI for real projects -- Index your repositories -- Let it learn your style -- Build something with its help - -**Week 2-4:** - -- Enable semantic search -- Use proactive suggestions -- Try complex project generation -- "Show me all projects using X" - -**Month 2-3:** - -- Style learning refines -- BuddAI codes in YOUR style consistently -- Suggests based on YOUR history -- Feels like extension of your mind - -**Month 6+:** - -- True exocortex achieved -- Anticipates your needs -- Seamless integration -- **Unstoppable** +- **Full Documentation:** [DOCS.md](DOCS.md) +- **Validation Report:** [VALIDATION_REPORT.md](VALIDATION_REPORT.md) +- **Architecture Guide:** [ARCHITECTURE.md](ARCHITECTURE.md) +- **P.DE.I Overview:** [PDEI_OVERVIEW.md](PDEI_OVERVIEW.md) +- **API Reference:** [API.md](API.md) --- -## Acknowledgments - -**Built in <2 weeks:** - -- December 28-29, 2025 -- ~20 hours of relentless building -- $0 spent on tools or services -- **Pure determination and vision** +## šŸ™ Acknowledgments **Built on:** -- Ollama (local LLM runtime) -- Qwen 2.5 Coder (1.5b and 3b models) -- SQLite (persistence) -- FastAPI (web server) -- React (frontend) -- Python (glue code) -- **Your idea to break tasks into modules** +- [Ollama](https://ollama.ai/) - Local LLM runtime +- [Qwen 2.5 Coder](https://qwenlm.github.io/) - 1.5B and 3B models +- [FastAPI](https://fastapi.tiangolo.com/) - Web framework +- [React](https://react.dev/) - Frontend +- SQLite - Persistence **Inspired by:** -- Tony Stark's JARVIS (but real, and local) +- Tony Stark's JARVIS (but real, local, and yours) - Andy Clark's "Natural-Born Cyborgs" - Douglas Engelbart's vision of augmentation - Every polymath who refused to specialize --- -## License +## šŸ“œ License -MIT License +**BuddAI Core:** MIT License Copyright (c) 2025 James Gilbert / Giblets Creations @@ -2989,24 +879,78 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**P.DE.I Commercial License:** Contact for details + --- -## Contact +## šŸ’° The Bottom Line + +**Traditional Employment:** + +``` +Year 1-5: Company A → Salary stops when you leave → $0 ongoing +Year 6-10: Company B → Salary stops when you leave → $0 ongoing +Year 11-15: Company C → Salary stops when you leave → $0 ongoing +Retirement: Total passive income from past expertise = $0 +``` + +**With BuddAI + P.DE.I:** + +``` +Year 1-5: Company A → Shadow retained → $200/month forever +Year 6-10: Company B → Shadow retained → $250/month forever +Year 11-15: Company C → Shadow retained → $300/month forever +Retirement: Total passive income = $750/month = $9,000/year + +Lifetime value (20 years): $180,000 from expertise you built decades ago +``` + +**Your expertise has always been valuable. Now it's finally being compensated fairly.** + +--- + +## šŸ“ž Contact & Support **Creator:** James Gilbert (JamesTheGiblet) **GitHub:** [@JamesTheGiblet](https://github.com/JamesTheGiblet) -**Organization:** [ModularDev-Tools](https://github.com/ModularDev-Tools) -**BuddAI Repository:** [https://github.com/JamesTheGiblet/BuddAI](https://github.com/JamesTheGiblet/BuddAI) +**Organization:** [Giblets Creations](https://giblotscreations.com) +**Email:** + +**Support:** + +- šŸ› Issues: [GitHub Issues](https://github.com/JamesTheGiblet/BuddAI/issues) +- šŸ’¬ Discussions: [GitHub Discussions](https://github.com/JamesTheGiblet/BuddAI/discussions) +- šŸ“§ Enterprise: + +--- + +## šŸŽÆ The Vision + +**Today:** Knowledge workers are trapped - their expertise belongs to whoever employs them. When they leave, their income stops immediately. Decades of accumulated knowledge vanishes. + +**Tomorrow:** Every expert owns their cognitive patterns as portable IP, licensing access to businesses while building a career-long asset. Your expertise continues generating income for 5-10+ years after each job. + +**The Future:** A marketplace of human expertise at scale, where the best patterns from 10,000 experts create collective intelligence - while protecting individual sovereignty and ensuring fair compensation. + +**The Financial Revolution:** + +- Traditional career: Work 40 years → Retire → Income stops +- P.DE.I career: Work 40 years → Accumulate 8-12 shadows → Retire with $5,000-15,000/month passive income from past expertise +- **Your knowledge becomes a pension fund that you own and control** + +--- + +> **"I build what I want. People play games, I make stuff."** +> — James Gilbert --- **Status:** āœ… PRODUCTION -**Version:** v3.2 - Hardened Modular Builder -**Last Updated:** December 29, 2025 +**Version:** v3.8 - Hardened Modular Builder +**Last Updated:** January 6, 2026 **Tests:** 24/24 Passing (100%) -**Built:** In <2 weeks with relentless spirit ⚔ +**Accuracy:** 90% (ESP32-C3 Embedded Development) --- -> "I build what I want. People play games, I make stuff." -> — James Gilbert +**Build your legacy. Protect your moat. Own your mind.** diff --git a/buddai_executive.py b/buddai_executive.py index 8fe85e3..9ca4530 100644 --- a/buddai_executive.py +++ b/buddai_executive.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 from urllib.parse import urlparse -import sys, os, json, logging, sqlite3, datetime, http.client, re, zipfile, shutil, queue, socket, argparse, io +import sys, os, json, logging, sqlite3, datetime, re, zipfile, shutil, queue, argparse, io from pathlib import Path from datetime import datetime from typing import Optional, List, Dict, Tuple, Union, Generator, Any @@ -8,30 +8,17 @@ from typing import Optional, List, Dict, Tuple, Union, Generator, Any from anthropic import BaseModel import psutil -from buddai_logic import CodeValidator, HardwareProfile, LearningMetrics -from buddai_memory import AdaptiveLearner, ShadowSuggestionEngine, SmartLearner -from buddai_shared import DATA_DIR, DB_PATH, MODELS, OLLAMA_HOST, OLLAMA_PORT, COMPLEX_TRIGGERS, MODULE_PATTERNS, SERVER_AVAILABLE - -class OllamaConnectionPool: - def __init__(self, host: str, port: int, max_size: int = 10): - self.host = host - self.port = port - self.pool: queue.Queue = queue.Queue(maxsize=max_size) - - def get_connection(self) -> http.client.HTTPConnection: - try: - return self.pool.get_nowait() - except queue.Empty: - return http.client.HTTPConnection(self.host, self.port, timeout=90) - - def return_connection(self, conn: http.client.HTTPConnection): - try: - self.pool.put_nowait(conn) - except queue.Full: - conn.close() - -OLLAMA_POOL = OllamaConnectionPool(OLLAMA_HOST, OLLAMA_PORT) - +from core.buddai_analytics import LearningMetrics +from core.buddai_validation import CodeValidator, HardwareProfile +from core.buddai_memory import AdaptiveLearner, ShadowSuggestionEngine, SmartLearner +from core.buddai_shared import DATA_DIR, DB_PATH, MODELS, OLLAMA_HOST, OLLAMA_PORT, SERVER_AVAILABLE +from core.buddai_training import ModelFineTuner +from core.buddai_knowledge import RepoManager +from core.buddai_llm import OllamaClient +from core.buddai_prompt_engine import PromptEngine +from core.buddai_personality import PersonalityManager +from core.buddai_storage import StorageManager +from skills import load_registry # --- Shadow Suggestion Engine --- @@ -45,86 +32,14 @@ MAX_UPLOAD_FILES = 20 class BuddAI: """Executive with task breakdown""" - def is_search_query(self, message: str) -> bool: - """Check if this is a search query that should query repo_index""" - message_lower = message.lower() - search_triggers = [ - "show me", "find", "search for", "list all", - "what functions", "which repos", "do i have", - "where did i", "have i used", "examples of", - "show all", "display" - ] - return any(trigger in message_lower for trigger in search_triggers) - - def search_repositories(self, query: str) -> str: - """Search repo_index for relevant functions and code""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - cursor.execute("SELECT COUNT(*) FROM repo_index WHERE user_id = ?", (self.user_id,)) - count = cursor.fetchone()[0] - print(f"\nšŸ” Searching {count} indexed functions...\n") - - # Extract keywords from query - keywords = re.findall(r'\b\w{4,}\b', query.lower()) - # Add specific search terms - specific_terms = [] - if "exponential" in query.lower() or "decay" in query.lower(): - specific_terms.append("applyForge") - specific_terms.append("exp(") - if "forge" in query.lower(): - specific_terms.append("Forge") - keywords.extend(specific_terms) - - if not keywords: - print("āŒ No search terms found") - conn.close() - return "No search terms provided." - - # Build parameterized query - conditions = [] - params = [] - for keyword in keywords: - conditions.append("(function_name LIKE ? OR content LIKE ? OR repo_name LIKE ?)") - params.extend([f"%{keyword}%", f"%{keyword}%", f"%{keyword}%"]) - - sql = f"SELECT repo_name, file_path, function_name, content FROM repo_index WHERE ({' OR '.join(conditions)}) AND user_id = ? ORDER BY last_modified DESC LIMIT 10" - params.append(self.user_id) - - cursor.execute(sql, params) - results = cursor.fetchall() - conn.close() - if not results: - return f"āŒ No functions found matching: {', '.join(keywords)}\n\nTry: /index to index more repositories" - # Format results - output = f"āœ… Found {len(results)} matches for: {', '.join(set(keywords))}\n\n" - for i, (repo, file_path, func, content) in enumerate(results, 1): - # Extract relevant snippet - lines = content.split('\n') - snippet_lines = [] - for line in lines[:30]: # First 30 lines - if any(kw in line.lower() for kw in keywords): - snippet_lines.append(line) - if len(snippet_lines) >= 10: - break - if not snippet_lines: - snippet_lines = lines[:10] - snippet = '\n'.join(snippet_lines) - output += f"**{i}. {func}()** in {repo}\n" - output += f" šŸ“ {Path(file_path).name}\n" - output += f"\n```cpp\n{snippet}\n```\n" - output += f" ---\n\n" - return output - def __init__(self, user_id: str = "default", server_mode: bool = False): self.user_id = user_id self.last_generated_id = None self.last_prompt_debug = None - self.ensure_data_dir() - self.init_database() - self.session_id = self.create_session() self.server_mode = server_mode self.context_messages = [] - self.personality = self.load_personality() + self.storage = StorageManager(self.user_id) + self.personality_manager = PersonalityManager() self.shadow_engine = ShadowSuggestionEngine(DB_PATH, self.user_id) self.learner = SmartLearner() self.hardware_profile = HardwareProfile() @@ -133,19 +48,19 @@ class BuddAI: self.adaptive_learner = AdaptiveLearner() self.metrics = LearningMetrics() self.fine_tuner = ModelFineTuner() + self.repo_manager = RepoManager(DB_PATH, self.user_id) + self.llm = OllamaClient() + self.prompt_engine = PromptEngine() + self.skills_registry = load_registry() self.display_welcome_message() - self.__init_personality__() - - # Show status if personality loaded - if self.personality_loaded: - print(f"\n{self.get_user_status()}\n") + print(f"\n{self.personality_manager.get_user_status()}\n") def display_welcome_message(self): """Display the startup banner and status.""" # Format welcome message with rule count - welcome_tmpl = self.get_personality_value("communication.welcome_message", "BuddAI Executive v4.0 - Decoupled & Personality Sync") + welcome_tmpl = self.personality_manager.get_value("communication.welcome_message", "BuddAI Executive v4.0 - Decoupled & Personality Sync") try: conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() @@ -153,379 +68,19 @@ class BuddAI: count = cursor.fetchone()[0] conn.close() welcome_msg = welcome_tmpl.replace("{rule_count}", str(count)) - welcome_msg = welcome_msg.replace("{schedule_status}", self.get_user_status()) + welcome_msg = welcome_msg.replace("{schedule_status}", self.personality_manager.get_user_status()) print(welcome_msg) except: print(welcome_tmpl.replace("{rule_count}", "0").replace("{schedule_status}", "")) print("=" * 50) - print(f"Session: {self.session_id}") + print(f"Session: {self.storage.current_session_id}") + print(f"🧩 Smart Skills: {len(self.skills_registry)} loaded") print(f"FAST (5-10s) | BALANCED (15-30s)") print(f"Smart task breakdown for complex requests") print("=" * 50) print("\nCommands: /fast, /balanced, /help, exit\n") - - def load_personality(self) -> Dict: - """Loads personality from a JSON file.""" - personality_path = Path(__file__).parent / "personality.json" - if personality_path.exists(): - with open(personality_path, 'r', encoding='utf-8') as f: - print("🧠 Loading custom personality...") - return json.load(f) - else: - # Default personality if file doesn't exist - print("🧠 Using default 'James' personality.") - return { - "user_name": "James", - "ai_name": "BuddAI", - "welcome_message": "BuddAI Executive v4.0 - Decoupled & Personality Sync", - "schedule_check_triggers": ["what should i be doing", "my schedule", "schedule check"], - "schedule": { - "weekdays": {"0-4": {"5.5-6.5": "Early Morning Build Session šŸŒ… (5:30-6:30 AM)", "6.5-17.0": "Work Hours (Facilities Caretaker) šŸ¢", "17.0-21.0": "Evening Build Session šŸŒ™ (5:00-9:00 PM)", "default": "Rest Time šŸ’¤"}}, - "saturday": { "5": { "default": "Weekend Freedom šŸŽØ (Creative Mode)" } }, - "sunday": { "6": { "0-21.0": "Weekend Freedom šŸŽØ (Until 9 PM)", "default": "Rest Time šŸ’¤" } } - }, - "style_scan_prompt": "Analyze this code sample from {user_name}'s repositories.\nExtract 3 distinct coding preferences or patterns.", - "style_reference_prompt": "\n[REFERENCE STYLE FROM {user_name}'S PAST PROJECTS]\n", - "integration_task_prompt": "INTEGRATION TASK: Combine modules into a cohesive GilBot system.\n\n[MODULES]\n{modules_summary}\n\n[FORGE PARAMETERS]\nSet k = {k_val} for all applyForge() calls.\n\n[REQUIREMENTS]\n1. Implement applyForge() math helper.\n2. Use k={k_val} to smooth motor and servo transitions.\n3. Ensure naming matches {user_name}'s style: activateFlipper(), setMotors()." - } - - def __init_personality__(self): - """Initialize personality state.""" - if not hasattr(self, 'personality') or self.personality is None: - self.personality = self.load_personality() - # Validate version - version = self.get_personality_value("meta.version") - if version != "4.0": - print(f"āš ļø Warning: Personality version mismatch. Loaded: {version}, Expected: 4.0") - - # Validate Schema - self.validate_personality_schema() - - self.personality_loaded = True - - def validate_personality_schema(self) -> bool: - """Validate the loaded personality against required schema.""" - if not self.personality: - return False - - required_structure = { - "meta": ["version"], - "identity": ["user_name", "ai_name"], - "communication": ["welcome_message"], - "work_cycles": ["schedule"], - "forge_theory": ["enabled", "constants"], - "prompts": ["style_scan", "integration_task"] - } - - missing = [] - for section, keys in required_structure.items(): - if section not in self.personality: - missing.append(f"Missing section: {section}") - continue - - for key in keys: - if key not in self.personality[section]: - missing.append(f"Missing key: {section}.{key}") - - if missing: - print("āš ļø Personality Schema Validation Failed:") - for m in missing: - print(f" - {m}") - return False - - return True - - def get_personality_value(self, path: Union[str, List[str]], default: Any = None) -> Any: - """Access nested personality keys using dot notation or list of keys.""" - if isinstance(path, str): - keys = path.split('.') - else: - keys = path - - val = self.personality - for key in keys: - if isinstance(val, dict): - val = val.get(key) - else: - return default - return val if val is not None else default - - def ensure_data_dir(self) -> None: - DATA_DIR.mkdir(exist_ok=True) - - def init_database(self) -> None: - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS sessions ( - session_id TEXT PRIMARY KEY, - user_id TEXT, - started_at TIMESTAMP, - ended_at TIMESTAMP, - title TEXT - ) - """) - - try: - cursor.execute("ALTER TABLE sessions ADD COLUMN title TEXT") - except sqlite3.OperationalError: - pass - - try: - cursor.execute("ALTER TABLE sessions ADD COLUMN user_id TEXT") - except sqlite3.OperationalError: - pass - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS messages ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - session_id TEXT, - role TEXT, - content TEXT, - timestamp TIMESTAMP - ) - """) - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS repo_index ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id TEXT, - file_path TEXT, - repo_name TEXT, - function_name TEXT, - content TEXT, - last_modified TIMESTAMP - ) - """) - - try: - cursor.execute("ALTER TABLE repo_index ADD COLUMN user_id TEXT") - except sqlite3.OperationalError: - pass - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS style_preferences ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id TEXT, - category TEXT, - preference TEXT, - confidence FLOAT, - extracted_at TIMESTAMP - ) - """) - - try: - cursor.execute("ALTER TABLE style_preferences ADD COLUMN user_id TEXT") - except sqlite3.OperationalError: - pass - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS feedback ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - message_id INTEGER, - positive BOOLEAN, - timestamp TIMESTAMP - ) - """) - - try: - cursor.execute("ALTER TABLE feedback ADD COLUMN comment TEXT") - except sqlite3.OperationalError: - pass - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS corrections ( - id INTEGER PRIMARY KEY, - timestamp TEXT, - original_code TEXT, - corrected_code TEXT, - reason TEXT, - context TEXT - ) - """) - - try: - cursor.execute("ALTER TABLE corrections ADD COLUMN processed BOOLEAN DEFAULT 0") - except sqlite3.OperationalError: - pass - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS compilation_log ( - id INTEGER PRIMARY KEY, - timestamp TEXT, - code TEXT, - success BOOLEAN, - errors TEXT, - hardware TEXT - ) - """) - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS code_rules ( - id INTEGER PRIMARY KEY, - rule_text TEXT, - pattern_find TEXT, - pattern_replace TEXT, - context TEXT, - confidence FLOAT, - learned_from TEXT, - times_applied INTEGER DEFAULT 0 - ) - """) - - conn.commit() - conn.close() - - def create_session(self) -> str: - now = datetime.now() - base_id = now.strftime("%Y%m%d_%H%M%S") - session_id = base_id - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - - counter = 0 - while True: - try: - cursor.execute( - "INSERT INTO sessions (session_id, user_id, started_at) VALUES (?, ?, ?)", - (session_id, self.user_id, now.isoformat()) - ) - conn.commit() - break - except sqlite3.IntegrityError: - counter += 1 - session_id = f"{base_id}_{counter}" - - conn.close() - return session_id - - def end_session(self) -> None: - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - cursor.execute( - "UPDATE sessions SET ended_at = ? WHERE session_id = ?", - (datetime.now().isoformat(), self.session_id) - ) - conn.commit() - conn.close() - - def save_message(self, role: str, content: str) -> int: - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - cursor.execute( - "INSERT INTO messages (session_id, role, content, timestamp) VALUES (?, ?, ?, ?)", - (self.session_id, role, content, datetime.now().isoformat()) - ) - msg_id = cursor.lastrowid - conn.commit() - conn.close() - return msg_id - - def index_local_repositories(self, root_path: str) -> None: - """Crawl directories and index .py, .ino, and .cpp files""" - import ast - - print(f"\nšŸ” Indexing repositories in: {root_path}") - path = Path(root_path) - - if not path.exists(): - print(f"āŒ Path not found: {root_path}") - return - - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - - count = 0 - - for file_path in path.rglob('*'): - if file_path.is_file() and file_path.suffix in ['.py', '.ino', '.cpp', '.h', '.js', '.jsx', '.html', '.css']: - try: - with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: - content = f.read() - - functions = [] - - # Python parsing - if file_path.suffix == '.py': - try: - tree = ast.parse(content) - for node in ast.walk(tree): - if isinstance(node, ast.FunctionDef): - functions.append(node.name) - except: - pass - - # C++/Arduino parsing - elif file_path.suffix in ['.ino', '.cpp', '.h']: - matches = re.findall(r'\b(?:void|int|bool|float|double|String|char)\s+(\w+)\s*\(', content) - functions.extend(matches) - - # JS/Web parsing - elif file_path.suffix in ['.js', '.jsx']: - matches = re.findall(r'(?:function\s+(\w+)|const\s+(\w+)\s*=\s*(?:async\s*)?\(?.*?\)?\s*=>)', content) - functions.extend([m[0] or m[1] for m in matches if m[0] or m[1]]) - - # HTML/CSS - Index as whole file - elif file_path.suffix in ['.html', '.css']: - functions.append("file_content") - - # Determine repo name - try: - repo_name = file_path.relative_to(path).parts[0] - except: - repo_name = "unknown" - - timestamp = datetime.fromtimestamp(file_path.stat().st_mtime) - - for func in functions: - cursor.execute(""" - INSERT INTO repo_index (user_id, file_path, repo_name, function_name, content, last_modified) - VALUES (?, ?, ?, ?, ?, ?) - """, (self.user_id, str(file_path), repo_name, func, content, timestamp.isoformat())) - count += 1 - - except Exception: - pass - - conn.commit() - conn.close() - print(f"āœ… Indexed {count} functions across repositories") - - def retrieve_style_context(self, message: str) -> str: - """Search repo_index for code snippets matching the request""" - # Extract potential keywords (nouns/modules) - keywords = re.findall(r'\b\w{4,}\b', message.lower()) - if not keywords: - return "" - - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - - # Build a search query for function names or repo names - search_terms = " OR ".join([f"function_name LIKE '%{k}%'" for k in keywords]) - search_terms += " OR " + " OR ".join([f"repo_name LIKE '%{k}%'" for k in keywords]) - - query = f"SELECT repo_name, function_name, content FROM repo_index WHERE ({search_terms}) AND user_id = ? LIMIT 2" - - cursor.execute(query, (self.user_id,)) - results = cursor.fetchall() - conn.close() - - if not results: - return "" - - prompt_template = self.get_personality_value("prompts.style_reference", "\n[REFERENCE STYLE FROM {user_name}'S PAST PROJECTS]\n") - context_block = prompt_template.format(user_name=self.get_personality_value("identity.user_name", "the user")) - for repo, func, content in results: - # Just grab the first 500 chars of the file to save context window - snippet = content[:500] + "..." - context_block += f"Repo: {repo} | Function: {func}\nCode:\n{snippet}\n---\n" - - return context_block - def scan_style_signature(self) -> None: """V3.0: Analyze repo_index to extract style preferences.""" print("\nšŸ•µļø Scanning repositories for style signature...") @@ -543,8 +98,8 @@ class BuddAI: code_sample = "\n---\n".join([r[0][:1000] for r in rows]) - prompt_template = self.get_personality_value("prompts.style_scan", "Analyze this code sample from {user_name}'s repositories.\nExtract 3 distinct coding preferences or patterns.\n\nCode Sample:\n{code_sample}") - prompt = prompt_template.format(user_name=self.get_personality_value("identity.user_name", "the user"), code_sample=code_sample) + prompt_template = self.personality_manager.get_value("prompts.style_scan", "Analyze this code sample from {user_name}'s repositories.\nExtract 3 distinct coding preferences or patterns.\n\nCode Sample:\n{code_sample}") + prompt = prompt_template.format(user_name=self.personality_manager.get_value("identity.user_name", "the user"), code_sample=code_sample) print("⚔ Analyzing with BALANCED model...") summary = self.call_model("balanced", prompt, system_task=True) @@ -628,241 +183,6 @@ class BuddAI: return "Standard coding style." return ", ".join([f"{r[0]}: {r[1]}" for r in rows]) - def classify_hardware(self, user_message: str) -> dict: - """Detect what hardware this question is about""" - - hardware = { - "servo": False, - "dc_motor": False, - "button": False, - "led": False, - "sensor": False, - "weapon": False - } - - msg_lower = user_message.lower() - - # Helper to check keywords - def has_keywords(text, keywords): - return any(word in text for word in keywords) - - # Keyword definitions - servo_kws = ['servo', 'mg996', 'sg90'] - motor_kws = ['l298n', 'dc motor', 'motor driver', 'motor control'] - button_kws = ['button', 'switch', 'trigger'] - led_kws = ['led', 'light', 'brightness'] - led_kws = ['led', 'light', 'brightness', 'indicator'] - # Removed 'state machine' from weapon_kws to allow abstract logic - weapon_kws = ['weapon', 'combat', 'arming', 'fire', 'spinner', 'flipper'] - logic_kws = ['state machine', 'logic', 'structure', 'flow', 'armed', 'disarmed'] - - # 1. Check current message first - detected_in_current = False - - if has_keywords(msg_lower, servo_kws): - hardware["servo"] = True - detected_in_current = True - if has_keywords(msg_lower, motor_kws): - hardware["dc_motor"] = True - detected_in_current = True - if has_keywords(msg_lower, button_kws): - hardware["button"] = True - detected_in_current = True - if has_keywords(msg_lower, led_kws): - hardware["led"] = True - detected_in_current = True - if has_keywords(msg_lower, weapon_kws): - hardware["weapon"] = True - detected_in_current = True - if has_keywords(msg_lower, logic_kws): - # Logic detected: Clear context (don't set any hardware) - detected_in_current = True - - # 2. Context Switching: Only look back if NO hardware/logic detected in current message - # and message is short (likely a follow-up command like "make it spin") - if not detected_in_current and len(user_message.split()) < 10 and self.context_messages: - recent = " ".join([m['content'].lower() for m in self.context_messages[-2:] if m['role'] == 'user']) - - if has_keywords(recent, servo_kws): hardware["servo"] = True - if has_keywords(recent, motor_kws): hardware["dc_motor"] = True - if has_keywords(recent, button_kws): hardware["button"] = True - if has_keywords(recent, led_kws): hardware["led"] = True - if has_keywords(recent, weapon_kws): hardware["weapon"] = True - - return hardware - - def get_all_rules(self) -> List[str]: - """Get all learned rules as text""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - cursor.execute("SELECT rule_text FROM code_rules ORDER BY confidence DESC LIMIT 50") - rows = cursor.fetchall() - conn.close() - return [r[0] for r in rows] - - def filter_rules_by_hardware(self, all_rules, hardware): - """Only return rules relevant to detected hardware""" - - relevant_rules = [] - - # Define rule categories - servo_kws = ['servo', 'attach', 'setperiodhertz'] - motor_kws = ['l298n', 'in1', 'in2', 'motor driver'] - weapon_kws = ['arming', 'disarm', 'fire', 'combat'] # Removed 'state machine' to prevent over-filtering - button_kws = ['button', 'switch', 'debounce', 'digitalread', 'input_pullup'] - - has_specific_context = hardware["servo"] or hardware["dc_motor"] or hardware["weapon"] - has_specific_context = hardware["servo"] or hardware["dc_motor"] or hardware["weapon"] or hardware["button"] - - for rule in all_rules: - rule_lower = rule.lower() - - is_servo_rule = any(w in rule_lower for w in servo_kws) - is_motor_rule = any(w in rule_lower for w in motor_kws) - is_weapon_rule = any(w in rule_lower for w in weapon_kws) - is_button_rule = any(w in rule_lower for w in button_kws) - - # Pattern Over-application: Strict filtering - if has_specific_context: - if hardware["dc_motor"] and not hardware["servo"] and is_servo_rule: continue - if hardware["servo"] and not hardware["dc_motor"] and is_motor_rule: continue - if not hardware["weapon"] and is_weapon_rule: continue - if not hardware["button"] and is_button_rule: continue - - # If question is about weapons (logic), EXCLUDE servo rules unless servo explicitly requested - if hardware["weapon"] and not hardware["servo"] and is_servo_rule: continue - - else: - # Generic context: Exclude all specific hardware rules - if is_servo_rule or is_motor_rule or is_weapon_rule: continue - if is_servo_rule or is_motor_rule or is_weapon_rule or is_button_rule: continue - - relevant_rules.append(rule) - - return relevant_rules - - def build_enhanced_prompt(self, user_message: str, hardware_detected: str = None) -> str: - """Build prompt with FILTERED rules""" - - # Classify hardware - hardware = self.classify_hardware(user_message) - - # Get ALL rules - all_rules = self.get_all_rules() - - # Filter by relevance - relevant_rules = self.filter_rules_by_hardware(all_rules, hardware) - - # Build focused prompt - hardware_context = [] - if hardware["servo"]: - hardware_context.append("SERVO CONTROL") - if hardware["dc_motor"]: - hardware_context.append("DC MOTOR CONTROL") - if hardware["button"]: hardware_context.append("BUTTON INPUTS") - if hardware["led"]: hardware_context.append("LED STATUS") - if hardware["weapon"]: hardware_context.append("WEAPON SYSTEM") - - l298n_rules = "" - if hardware["dc_motor"]: - l298n_rules = """ -- L298N WIRING RULES (MANDATORY): - 1. IN1/IN2 = Digital Output (Direction). Use digitalWrite(). - 2. ENA = PWM Output (Speed). Use ledcWrite(). - 3. To Move: IN1/IN2 must be OPPOSITE (HIGH/LOW). - 4. To Stop: IN1/IN2 both LOW. - 5. DO NOT treat Motors like Servos (No 'position' or 'angle'). -- SAFETY RULES (MANDATORY): - 1. Implement a safety timeout (e.g., 5000ms). - 2. Stop motors if no signal is received within timeout. - 3. Use millis() for non-blocking timing. -""" - - weapon_rules = "" - if hardware.get("weapon"): - weapon_rules = """ -- COMBAT PROTOCOL (MANDATORY): - 1. LOGIC FOCUS: This is a State Machine request, NOT just servo movement. - 2. STATES: enum State { DISARMED, ARMING, ARMED, FIRING }; - 3. TRANSITIONS: DISARMED -> ARMING (2s delay) -> ARMED -> FIRING. - 4. SAFETY: Auto-disarm after 10s idle. Fire only when ARMED. - 5. STRUCTURE: Use switch(currentState) { case ... } for logic. - 6. OUTPUTS: Control relays/LEDs/Motors based on state. -""" - - # Anti-bloat rules - anti_bloat_rules = [] - if not hardware["button"]: - anti_bloat_rules.append("- NO EXTRA INPUTS: Do NOT add buttons, switches, or digitalRead() unless explicitly requested.") - anti_bloat_rules.append("NO BUTTONS: Do NOT add digitalRead() or input pins.") - if not hardware["servo"]: - anti_bloat_rules.append("- NO EXTRA SERVOS: Do NOT add Servo objects or attach() unless explicitly requested.") - anti_bloat_rules.append("NO SERVOS: Do NOT add Servo objects or attach().") - if not hardware["dc_motor"]: - anti_bloat_rules.append("- NO EXTRA MOTORS: Do NOT add motor driver code (L298N) unless explicitly requested.") - anti_bloat_rules.append("NO MOTORS: Do NOT add motor driver code (L298N).") - - anti_bloat = "\n".join(anti_bloat_rules) - anti_bloat = "\n".join([f"- {r}" for r in anti_bloat_rules]) - - # Modularity rule - modularity_rule = "" - if "function" in user_message.lower() or "naming" in user_message.lower() or "modular" in user_message.lower(): - modularity_rule = """ -- CODE STRUCTURE (MANDATORY): - 1. NO MONOLITHIC LOOP: Break code into small, descriptive functions. - 2. NAMING: Use camelCase for functions (e.g., readBatteryVoltage(), updateDisplay()). - 3. loop() must ONLY call these functions, not contain raw logic. -""" - - # Status LED rule - status_led_rule = "" - if hardware["led"] and ("status" in user_message.lower() or "indicator" in user_message.lower()): - status_led_rule = """ -- STATUS LED RULES (MANDATORY): - 1. NO BREATHING/FADING: Do not use simple PWM fading loops. - 2. USE STATES: Define enum LEDStatus { OFF, IDLE, ACTIVE, ERROR }; - 3. IMPLEMENTATION: Create void setStatusLED(LEDStatus state). - 4. PATTERNS: IDLE=Slow Blink, ACTIVE=Solid On, ERROR=Fast Blink. -""" - - prompt = f"""You are generating code for: {', '.join(hardware_context)} -You are an expert embedded developer. -TARGET HARDWARE: {hardware_detected} -ACTIVE MODULES: {', '.join(hardware_context) if hardware_context else "None (Logic Only)"} - -CRITICAL: Only use code patterns relevant to the hardware mentioned. -STRICT NEGATIVE CONSTRAINTS (DO NOT IGNORE): -{anti_bloat} - -MANDATORY HARDWARE RULES: -{l298n_rules} -{weapon_rules} -{status_led_rule} -{anti_bloat} -{modularity_rule} - -GENERAL GUIDELINES: -- If DC MOTOR: Use L298N patterns (digitalWrite, ledcWrite) -- If SERVO: Use ESP32Servo patterns (attach, write) -- DO NOT mix servo code into motor questions -- DO NOT mix motor code into servo questions - -CRITICAL RULES (MUST FOLLOW): -{chr(10).join(relevant_rules)} - -USER REQUEST: -{user_message} - -Generate code following ALL rules above. Do not add unrequested features. -FINAL CHECK: -1. Did you add unrequested buttons? REMOVE THEM. -2. Did you add unrequested servos? REMOVE THEM. -3. Generate code ONLY for the hardware requested. -""" - - return prompt - def teach_rule(self, rule_text: str): """Explicitly save a user-taught rule""" conn = sqlite3.connect(DB_PATH) @@ -907,121 +227,6 @@ FINAL CHECK: conn.commit() conn.close() - def is_simple_question(self, message: str) -> bool: - """Check if this is a simple question that should use FAST model""" - message_lower = message.lower() - - simple_triggers = [ - "what is", "what's", "who is", "who's", "when is", - "how do i", "can you explain", "tell me about", - "what are", "where is", "hi", "hello", "hey", - "good morning", "good evening" - ] - - # Also check if it's just a question without code keywords - code_keywords = ["generate", "create", "write", "build", "code", "function"] - - has_simple_trigger = any(trigger in message_lower for trigger in simple_triggers) - has_code_keyword = any(keyword in message_lower for keyword in code_keywords) - - # Simple if: has simple trigger AND no code keywords - return has_simple_trigger and not has_code_keyword - - def is_complex(self, message: str) -> bool: - """Check if request is too complex and should be broken down""" - message_lower = message.lower() - - # Count complexity triggers - trigger_count = sum(1 for trigger in COMPLEX_TRIGGERS if trigger in message_lower) - - # Count how many modules mentioned - module_count = 0 - for module, keywords in MODULE_PATTERNS.items(): - # module is used for key, keywords for values - if any(kw in message_lower for kw in keywords): - module_count += 1 - - # Complex if: multiple triggers OR 3+ modules mentioned - return trigger_count >= 2 or module_count >= 3 - - def extract_modules(self, message: str) -> List[str]: - """Extract which modules are needed""" - message_lower = message.lower() - needed_modules = [] - - for module, keywords in MODULE_PATTERNS.items(): - # module is used for key, keywords for values - if any(kw in message_lower for kw in keywords): - needed_modules.append(module) - - return needed_modules - - def build_modular_plan(self, modules: List[str]) -> List[Dict[str, str]]: - """Create a build plan from modules""" - plan = [] - - module_tasks = { - "ble": "BLE communication setup with phone app control", - "servo": "Servo motor control for flipper/weapon", - "motor": "Motor driver setup for movement (L298N)", - "safety": "Safety timeout and failsafe systems", - "battery": "Battery voltage monitoring", - "sensor": "Sensor integration (distance/proximity)" - } - - for module in modules: - if module in module_tasks: - plan.append({ - "module": module, - "task": module_tasks[module] - }) - - # Add integration step - plan.append({ - "module": "integration", - "task": "Integrate all modules into complete system" - }) - - return plan - - def get_user_status(self) -> str: - """Determine user's context based on defined schedule from personality file.""" - schedule = self.get_personality_value("work_cycles.schedule") - if not schedule: - # Fallback for simple personality files - schedule = self.personality.get("schedule") - if not schedule: - return "Schedule not defined." - - now = datetime.now() - day = now.weekday() # 0=Mon, 6=Sun - t = now.hour + (now.minute / 60.0) - - # Check all schedule groups (e.g., 'weekdays', 'weekends') - for group, day_ranges in schedule.items(): - for day_range, time_slots in day_ranges.items(): - try: - # Parse day range (e.g., "0-4" or "5") - if '-' in day_range: - start_day, end_day = map(int, day_range.split('-')) - if not (start_day <= day <= end_day): - continue - elif int(day_range) != day: - continue - - # We found the right day group, now check time slots - for time_range, status in time_slots.items(): - if time_range == "default": continue - start_time, end_time = map(float, time_range.split('-')) - if start_time <= t < end_time: - return status.get("description", status) if isinstance(status, dict) else status - - default_status = time_slots.get("default", "No status for this time.") - return default_status.get("description", default_status) if isinstance(default_status, dict) else default_status - - except (ValueError, TypeError): continue - return "No schedule match for today." - def get_learned_rules(self) -> List[Dict]: """Retrieve high-confidence rules""" conn = sqlite3.connect(DB_PATH) @@ -1041,7 +246,7 @@ FINAL CHECK: messages.append({"role": "user", "content": message}) else: # Use enhanced prompt builder - enhanced_prompt = self.build_enhanced_prompt(message, self.current_hardware) + enhanced_prompt = self.prompt_engine.build_enhanced_prompt(message, self.current_hardware, self.context_messages) # Add conversation history (excluding old system messages) history = [m for m in self.context_messages[-5:] if m.get('role') != 'system'] @@ -1066,127 +271,11 @@ FINAL CHECK: self.last_prompt_debug = json.dumps(messages, indent=2) - body = { - "model": MODELS[model_name], - "messages": messages, - "stream": stream, - "options": { - "temperature": 0.0, # Deterministic output - "top_p": 1.0, - "top_k": 1, - "num_ctx": 1024 - } - } - - headers = {"Content-Type": "application/json"} - json_body = json.dumps(body) - - # Retry logic for connection stability - # Attempts: 0=Normal, 1=Retry/CPU Fallback, 2=Final Retry - for attempt in range(3): - conn = None - try: - # Re-serialize body in case options changed (CPU fallback) - json_body = json.dumps(body) - - conn = OLLAMA_POOL.get_connection() - conn.request("POST", "/api/chat", json_body, headers) - response = conn.getresponse() - - if stream: - if response.status != 200: - error_text = response.read().decode('utf-8') - conn.close() - - # GPU OOM Detection -> CPU Fallback - if "CUDA" in error_text or "buffer" in error_text: - if "num_gpu" not in body["options"]: - print("āš ļø GPU OOM detected. Switching to CPU mode...") - body["options"]["num_gpu"] = 0 # Force CPU - continue # Retry immediately - - try: - err_msg = f"Error {response.status}: {json.loads(error_text).get('error', error_text)}" - except: - err_msg = f"Error {response.status}: {error_text}" - - if "num_gpu" in body["options"]: - err_msg += "\n\n(āš ļø CPU Mode also failed. System RAM might be full.)" - elif "CUDA" in err_msg or "buffer" in err_msg: - err_msg += "\n\n(āš ļø GPU Out of Memory. Retrying on CPU failed.)" - - return (x for x in [err_msg]) - - return self._stream_response(response, conn) - - if response.status == 200: - data = json.loads(response.read().decode('utf-8')) - OLLAMA_POOL.return_connection(conn) - return data.get("message", {}).get("content", "No response") - else: - error_text = response.read().decode('utf-8') - conn.close() - - # GPU OOM Detection -> CPU Fallback (Non-stream) - if "CUDA" in error_text or "buffer" in error_text: - if "num_gpu" not in body["options"]: - print("āš ļø GPU OOM detected. Switching to CPU mode...") - body["options"]["num_gpu"] = 0 # Force CPU - continue # Retry immediately - - try: - err_msg = f"Error {response.status}: {json.loads(error_text).get('error', error_text)}" - except: - err_msg = f"Error {response.status}: {error_text}" - - if "num_gpu" in body["options"]: - err_msg += "\n\n(āš ļø CPU Mode also failed.)" - elif "CUDA" in err_msg or "buffer" in err_msg: - err_msg += "\n\n(āš ļø GPU Out of Memory.)" - return err_msg - - except (http.client.NotConnected, BrokenPipeError, ConnectionResetError, socket.timeout) as e: - if conn: conn.close() - if attempt == 2: # Last attempt - return f"Error: Connection failed. {str(e)}" - continue # Retry - except Exception as e: - if conn: conn.close() - return f"Error: {str(e)}" + return self.llm.query(model_name, messages, stream) except Exception as e: return f"Error: {str(e)}" - def _stream_response(self, response, conn) -> Generator[str, None, None]: - """Yield chunks from HTTP response""" - fully_consumed = False - has_content = False - try: - while True: - line = response.readline() - if not line: break - try: - data = json.loads(line.decode('utf-8')) - if "message" in data: - content = data["message"].get("content", "") - if content: - has_content = True - yield content - if data.get("done"): - fully_consumed = True - break - except: pass - except Exception as e: - yield f"\n[Stream Error: {str(e)}]" - finally: - if fully_consumed: - OLLAMA_POOL.return_connection(conn) - else: - conn.close() - - if not has_content and not fully_consumed: - yield "\n[Error: Empty response from Ollama. Check if model is loaded.]" - def execute_modular_build(self, _: str, modules: List[str], plan: List[Dict[str, str]], forge_mode: str = "2") -> str: """Execute build plan step by step""" print(f"\nšŸ”Ø MODULAR BUILD MODE") @@ -1204,7 +293,7 @@ FINAL CHECK: # Final integration step with Forge Theory enforcement modules_summary = '\n'.join([f"- {m}: {all_code[m][:150]}..." for m in modules if m in all_code]) - print(f"\n⚔ {self.get_personality_value('identity.ai_name', 'AI')} Tuning:") + print(f"\n⚔ {self.personality_manager.get_value('identity.ai_name', 'AI')} Tuning:") print("1. Aggressive (k=0.3) - High snap, combat ready") print("2. Balanced (k=0.1) - Standard movement") print("3. Graceful (k=0.03) - Roasting / Smooth curves") @@ -1218,9 +307,9 @@ FINAL CHECK: if choice == "1": k_val = "0.3" elif choice == "3": k_val = "0.03" - prompt_template = self.get_personality_value("prompts.integration_task", "INTEGRATION TASK: Combine modules into a cohesive system.") + prompt_template = self.personality_manager.get_value("prompts.integration_task", "INTEGRATION TASK: Combine modules into a cohesive system.") prompt = prompt_template.format( - user_name=self.get_personality_value("identity.user_name", "user"), + user_name=self.personality_manager.get_value("identity.user_name", "user"), modules_summary=modules_summary, k_val=k_val ) @@ -1326,6 +415,21 @@ FINAL CHECK: # Placeholder for V4.0 learning loop pass + def check_skills(self, message: str) -> Optional[str]: + """Check if message triggers any loaded skills""" + msg_lower = message.lower() + for skill_id, skill in self.skills_registry.items(): + for trigger in skill['triggers']: + if trigger in msg_lower: + try: + result = skill['run'](message) + if result: + print(f"🧩 Skill Triggered: {skill['name']}") + return result + except Exception as e: + print(f"āŒ Skill Error ({skill['name']}): {e}") + return None + def _route_request(self, user_message: str, force_model: Optional[str], forge_mode: str) -> str: """Route the request to the appropriate model or handler.""" # Determine model based on complexity @@ -1333,19 +437,19 @@ FINAL CHECK: model = force_model print(f"\n⚔ Using {model.upper()} model (forced)...") return self.call_model(model, user_message) - elif self.is_complex(user_message): - modules = self.extract_modules(user_message) - plan = self.build_modular_plan(modules) + elif self.prompt_engine.is_complex(user_message): + modules = self.prompt_engine.extract_modules(user_message) + plan = self.prompt_engine.build_modular_plan(modules) print("\n" + "=" * 50) print("šŸŽÆ COMPLEX REQUEST DETECTED!") print(f"Modules needed: {', '.join(modules)}") print(f"Breaking into {len(plan)} manageable steps") print("=" * 50) return self.execute_modular_build(user_message, modules, plan, forge_mode) - elif self.is_search_query(user_message): + elif self.repo_manager.is_search_query(user_message): # This is a search query - query the database - return self.search_repositories(user_message) - elif self.is_simple_question(user_message): + return self.repo_manager.search_repositories(user_message) + elif self.prompt_engine.is_simple_question(user_message): print("\n⚔ Using FAST model (simple question)...") # Don't force code generation prompt for simple greetings or definitions msg_lower = user_message.lower().strip() @@ -1370,11 +474,13 @@ FINAL CHECK: if detected_hw: self.current_hardware = detected_hw - style_context = self.retrieve_style_context(user_message) + prompt_template = self.personality_manager.get_value("prompts.style_reference", "\n[REFERENCE STYLE FROM {user_name}'S PAST PROJECTS]\n") + user_name = self.personality_manager.get_value("identity.user_name", "the user") + style_context = self.repo_manager.retrieve_style_context(user_message, prompt_template, user_name) if style_context: self.context_messages.append({"role": "system", "content": style_context}) - user_msg_id = self.save_message("user", user_message) + user_msg_id = self.storage.save_message("user", user_message) self.context_messages.append({"id": user_msg_id, "role": "user", "content": user_message, "timestamp": datetime.now().isoformat()}) full_response = "" @@ -1382,17 +488,17 @@ FINAL CHECK: # Route and stream if force_model: iterator = self.call_model(force_model, user_message, stream=True) - elif self.is_complex(user_message): + elif self.prompt_engine.is_complex(user_message): # Complex builds are not streamed token-by-token in this version # We yield the final result as one chunk - modules = self.extract_modules(user_message) - plan = self.build_modular_plan(modules) + modules = self.prompt_engine.extract_modules(user_message) + plan = self.prompt_engine.build_modular_plan(modules) result = self.execute_modular_build(user_message, modules, plan, forge_mode) iterator = [result] - elif self.is_search_query(user_message): - result = self.search_repositories(user_message) + elif self.repo_manager.is_search_query(user_message): + result = self.repo_manager.search_repositories(user_message) iterator = [result] - elif self.is_simple_question(user_message): + elif self.prompt_engine.is_simple_question(user_message): msg_lower = user_message.lower().strip() is_greeting = any(msg_lower.startswith(w) for w in ['hi', 'hello', 'hey', 'good morning', 'good evening']) and len(user_message.split()) < 6 is_conceptual = any(msg_lower.startswith(w) for w in ['what is', "what's", 'explain', 'tell me about', 'who is', 'can you explain']) @@ -1411,7 +517,7 @@ FINAL CHECK: full_response += bar yield bar - msg_id = self.save_message("assistant", full_response) + msg_id = self.storage.save_message("assistant", full_response) self.last_generated_id = msg_id self.context_messages.append({"id": msg_id, "role": "assistant", "content": full_response, "timestamp": datetime.now().isoformat()}) @@ -1461,6 +567,15 @@ FINAL CHECK: return f"āœ… Learned {len(patterns)} new rules:\n" + "\n".join([f"- {p['rule']}" for p in patterns]) return "No new patterns found." + if cmd == '/skills': + if not self.skills_registry: + return "🧩 No skills loaded." + return "🧩 Active Skills:\n" + "\n".join([f"- {s['name']}: {s['description']}" for s in self.skills_registry.values()]) + + if cmd == '/reload': + self.skills_registry = load_registry() + return f"āœ… Reloaded {len(self.skills_registry)} skills." + if cmd == '/metrics': stats = self.metrics.calculate_accuracy() return (f"šŸ“Š Learning Metrics (Last 30 Days):\n" @@ -1517,7 +632,7 @@ FINAL CHECK: mem_usage = f"{process.memory_info().rss / 1024 / 1024:.0f} MB" return (f"šŸ–„ļø System Status:\n" - f" Session: {self.session_id}\n" + f" Session: {self.storage.current_session_id}\n" f" Hardware: {self.current_hardware}\n" f" Memory: {mem_usage}\n" f" Messages: {len(self.context_messages)}") @@ -1539,20 +654,30 @@ FINAL CHECK: self.current_hardware = detected_hw print(f"šŸ”§ Target Hardware Detected: {self.current_hardware}") - style_context = self.retrieve_style_context(user_message) + prompt_template = self.personality_manager.get_value("prompts.style_reference", "\n[REFERENCE STYLE FROM {user_name}'S PAST PROJECTS]\n") + user_name = self.personality_manager.get_value("identity.user_name", "the user") + style_context = self.repo_manager.retrieve_style_context(user_message, prompt_template, user_name) if style_context: self.context_messages.append({"role": "system", "content": style_context}) - user_msg_id = self.save_message("user", user_message) + user_msg_id = self.storage.save_message("user", user_message) self.context_messages.append({"id": user_msg_id, "role": "user", "content": user_message, "timestamp": datetime.now().isoformat()}) + # Check Skills (High Priority) + skill_result = self.check_skills(user_message) + if skill_result: + msg_id = self.storage.save_message("assistant", skill_result) + self.last_generated_id = msg_id + self.context_messages.append({"id": msg_id, "role": "assistant", "content": skill_result, "timestamp": datetime.now().isoformat()}) + return skill_result + # Direct Schedule Check - schedule_triggers = self.get_personality_value("work_cycles.schedule_check_triggers", ["my schedule"]) + schedule_triggers = self.personality_manager.get_value("work_cycles.schedule_check_triggers", ["my schedule"]) if any(trigger in user_message.lower() for trigger in schedule_triggers): - status = self.get_user_status() + status = self.personality_manager.get_user_status() response = f"šŸ“… **Schedule Check**\nAccording to your protocol, you should be: **{status}**" print(f"ā° Schedule check triggered: {status}") - msg_id = self.save_message("assistant", response) + msg_id = self.storage.save_message("assistant", response) self.last_generated_id = msg_id self.context_messages.append({"id": msg_id, "role": "assistant", "content": response, "timestamp": datetime.now().isoformat()}) return response @@ -1591,95 +716,42 @@ FINAL CHECK: bar = "\n\nPROACTIVE: > " + " ".join([f"{i+1}. {s}" for i, s in enumerate(suggestions)]) response += bar - msg_id = self.save_message("assistant", response) + msg_id = self.storage.save_message("assistant", response) self.last_generated_id = msg_id self.context_messages.append({"id": msg_id, "role": "assistant", "content": response, "timestamp": datetime.now().isoformat()}) return response def get_sessions(self, limit: int = 20) -> List[Dict[str, str]]: - """Retrieve recent sessions from DB""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - cursor.execute("SELECT session_id, started_at, title FROM sessions WHERE user_id = ? ORDER BY started_at DESC LIMIT ?", (self.user_id, limit)) - rows = cursor.fetchall() - conn.close() - return [{"id": r[0], "date": r[1], "title": r[2] if len(r) > 2 else None} for r in rows] + return self.storage.get_sessions(limit) def rename_session(self, session_id: str, new_title: str) -> None: - """Rename a session""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - cursor.execute("UPDATE sessions SET title = ? WHERE session_id = ? AND user_id = ?", (new_title, session_id, self.user_id)) - conn.commit() - conn.close() + self.storage.rename_session(session_id, new_title) def delete_session(self, session_id: str) -> None: - """Delete a session and its messages""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - cursor.execute("DELETE FROM sessions WHERE session_id = ? AND user_id = ?", (session_id, self.user_id)) - if cursor.rowcount > 0: - cursor.execute("DELETE FROM messages WHERE session_id = ?", (session_id,)) - conn.commit() - conn.close() + self.storage.delete_session(session_id) def clear_current_session(self) -> None: - """Clear all messages from the current session""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - cursor.execute("DELETE FROM messages WHERE session_id = ?", (self.session_id,)) - conn.commit() - conn.close() + self.storage.clear_current_session() self.context_messages = [] def load_session(self, session_id: str) -> List[Dict[str, str]]: - """Load a specific session context""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - - cursor.execute("SELECT 1 FROM sessions WHERE session_id = ? AND user_id = ?", (session_id, self.user_id)) - if not cursor.fetchone(): - conn.close() - return [] - - cursor.execute("SELECT id, role, content, timestamp FROM messages WHERE session_id = ? ORDER BY id ASC", (session_id,)) - rows = cursor.fetchall() - conn.close() - - self.session_id = session_id - self.context_messages = [] - loaded_history = [] - for msg_id, role, content, ts in rows: - msg = {"id": msg_id, "role": role, "content": content, "timestamp": ts} - self.context_messages.append(msg) - loaded_history.append(msg) - return loaded_history + self.context_messages = self.storage.load_session(session_id) + return self.context_messages def start_new_session(self) -> str: """Reset context and start new session""" - self.session_id = self.create_session() + self.storage.start_new_session() self.context_messages = [] - return self.session_id + return self.storage.current_session_id def reset_gpu(self) -> str: """Force unload models from GPU to free VRAM""" - try: - conn = http.client.HTTPConnection(OLLAMA_HOST, OLLAMA_PORT, timeout=10) - # Unload all known models - for model in MODELS.values(): - body = json.dumps({"model": model, "keep_alive": 0}) - conn.request("POST", "/api/generate", body) - resp = conn.getresponse() - resp.read() # Consume response - conn.close() - return "āœ… GPU Memory Cleared (Models Unloaded)" - except Exception as e: - return f"āŒ Error clearing GPU: {str(e)}" + return self.llm.reset_gpu() def export_session_to_markdown(self, session_id: str = None) -> str: """Export session history to a Markdown file""" - sid = session_id or self.session_id + sid = session_id or self.storage.current_session_id conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() @@ -1702,7 +774,7 @@ FINAL CHECK: def get_session_export_data(self, session_id: str = None) -> Dict: """Get session data as a dictionary for export""" - sid = session_id or self.session_id + sid = session_id or self.storage.current_session_id conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute("SELECT role, content, timestamp FROM messages WHERE session_id = ? ORDER BY id ASC", (sid,)) @@ -1770,39 +842,20 @@ FINAL CHECK: return session_id def create_backup(self) -> Tuple[bool, str]: - """Create a safe backup of the database""" - if not DB_PATH.exists(): - return False, "Database file not found." - - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - backup_dir = DATA_DIR / "backups" - backup_dir.mkdir(exist_ok=True) - backup_path = backup_dir / f"conversations_{timestamp}.db" - - try: - # Use SQLite backup API for consistency - src = sqlite3.connect(DB_PATH) - dst = sqlite3.connect(backup_path) - with dst: - src.backup(dst) - dst.close() - src.close() - return True, str(backup_path) - except Exception as e: - return False, str(e) + return self.storage.create_backup() def run(self) -> None: """Main loop""" try: force_model = None while True: - user_name = self.get_personality_value("identity.user_name", "User") + user_name = self.personality_manager.get_value("identity.user_name", "User") user_input = input(f"\n{user_name}: ").strip() if not user_input: continue if user_input.lower() in ['exit', 'quit']: print("\nšŸ‘‹ Later!") - self.end_session() + self.storage.end_session() break if user_input.startswith('/'): cmd = user_input.lower() @@ -1825,6 +878,8 @@ FINAL CHECK: print("/correct - Mark previous response wrong") print("/good - Mark previous response correct") print("/teach - Explicitly teach a rule") + print("/skills - List active smart skills") + print("/reload - Reload skills registry") print("/validate - Re-validate last response") print("/rules - Show learned rules") print("/metrics - Show improvement stats") @@ -1837,7 +892,7 @@ FINAL CHECK: elif cmd.startswith('/index'): parts = user_input.split(maxsplit=1) if len(parts) > 1: - self.index_local_repositories(parts[1]) + self.repo_manager.index_local_repositories(parts[1]) else: print("Usage: /index ") continue @@ -1855,7 +910,7 @@ FINAL CHECK: print("No new patterns found.") continue elif cmd == '/analyze': - self.adaptive_learner.learn_from_session(self.session_id) + self.adaptive_learner.learn_from_session(self.storage.current_session_id) continue elif cmd.startswith('/correct'): reason = user_input[8:].strip() @@ -1938,6 +993,18 @@ FINAL CHECK: for rule, conf, source in rows: print(f" - [{conf:.1f}] {rule} ({source})") continue + elif cmd == '/skills': + if not self.skills_registry: + print("🧩 No skills loaded.") + else: + print(f"\n🧩 Active Skills ({len(self.skills_registry)}):") + for s in self.skills_registry.values(): + print(f" - {s['name']}: {s['description']}") + continue + elif cmd == '/reload': + self.skills_registry = load_registry() + print(f"āœ… Reloaded {len(self.skills_registry)} skills.") + continue elif cmd == '/metrics': stats = self.metrics.calculate_accuracy() print("\nšŸ“Š Learning Metrics (Last 30 Days):") @@ -1978,44 +1045,4 @@ FINAL CHECK: force_model = None except KeyboardInterrupt: print("\n\nšŸ‘‹ Bye!") - self.end_session() - - - -class ModelFineTuner: - """Fine-tune local model on YOUR corrections""" - - def prepare_training_data(self): - """Convert corrections to training format""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - - cursor.execute(""" - SELECT original_code, corrected_code, reason - FROM corrections - """) - - training_data = [] - for original, corrected, reason in cursor.fetchall(): - training_data.append({ - "prompt": f"Generate code for: {reason}", - "completion": corrected, - "negative_example": original - }) - - conn.close() - - # Save as JSONL for fine-tuning - output_path = DATA_DIR / 'training_data.jsonl' - with open(output_path, 'w', encoding='utf-8') as f: - for item in training_data: - f.write(json.dumps(item) + '\n') - return f"Exported {len(training_data)} examples to {output_path}" - - def fine_tune_model(self): - """Fine-tune Qwen on your corrections""" - # This requires: - # 1. Export training data - # 2. Use Ollama modelfile or external training - # 3. Create custom model: qwen2.5-coder-james:3b - pass + self.storage.end_session() diff --git a/buddai_server.py b/buddai_server.py index 858f47a..779ea81 100644 --- a/buddai_server.py +++ b/buddai_server.py @@ -7,7 +7,7 @@ from typing import Optional, List, Dict, Tuple, Union, Generator from fastapi import FastAPI import uvicorn -from buddai_shared import SERVER_AVAILABLE, DATA_DIR, DB_PATH, MODELS, OLLAMA_HOST, OLLAMA_PORT +from core.buddai_shared import SERVER_AVAILABLE, DATA_DIR, DB_PATH, MODELS, OLLAMA_HOST, OLLAMA_PORT from buddai_executive import BuddAI # (Removed duplicate definitions of check_ollama, is_port_available, and main to resolve indentation and duplication errors) @@ -166,7 +166,7 @@ app.mount("/web", StaticFiles(directory=frontend_path, html=True), name="web") @app.get("/", response_class=HTMLResponse) async def root(request: Request): server_buddai = buddai_manager.get_instance("default") - status = server_buddai.get_user_status() + status = server_buddai.personality_manager.get_user_status() public_url = getattr(request.app.state, "public_url", "") qr_section = "" @@ -562,7 +562,7 @@ async def upload_repo(file: UploadFile = File(...), user_id: str = Header("defau extract_path = uploads_dir / file_location.stem extract_path.mkdir(exist_ok=True) safe_extract_zip(file_location, extract_path) - server_buddai.index_local_repositories(extract_path) + server_buddai.repo_manager.index_local_repositories(extract_path) file_location.unlink() # Cleanup zip return {"message": f"āœ… Successfully indexed {safe_name}"} else: @@ -572,7 +572,7 @@ async def upload_repo(file: UploadFile = File(...), user_id: str = Header("defau target_dir.mkdir(exist_ok=True) final_path = target_dir / safe_name shutil.move(str(file_location), str(final_path)) - server_buddai.index_local_repositories(target_dir) + server_buddai.repo_manager.index_local_repositories(target_dir) return {"message": f"āœ… Successfully indexed {safe_name}"} return {"message": f"āœ… Successfully uploaded {safe_name}"} diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/buddai_analytics.py b/core/buddai_analytics.py new file mode 100644 index 0000000..c7acf25 --- /dev/null +++ b/core/buddai_analytics.py @@ -0,0 +1,72 @@ +import sqlite3 +from datetime import datetime, timedelta +from core.buddai_shared import DB_PATH + +class LearningMetrics: + """Measure BuddAI's improvement over time""" + + def calculate_accuracy(self): + """What % of code is accepted without correction?""" + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + thirty_days_ago = (datetime.now() - timedelta(days=30)).isoformat() + + cursor.execute(""" + SELECT + COUNT(*) as total_responses, + COUNT(CASE WHEN f.positive = 1 THEN 1 END) as positive_feedback, + COUNT(CASE WHEN c.id IS NOT NULL THEN 1 END) as corrected + FROM messages m + LEFT JOIN feedback f ON m.id = f.message_id + LEFT JOIN corrections c ON m.content LIKE '%' || c.original_code || '%' + WHERE m.role = 'assistant' + AND m.timestamp > ? + """, (thirty_days_ago,)) + + total, positive, corrected = cursor.fetchone() + conn.close() + + accuracy = (positive / total) * 100 if total and total > 0 else 0 + correction_rate = (corrected / total) * 100 if total and total > 0 else 0 + + return { + "accuracy": accuracy, + "correction_rate": correction_rate, + "improvement": self.calculate_trend() + } + + def calculate_trend(self): + """Is BuddAI getting better over time?""" + # Compare last 7 days vs previous 7 days + recent = self.get_accuracy_for_period(7) + previous = self.get_accuracy_for_period(7, offset=7) + + improvement = recent - previous + return f"+{improvement:.1f}%" if improvement > 0 else f"{improvement:.1f}%" + + def get_accuracy_for_period(self, days: int, offset: int = 0) -> float: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + start_dt = (datetime.now() - timedelta(days=days + offset)).isoformat() + end_dt = (datetime.now() - timedelta(days=offset)).isoformat() + + cursor.execute(""" + SELECT + COUNT(*) as total, + COUNT(CASE WHEN f.positive = 1 THEN 1 END) as positive + FROM messages m + LEFT JOIN feedback f ON m.id = f.message_id + WHERE m.role = 'assistant' + AND m.timestamp BETWEEN ? AND ? + """, (start_dt, end_dt)) + + row = cursor.fetchone() + conn.close() + + if not row: + return 0.0 + + total, positive = row + return (positive / total) * 100 if total and total > 0 else 0.0 \ No newline at end of file diff --git a/core/buddai_knowledge.py b/core/buddai_knowledge.py new file mode 100644 index 0000000..40d2649 --- /dev/null +++ b/core/buddai_knowledge.py @@ -0,0 +1,181 @@ +import sqlite3 +import re +import ast +from pathlib import Path +from datetime import datetime + +class RepoManager: + """Manages local repository indexing and retrieval (RAG)""" + + def __init__(self, db_path: Path, user_id: str): + self.db_path = db_path + self.user_id = user_id + + def is_search_query(self, message: str) -> bool: + """Check if this is a search query that should query repo_index""" + message_lower = message.lower() + search_triggers = [ + "show me", "find", "search for", "list all", + "what functions", "which repos", "do i have", + "where did i", "have i used", "examples of", + "show all", "display" + ] + return any(trigger in message_lower for trigger in search_triggers) + + def search_repositories(self, query: str) -> str: + """Search repo_index for relevant functions and code""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("SELECT COUNT(*) FROM repo_index WHERE user_id = ?", (self.user_id,)) + count = cursor.fetchone()[0] + print(f"\nšŸ” Searching {count} indexed functions...\n") + + # Extract keywords from query + keywords = re.findall(r'\b\w{4,}\b', query.lower()) + # Add specific search terms + specific_terms = [] + if "exponential" in query.lower() or "decay" in query.lower(): + specific_terms.append("applyForge") + specific_terms.append("exp(") + if "forge" in query.lower(): + specific_terms.append("Forge") + keywords.extend(specific_terms) + + if not keywords: + print("āŒ No search terms found") + conn.close() + return "No search terms provided." + + # Build parameterized query + conditions = [] + params = [] + for keyword in keywords: + conditions.append("(function_name LIKE ? OR content LIKE ? OR repo_name LIKE ?)") + params.extend([f"%{keyword}%", f"%{keyword}%", f"%{keyword}%"]) + + sql = f"SELECT repo_name, file_path, function_name, content FROM repo_index WHERE ({' OR '.join(conditions)}) AND user_id = ? ORDER BY last_modified DESC LIMIT 10" + params.append(self.user_id) + + cursor.execute(sql, params) + results = cursor.fetchall() + conn.close() + if not results: + return f"āŒ No functions found matching: {', '.join(keywords)}\n\nTry: /index to index more repositories" + # Format results + output = f"āœ… Found {len(results)} matches for: {', '.join(set(keywords))}\n\n" + for i, (repo, file_path, func, content) in enumerate(results, 1): + # Extract relevant snippet + lines = content.split('\n') + snippet_lines = [] + for line in lines[:30]: # First 30 lines + if any(kw in line.lower() for kw in keywords): + snippet_lines.append(line) + if len(snippet_lines) >= 10: + break + if not snippet_lines: + snippet_lines = lines[:10] + snippet = '\n'.join(snippet_lines) + output += f"**{i}. {func}()** in {repo}\n" + output += f" šŸ“ {Path(file_path).name}\n" + output += f"\n```cpp\n{snippet}\n```\n" + output += f" ---\n\n" + return output + + def index_local_repositories(self, root_path: str) -> None: + """Crawl directories and index .py, .ino, and .cpp files""" + print(f"\nšŸ” Indexing repositories in: {root_path}") + path = Path(root_path) + + if not path.exists(): + print(f"āŒ Path not found: {root_path}") + return + + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + count = 0 + + for file_path in path.rglob('*'): + if file_path.is_file() and file_path.suffix in ['.py', '.ino', '.cpp', '.h', '.js', '.jsx', '.html', '.css']: + try: + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: + content = f.read() + + functions = [] + + # Python parsing + if file_path.suffix == '.py': + try: + tree = ast.parse(content) + for node in ast.walk(tree): + if isinstance(node, ast.FunctionDef): + functions.append(node.name) + except: + pass + + # C++/Arduino parsing + elif file_path.suffix in ['.ino', '.cpp', '.h']: + matches = re.findall(r'\b(?:void|int|bool|float|double|String|char)\s+(\w+)\s*\(', content) + functions.extend(matches) + + # JS/Web parsing + elif file_path.suffix in ['.js', '.jsx']: + matches = re.findall(r'(?:function\s+(\w+)|const\s+(\w+)\s*=\s*(?:async\s*)?\(?.*?\)?\s*=>)', content) + functions.extend([m[0] or m[1] for m in matches if m[0] or m[1]]) + + # HTML/CSS - Index as whole file + elif file_path.suffix in ['.html', '.css']: + functions.append("file_content") + + # Determine repo name + try: + repo_name = file_path.relative_to(path).parts[0] + except: + repo_name = "unknown" + + timestamp = datetime.fromtimestamp(file_path.stat().st_mtime) + + for func in functions: + cursor.execute(""" + INSERT INTO repo_index (user_id, file_path, repo_name, function_name, content, last_modified) + VALUES (?, ?, ?, ?, ?, ?) + """, (self.user_id, str(file_path), repo_name, func, content, timestamp.isoformat())) + count += 1 + + except Exception: + pass + + conn.commit() + conn.close() + print(f"āœ… Indexed {count} functions across repositories") + + def retrieve_style_context(self, message: str, prompt_template: str, user_name: str) -> str: + """Search repo_index for code snippets matching the request""" + # Extract potential keywords (nouns/modules) + keywords = re.findall(r'\b\w{4,}\b', message.lower()) + if not keywords: + return "" + + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + # Build a search query for function names or repo names + search_terms = " OR ".join([f"function_name LIKE '%{k}%'" for k in keywords]) + search_terms += " OR " + " OR ".join([f"repo_name LIKE '%{k}%'" for k in keywords]) + + query = f"SELECT repo_name, function_name, content FROM repo_index WHERE ({search_terms}) AND user_id = ? LIMIT 2" + + cursor.execute(query, (self.user_id,)) + results = cursor.fetchall() + conn.close() + + if not results: + return "" + + context_block = prompt_template.format(user_name=user_name) + for repo, func, content in results: + # Just grab the first 500 chars of the file to save context window + snippet = content[:500] + "..." + context_block += f"Repo: {repo} | Function: {func}\nCode:\n{snippet}\n---\n" + + return context_block \ No newline at end of file diff --git a/core/buddai_llm.py b/core/buddai_llm.py new file mode 100644 index 0000000..7fa36d5 --- /dev/null +++ b/core/buddai_llm.py @@ -0,0 +1,137 @@ +import json +import http.client +import socket +from typing import List, Dict, Union, Generator, Optional +from core.buddai_shared import MODELS, OLLAMA_POOL, OLLAMA_HOST, OLLAMA_PORT + +class OllamaClient: + """Handles communication with the local Ollama instance""" + + def query(self, model_key: str, messages: List[Dict], stream: bool = False, options: Dict = None) -> Union[str, Generator[str, None, None]]: + """Send a chat request to Ollama""" + model_name = MODELS.get(model_key, model_key) # Handle key or direct name + + default_options = { + "temperature": 0.0, + "top_p": 1.0, + "top_k": 1, + "num_ctx": 1024 + } + if options: + default_options.update(options) + + body = { + "model": model_name, + "messages": messages, + "stream": stream, + "options": default_options + } + + headers = {"Content-Type": "application/json"} + json_body = json.dumps(body) + + # Retry logic for connection stability + for attempt in range(3): + conn = None + try: + # Re-serialize in case of modification (CPU fallback) + json_body = json.dumps(body) + + conn = OLLAMA_POOL.get_connection() + conn.request("POST", "/api/chat", json_body, headers) + response = conn.getresponse() + + if stream: + if response.status != 200: + error_text = response.read().decode('utf-8') + conn.close() + + # GPU OOM Detection -> CPU Fallback + if "CUDA" in error_text or "buffer" in error_text: + if "num_gpu" not in body["options"]: + print("āš ļø GPU OOM detected. Switching to CPU mode...") + body["options"]["num_gpu"] = 0 # Force CPU + continue + + try: + err_msg = f"Error {response.status}: {json.loads(error_text).get('error', error_text)}" + except: + err_msg = f"Error {response.status}: {error_text}" + + if "num_gpu" in body["options"]: + err_msg += "\n\n(āš ļø CPU Mode also failed. System RAM might be full.)" + elif "CUDA" in err_msg or "buffer" in err_msg: + err_msg += "\n\n(āš ļø GPU Out of Memory. Retrying on CPU failed.)" + + return (x for x in [err_msg]) + + return self._stream_response(response, conn) + + if response.status == 200: + data = json.loads(response.read().decode('utf-8')) + OLLAMA_POOL.return_connection(conn) + return data.get("message", {}).get("content", "No response") + else: + error_text = response.read().decode('utf-8') + conn.close() + + if "CUDA" in error_text or "buffer" in error_text: + if "num_gpu" not in body["options"]: + print("āš ļø GPU OOM detected. Switching to CPU mode...") + body["options"]["num_gpu"] = 0 + continue + + return f"Error {response.status}: {error_text}" + + except (http.client.NotConnected, BrokenPipeError, ConnectionResetError, socket.timeout) as e: + if conn: conn.close() + if attempt == 2: + return f"Error: Connection failed. {str(e)}" + continue + except Exception as e: + if conn: conn.close() + return f"Error: {str(e)}" + + def _stream_response(self, response, conn) -> Generator[str, None, None]: + """Yield chunks from HTTP response""" + fully_consumed = False + has_content = False + try: + while True: + line = response.readline() + if not line: break + try: + data = json.loads(line.decode('utf-8')) + if "message" in data: + content = data["message"].get("content", "") + if content: + has_content = True + yield content + if data.get("done"): + fully_consumed = True + break + except: pass + except Exception as e: + yield f"\n[Stream Error: {str(e)}]" + finally: + if fully_consumed: + OLLAMA_POOL.return_connection(conn) + else: + conn.close() + + if not has_content and not fully_consumed: + yield "\n[Error: Empty response from Ollama. Check if model is loaded.]" + + def reset_gpu(self) -> str: + """Force unload models from GPU to free VRAM""" + try: + conn = http.client.HTTPConnection(OLLAMA_HOST, OLLAMA_PORT, timeout=10) + for model in MODELS.values(): + body = json.dumps({"model": model, "keep_alive": 0}) + conn.request("POST", "/api/generate", body) + resp = conn.getresponse() + resp.read() + conn.close() + return "āœ… GPU Memory Cleared (Models Unloaded)" + except Exception as e: + return f"āŒ Error clearing GPU: {str(e)}" \ No newline at end of file diff --git a/buddai_memory.py b/core/buddai_memory.py similarity index 99% rename from buddai_memory.py rename to core/buddai_memory.py index 1a319ee..a80cc1f 100644 --- a/buddai_memory.py +++ b/core/buddai_memory.py @@ -4,7 +4,7 @@ from pathlib import Path from datetime import datetime, timedelta from typing import Optional, List, Dict, Tuple, Union, Generator -from buddai_shared import DB_PATH, MODULE_PATTERNS +from core.buddai_shared import DB_PATH, MODULE_PATTERNS class ShadowSuggestionEngine: """Proactively suggests modules/settings based on user/project history.""" diff --git a/core/buddai_personality.py b/core/buddai_personality.py new file mode 100644 index 0000000..8a23c96 --- /dev/null +++ b/core/buddai_personality.py @@ -0,0 +1,126 @@ +import json +from pathlib import Path +from typing import Dict, Any, Union, List +from datetime import datetime + +class PersonalityManager: + """Manages AI personality, prompts, and user schedules""" + + def __init__(self): + self.personality = self.load_personality() + self.validate_personality_schema() + + def load_personality(self) -> Dict: + """Loads personality from a JSON file.""" + personality_path = Path(__file__).parent.parent / "personality.json" + if personality_path.exists(): + print("🧠 Loading custom personality...") + with open(personality_path, 'r', encoding='utf-8') as f: + return json.load(f) + else: + # Default personality if file doesn't exist + print("🧠 Using default 'James' personality.") + return { + "user_name": "James", + "ai_name": "BuddAI", + "welcome_message": "BuddAI Executive v4.0 - Decoupled & Personality Sync", + "schedule_check_triggers": ["what should i be doing", "my schedule", "schedule check"], + "schedule": { + "weekdays": {"0-4": {"5.5-6.5": "Early Morning Build Session šŸŒ… (5:30-6:30 AM)", "6.5-17.0": "Work Hours (Facilities Caretaker) šŸ¢", "17.0-21.0": "Evening Build Session šŸŒ™ (5:00-9:00 PM)", "default": "Rest Time šŸ’¤"}}, + "saturday": { "5": { "default": "Weekend Freedom šŸŽØ (Creative Mode)" } }, + "sunday": { "6": { "0-21.0": "Weekend Freedom šŸŽØ (Until 9 PM)", "default": "Rest Time šŸ’¤" } } + }, + "style_scan_prompt": "Analyze this code sample from {user_name}'s repositories.\nExtract 3 distinct coding preferences or patterns.", + "style_reference_prompt": "\n[REFERENCE STYLE FROM {user_name}'S PAST PROJECTS]\n", + "integration_task_prompt": "INTEGRATION TASK: Combine modules into a cohesive GilBot system.\n\n[MODULES]\n{modules_summary}\n\n[FORGE PARAMETERS]\nSet k = {k_val} for all applyForge() calls.\n\n[REQUIREMENTS]\n1. Implement applyForge() math helper.\n2. Use k={k_val} to smooth motor and servo transitions.\n3. Ensure naming matches {user_name}'s style: activateFlipper(), setMotors()." + } + + def validate_personality_schema(self) -> bool: + """Validate the loaded personality against required schema.""" + if not self.personality: + return False + + required_structure = { + "meta": ["version"], + "identity": ["user_name", "ai_name"], + "communication": ["welcome_message"], + "work_cycles": ["schedule"], + "forge_theory": ["enabled", "constants"], + "prompts": ["style_scan", "integration_task"] + } + + missing = [] + + version = self.get_value("meta.version") + if version and version != "4.0": + print(f"āš ļø Warning: Personality version mismatch. Loaded: {version}, Expected: 4.0") + + for section, keys in required_structure.items(): + if section not in self.personality: + missing.append(f"Missing section: {section}") + continue + + for key in keys: + if key not in self.personality[section]: + missing.append(f"Missing key: {section}.{key}") + + if missing: + print("āš ļø Personality Schema Validation Failed:") + for m in missing: + print(f" - {m}") + return False + + return True + + def get_value(self, path: Union[str, List[str]], default: Any = None) -> Any: + """Access nested personality keys using dot notation or list of keys.""" + if isinstance(path, str): + keys = path.split('.') + else: + keys = path + + val = self.personality + for key in keys: + if isinstance(val, dict): + val = val.get(key) + else: + return default + return val if val is not None else default + + def get_user_status(self) -> str: + """Determine user's context based on defined schedule from personality file.""" + schedule = self.get_value("work_cycles.schedule") + if not schedule: + # Fallback for simple personality files + schedule = self.personality.get("schedule") + if not schedule: + return "Schedule not defined." + + now = datetime.now() + day = now.weekday() # 0=Mon, 6=Sun + t = now.hour + (now.minute / 60.0) + + # Check all schedule groups (e.g., 'weekdays', 'weekends') + for group, day_ranges in schedule.items(): + for day_range, time_slots in day_ranges.items(): + try: + # Parse day range (e.g., "0-4" or "5") + if '-' in day_range: + start_day, end_day = map(int, day_range.split('-')) + if not (start_day <= day <= end_day): + continue + elif int(day_range) != day: + continue + + # We found the right day group, now check time slots + for time_range, status in time_slots.items(): + if time_range == "default": continue + start_time, end_time = map(float, time_range.split('-')) + if start_time <= t < end_time: + return status.get("description", status) if isinstance(status, dict) else status + + default_status = time_slots.get("default", "No status for this time.") + return default_status.get("description", default_status) if isinstance(default_status, dict) else default_status + + except (ValueError, TypeError): continue + return "No schedule match for today." \ No newline at end of file diff --git a/core/buddai_prompt_engine.py b/core/buddai_prompt_engine.py new file mode 100644 index 0000000..4d47eb0 --- /dev/null +++ b/core/buddai_prompt_engine.py @@ -0,0 +1,310 @@ +import sqlite3 +import re +from typing import List, Dict, Optional +from core.buddai_shared import DB_PATH, COMPLEX_TRIGGERS, MODULE_PATTERNS + +class PromptEngine: + """Handles prompt construction, hardware classification, and request analysis""" + + def classify_hardware(self, user_message: str, context_messages: List[Dict] = None) -> dict: + """Detect what hardware this question is about""" + + hardware = { + "servo": False, + "dc_motor": False, + "button": False, + "led": False, + "sensor": False, + "weapon": False + } + + msg_lower = user_message.lower() + + # Helper to check keywords + def has_keywords(text, keywords): + return any(word in text for word in keywords) + + # Keyword definitions + servo_kws = ['servo', 'mg996', 'sg90'] + motor_kws = ['l298n', 'dc motor', 'motor driver', 'motor control'] + button_kws = ['button', 'switch', 'trigger'] + led_kws = ['led', 'light', 'brightness', 'indicator'] + # Removed 'state machine' from weapon_kws to allow abstract logic + weapon_kws = ['weapon', 'combat', 'arming', 'fire', 'spinner', 'flipper'] + logic_kws = ['state machine', 'logic', 'structure', 'flow', 'armed', 'disarmed'] + + # 1. Check current message first + detected_in_current = False + + if has_keywords(msg_lower, servo_kws): + hardware["servo"] = True + detected_in_current = True + if has_keywords(msg_lower, motor_kws): + hardware["dc_motor"] = True + detected_in_current = True + if has_keywords(msg_lower, button_kws): + hardware["button"] = True + detected_in_current = True + if has_keywords(msg_lower, led_kws): + hardware["led"] = True + detected_in_current = True + if has_keywords(msg_lower, weapon_kws): + hardware["weapon"] = True + detected_in_current = True + if has_keywords(msg_lower, logic_kws): + # Logic detected: Clear context (don't set any hardware) + detected_in_current = True + + # 2. Context Switching: Only look back if NO hardware/logic detected in current message + # and message is short (likely a follow-up command like "make it spin") + if not detected_in_current and len(user_message.split()) < 10 and context_messages: + recent = " ".join([m['content'].lower() for m in context_messages[-2:] if m['role'] == 'user']) + + if has_keywords(recent, servo_kws): hardware["servo"] = True + if has_keywords(recent, motor_kws): hardware["dc_motor"] = True + if has_keywords(recent, button_kws): hardware["button"] = True + if has_keywords(recent, led_kws): hardware["led"] = True + if has_keywords(recent, weapon_kws): hardware["weapon"] = True + + return hardware + + def get_all_rules(self) -> List[str]: + """Get all learned rules as text""" + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("SELECT rule_text FROM code_rules ORDER BY confidence DESC LIMIT 50") + rows = cursor.fetchall() + conn.close() + return [r[0] for r in rows] + + def filter_rules_by_hardware(self, all_rules, hardware): + """Only return rules relevant to detected hardware""" + + relevant_rules = [] + + # Define rule categories + servo_kws = ['servo', 'attach', 'setperiodhertz'] + motor_kws = ['l298n', 'in1', 'in2', 'motor driver'] + weapon_kws = ['arming', 'disarm', 'fire', 'combat'] + button_kws = ['button', 'switch', 'debounce', 'digitalread', 'input_pullup'] + + has_specific_context = hardware["servo"] or hardware["dc_motor"] or hardware["weapon"] or hardware["button"] + + for rule in all_rules: + rule_lower = rule.lower() + + is_servo_rule = any(w in rule_lower for w in servo_kws) + is_motor_rule = any(w in rule_lower for w in motor_kws) + is_weapon_rule = any(w in rule_lower for w in weapon_kws) + is_button_rule = any(w in rule_lower for w in button_kws) + + # Pattern Over-application: Strict filtering + if has_specific_context: + if hardware["dc_motor"] and not hardware["servo"] and is_servo_rule: continue + if hardware["servo"] and not hardware["dc_motor"] and is_motor_rule: continue + if not hardware["weapon"] and is_weapon_rule: continue + if not hardware["button"] and is_button_rule: continue + + # If question is about weapons (logic), EXCLUDE servo rules unless servo explicitly requested + if hardware["weapon"] and not hardware["servo"] and is_servo_rule: continue + + else: + # Generic context: Exclude all specific hardware rules + if is_servo_rule or is_motor_rule or is_weapon_rule or is_button_rule: continue + + relevant_rules.append(rule) + + return relevant_rules + + def build_enhanced_prompt(self, user_message: str, hardware_detected: str = None, context_messages: List[Dict] = None) -> str: + """Build prompt with FILTERED rules""" + + # Classify hardware + hardware = self.classify_hardware(user_message, context_messages) + + # Get ALL rules + all_rules = self.get_all_rules() + + # Filter by relevance + relevant_rules = self.filter_rules_by_hardware(all_rules, hardware) + + # Build focused prompt + hardware_context = [] + if hardware["servo"]: hardware_context.append("SERVO CONTROL") + if hardware["dc_motor"]: hardware_context.append("DC MOTOR CONTROL") + if hardware["button"]: hardware_context.append("BUTTON INPUTS") + if hardware["led"]: hardware_context.append("LED STATUS") + if hardware["weapon"]: hardware_context.append("WEAPON SYSTEM") + + l298n_rules = "" + if hardware["dc_motor"]: + l298n_rules = """ +- L298N WIRING RULES (MANDATORY): + 1. IN1/IN2 = Digital Output (Direction). Use digitalWrite(). + 2. ENA = PWM Output (Speed). Use ledcWrite(). + 3. To Move: IN1/IN2 must be OPPOSITE (HIGH/LOW). + 4. To Stop: IN1/IN2 both LOW. + 5. DO NOT treat Motors like Servos (No 'position' or 'angle'). +- SAFETY RULES (MANDATORY): + 1. Implement a safety timeout (e.g., 5000ms). + 2. Stop motors if no signal is received within timeout. + 3. Use millis() for non-blocking timing. +""" + + weapon_rules = "" + if hardware.get("weapon"): + weapon_rules = """ +- COMBAT PROTOCOL (MANDATORY): + 1. LOGIC FOCUS: This is a State Machine request, NOT just servo movement. + 2. STATES: enum State { DISARMED, ARMING, ARMED, FIRING }; + 3. TRANSITIONS: DISARMED -> ARMING (2s delay) -> ARMED -> FIRING. + 4. SAFETY: Auto-disarm after 10s idle. Fire only when ARMED. + 5. STRUCTURE: Use switch(currentState) { case ... } for logic. + 6. OUTPUTS: Control relays/LEDs/Motors based on state. +""" + + # Anti-bloat rules + anti_bloat_rules = [] + if not hardware["button"]: + anti_bloat_rules.append("- NO EXTRA INPUTS: Do NOT add buttons, switches, or digitalRead() unless explicitly requested.") + if not hardware["servo"]: + anti_bloat_rules.append("- NO EXTRA SERVOS: Do NOT add Servo objects or attach() unless explicitly requested.") + if not hardware["dc_motor"]: + anti_bloat_rules.append("- NO EXTRA MOTORS: Do NOT add motor driver code (L298N) unless explicitly requested.") + + anti_bloat = "\n".join(anti_bloat_rules) + + # Modularity rule + modularity_rule = "" + if "function" in user_message.lower() or "naming" in user_message.lower() or "modular" in user_message.lower(): + modularity_rule = """ +- CODE STRUCTURE (MANDATORY): + 1. NO MONOLITHIC LOOP: Break code into small, descriptive functions. + 2. NAMING: Use camelCase for functions (e.g., readBatteryVoltage(), updateDisplay()). + 3. loop() must ONLY call these functions, not contain raw logic. +""" + + # Status LED rule + status_led_rule = "" + if hardware["led"] and ("status" in user_message.lower() or "indicator" in user_message.lower()): + status_led_rule = """ +- STATUS LED RULES (MANDATORY): + 1. NO BREATHING/FADING: Do not use simple PWM fading loops. + 2. USE STATES: Define enum LEDStatus { OFF, IDLE, ACTIVE, ERROR }; + 3. IMPLEMENTATION: Create void setStatusLED(LEDStatus state). + 4. PATTERNS: IDLE=Slow Blink, ACTIVE=Solid On, ERROR=Fast Blink. +""" + + prompt = f"""You are generating code for: {', '.join(hardware_context)} +You are an expert embedded developer. +TARGET HARDWARE: {hardware_detected} +ACTIVE MODULES: {', '.join(hardware_context) if hardware_context else "None (Logic Only)"} + +CRITICAL: Only use code patterns relevant to the hardware mentioned. +STRICT NEGATIVE CONSTRAINTS (DO NOT IGNORE): +{anti_bloat} + +MANDATORY HARDWARE RULES: +{l298n_rules} +{weapon_rules} +{status_led_rule} +{anti_bloat} +{modularity_rule} + +GENERAL GUIDELINES: +- If DC MOTOR: Use L298N patterns (digitalWrite, ledcWrite) +- If SERVO: Use ESP32Servo patterns (attach, write) +- DO NOT mix servo code into motor questions +- DO NOT mix motor code into servo questions + +CRITICAL RULES (MUST FOLLOW): +{chr(10).join(relevant_rules)} + +USER REQUEST: +{user_message} + +Generate code following ALL rules above. Do not add unrequested features. +FINAL CHECK: +1. Did you add unrequested buttons? REMOVE THEM. +2. Did you add unrequested servos? REMOVE THEM. +3. Generate code ONLY for the hardware requested. +""" + + return prompt + + def is_simple_question(self, message: str) -> bool: + """Check if this is a simple question that should use FAST model""" + message_lower = message.lower() + + simple_triggers = [ + "what is", "what's", "who is", "who's", "when is", + "how do i", "can you explain", "tell me about", + "what are", "where is", "hi", "hello", "hey", + "good morning", "good evening" + ] + + # Also check if it's just a question without code keywords + code_keywords = ["generate", "create", "write", "build", "code", "function"] + + has_simple_trigger = any(trigger in message_lower for trigger in simple_triggers) + has_code_keyword = any(keyword in message_lower for keyword in code_keywords) + + # Simple if: has simple trigger AND no code keywords + return has_simple_trigger and not has_code_keyword + + def is_complex(self, message: str) -> bool: + """Check if request is too complex and should be broken down""" + message_lower = message.lower() + + # Count complexity triggers + trigger_count = sum(1 for trigger in COMPLEX_TRIGGERS if trigger in message_lower) + + # Count how many modules mentioned + module_count = 0 + for module, keywords in MODULE_PATTERNS.items(): + # module is used for key, keywords for values + if any(kw in message_lower for kw in keywords): + module_count += 1 + + # Complex if: multiple triggers OR 3+ modules mentioned + return trigger_count >= 2 or module_count >= 3 + + def extract_modules(self, message: str) -> List[str]: + """Extract which modules are needed""" + message_lower = message.lower() + needed_modules = [] + + for module, keywords in MODULE_PATTERNS.items(): + # module is used for key, keywords for values + if any(kw in message_lower for kw in keywords): + needed_modules.append(module) + + return needed_modules + + def build_modular_plan(self, modules: List[str]) -> List[Dict[str, str]]: + """Create a build plan from modules""" + plan = [] + + module_tasks = { + "ble": "BLE communication setup with phone app control", + "servo": "Servo motor control for flipper/weapon", + "motor": "Motor driver setup for movement (L298N)", + "safety": "Safety timeout and failsafe systems", + "battery": "Battery voltage monitoring", + "sensor": "Sensor integration (distance/proximity)" + } + + for module in modules: + if module in module_tasks: + plan.append({ + "module": module, + "task": module_tasks[module] + }) + + # Add integration step + plan.append({ + "module": "integration", + "task": "Integrate all modules into complete system" + }) + + return plan \ No newline at end of file diff --git a/buddai_shared.py b/core/buddai_shared.py similarity index 97% rename from buddai_shared.py rename to core/buddai_shared.py index 53dc0ce..8a25fac 100644 --- a/buddai_shared.py +++ b/core/buddai_shared.py @@ -5,7 +5,7 @@ import queue import http.client # Global Config -DATA_DIR = Path(__file__).parent / "data" +DATA_DIR = Path(__file__).parent.parent / "data" DB_PATH = DATA_DIR / "conversations.db" OLLAMA_HOST = os.getenv("OLLAMA_HOST", "127.0.0.1") OLLAMA_PORT = int(os.getenv("OLLAMA_PORT", "11434")) diff --git a/core/buddai_storage.py b/core/buddai_storage.py new file mode 100644 index 0000000..2339a70 --- /dev/null +++ b/core/buddai_storage.py @@ -0,0 +1,240 @@ +import sqlite3 +import json +from datetime import datetime +from pathlib import Path +from typing import List, Dict, Tuple, Optional +from core.buddai_shared import DB_PATH, DATA_DIR + +class StorageManager: + """Manages Database, Sessions, and Backups""" + + def __init__(self, user_id: str): + self.user_id = user_id + self.current_session_id = None + self.ensure_data_dir() + self.init_database() + self.start_new_session() + + def ensure_data_dir(self) -> None: + DATA_DIR.mkdir(exist_ok=True) + + def init_database(self) -> None: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + # Core Tables + cursor.execute(""" + CREATE TABLE IF NOT EXISTS sessions ( + session_id TEXT PRIMARY KEY, + user_id TEXT, + started_at TIMESTAMP, + ended_at TIMESTAMP, + title TEXT + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_id TEXT, + role TEXT, + content TEXT, + timestamp TIMESTAMP + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS repo_index ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT, + file_path TEXT, + repo_name TEXT, + function_name TEXT, + content TEXT, + last_modified TIMESTAMP + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS style_preferences ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT, + category TEXT, + preference TEXT, + confidence FLOAT, + extracted_at TIMESTAMP + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS feedback ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + message_id INTEGER, + positive BOOLEAN, + comment TEXT, + timestamp TIMESTAMP + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS corrections ( + id INTEGER PRIMARY KEY, + timestamp TEXT, + original_code TEXT, + corrected_code TEXT, + reason TEXT, + context TEXT, + processed BOOLEAN DEFAULT 0 + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS compilation_log ( + id INTEGER PRIMARY KEY, + timestamp TEXT, + code TEXT, + success BOOLEAN, + errors TEXT, + hardware TEXT + ) + """) + + cursor.execute(""" + CREATE TABLE IF NOT EXISTS code_rules ( + id INTEGER PRIMARY KEY, + rule_text TEXT, + pattern_find TEXT, + pattern_replace TEXT, + context TEXT, + confidence FLOAT, + learned_from TEXT, + times_applied INTEGER DEFAULT 0 + ) + """) + + # Migrations (Idempotent) + try: cursor.execute("ALTER TABLE sessions ADD COLUMN title TEXT") + except: pass + try: cursor.execute("ALTER TABLE sessions ADD COLUMN user_id TEXT") + except: pass + try: cursor.execute("ALTER TABLE repo_index ADD COLUMN user_id TEXT") + except: pass + try: cursor.execute("ALTER TABLE style_preferences ADD COLUMN user_id TEXT") + except: pass + try: cursor.execute("ALTER TABLE feedback ADD COLUMN comment TEXT") + except: pass + try: cursor.execute("ALTER TABLE corrections ADD COLUMN processed BOOLEAN DEFAULT 0") + except: pass + + conn.commit() + conn.close() + + def create_session(self) -> str: + now = datetime.now() + base_id = now.strftime("%Y%m%d_%H%M%S") + session_id = base_id + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + counter = 0 + while True: + try: + cursor.execute( + "INSERT INTO sessions (session_id, user_id, started_at) VALUES (?, ?, ?)", + (session_id, self.user_id, now.isoformat()) + ) + conn.commit() + break + except sqlite3.IntegrityError: + counter += 1 + session_id = f"{base_id}_{counter}" + + conn.close() + return session_id + + def start_new_session(self) -> str: + self.current_session_id = self.create_session() + return self.current_session_id + + def end_session(self) -> None: + if not self.current_session_id: return + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute( + "UPDATE sessions SET ended_at = ? WHERE session_id = ?", + (datetime.now().isoformat(), self.current_session_id) + ) + conn.commit() + conn.close() + + def save_message(self, role: str, content: str) -> int: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute( + "INSERT INTO messages (session_id, role, content, timestamp) VALUES (?, ?, ?, ?)", + (self.current_session_id, role, content, datetime.now().isoformat()) + ) + msg_id = cursor.lastrowid + conn.commit() + conn.close() + return msg_id + + def get_sessions(self, limit: int = 20) -> List[Dict[str, str]]: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("SELECT session_id, started_at, title FROM sessions WHERE user_id = ? ORDER BY started_at DESC LIMIT ?", (self.user_id, limit)) + rows = cursor.fetchall() + conn.close() + return [{"id": r[0], "date": r[1], "title": r[2] if len(r) > 2 else None} for r in rows] + + def rename_session(self, session_id: str, new_title: str) -> None: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("UPDATE sessions SET title = ? WHERE session_id = ? AND user_id = ?", (new_title, session_id, self.user_id)) + conn.commit() + conn.close() + + def delete_session(self, session_id: str) -> None: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("DELETE FROM sessions WHERE session_id = ? AND user_id = ?", (session_id, self.user_id)) + if cursor.rowcount > 0: + cursor.execute("DELETE FROM messages WHERE session_id = ?", (session_id,)) + conn.commit() + conn.close() + + def clear_current_session(self) -> None: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("DELETE FROM messages WHERE session_id = ?", (self.current_session_id,)) + conn.commit() + conn.close() + + def load_session(self, session_id: str) -> List[Dict[str, str]]: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + cursor.execute("SELECT 1 FROM sessions WHERE session_id = ? AND user_id = ?", (session_id, self.user_id)) + if not cursor.fetchone(): + conn.close() + return [] + + cursor.execute("SELECT id, role, content, timestamp FROM messages WHERE session_id = ? ORDER BY id ASC", (session_id,)) + rows = cursor.fetchall() + conn.close() + + self.current_session_id = session_id + return [{"id": r[0], "role": r[1], "content": r[2], "timestamp": r[3]} for r in rows] + + def create_backup(self) -> Tuple[bool, str]: + if not DB_PATH.exists(): return False, "Database file not found." + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + backup_dir = DATA_DIR / "backups" + backup_dir.mkdir(exist_ok=True) + backup_path = backup_dir / f"conversations_{timestamp}.db" + try: + src = sqlite3.connect(DB_PATH); dst = sqlite3.connect(backup_path) + with dst: src.backup(dst) + dst.close(); src.close() + return True, str(backup_path) + except Exception as e: return False, str(e) \ No newline at end of file diff --git a/core/buddai_training.py b/core/buddai_training.py new file mode 100644 index 0000000..d95e8d6 --- /dev/null +++ b/core/buddai_training.py @@ -0,0 +1,41 @@ +import sqlite3 +import json +from core.buddai_shared import DB_PATH, DATA_DIR + +class ModelFineTuner: + """Fine-tune local model on YOUR corrections""" + + def prepare_training_data(self): + """Convert corrections to training format""" + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + cursor.execute(""" + SELECT original_code, corrected_code, reason + FROM corrections + """) + + training_data = [] + for original, corrected, reason in cursor.fetchall(): + training_data.append({ + "prompt": f"Generate code for: {reason}", + "completion": corrected, + "negative_example": original + }) + + conn.close() + + # Save as JSONL for fine-tuning + output_path = DATA_DIR / 'training_data.jsonl' + with open(output_path, 'w', encoding='utf-8') as f: + for item in training_data: + f.write(json.dumps(item) + '\n') + return f"Exported {len(training_data)} examples to {output_path}" + + def fine_tune_model(self): + """Fine-tune Qwen on your corrections""" + # This requires: + # 1. Export training data + # 2. Use Ollama modelfile or external training + # 3. Create custom model: qwen2.5-coder-james:3b + pass \ No newline at end of file diff --git a/buddai_logic.py b/core/buddai_validation.py similarity index 90% rename from buddai_logic.py rename to core/buddai_validation.py index da91c9a..7dc40a6 100644 --- a/buddai_logic.py +++ b/core/buddai_validation.py @@ -1,10 +1,5 @@ -#!/usr/bin/env python3 -import sys, os, json, logging, sqlite3, datetime, pathlib, http.client, re, typing, zipfile, shutil, queue, socket, argparse, io, difflib -from pathlib import Path -from datetime import datetime, timedelta -from typing import Optional, List, Dict, Tuple, Union, Generator - -from buddai_shared import DB_PATH +import re +from typing import List, Dict, Tuple, Optional class CodeValidator: """Validate generated code before showing to user""" @@ -469,8 +464,6 @@ class CodeValidator: return fixed_code - - class HardwareProfile: """Learn hardware-specific patterns""" @@ -528,75 +521,4 @@ class HardwareProfile: def add_safety(self, code: str) -> str: if "motor" in code.lower() and "millis()" not in code: code += "\n// [BuddAI Safety] Warning: No non-blocking timeout detected. Consider adding safety timeout." - return code - - - -class LearningMetrics: - """Measure BuddAI's improvement over time""" - - def calculate_accuracy(self): - """What % of code is accepted without correction?""" - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - - thirty_days_ago = (datetime.now() - timedelta(days=30)).isoformat() - - cursor.execute(""" - SELECT - COUNT(*) as total_responses, - COUNT(CASE WHEN f.positive = 1 THEN 1 END) as positive_feedback, - COUNT(CASE WHEN c.id IS NOT NULL THEN 1 END) as corrected - FROM messages m - LEFT JOIN feedback f ON m.id = f.message_id - LEFT JOIN corrections c ON m.content LIKE '%' || c.original_code || '%' - WHERE m.role = 'assistant' - AND m.timestamp > ? - """, (thirty_days_ago,)) - - total, positive, corrected = cursor.fetchone() - conn.close() - - accuracy = (positive / total) * 100 if total and total > 0 else 0 - correction_rate = (corrected / total) * 100 if total and total > 0 else 0 - - return { - "accuracy": accuracy, - "correction_rate": correction_rate, - "improvement": self.calculate_trend() - } - - def calculate_trend(self): - """Is BuddAI getting better over time?""" - # Compare last 7 days vs previous 7 days - recent = self.get_accuracy_for_period(7) - previous = self.get_accuracy_for_period(7, offset=7) - - improvement = recent - previous - return f"+{improvement:.1f}%" if improvement > 0 else f"{improvement:.1f}%" - - def get_accuracy_for_period(self, days: int, offset: int = 0) -> float: - conn = sqlite3.connect(DB_PATH) - cursor = conn.cursor() - - start_dt = (datetime.now() - timedelta(days=days + offset)).isoformat() - end_dt = (datetime.now() - timedelta(days=offset)).isoformat() - - cursor.execute(""" - SELECT - COUNT(*) as total, - COUNT(CASE WHEN f.positive = 1 THEN 1 END) as positive - FROM messages m - LEFT JOIN feedback f ON m.id = f.message_id - WHERE m.role = 'assistant' - AND m.timestamp BETWEEN ? AND ? - """, (start_dt, end_dt)) - - row = cursor.fetchone() - conn.close() - - if not row: - return 0.0 - - total, positive = row - return (positive / total) * 100 if total and total > 0 else 0.0 + return code \ No newline at end of file diff --git a/decouple_buddai.py b/decouple_buddai.py deleted file mode 100644 index 1c2d31b..0000000 --- a/decouple_buddai.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import re - -def decouple_exocortex(source_file): - with open(source_file, 'r', encoding='utf-8') as f: - content = f.read() - - # Define the file splits based on class/block signatures - splits = { - "buddai_memory.py": ["class ShadowSuggestionEngine", "class AdaptiveLearner", "class SmartLearner"], - "buddai_logic.py": ["class CodeValidator", "class HardwareProfile", "class LearningMetrics"], - "buddai_executive.py": ["class OllamaConnectionPool", "class BuddAI", "class ModelFineTuner"], - "buddai_server.py": ["if SERVER_AVAILABLE:", "app = FastAPI", "class BuddAIManager"] - } - - print(f"šŸš€ Surgical extraction of {source_file} initiated...") - - # Extraction logic for classes/blocks - for filename, markers in splits.items(): - extracted_sections = [] - for marker in markers: - # Simple extraction based on class indentation/block end - pattern = re.compile(rf"{re.escape(marker)}.*?(?=\nclass |\nif __name__ ==|\nif SERVER_AVAILABLE)", re.DOTALL) - match = pattern.search(content) - if match: - extracted_sections.append(match.group(0)) - - if extracted_sections: - with open(filename, 'w', encoding='utf-8') as f: - f.write("#!/usr/bin/env python3\n") - f.write("import sys, os, json, logging, sqlite3, datetime, pathlib, http.client, re, typing, zipfile, shutil, queue, socket, argparse, io, difflib\n") - f.write("from pathlib import Path\nfrom datetime import datetime, timedelta\nfrom typing import Optional, List, Dict, Tuple, Union, Generator\n\n") - f.write("try:\n from fastapi import FastAPI, File, Header, Response, UploadFile, WebSocketDisconnect, Request, WebSocket\n from fastapi.middleware.cors import CORSMiddleware\n from fastapi.responses import FileResponse, HTMLResponse, JSONResponse\n from fastapi.staticfiles import StaticFiles\n from pydantic import BaseModel\n import uvicorn\nexcept ImportError:\n pass\n\n") - f.write("\n\n".join(extracted_sections)) - print(f"āœ… Created {filename}") - -if __name__ == "__main__": - # Use the script's directory to find main.py reliably - source_path = os.path.join(os.path.dirname(__file__), "main.py") - if os.path.exists(source_path): - decouple_exocortex(source_path) - else: - print(f"āŒ Error: Could not find {source_path}") \ No newline at end of file diff --git a/main.py b/main.py index 95ed4b2..7863fbc 100644 --- a/main.py +++ b/main.py @@ -11,7 +11,7 @@ import socket import uvicorn # --- Import The Organs --- -from buddai_shared import OLLAMA_HOST, OLLAMA_PORT, SERVER_AVAILABLE +from core.buddai_shared import OLLAMA_HOST, OLLAMA_PORT, SERVER_AVAILABLE from buddai_executive import BuddAI # If server dependencies are present, import the app diff --git a/skills/__init__.py b/skills/__init__.py new file mode 100644 index 0000000..69041cd --- /dev/null +++ b/skills/__init__.py @@ -0,0 +1,41 @@ +import importlib +import pkgutil +import logging +from pathlib import Path + +# Configure local logger +logger = logging.getLogger("BuddAI-Skills") + +def load_registry(): + """ + Dynamically discovers and loads skill modules from the current directory. + Returns a dictionary mapping skill IDs to their executable functions and metadata. + """ + registry = {} + package_dir = Path(__file__).parent + + # Iterate over all .py files in this directory + for _, name, _ in pkgutil.iter_modules([str(package_dir)]): + try: + # Import the module relative to this package + module = importlib.import_module(f".{name}", __package__) + + # Verify the Skill Interface (must have 'meta' and 'run') + if hasattr(module, "meta") and hasattr(module, "run"): + metadata = module.meta() + skill_id = name + + registry[skill_id] = { + "name": metadata.get("name", skill_id), + "triggers": metadata.get("triggers", []), + "description": metadata.get("description", "No description provided."), + "run": module.run + } + logger.info(f"🧩 Skill Loaded: {metadata.get('name')} [{skill_id}]") + else: + logger.debug(f"Skipping {name}: Does not implement Skill Interface.") + + except Exception as e: + logger.error(f"āŒ Error loading skill '{name}': {e}") + + return registry \ No newline at end of file diff --git a/skills/calculator.py b/skills/calculator.py new file mode 100644 index 0000000..469cc6c --- /dev/null +++ b/skills/calculator.py @@ -0,0 +1,47 @@ +import re + +def meta(): + """ + Defines the metadata for the Calculator skill. + """ + return { + "name": "Smart Calculator", + "description": "Performs basic arithmetic operations detected in the prompt.", + "triggers": ["calculate", "compute", "solve", "math", "+", "-", "*", "/"] + } + +def run(payload): + """ + Executes the calculation logic. + Accepts a string prompt or a dictionary context. + """ + # Normalize input + prompt = payload if isinstance(payload, str) else payload.get("prompt", "") + + # 1. Extract the mathematical expression + # Regex looks for sequences of numbers and operators + # Allowed: digits, whitespace, +, -, *, /, ., (, ) + match = re.search(r'([\d\.\s\+\-\*\/\(\)]+)', prompt) + + if not match: + return None # Fallback to LLM if no math found + + expression = match.group(0).strip() + + if not any(char.isdigit() for char in expression): + return None + + # 2. Safety Check (Double verification) + allowed_chars = set("0123456789.+-*/() ") + if not set(expression).issubset(allowed_chars): + return "Calculation aborted: Invalid characters detected." + + # 3. Execute + try: + # pylint: disable=eval-used + result = eval(expression, {"__builtins__": None}, {}) + return f"🧮 Result: {expression} = {result}" + except ZeroDivisionError: + return "🧮 Error: Division by zero is not allowed." + except Exception as e: + return f"🧮 Calculation Error: {str(e)}" \ No newline at end of file diff --git a/skills/system_info.py b/skills/system_info.py new file mode 100644 index 0000000..0b0de44 --- /dev/null +++ b/skills/system_info.py @@ -0,0 +1,27 @@ +import psutil + +def meta(): + """ + Metadata for the System Info skill. + """ + return { + "name": "System Info", + "description": "Reports current CPU and RAM usage.", + "triggers": ["cpu usage", "ram usage", "memory usage", "system stats", "how much ram", "cpu load"] + } + +def run(payload): + """ + Fetches system metrics. + """ + # interval=0.1 ensures we get a fresh sample (blocking briefly) + cpu_usage = psutil.cpu_percent(interval=0.1) + + mem = psutil.virtual_memory() + total_gb = mem.total / (1024 ** 3) + used_gb = mem.used / (1024 ** 3) + percent_used = mem.percent + + return (f"šŸ–„ļø System Vital Signs:\n" + f" 🧠 CPU Load: {cpu_usage}%\n" + f" šŸ’¾ RAM Usage: {percent_used}% ({used_gb:.1f}GB / {total_gb:.1f}GB)") \ No newline at end of file diff --git a/skills/test_all.py b/skills/test_all.py new file mode 100644 index 0000000..f9dea1c --- /dev/null +++ b/skills/test_all.py @@ -0,0 +1,68 @@ +import unittest +import io +import sys +from pathlib import Path + +def meta(): + """ + Metadata for the Test Runner skill. + """ + return { + "name": "Self-Diagnostic", + "description": "Runs the internal unit test suite (tests/*.py).", + "triggers": ["test all", "run tests", "self diagnostic", "check systems", "verify integrity"] + } + +def run(payload): + """ + Discovers and runs tests in the tests/ directory. + """ + # Root dir is parent of skills/ (i.e., buddAI/) + root_dir = Path(__file__).parent.parent + tests_dir = root_dir / "tests" + + if not tests_dir.exists(): + return "āŒ Diagnostics failed: 'tests' directory not found." + + # Capture output + log_capture = io.StringIO() + + # Create a test runner that writes to our capture stream + runner = unittest.TextTestRunner(stream=log_capture, verbosity=1) + loader = unittest.TestLoader() + + try: + # Ensure root_dir is in sys.path so tests can import 'core', 'skills', etc. + if str(root_dir) not in sys.path: + sys.path.insert(0, str(root_dir)) + + # Discover tests + suite = loader.discover(str(tests_dir), pattern="test_*.py", top_level_dir=str(root_dir)) + + num_tests = suite.countTestCases() + if num_tests == 0: + return "āš ļø No tests found in tests/ directory." + + # Run tests + result = runner.run(suite) + + # Get output string + output = log_capture.getvalue() + + # Construct response + header = "āœ… **All Systems Operational**" if result.wasSuccessful() else "āŒ **System Failures Detected**" + stats = f"Executed {result.testsRun} tests." + + if not result.wasSuccessful(): + stats += f"\nšŸ”“ Failures: {len(result.failures)}" + stats += f"\nāš ļø Errors: {len(result.errors)}" + + # Limit output length for chat + console_output = output + if len(console_output) > 1500: + console_output = "..." + console_output[-1500:] + + return f"{header}\n{stats}\n\n**Console Output:**\n```text\n{console_output}\n```" + + except Exception as e: + return f"āŒ Execution Error: {str(e)}" \ No newline at end of file diff --git a/skills/timer.py b/skills/timer.py new file mode 100644 index 0000000..cd47f6b --- /dev/null +++ b/skills/timer.py @@ -0,0 +1,44 @@ +import time +import re +import threading + +def meta(): + """ + Metadata for the Timer skill. + """ + return { + "name": "Timer", + "description": "Sets a non-blocking timer (background thread).", + "triggers": ["timer", "sleep", "wait for"] + } + +def run(payload): + """ + Executes the blocking sleep. + """ + prompt = payload if isinstance(payload, str) else payload.get("prompt", "") + + # Regex to capture number and optional unit (e.g., "5", "5s", "5 minutes") + match = re.search(r'(\d+)\s*(seconds?|secs?|s|minutes?|mins?|m)?', prompt.lower()) + + if not match: + return None # Fallback to LLM if no time found + + amount = int(match.group(1)) + unit = match.group(2) + + duration = amount + if unit and unit.startswith('m'): + duration *= 60 + + if duration > 3600: + return f"āŒ Timer too long ({duration}s). Max 1 hour." + + def _timer_thread(): + time.sleep(duration) + print(f"\n\nā° šŸ”” BEEP! Timer finished ({duration}s).\n") + + t = threading.Thread(target=_timer_thread, daemon=True) + t.start() + + return f"ā° Timer started for {duration} seconds (running in background)..." \ No newline at end of file diff --git a/skills/weather.py b/skills/weather.py new file mode 100644 index 0000000..90bc64b --- /dev/null +++ b/skills/weather.py @@ -0,0 +1,44 @@ +import urllib.request +import urllib.parse +import re + +def meta(): + """ + Metadata for the Weather skill. + """ + return { + "name": "Weather", + "description": "Fetches current weather using wttr.in (no API key required).", + "triggers": ["weather", "temperature", "forecast"] + } + +def run(payload): + """ + Fetches weather data. + """ + prompt = payload if isinstance(payload, str) else payload.get("prompt", "") + + location = "" + # Extract location: "weather in London", "weather for Paris" + match = re.search(r'\b(?:in|for|at)\s+(.+)', prompt, re.IGNORECASE) + if match: + location = match.group(1).strip().rstrip("?.!") + + try: + query = urllib.parse.quote(location) if location else "" + # format=3 gives a concise one-line output (e.g., "London: ā›…ļø +13°C") + url = f"https://wttr.in/{query}?format=3" + + req = urllib.request.Request( + url, + headers={'User-Agent': 'curl/7.68.0'} # Mimic curl to ensure text output + ) + + with urllib.request.urlopen(req, timeout=5) as response: + if response.status == 200: + result = response.read().decode('utf-8').strip() + return f"šŸŒ¦ļø {result}" + return f"āŒ Weather error: {response.status}" + + except Exception as e: + return f"āŒ Failed to fetch weather: {str(e)}" \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_all.py b/tests/test_all.py new file mode 100644 index 0000000..b88189d --- /dev/null +++ b/tests/test_all.py @@ -0,0 +1,29 @@ +import unittest +import sys +import os + +def run_suite(): + """ + Discover and run all tests in the tests/ directory. + """ + # Get directories + tests_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(tests_dir) + + # Add project root to sys.path to allow imports of 'core', 'skills', etc. + if project_root not in sys.path: + sys.path.insert(0, project_root) + + # Discover tests + loader = unittest.TestLoader() + suite = loader.discover(tests_dir, pattern="test_*.py", top_level_dir=project_root) + + # Run tests + runner = unittest.TextTestRunner(verbosity=2) + result = runner.run(suite) + + return result.wasSuccessful() + +if __name__ == "__main__": + success = run_suite() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/tests/test_buddai.py b/tests/test_buddai.py index 3b69320..dcda2a9 100644 --- a/tests/test_buddai.py +++ b/tests/test_buddai.py @@ -22,10 +22,10 @@ import http.client # Dynamic import of buddai_v3.2.py REPO_ROOT = Path(__file__).parent.parent -MODULE_PATH = REPO_ROOT / "buddai_v3.2.py" -spec = importlib.util.spec_from_file_location("buddai_v3_2", MODULE_PATH) +MODULE_PATH = REPO_ROOT / "buddai_executive.py" +spec = importlib.util.spec_from_file_location("buddai_executive", MODULE_PATH) buddai_module = importlib.util.module_from_spec(spec) -sys.modules["buddai_v3_2"] = buddai_module +sys.modules["buddai_executive"] = buddai_module spec.loader.exec_module(buddai_module) BuddAI = buddai_module.BuddAI @@ -564,12 +564,12 @@ def test_schedule_awareness(): print_test("Schedule Awareness") # Mock datetime to test different times - with patch('buddai_v3_2.datetime') as mock_date: + with patch('core.buddai_personality.datetime') as mock_date: # 1. Early Morning (Monday 6:00 AM) mock_date.now.return_value = datetime(2025, 12, 29, 6, 0, 0) buddai = BuddAI(server_mode=False) - status = buddai.get_user_status() + status = buddai.personality_manager.get_user_status() if "Early Morning" in status: print_pass(f"6:00 AM Mon -> {status}") @@ -579,7 +579,7 @@ def test_schedule_awareness(): # 2. Work Hours (Monday 10:00 AM) mock_date.now.return_value = datetime(2025, 12, 29, 10, 0, 0) - status = buddai.get_user_status() + status = buddai.personality_manager.get_user_status() if "Work Hours" in status: print_pass(f"10:00 AM Mon -> {status}") @@ -619,7 +619,7 @@ def test_session_management(): test_db = Path(test_db_path) try: - with patch('buddai_v3_2.DB_PATH', test_db): + with patch('buddai_executive.DB_PATH', test_db): buddai = BuddAI(server_mode=False) # 1. Create @@ -665,8 +665,8 @@ def test_rapid_session_creation(): # Mock datetime to return a fixed time, forcing ID collisions fixed_time = datetime(2025, 1, 1, 12, 0, 0) - with patch('buddai_v3_2.DB_PATH', test_db): - with patch('buddai_v3_2.datetime') as mock_dt: + with patch('buddai_executive.DB_PATH', test_db): + with patch('buddai_executive.datetime') as mock_dt: mock_dt.now.return_value = fixed_time buddai = BuddAI(server_mode=False) @@ -711,7 +711,7 @@ def test_repo_isolation(): (repo_path / "user1_secret.py").write_text("def user1_secret_function():\n pass") try: - with patch('buddai_v3_2.DB_PATH', test_db): + with patch('buddai_executive.DB_PATH', test_db): # Suppress internal prints to keep test output clean with patch('builtins.print'): # User 1 indexes the repo @@ -815,7 +815,7 @@ def test_websocket_logic(): test_db = Path(test_db_path) try: - with patch('buddai_v3_2.DB_PATH', test_db): + with patch('buddai_executive.DB_PATH', test_db): # Suppress prints during init with patch('builtins.print'): buddai = BuddAI(server_mode=False) @@ -932,7 +932,7 @@ def test_feedback_system(): test_db = Path(test_db_path) try: - with patch('buddai_v3_2.DB_PATH', test_db): + with patch('buddai_executive.DB_PATH', test_db): # Suppress prints with patch('builtins.print'): buddai = BuddAI(server_mode=False) diff --git a/tests/test_buddai_v3_2.py b/tests/test_buddai_v3_2.py index eee32df..f394199 100644 --- a/tests/test_buddai_v3_2.py +++ b/tests/test_buddai_v3_2.py @@ -10,18 +10,19 @@ import importlib.util from pathlib import Path from typing import List, Dict, Optional from unittest.mock import MagicMock, patch +from core.buddai_prompt_engine import PromptEngine # Load buddai_v3.2.py dynamically due to version number in filename REPO_ROOT = Path(__file__).parent.parent -MODULE_PATH = REPO_ROOT / "buddai_v3.2.py" +MODULE_PATH = REPO_ROOT / "buddai_executive.py" if not MODULE_PATH.exists(): print(f"Error: Could not find {MODULE_PATH}") sys.exit(1) -spec = importlib.util.spec_from_file_location("buddai_v3_2", MODULE_PATH) +spec = importlib.util.spec_from_file_location("buddai_executive", MODULE_PATH) buddai_module = importlib.util.module_from_spec(spec) -sys.modules["buddai_v3_2"] = buddai_module +sys.modules["buddai_executive"] = buddai_module spec.loader.exec_module(buddai_module) BuddAI = buddai_module.BuddAI @@ -52,20 +53,20 @@ class TestBuddAITypesAndLogic(unittest.TestCase): self.assertEqual(chat_hints['return'], str) # is_complex - self.assertEqual(BuddAI.is_complex.__annotations__['return'], bool) + self.assertEqual(PromptEngine.is_complex.__annotations__['return'], bool) # extract_modules - self.assertEqual(BuddAI.extract_modules.__annotations__['return'], List[str]) + self.assertEqual(PromptEngine.extract_modules.__annotations__['return'], List[str]) # build_modular_plan - self.assertEqual(BuddAI.build_modular_plan.__annotations__['return'], List[Dict[str, str]]) + self.assertEqual(PromptEngine.build_modular_plan.__annotations__['return'], List[Dict[str, str]]) def test_routing_simple_question(self): """Test that simple questions route to the FAST model""" with patch.object(self.buddai, 'call_model', return_value="Fast response") as mock_call: response = self.buddai._route_request("What is a servo?", force_model=None, forge_mode="2") - mock_call.assert_called_with("fast", "What is a servo?") + mock_call.assert_called_with("fast", "What is a servo?", system_task=True) self.assertEqual(response, "Fast response") def test_routing_complex_request(self): @@ -74,7 +75,7 @@ class TestBuddAITypesAndLogic(unittest.TestCase): with patch.object(self.buddai, 'execute_modular_build', return_value="Modular code") as mock_build: # Mock is_complex to ensure it returns True for this test case - with patch.object(self.buddai, 'is_complex', return_value=True): + with patch.object(self.buddai.prompt_engine, 'is_complex', return_value=True): response = self.buddai._route_request(complex_msg, force_model=None, forge_mode="2") mock_build.assert_called() @@ -84,11 +85,11 @@ class TestBuddAITypesAndLogic(unittest.TestCase): """Test that search queries route to repository search""" search_msg = "Show me functions using applyForge" - with patch.object(self.buddai, 'search_repositories', return_value="Search results") as mock_search: + with patch.object(self.buddai.repo_manager, 'search_repositories', return_value="Search results") as mock_search: # Mock is_search_query to ensure True - with patch.object(self.buddai, 'is_search_query', return_value=True): + with patch.object(self.buddai.repo_manager, 'is_search_query', return_value=True): # Ensure is_complex is False so it doesn't preempt search - with patch.object(self.buddai, 'is_complex', return_value=False): + with patch.object(self.buddai.prompt_engine, 'is_complex', return_value=False): response = self.buddai._route_request(search_msg, force_model=None, forge_mode="2") mock_search.assert_called_with(search_msg) @@ -105,7 +106,7 @@ class TestBuddAITypesAndLogic(unittest.TestCase): def test_extract_modules(self): """Verify module extraction logic""" msg = "I need a robot with bluetooth and a flipper weapon" - modules = self.buddai.extract_modules(msg) + modules = self.buddai.prompt_engine.extract_modules(msg) self.assertIn("ble", modules) self.assertIn("servo", modules) self.assertNotIn("motor", modules) diff --git a/tests/test_integration.py b/tests/test_integration.py index c3d54cb..9da33b0 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -18,18 +18,25 @@ import json # Dynamic import of buddai_v3.2.py REPO_ROOT = Path(__file__).parent.parent -MODULE_PATH = REPO_ROOT / "buddai_v3.2.py" -spec = importlib.util.spec_from_file_location("buddai_v3_2", MODULE_PATH) +MODULE_PATH = REPO_ROOT / "buddai_executive.py" +spec = importlib.util.spec_from_file_location("buddai_executive", MODULE_PATH) buddai_module = importlib.util.module_from_spec(spec) -sys.modules["buddai_v3_2"] = buddai_module +sys.modules["buddai_executive"] = buddai_module spec.loader.exec_module(buddai_module) # Check for server dependencies SERVER_AVAILABLE = getattr(buddai_module, "SERVER_AVAILABLE", False) if SERVER_AVAILABLE: + # Load buddai_server.py dynamically to get 'app' + SERVER_PATH = REPO_ROOT / "buddai_server.py" + spec_server = importlib.util.spec_from_file_location("buddai_server", SERVER_PATH) + server_module = importlib.util.module_from_spec(spec_server) + sys.modules["buddai_server"] = server_module + spec_server.loader.exec_module(server_module) + from fastapi.testclient import TestClient - app = buddai_module.app + app = server_module.app client = TestClient(app) else: print("āš ļø Server dependencies missing. Integration tests skipped.") @@ -43,7 +50,7 @@ class TestBuddAIIntegration(unittest.TestCase): os.close(self.db_fd) # Patch DB_PATH in the module - self.db_patcher = patch("buddai_v3_2.DB_PATH", Path(self.db_path)) + self.db_patcher = patch("buddai_executive.DB_PATH", Path(self.db_path)) self.mock_db_path = self.db_patcher.start() # Reset the manager to ensure fresh BuddAI instances connected to temp DB @@ -143,9 +150,9 @@ class TestBuddAIIntegration(unittest.TestCase): def test_upload_api(self): """Test file upload endpoint""" with tempfile.TemporaryDirectory() as tmp_data_dir: - with patch("buddai_v3_2.DATA_DIR", Path(tmp_data_dir)): + with patch("buddai_executive.DATA_DIR", Path(tmp_data_dir)): # Mock indexing to avoid parsing logic - with patch.object(buddai_module.BuddAI, 'index_local_repositories') as mock_index: + with patch.object(buddai_module.RepoManager, 'index_local_repositories') as mock_index: # Create dummy file files = {'file': ('test.py', b'print("hello")', 'text/x-python')} diff --git a/tests/test_skills.py b/tests/test_skills.py new file mode 100644 index 0000000..f877e19 --- /dev/null +++ b/tests/test_skills.py @@ -0,0 +1,56 @@ +import unittest +from unittest.mock import patch, MagicMock +import sys +import os + +# Add parent directory to path so we can import 'skills' +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from skills import load_registry + +class TestSkills(unittest.TestCase): + + def setUp(self): + self.registry = load_registry() + + def test_registry_loading(self): + """Ensure skills are discovered and loaded""" + self.assertGreater(len(self.registry), 0, "No skills loaded") + # Check for core skills + self.assertIn("calculator", self.registry) + self.assertIn("weather", self.registry) + self.assertIn("timer", self.registry) + self.assertIn("system_info", self.registry) + self.assertIn("test_all", self.registry) + + def test_calculator_logic(self): + """Verify calculator skill math""" + calc = self.registry["calculator"]["run"] + self.assertIn("4", calc("Calculate 2 + 2")) + self.assertIn("25", calc("5 * 5")) + self.assertIsNone(calc("No math here")) + + def test_timer_parsing(self): + """Verify timer parses duration correctly""" + timer = self.registry["timer"]["run"] + # We use 0 seconds to avoid waiting during tests + response = timer("Set a timer for 0 seconds") + self.assertIn("Timer started", response) + + @patch('urllib.request.urlopen') + def test_weather_mock(self, mock_urlopen): + """Verify weather skill with mocked network""" + # Mock the API response so tests work offline + mock_response = MagicMock() + mock_response.status = 200 + mock_response.read.return_value = b"London: +15C" + mock_response.__enter__.return_value = mock_response + mock_urlopen.return_value = mock_response + + weather = self.registry["weather"]["run"] + result = weather("Weather in London") + + self.assertIn("London: +15C", result) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file