From 191ddd91d1e76d2a200b5424579217712f3e83d7 Mon Sep 17 00:00:00 2001 From: dafit Date: Fri, 6 Feb 2026 20:44:01 +0100 Subject: [PATCH] feat: Add portfolio as Phase 3 nervous system implementation - PLAN.md: Architecture for FunctionGemma + Math Cells + NATS - functiongemma_tools.py: 6 working tools for portfolio queries - fetch_document: Section extraction from docs - compute_git_stats: Git activity metrics - query_tasks: Phoebe task queries - search_docs: Documentation search - show_architecture: ASCII diagrams - get_project_info: Project metadata The portfolio IS the first nervous system organism! Next: NATS + Ollama deployment, Streamlit frontend Co-Authored-By: Claude Opus 4.5 --- portfolio/PLAN.md | 311 ++++++++++++++++++++++++ portfolio/functiongemma_tools.py | 399 +++++++++++++++++++++++++++++++ 2 files changed, 710 insertions(+) create mode 100644 portfolio/PLAN.md create mode 100644 portfolio/functiongemma_tools.py diff --git a/portfolio/PLAN.md b/portfolio/PLAN.md new file mode 100644 index 0000000..5035d33 --- /dev/null +++ b/portfolio/PLAN.md @@ -0,0 +1,311 @@ +--- +type: implementation_plan +status: planning +created: 2026-02-06 +author: Nyx (with dafit) +purpose: Phase 3 implementation via living portfolio +--- + +# Portfolio: Phase 3 Living Implementation + +> *"The portfolio IS the nervous system's first organism."* +> — The Synthesis (2026-02-06) + +--- + +## Overview + +The nimmerverse portfolio website serves dual purpose: +1. **Job search**: Interactive resume showcasing skills through demonstration +2. **Phase 3 implementation**: First real deployment of NATS, Function Gemma, and Math Cells + +**URL**: `resume.nimmerverse.com` (or `portfolio.nimmerverse.com`) +**VIP Available**: 213.188.249.164 (nimmerverse.eachpath.com) + +--- + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PORTFOLIO ARCHITECTURE │ +│ (Phase 3 Nervous System) │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ User Browser │ +│ │ │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ Frontend │ Streamlit / Astro / simple HTML │ +│ │ (K8s Pod) │ │ +│ └────────┬────────┘ │ +│ │ HTTP/WebSocket │ +│ ▼ │ +│ ┌─────────────────┐ │ +│ │ NATS Router │ Message bus (nimmerverse-infra namespace) │ +│ └────────┬────────┘ │ +│ │ │ +│ ┌─────┴─────────────────┬─────────────────┐ │ +│ ▼ ▼ ▼ │ +│ ┌──────────┐ ┌───────────┐ ┌───────────┐ │ +│ │ Function │ │ Math Cell │ │ RAG Cell │ │ +│ │ Gemma │ │ git_stats │ │ doc_query │ │ +│ └────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ +│ │ │ │ │ +│ Parse intent Query phoebe ChromaDB/Iris │ +│ → structured + git history over nimmerverse │ +│ JSON → statistics docs │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Components + +### 1. NATS Message Router + +**Purpose**: Route user queries to appropriate handlers +**Namespace**: `nimmerverse-infra` +**Topics**: +- `portfolio.query.intent` → Function Gemma (parse user input) +- `portfolio.query.stats` → Math Cells (compute statistics) +- `portfolio.query.docs` → RAG Cell (document retrieval) +- `portfolio.response` → Aggregate and return to frontend + +### 2. FunctionGemma Cell + +**Purpose**: Parse natural language → structured JSON (intent + API calls) +**Model**: `google/functiongemma-270m-it` +**Deployment**: Ollama (`ollama pull functiongemma`) + +**Why FunctionGemma?** +| Spec | Value | Benefit | +|------|-------|---------| +| Parameters | 270M | Tiny, fast | +| RAM | ~550MB | Runs on anything | +| VRAM | ~1-2GB | CPU or minimal GPU | +| Vocab | 256K (JSON-optimized) | Clean structured output | + +**Reference**: [`/references/software/functiongemma/FunctionGemma-Overview.md`](../../references/software/functiongemma/FunctionGemma-Overview.md) + +**Input**: Raw user query +**Output**: Structured function call + +```json +{ + "function": "fetch_document", + "params": { + "path": "Endgame-Vision.md", + "section": "K8s Cluster Architecture" + } +} +``` + +**Function Definitions** (our API surface): +```python +PORTFOLIO_FUNCTIONS = [ + { + "name": "fetch_document", + "description": "Retrieve a document or section from the nimmerverse", + "params": {"path": "string", "section": "string (optional)"} + }, + { + "name": "compute_git_stats", + "description": "Get git statistics (commits, LOC, activity)", + "params": {"period": "week|month|all"} + }, + { + "name": "query_tasks", + "description": "List tasks from the nimmerverse task planner", + "params": {"status": "todo|in_progress|done|all", "project": "string (optional)"} + }, + { + "name": "search_docs", + "description": "Search across all documentation", + "params": {"query": "string"} + }, + { + "name": "show_architecture", + "description": "Display architecture diagrams", + "params": {"component": "k8s|network|cells|full"} + } +] +``` + +**Chat Template Format** (FunctionGemma-specific): + +``` +developer +You can do function calling with the following functions: + +declaration:fetch_document{ + description: "Retrieve a document or section from the nimmerverse", + parameters: { path: STRING, section: STRING (optional) } +} + + +declaration:compute_git_stats{ + description: "Get git statistics (commits, LOC, activity)", + parameters: { period: STRING } +} + + +declaration:query_tasks{ + description: "List tasks from the nimmerverse task planner", + parameters: { status: STRING, project: STRING (optional) } +} + + +declaration:search_docs{ + description: "Search across all documentation", + parameters: { query: STRING } +} + + +declaration:show_architecture{ + description: "Display architecture diagrams", + parameters: { component: STRING } +} + + + +user +How active is this project? + + +model + +The user wants to know about project activity. I should use compute_git_stats +with period "month" to show recent activity. + +call:compute_git_stats{ + period: "month" +} + +``` + +**Fine-Tuning Option** (for nimmerverse-specific reasoning): +- Unsloth notebooks: [Reason before Tool Calling](https://colab.research.google.com/...) +- `` blocks for chain-of-thought before function calls +- Could train on our actual function surface + nimmerverse context + +**Deployment**: +```bash +# On k8s-master or dioscuri (minimal resources needed) +ollama pull functiongemma +# Expose via K8s service +``` + +### 3. Math Cells + +**git_stats_cell**: +- Total commits (all time, this month, this week) +- Lines of code +- Files changed +- Commit frequency graph data + +**task_stats_cell**: +- Query phoebe `nimmerverse_tasks` table +- Tasks by status (done/in_progress/todo) +- Tasks by priority +- Progress percentage + +**project_stats_cell**: +- Submodule count +- Documentation pages +- Architecture components + +### 4. RAG Cell (doc_query) + +**Purpose**: Answer questions about the nimmerverse +**Index**: ChromaDB (iris) or simple in-memory FAISS +**Corpus**: +- Endgame-Vision.md +- Architecture docs +- ADR records +- Task history + +### 5. Frontend + +**Options** (decide later): +- **Streamlit**: Fast to build, Python native, good for chat UI +- **Astro**: Static + islands, professional look +- **Simple HTML + HTMX**: Lightweight, fast + +**Pages**: +- `/` - Landing with project overview +- `/chat` - Interactive query interface +- `/architecture` - Rendered diagrams +- `/timeline` - Git history visualization +- `/resume` - Traditional CV (PDF download) + +--- + +## Implementation Phases + +### Week 1: Foundation + +- [ ] Deploy NATS on K8s (`nimmerverse-infra` namespace) +- [ ] Create Function Gemma cell (intent parsing) +- [ ] Create git_stats math cell +- [ ] Simple frontend (Streamlit MVP) +- [ ] Basic query flow working end-to-end + +### Week 2: Content & Polish + +- [ ] RAG cell over nimmerverse docs +- [ ] task_stats cell (phoebe queries) +- [ ] Timeline visualization +- [ ] Architecture diagram rendering +- [ ] CV/resume page with PDF download + +### Week 3: Professional Presence + +- [ ] LinkedIn profile created +- [ ] GitHub curated (public repos) +- [ ] Domain configured (Traefik ingress) +- [ ] SSL certificate (Let's Encrypt) +- [ ] Final polish and testing + +--- + +## Infrastructure + +**K8s Namespaces**: +``` +nimmerverse-infra # NATS, shared infrastructure +nimmerverse-portfolio # Frontend, cells +``` + +**Ingress**: +- Traefik already at 10.0.30.200 +- Configure `resume.nimmerverse.com` → portfolio service + +**External DNS**: +- Point domain to 213.188.249.164 +- Vulkan NAT → Traefik LoadBalancer + +--- + +## Success Criteria + +1. **Visitor can ask questions** and get meaningful answers about the project +2. **Statistics are live** - pulled from git and phoebe in real-time +3. **Architecture is visible** - diagrams render correctly +4. **Professional presence** - LinkedIn and GitHub linked +5. **PDF resume downloadable** - ATS-friendly format available + +--- + +## Links + +- [Endgame-Vision.md](../Endgame-Vision.md) - Main architecture doc +- [Gateway-Architecture.md](../architecture/Gateway-Architecture.md) - Thalamus/routing design +- [Cellular-Architecture.md](../architecture/Cellular-Architecture.md) - Cell patterns + +--- + +**Created**: 2026-02-06 +**Status**: Planning +**Philosophy**: "Build Phase 3 with purpose - the portfolio IS the first organism." diff --git a/portfolio/functiongemma_tools.py b/portfolio/functiongemma_tools.py new file mode 100644 index 0000000..48b1366 --- /dev/null +++ b/portfolio/functiongemma_tools.py @@ -0,0 +1,399 @@ +""" +FunctionGemma Tool Definitions for Nimmerverse Portfolio + +This module defines the tools that FunctionGemma can call to answer +visitor queries about the nimmerverse project. + +Reference: Unsloth Multi-Turn Tool Calling notebook +""" + +import re +import subprocess +from datetime import datetime +from pathlib import Path +from typing import Optional + +# ============================================================================ +# TOOL DEFINITIONS +# ============================================================================ + +def fetch_document(path: str, section: Optional[str] = None): + """ + Retrieves a document or section from the nimmerverse documentation. + + Args: + path: The document path relative to nimmerverse root, e.g. "nimmerverse-sensory-network/Endgame-Vision.md" + section: Optional section header to extract, e.g. "K8s Cluster Architecture" + + Returns: + content: The document or section content + found: Whether the document/section was found + """ + base_path = Path("/home/dafit/nimmerverse") + full_path = base_path / path + + if not full_path.exists(): + return {"content": f"Document not found: {path}", "found": False} + + content = full_path.read_text() + + if section: + # Find the section header line + lines = content.split('\n') + start_idx = None + header_level = None + + for i, line in enumerate(lines): + if section.lower() in line.lower() and line.strip().startswith('#'): + start_idx = i + header_level = len(line) - len(line.lstrip('#')) + break + + if start_idx is not None: + # Find the end of this section (next header of same or higher level) + end_idx = len(lines) + for i in range(start_idx + 1, len(lines)): + line = lines[i].strip() + if line.startswith('#'): + level = len(line) - len(line.lstrip('#')) + if level <= header_level: + end_idx = i + break + + section_content = '\n'.join(lines[start_idx:end_idx]) + return {"content": section_content.strip(), "found": True} + else: + return {"content": f"Section '{section}' not found in {path}", "found": False} + + return {"content": content, "found": True} + + +def compute_git_stats(period: str = "month"): + """ + Gets git statistics for the nimmerverse project. + + Args: + period: Time period for stats (choices: ["week", "month", "year", "all"]) + + Returns: + commits: Number of commits in period + files_changed: Number of files modified + insertions: Lines added + deletions: Lines removed + authors: List of contributors + """ + base_path = "/home/dafit/nimmerverse" + + # Map period to git since date + since_map = { + "week": "1 week ago", + "month": "1 month ago", + "year": "1 year ago", + "all": "" + } + since = since_map.get(period, "1 month ago") + + try: + # Get commit count + since_arg = f"--since='{since}'" if since else "" + cmd = f"git -C {base_path} rev-list --count HEAD {since_arg}" + commits = int(subprocess.check_output(cmd, shell=True).decode().strip()) + + # Get shortstat + cmd = f"git -C {base_path} log --shortstat {since_arg} --pretty=format:''" + output = subprocess.check_output(cmd, shell=True).decode() + + insertions = sum(int(m) for m in re.findall(r"(\d+) insertion", output)) + deletions = sum(int(m) for m in re.findall(r"(\d+) deletion", output)) + files = len(set(re.findall(r"(\d+) files? changed", output))) + + # Get authors + cmd = f"git -C {base_path} log --format='%an' {since_arg} | sort -u" + authors = subprocess.check_output(cmd, shell=True).decode().strip().split('\n') + + return { + "commits": commits, + "files_changed": files, + "insertions": insertions, + "deletions": deletions, + "authors": [a for a in authors if a], + "period": period + } + except Exception as e: + return {"error": str(e)} + + +def query_tasks(status: str = "all", project: Optional[str] = None): + """ + Lists tasks from the nimmerverse task planner (phoebe database). + + Args: + status: Filter by status (choices: ["todo", "in_progress", "done", "blocked", "all"]) + project: Optional project filter, e.g. "infrastructure", "nimmerhovel" + + Returns: + tasks: List of matching tasks with name, status, priority + count: Number of tasks found + """ + import psycopg2 + + try: + conn = psycopg2.connect( + host="phoebe.eachpath.local", + database="nimmerverse", + user="nimmerverse-user", + sslmode="disable" + ) + cur = conn.cursor() + + query = "SELECT project, task_name, status, priority FROM nimmerverse_tasks" + conditions = [] + params = [] + + if status != "all": + conditions.append("status = %s") + params.append(status) + if project: + conditions.append("project = %s") + params.append(project) + + if conditions: + query += " WHERE " + " AND ".join(conditions) + + query += " ORDER BY priority, project" + + cur.execute(query, params) + rows = cur.fetchall() + + tasks = [ + {"project": r[0], "name": r[1], "status": r[2], "priority": r[3]} + for r in rows + ] + + conn.close() + return {"tasks": tasks, "count": len(tasks)} + + except Exception as e: + return {"error": str(e)} + + +def search_docs(query: str): + """ + Searches across all nimmerverse documentation using grep. + + Args: + query: Search query string + + Returns: + matches: List of matching files and snippets + count: Number of matches found + """ + base_path = "/home/dafit/nimmerverse" + + try: + cmd = f"grep -r -l -i '{query}' {base_path} --include='*.md' 2>/dev/null | head -20" + output = subprocess.check_output(cmd, shell=True).decode() + files = [f.replace(base_path + "/", "") for f in output.strip().split('\n') if f] + + # Get snippets from each file + matches = [] + for f in files[:5]: # Limit to 5 files + cmd = f"grep -i -C 1 '{query}' {base_path}/{f} | head -6" + snippet = subprocess.check_output(cmd, shell=True).decode().strip() + matches.append({"file": f, "snippet": snippet}) + + return {"matches": matches, "count": len(files)} + + except Exception as e: + return {"error": str(e), "matches": [], "count": 0} + + +def show_architecture(component: str = "full"): + """ + Returns architecture information for a specific component. + + Args: + component: Which architecture to show (choices: ["k8s", "network", "cells", "full", "portfolio"]) + + Returns: + description: Text description of the architecture + diagram: ASCII diagram if available + """ + architectures = { + "k8s": { + "description": "3-node Kubernetes cluster with GPU workers", + "diagram": """ + k8s-master (VM 101) + 10.0.30.101 + Control Plane + │ + ┌─────────────┴─────────────┐ + │ │ + theia (GPU Worker) dioscuri (GPU Worker) + 10.0.30.21 10.0.30.22 + RTX PRO 6000 96GB 2x RTX 4000 Ada 40GB + + Total: 136GB VRAM | kubeadm v1.31.14 | Flannel CNI +""" + }, + "network": { + "description": "Spine-leaf network with 80Gbps fabric capacity", + "diagram": """ + vulkan (OPNsense) ──20Gbps──┐ + │ + spine-crs309 + (8x 10G SFP+) + │ + ┌───────────┬───────────┬───┴───────┐ + │ │ │ │ + saturn theia dioscuri access-crs326 + 20Gbps 10Gbps 10Gbps 20Gbps +""" + }, + "cells": { + "description": "Cellular architecture: Cells → Nerves → Organisms", + "diagram": """ + ORGANISM (emergent pattern) + │ + NERVES (behavioral state machines) + │ + CELLS (atomic: sensors, motors, organs, math) + │ + HARDWARE (ESP32, GPUs, sensors) +""" + }, + "portfolio": { + "description": "Portfolio as Phase 3 nervous system implementation", + "diagram": """ + User Browser + │ + ┌────┴────┐ + │ Frontend │ (Streamlit) + └────┬────┘ + │ + ┌────┴────┐ + │ NATS │ Message Router + └────┬────┘ + │ + ┌──────┼──────┬──────────┐ + │ │ │ │ +Function Math RAG Other + Gemma Cells Cell Cells +""" + } + } + + if component == "full": + return { + "description": "Complete nimmerverse architecture", + "components": list(architectures.keys()), + "total_vram": "136GB", + "total_fabric": "80Gbps" + } + + return architectures.get(component, {"description": "Unknown component", "diagram": ""}) + + +def get_project_info(): + """ + Gets general information about the nimmerverse project. + + Returns: + name: Project name + started: When the project started + status: Current project status + philosophy: Core philosophy + """ + return { + "name": "The Nimmerverse", + "started": "November 2025", + "status": "Phase 3 - Nervous System Deployment", + "philosophy": "May the Nimmerverse we build truly never end.", + "covenant_date": "2025-11-04", + "total_vram": "136GB", + "cluster_nodes": 3, + "tracked_tasks": 58 + } + + +# ============================================================================ +# FUNCTION MAPPING & TOOLS +# ============================================================================ + +FUNCTION_MAPPING = { + "fetch_document": fetch_document, + "compute_git_stats": compute_git_stats, + "query_tasks": query_tasks, + "search_docs": search_docs, + "show_architecture": show_architecture, + "get_project_info": get_project_info, +} + +TOOLS = list(FUNCTION_MAPPING.values()) + + +# ============================================================================ +# PARSING & INFERENCE HELPERS +# ============================================================================ + +def extract_tool_calls(text: str): + """Extract tool calls from FunctionGemma output.""" + def cast(v): + try: return int(v) + except: + try: return float(v) + except: return {'true': True, 'false': False}.get(v.lower(), v.strip("'\"")) + + return [{ + "name": name, + "arguments": { + k: cast((v1 or v2).strip()) + for k, v1, v2 in re.findall(r"(\w+):(?:(.*?)|([^,}]*))", args) + } + } for name, args in re.findall(r"call:(\w+)\{(.*?)\}", text, re.DOTALL)] + + +def process_tool_calls(output: str, messages: list): + """Execute tool calls and add results to message chain.""" + calls = extract_tool_calls(output) + if not calls: + return messages + + messages.append({ + "role": "assistant", + "tool_calls": [{"type": "function", "function": call} for call in calls] + }) + + results = [] + for c in calls: + func = FUNCTION_MAPPING.get(c['name']) + if func: + result = func(**c['arguments']) + results.append({"name": c['name'], "response": result}) + else: + results.append({"name": c['name'], "response": {"error": f"Unknown function: {c['name']}"}}) + + messages.append({"role": "tool", "content": results}) + return messages + + +# ============================================================================ +# MAIN (for testing) +# ============================================================================ + +if __name__ == "__main__": + # Test the tools + print("=== Testing fetch_document ===") + print(fetch_document("nimmerverse-sensory-network/Endgame-Vision.md", "K8s Cluster Architecture")) + + print("\n=== Testing compute_git_stats ===") + print(compute_git_stats("week")) + + print("\n=== Testing query_tasks ===") + print(query_tasks("in_progress")) + + print("\n=== Testing show_architecture ===") + print(show_architecture("k8s")) + + print("\n=== Testing get_project_info ===") + print(get_project_info())