1828 lines
96 KiB
Markdown
1828 lines
96 KiB
Markdown
# ARCHITECTURE.md – Gott
|
||
|
||
**Gott** ist eine selbst-gehostete AI-Chat-Plattform, gebaut auf **.NET 10** mit **Anthropic Claude API** als Haupt-Provider sowie **OpenAI-kompatiblen Providern** (Nvidia, Pollinations, Gemini). Sie bietet Multi-User-Support, persistente Agent-Memory, 16 Tools (Dateien, Shell, Suche, Memory, Remote-Execution), semantische Suche via lokalen Embeddings, Telegram-Integration, Cronjobs, Remote-Verbindungen und eine Krea-Bildgenerierung – alles über ein Vanilla-JavaScript-Frontend gesteuert.
|
||
|
||
---
|
||
|
||
## Inhaltsverzeichnis
|
||
|
||
1. [Überblick](#überblick)
|
||
2. [Tech-Stack](#tech-stack)
|
||
3. [Projektstruktur](#projektstruktur)
|
||
4. [Datenmodell](#datenmodell)
|
||
5. [Backend-Architektur](#backend-architektur)
|
||
- [Program.cs – Startup](#programcs--startup)
|
||
- [Services](#services)
|
||
- [Endpoints (API)](#endpoints-api)
|
||
- [Middleware](#middleware)
|
||
- [Models](#models)
|
||
- [Templates](#templates)
|
||
6. [Frontend-Architektur](#frontend-architektur)
|
||
7. [Multi-Provider-System (OpenAI-kompatibel)](#multi-provider-system-openai-kompatibel)
|
||
8. [Chat-Pipeline](#chat-pipeline)
|
||
9. [Tool-System](#tool-system)
|
||
10. [Agent-System](#agent-system)
|
||
11. [System-Prompt-Aufbau](#system-prompt-aufbau)
|
||
12. [Memory-System](#memory-system)
|
||
13. [Skill-System](#skill-system)
|
||
14. [Telegram-Integration](#telegram-integration)
|
||
15. [Cronjob-System](#cronjob-system)
|
||
16. [Krea-Bildgenerierung](#krea-bildgenerierung)
|
||
17. [Auth & Multi-User](#auth--multi-user)
|
||
18. [Admin-Benutzerverwaltung](#admin-benutzerverwaltung)
|
||
19. [Multi-Agent System (Preparer + Review + Finalizer)](#multi-agent-system-preparer--review--finalizer)
|
||
20. [Interaktive Rückfragen (ask_user_choice)](#interaktive-rückfragen-ask_user_choice)
|
||
21. [Plan-Modus](#plan-modus)
|
||
22. [Nachrichten teilen (Shared Messages)](#nachrichten-teilen-shared-messages)
|
||
23. [Workspace & File-Explorer](#workspace--file-explorer)
|
||
24. [Avatar-System](#avatar-system)
|
||
25. [LaTeX/Math-Rendering](#latexmath-rendering)
|
||
26. [Windows Desktop App](#windows-desktop-app)
|
||
27. [OpenClaw Import](#openclaw-import)
|
||
28. [Remote-Verbindungen](#remote-verbindungen)
|
||
29. [Semantische Suche (Embedding)](#semantische-suche-embedding)
|
||
30. [API-Key-Verwaltung](#api-key-verwaltung)
|
||
31. [Deployment](#deployment)
|
||
32. [Konfiguration](#konfiguration)
|
||
33. [Datenbank-Migrationen](#datenbank-migrationen)
|
||
|
||
---
|
||
|
||
## Überblick
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Browser / WebView2 │
|
||
│ Vanilla JS SPA (wwwroot/) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ ASP.NET 10 Minimal API │
|
||
│ http://0.0.0.0:19000 │
|
||
├──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬────┤
|
||
│ Auth │ Chat │ Hist │ Sess │Agent │ Tele │ Cron │ Krea │Adm │
|
||
│ │ SSE │ │ │ │ gram │ jobs │ Img │ in │
|
||
│ │ │ │ │ │ │ │ │Keys│
|
||
├──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴────┤
|
||
│ Services Layer │
|
||
│ AnthropicService · OpenAiService · ChatService · ToolService│
|
||
│ DatabaseService · MemoryService · SkillService · SseHelper │
|
||
│ EmbeddingService · VectorSearchService │
|
||
│ RemoteConnectionManager · TelegramService │
|
||
│ CronjobService · KreaService │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ SQLite (data/gott.db) via Dapper │
|
||
│ ONNX Runtime (Embeddings) │
|
||
│ Filesystem (data/agents/, data/skills/) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ Anthropic Claude API (claude-sonnet/opus/haiku) │
|
||
│ OpenAI-kompatible APIs (Nvidia, Pollinations, Gemini) │
|
||
│ Krea API (Bildgenerierung) │
|
||
│ Telegram Bot API │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Tech-Stack
|
||
|
||
| Schicht | Technologie |
|
||
|---------------|-----------------------------------------------------------|
|
||
| **Runtime** | .NET 10 (ohne Nullable, ohne Implicit Usings) |
|
||
| **Web** | ASP.NET Minimal API, Kestrel |
|
||
| **Datenbank** | SQLite via Microsoft.Data.Sqlite + Dapper |
|
||
| **AI** | Anthropic Claude API (Streaming SSE + Non-Streaming), OpenAI-kompatible APIs (Nvidia, Pollinations, Gemini) |
|
||
| **Images** | SixLabors.ImageSharp (Resize, Thumbnails) |
|
||
| **Cron** | Cronos (Cron-Expression-Parsing) |
|
||
| **Frontend** | Vanilla JavaScript SPA (kein Framework) |
|
||
| **Markdown** | marked.js (Client-side Rendering) |
|
||
| **LaTeX** | KaTeX 0.16.21 (Math-Rendering, self-hosted mit woff2-Fonts) |
|
||
| **Editor** | CodeMirror 5 + EasyMDE |
|
||
| **Embeddings**| Microsoft.ML.OnnxRuntime + all-MiniLM-L6-v2 (384-dim Sentence-Embeddings) |
|
||
| **Icons** | Font Awesome |
|
||
| **Fonts** | Inter, JetBrains Mono (self-hosted) |
|
||
| **Desktop** | WPF + WebView2 (.NET 10, Windows-only) |
|
||
|
||
---
|
||
|
||
## Projektstruktur
|
||
|
||
```
|
||
gott/
|
||
├── Program.cs # Startup, DI, Middleware, Routing
|
||
├── gott.csproj # Projekt-Definition (.NET 10)
|
||
├── appsettings.json # Konfiguration (Tokens, Limits)
|
||
├── deploy.sh # Deployment-Script (systemctl)
|
||
│
|
||
├── Models/ # Daten-Modelle (Records)
|
||
│ ├── UserRecord.cs
|
||
│ ├── AgentRecord.cs
|
||
│ ├── SessionRecord.cs
|
||
│ ├── ChainRecord.cs
|
||
│ ├── MessageRecord.cs # + AttachmentRecord
|
||
│ ├── ArchiveRecord.cs
|
||
│ ├── CronjobRecord.cs
|
||
│ ├── KreaImageRecord.cs
|
||
│ ├── SharedMessageRecord.cs # Geteilte Nachrichten (Share-Token)
|
||
│ ├── TelegramBotRecord.cs # Telegram Bot Entität
|
||
│ ├── ChatModels.cs # ChatMessage, ChatRequest, AttachmentInfo/Block
|
||
│ ├── ApiKeyRecord.cs # API-Key-Verwaltung (Provider, Name, Key, IsActive)
|
||
│ └── ChatContext.cs # Runtime-Kontext für einen Chat + ReviewContext
|
||
│
|
||
├── Services/ # Business-Logic
|
||
│ ├── DatabaseService.cs # SQLite Schema, Migrationen, Core (partial class)
|
||
│ ├── DatabaseService.Sessions.cs # Sessions, Archives, Chains, Messages, Attachments
|
||
│ ├── DatabaseService.Agents.cs # Agents, Agent Skills
|
||
│ ├── DatabaseService.Users.cs # Users, Shared Messages
|
||
│ ├── DatabaseService.Integrations.cs # Telegram, Cronjobs, API Keys, OpenClaw Import
|
||
│ ├── AuthService.cs # Token-basierte Authentifizierung
|
||
│ ├── ChatService.cs # Kontext-Aufbau, History-Builder
|
||
│ ├── AnthropicService.cs # Claude API (Streaming + Non-Streaming)
|
||
│ ├── OpenAiService.cs # OpenAI-kompatible Provider (Nvidia, Pollinations, Gemini)
|
||
│ ├── SseHelper.cs # Shared SSE-Utilities (WriteSseEvent, TryWriteSse, FinalizeChain)
|
||
│ ├── ToolService.cs # Tool-Definitionen + Ausführung (16 Tools)
|
||
│ ├── SystemPromptBuilder.cs # Modularer System-Prompt aus Blöcken
|
||
│ ├── MemoryService.cs # Agent-Gedächtnis (Dateisystem)
|
||
│ ├── SkillService.cs # Skill-Verwaltung (Dateisystem)
|
||
│ ├── EmbeddingService.cs # Lokales Sentence-Embedding (all-MiniLM-L6-v2, ONNX)
|
||
│ ├── VectorSearchService.cs # Semantische Suche über Message-Embeddings + Memory-Files
|
||
│ ├── EmbeddingBackgroundService.cs # Backfill + Live-Embedding neuer Messages (BackgroundService)
|
||
│ ├── RemoteConnectionManager.cs # WebSocket-basierte Remote-Verbindungen (Command-Execution)
|
||
│ ├── TelegramService.cs # Telegram Bot API Integration
|
||
│ ├── CronjobService.cs # BackgroundService für Cron-Tasks
|
||
│ ├── KreaService.cs # Krea Image Generation API
|
||
│ ├── ImageService.cs # Bild-Resize + WebP-Konvertierung (512px + 256px Thumbnail)
|
||
│ ├── StreamingTracker.cs # Tracking aktiver Streams + CancellationTokenSource für Stop
|
||
│ └── WorkspaceHelper.cs # Workspace-Pfad-Auflösung (relativ → absolut)
|
||
│
|
||
├── Endpoints/ # HTTP API (Minimal API)
|
||
│ ├── AuthEndpoint.cs # Login, Logout, Me
|
||
│ ├── ChatEndpoint.cs # POST /api/chat (SSE Streaming)
|
||
│ ├── HistoryEndpoint.cs # History, Archive, Attachments, Marks
|
||
│ ├── SessionEndpoint.cs # CRUD + SystemPrompt, Exec, Git-Status
|
||
│ ├── AgentEndpoint.cs # CRUD
|
||
│ ├── WorkspaceEndpoint.cs # File-Tree, File-Read/Write, Browse, Agent-Files
|
||
│ ├── TelegramEndpoint.cs # Bot-CRUD, Webhook, Session-Config, Registration
|
||
│ ├── OpenClawEndpoint.cs # OpenClaw Session Import
|
||
│ ├── CronjobEndpoint.cs # CRUD + Toggle, Manual Run
|
||
│ ├── KreaEndpoint.cs # Generate, List, File-Serve
|
||
│ ├── SkillEndpoint.cs # List, Agent-Skill-Mapping, Output-Files
|
||
│ ├── ModelsEndpoint.cs # Provider-Modelle (nvidia, pollinations, gemini, refresh)
|
||
│ ├── SharedEndpoint.cs # Nachrichten teilen (öffentliche Links)
|
||
│ ├── AdminEndpoint.cs # Admin: User-CRUD, Impersonate (Admin-only)
|
||
│ ├── ApiKeysEndpoint.cs # Admin: API-Key-CRUD, Activate/Deactivate, Check (Admin-only)
|
||
│ ├── RemoteEndpoint.cs # Remote-Verbindungen: WebSocket, Assign/Unassign, PowerShell-Script
|
||
│ ├── ServerSettingsEndpoint.cs # Admin: Server-Settings lesen/schreiben (Admin-only)
|
||
│ ├── ProfileEndpoint.cs # Profil, Persona, Passwort
|
||
│ ├── SettingsEndpoint.cs # User-Settings (JSON blob)
|
||
│ ├── BackupEndpoint.cs # Backup als tar.gz Download
|
||
│ └── InfoEndpoint.cs # Build-Zeitstempel + Embedding-Status
|
||
│
|
||
├── Middleware/
|
||
│ └── AuthMiddleware.cs # Cookie-basierte Token-Validierung
|
||
│
|
||
├── Extensions/
|
||
│ └── HttpContextExtensions.cs # GetUserId(), IsAdmin()
|
||
│
|
||
├── Templates/ # Standard-Templates für neue Agents
|
||
│ ├── IDENTITY.md # Wer bin ich? (Name, Sprache)
|
||
│ ├── SOUL.md # Persönlichkeit, Verhalten
|
||
│ └── USER.md # Infos über den User
|
||
│
|
||
├── wwwroot/ # Frontend (Vanilla JS SPA)
|
||
│ ├── index.html # Single HTML Entry Point
|
||
│ ├── css/ # Stylesheets
|
||
│ │ ├── theme.css # CSS Custom Properties (Dark Theme)
|
||
│ │ ├── base.css # Reset, Grundlayout
|
||
│ │ ├── main.css # Sidebar, Layout-Struktur
|
||
│ │ ├── chat.css # Chat-Nachrichten, Bubbles
|
||
│ │ ├── dashboard.css # Dashboard-Grid
|
||
│ │ ├── form.css # Formulare, Inputs, Avatar-Editor-Modal
|
||
│ │ ├── markdown.css # Markdown + KaTeX Rendering-Styles
|
||
│ │ ├── file-explorer.css # File-Explorer Panel
|
||
│ │ ├── login.css # Login-Seite
|
||
│ │ ├── mobile.css # Responsive Anpassungen
|
||
│ │ ├── tooltip.css # Tooltip-Styles
|
||
│ │ ├── fonts.css # @font-face Definitionen
|
||
│ │ ├── placeholder.css # Placeholder/Loading States
|
||
│ │ ├── katex.min.css # KaTeX Math-Rendering Styles
|
||
│ │ ├── codemirror.min.css # CodeMirror Editor
|
||
│ │ ├── easymde.min.css # EasyMDE Editor
|
||
│ │ └── fonts/ # KaTeX woff2 Fonts (21 Dateien)
|
||
│ ├── js/
|
||
│ │ ├── app.js # App-Initialisierung, Auth-Check
|
||
│ │ ├── router.js # Client-side SPA Router
|
||
│ │ ├── api.js # API-Client (fetch wrapper)
|
||
│ │ ├── ui-helpers.js # DOM-Helpers ($, esc, truncate), Toast, ScrollDown, Meta-Bar
|
||
│ │ ├── markdown.js # Markdown-Rendering (renderMarkdown, LaTeX/Code-Schutz)
|
||
│ │ ├── cards.js # Card-Builder (buildSessionCard, buildAgentCard)
|
||
│ │ ├── msg-actions.js # Nachrichten-Aktionen (Delete, Mark, Share, Reply, Timestamp)
|
||
│ │ ├── tool-ui.js # Tool-Boxes, Diffs, Todo-Liste, Visibility-Logik
|
||
│ │ ├── modals.js # Modale Dialoge (System-Prompt, Command-Output, Krea-Image, Remote)
|
||
│ │ ├── stream-ui.js # Stream-Bubble, Review-Streaming
|
||
│ │ ├── bubbles.js # Message Bubbles (User, Assistant, Error, Warning, Choice, Review)
|
||
│ │ ├── chat.js # Chat-Logic, SSE-Streaming, Plan-Mode
|
||
│ │ ├── history.js # History-Rendering, Polling
|
||
│ │ ├── archive.js # Archiv-Loading
|
||
│ │ ├── reply.js # Reply/Quote-Handling
|
||
│ │ ├── files.js # File-Upload, Workspace-Refs
|
||
│ │ ├── session.js # Session-Verwaltung
|
||
│ │ ├── file-explorer.js # File-Explorer Komponente
|
||
│ │ ├── avatar-editor.js # Wiederverwendbares Avatar-Editor-Modal
|
||
│ │ ├── commands-ui.js # Custom-Commands UI
|
||
│ │ ├── emoji.js # Emoji-Picker
|
||
│ │ ├── emoji.userdata.js # Häufig genutzte Emojis
|
||
│ │ ├── tooltip.js # Tooltip-System
|
||
│ │ ├── title.js # Tab-Titel-Management + Embedding-Warnung
|
||
│ │ ├── pages/ # Seiten-Komponenten
|
||
│ │ │ ├── chat-page.js # Haupt-Chat-Ansicht (inkl. Remote-Button)
|
||
│ │ │ ├── dashboard.js # Dashboard / Home (inkl. API-Keys-Link)
|
||
│ │ │ ├── session-page.js # Session-Einstellungen
|
||
│ │ │ ├── agent-page.js # Agent-Editor
|
||
│ │ │ ├── telegram-bot-page.js # Telegram-Bot-Editor
|
||
│ │ │ ├── cronjob-page.js # Cronjob-Verwaltung
|
||
│ │ │ ├── profile-page.js # Profil-Bearbeitung
|
||
│ │ │ ├── admin-page.js # Admin: Benutzerverwaltung
|
||
│ │ │ ├── api-keys-page.js # Admin: API-Key-Verwaltung + Heartbeat-Check
|
||
│ │ │ ├── settings-page.js # App-Einstellungen
|
||
│ │ │ ├── login-page.js # Login-Formular
|
||
│ │ │ └── shared-page.js # Öffentliche geteilte Nachricht
|
||
│ │ └── lib/ # Vendor-Libraries
|
||
│ │ ├── marked.min.js # Markdown Parser
|
||
│ │ ├── codemirror.min.js # Code Editor
|
||
│ │ ├── codemirror.cjs # CM CommonJS Wrapper
|
||
│ │ ├── easymde.min.js # Markdown Editor
|
||
│ │ ├── katex.min.js # KaTeX Math-Rendering Engine
|
||
│ │ └── cm-mode-*.min.js # CodeMirror Syntax-Modes
|
||
│ ├── remote.ps1 # PowerShell-Script für Remote-Client-Verbindung
|
||
│ ├── fonts/ # Self-hosted Fonts (Inter, JetBrains Mono)
|
||
│ └── lib/font-awesome/ # Font Awesome Icons
|
||
│
|
||
├── WindowsApp/ # Windows Desktop Client (WPF)
|
||
│ ├── Gott.csproj # .NET 10 Windows, WebView2
|
||
│ ├── App.xaml / App.xaml.cs # WPF Application
|
||
│ ├── MainWindow.xaml/cs # WebView2 Host Window
|
||
│ └── appsettings.json # Server-URL Konfiguration
|
||
│
|
||
└── Build/ # Build-Output (NICHT editieren!)
|
||
├── Linux/
|
||
│ ├── bin/ obj/ publish/
|
||
└── Windows/
|
||
├── bin/ obj/ publish/
|
||
└── data/
|
||
├── gott.db # SQLite Datenbank
|
||
├── agents/ # Agent Memory-Dateien
|
||
│ └── {agentId}/
|
||
│ ├── IDENTITY.md
|
||
│ ├── SOUL.md
|
||
│ ├── MEMORY.md
|
||
│ └── memory/ # Tages-Logs etc.
|
||
├── skills/ # Skill-Verzeichnisse
|
||
│ └── {slug}/
|
||
│ ├── SKILL.md
|
||
│ ├── PROMPT.md
|
||
│ └── output/
|
||
├── images/ # Krea-generierte Bilder
|
||
├── models/ # ONNX-Modelle (Embedding)
|
||
│ └── all-MiniLM-L6-v2/ # Sentence-Embedding-Modell (model.onnx + vocab.txt)
|
||
├── models_cache.json # Gecachte Provider-Modelle (Nvidia, Pollinations, Gemini)
|
||
├── logs/ # Payload-Logs (optional)
|
||
└── backup/ # Backup-Archive
|
||
```
|
||
|
||
---
|
||
|
||
## Datenmodell
|
||
|
||
### Entity-Relationship
|
||
|
||
```
|
||
User (1) ─── (*) Agent
|
||
User (1) ─── (*) Session
|
||
Agent (1) ─── (*) Session (Session hat optional einen Agent)
|
||
Agent (1) ─── (*) Session (Session hat optional einen Review-Agent)
|
||
Agent (1) ─── (*) Session (Session hat optional einen Finalizer-Agent)
|
||
Agent (*) ─── (*) Skill (via agent_skills Tabelle)
|
||
Session (1) ─── (*) Chain (ein Chat-Request = eine Chain)
|
||
Session (1) ─── (*) Archive (archivierte Chains)
|
||
Chain (1) ─── (*) Message
|
||
Message (1) ─── (*) Attachment
|
||
Session (1) ─── (*) Cronjob
|
||
User (1) ─── (*) Cronjob
|
||
User (1) ─── (*) SharedMessage
|
||
Message (1) ─── (*) SharedMessage
|
||
User (1) ─── (*) TelegramBot
|
||
TelegramBot (1) ─── (*) Session (via telegram_bot_id)
|
||
```
|
||
|
||
### Tabellen
|
||
|
||
| Tabelle | Beschreibung |
|
||
|-------------------|-----------------------------------------------------|
|
||
| `users` | Benutzer (username, password_hash, display_name, is_admin, persona, settings, default_path) |
|
||
| `tokens` | Auth-Tokens (token → user_id) |
|
||
| `agents` | AI-Agenten (model – z.B. `claude-sonnet-4-6` oder `nvidia:deepseek-ai/deepseek-r1`, custom_commands, auto_commit, use_soul/identity/user_info/memory, use_web_search, web_search_version, allow_user_choice, task_instruction, avatar_scale/offset, avatar_small, is_public) |
|
||
| `sessions` | Chat-Sessions (description, image, image_small, workspace, context, custom_commands, telegram_bot_id, telegram_chat_id, avatar_scale/offset, preparer_agent_id, review_agent_id, max_review_iterations, finalizer_agent_id) |
|
||
| `chains` | Request-Chains (model, tokens, iterations, timing, tool_choice, tool_names, stream_error_info, plan_mode, archive_id) |
|
||
| `messages` | Einzelne Nachrichten (text, tool_use, tool_result, error, web_search_result), Tag für Sub-Agent-Markierung oder Import-Herkunft (`openclaw-import`), SortKey für korrekte Reihenfolge, thought_signature (Gemini) |
|
||
| `attachments` | Datei-Anhänge (base64, image/document) |
|
||
| `archives` | Archivierte Chain-Gruppen |
|
||
| `agent_skills` | Agent ↔ Skill Zuordnung |
|
||
| `cronjobs` | Geplante Tasks (cron expression, prompt, notify_session_id, is_enabled, last_run, next_run) |
|
||
| `shared_messages` | Geteilte Nachrichten (Share-Token, Ablaufdatum) |
|
||
| `telegram_bots` | Telegram Bots (bot_token, webhook_secret, name, is_public) – eigenständige Entität |
|
||
| `api_keys` | API-Keys pro Provider (key_id, provider, name, api_key, is_active) – nur ein Key pro Provider aktiv |
|
||
| `message_embeddings` | Sentence-Embeddings für semantische Suche (message_id, vector BLOB, session_id, agent_id) |
|
||
| `memory_file_embeddings` | Embeddings von Memory-Dateien (agent_id, file_path, chunk_index, vector BLOB) |
|
||
|
||
### Zentrale Records
|
||
|
||
**ChainRecord** – Repräsentiert einen kompletten Request-Response-Zyklus:
|
||
- `ChainId` – Eindeutige ID
|
||
- `SessionId` – Zugehörige Session
|
||
- `AgentId` – Zugehöriger Agent
|
||
- `Model` – Verwendetes Modell (z.B. `claude-sonnet-4-6`, `nvidia:deepseek-ai/deepseek-r1`, `gemini:gemini-2.5-flash`)
|
||
- `SystemPrompt` – Der vollständige System-Prompt (gespeichert für Nachvollziehbarkeit)
|
||
- `InputTokens`, `OutputTokens`, `CacheCreationTokens`, `CacheReadTokens` – Token-Verbrauch
|
||
- `Iterations` – Anzahl der Tool-Loop-Durchläufe
|
||
- `StopReason` – `end_turn`, `tool_use`, `max_iterations`, `error`, `client_disconnected`, `awaiting_user_choice`, `preparer_query`
|
||
- `ToolChoice` – Verwendete Tool-Choice-Strategie (automatisch: `any` wenn Workspace/Skills/Memory, sonst `auto`)
|
||
- `ToolNames` – Komma-separierte Liste der verwendeten Tools
|
||
- `ArchiveId` – Zugehöriges Archiv (wenn archiviert)
|
||
- `StreamErrorInfo` – Fehlerdetails bei Stream-Abbrüchen
|
||
- `PlanMode` – Ob die Chain im Plan-Modus erstellt wurde (informativ)
|
||
- `DurationMs` – Gesamtdauer der Chain
|
||
|
||
**MessageRecord** – Eine einzelne Nachricht innerhalb einer Chain:
|
||
- `Role` – `user`, `assistant`
|
||
- `Type` – `text`, `tool_use`, `tool_result`, `error`, `web_search_result`
|
||
- `Content` – Textinhalt oder JSON (bei tool_use: Input-JSON)
|
||
- `ToolName`, `ToolCallId` – Bei Tool-Aufrufen
|
||
- `IsError` – Ob die Nachricht ein Fehler ist
|
||
- `Iteration` – Welche Tool-Loop-Iteration
|
||
- `AttachmentCount` – Anzahl angehängter Dateien
|
||
- `AttachmentNames` – JSON-Array mit Dateinamen
|
||
- `Tag` – Markierung (`null` = normal, `"preparer"/"review"/"finalizer"` = Sub-Agent-Message (wird aus API-History gefiltert), `"openclaw-import"` = importierte Nachricht (wird immer in History einbezogen))
|
||
- `IsMarked` – Als wichtig markierte Nachricht (wird in System-Prompt aufgenommen)
|
||
- `ThoughtSignature` – Gemini thought_signature (für Gemini-Tool-Calls, wird im nächsten Request zurückgesendet)
|
||
- `DeletedOn` – Soft-Delete Zeitstempel
|
||
|
||
**SessionWithStats** – Erweitertes Session-Objekt für Listen-Endpunkt:
|
||
- Enthält alle `SessionRecord`-Felder + `MessageCount`, `ArchiveMessageCount`, `TotalMessageCount`
|
||
- `TelegramConfigured`, `TelegramLinked` – Telegram-Status (Bot zugewiesen / Chat-ID verknüpft)
|
||
- `GitStatus` – Inline Git-Status-Summary (`Add`, `Edit`, `Delete`, `Outgoing`) oder `null` wenn kein Workspace (wird im Endpoint hinzugefügt, nicht Teil des Records)
|
||
- `Image` wird durch `ImageSmall` ersetzt (Thumbnail für Dashboard-Performance)
|
||
|
||
**GitStatusSummary** – Kompakte Git-Status-Info:
|
||
- `Add`, `Edit`, `Delete` – Anzahl geänderter Dateien nach Typ
|
||
- `Outgoing` – Anzahl ausgehender (nicht gepushter) Commits
|
||
|
||
---
|
||
|
||
## Backend-Architektur
|
||
|
||
### Program.cs – Startup
|
||
|
||
Die `Program.cs` ist der zentrale Einstiegspunkt und konfiguriert:
|
||
|
||
1. **Service-Registrierung** – Alle Services als Singletons im DI-Container
|
||
2. **Datenbank-Initialisierung** – `DatabaseService` erstellt Schema + Migrationen
|
||
3. **Default-User** – Wird automatisch angelegt falls keine User existieren
|
||
4. **Middleware** – Static Files → `AuthMiddleware` → Endpoints
|
||
5. **Endpoint-Mapping** – Alle 17 Endpoint-Gruppen registriert
|
||
6. **Server** – Kestrel auf `http://0.0.0.0:{Port}` (Port aus Config, Default 19000)
|
||
|
||
Initialisierungsreihenfolge:
|
||
```
|
||
RemoteConnectionManager → AuthService → DatabaseService → EnsureDefaultUser
|
||
→ ImageService, StreamingTracker, MemoryService, SkillService
|
||
→ EmbeddingService → VectorSearchService
|
||
→ ToolService → ChatService
|
||
→ AnthropicService (Token aus DB: api_keys) → OpenAiService (+ LoadModelsAsync)
|
||
→ TelegramService → KreaService
|
||
→ CronjobService (BackgroundService) + EmbeddingBackgroundService (BackgroundService)
|
||
```
|
||
|
||
### Services
|
||
|
||
| Service | Typ | Aufgabe |
|
||
|------------------------|--------------|------------------------------------------------------------------|
|
||
| `DatabaseService` | Singleton | SQLite CRUD, Schema-Init, Migrationen, alle DB-Operationen |
|
||
| `AuthService` | Singleton | Token-Erzeugung/-Validierung, Passwort-Hashing (SHA256) |
|
||
| `ChatService` | Singleton | Chat-Kontext aufbauen, History aus DB rekonstruieren |
|
||
| `AnthropicService` | Singleton | Claude API Kommunikation (Streaming SSE + Non-Streaming) |
|
||
| `OpenAiService` | Singleton | OpenAI-kompatible Provider (Nvidia, Pollinations, Gemini) mit Tool-Calling, Streaming, Modell-Caching |
|
||
| `ToolService` | Singleton | Tool-Definitionen bereitstellen, Tool-Aufrufe ausführen |
|
||
| `WorkspaceHelper` | Static | Workspace-Pfad-Auflösung (relativ → absolut), Path-Traversal-Schutz |
|
||
| `SystemPromptBuilder` | Static | Modularer System-Prompt aus konfigurierbaren Blöcken + Reminder-Generierung |
|
||
| `MemoryService` | Singleton | Agent-Gedächtnis auf Dateisystem (MEMORY.md, IDENTITY.md etc.) |
|
||
| `SkillService` | Singleton | Skill-Discovery und -Management aus Dateisystem |
|
||
| `TelegramService` | Singleton | Telegram Bot API, Webhook-Handling, Nachrichten senden |
|
||
| `CronjobService` | Background | Minütlicher Check auf fällige Cronjobs, Ausführung via Claude |
|
||
| `SseHelper` | Static | Shared SSE-Utilities: `WriteSseEvent`, `TryWriteSse`, `FinalizeChain`, `TruncateToolResult` |
|
||
| `EmbeddingService` | Singleton | Lokales Sentence-Embedding via all-MiniLM-L6-v2 (ONNX, 384 Dimensionen), WordPiece-Tokenisierung |
|
||
| `VectorSearchService` | Singleton | Semantische Suche über Message-Embeddings + Memory-File-Embeddings (Cosine Similarity) |
|
||
| `EmbeddingBackgroundService` | Background | Backfill bestehender Messages + Live-Embedding neuer Messages/Memory-Files |
|
||
| `RemoteConnectionManager` | Singleton | WebSocket-basierte Remote-Verbindungen, Command-Execution auf verbundenen Clients |
|
||
| `KreaService` | Singleton | Krea API für Bildgenerierung, Job-Polling, Image-Download |
|
||
| `ImageService` | Singleton | Bild-Resize + WebP-Konvertierung (512px + 256px Thumbnail) für Avatare und Session-Bilder |
|
||
| `StreamingTracker` | Singleton | Trackt aktive Streams + CancellationTokenSource pro Session für Stop-Button |
|
||
|
||
### Endpoints (API)
|
||
|
||
Alle Endpoints folgen dem **Minimal API**-Pattern mit Extension Methods (`MapXxxEndpoints`).
|
||
|
||
| Gruppe | Basis-Route | Methoden |
|
||
|---------------|----------------------------|---------------------------------------------|
|
||
| **Auth** | `/api/auth/` | `POST login`, `POST logout`, `GET me`, `GET autologin` |
|
||
| **Chat** | `/api/chat` | `POST` (SSE Streaming), `POST stop` |
|
||
| **History** | `/api/history` | `GET history`, `GET poll`, `POST archive`, `GET archives`, `GET archive/{id}`, `GET attachments/{id}`, `PUT messages/{id}/mark`, `PUT messages/{id}/delete`, `GET chains/{id}` |
|
||
| **Sessions** | `/api/sessions/` | CRUD + `GET system-prompt`, `GET system-prompt-blocks`, `GET streaming`, `POST exec`, `GET git-status` (Liste inkl. inline Git-Status + Thumbnail) |
|
||
| **Agents** | `/api/agents/` | CRUD (Liste inkl. inline Skills + Thumbnail) |
|
||
| **Admin** | `/api/admin/` | `GET users`, `POST users`, `PUT users/{id}`, `PUT users/{id}/password`, `DELETE users/{id}`, `POST users/{id}/impersonate`, `POST users/{id}/login-link` (Admin-only) |
|
||
| **Workspace** | `/api/workspace/` | `GET tree`, `GET/PUT file`, `GET browse`, `POST mkdir`, `DELETE dir`, `POST browse/mkdir`, `GET agent/tree`, `GET/PUT agent/file`, `POST agent/mkdir`, `GET raw` |
|
||
| **Telegram** | `/api/telegram/` | Bot-CRUD (`/bots`, `/bots/available`), `POST webhook/{botId}` (unauthenticated), Session-Config, Webhook-Management |
|
||
| **OpenClaw** | `/api/openclaw/` | `GET sessions` (Liste importierbarer Sessions), `POST import` (Import in bestehende Session) |
|
||
| **Cronjobs** | `/api/cronjobs/` | CRUD + `POST toggle`, `POST run` |
|
||
| **Krea** | `/api/images/` | `POST generate`, `GET list`, `GET file/{name}`, `GET file/{name}/thumb`, `GET status` |
|
||
| **Skills** | `/api/skills` | `GET list`, `GET/PUT agent skills`, Output-Files + Thumbnails |
|
||
| **Profile** | `/api/profile` | `GET`, `PUT`, `PUT password` |
|
||
| **Settings** | `/api/settings` | `GET`, `PUT` |
|
||
| **Backup** | `/api/backup` | `POST` (Admin only, tar.gz Download) |
|
||
| **Shared** | `/api/shared/`, `/api/messages/{id}/share` | Share erstellen, öffentlich abrufen, auflisten, löschen |
|
||
| **Models** | `/api/models/` | `GET nvidia`, `GET pollinations`, `GET gemini`, `POST refresh` |
|
||
| **API Keys** | `/api/admin/api-keys/` | CRUD + `PUT activate/deactivate`, `POST check` (Heartbeat-Check für Anthropic-Keys) (Admin-only) |
|
||
| **Remote** | `/api/remote` | WebSocket-Endpoint für Remote-Clients, `GET connections`, `POST assign/unassign`, `GET /remote.ps1` |
|
||
| **Info** | `/api/info` | `GET` (Build-Zeitstempel + Embedding-Verfügbarkeit) |
|
||
| **ServerSettings** | `/api/admin/server-settings` | `GET`, `PUT` (Admin-only, liest/schreibt `appsettings.Production.json`) |
|
||
|
||
### Middleware
|
||
|
||
**AuthMiddleware** prüft jede `/api/`-Anfrage auf ein gültiges `gott_token`-Cookie.
|
||
|
||
Ausnahmen (kein Auth nötig):
|
||
- Statische Dateien (nicht `/api/`)
|
||
- `POST /api/auth/login`
|
||
- `GET /api/auth/autologin` (Token-basierter Autologin via Link)
|
||
- `POST /api/telegram/webhook/{botId}` (authentifiziert via Secret-Header)
|
||
- `GET /api/shared/{token}` (öffentlicher Zugriff auf geteilte Nachrichten)
|
||
- `/api/remote` (WebSocket-Endpoint für Remote-Clients)
|
||
- `GET /api/images/file/*` (Krea-Bilder und Thumbnails, öffentlich)
|
||
|
||
**API-Key Auth** (ohne User-Kontext):
|
||
- `/api/images/*`-Routen akzeptieren einen `X-Api-Key`-Header. Wenn der Key mit `KreaApiKey` aus der Config übereinstimmt, wird der Request ohne User-Authentifizierung durchgelassen. Normaler Cookie-Auth funktioniert weiterhin als Fallback.
|
||
|
||
Bei Erfolg werden `UserId` und `IsAdmin` in `HttpContext.Items` gesetzt (bei API-Key-Auth sind diese nicht gesetzt).
|
||
|
||
### Models
|
||
|
||
Die Models in `Models/` sind reine Daten-Records ohne Business-Logic (Ausnahme: `ChatMessage` hat Helper-Methoden für Content-Extraktion und `UserRecord` hat `GetEffectivePersona()`).
|
||
|
||
**ChatContext** ist ein Runtime-Objekt das den vollständigen Kontext eines Chat-Requests hält:
|
||
- Chain-ID, Session-ID, Model-ID
|
||
- Aufgelöster System-Prompt (Text + strukturierte Blöcke)
|
||
- Workspace-Pfad, Tool-Choice (automatisch bestimmt)
|
||
- Prompt-Blöcke (`PromptBlocks`) – für Reminder-Generierung in der History
|
||
- Referenzen auf Agent und Session Records
|
||
|
||
### Templates
|
||
|
||
`Templates/` enthält Vorlagen die beim Erstellen eines neuen Agents in dessen Memory-Verzeichnis kopiert werden:
|
||
|
||
- **IDENTITY.md** – Name und Grundinfos (Platzhalter `{NAME}` wird ersetzt)
|
||
- **SOUL.md** – Persönlichkeit und Verhaltensgrundsätze
|
||
- **USER.md** – Informationen über den User (global, nicht agent-spezifisch)
|
||
|
||
---
|
||
|
||
## Frontend-Architektur
|
||
|
||
Das Frontend ist eine **Single-Page Application** ohne Build-Step, komplett in Vanilla JavaScript.
|
||
|
||
### Routing
|
||
|
||
`router.js` implementiert ein Hash-basiertes Client-Side-Routing:
|
||
|
||
| Route | Page | Beschreibung |
|
||
|--------------------------|---------------------|---------------------------|
|
||
| `#/` | `dashboard.js` | Session-Übersicht |
|
||
| `#/chat/{sessionId}` | `chat-page.js` | Chat-Ansicht |
|
||
| `#/session/{sessionId}` | `session-page.js` | Session-Einstellungen |
|
||
| `#/agent/{agentId}` | `agent-page.js` | Agent-Editor |
|
||
| `#/telegram-bot/{botId}` | `telegram-bot-page.js` | Telegram-Bot-Editor |
|
||
| `#/cronjobs` | `cronjob-page.js` | Cronjob-Verwaltung |
|
||
| `#/profile` | `profile-page.js` | Profil |
|
||
| `#/settings` | `settings-page.js` | App-Einstellungen |
|
||
| `#/admin` | `admin-page.js` | Benutzerverwaltung (Admin-only) |
|
||
| `#/server-settings` | `server-settings-page.js` | Servereinstellungen (Admin-only) |
|
||
| `#/login` | `login-page.js` | Login |
|
||
| `#/shared/{token}` | `shared-page.js` | Öffentliche geteilte Nachricht (kein Auth) |
|
||
|
||
### Kern-Module
|
||
|
||
- **app.js** – Initialisierung, Auth-Check, globale Event-Handler
|
||
- **api.js** – Zentraler API-Client mit `fetch()`-Wrapper, Auth-Redirect bei 401
|
||
- **ui-helpers.js** – DOM-Helpers (`$`, `esc`, `truncate`), Toast-System, ScrollDown, Meta-Bar, Button-Builder
|
||
- **markdown.js** – Markdown-Rendering (`renderMarkdown`), LaTeX/Code-Schutz, Krea-Gallery-Wrapping
|
||
- **cards.js** – Card-Builder (`buildAgentCard()`, `buildSessionCard()`, `buildTelegramBotCard()`)
|
||
- **msg-actions.js** – Nachrichten-Aktionen (Delete, Mark, Share, Reply-Button, Timestamp)
|
||
- **tool-ui.js** – Tool-Boxes (Diffs, Todos, File-Content), Visibility-Logik
|
||
- **modals.js** – Modale Dialoge (System-Prompt, Command-Output, Krea-Image)
|
||
- **stream-ui.js** – Stream-Bubble, Review-Streaming-Rendering
|
||
- **bubbles.js** – Message Bubbles (User, Assistant, Error, Warning, Choice, Review, Attachments)
|
||
- **chat.js** – SSE-Stream-Handling, sendMessage, Plan-Mode
|
||
- **history.js** – Chat-History laden, paginieren, rendern, Polling
|
||
- **archive.js** – Archiv-Loading und -Rendering
|
||
- **reply.js** – Reply/Quote-Handling (Set, Clear, Format)
|
||
- **files.js** – File-Upload, Workspace-Refs
|
||
- **session.js** – Session-State-Management
|
||
- **file-explorer.js** – Workspace File-Explorer Panel (Dateien, Ordner erstellen/löschen, Folder-Browser)
|
||
- **avatar-editor.js** – Wiederverwendbares Avatar-Editor-Modal (Upload, Paste, AI-Generate, Scale)
|
||
- **commands-ui.js** – Custom-Commands UI-Komponente
|
||
|
||
### Wiederverwendbare Card-Builder (cards.js)
|
||
|
||
- **`buildAgentCard(agent)`** – Erzeugt ein `.card` DOM-Element für einen Agent (Avatar, Name, Model-Badge, Feature-Badges: autoCommit, useSoul, useIdentity, useUserInfo, useMemory, webSearch, Skills, Public/Fremd). Skills werden inline vom API-Endpunkt mitgeliefert. Wird im Dashboard und im Agent-Picker-Modal (Session-Formular) verwendet.
|
||
- **`buildSessionCard(session, agent)`** – Erzeugt ein `.card` DOM-Element für eine Session (Avatar, Name, Badges: Agent, Kontext, Telegram, Meta: Nachrichten/Archiv/Workspace, Git-Status). Git-Status wird inline vom API-Endpunkt mitgeliefert. Wird im Dashboard und im Session-Picker-Modal (Cronjob-Formular) verwendet.
|
||
- **`buildTelegramBotCard(bot)`** – Erzeugt ein `.card` DOM-Element für einen Telegram Bot (Name, maskierter Token, Session-Count, Public/Fremd-Badges). Wird im Dashboard und im Bot-Picker-Modal (Session-Formular) verwendet.
|
||
- **Picker-Modals** – Agent- und Session-Auswahl in Formularen (Session-/Cronjob-Seiten) erfolgt über modale Dialoge mit den Card-Buildern statt über Dropdowns.
|
||
- **emoji.js** – Emoji-Picker mit Kategorien und Suche
|
||
- **tooltip.js** – Tooltip-System für Hover-Infos
|
||
- **title.js** – Dynamisches Tab-Titel-Management
|
||
|
||
### Styling
|
||
|
||
CSS ist in modulare Dateien aufgeteilt mit CSS Custom Properties für Theming:
|
||
|
||
- `theme.css` – Alle Farben, Abstände, Schriften als CSS Variables
|
||
- `base.css` – Reset und Grundlayout
|
||
- `main.css` – Sidebar, Content-Area, Responsive Layout
|
||
- `chat.css` – Chat-Bubbles, Streaming-Animation, Tool-Outputs
|
||
- `markdown.css` – Rendering von Markdown in Nachrichten
|
||
- `mobile.css` – Responsive Breakpoints und Anpassungen
|
||
|
||
---
|
||
|
||
## Multi-Provider-System (OpenAI-kompatibel)
|
||
|
||
Neben Anthropic Claude unterstützt Gott drei **OpenAI-kompatible Provider**. Der Agent-Model-String bestimmt den Provider:
|
||
|
||
| Prefix | Provider | Base-URL | API-Key |
|
||
|--------|----------|----------|---------|
|
||
| (kein) | Anthropic Claude | `api.anthropic.com` | `AnthropicToken` |
|
||
| `nvidia:` | Nvidia (g4f.space Proxy) | `g4f.space/api/nvidia/v1` | keiner |
|
||
| `pollinations:` | Pollinations | `text.pollinations.ai/openai` | keiner |
|
||
| `gemini:` | Google Gemini | `generativelanguage.googleapis.com/v1beta/openai` | `GeminiApiKey` |
|
||
|
||
### Model-String-Format
|
||
|
||
`provider:model-id` – z.B. `nvidia:deepseek-ai/deepseek-r1`, `gemini:gemini-2.5-flash`, `pollinations:openai-fast`
|
||
|
||
### Modell-Caching
|
||
|
||
Beim Server-Start werden verfügbare Modelle von den Provider-APIs geladen und in `data/models_cache.json` gecacht. Bei nachfolgenden Starts wird der Cache verwendet (kein API-Call). Manueller Refresh via `POST /api/models/refresh`.
|
||
|
||
### OpenAiService
|
||
|
||
`OpenAiService` implementiert:
|
||
- **Streaming via SSE** (OpenAI-Format: `choices[0].delta`)
|
||
- **Tool-Calling** (identische Tools wie AnthropicService)
|
||
- **Agentic Loop** (Tool-Results → nächste Iteration)
|
||
- **Gemini thought_signature** – wird aus `extra_content.google.thought_signature` gelesen, in DB persistiert (`MessageRecord.ThoughtSignature`), und beim History-Rebuild in `extra_content.google.thought_signature` zurückgesendet
|
||
|
||
### Routing
|
||
|
||
`ChatService.ResolveModelId()` prüft den Model-String:
|
||
- Enthält `:` mit bekanntem Provider-Prefix → `OpenAiService.StreamChat()`
|
||
- Sonst → `AnthropicService.StreamChat()`
|
||
|
||
---
|
||
|
||
## Chat-Pipeline
|
||
|
||
Der Chat-Flow von User-Input bis zur gerenderten Antwort:
|
||
|
||
```
|
||
1. User tippt Nachricht + optionale Attachments (Bilder/Dokumente)
|
||
↓
|
||
2. Frontend: POST /api/chat { content, sessionId }
|
||
↓
|
||
3. ChatEndpoint:
|
||
a) User-Message + Attachments in DB speichern
|
||
b) ChatService.PrepareContext() → Agent, Model, System-Prompt, Chain
|
||
c) ChatService.BuildHistoryFromDb() → vollständige Message-History
|
||
d) Model-Routing: Anthropic → AnthropicService.StreamChat(), OpenAI-Provider (nvidia:/pollinations:/gemini:) → OpenAiService.StreamChat()
|
||
↓
|
||
4. Agentic Loop (AnthropicService oder OpenAiService):
|
||
┌─────────────────────────────────────────┐
|
||
│ while (iterations < maxIterations) │
|
||
│ → Claude API Request (SSE) │
|
||
│ → Stream-Processing: │
|
||
│ • Text-Tokens → SSE an Frontend │
|
||
│ • Tool-Use → ToolService.Execute() │
|
||
│ • Tool-Results → nächste Iteration │
|
||
│ → Messages in DB persistieren │
|
||
│ → Stop wenn: end_turn / max_iterations│
|
||
│ → CancellationToken bei Abbruch │
|
||
└─────────────────────────────────────────┘
|
||
↓
|
||
4b. Review-Phase (optional, wenn ReviewAgent konfiguriert):
|
||
┌─────────────────────────────────────────┐
|
||
│ Nach end_turn des Worker-Agents: │
|
||
│ → RunReview() mit Review-Agent │
|
||
│ → Review-Agent prüft (eigene Tools) │
|
||
│ → [APPROVED] → Fertig │
|
||
│ → Feedback → an Worker als User-Msg │
|
||
│ → Worker korrigiert → erneuter Review │
|
||
│ → Max Review-Runden → Fertig │
|
||
└─────────────────────────────────────────┘
|
||
↓
|
||
4c. Finalizer-Phase (optional, wenn FinalizerAgent konfiguriert):
|
||
┌─────────────────────────────────────────┐
|
||
│ Nach Review (oder direkt nach Worker): │
|
||
│ → RunFinalizer() mit Finalizer-Agent │
|
||
│ → Finalizer arbeitet (eigene Tools) │
|
||
│ → z.B. git commit, deploy, cleanup │
|
||
│ → Kein Feedback-Loop (einmalig) │
|
||
└─────────────────────────────────────────┘
|
||
↓
|
||
5. Frontend: SSE Events empfangen und rendern
|
||
• "text" → Markdown parsen, Token für Token anzeigen
|
||
• "tool_*" → Tool-Aufrufe collapsible darstellen
|
||
• "review_*" → Review-Bubbles mit eigenem Styling
|
||
• "done" → Token-Stats, Dauer, Model anzeigen
|
||
```
|
||
|
||
### Abbruch (Stop-Button)
|
||
|
||
Der Stop-Mechanismus arbeitet auf zwei Ebenen:
|
||
|
||
**1. Expliziter Stop-Endpoint (`POST /api/chat/stop`):**
|
||
- Frontend ruft `apiStopChat(sessionId)` auf
|
||
- `StreamingTracker` hält pro Session einen `CancellationTokenSource`
|
||
- `StreamingTracker.Cancel(sessionId)` cancelt den Token sofort
|
||
- Der Chat-Handler verknüpft den Stop-Token mit dem Request-Token via `CreateLinkedTokenSource`
|
||
|
||
**2. CancellationToken-Propagierung:**
|
||
1. Frontend: `AbortController.abort()` → SSE-Fetch abgebrochen + `POST /api/chat/stop`
|
||
2. `StreamingTracker.Cancel()` → CancellationTokenSource wird gecancelled
|
||
3. `AnthropicService`: `ThrowIfCancellationRequested()` am Loop-Anfang, vor/nach Tool-Execution
|
||
4. `ToolService.ExecuteTool()`: CancellationToken wird durchgereicht → bei `execute` wird der Prozess gekillt
|
||
5. `WriteSseEvent()`: Alle SSE-Writes mit CancellationToken → sofortiger Abbruch
|
||
6. Chain wird mit `StopReason = "client_disconnected"` finalisiert
|
||
|
||
**Warum zwei Ebenen?** Kestrel erkennt einen Client-Disconnect erst beim nächsten Write auf den Response-Stream. Wenn das Backend in einer langen Tool-Ausführung steckt, wird nichts geschrieben – der explizite Stop-Endpoint garantiert sofortigen Abbruch.
|
||
|
||
### Streaming vs Non-Streaming
|
||
|
||
- **Web-Chat**: Streaming via SSE (`text/event-stream`). Tokens werden in Echtzeit gerendert.
|
||
- **Telegram**: Non-Streaming via `ChatNonStreaming()`. Vollständige Antwort wird abgewartet und als Telegram-Nachricht gesendet.
|
||
- **Cronjobs**: Non-Streaming, wie Telegram.
|
||
|
||
### History-Optimierung
|
||
|
||
Die History-Rekonstruktion in `BuildHistoryFromDb()` ist optimiert:
|
||
|
||
- **Max 120 Messages**: Die History wird auf maximal 120 Messages begrenzt (älteste entfernt)
|
||
- **Ältere Chains**: Nur `text`-Messages (user + assistant), keine Tool-Calls
|
||
- **Letzte Chain**: Vollständig mit allen Tool-Use/Tool-Result Blöcken
|
||
- **Attachments**: Nur für die letzte User-Message geladen (spart enorme Token)
|
||
- **Tool-Results**: In der History auf 20.000 Zeichen gekürzt (vollständig in DB)
|
||
- **System-Prompt-Reminder**: Alle ~30 Messages wird ein automatisch generierter Reminder als User/Assistant-Paar injiziert (z.B. `[System Reminder] Beachte weiterhin den System-Prompt (Identity, Workspace, Kontext, Memory, ...)`). Bekämpft das "Lost-in-the-Middle"-Problem bei langen Konversationen. Der Reminder-Text wird aus den aktiven Prompt-Block-Labels generiert (`SystemPromptBuilder.BuildReminder()`). Nur aktiv wenn ≥2 Blöcke vorhanden.
|
||
|
||
---
|
||
|
||
## Tool-System
|
||
|
||
Der Agent hat Zugriff auf folgende Tools, abhängig von Konfiguration:
|
||
|
||
### Datei-Tools (wenn Workspace konfiguriert)
|
||
|
||
| Tool | Beschreibung |
|
||
|---------------|--------------------------------------------------|
|
||
| `read_file` | Datei lesen (relativer Pfad zum Workspace) |
|
||
| `write_file` | Datei erstellen/überschreiben |
|
||
| `edit_file` | Exakten Textabschnitt in Datei ersetzen |
|
||
| `list_files` | Verzeichnis-Listing |
|
||
| `delete_file` | Datei oder leeres Verzeichnis löschen |
|
||
| `execute` | Shell-Befehl ausführen (bash, konfigurierbares Timeout) |
|
||
| `background_execute` | Shell-Befehl als Hintergrund-Prozess starten (gibt PID zurück, wartet nicht auf Ende) |
|
||
| `glob` | Dateien via Glob-Pattern finden |
|
||
| `grep` | Dateien nach Regex/Text durchsuchen |
|
||
| `todo_write` | Todo-Liste schreiben (als Datei im Workspace) |
|
||
|
||
### Memory-Tools (wenn UseMemory aktiviert)
|
||
|
||
| Tool | Beschreibung |
|
||
|----------------|-------------------------------------------|
|
||
| `memory_read` | Memory-Datei des Agents lesen |
|
||
| `memory_write` | Memory-Datei schreiben (überschreiben/append) |
|
||
| `memory_list` | Alle Memory-Dateien des Agents auflisten |
|
||
| `memory_search` | Semantische Suche über vergangene Chat-Nachrichten und Memory-Dateien (benötigt Embedding-Modell) |
|
||
|
||
### Remote-Execution (wenn Remote-Verbindung zugeordnet)
|
||
|
||
| Tool | Beschreibung |
|
||
|--------------------|-----------------------------------------------------|
|
||
| `remote_execute` | Shell-Befehl auf einem verbundenen Remote-Client ausführen (via WebSocket) |
|
||
|
||
### Cross-Session Messaging (wenn konfiguriert)
|
||
|
||
| Tool | Beschreibung |
|
||
|--------------------|-----------------------------------------------------|
|
||
| `send_to_session` | Nachricht an eine andere Session senden und dort eine Agent-Antwort auslösen |
|
||
|
||
### Interaktive Rückfragen (wenn AllowUserChoice aktiviert)
|
||
|
||
| Tool | Beschreibung |
|
||
|--------------------|-----------------------------------------------------|
|
||
| `ask_user_choice` | Stellt dem User eine Frage mit 2-5 Auswahlmöglichkeiten |
|
||
|
||
Dieses Tool wird **nicht ausgeführt**, sondern als SSE-Event an den Client weitergeleitet. Siehe [Interaktive Rückfragen](#interaktive-rückfragen-ask_user_choice).
|
||
|
||
### Web-Suche (wenn WebSearchVersion konfiguriert)
|
||
|
||
Nutzt Anthropics eingebaute Web-Suche als Server-Side-Tool.
|
||
|
||
### Plan-Modus: Read-only Tools
|
||
|
||
Im Plan-Modus werden nur folgende Tools bereitgestellt: `read_file`, `list_files`, `glob`, `grep`, `execute`, `todo_write`, `memory_*`, `memory_search`, `remote_execute`, `ask_user_choice`. Write-Tools (`write_file`, `edit_file`, `delete_file`) werden gefiltert.
|
||
|
||
### Tool-Choice (automatisch)
|
||
|
||
Die Tool-Choice-Strategie wird automatisch bestimmt (nicht konfigurierbar pro Agent):
|
||
|
||
- **`any`**: Automatisch wenn Workspace, Skills oder Memory vorhanden. Erste Iteration erzwingt Tool-Nutzung, danach `auto`. Verhindert dass der Agent nur Text antwortet wenn Tools benötigt werden.
|
||
- **`auto`**: Wenn keine Tools verfügbar. Claude entscheidet frei.
|
||
- **`none`**: Im `/no-tools`-Modus.
|
||
|
||
### Pfad-Sicherheit
|
||
|
||
Alle Dateipfade werden via `ResolvePath()` aufgelöst und gegen den Workspace/Memory-Ordner geprüft – Path-Traversal-Angriffe werden verhindert.
|
||
|
||
---
|
||
|
||
## Agent-System
|
||
|
||
Ein **Agent** definiert die Persönlichkeit und Fähigkeiten der AI:
|
||
|
||
| Eigenschaft | Beschreibung |
|
||
|-------------------|------------------------------------------------------------------|
|
||
| `Name` | Anzeigename |
|
||
| `Avatar` | Base64 Data-URL (WebP, 512px) + `AvatarSmall` (WebP, 256px Thumbnail) |
|
||
| `Model` | `sonnet`, `sonnet-4-5`, `sonnet-4-6`, `opus`, `opus-4-5`, `opus-4-6`, `haiku`, `haiku-4-5` |
|
||
| `CustomCommands` | JSON-Array mit Slash-Commands (Label + Prompt) |
|
||
| `AutoCommit` | `""` (nichts), `"mention"` (anmerken – nur auf User-Request), `"enforce"` (erzwingen – nach jeder Änderung), `"forbid"` (strikt verboten) |
|
||
| `UseSoul` | SOUL.md in System-Prompt einbinden |
|
||
| `UseIdentity` | IDENTITY.md in System-Prompt einbinden |
|
||
| `UseUserInfo` | User-Persona in System-Prompt einbinden |
|
||
| `UseMemory` | Memory-Tools aktivieren |
|
||
| `WebSearchVersion`| Web-Suche aktivieren (z.B. `web_search_20260209`) |
|
||
| `AllowUserChoice` | `ask_user_choice`-Tool verfügbar (Default: true) |
|
||
| `TaskInstruction` | Aufgabe/Anweisung – optionaler Text, wird als `## Anweisungen`-Block in System-Prompt eingefügt |
|
||
| `IsPublic` | Andere User können den Agent in ihren Sessions verwenden (aber nicht bearbeiten) |
|
||
|
||
### Model-Auflösung
|
||
|
||
```
|
||
"sonnet" → claude-sonnet-4-6
|
||
"sonnet-4-5" → claude-sonnet-4-5
|
||
"sonnet-4-6" → claude-sonnet-4-6
|
||
"opus" → claude-opus-4-6
|
||
"opus-4-5" → claude-opus-4-5
|
||
"opus-4-6" → claude-opus-4-6
|
||
"haiku" → claude-haiku-4-5
|
||
"haiku-4-5" → claude-haiku-4-5
|
||
(default) → claude-sonnet-4-6
|
||
```
|
||
|
||
---
|
||
|
||
## System-Prompt-Aufbau
|
||
|
||
Der `SystemPromptBuilder` erzeugt den System-Prompt modular aus konfigurierbaren **Blöcken**:
|
||
|
||
```
|
||
┌──────────────────────────────────────────┐
|
||
│ 1. IDENTITY.md (UseIdentity) │ → Wer ist der Agent?
|
||
│ 2. SOUL.md (UseSoul) │ → Persönlichkeit, Verhalten
|
||
│ 3. Anweisungen (TaskInstruction) │ → Aufgaben-/Anweisungstext (optional)
|
||
│ 4. User-Persona (UseUserInfo) │ → Infos über den User
|
||
│ 5. Workspace-Info │ → Tools + Workspace-Pfad
|
||
│ 6. Rückfragen (AllowUserChoice) │ → Hinweis zu ask_user_choice Tool
|
||
│ 7. Session-Kontext │ → Freiformtext
|
||
│ 8. Skills (Agent-Skills) │ → Skill-Prompts
|
||
│ 9. Memory (UseMemory) │ → Memory-Regeln + MEMORY.md Inhalt
|
||
│10. Git-Verhalten (AutoCommit) │ → Commit-Regeln
|
||
│11. Markierte Nachrichten │ → Vom User als wichtig markierte Messages
|
||
│ [Plan-Modus-Block] │ → Nur wenn planMode=true (überspringt 10)
|
||
│ [Preparer-Instruktionsblock] │ → Nur für Preparer-Agents
|
||
└──────────────────────────────────────────┘
|
||
```
|
||
|
||
Jeder Block hat:
|
||
- **Icon** – Font Awesome Icon-Klasse
|
||
- **Label** – Beschreibender Titel
|
||
- **Content** – Markdown-Text
|
||
|
||
Die Blöcke sind über die API als JSON abrufbar (`GET /api/sessions/{id}/system-prompt-blocks`), sodass das Frontend sie einzeln inspizierbar darstellen kann.
|
||
|
||
---
|
||
|
||
## Memory-System
|
||
|
||
Jeder Agent hat ein eigenes **Dateisystem-basiertes Gedächtnis**:
|
||
|
||
```
|
||
data/agents/{agentId}/
|
||
├── IDENTITY.md # Identität (aus Template initialisiert)
|
||
├── SOUL.md # Persönlichkeit (aus Template initialisiert)
|
||
├── MEMORY.md # Haupt-Memory (kuratierte Informationen)
|
||
└── memory/ # Unterordner für Tages-Logs, Themen
|
||
├── YYYY-MM-DD.md
|
||
└── ...
|
||
```
|
||
|
||
### Funktionsweise
|
||
|
||
1. **Initialisierung**: Beim Erstellen eines Agents werden `IDENTITY.md` und `SOUL.md` aus `Templates/` kopiert
|
||
2. **Lesen**: Der Agent kann seine Memory-Dateien via `memory_read` Tool lesen
|
||
3. **Schreiben**: Via `memory_write` (überschreiben oder append)
|
||
4. **Auflisten**: Via `memory_list` alle Dateien durchsuchen
|
||
5. **System-Prompt**: `MEMORY.md` Inhalt wird automatisch in den System-Prompt eingebettet
|
||
6. **IDENTITY.md** und **SOUL.md**: Werden als eigene System-Prompt-Blöcke eingebunden (wenn aktiviert)
|
||
|
||
### Sandbox
|
||
|
||
Alle Pfade werden gegen das Agent-Verzeichnis geprüft – kein Zugriff außerhalb möglich.
|
||
|
||
---
|
||
|
||
## Skill-System
|
||
|
||
Skills sind **Dateisystem-basierte Erweiterungen** die einem Agent zugeordnet werden können:
|
||
|
||
```
|
||
data/skills/{slug}/
|
||
├── SKILL.md # Metadaten (Name, Description, Version im Frontmatter)
|
||
├── PROMPT.md # System-Prompt-Erweiterung (in Agent-Prompt eingebettet)
|
||
├── _meta.json # Alternative Metadaten (Fallback)
|
||
└── output/ # Skill-Output (Bilder, Dateien, etc.)
|
||
```
|
||
|
||
### SKILL.md Frontmatter
|
||
|
||
```yaml
|
||
---
|
||
name: Mein Skill
|
||
description: Beschreibung
|
||
version: 1.0
|
||
---
|
||
```
|
||
|
||
### Agent-Zuordnung
|
||
|
||
Skills werden über die `agent_skills` Tabelle einem Agent zugeordnet. Der `PROMPT.md`-Inhalt aller zugeordneten Skills wird als ein System-Prompt-Block eingefügt.
|
||
|
||
### Workspace-Implikation
|
||
|
||
Wenn ein Agent Skills hat aber kein Workspace konfiguriert ist, wird automatisch `/tmp` als Workspace gesetzt (damit `execute` für Skill-Commands funktioniert).
|
||
|
||
---
|
||
|
||
## Telegram-Integration
|
||
|
||
Telegram Bots sind **eigenständige Entitäten** mit einer eigenen Tabelle (`telegram_bots`), die am Dashboard verwaltet werden (wie Agents, Sessions, Cronjobs). Ein Bot kann **mehreren Sessions** zugewiesen werden – jede Session hat ihren eigenen Chat-Kontext.
|
||
|
||
### Datenmodell
|
||
|
||
**Telegram Bot** (`telegram_bots`):
|
||
- `BotId` – UUID
|
||
- `UserId` – Owner
|
||
- `Name` – Anzeigename
|
||
- `BotToken` – Bot-Token von @BotFather
|
||
- `WebhookSecret` – Einmalig generierter Token für Webhook-Verifizierung
|
||
- `IsPublic` – Andere User können den Bot in ihren Sessions verwenden (aber nicht bearbeiten)
|
||
|
||
**Session-Felder:**
|
||
- `TelegramBotId` – Verweis auf den zugewiesenen Bot (wie `AgentId`)
|
||
- `TelegramChatId` – Verknüpfte Chat-ID (1 Session = max. 1 Telegram-Chat)
|
||
|
||
### Webhook-Flow
|
||
|
||
```
|
||
1. User erstellt Telegram Bot im Dashboard (Name + Token)
|
||
2. User registriert Webhook auf der Bot-Page
|
||
→ Setzt Webhook-URL: {baseUrl}/api/telegram/webhook/{botId}
|
||
3. User weist Bot einer Session zu (Session-Settings, Bot-Picker)
|
||
4. Telegram sendet Updates an Webhook
|
||
5. AuthMiddleware lässt Webhook-Requests durch (kein Cookie nötig)
|
||
6. TelegramEndpoint prüft X-Telegram-Bot-Api-Secret-Token Header
|
||
7. TelegramService verarbeitet Update:
|
||
- Bot laden via botId
|
||
- Chat-ID extrahieren
|
||
- Session suchen: telegram_bot_id = botId AND telegram_chat_id = chatId
|
||
- /start → Auto-Link (wenn genau 1 unverknüpfte Session) oder Chat-ID anzeigen
|
||
- /clear → History archivieren
|
||
- /status → Session-Info anzeigen
|
||
- Text → An Claude weiterleiten (Non-Streaming)
|
||
```
|
||
|
||
### Auto-Link-Logik
|
||
|
||
Bei `/start` oder einer normalen Nachricht, wenn keine Session für den Chat verknüpft ist:
|
||
1. Suche Sessions mit diesem Bot, die **noch keine Chat-ID** haben
|
||
2. **Genau 1 gefunden** → Automatisch verknüpfen + Bestätigungsnachricht
|
||
3. **0 oder mehrere gefunden** → "Deine Chat-ID ist X, bitte im Web-UI zuweisen"
|
||
|
||
Unlink ist **nur über das Web-UI** möglich (kein Telegram-Befehl).
|
||
|
||
### Non-Streaming Chat
|
||
|
||
Telegram-Nachrichten werden über `AnthropicService.ChatNonStreaming()` verarbeitet. Die History wird genauso aus der DB aufgebaut wie beim Web-Chat. Antworten über 4096 Zeichen werden automatisch in Chunks aufgeteilt.
|
||
|
||
### Dashboard & Frontend
|
||
|
||
- **Dashboard**: Telegram-Bots-Section mit Cards (Name, maskierter Token, Session-Count)
|
||
- **Bot-Page** (`#/telegram-bot/{botId}`): Name, Token, Webhook-Secret (readonly), Webhook-Verwaltung (registrieren/status/entfernen), Public-Checkbox, Löschen
|
||
- **Session-Page**: Bot-Picker (wie Agent-Picker), readonly Chat-ID mit Unlink-Button
|
||
|
||
---
|
||
|
||
## Cronjob-System
|
||
|
||
Der `CronjobService` ist ein **BackgroundService** der minütlich nach fälligen Jobs prüft.
|
||
|
||
### Datenmodell
|
||
|
||
| Feld | Beschreibung |
|
||
|---------------------|------------------------------------------------|
|
||
| `CronExpression` | Standard-Cron-Format (5 Felder), via Cronos |
|
||
| `SessionId` | Session in der der Job läuft |
|
||
| `Prompt` | Text der als User-Message an Claude gesendet wird |
|
||
| `NotifySessionId` | Optionale Session für Benachrichtigung |
|
||
| `IsEnabled` | Aktiviert/Deaktiviert |
|
||
| `NextRunAt` | Nächste geplante Ausführung (UTC) |
|
||
|
||
### Ausführungsflow
|
||
|
||
```
|
||
1. CronjobService prüft jede Minute
|
||
2. Fällige Jobs (next_run_at <= jetzt) werden geladen
|
||
3. Für jeden Job:
|
||
a) ChatService.PrepareContext() – Chain + System-Prompt aufbauen
|
||
b) History aus DB laden
|
||
c) Prompt als User-Message anhängen + in DB speichern
|
||
d) AnthropicService.ChatNonStreaming() ausführen
|
||
e) Telegram-Benachrichtigung senden (auf Arbeits-Session)
|
||
f) Optional: Zusammenfassung in Notify-Session schreiben + Telegram dort
|
||
g) Nächste Ausführung berechnen + DB aktualisieren
|
||
```
|
||
|
||
### Benachrichtigungen
|
||
|
||
- **Arbeits-Session**: Wenn die Session Telegram hat, wird die Antwort dort gepostet
|
||
- **Notify-Session**: Wenn konfiguriert, wird eine Zusammenfassung in eine andere Session geschrieben und der Agent dort antwortet darauf
|
||
|
||
---
|
||
|
||
## Krea-Bildgenerierung
|
||
|
||
Die `KreaService` ermöglicht Bildgenerierung über die Krea API.
|
||
|
||
### Unterstützte Modelle
|
||
|
||
```
|
||
flux, flux-kontext, flux-1.1-pro, flux-1.1-pro-ultra,
|
||
nano-banana, nano-banana-pro, imagen-3, imagen-4, imagen-4-fast, imagen-4-ultra,
|
||
ideogram-2-turbo, ideogram-3, seedream-3, seedream-4,
|
||
chatgpt-image, runway-gen-4
|
||
```
|
||
|
||
### Flow
|
||
|
||
```
|
||
1. POST /api/images/generate { prompt, model, width, height, ... }
|
||
2. KreaService sendet Request an Krea API
|
||
3. Job-Polling bis Status "completed"
|
||
4. Bilder herunterladen nach data/images/
|
||
5. Metadaten als JSON speichern
|
||
6. Response mit Dateinamen + URLs
|
||
```
|
||
|
||
### Bild-Serving
|
||
|
||
- `GET /api/images` – Liste aller generierten Bilder (aus Metadaten-JSON)
|
||
- `GET /api/images/file/{filename}` – Einzelnes Bild ausliefern (ohne Auth)
|
||
- `GET /api/images/file/{filename}/thumb` – Thumbnail (320px WebP, gecacht in `data/cache/images/`, ohne Auth)
|
||
- `GET /api/images/status` – Generierungsstatus abfragen
|
||
|
||
Bild-Dateien und Thumbnails sind **ohne Authentifizierung** abrufbar (für externe Einbindung).
|
||
|
||
---
|
||
|
||
## Auth & Multi-User
|
||
|
||
### Authentifizierung
|
||
|
||
- **Login**: Username + Passwort → SHA256 Hash-Vergleich → Token generieren
|
||
- **Token**: 32 Bytes Random, als `gott_token` HttpOnly Cookie (365 Tage)
|
||
- **Validierung**: Jeder API-Call prüft Token → User-ID
|
||
- **Logout**: Token aus DB löschen + Cookie entfernen
|
||
|
||
### Multi-User-Isolation
|
||
|
||
- Sessions sind an `user_id` gebunden
|
||
- Agents sind an `user_id` gebunden
|
||
- Cronjobs sind an `user_id` gebunden
|
||
- Jeder User sieht nur seine eigenen Daten
|
||
- Admin-Flag (`is_admin`) für Backup-Endpoint, Admin-Benutzerverwaltung, Impersonate
|
||
|
||
### Default-Path (Base-Path)
|
||
|
||
Jeder User hat einen optionalen `DefaultPath` – den Basis-Ordner für alle Workspaces. Wird **nur vom Admin** gesetzt.
|
||
|
||
- **Session-Workspace**: Speichert nur den **relativen Pfad** zum DefaultPath (z.B. `myapp` statt `/home/user/projects/myapp`)
|
||
- **Auflösung**: `WorkspaceHelper.ResolveWorkspace()` kombiniert DefaultPath + relativen Workspace → absoluter Pfad
|
||
- **System-Prompt**: Bekommt den **aufgelösten absoluten Pfad**, damit der Agent korrekt navigieren kann
|
||
- **Path-Traversal-Schutz**: `..` in relativen Workspace-Pfaden wird blockiert (400 Bad Request)
|
||
- **Legacy-Support**: Absolute Pfade in `session.Workspace` funktionieren weiterhin als Fallback
|
||
- **Folder-Browser**: Startet beim DefaultPath, User kann nicht über den Base-Path hinaus navigieren
|
||
|
||
### Default-User
|
||
|
||
Beim ersten Start wird automatisch ein Default-User erstellt (`DatabaseService.EnsureDefaultUser()`).
|
||
|
||
### User-Persona
|
||
|
||
Jeder User hat eine optionale **Persona** – ein Freitext der beschreibt wer er ist. Diese wird (wenn `UseUserInfo` am Agent aktiv ist) in den System-Prompt als "Dein Gegenüber" eingebettet.
|
||
|
||
---
|
||
|
||
## Admin-Benutzerverwaltung
|
||
|
||
Admins können alle User verwalten über `AdminEndpoint.cs` (alle Endpoints erfordern `is_admin`).
|
||
|
||
### API-Endpunkte
|
||
|
||
| Methode | Route | Beschreibung |
|
||
|----------|-----------------------------------------|-------------------------------------------|
|
||
| `GET` | `/api/admin/users` | Alle User auflisten |
|
||
| `POST` | `/api/admin/users` | Neuen User erstellen |
|
||
| `PUT` | `/api/admin/users/{id}` | User bearbeiten (Name, Username, Admin, DefaultPath) |
|
||
| `PUT` | `/api/admin/users/{id}/password` | Passwort zurücksetzen |
|
||
| `DELETE` | `/api/admin/users/{id}` | User löschen (+ Tokens, Shared Messages) |
|
||
| `POST` | `/api/admin/users/{id}/impersonate` | Als anderer User einloggen (Login-as-User) |
|
||
| `POST` | `/api/admin/users/{id}/login-link` | Autologin-Token generieren → URL zurückgeben |
|
||
|
||
### Impersonate (Login-as-User)
|
||
|
||
Admins können sich als beliebiger User einloggen ohne dessen Passwort zu kennen:
|
||
1. `POST /api/admin/users/{id}/impersonate` → Erstellt Token für Zieluser
|
||
2. Setzt neuen `gott_token`-Cookie → Admin-Session wird zum Zieluser
|
||
3. `location.reload()` → Frontend lädt mit neuem User-Kontext
|
||
|
||
### Login-Link generieren
|
||
|
||
Admins können für jeden User einen **Autologin-Link** generieren – z.B. für Setup oder Support:
|
||
1. `POST /api/admin/users/{id}/login-link` → Erstellt Token, gibt `{ token }` zurück
|
||
2. Frontend baut URL: `{window.location.origin}/api/auth/autologin?token={token}`
|
||
3. Link wird im Modal angezeigt mit Copy-Button
|
||
4. `GET /api/auth/autologin?token={token}` → Setzt Cookie + Redirect zu `/` (ohne Auth, wie Login)
|
||
|
||
### Frontend (`admin-page.js`)
|
||
|
||
- Erreichbar über Dashboard-Link "Benutzerverwaltung" (nur für Admins sichtbar)
|
||
- User-Liste als Cards mit Edit/Delete/Login-Link-Buttons
|
||
- Inline-Formular für Create/Edit
|
||
- Login-Link-Modal mit Copy-Funktion
|
||
- Sich selbst löschen ist nicht möglich
|
||
|
||
---
|
||
|
||
## Multi-Agent System (Preparer + Review + Finalizer)
|
||
|
||
Das Multi-Agent-System ermöglicht bis zu **vier Agents** pro Session: einen **Worker**, einen optionalen **Vorbereiter (Preparer)**, einen optionalen **Reviewer** und einen optionalen **Finalizer**.
|
||
|
||
### Architektur
|
||
|
||
```
|
||
User-Message
|
||
→ Preparer-Phase (optional):
|
||
→ Preparer-Agent bekommt: User-Message + History (inkl. eigener vorheriger Messages)
|
||
→ Preparer arbeitet (kann Tools nutzen: read_file, execute etc.)
|
||
→ Erledigt Vorarbeit mit seinen Fähigkeiten (z.B. JSON bauen, Daten aufbereiten)
|
||
→ [QUESTION] → Rückfrage an User, Flow stoppt, User muss erneut eingeben
|
||
→ Antwort ohne [QUESTION] → automatisch weiterleiten an Worker
|
||
→ Worker-Agent arbeitet (Tools, Iterationen) → end_turn
|
||
→ Review-Phase (optional):
|
||
→ Review-Agent bekommt: Konversation als Kontext
|
||
→ Review-Agent prüft (kann Tools nutzen: read_file, execute etc.)
|
||
→ [APPROVED] → Fertig, Antwort an User
|
||
→ Feedback-Text → wird als User-Message an Worker-Agent eingefügt
|
||
→ Worker-Agent korrigiert → end_turn → Review-Agent prüft erneut
|
||
→ Max Review-Runden erreicht → Fertig (mit letzter Worker-Antwort)
|
||
→ Finalizer-Phase (optional):
|
||
→ Finalizer-Agent bekommt: gesamte Konversation
|
||
→ Finalizer arbeitet (eigene Tools, z.B. git commit, deploy)
|
||
→ Kein Feedback-Loop – einmalige Ausführung
|
||
→ Fertig
|
||
```
|
||
|
||
### Konfiguration
|
||
|
||
Auf **Session-Ebene** (nicht Agent-Ebene):
|
||
|
||
| DB-Feld | SessionRecord | Beschreibung |
|
||
|---------|---------------|-------------|
|
||
| `preparer_agent_id` | `PreparerAgentId` | ID des Vorbereiter-Agents (optional, null = kein Preparer) |
|
||
| `review_agent_id` | `ReviewAgentId` | ID des Review-Agents (optional, null = kein Review) |
|
||
| `max_review_iterations` | `MaxReviewIterations` | Max. Review-Runden (Default: 3) |
|
||
| `finalizer_agent_id` | `FinalizerAgentId` | ID des Finalizer-Agents (optional, null = kein Finalizer) |
|
||
|
||
### Vorbereiter-Agent (Preparer)
|
||
|
||
Der Preparer wird **vor** dem Worker-Agent aufgerufen. Er analysiert die User-Anfrage, kann Vorarbeit leisten (z.B. Daten aufbereiten, JSON bauen, Code analysieren) und gibt das Ergebnis an den Worker weiter. Bei unklaren Anfragen stellt er Rückfragen.
|
||
|
||
#### Logik-Pattern: Default = Forward
|
||
|
||
Die Logik ist **invertiert** gegenüber dem Review-Pattern: Jede Antwort wird automatisch weitergeleitet, **außer** sie enthält explizit `[QUESTION]`. Das ist robuster als ein Pflicht-Tag wie `[FORWARD]`, weil LLMs dazu neigen, Tags zu vergessen.
|
||
|
||
| Antwort | Ergebnis |
|
||
|---------|----------|
|
||
| Ohne `[QUESTION]` | Automatisch weitergeleitet (`ShouldForward = true`) |
|
||
| Mit `[QUESTION]` | Rückfrage an User, Flow stoppt (`ShouldForward = false`) |
|
||
|
||
#### Backend-Flow (Preparer)
|
||
|
||
1. **ChatEndpoint** – Prüft ob `ctx.Preparer` vorhanden
|
||
2. **AnthropicService.RunPreparer()** – Ruft `RunAgentChat()` auf mit `agentType = "preparer"`
|
||
3. **Entscheidung** – `ShouldForward` prüft ob `[QUESTION]` im Text:
|
||
- **Forward**: Aufbereiteter Text wird als `[Vorbereitet von AgentName]:`-User-Message in DB gespeichert, History wird neu geladen, StreamChat startet
|
||
- **Rückfrage**: SSE `preparer_done` mit `forwarded: false`, Stream endet mit `stopReason: "preparer_query"`, User muss erneut eingeben
|
||
|
||
#### Preparer-History
|
||
|
||
Der Preparer bekommt seine eigene History (`BuildHistoryFromDb(sessionId, includeTag: "preparer")`), damit er seine vorherigen Rückfragen und User-Antworten sieht. Der Session-Agent bekommt die History **ohne** Preparer-Messages (Tag-Filter).
|
||
|
||
#### System-Prompt
|
||
|
||
Der Preparer bekommt einen speziellen Instruktionsblock an seinen System-Prompt angehängt:
|
||
- Erklärt die Rolle als Vorbereiter
|
||
- Definiert `[QUESTION]`-Mechanismus für Rückfragen
|
||
- Instruiert aktive Vorarbeit mit eigenen Fähigkeiten und Tools
|
||
- Betont: Antworten werden automatisch weitergeleitet
|
||
|
||
#### SSE-Events (Preparer)
|
||
|
||
| Event | Daten | Beschreibung |
|
||
|-------|-------|-------------|
|
||
| `preparer_start` | `{ agent: "Name" }` | Preparer-Phase beginnt |
|
||
| `preparer_text` | `{ text: "..." }` | Preparer-Agent-Text (Token-Stream) |
|
||
| `preparer_tool_use` | `{ tool: "name", input: "..." }` | Preparer nutzt Tool |
|
||
| `preparer_done` | `{ forwarded: true/false }` | Preparer-Phase beendet |
|
||
|
||
### Backend-Flow (Review)
|
||
|
||
1. **ChatService.PrepareContext()** – Wenn `session.ReviewAgentId` gesetzt:
|
||
- Review-Agent aus DB laden
|
||
- Eigenen SystemPrompt für den Review-Agent bauen (Identity, Soul, Memory, Skills)
|
||
- Eigene Tool-Definitionen für den Review-Agent
|
||
- `ReviewContext` mit Model, SystemPrompt, Tools, Agent-Referenz erstellen
|
||
|
||
2. **ChatEndpoint** – Übergibt `ctx.Review` an `AnthropicService.StreamChat()`
|
||
|
||
3. **AnthropicService.StreamChat()** – Nach `end_turn` des Worker-Agents:
|
||
- Prüft ob `ReviewContext` vorhanden und `reviewRound < MaxReviewIterations`
|
||
- Ruft `RunReview()` auf
|
||
|
||
4. **RunReview()** – Orchestriert eine einzelne Review-Runde:
|
||
- Baut Review-Messages auf: Konversation als Context + Prüfanweisung
|
||
- SSE-Event `review_start` an Client senden
|
||
- Ruft `RunAgentChat()` auf (generische Methode für Review/Finalizer, eigener API-Call an Claude)
|
||
- Review-Agent kann Tools nutzen (eigener Tool-Loop mit max. 25 Iterationen)
|
||
- SSE-Events `review_text` und `review_tool_use` für Live-Streaming
|
||
- Prüft ob Antwort `[APPROVED]` enthält:
|
||
- **Ja** → SSE `review_approved`, return `HasFeedback = false`
|
||
- **Nein** → SSE `review_feedback`, return `HasFeedback = true` + Feedback-Text
|
||
|
||
5. **Zurück in StreamChat()** – Bei Feedback:
|
||
- Feedback als User-Message in DB speichern (mit `[Review-Feedback von AgentName (Runde X/Y)]` Prefix)
|
||
- Worker-Agent bekommt Feedback als nächste User-Message
|
||
- `continue` → nächste while-Loop-Iteration → Worker korrigiert → Review erneut
|
||
- `reviewRound++` bis `MaxReviewIterations` erreicht
|
||
|
||
### ReviewContext / FinalizerContext (Model)
|
||
|
||
```csharp
|
||
// Identische Struktur für Review und Finalizer
|
||
public class ReviewContext
|
||
{
|
||
public string Model { get; set; } // Claude-Modell des Agents
|
||
public string SystemPrompt { get; set; } // Eigener System-Prompt
|
||
public AgentRecord Agent { get; set; } // Agent-Record für Name/Avatar
|
||
public List<object> Tools { get; set; } // Tool-Definitionen
|
||
public int MaxReviewIterations { get; set; } // Max Runden (nur bei Review relevant)
|
||
}
|
||
// ChatContext hat: ReviewContext Review + ReviewContext Finalizer
|
||
```
|
||
|
||
### SSE-Events
|
||
|
||
| Event | Daten | Beschreibung |
|
||
|-------|-------|-------------|
|
||
| `review_start` | `{ agent: "Name" }` | Review-Phase beginnt |
|
||
| `review_text` | `{ text: "..." }` | Review-Agent-Text (Token-Stream) |
|
||
| `review_tool_use` | `{ tool: "name", input: "..." }` | Review-Agent nutzt Tool |
|
||
| `review_feedback` | `{ feedback: "..." }` | Review nicht bestanden, Feedback an Worker |
|
||
| `review_approved` | `{ message: "..." }` | Review bestanden |
|
||
|
||
### DB-Persistierung
|
||
|
||
- **Worker-Antworten**: Normale Messages (role=assistant, type=text, tag=null)
|
||
- **Preparer-Antworten**: Messages mit `tag="preparer"` (aus API-History gefiltert, nur für Frontend-Anzeige)
|
||
- **Aufbereiteter Prompt**: Als User-Message mit Prefix `[Vorbereitet von AgentName]:`
|
||
- **Review-Feedback**: Als User-Messages mit Prefix `[Review-Feedback von AgentName (Runde X/Y)]:`
|
||
- **Review-Agent-Antworten**: Messages mit `tag="review"` (aus API-History gefiltert)
|
||
- **Finalizer-Antworten**: Messages mit `tag="finalizer"` (aus API-History gefiltert)
|
||
|
||
### Frontend
|
||
|
||
**Session-Page** (`session-page.js`):
|
||
- Zweiter Agent-Picker: "Review-Agent" (nutzt `buildAgentCard()` + `openAgentPickerModal()`)
|
||
- Input für "Max. Review-Runden" (Number-Input, Default 3)
|
||
|
||
**Chat-UI** (`chat.js`):
|
||
- SSE-Event-Handler für `review_start`, `review_text`, `review_tool_use`, `review_feedback`, `review_approved`
|
||
- Review-Bubbles mit eigenem Styling (sekundäre Farbe, Agent-Name-Header)
|
||
|
||
**History-Rendering** (`history.js`, `bubbles.js`):
|
||
- `renderMarkdown` parst `[Review: AgentName]` → styled Header mit Icon
|
||
- `renderMarkdown` parst `[Review-Feedback von AgentName (...)]` → styled Feedback-Header
|
||
- `renderMarkdown` filtert `[APPROVED]` aus (wird ausgeblendet)
|
||
- History erkennt Review-Nachrichten und rendert sie als `.review-bubble` statt normaler Bubbles
|
||
- Review-Nachrichten werden nicht mit normalen Nachrichten gemergt
|
||
|
||
**Shared-Page** (`shared-page.js`):
|
||
- Gleiche Review-Bubble-Erkennung für geteilte Nachrichten
|
||
|
||
### Finalizer-Agent
|
||
|
||
Der Finalizer wird **nach** dem Review (oder direkt nach dem Worker, falls kein Review-Agent konfiguriert) aufgerufen. Er führt abschließende Arbeiten durch (z.B. Git-Commit, Deployment, Cleanup) – **ohne Feedback-Loop**.
|
||
|
||
#### Konfiguration
|
||
|
||
| DB-Feld | SessionRecord | Beschreibung |
|
||
|---------|---------------|-------------|
|
||
| `finalizer_agent_id` | `FinalizerAgentId` | ID des Finalizer-Agents (optional, null = kein Finalizer) |
|
||
|
||
#### Backend-Flow
|
||
|
||
1. **ChatService.PrepareContext()** – Wenn `session.FinalizerAgentId` gesetzt:
|
||
- Finalizer-Agent aus DB laden
|
||
- `FinalizerContext` aufbauen (analog zu `ReviewContext`: Model, SystemPrompt, Tools)
|
||
|
||
2. **AnthropicService.StreamChat()** – Nach Review-Phase (oder direkt nach Worker):
|
||
- Prüft ob `FinalizerContext` vorhanden
|
||
- Ruft `RunFinalizer()` auf
|
||
|
||
3. **RunFinalizer()** – Baut Messages auf (gesamte Konversation + Finalisierungs-Anweisung), ruft `RunAgentChat()` auf (generische Methode, auch vom Review genutzt)
|
||
|
||
4. **RunAgentChat()** – Generische Methode für Preparer/Review/Finalizer:
|
||
- Eigener API-Call an Claude mit Agent-Config
|
||
- Tool-Loop mit SSE-Events (`{type}_text`, `{type}_tool_use`)
|
||
- Antwort als Message in DB persistieren (mit `Tag = agentType`, wird aus API-History gefiltert)
|
||
|
||
#### SSE-Events
|
||
|
||
| Event | Daten | Beschreibung |
|
||
|-------|-------|-------------|
|
||
| `finalizer_start` | `{ agent: "Name" }` | Finalizer-Phase beginnt |
|
||
| `finalizer_text` | `{ text: "..." }` | Finalizer-Agent-Text (Token-Stream) |
|
||
| `finalizer_tool_use` | `{ tool: "name", input: "..." }` | Finalizer nutzt Tool |
|
||
| `finalizer_done` | `{}` | Finalizer-Phase beendet |
|
||
|
||
#### Frontend
|
||
|
||
- **Session-Page**: Vierter Agent-Picker: "Vorbereiter-Agent", "Review-Agent", "Finalizer-Agent" im "Weitere Einstellungen"-Bereich
|
||
- **Chat-UI**: Preparer-Bubbles (`.preparer-bubble`, Info-Farbe, 🔍-Icon), Finalizer-Bubbles (`.finalizer-bubble`, grüne Farbe, 🏁-Icon)
|
||
- **History**: `[Preparer: AgentName]` → styled Header mit `fa-filter` Icon, `[Vorbereitet von AgentName]` → styled Forwarded-Header, `[Finalizer: AgentName]` → styled Header, `[FORWARD]`/`[QUESTION]` werden ausgeblendet
|
||
- **Dashboard**: 🏁-Badge auf Session-Cards mit Finalizer
|
||
|
||
---
|
||
|
||
## Interaktive Rückfragen (ask_user_choice)
|
||
|
||
Das `ask_user_choice`-Tool ermöglicht dem Agent, dem User **klickbare Auswahlmöglichkeiten** zu präsentieren, wenn es mehrere sinnvolle Wege gibt oder eine Entscheidung vom User getroffen werden muss.
|
||
|
||
### Tool-Definition
|
||
|
||
```json
|
||
{
|
||
"name": "ask_user_choice",
|
||
"description": "Stellt dem User eine Frage mit Auswahlmöglichkeiten",
|
||
"input_schema": {
|
||
"properties": {
|
||
"question": { "type": "string" },
|
||
"choices": { "type": "array", "items": { "type": "string" }, "minItems": 2, "maxItems": 5 }
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Agent-Konfiguration
|
||
|
||
- **`allow_user_choice`** (AgentRecord) – Boolean, Default `true`. Steuert ob das Tool verfügbar ist.
|
||
- Dashboard-Badge: `🔵 Rückfragen` wenn aktiv
|
||
- System-Prompt-Hinweis: "Nutze `ask_user_choice` sparsam – nur wenn die Entscheidung vom User getroffen werden soll"
|
||
- Bei Review/Finalizer/Cronjobs: **Immer deaktiviert** (kein User zum Fragen)
|
||
|
||
### Backend-Flow
|
||
|
||
1. Claude ruft `ask_user_choice` auf → Backend erkennt im `ProcessStream`
|
||
2. Tool wird **nicht ausgeführt** – stattdessen SSE-Event `user_choice` gesendet
|
||
3. `stopReason` wird auf `"awaiting_user_choice"` gesetzt (Guard-Clause verhindert Überschreibung durch `message_delta`)
|
||
4. `tool_use`-Block wird in DB gespeichert, Chain wird finalisiert
|
||
5. User wählt Option → Frontend sendet `POST /api/chat` mit `choiceToolCallId` + `choiceResult`
|
||
6. Backend findet die Chain mit dem offenen `tool_use`, speichert `tool_result` dort
|
||
7. Neuer `StreamChat` startet – History enthält `tool_use` + `tool_result`, Claude arbeitet weiter
|
||
|
||
### Frontend-Flow
|
||
|
||
- **Live**: SSE-Event `user_choice` → `addChoiceBubble()` rendert Frage + klickbare Buttons
|
||
- **History**: `tool_use` mit `toolName === 'ask_user_choice'` → Voraus-Suche nach `tool_result`:
|
||
- Gefunden: `addChoiceBubbleStatic()` mit ausgewählter Option (hervorgehoben, andere ausgegraut)
|
||
- Nicht gefunden: Aktive Buttons (klickbar, z.B. nach Page-Refresh)
|
||
- **Robustheit**: Guards gegen `choices` als String statt Array (`Array.isArray()` Check)
|
||
- **CSS**: `.choice-bubble`, `.choice-btn` (Primary-Color-Outline), `.choice-selected`/`.choice-unselected`
|
||
|
||
### SSE-Events
|
||
|
||
| Event | Daten | Beschreibung |
|
||
|-------|-------|-------------|
|
||
| `user_choice` | `{ toolCallId, question, choices[] }` | Rückfrage mit Auswahloptionen |
|
||
|
||
---
|
||
|
||
## Plan-Modus
|
||
|
||
Der Plan-Modus ist ein **Read-only-Betriebsmodus**, in dem der Agent den Code analysieren, Rückfragen stellen und Pläne erstellen kann – aber keine Dateien ändern oder Befehle ausführen darf.
|
||
|
||
### Aktivierung
|
||
|
||
- **Frontend**: Toggle-Button (🗺️ `fa-map`) im Chat-Input-Bereich
|
||
- **API**: `planMode: true` im `ChatRequest`
|
||
- **Pro Chain**: `plan_mode` Feld in der Chain (rein informativ für History-Anzeige)
|
||
- Kein Agent-Setting – wird **pro Nachricht** vom User gesteuert
|
||
|
||
### Backend-Auswirkungen
|
||
|
||
| Aspekt | Normal | Plan-Modus |
|
||
|--------|--------|-----------|
|
||
| **Tools** | Alle (read_file, write_file, edit_file, delete_file, execute, ...) | Nur read-only: `read_file`, `list_files`, `glob`, `grep`, `execute`, `todo_write`, `memory_*`, `ask_user_choice` |
|
||
| **System-Prompt** | Normal | + Plan-Modus-Block: "Du darfst NUR lesen und analysieren, KEINE Änderungen vornehmen" |
|
||
| **toolChoice** | `"any"` (automatisch wenn Workspace/Skills/Memory) | Immer `"auto"` (Claude darf frei antworten) |
|
||
| **Chain-Speicherung** | `plan_mode = 0` | `plan_mode = 1` |
|
||
|
||
### System-Prompt-Anpassungen im Plan-Modus
|
||
|
||
Folgende Blöcke werden **übersprungen**: Git-Commits. Der Tools-Block listet nur Read-only-Tools auf. Skills werden als "nur zur Kenntnis, nicht ausführbar" markiert.
|
||
|
||
### Frontend
|
||
|
||
- **Toggle**: `#planModeBtn` – wechselt `_planModeActive`, ändert Button-Styling + Placeholder-Text
|
||
- **Chain-Meta**: Badge `📋 Plan-Modus` bei Chains die im Plan-Modus erstellt wurden
|
||
- **System-Prompt-Modal**: Zeigt den angepassten System-Prompt wenn Plan-Modus aktiv
|
||
- **Session-Wechsel**: Plan-Modus wird zurückgesetzt
|
||
|
||
### Choice-Response im Plan-Modus
|
||
|
||
Wenn im Plan-Modus eine `ask_user_choice`-Rückfrage kommt und der User antwortet, erbt die Choice-Response den `planMode` des vorherigen Chains.
|
||
|
||
---
|
||
|
||
## Web-Search
|
||
|
||
Anthropic Claude unterstützt Web-Suche als Server-Tool. Das System integriert Web-Search mit Source-Tracking und Rendering.
|
||
|
||
### Backend
|
||
|
||
- **Streaming**: `server_tool_use` (Suchanfrage) → `web_search_tool_result` (Ergebnisse mit Sources)
|
||
- **Sources extrahieren**: Aus `web_search_tool_result`-Block werden `url` + `title` als JSON-Array extrahiert
|
||
- **Persistierung**: `web_search_result`-Messages in DB (type=`web_search_result`, content = JSON-Array der Sources). Leere Ergebnisse (Sources=[]) werden **nicht** gespeichert.
|
||
- **SSE-Events**: `web_search_start` (Suche beginnt) → `web_search_end` (Suche fertig, mit Sources)
|
||
|
||
### Frontend – Streaming
|
||
|
||
- `web_search_start` → Tool-Box mit Spinner erstellen, Stream-Bubble **pausieren** (nicht finalisieren)
|
||
- `web_search_end` → Tool-Box mit Sources finalisieren
|
||
- `text_start` → in bestehende Bubble weiter schreiben (kein neuer Block)
|
||
- `pauseStreamBubble()` / `endStreamBubble()` Konzept: Text-Blöcke zwischen Web-Searches werden in **einer** Bubble zusammengefasst
|
||
- `text_end` pausiert nur (Cursor weg), `endStreamBubble()` nur bei finalen Events (`done`, `tool_start`, `error`)
|
||
|
||
### Frontend – History
|
||
|
||
- `web_search_result`-Messages werden als Tool-Box mit klickbaren Source-Links gerendert (Domain + Title)
|
||
- `_mergeConsecutiveAssistantTexts()` fügt Text-Messages zusammen, `web_search_result`-Messages dazwischen unterbrechen die Merge-Kette nicht
|
||
- Leere `web_search_result`-Messages (Sources=[]) werden übersprungen
|
||
|
||
### Frontend – Shared-Page
|
||
|
||
- `SharedEndpoint` gibt `web_search_result`-Messages mit aus (Typ-Filter: `text` + `web_search_result`)
|
||
- `shared-page.js` rendert `web_search_result` als Tool-Box mit Source-Links
|
||
- Merge-Funktion überspringt `web_search_result` beim Rückwärts-Suchen, damit Texte zusammengeführt werden
|
||
|
||
---
|
||
|
||
## Nachrichten teilen (Shared Messages)
|
||
|
||
Einzelne Nachrichten oder ganze Threads können als **öffentliche Links** geteilt werden – ohne Login aufrufbar.
|
||
|
||
### Datenmodell
|
||
|
||
**Tabelle `shared_messages`:**
|
||
| Spalte | Typ | Beschreibung |
|
||
|------------------|---------|-------------------------------------------|
|
||
| `id` | INTEGER | Primary Key |
|
||
| `share_token` | TEXT | UUID – öffentlicher Zugangsschlüssel |
|
||
| `message_id` | INTEGER | FK auf `messages.id` |
|
||
| `user_id` | INTEGER | Wer hat geteilt |
|
||
| `include_context`| INTEGER | 0 = Einzelnachricht, 1 = Thread bis dahin |
|
||
| `created_at` | TEXT | Erstellzeitpunkt |
|
||
| `expires_at` | TEXT | Optional: Ablaufdatum |
|
||
|
||
### API-Endpunkte (`SharedEndpoint.cs`)
|
||
|
||
| Methode | Route | Auth | Beschreibung |
|
||
|----------|--------------------------------|------|---------------------------------------|
|
||
| `POST` | `/api/messages/{id}/share` | ✅ | Share erstellen (Body: `{includeContext}`) |
|
||
| `GET` | `/api/shared/{token}` | ❌ | Öffentlich: Geteilte Nachricht abrufen |
|
||
| `GET` | `/api/shared` | ✅ | Eigene Shares auflisten |
|
||
| `DELETE` | `/api/shared/{token}` | ✅ | Share-Link widerrufen |
|
||
|
||
### Frontend-Flow
|
||
|
||
1. **Share-Button** (🔗) erscheint an jeder Nachricht im Chat (neben Reply, Delete, Mark)
|
||
2. Klick öffnet **Kontext-Menü**: "Einzelne Nachricht" oder "Thread teilen"
|
||
3. `POST /api/messages/{id}/share` → Token zurück → URL in Zwischenablage kopiert
|
||
4. **Toast**: "Link kopiert!"
|
||
|
||
### Öffentliche Ansicht (`#/shared/{token}`)
|
||
|
||
- Route `#/shared/{token}` → `shared-page.js`
|
||
- **Kein Auth nötig** – Auth-Bypass in `app.js` + `AuthMiddleware`
|
||
- Minimales Layout: Header ("// gott · Geteilte Nachricht"), Nachrichten mit Markdown-Rendering
|
||
- Zwei Modi: `single` (eine Nachricht) oder `context` (Thread mit User/Assistant-Wechsel)
|
||
|
||
---
|
||
|
||
## Workspace & File-Explorer
|
||
|
||
### Workspace
|
||
|
||
Eine Session kann einen **Workspace-Pfad** haben. Dieser wird **relativ** zum `DefaultPath` des Users gespeichert (z.B. `myapp`) und via `WorkspaceHelper.ResolveWorkspace()` zum absoluten Pfad aufgelöst. Der Workspace:
|
||
- Aktiviert die Datei-/Shell-Tools für den Agent
|
||
- Ermöglicht den File-Explorer im Frontend
|
||
- Bietet `git status` Informationen
|
||
- Erlaubt das Ausführen von Shell-Befehlen (foreground + background)
|
||
|
||
### WorkspaceHelper
|
||
|
||
`Services/WorkspaceHelper.cs` – Statische Hilfsklasse für Workspace-Pfade:
|
||
|
||
| Methode | Beschreibung |
|
||
|----------------------------|-----------------------------------------------------------|
|
||
| `ResolveWorkspace(session, user)` | Relativer Workspace + DefaultPath → absoluter Pfad |
|
||
| `ValidateRelativeWorkspace(workspace)` | Prüft auf `..`, `~`, absolute Pfade → null bei ungültig |
|
||
| `ToRelativeWorkspace(absolute, defaultPath)` | Absoluter Pfad → relativ zum DefaultPath |
|
||
|
||
### File-Explorer API
|
||
|
||
| Endpoint | Beschreibung |
|
||
|-------------------------------------|------------------------------------------|
|
||
| `GET /api/workspace/tree` | Verzeichnisbaum (mit Ignore-Filtern) |
|
||
| `GET /api/workspace/file` | Dateiinhalt lesen (max 2MB) |
|
||
| `PUT /api/workspace/file` | Datei speichern |
|
||
| `POST /api/workspace/mkdir` | Ordner erstellen im Workspace |
|
||
| `DELETE /api/workspace/dir` | Leeren Ordner löschen im Workspace |
|
||
| `GET /api/workspace/browse` | Verzeichnis-Browser (für Pfad-Auswahl) |
|
||
| `POST /api/workspace/browse/mkdir` | Ordner erstellen per absolutem Pfad |
|
||
| `GET /api/workspace/raw` | Raw-Datei ausliefern (Bilder, PDFs) |
|
||
| `GET /api/workspace/agent/tree` | Agent-Memory-Verzeichnis |
|
||
| `GET/PUT /api/workspace/agent/file` | Agent-Memory-Dateien lesen/schreiben |
|
||
| `POST /api/workspace/agent/mkdir` | Ordner erstellen im Agent-Memory |
|
||
|
||
### Ignored Directories
|
||
|
||
`bin`, `obj`, `node_modules`, `.git`, `.vs`, `.idea`, `publish`, `dist`, `out`, `build`, `target`, `.cache`
|
||
|
||
### Session-Exec
|
||
|
||
`POST /api/sessions/{id}/exec` führt Shell-Befehle im Workspace aus:
|
||
- **foreground**: Synchron, wartet auf Ergebnis (30s Timeout)
|
||
- **background**: Fire-and-forget via `systemd-run`
|
||
|
||
---
|
||
|
||
## Avatar-System
|
||
|
||
Sessions und Agents können ein **Bild/Avatar** haben mit konfigurierbarem Zoom.
|
||
|
||
### Datenfelder
|
||
|
||
| Feld | Tabellen | Beschreibung |
|
||
|------|----------|-------------|
|
||
| `image` / `avatar` | `sessions` / `agents` | Base64-kodiertes Bild (WebP, max 512px via ImageService) |
|
||
| `image_small` / `avatar_small` | `sessions` / `agents` | Thumbnail (WebP, 256px, Q70) für Listen/Dashboard |
|
||
| `avatar_scale` | `sessions`, `agents` | Zoom-Faktor (0.5× – 3×, Default 1.0) |
|
||
| `avatar_offset_x` | `sessions`, `agents` | Horizontale Position (-50% – +50%, Default 0), via CSS `translate()` |
|
||
| `avatar_offset_y` | `sessions`, `agents` | Vertikale Position (-50% – +50%, Default 0), via CSS `translate()` |
|
||
|
||
Darstellung via CSS `transform: scale(S) translate(X%, Y%)` im `.card-image` Container (`overflow: hidden`).
|
||
|
||
### Bild-Verarbeitung (ImageService)
|
||
|
||
Alle Bilder werden beim Upload in **WebP** konvertiert (statt PNG), was ~60-80% Speicherersparnis bringt:
|
||
|
||
| Variante | Methode | Max. Größe | Qualität | Verwendung |
|
||
|----------|---------|------------|----------|------------|
|
||
| Voll | `ResizeDataUrl()` | 512px | Q80 | Editor, Detail-Ansicht (`GET /api/agents/{id}`, `GET /api/sessions/{id}`) |
|
||
| Thumbnail | `ResizeDataUrlSmall()` | 256px | Q70 | Listen, Dashboard (`GET /api/agents`, `GET /api/sessions`) |
|
||
|
||
Die Listen-Endpoints liefern das Thumbnail als `avatar`/`image`-Feld (Fallback auf Voll-Bild wenn kein Thumbnail existiert). Die Detail-Endpoints liefern das volle Bild.
|
||
|
||
### Avatar-Editor (`avatar-editor.js`)
|
||
|
||
Wiederverwendbares Modal-Modul, genutzt in Session- und Agent-Pages:
|
||
|
||
```javascript
|
||
const editor = AvatarEditor.init({
|
||
previewEl: document.querySelector('.avatar-preview'),
|
||
currentImage: existingBase64OrNull,
|
||
currentScale: 1.0,
|
||
initialOffsetX: 0,
|
||
initialOffsetY: 0,
|
||
onChange: (imageData, scale, offsetX, offsetY) => { /* Live-Preview */ }
|
||
});
|
||
// Werte auslesen: editor.getImage(), editor.getScale(), editor.getOffsetX(), editor.getOffsetY()
|
||
// Aufräumen: editor.destroy()
|
||
```
|
||
|
||
**Features:**
|
||
- **Upload**: Datei-Input für Bild-Upload
|
||
- **Paste**: Ctrl+V → Bild aus Zwischenablage einfügen
|
||
- **AI-Generate**: Text-Prompt → Krea API → Bild generieren (nur wenn `KreaToken` konfiguriert)
|
||
- **Scale**: Range-Slider 0.5× – 3× mit Live-Preview
|
||
- **Offset X/Y**: Range-Slider -50% – +50% für horizontale/vertikale Positionierung
|
||
- **Clear**: Bild entfernen
|
||
|
||
### Dashboard-Darstellung
|
||
|
||
Cards zeigen Avatare mit `transform: scale(S) translate(X%, Y%)` im `.card-image` Container (`overflow: hidden`). Die Helper `buildAgentCard()` und `buildSessionCard()` in `cards.js` nutzen `buildTransformStyle(scale, offsetX, offsetY)` dafür.
|
||
|
||
---
|
||
|
||
## LaTeX/Math-Rendering
|
||
|
||
Chat-Nachrichten unterstützen **LaTeX-Formeln** via KaTeX:
|
||
|
||
### Syntax
|
||
|
||
- `$$...$$` → **Block-Math** (zentriert, eigene Zeile)
|
||
- `$...$` → **Inline-Math** (im Fließtext)
|
||
|
||
### Rendering-Pipeline (in `markdown.js` `renderMarkdown()`)
|
||
|
||
```
|
||
1. Code-Blöcke extrahieren und schützen (Platzhalter)
|
||
2. Block-LaTeX ($$...$$) extrahieren und schützen
|
||
3. Inline-LaTeX ($...$) extrahieren und schützen
|
||
4. Markdown-Rendering via marked.js
|
||
5. Code-Blöcke wiederherstellen
|
||
6. LaTeX-Platzhalter durch KaTeX.renderToString() ersetzen
|
||
7. Bei fehlerhaftem LaTeX: Fallback als <code class="katex-error">
|
||
```
|
||
|
||
### Abhängigkeiten
|
||
|
||
- `wwwroot/js/lib/katex.min.js` – KaTeX Engine (v0.16.21)
|
||
- `wwwroot/css/katex.min.css` – Styles
|
||
- `wwwroot/css/fonts/` – 21 woff2-Fonts (self-hosted)
|
||
|
||
Funktioniert überall wo `renderMarkdown()` aufgerufen wird: Chat, Streaming, History, Archiv, Shared Page.
|
||
|
||
---
|
||
|
||
## Windows Desktop App
|
||
|
||
Im `WindowsApp/`-Ordner liegt ein minimaler **WPF WebView2 Client**:
|
||
|
||
- .NET 10 Windows, `Microsoft.Web.WebView2`
|
||
- Lädt die Web-UI in einem nativen Fenster
|
||
- Server-URL konfigurierbar via `appsettings.json`
|
||
- Dark Background um weißen Flash zu vermeiden
|
||
- Externe Links öffnen im Standard-Browser
|
||
- Fenster: 930px breit, 95% Bildschirmhöhe, zentriert
|
||
|
||
---
|
||
|
||
## OpenClaw Import
|
||
|
||
Sessions können mit Chatverlauf aus einer **OpenClaw-Instanz** befüllt werden. Die Import-Quelle ist eine SQLite-Datenbank (`Build/<OS>/publish/data/openclaw-sessions.db`).
|
||
|
||
### Funktionsweise
|
||
|
||
- **`GET /api/openclaw/sessions`** – Listet alle Sessions aus der OpenClaw-DB mit Metadaten (Message-Count, Model, Summary, Preview-Messages)
|
||
- **`POST /api/openclaw/import`** – Importiert eine OpenClaw-Session in eine bestehende Gott-Session
|
||
|
||
### Import-Logik
|
||
|
||
1. Liest alle `message`-Entries aus der OpenClaw-DB, gruppiert nach `chainId`
|
||
2. **Nur Text-Nachrichten** werden importiert – Tool-Use/Tool-Result werden übersprungen
|
||
3. **User-Message-Bereinigung**: Webchat-Prefixe (`[Direct 1:1 webchat ...]`, `[Current message ...]`, `[cron:...]`), `Conversation info`-Blöcke werden entfernt
|
||
4. Pro Chain wird ein `ChainRecord` + `MessageRecords` erstellt, alle mit `tag = 'openclaw-import'`
|
||
5. Timestamps werden aus OpenClaw übernommen (`created_at`)
|
||
6. **Re-Import = Update**: Vorherige Import-Chains (erkennbar am Tag) werden gelöscht und neu importiert. Bestehende Compactions werden ebenfalls gelöscht.
|
||
|
||
### Sortierung (sort_key)
|
||
|
||
Importierte Messages werden **immer vor** normalen Messages einsortiert, da sie historisch älter sind:
|
||
|
||
```sql
|
||
sort_key = CASE WHEN tag = 'openclaw-import' THEN id - 10000000000 ELSE id END
|
||
```
|
||
|
||
Alle History-Queries (initial load, before/after-Paging, Polling) sortieren nach `sort_key`. OpenClaw-Messages bekommen negative sort_keys → erscheinen immer ganz oben. Innerhalb jeder Gruppe (OpenClaw / normal) wird nach `id` sortiert.
|
||
|
||
### Frontend
|
||
|
||
- **Dashboard**: Session-Menü → „OpenClaw Import" → Modal mit Session-Auswahl + Preview
|
||
- **Chat**: Importierte Nachrichten zeigen ein blaues „OC"-Badge
|
||
|
||
---
|
||
|
||
## Remote-Verbindungen
|
||
|
||
Remote-Clients (z.B. Windows-PCs) können sich via **WebSocket** mit dem Server verbinden. Über die Verbindung kann der AI-Agent Shell-Befehle auf dem Remote-Client ausführen.
|
||
|
||
### Architektur
|
||
|
||
```
|
||
┌──────────────┐ WebSocket ┌───────────────┐ Tool-Call ┌───────────────┐
|
||
│ Remote-Client│◄────────────►│ Gott Server │◄───────────►│ AI-Agent │
|
||
│ (PowerShell) │ (bidirekt.) │ RemoteEndpoint│ │ remote_execute│
|
||
└──────────────┘ └───────────────┘ └───────────────┘
|
||
```
|
||
|
||
### Verbindungsaufbau
|
||
|
||
1. Remote-Client führt `irm https://server/remote | iex` aus → lädt `remote.ps1`
|
||
2. Script baut WebSocket-Verbindung zu `/api/remote` auf
|
||
3. Sendet `hello`-Message mit Hostname, Username, OS-Version
|
||
4. `RemoteConnectionManager` registriert die Verbindung
|
||
|
||
### Session-Zuweisung
|
||
|
||
- Im Chat: Remote-Button → Modal zeigt aktive Verbindungen
|
||
- Admin weist Verbindung einer Session zu (`POST /api/remote/assign`)
|
||
- AI-Agent bekommt `remote_execute`-Tool → führt Befehle auf dem Remote-Client aus
|
||
- Ergebnis wird synchron via WebSocket zurückgeliefert (Timeout: konfigurierbarer `ExecuteTimeout`)
|
||
|
||
### Protokoll (JSON über WebSocket)
|
||
|
||
| Richtung | Type | Payload |
|
||
|----------|------|---------|
|
||
| Client → Server | `hello` | `hostname`, `username`, `os` |
|
||
| Server → Client | `execute` | `command` |
|
||
| Client → Server | `result` | `exitCode`, `output`, `error` |
|
||
| Client → Server | `ping` | – |
|
||
| Server → Client | `pong` | – |
|
||
|
||
---
|
||
|
||
## Semantische Suche (Embedding)
|
||
|
||
Lokale Sentence-Embeddings ermöglichen semantische Suche über Chat-Nachrichten und Memory-Dateien.
|
||
|
||
### Technologie
|
||
|
||
- **Modell:** all-MiniLM-L6-v2 (ONNX Runtime, 384-dimensionale Embeddings)
|
||
- **Tokenisierung:** WordPiece (BERT-kompatibel, eigene Implementierung)
|
||
- **Speicherung:** SQLite-Tabellen `message_embeddings` + `memory_file_embeddings` (Vektor als BLOB)
|
||
- **Suche:** Brute-Force Cosine Similarity (ausreichend für <100k Embeddings)
|
||
|
||
### Komponenten
|
||
|
||
| Service | Aufgabe |
|
||
|---------|---------|
|
||
| `EmbeddingService` | ONNX-Modell laden, Embeddings generieren (thread-safe via SemaphoreSlim) |
|
||
| `VectorSearchService` | Schema-Management, Embedding-Storage, Similarity-Search |
|
||
| `EmbeddingBackgroundService` | Backfill beim Start + Live-Queue für neue Messages/Memory-Files |
|
||
|
||
### Funktionsweise
|
||
|
||
1. **Backfill beim Start:** Alle bestehenden Text-Messages (user + assistant, ≥10 Zeichen) werden embedded
|
||
2. **Memory-Backfill:** Alle `.md`-Dateien in Agent-Verzeichnissen werden in 500-Zeichen-Chunks embedded
|
||
3. **Live-Embedding:** Neue Messages/Memory-Writes werden via Queue sofort embedded
|
||
4. **Suche (`memory_search` Tool):** Query wird embedded, Cosine Similarity gegen alle Embeddings berechnet, Top-N Ergebnisse zurückgegeben
|
||
|
||
### Konfiguration
|
||
|
||
Das ONNX-Modell muss unter `data/models/all-MiniLM-L6-v2/` liegen (`model.onnx` + `vocab.txt`). Wenn nicht vorhanden, wird der Service deaktiviert und das Frontend zeigt eine Warnung.
|
||
|
||
---
|
||
|
||
## API-Key-Verwaltung
|
||
|
||
API-Keys werden in der Datenbank verwaltet (Tabelle `api_keys`) statt in der Konfigurationsdatei.
|
||
|
||
### Funktionsweise
|
||
|
||
- **Mehrere Keys** pro Provider (z.B. `anthropic`) können gespeichert werden
|
||
- **Nur ein Key pro Provider** kann gleichzeitig aktiv sein
|
||
- **Aktivierung** deaktiviert automatisch alle anderen Keys desselben Providers
|
||
- **Live-Aktualisierung:** Bei Aktivierung/Deaktivierung wird der Token sofort im laufenden Service aktualisiert (kein Neustart nötig)
|
||
- **Migration:** Bestehender `AnthropicToken` aus `appsettings.Production.json` wird beim ersten Start automatisch migriert
|
||
|
||
### Heartbeat-Check (nur Anthropic)
|
||
|
||
`POST /api/admin/api-keys/{id}/check` sendet eine minimale Anfrage an die Anthropic API und liest die Rate-Limit-Headers aus:
|
||
- **Gültig/Ungültig** – HTTP 200 vs. 401
|
||
- **Subscription vs. API Console** – Anhand der `unified-*` Headers
|
||
- **Budget-Auslastung** – 5h und 7d Prozentwerte
|
||
- **Plan-Erkennung** – `Max` (overage > 0), `Pro` (kein fallback-Header), `Pro/Max` (sonst)
|
||
|
||
---
|
||
|
||
## Deployment
|
||
|
||
**WICHTIG:** Das Deployment ist ein **manueller Vorgang**, der durch den Benutzer auf dem Server durchgeführt werden muss. Das System führt kein automatisches Deployment durch.
|
||
|
||
### Goldene Regel für Änderungen
|
||
* **Arbeiten Sie AUSSCHLIESSLICH im Verzeichnis `wwwroot/`** für Frontend-Änderungen.
|
||
* **Editiere NIEMALS Dateien in `Build/*/publish/wwwroot/`!** Diese Verzeichnisse werden bei jedem Build überschrieben.
|
||
* Das Verzeichnis `Build/*/publish/data/gott.db` darf niemals manuell gelöscht, geleert oder direkt modifiziert werden.
|
||
|
||
### Deployment-Vorgang (Beispiel für Linux)
|
||
Das vorhandene Shell-Skript `deploy.sh` dient lediglich als Hilfestellung für den manuellen Prozess:
|
||
|
||
1. **Frontend-Anpassungen vornehmen** (in `wwwroot/`).
|
||
2. **Manuelles Deployment** auf dem Server ausführen:
|
||
```bash
|
||
# Beispiel-Ablauf:
|
||
systemctl stop gott
|
||
dotnet publish -c Release -o Build/Linux/publish
|
||
systemctl start gott
|
||
```
|
||
|
||
### systemd
|
||
Die Anwendung ist als systemd-Service konfiguriert und läuft standardmäßig auf Port 19000.
|
||
|
||
---
|
||
|
||
## Konfiguration
|
||
|
||
### appsettings.json
|
||
|
||
```json
|
||
{
|
||
"Port": 19000, // Server-Port (Default 19000, erfordert Neustart)
|
||
"MaxIterations": 100, // Max Tool-Loop Iterationen pro Chat
|
||
"MaxHistory": 120, // Max Messages in Chat-History
|
||
"ReminderInterval": 30, // System-Prompt-Reminder alle N Messages
|
||
"ExecuteTimeout": 180, // Timeout für Shell-Befehle in Sekunden
|
||
"PayloadLogging": false, // API-Payloads loggen (Debug)
|
||
"PayloadLoggingGzip": false, // Payload-Logs als .gz komprimieren
|
||
"KreaToken": "", // Krea API Token (optional)
|
||
"KreaApiKey": "", // API-Key für Krea-Endpoints ohne User-Auth (optional)
|
||
"GeminiApiKey": "" // Google Gemini API Key (kostenlos via aistudio.google.com/apikey)
|
||
}
|
||
```
|
||
|
||
> **Hinweis:** `AnthropicToken` wird nicht mehr über `appsettings.json` konfiguriert, sondern über die **API-Key-Verwaltung** im Admin-Bereich (DB-Tabelle `api_keys`). Bestehende Tokens werden beim ersten Start automatisch aus `appsettings.Production.json` migriert.
|
||
|
||
### appsettings.Production.json
|
||
|
||
Überschreibt Werte aus `appsettings.json` im Production-Environment. Wird **nicht** beim Publish mitkopiert (liegt nur im `Build/<OS>/publish/`-Verzeichnis). Verwaltung über die **Admin-UI** (Servereinstellungen, `#/server-settings`).
|
||
|
||
### Environment
|
||
|
||
- `ASPNETCORE_ENVIRONMENT=Production` → lädt `appsettings.Production.json`
|
||
- Tokens und Secrets werden über die Server-Settings-UI in `appsettings.Production.json` geschrieben
|
||
|
||
---
|
||
|
||
## Datenbank-Migrationen
|
||
|
||
Die `DatabaseService.InitializeDatabase()` handhabt Schema-Erstellung und Migrationen in einem einzigen Methoden-Aufruf:
|
||
|
||
1. **Tabellen-Erstellung** via `CREATE TABLE IF NOT EXISTS`
|
||
2. **Spalten-Migration** via `TryAddColumn()` – fängt "column already exists" ab
|
||
3. **Daten-Migration** (z.B. `auto_commit` von INTEGER zu TEXT, `use_web_search` zu `web_search_version`)
|
||
4. **Token-Migration** – Bestehender `AnthropicToken` aus `appsettings.Production.json` wird automatisch in `api_keys`-Tabelle migriert
|
||
5. **Bild-Migration** – Konvertierung bestehender PNG/JPEG-Avatare zu WebP + Generierung von Thumbnails (`avatar_small`, `image_small`)
|
||
|
||
Dieses Pattern ermöglicht:
|
||
- Idempotente Ausführung bei jedem App-Start
|
||
- Keine separate Migrations-Verwaltung nötig
|
||
- Neue Features können einfach Spalten hinzufügen
|
||
- Abwärtskompatibel – bestehende Daten bleiben erhalten
|