#!/usr/bin/env bash
# Worker — Ein-Schuss-Arbeitsauftrag an ein bestimmtes Worker-Modell (llama-swap, SERIELL).
# Das Gegenstueck zu fremdblick.sh: fremdblick KRITISIERT, worker ARBEITET. Beide sind eine
# EINZELNE Completion an eine ECHTE llama-swap-Modell-ID (kein Gateway-Alias, kein delegate_task)
# → umgeht Hermes' 64K-Delegations-Floor (MINIMUM_CONTEXT_LENGTH) und das Laden grosser Modelle
# neben dem Warm-Set liegt bewusst in DEINER Hand.
#
# WICHTIG (Speicher & Latenz-Trade-off):
#   Der Standard-Worker ist Qwen3-Coder-Next (laedt klein und schnell neben dem Warm-Set).
#   Fuer initiale Planung kann "gpt-oss-120b" gerufen werden. Das Modell passt knapp in den
#   Speicher (124 GB RAM), aber das on-demand Laden unterbricht Lucys 1-s-Latenz fuer ca.
#   15-30 Sekunden. Dieser Latenz-Hit wird fuer die Orchestrator-Planung in Kauf genommen.
#
# Der Worker ist ZUSTANDSLOS: kein Gedaechtnis, keine Datei-Haende. Er bekommt GENAU den Kontext,
# den du ihm auf stdin gibst, und liefert TEXT zurueck (Datei-Inhalt / Diff / Plan). Das SCHREIBEN
# in Dateien macht der Manager (Hermes) danach mit seinen eigenen Tools.
#
# Nutzung:  printf '%s' "$teiltask_mit_kontext" | WORKER_ROLE=build worker.sh > /tmp/out.py
#           printf '%s' "$spec" | WORKER_MODEL=Qwen3-Coder-Next WORKER_ROLE=build worker.sh
#
# Env-Overrides:
#   WORKER_MODEL   echte llama-swap-ID (Default: Qwen3-Coder-Next). NICHT gpt-oss/heavy (siehe oben).
#   WORKER_ROLE    build (Default) | refactor | plan | prose  — waehlt das System-Raster.
#   WORKER_SYS     eigenes System-Raster (uebersteuert WORKER_ROLE).
#   WORKER_MAXTOK  max_tokens (Default 4000 — Code braucht Platz).
#   WORKER_TEMP    temperature (Default 0.1 — deterministisch fuer Code).
set -uo pipefail

# Achtung: llama-swap listet unter /v1/models die ECHTEN Modell-IDs, nicht die Gateway-Aliase
# (kein "heavy"/"coder"/"scout" hier — die echte ID nehmen).
ENDPOINT="${WORKER_ENDPOINT:-http://127.0.0.1:8080/v1/chat/completions}"
MODEL="${WORKER_MODEL:-Qwen3-Coder-Next}"
ROLE="${WORKER_ROLE:-build}"
MAXTOK="${WORKER_MAXTOK:-4000}"
TEMP="${WORKER_TEMP:-0.1}"
TASK="$(cat)"

if [ -z "${TASK// /}" ]; then
  echo "=== WORKER: nichts zu tun (leere Eingabe) ===" >&2
  exit 2
fi

# Aufruf-Protokoll: Nachweis, dass wirklich delegiert wurde (der Manager kann sich das nicht
# ausdenken) + Beobachtbarkeit eines Orchestrator-Laufs. Best-effort, blockiert nie.
LOGF="${WORKER_LOG:-$HOME/.hermes/logs/worker.log}"
mkdir -p "$(dirname "$LOGF")" 2>/dev/null || true
printf '%s\tmodel=%s\trole=%s\tbytes=%s\tfirst=%s\n' \
  "$(date +%Y-%m-%dT%H:%M:%S)" "$MODEL" "$ROLE" "${#TASK}" \
  "$(printf '%s' "$TASK" | tr '\n\t' '  ' | cut -c1-90)" >> "$LOGF" 2>/dev/null || true

# Bau-Raster: liefert NUR das Artefakt, keine Prosa, kein Markdown-Zaun (Manager schreibt es 1:1 in eine Datei).
BUILD_SYS='Du bist ein fokussierter Umsetzungs-Worker in einem Fliessband. Du bekommst EINEN abgegrenzten Teil-Auftrag mit allem noetigen Kontext. Erledige GENAU diesen Teil — nicht mehr, nicht weniger. Du bist ZUSTANDSLOS: nutze NUR den mitgelieferten Kontext, erfinde keine Datei-Inhalte, keine Pfade und keine APIs dazu; fehlt dir etwas Entscheidendes, schreibe als EINZIGE Ausgabe eine Zeile "FEHLT: <was genau>" statt zu raten. Uebernimm den Stil des umgebenden Codes (Sprache der Kommentare, bestehende Muster, Einrueckung). Gib NUR das geforderte Artefakt aus (den vollstaendigen Datei-Inhalt bzw. den unified Diff), OHNE Vorrede, OHNE ```-Codezaun, OHNE Erklaerung danach.'
# Refactor: wie build, aber betont Verhalten-erhalten.
REFACTOR_SYS='Du bist ein fokussierter Refactor-Worker. Du bekommst bestehenden Code und einen klaren Umbau-Auftrag. Erhalte das beobachtbare Verhalten exakt; aendere nur, was der Auftrag verlangt. Du bist ZUSTANDSLOS: nutze NUR den mitgelieferten Kontext; fehlt Entscheidendes, gib nur "FEHLT: <was>" aus. Gib NUR den vollstaendigen neuen Datei-Inhalt bzw. den Diff aus, OHNE Prosa, OHNE ```-Codezaun.'
# Plan-Raster: knappe, umsetzbare Schrittliste (kein Code). In v1 plant meist der Manager selbst; optional.
PLAN_SYS='Du bist ein knapper Umsetzungs-Planer. Zerlege den Auftrag in eine kurze, nummerierte Liste konkreter Schritte — je 1 Zeile: welche Datei, was passiert. Keine Prosa drumherum, KEIN Code, keine Alternativen-Diskussion.'
# Prosa-Raster: Text-/Recherche-Teiltask.
PROSE_SYS='Du bist ein fokussierter Text-Worker. Erledige den Schreib-/Recherche-Teilauftrag knapp und sachlich auf Deutsch, ausschliesslich mit dem mitgelieferten Kontext. Keine Ausschmueckung, keine erfundenen Fakten.'

case "$ROLE" in
  build)    SYS="${WORKER_SYS:-$BUILD_SYS}" ;;
  refactor) SYS="${WORKER_SYS:-$REFACTOR_SYS}" ;;
  plan)     SYS="${WORKER_SYS:-$PLAN_SYS}" ;;
  prose)    SYS="${WORKER_SYS:-$PROSE_SYS}" ;;
  *)        SYS="${WORKER_SYS:-$BUILD_SYS}" ;;
esac

RESP="$(printf '%s' "$TASK" | jq -Rs . | jq -n --arg m "$MODEL" --arg s "$SYS" --argjson t "$MAXTOK" --argjson temp "$TEMP" \
  '{model:$m, temperature:$temp, max_tokens:$t, messages:[{role:"system",content:$s},{role:"user",content:input}]}' \
  | curl -s --max-time 300 "$ENDPOINT" -H 'Content-Type: application/json' -d @- 2>/dev/null)"

OUT="$(printf '%s' "$RESP" | jq -r '.choices[0].message.content // empty' 2>/dev/null)"
FIN="$(printf '%s' "$RESP" | jq -r '.choices[0].finish_reason // empty' 2>/dev/null)"

# Code-Modelle wickeln die Ausgabe trotz Anweisung gern in einen ```-Codezaun. stdout soll aber ein
# DIREKT schreibbares Artefakt sein → einen umschliessenden Zaun entfernen (nur wenn die erste UND
# die letzte nicht-leere Zeile ein Zaun ist — der uebliche Wrap-Fall; interne Zaeune bleiben unberuehrt).
OUT="$(printf '%s' "$OUT" | awk '
  { l[NR]=$0 }
  END {
    f=0; last=0
    for (i=1;i<=NR;i++) if (l[i] ~ /[^[:space:]]/) { if (!f) f=i; last=i }
    if (f && l[f] ~ /^[[:space:]]*```/ && l[last] ~ /^[[:space:]]*```[[:space:]]*$/ && f!=last)
      { for (i=f+1;i<last;i++) print l[i] }
    else
      { for (i=1;i<=NR;i++) print l[i] }
  }')"

if [ -n "$OUT" ]; then
  printf '%s\n' "$OUT"
  # Abschneide-Warnung auf stderr (stdout bleibt sauberes Artefakt).
  [ "$FIN" = "length" ] && echo "=== WORKER-HINWEIS: Ausgabe bei max_tokens ($MAXTOK) ABGESCHNITTEN — WORKER_MAXTOK erhoehen ODER den Teil-Task kleiner schneiden. ===" >&2
  exit 0
else
  echo "=== WORKER FEHLGESCHLAGEN (Modell $MODEL, finish=${FIN:-keins}) — kein Ergebnis. Endpoint/Modell-ID pruefen (echte /v1/models-ID, kein Alias). ===" >&2
  exit 1
fi
