chore: Remove portfolio folder

Moved to dedicated nimmerverse-web repo.
Portfolio deserves its own deployable unit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 23:18:06 +01:00
parent 0ebb3e3645
commit 52b3fd818b
2 changed files with 0 additions and 710 deletions

View File

@@ -1,311 +0,0 @@
---
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):
```
<start_of_turn>developer
You can do function calling with the following functions:
<start_function_declaration>declaration:fetch_document{
description: "Retrieve a document or section from the nimmerverse",
parameters: { path: STRING, section: STRING (optional) }
}
<end_function_declaration>
<start_function_declaration>declaration:compute_git_stats{
description: "Get git statistics (commits, LOC, activity)",
parameters: { period: STRING }
}
<end_function_declaration>
<start_function_declaration>declaration:query_tasks{
description: "List tasks from the nimmerverse task planner",
parameters: { status: STRING, project: STRING (optional) }
}
<end_function_declaration>
<start_function_declaration>declaration:search_docs{
description: "Search across all documentation",
parameters: { query: STRING }
}
<end_function_declaration>
<start_function_declaration>declaration:show_architecture{
description: "Display architecture diagrams",
parameters: { component: STRING }
}
<end_function_declaration>
<end_of_turn>
<start_of_turn>user
How active is this project?
<end_of_turn>
<start_of_turn>model
<think>
The user wants to know about project activity. I should use compute_git_stats
with period "month" to show recent activity.
</think>
<start_function_call>call:compute_git_stats{
period: "month"
}
<end_function_call>
```
**Fine-Tuning Option** (for nimmerverse-specific reasoning):
- Unsloth notebooks: [Reason before Tool Calling](https://colab.research.google.com/...)
- `<think></think>` 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."

View File

@@ -1,399 +0,0 @@
"""
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+):(?:<escape>(.*?)<escape>|([^,}]*))", args)
}
} for name, args in re.findall(r"<start_function_call>call:(\w+)\{(.*?)\}<end_function_call>", 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())