Tarik Moussa 688699a6ad chore(llm): refresh defaults to match deployed Jetson backend
llm.py defaulted to AI_PROVIDER=anthropic, AI_MODEL=qwen-light, and the
dead .20 Jetson IP — none of which match how workflows/bot actually run
it (ollama / coder / .103). Update the defaults and docstring to reflect
the live self-hosted setup, and refresh the README note that flagged the
mismatch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:17:58 +02:00
2026-06-10 06:39:20 +02:00

ci-ai — KI-Schicht für Gitea

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 (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): /gen-tests bzw. /summarize in einem Issue/PR kommentieren → der commands-Job (siehe caller.example.yml) postet das Ergebnis. @ci-ai (F): @ci-ai <Frage> in einem Issue/PR → Antwort im Thread (mit Diff-Kontext).

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 (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
  reusable-commands.yml    on: workflow_call — Slash-Commands (/gen-tests, /summarize)
workflows/
  caller.example.yml       der EINE 12-Zeiler fürs Ziel-Repo (empfohlen)
  *.example.yml            standalone-Workflows (Alternative: kopieren)

Interaktiver Bot (bot/)

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 <Frage> → 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):

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. 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 auf (uses: user2595/ci-ai/.gitea/workflows/reusable-*.yml@main

  • secrets: inherit). Logik-Updates passieren einmal hier, alle Repos ziehen automatisch nach.

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. 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 — volle Logik (klont die Skripte selbst), mehr Copy-Paste, aber unabhängig von reusable-workflow-Support.

KI-Backend: Jetson (self-hosted, im Betrieb)

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:

AI_PROVIDER: ollama
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)

Hinweis: Die Code-Defaults in llm.py entsprechen jetzt dem Jetson-Betrieb (ollama / coder / .103). Workflows + Bot setzen die Werte trotzdem explizit per Env — ein Provider-Wechsel (z.B. zurück auf anthropic) bleibt damit ein reiner Env-Change, keine Code-Änderung.

Var Default (llm.py) Im Betrieb
AI_PROVIDER ollama ollama
AI_MODEL coder (ollama) / claude-3-5-haiku-latest (anthropic) coder
AI_BASE_URL http://192.168.178.103:11434/v1 …178.103…
JETSON_API_KEY Bearer für Ollama (optional)
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

  • 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).
Description
KI-Schicht für die Gitea-CI: PR-Review, Security-Erklärung & CI-Failure-Analyse (GitLab-Duo-Parität, provider-agnostisch: Anthropic jetzt, Jetson/Ollama später)
Readme 107 KiB
Languages
Python 97.8%
Dockerfile 2.2%