From 1c84d7fc2aa640fe84a7eeeadcb2abbce55c9d62 Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Sat, 13 Jun 2026 10:45:53 +0200 Subject: [PATCH] docs: document interactive ci-ai-bot + current Jetson backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: new Feature F (interactive @ci-ai webhook bot) — table, tree, dedicated section (events, build, deploy pointer, env); reflect the Jetson (Ollama, coder, .103) as the live backend instead of "Claude now / Jetson later" - flag that llm.py code defaults (anthropic / .20 / qwen-light) are stale vs. the configured ollama/coder/.103 — defaults refresh pending - new bot/README.md Co-Authored-By: Claude Opus 4.8 --- README.md | 126 +++++++++++++++++++++++++++++++++++--------------- bot/README.md | 29 ++++++++++++ 2 files changed, 117 insertions(+), 38 deletions(-) create mode 100644 bot/README.md diff --git a/README.md b/README.md index 412cfb6..756ac8d 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,46 @@ -# ci-ai — KI-Schicht für die Gitea-CI +# ci-ai — KI-Schicht für Gitea -Self-hosted Äquivalent zu **GitLab Duo** für Gitea + act_runner: automatische -PR-Reviews, PR-Summaries, KI-erklärte Security-Funde und CI-Failure-Analyse. -Läuft über den vorhandenen `gitea-runner` auf dem Pi. +Self-hosted Äquivalent zu **GitLab Duo** für Gitea: automatische PR-Reviews, +PR-/Issue-Summaries, KI-erklärte Security-Funde, CI-Failure-Analyse — **und** ein +interaktiver `@ci-ai`-Bot. KI-Backend ist der self-hosted **Jetson** (Ollama, GPU); +Anthropic bleibt als Alternative per Env-Var. Zwei Wege, dieselben Skripte: + +- **CI-Workflows** (über den `gitea-runner`): laufen bei `push` / `pull_request` / Slash-Command. +- **Interaktiver Bot** (`bot/`, Webhook-Dienst): `@ci-ai`-Chat + Auto-PR-Review in + **Sekunden** — ohne Runner, ohne Clone, ohne NAT-Hairpin. ## Bausteine | Feature | Skript | Trigger | |---|---|---| -| A — PR-Review + Summary | `scripts/ai_review.py` | `pull_request` | +| A — PR-Review + Summary | `scripts/ai_review.py` | `pull_request` (Runner) | | B — Security-Scan + KI-Erklärung (hart blockierend) | `security.yml` + `scripts/ai_explain.py` | `pull_request`, `push` | | C — CI-Failure-Analyse | `if:failure()`-Job + `scripts/ai_explain.py` | Job-Fehler | | D — Test-Generierung (Vorschlag) | `scripts/ai_tests.py` | Kommentar `/gen-tests` (PR) | | E — Issue-/PR-Zusammenfassung | `scripts/ai_summary.py` | Kommentar `/summarize` | +| **F — Interaktiver Bot** (`@ci-ai`-Chat + Auto-Review) | `bot/app.py` | Gitea-Webhook (`issue_comment`, `pull_request`) | -**Slash-Commands** (D + E): in einem Issue/PR `/gen-tests` bzw. `/summarize` kommentieren -→ der `commands`-Job (siehe `caller.example.yml`) postet das Ergebnis als Kommentar. +**Slash-Commands** (D + E): `/gen-tests` bzw. `/summarize` in einem Issue/PR kommentieren → +der `commands`-Job (siehe `caller.example.yml`) postet das Ergebnis. +**`@ci-ai`** (F): `@ci-ai ` in einem Issue/PR → Antwort im Thread (mit Diff-Kontext). -`scripts/llm.py` ist die **provider-agnostische** LLM-Schicht — heute Claude -(Anthropic), später der lokale Jetson (Ollama), umschaltbar per Env-Var. +`scripts/llm.py` ist die **provider-agnostische** LLM-Schicht — Jetson (Ollama) im Betrieb, +Anthropic als Alternative, umschaltbar per Env-Var. Reine Standardbibliothek, kein `pip install`. ## Aufbau ``` scripts/ - llm.py Provider-Abstraktion (anthropic | ollama), stdlib-only - gitea_api.py PR-Diff holen + idempotente Marker-Kommentare + llm.py Provider-Abstraktion (ollama | anthropic), stdlib-only + gitea_api.py PR-Diff/Thread holen, idempotente Marker-Kommentare, post_comment context.py Gitea-Actions-Event-Kontext (GITHUB_*-Vars) ai_review.py Feature A ai_explain.py Features B & C ai_tests.py Feature D (/gen-tests) ai_summary.py Feature E (/summarize) +bot/ + app.py Feature F — interaktiver Webhook-Bot (FastAPI) + Dockerfile Container fürs Deployment (vom Repo-ROOT bauen) .gitea/workflows/ reusable-security.yml on: workflow_call — zentraler Security-Scan reusable-ai-review.yml on: workflow_call — zentraler PR-Review @@ -40,60 +50,100 @@ workflows/ *.example.yml standalone-Workflows (Alternative: kopieren) ``` -Keine Abhängigkeiten — reine Python-Standardbibliothek, kein `pip install`. +## Interaktiver Bot (`bot/`) -## In ein Repo einbauen +Schlanker **FastAPI-Webhook-Dienst**, läuft als Docker-Container neben Gitea (auf dem Pi, +`web_network`). Erreicht die Gitea-API **intern** über `http://gitea:3000` (kein TLS, kein +Hairpin) und das KI-Backend (Jetson-Ollama) LAN-direkt → Antwort in Sekunden, ganz ohne +Runner/Clone. Teilt sich die Logik mit der CI-Variante (`scripts/{llm,gitea_api,ai_review}.py`). + +**Events** +- `issue_comment` mit `@ci-ai ` → Chat-Antwort im Thread (Thread- + Diff-Kontext, neuer Kommentar). +- `pull_request` (opened / synchronized / reopened) → idempotentes KI-Review (gleicher Prompt wie Feature A). + +**Sicherheit:** HMAC-Signaturcheck (`WEBHOOK_SECRET`); `BackgroundTasks` → sofort `200`, +LLM-Arbeit im Hintergrund (kein Gitea-Webhook-Timeout). + +**Bauen** (vom Repo-ROOT — der Dockerfile kopiert `scripts/` + `bot/app.py`): +```bash +docker build -f bot/Dockerfile -t ci-ai-bot:latest . +``` + +**Deployment & Gitea-Webhook:** Der Bot wird als Service im `docker_stack` des Schwester-Repos +**ansible-eulernest** deployt — Details, Vars und Troubleshooting dort in +[`docs/ci-ai-bot.md`](https://git.eulernest.eu/user2595/ansible-eulernest/src/branch/master/docs/ci-ai-bot.md). +Webhook (Org- oder Repo-Ebene): URL `http://ci-ai-bot:8080/webhook`, Events `issue_comment` + +`pull_request`, Secret = `WEBHOOK_SECRET`. ⚠️ Giteas SSRF-Schutz blockt Zustellung an private +IPs — `[webhook] ALLOWED_HOST_LIST` muss den Bot-Host (`ci-ai-bot`) erlauben. + +**Env-Vars (Bot)** +| Var | Beispiel | Zweck | +|---|---|---| +| `BOT_NAME` | `ci-ai` | Mention-Trigger (`@ci-ai`) | +| `WEBHOOK_SECRET` | — | HMAC-Secret, muss zum Gitea-Webhook passen (leer = Check aus) | +| `GITEA_TOKEN` | — | Token fürs Kommentieren (`write:issue`) | +| `GITEA_API` | `http://gitea:3000/api/v1` | Gitea-API (intern, kein Hairpin) | +| `AI_PROVIDER` / `AI_MODEL` / `AI_BASE_URL` | `ollama` / `coder` / `http://192.168.178.103:11434/v1` | KI-Backend (Jetson) | + +## In ein Repo einbauen (CI-Workflows) ### Empfohlen: globaler Aufruf (reusable workflow) -Nur **eine** Datei ins Ziel-Repo: `workflows/caller.example.yml` → -`.gitea/workflows/ci-ai.yml`. Sie ruft die zentrale Logik aus diesem Repo auf -(`uses: user2595/ci-ai/.gitea/workflows/reusable-*.yml@main` + `secrets: inherit`). -Logik-Updates passieren **einmal hier**, alle Repos ziehen automatisch nach. +Nur **eine** Datei ins Ziel-Repo: `workflows/caller.example.yml` → `.gitea/workflows/ci-ai.yml`. +Sie ruft die zentrale Logik auf (`uses: user2595/ci-ai/.gitea/workflows/reusable-*.yml@main` ++ `secrets: inherit`). Logik-Updates passieren **einmal hier**, alle Repos ziehen automatisch nach. -Einmalig zentral einzurichten: +Einmalig zentral: 1. Runner **instanzweit** registrieren (Admin → Actions → Runners), Label `eulernest` — sonst haben Repos außerhalb der org keinen Runner. 2. **Anonymen Clone für public Repos** erlauben (`REQUIRE_SIGNIN_VIEW=false`) — der Job klont `ci-ai` für die Skripte. -3. `ANTHROPIC_API_KEY` als **Org-/Instanz-Actions-Secret** (KI-Schritte; ohne ihn laufen - die Scans normal, die KI-Teile überspringen sich sauber). +3. KI-Backend = Jetson-Ollama (LAN, self-hosted) → **kein Secret nötig**. (Anthropic-Variante: + `ANTHROPIC_API_KEY` als Org-/Instanz-Actions-Secret; ohne Key überspringen sich die KI-Teile sauber.) 4. Für „hart blockieren": `security`-Check in der Branch-Protection als *required*. ### Alternative: standalone (kopieren) `workflows/ai-review.example.yml` + `workflows/security.example.yml` direkt nach -`.gitea/workflows/` kopieren. Enthält die volle Logik (klont die Skripte selbst) — -mehr Copy-Paste, aber unabhängig von reusable-workflow-Support. +`.gitea/workflows/` kopieren — volle Logik (klont die Skripte selbst), mehr Copy-Paste, +aber unabhängig von reusable-workflow-Support. -## Umstieg auf den Jetson (später) +## KI-Backend: Jetson (self-hosted, im Betrieb) -Sobald das lokale Ollama auf dem Jetson läuft — nur die Workflow-Env ändern, -**kein Code-Change**: +Das lokale Ollama auf dem Jetson Orin ist das aktive Backend — kein Anthropic-Key, keine +Kosten, kein Datenabfluss. In Workflows **und** Bot per Env gesetzt: ```yaml AI_PROVIDER: ollama -AI_MODEL: qwen-light -AI_BASE_URL: http://192.168.178.20:11434/v1 -JETSON_API_KEY: ${{ secrets.JETSON_API_KEY }} +AI_MODEL: coder # qwen2.5-coder:7b +AI_BASE_URL: http://192.168.178.103:11434/v1 ``` +Provider-Wechsel = **nur Env**, kein Code-Change (`scripts/llm.py`). Anthropic-Variante: +`AI_PROVIDER=anthropic` + `ANTHROPIC_API_KEY`. + ## Konfiguration (Env-Vars) -| Var | Default | Zweck | +> Hinweis: Die **Code-Defaults in `llm.py` sind noch der alte Stand** (Anthropic, Jetson-IP +> `.20`, `qwen-light`). Im Betrieb überschreiben Workflows + Bot diese per Env mit den +> Jetson-Werten (`ollama` / `coder` / `.103`). Ein Refresh der Defaults ist vorgemerkt. + +| Var | Default (`llm.py`) | Im Betrieb | |---|---|---| -| `AI_PROVIDER` | `anthropic` | `anthropic` oder `ollama` | -| `AI_MODEL` | `claude-3-5-haiku-latest` / `qwen-light` | Modell | -| `ANTHROPIC_API_KEY` | — | Pflicht bei provider=anthropic | -| `AI_BASE_URL` | `http://192.168.178.20:11434/v1` | Ollama-Endpoint | +| `AI_PROVIDER` | `anthropic` | **`ollama`** | +| `AI_MODEL` | `claude-3-5-haiku-latest` / `qwen-light` | **`coder`** | +| `AI_BASE_URL` | `http://192.168.178.20:11434/v1` *(veraltet)* | **`…178.103…`** | | `JETSON_API_KEY` | — | Bearer für Ollama (optional) | -| `GITEA_API` | `https://git.eulernest.eu/api/v1` | Gitea-API-Basis | +| `ANTHROPIC_API_KEY` | — | nur bei provider=anthropic | +| `GITEA_API` | `https://git.eulernest.eu/api/v1` | Bot: intern `http://gitea:3000/api/v1` | | `GITEA_TOKEN` / `GITEA_BOT_TOKEN` | — | Token fürs Kommentieren | ## Constraints -- Runner hat **capacity 1** → Jobs laufen seriell. -- Docker-Executor abgesichert (kein privileged, keine Host-Mounts) → Trivy - `fs`/`config` + Gitleaks laufen; Scans *laufender* Container nicht (unnötig). -- Kosten (Anthropic, Haiku-Klasse, gedeckelter Diff): Bruchteile eines Cent/PR. +- **CI-Pfad:** Runner hat **capacity 1** → Jobs laufen seriell; Docker-Executor abgesichert + (kein privileged, keine Host-Mounts) → Trivy `fs`/`config` + Gitleaks laufen, Scans + *laufender* Container nicht (unnötig). +- **Bot-Pfad:** umgeht Runner/Clone/Hairpin komplett (interner `gitea:3000`-Zugriff). +- **Modell:** `qwen2.5-coder:7b` (Jetson, GPU) — brauchbar, ein paar dt. Patzer, < Claude. + Kosten **0** pro Call (self-hosted). Anthropic-Variante: Bruchteile eines Cent/PR (Haiku-Klasse). diff --git a/bot/README.md b/bot/README.md new file mode 100644 index 0000000..a15d843 --- /dev/null +++ b/bot/README.md @@ -0,0 +1,29 @@ +# ci-ai-bot — interaktiver Gitea-Webhook-Bot + +Feature **F** der ci-ai-Schicht: ein schlanker FastAPI-Dienst, der auf Gitea-Webhooks +reagiert und in **Sekunden** antwortet — ohne Runner, Clone oder NAT-Hairpin. + +- `@ci-ai ` in einem Issue/PR → Chat-Antwort im Thread (Thread- + Diff-Kontext). +- `pull_request` opened/synchronized/reopened → idempotentes KI-Review. + +Läuft als Container neben Gitea (`web_network`): Gitea-API **intern** über `http://gitea:3000`, +KI-Backend (Jetson-Ollama) LAN-direkt. Wiederverwendet `../scripts/{llm,gitea_api,ai_review}.py`. + +## Bauen (vom Repo-ROOT, nicht aus `bot/`) + +```bash +docker build -f bot/Dockerfile -t ci-ai-bot:latest . +``` + +Der Dockerfile kopiert `scripts/` + `bot/app.py` → Build-Context muss das Repo-Root sein. + +## Env + +`BOT_NAME` · `WEBHOOK_SECRET` (HMAC) · `GITEA_TOKEN` · `GITEA_API` (intern `http://gitea:3000/api/v1`) +· `AI_PROVIDER` / `AI_MODEL` / `AI_BASE_URL` (Jetson). Endpunkte: `GET /healthz`, `POST /webhook`. + +## Deployment + +Als Service im `docker_stack` des Repos **ansible-eulernest** — Compose-Service, Vars, der +nötige Gitea-`ALLOWED_HOST_LIST`-Fix, Webhook-Einrichtung und Troubleshooting stehen dort in +`docs/ci-ai-bot.md`. Siehe auch die `bot/`-Sektion in der [Haupt-README](../README.md). -- 2.49.1