# AISE AI Code Editor — Technische Dokumentation KI-unterstützter Lightweight Code Editor auf Basis von Streamlit (AISE501 Spring 2026). --- ## Inhaltsverzeichnis 1. [Projektstruktur](#projektstruktur) 2. [Schnellstart](#schnellstart) 3. [Frontend](#frontend) 4. [Backend Manager](#backend-manager) 5. [Backend Agent (MCP-System)](#backend-agent-mcp-system) 6. [MCP-Server-Konfiguration](#mcp-server-konfiguration) 7. [Architektur-Übersicht](#architektur-übersicht) 8. [Wichtige Designentscheidungen](#wichtige-designentscheidungen) 9. [Tests](#tests) 10. [Umgebungsvariablen](#umgebungsvariablen) --- ## Projektstruktur ``` AISE_AIAgent/ ├── frontend/ # Streamlit UI-Komponenten │ ├── app.py # Einstiegspunkt der App; Seitenkonfiguration + Routing │ ├── state.py # Zentrale Session-State-Initialisierung │ ├── sidebar.py # Datei-Explorer + Navigations-Radio │ ├── editor.py # Ace-Editor-Tabs + Ausführungs-Output │ └── chat.py # Chat-Interface + Agent-Mode-UI │ ├── backend/ │ ├── managers/ # Business-Logik, direkt vom Frontend aufgerufen │ │ ├── file_manager.py # Workspace-CRUD mit Path-Traversal-Schutz │ │ ├── chat_manager.py # LLM-API-Wrapper + Sliding-Window-History │ │ ├── system_prompter.py # Kontextbewusste System-Prompt-Generierung │ │ ├── search_manager.py # DuckDuckGo-Websuche + Seitenabruf │ │ ├── execution_engine.py # Subprocess-basierte Code-Ausführung (Python, LaTeX) │ │ └── debug_logger.py # Rotierende Logdatei + Fehler-Aggregation │ │ │ └── agent/ # Autonomes KI-Agenten-System (MCP-basiert) │ ├── coding_agent.py # Plan→Aktion→Beobachten-Schleife │ ├── mcp_server_adapter.py # Verbindet den Agenten mit MCP-Tool-Servern │ ├── mcp_server_config.json # Welche MCP-Server gestartet werden (Pfade + Befehle) │ └── servers/ # MCP-Server-Implementierungen (stdio-Transport) │ ├── mcp_server_code_execution.py # Tool: Sandbox-Python-Ausführung + Linting │ ├── mcp_server_file_search.py # Tool: Workspace-Datei lesen/schreiben/suchen │ └── mcp_server_web_search.py # Tool: DuckDuckGo-Suche + Seitenabruf │ ├── tests/ # pytest-Unit-Tests │ ├── conftest.py # Globaler MCP-Mock (keine echten Subprozesse in Tests) │ ├── test_chat_manager.py │ ├── test_coding_agent.py │ ├── test_debug_logger.py │ ├── test_execution_engine.py │ ├── test_file_manager.py │ ├── test_mcp_server_code_execution.py │ ├── test_mcp_server_file_search.py │ ├── test_mcp_server_web_search.py │ ├── test_search_manager.py │ └── test_system_prompter.py │ ├── workspace/ # Sandbox-Verzeichnis für Agent- und Editor-Dateien ├── logs/ # Rotierende Logdateien (app.log, errors.log) ├── .env # Lokale Umgebungsvariablen (nicht eingecheckt) └── .env.example # Vorlage für erforderliche Umgebungsvariablen ``` --- ## Schnellstart ### 1. Repository klonen ```bash git clone https://gitea.fhgr.ch/meulilivio/AISE1_Project.git cd AISE1_Project ``` ### 2. Virtuelle Umgebung aktivieren ```bash # Windows .\.venv\Scripts\Activate.ps1 # macOS / Linux source .venv/bin/activate ``` ### 3. Abhängigkeiten installieren ```bash pip install -r requirements.txt ``` ### 4. Umgebungsvariablen konfigurieren ```bash cp .env.example .env # .env öffnen und HOST, PORT, API_KEY, MODEL eintragen ``` ### 5. App starten ```bash streamlit run frontend/app.py ``` ### 6. Tests ausführen ```bash pytest tests/ -v ``` **Optionale Abhängigkeit:** Die LaTeX-Unterstützung (`.tex`-Ausführung) erfordert `pdflatex` im System-`PATH`. Ohne `pdflatex` funktioniert der Editor weiterhin; beim Ausführen einer `.tex`-Datei erscheint ein `FileNotFoundError` im stderr-Panel. --- ## Frontend Alle Frontend-Module sind reine Streamlit-Komponenten. Sie enthalten keine Business-Logik, sondern delegieren alles an die Backend-Manager. ### `app.py` Einstiegspunkt der Applikation. Ruft `init_state()` auf Modul-Ebene auf (vor `main()`), damit alle Session-State-Schlüssel existieren, bevor ein Widget gerendert wird. Delegiert an `render_sidebar()`, `render_editor()` und `render_chat()` basierend auf dem Navigations-Radio. ### `state.py` Zentrale Quelle aller `st.session_state`-Schlüsselnamen und ihrer Standardwerte. Alle Schlüssel nutzen `if key not in st.session_state`-Guards, damit bei Streamlit-Reruns keine bestehenden Werte überschrieben werden. Aktuell verwaltete Schlüssel: | Schlüssel | Standard | Zweck | |-----------|----------|-------| | `last_selected` | `None` | Zuletzt angeklickter Baum-Knoten (verhindert erneutes Ausführen bei jedem Rerender) | | `selected_folder` / `selected_folder_rel` | `None` | Aktuell markierter Ordner | | `chat_manager` | `ChatManager()` | Live-ChatManager-Instanz | | `open_files` | `[]` | Geordnete Liste absoluter Pfade als Editor-Tabs | | `files_content` | `{}` | Pfad → aktueller Editor-Inhalt (kann von Disk abweichen) | | `active_file` | `None` | Absoluter Pfad des aktiven Editor-Tabs | | `exec_results` | `{}` | Pfad → letztes Ausführungsergebnis-Dict | | `chat_history` | `[]` | Flache Liste von `{role, content}`-Dicts zur Anzeige | | `agent_mode` | `False` | Ob die Agent-Mode-UI aktiv ist | | `coding_agent` | `None` | Live-`CodingAgent`-Instanz während einer Aufgabe | | `agent_status` | `"idle"` | `"idle"` / `"waiting_approval"` / `"done"` | | `agent_log` | `[]` | Liste abgeschlossener Schritt-Einträge | | `agent_pending_action` | `None` | Vorgeschlagene Aktion, die auf Benutzer-Genehmigung wartet | | `search_results` | `[]` | Aktive Websuchergebnisse für Kontext-Injektion | ### `sidebar.py` Rendert das Navigations-Radio und den Workspace-Datei-Explorer (basierend auf `streamlit-arborist` für einen interaktiven Baum). Datei-Klicks öffnen einen neuen Editor-Tab; Ordner-Klicks zeigen eine Aktionsleiste mit «Datei hinzufügen» / «Ordner hinzufügen» / «Löschen». Ein Popover am unteren Rand ermöglicht das Erstellen und Hochladen von Dateien (bis 1 MB) im Workspace-Wurzelverzeichnis. ### `editor.py` Verwendet `streamlit-ace` für syntaxhervorgehobene Bearbeitung. Jede geöffnete Datei erhält einen eigenen Tab via `st.tabs()`. Die aktive Datei steuert die Schaltflächen «Code ausführen», «Herunterladen», «Schliessen», «Umbenennen» und «Löschen». Der Button **Code ausführen** führt für Python-Dateien zuerst `ast.parse()` durch, um Syntaxfehler vor dem Subprocess zu erkennen. Der Button **Mit KI debuggen** (nach einem fehlgeschlagenen Lauf eingeblendet) formatiert die Fehlerausgabe und navigiert zur Chat-Ansicht mit einer vorausgefüllten Debug-Nachricht. ### `chat.py` Zwei sich gegenseitig ausschliessende Ansichten, umgeschaltet via `st.toggle("Agent Mode")`: **Normaler Chat** (`render_normal_chat()`): - Kompakte 4-spaltige Toolbar direkt über dem Chat-Input: `[● Agent Mode]` `[🔍 Search]` `[🗑️ Clear]` `[⚙️ Settings]` — Web-Suche und Einstellungen jeweils als `st.popover`, Clear öffnet einen Bestätigungs-Dialog. - System-Prompt wird vor jeder ausgehenden Nachricht neu generiert (`_set_system_prompt()`). - Unterstützt Slash-Befehle `/search ` und `/search clear`. - Settings-Popover: Dateikontext-Toggle, Modell-Auswahl, Max-Token-Slider, benutzerdefinierter System-Prompt. - «Mit KI debuggen»-Nachrichten vom Editor werden über `pending_debug_message` im Session-State weitergeleitet. **Agent Mode** (`render_agent_mode()`): - `idle` → Aufgabeneingabe + Start-Schaltfläche. - `waiting_approval` → zeigt vorgeschlagenen Gedanken + Tool + Argumente; Benutzer kann Genehmigen, Ablehnen (mit Feedback) oder Abbrechen. - `done` → Erfolgsmeldung + Folgefrage-Eingabe zum Weiterführen der Aufgabe. --- ## Backend Manager ### `file_manager.py` Alle öffentlichen Methoden lösen Pfade auf und prüfen, ob sie innerhalb von `workspace/` bleiben, bevor sie das Dateisystem berühren (**Path-Traversal-Schutz**). Je nach Operation werden relative oder absolute Pfade akzeptiert und zurückgegeben: | Methode | Pfad-Typ | Hinweise | |---------|----------|----------| | `create_folder(relative_path, name)` | Workspace-relativ | Erstellt eine Ebene | | `create_file(relative_path, name)` | Workspace-relativ | Standard: `.txt` | | `read_file(absolute_path)` | Absolutes `Path`-Objekt | Vom Editor verwendet | | `save_file(absolute_path, content)` | Absoluter String | Überschreibt vorhandenes | | `rename_file(relative_path, new_name)` | Workspace-relativ | Erweiterung bleibt immer erhalten | | `delete_file(relative_path)` | Workspace-relativ | | | `delete_folder(relative_path)` | Workspace-relativ | Rekursiv via `shutil.rmtree` | | `get_file_tree()` | — | Gibt verschachteltes Dict zurück; Verzeichnisse → Dict, Dateien → None | Dateien werden in `get_file_tree()` standardmässig auf `CODE_EXTENSIONS` gefiltert. ### `chat_manager.py` Kapselt einen OpenAI-kompatiblen REST-Endpunkt, konfiguriert via `.env`. **Sliding-Window-History:** `_build_payload_messages()` sendet immer zuerst die System-Nachricht (damit sie nie verworfen wird), gefolgt von den letzten `max_history_messages` (20) Nicht-System-Nachrichten. Das begrenzt die Payload-Grösse, ohne den System-Prompt zu verlieren. **Fehlerbehandlung:** Verbindungs-Timeouts und HTTP-Fehler werden abgefangen, geloggt und als Assistenten-Nachrichten in der History gespeichert (sodass die UI den Fehler inline anzeigt). **API-Key:** Falls `API_KEY` den Wert `"EMPTY"` hat oder fehlt, wird kein `Authorization`-Header gesendet (unterstützt lokale/anonyme Endpunkte). ### `system_prompter.py` Generiert kontextbewusste System-Prompts. Signatur: ```python SystemPrompter.generate_prompt( user_message="", file_context=None, # {"name": str, "content": str} search_context=None, # list[{"title", "url", "snippet"}] — in system_prompter.py implementiert, # aber in chat.py nicht verwendet: dort wird Search-Kontext direkt # als -Block vor die Nachricht eingefügt task_type="default", # "debug" | "explain" | "optimize" | "default" ) ``` Der Aufgabentyp wird anhand von Schlüsselwörtern in der Benutzernachricht durch `_detect_task_type()` in `chat.py` ermittelt. Der Dateiinhalt wird wörtlich in einen XML-ähnlichen `...`-Block eingebettet und bei `MAX_FILE_CHARS` Zeichen abgeschnitten. `_extract_relevant_context()` nutzt `ast.parse()`, um nur die spezifische Funktion oder Klasse zurückzugeben, nach der der Benutzer fragt, anstatt die gesamte Datei. ### `execution_engine.py` Führt Dateien in einem Subprocess mit `capture_output=True`, `text=True` und einem `RUN_TIMEOUT` von 30 Sekunden aus. Aktuell unterstützt: - `.py` — via `sys.executable` (plattformübergreifend; zeigt auf den aktuell aktiven Python-Interpreter) - `.tex` — via `pdflatex -interaction=nonstopmode` (erfordert pdflatex im PATH) Rückgabe: `{"stdout": str, "stderr": str, "rc": int}`. ### `search_manager.py` DuckDuckGo-basierte Websuche und Seitenabruf für die Chat-Ansicht. SSRF-geschützt: `_validate_url()` blockiert Nicht-HTTP(S)-Schemata, Loopback- und RFC-1918-private IP-Bereiche. `fetch_page()` extrahiert lesbaren Text via BeautifulSoup, entfernt `