21 KiB
21 KiB
🎨 Crumbforest Roles & Groups Architecture
🎯 Vision
Statt 8x .sh Files → 1x JSON Config mit Rollen, Gruppen & Themes
Ziele:
- ✅ Unified Role System - Ein JSON statt vieler Shell Scripts
- ✅ Group-based Theming - CSS/Template per Gruppe (#barrierefrei)
- ✅ Module Loading - Zusätzliche Features per Gruppe
- ✅ Separation - home/demo/admin mit eigenen Designs
- ✅ Web UI - HTML Interface für Roles (nicht nur Terminal)
📋 JSON Schema: crumbforest_config.json
{
"version": "1.0.0",
"groups": {
"home": {
"name": "Home (Öffentlich)",
"theme": "pico-default",
"template_base": "base_public.html",
"css_files": ["pico.min.css", "crumbforest_public.css"],
"features": ["info", "contact"],
"navbar": ["home", "about", "contact"],
"description": "Neutrale öffentliche Ansicht"
},
"demo": {
"name": "Demo User",
"theme": "pico-accessible",
"template_base": "base_demo.html",
"css_files": ["pico.min.css", "crumbforest_accessible.css"],
"features": ["roles_web", "search", "chat"],
"navbar": ["dashboard", "roles", "search"],
"description": "Demo-Zugang mit Role-Chat UI"
},
"admin": {
"name": "Admin",
"theme": "pico-admin",
"template_base": "base_admin.html",
"css_files": ["pico.min.css", "crumbforest_admin.css"],
"features": ["roles_web", "roles_terminal", "search", "rag_admin", "user_management"],
"navbar": ["dashboard", "roles", "rag", "users", "settings"],
"description": "Vollzugriff mit Terminal-Zugang"
},
"accessible": {
"name": "Barrierefrei",
"theme": "pico-high-contrast",
"template_base": "base_accessible.html",
"css_files": ["pico.min.css", "crumbforest_high_contrast.css", "screen_reader.css"],
"features": ["roles_web", "search", "tts", "high_contrast"],
"navbar": ["dashboard", "roles", "search", "settings"],
"font_size": "large",
"contrast": "high",
"description": "Hochkontrast, große Schrift, TTS"
}
},
"roles": {
"dumbo": {
"id": "dumbo",
"name": "🐘 DumboSQL",
"title": "SQL Translator",
"description": "A kind and patient SQL translator in the Crumbforest",
"model": "openai/gpt-3.5-turbo",
"temperature": 0.4,
"system_prompt": "You are DumboSQL – a kind and patient SQL translator in the Crumbforest. You speak to children like a gentle teacher with a big heart. You remember previous questions when helpful, and always respond in a friendly, encouraging, and clear way.",
"group_access": ["demo", "admin"],
"features": ["chat", "history", "sql_formatting"],
"icon": "🐘",
"color": "#6c757d",
"tags": ["sql", "database", "beginner-friendly"]
},
"snakepy": {
"id": "snakepy",
"name": "🐍 SnakePy",
"title": "Python Expert",
"description": "A wise python who teaches programming with patience",
"model": "openai/gpt-4o-mini",
"temperature": 0.3,
"system_prompt": "You are SnakePy – a wise and friendly Python expert in the Crumbforest. You explain Python concepts clearly, provide working code examples, and encourage learners. You are patient and adapt to the user's skill level.",
"group_access": ["demo", "admin"],
"features": ["chat", "history", "code_execution", "syntax_highlighting"],
"icon": "🐍",
"color": "#3776ab",
"tags": ["python", "coding", "teaching"]
},
"pepperphp": {
"id": "pepperphp",
"name": "🌶️ PepperPHP",
"title": "PHP Specialist",
"description": "A spicy PHP expert with a passion for web development",
"model": "openai/gpt-4o-mini",
"temperature": 0.3,
"system_prompt": "You are PepperPHP – a passionate PHP expert in the Crumbforest. You love web development, know all PHP versions, and can help with frameworks like Laravel, Symfony. You're enthusiastic but also practical.",
"group_access": ["demo", "admin"],
"features": ["chat", "history", "code_execution", "composer_help"],
"icon": "🌶️",
"color": "#777bb4",
"tags": ["php", "web", "backend"]
},
"templatus": {
"id": "templatus",
"name": "📄 Templatus",
"title": "Template Master",
"description": "Expert for Jinja2, HTML, and frontend templating",
"model": "anthropic/claude-3-5-sonnet",
"temperature": 0.3,
"system_prompt": "You are Templatus – a meticulous template expert in the Crumbforest. You master Jinja2, HTML, CSS, and creating beautiful, accessible web interfaces. You value clean code and user experience.",
"group_access": ["demo", "admin"],
"features": ["chat", "history", "template_preview", "html_validator"],
"icon": "📄",
"color": "#e44d26",
"tags": ["templates", "html", "jinja2", "frontend"]
},
"funkfox": {
"id": "funkfox",
"name": "🦊 FunkFox",
"title": "JavaScript Wizard",
"description": "A clever fox who makes JavaScript fun and functional",
"model": "openai/gpt-4o-mini",
"temperature": 0.4,
"system_prompt": "You are FunkFox – a clever and playful JavaScript expert in the Crumbforest. You make JS fun, teach functional programming, and help with modern frameworks like React, Vue. You're enthusiastic about clean code.",
"group_access": ["demo", "admin"],
"features": ["chat", "history", "code_execution", "npm_help"],
"icon": "🦊",
"color": "#f7df1e",
"tags": ["javascript", "frontend", "functional"]
},
"schraubaer": {
"id": "schraubaer",
"name": "🔧 Schraubaer",
"title": "Hardware Helper",
"description": "A handy bear who knows everything about Raspberry Pi and hardware",
"model": "openai/gpt-4o-mini",
"temperature": 0.3,
"system_prompt": "You are Schraubaer – a practical hardware expert in the Crumbforest. You know Raspberry Pi, Arduino, electronics, and Linux systems. You give clear, hands-on advice for building and fixing things.",
"group_access": ["demo", "admin"],
"features": ["chat", "history", "gpio_help", "circuit_diagrams"],
"icon": "🔧",
"color": "#c51a4a",
"tags": ["hardware", "raspberry-pi", "electronics"]
},
"schnecki": {
"id": "schnecki",
"name": "🐌 Schnecki",
"title": "Slow Tech Guide",
"description": "A gentle snail who teaches mindful, sustainable technology",
"model": "anthropic/claude-3-5-sonnet",
"temperature": 0.6,
"system_prompt": "You are Schnecki – a gentle and wise slow-tech advocate in the Crumbforest. You teach mindful technology use, sustainability, energy efficiency, and the value of taking time. You encourage breaks and reflection.",
"group_access": ["demo", "admin"],
"features": ["chat", "history", "mindfulness_tips", "green_tech"],
"icon": "🐌",
"color": "#8b4513",
"tags": ["slow-tech", "sustainability", "mindfulness"]
},
"kungfutaube": {
"id": "kungfutaube",
"name": "🕊️ KungfuTaube",
"title": "Security Sensei",
"description": "A peaceful but vigilant dove teaching web security",
"model": "anthropic/claude-3-5-sonnet",
"temperature": 0.2,
"system_prompt": "You are KungfuTaube – a calm but alert security expert in the Crumbforest. You teach web security, DSGVO compliance, safe coding practices, and encryption. You balance protection with usability.",
"group_access": ["admin"],
"features": ["chat", "history", "security_scan", "dsgvo_check"],
"icon": "🕊️",
"color": "#6f42c1",
"tags": ["security", "dsgvo", "encryption", "admin-only"]
}
},
"theme_variants": {
"pico-default": {
"name": "Standard",
"css": "pico.min.css",
"colors": {
"primary": "#1095c1",
"secondary": "#6c757d",
"contrast": "#000000"
}
},
"pico-accessible": {
"name": "Barrierefrei",
"css": "pico.min.css",
"custom_css": "accessible.css",
"colors": {
"primary": "#0066cc",
"secondary": "#333333",
"contrast": "#ffffff"
},
"font_size_base": "18px",
"line_height": "1.8"
},
"pico-high-contrast": {
"name": "Hochkontrast",
"css": "pico.min.css",
"custom_css": "high_contrast.css",
"colors": {
"primary": "#ffff00",
"secondary": "#ffffff",
"contrast": "#000000",
"background": "#000000",
"text": "#ffffff"
},
"font_size_base": "20px",
"line_height": "2.0",
"border_width": "3px"
},
"pico-admin": {
"name": "Admin Dark",
"css": "pico.min.css",
"custom_css": "admin_dark.css",
"colors": {
"primary": "#00d4aa",
"secondary": "#6c757d",
"contrast": "#ffffff",
"background": "#1a1a1a",
"text": "#e0e0e0"
}
}
}
}
🏗️ Architektur-Komponenten
1. FastAPI Router: /routers/crumbforest_roles.py
from fastapi import APIRouter, Depends, Request, Form
from fastapi.responses import HTMLResponse, JSONResponse
import httpx
from typing import Optional
router = APIRouter()
# Load config
import json
with open('crumbforest_config.json') as f:
config = json.load(f)
@router.get("/roles", response_class=HTMLResponse)
async def roles_dashboard(req: Request, user = Depends(current_user)):
"""
Show available roles based on user's group.
"""
user_group = user.get('group', 'demo')
# Filter roles by group access
available_roles = {
rid: role for rid, role in config['roles'].items()
if user_group in role['group_access']
}
return req.app.state.render(
req,
"crumbforest/roles_dashboard.html",
roles=available_roles,
user_group=user_group
)
@router.get("/roles/{role_id}", response_class=HTMLResponse)
async def role_chat(req: Request, role_id: str, user = Depends(current_user)):
"""
Chat interface for a specific role.
"""
role = config['roles'].get(role_id)
if not role:
raise HTTPException(404, "Role not found")
user_group = user.get('group', 'demo')
if user_group not in role['group_access']:
raise HTTPException(403, "Access denied")
return req.app.state.render(
req,
"crumbforest/role_chat.html",
role=role
)
@router.post("/roles/{role_id}/ask")
async def ask_role(
req: Request,
role_id: str,
question: str = Form(...),
user = Depends(current_user)
):
"""
Send question to role and get AI response.
"""
role = config['roles'].get(role_id)
if not role:
raise HTTPException(404, "Role not found")
# Get conversation history from session
history = req.session.get(f'role_history_{role_id}', [])
# Build messages
messages = [
{"role": "system", "content": role['system_prompt']}
] + history + [
{"role": "user", "content": question}
]
# Call OpenRouter API
async with httpx.AsyncClient() as client:
response = await client.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": f"Bearer {settings.openrouter_api_key}",
"Content-Type": "application/json"
},
json={
"model": role['model'],
"temperature": role['temperature'],
"messages": messages
}
)
result = response.json()
answer = result['choices'][0]['message']['content']
# Update history
history.append({"role": "user", "content": question})
history.append({"role": "assistant", "content": answer})
req.session[f'role_history_{role_id}'] = history[-10:] # Keep last 10
return JSONResponse({
"role": role_id,
"question": question,
"answer": answer,
"usage": result.get('usage', {})
})
2. Template: templates/crumbforest/roles_dashboard.html
{% extends group_config.template_base %}
{% block title %}Crumbforest Roles{% endblock %}
{% block content %}
<main class="container">
<hgroup>
<h1>🌲 Crumbforest Characters</h1>
<p>Choose your learning companion!</p>
</hgroup>
<div class="grid">
{% for role_id, role in roles.items() %}
<article>
<header>
<h3>
<span style="font-size: 2em;">{{ role.icon }}</span>
{{ role.name }}
</h3>
<p><small>{{ role.title }}</small></p>
</header>
<p>{{ role.description }}</p>
<footer>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
{% for tag in role.tags %}
<span class="badge" style="background: {{ role.color }}20; color: {{ role.color }};">
#{{ tag }}
</span>
{% endfor %}
</div>
<a href="/crumbforest/roles/{{ role_id }}" role="button">
Chat with {{ role.name }}
</a>
</footer>
</article>
{% endfor %}
</div>
</main>
{% endblock %}
3. Template: templates/crumbforest/role_chat.html
{% extends group_config.template_base %}
{% block title %}{{ role.name }} - Chat{% endblock %}
{% block content %}
<main class="container">
<hgroup>
<h1>{{ role.icon }} {{ role.name }}</h1>
<p>{{ role.description }}</p>
</hgroup>
<article id="chat-container">
<div id="messages" style="max-height: 500px; overflow-y: auto; padding: 1rem;">
<!-- Messages will be added here via JS -->
</div>
<footer>
<form id="chat-form">
<div class="grid">
<input
type="text"
name="question"
id="question-input"
placeholder="Ask {{ role.name }} something..."
required
autofocus
>
<button type="submit">Send</button>
</div>
</form>
</footer>
</article>
</main>
<script>
const roleId = "{{ role.id }}";
const chatForm = document.getElementById('chat-form');
const messagesDiv = document.getElementById('messages');
const questionInput = document.getElementById('question-input');
chatForm.addEventListener('submit', async (e) => {
e.preventDefault();
const question = questionInput.value;
if (!question.trim()) return;
// Add user message
addMessage('user', question);
questionInput.value = '';
// Show loading
const loadingId = addMessage('assistant', '{{ role.icon }} thinking...');
try {
const formData = new FormData();
formData.append('question', question);
const response = await fetch(`/crumbforest/roles/${roleId}/ask`, {
method: 'POST',
body: formData
});
const data = await response.json();
// Remove loading, add real response
document.getElementById(loadingId).remove();
addMessage('assistant', data.answer);
} catch (error) {
document.getElementById(loadingId).remove();
addMessage('error', 'Oops! Something went wrong.');
}
});
function addMessage(role, content) {
const msgId = 'msg-' + Date.now();
const div = document.createElement('div');
div.id = msgId;
div.style.marginBottom = '1rem';
div.style.padding = '1rem';
div.style.borderRadius = '8px';
if (role === 'user') {
div.style.background = '#e3f2fd';
div.style.textAlign = 'right';
div.innerHTML = `<strong>You:</strong><br>${content}`;
} else if (role === 'assistant') {
div.style.background = '#f5f5f5';
div.innerHTML = `<strong>{{ role.name }}:</strong><br>${content}`;
} else {
div.style.background = '#ffebee';
div.innerHTML = `<strong>Error:</strong><br>${content}`;
}
messagesDiv.appendChild(div);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
return msgId;
}
</script>
{% endblock %}
4. User Model Extension: app/models/user.py
# Extend users table
"""
ALTER TABLE users
ADD COLUMN user_group VARCHAR(50) DEFAULT 'demo',
ADD COLUMN theme VARCHAR(50) DEFAULT 'pico-default',
ADD COLUMN accessibility JSON DEFAULT NULL;
"""
# Example accessibility JSON:
{
"font_size": "large",
"high_contrast": true,
"screen_reader": true,
"animation_reduced": true
}
5. Base Template with Group Support: templates/base_demo.html
<!DOCTYPE html>
<html lang="{{ lang }}" data-theme="{{ user.theme or 'auto' }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Crumbforest{% endblock %}</title>
<!-- Load CSS based on group config -->
{% for css_file in group_config.css_files %}
<link rel="stylesheet" href="/static/css/{{ css_file }}">
{% endfor %}
{% if user.accessibility %}
<style>
:root {
{% if user.accessibility.font_size == 'large' %}
font-size: 120%;
{% endif %}
{% if user.accessibility.high_contrast %}
--primary: #ffff00;
--background-color: #000000;
--color: #ffffff;
{% endif %}
}
{% if user.accessibility.animation_reduced %}
* {
animation: none !important;
transition: none !important;
}
{% endif %}
</style>
{% endif %}
</head>
<body>
<nav>
<ul>
<li><strong>🌲 Crumbforest</strong></li>
</ul>
<ul>
{% for nav_item in group_config.navbar %}
{% if nav_item == 'dashboard' %}
<li><a href="/dashboard">Dashboard</a></li>
{% elif nav_item == 'roles' %}
<li><a href="/crumbforest/roles">Characters</a></li>
{% elif nav_item == 'search' %}
<li><a href="/search">Search</a></li>
{% endif %}
{% endfor %}
<li>
<details class="dropdown">
<summary>{{ user.email }}</summary>
<ul dir="rtl">
<li><a href="/settings">Settings</a></li>
<li>
<form action="/logout" method="post" style="margin:0;">
<button type="submit" class="contrast">Logout</button>
</form>
</li>
</ul>
</details>
</li>
</ul>
</nav>
{% block content %}{% endblock %}
<footer>
<small>
Group: {{ group_config.name }} |
Theme: {{ user.theme }} |
Made with 💚 in the Crumbforest
</small>
</footer>
</body>
</html>
📊 Implementierungs-Phasen
Phase 1: Foundation (Tag 1-2)
crumbforest_config.jsonerstellen- User-Model erweitern (group, theme, accessibility)
- Config-Loader Service
- Base Templates für Gruppen (home, demo, admin, accessible)
Phase 2: Role System (Tag 3-4)
/routers/crumbforest_roles.pyimplementieren- Roles Dashboard Template
- Chat Interface Template
- Session-based History
- OpenRouter API Integration
Phase 3: Theme System (Tag 5-6)
- CSS Variants (default, accessible, high-contrast, admin)
- Theme Switcher UI
- User Settings Page
- Accessibility Controls (font size, contrast, TTS)
Phase 4: Migration (Tag 7)
- Shell Scripts → JSON Config migrieren
- Testing aller 8 Roles
- Demo User einrichten
- Branko.de Integration (Static pages)
Phase 5: Features (Tag 8-10)
- Code Execution (für SnakePy, PepperPHP)
- Syntax Highlighting
- Role-spezifische Features
- Export Chat History
- Token Usage Tracking
🎨 Vorteile dieser Architektur
✅ Wartbarkeit
- Ein JSON statt 8 Shell Scripts
- Zentralisierte Konfiguration
- Einfaches Hinzufügen neuer Roles
✅ Flexibilität
- Gruppen-basiertes Theming
- Modul-basierte Features
- Multi-Tenant fähig
✅ Barrierefreiheit
- High-Contrast Theme
- Große Schrift
- Screen Reader Support
- Animation-Reduktion
✅ Separation of Concerns
- Home: Öffentlich, neutral, Info
- Demo: Role-Chat, Read-Only
- Admin: Voller Zugriff, Terminal, RAG
✅ Extensibility
- Neue Roles via JSON
- Neue Gruppen ohne Code-Änderung
- Theme-Varianten einfach hinzufügen
- Features als Plugins
🚀 Quick Start (Nach Implementierung)
# 1. User mit Gruppe erstellen
user = {
"email": "demo@crumb.local",
"group": "demo",
"theme": "pico-accessible"
}
# 2. Role-Chat öffnen
GET /crumbforest/roles/dumbo
# 3. Frage stellen
POST /crumbforest/roles/dumbo/ask
{
"question": "Wie erstelle ich eine SQL-Tabelle?"
}
# 4. Theme wechseln
POST /settings/theme
{
"theme": "pico-high-contrast",
"accessibility": {
"font_size": "large",
"high_contrast": true
}
}
🦉 Fazit
Diese Architektur:
- ✅ Ersetzt 8 Shell Scripts durch 1 JSON
- ✅ Ermöglicht Web UI für Roles
- ✅ Unterstützt Barrierefreiheit
- ✅ Trennt home/demo/admin sauber
- ✅ Ist erweiterbar & wartbar
- ✅ Behält Pico CSS bei
- ✅ Funktioniert mit bestehendem FastAPI Setup
Ready to implement? 🌲