383 lines
14 KiB
YAML
383 lines
14 KiB
YAML
# Taskfile dla agentic-codemod-pipeline (https://taskfile.dev)
|
|
#
|
|
# Instalacja go-task:
|
|
# macOS: brew install go-task
|
|
# pip: pip install go-task-bin
|
|
# linux: sh -c "$(curl -sSL https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
|
|
#
|
|
# Wszystkie zadania pracują w lokalnym .venv. Nic nie jest instalowane do systemowego
|
|
# Pythona - na macOS z Homebrew kończyłoby się to błędem "externally-managed-environment"
|
|
# (PEP 668), a w najlepszym razie zaśmieceniem interpretera systemowego.
|
|
|
|
version: "3"
|
|
silent: true
|
|
|
|
# Lokalna konfiguracja developerska. Plik jest opcjonalny i nie trafia do repozytorium
|
|
# (.gitignore). Zmienne wyeksportowane w powłoce mają pierwszeństwo przed .env,
|
|
# więc `eval "$(task llm:env)"` zawsze wygrywa z tym, co tu wpisano.
|
|
dotenv: [".env"]
|
|
|
|
vars:
|
|
VENV: .venv
|
|
BIN: "{{.VENV}}/bin"
|
|
PY: "{{.VENV}}/bin/python"
|
|
CLI: "{{.VENV}}/bin/agentic-codemod"
|
|
FIXTURE: examples/fixtures/acme-app
|
|
# Interpreter bazowy do utworzenia venva. Na macOS z systemowym Pythonem 3.9:
|
|
# PYTHON=python3.12 task install
|
|
PYTHON: '{{.PYTHON | default "python3"}}'
|
|
# Backend LLM: auto (wykrycie platformy) | vllm-gpu | vllm-metal | ollama | mock
|
|
LLM_BACKEND: '{{.LLM_BACKEND | default "auto"}}'
|
|
LLM_RUN_DIR: .runs/llm
|
|
COMPOSE: docker compose -f llm/docker-compose.yml
|
|
BACKEND_PY: "{{.VENV}}/bin/python llm/scripts/backend.py"
|
|
|
|
env:
|
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
|
|
tasks:
|
|
default:
|
|
desc: Lista dostępnych zadań
|
|
cmds:
|
|
- task --list
|
|
|
|
# ------------------------------------------------------------- środowisko
|
|
doctor:
|
|
desc: Diagnostyka środowiska - uruchom to najpierw, gdy coś nie działa
|
|
cmds:
|
|
- |
|
|
printf 'system : %s %s\n' "$(uname -s)" "$(uname -m)"
|
|
printf 'interpreter bazowy: %s -> %s\n' "{{.PYTHON}}" "$({{.PYTHON}} -V 2>&1 || echo BRAK)"
|
|
printf 'venv : %s\n' "$(test -x {{.PY}} && {{.PY}} -V 2>&1 || echo 'brak (uruchom: task install)')"
|
|
printf 'uv : %s\n' "$(command -v uv >/dev/null 2>&1 && uv --version || echo 'brak (opcjonalny, przyspiesza instalację)')"
|
|
printf 'git : %s\n' "$(git --version 2>/dev/null || echo BRAK)"
|
|
printf 'ripgrep : %s\n' "$(rg --version 2>/dev/null | head -1 || echo 'brak (opcjonalny, używany przez skille)')"
|
|
printf 'apm : %s\n' "$(apm --version 2>/dev/null || echo 'brak (potrzebny do apm install)')"
|
|
printf 'agno : %s\n' "$(test -x {{.PY}} && {{.PY}} -c 'import agno; print(agno.__version__)' 2>/dev/null || echo 'brak')"
|
|
|
|
venv:
|
|
internal: true
|
|
preconditions:
|
|
- sh: command -v {{.PYTHON}} >/dev/null 2>&1
|
|
msg: |
|
|
Nie znaleziono interpretera "{{.PYTHON}}".
|
|
Na macOS: brew install python@3.12, a potem PYTHON=python3.12 task install
|
|
- sh: '{{.PYTHON}} -c "import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)"'
|
|
msg: |
|
|
Wymagany Python 3.10 lub nowszy ({{.PYTHON}} jest starszy).
|
|
macOS ma w systemie 3.9 - zainstaluj nowszy i wskaż go:
|
|
brew install python@3.12 && PYTHON=python3.12 task install
|
|
status:
|
|
- test -x {{.PY}}
|
|
cmds:
|
|
- |
|
|
# Niekompletny venv (przerwana instalacja, skasowany interpreter) jest gorszy
|
|
# niż jego brak - narzędzia zgłaszają wtedy mylące błędy. Odtwarzamy go od zera.
|
|
if [ -d "{{.VENV}}" ] && [ ! -x "{{.PY}}" ]; then
|
|
echo "usuwam niekompletny {{.VENV}}"
|
|
rm -rf "{{.VENV}}"
|
|
fi
|
|
if command -v uv >/dev/null 2>&1; then
|
|
uv venv --python {{.PYTHON}} {{.VENV}}
|
|
else
|
|
{{.PYTHON}} -m venv {{.VENV}}
|
|
fi
|
|
|
|
install:
|
|
desc: Tworzy .venv i instaluje zależności (runtime + dev)
|
|
deps: [venv]
|
|
status:
|
|
- test -f {{.VENV}}/.install-stamp
|
|
- test {{.VENV}}/.install-stamp -nt pyproject.toml
|
|
cmds:
|
|
- |
|
|
if command -v uv >/dev/null 2>&1; then
|
|
uv pip install --python {{.PY}} -e ".[dev]"
|
|
else
|
|
{{.PY}} -m pip install --quiet --upgrade pip
|
|
{{.PY}} -m pip install -e ".[dev]"
|
|
fi
|
|
- touch {{.VENV}}/.install-stamp
|
|
- 'echo "gotowe: {{.CLI}}"'
|
|
|
|
context:
|
|
desc: Instaluje i audytuje kontekst agentowy (APM), potem go wypisuje
|
|
deps: [install]
|
|
preconditions:
|
|
- sh: command -v apm >/dev/null 2>&1
|
|
msg: |
|
|
Brak APM CLI. Instalacja:
|
|
macOS: brew install apm (albo pip install apm-cli)
|
|
inne: curl -sSL https://aka.ms/apm-unix | sh
|
|
Repozytorium działa też bez APM CLI - wtedy używany jest wyłącznie
|
|
lokalny kontekst z .apm/ (bez zależności zewnętrznych).
|
|
cmds:
|
|
- apm install
|
|
- apm audit
|
|
- "{{.CLI}} context"
|
|
|
|
context:show:
|
|
desc: Wypisuje kontekst agentowy widziany przez silnik (bez apm install)
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.CLI}} context"
|
|
|
|
# ------------------------------------------------------------------ jakość
|
|
lint:
|
|
desc: Statyczna analiza i sprawdzenie formatowania
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.BIN}}/ruff check src tests llm"
|
|
- "{{.BIN}}/ruff format --check src tests llm"
|
|
|
|
fmt:
|
|
desc: Automatyczna poprawa formatowania i prostych błędów
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.BIN}}/ruff check --fix src tests llm"
|
|
- "{{.BIN}}/ruff format src tests llm"
|
|
|
|
test:
|
|
desc: Testy jednostkowe i integracyjne (bez modelu językowego)
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.BIN}}/pytest -q {{.CLI_ARGS}}"
|
|
|
|
test:cov:
|
|
desc: Testy z raportem pokrycia
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.BIN}}/pytest -q --cov=agentic_codemod --cov-report=term-missing"
|
|
|
|
ci:
|
|
desc: To, co sprawdza pipeline - uruchom przed pushem
|
|
cmds:
|
|
- task: lint
|
|
- task: test
|
|
- task: demo
|
|
|
|
# ------------------------------------------------------------- uruchomienia
|
|
demo:
|
|
desc: Pełny przebieg offline na fixture (bez LLM, bez sieci)
|
|
deps: [install]
|
|
vars:
|
|
RUN_DIR: '{{.RUN_DIR | default ".runs/demo"}}'
|
|
cmds:
|
|
- |
|
|
{{.CLI}} run \
|
|
--repo {{.FIXTURE}} \
|
|
--package acme-sdk --module acme --to-version 2.1.0 \
|
|
--offline --run-dir "{{.RUN_DIR}}"
|
|
- 'echo "diff: {{.RUN_DIR}}/changes.patch | manifest: {{.RUN_DIR}}/run.json"'
|
|
|
|
plan:
|
|
desc: 'Sam plan zmian, bez modyfikacji kodu. Użycie: task plan -- --repo ../repo --package acme-sdk --to-version 2.1.0'
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.CLI}} run --plan-only --run-dir .runs/plan {{.CLI_ARGS}}"
|
|
|
|
run:
|
|
desc: 'Przebieg z modelem. Użycie: task run -- --repo ../repo --package acme-sdk --to-version 2.1.0'
|
|
deps: [install]
|
|
preconditions:
|
|
- sh: test -n "$CODEMOD_LLM_BASE_URL"
|
|
msg: |
|
|
Nie ustawiono CODEMOD_LLM_BASE_URL. Skopiuj .env.example do .env i wyeksportuj zmienne,
|
|
albo uruchom przebieg deterministyczny: task demo
|
|
cmds:
|
|
- "{{.CLI}} run --run-dir .runs/local {{.CLI_ARGS}}"
|
|
|
|
# ------------------------------------------------------------------- LLM
|
|
# Kontrakt jest jeden: endpoint zgodny z OpenAI pod CODEMOD_LLM_BASE_URL.
|
|
# Backendy różnią się wyłącznie tym, jak ten endpoint powstaje.
|
|
# Konfiguracja: llm/models.yml. Wybór ręczny: LLM_BACKEND=ollama task llm:up
|
|
|
|
llm:list:
|
|
desc: Dostępne backendy LLM i który pasuje do tej maszyny
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.BACKEND_PY}} list"
|
|
|
|
llm:env:
|
|
desc: 'Zmienne środowiskowe dla wybranego backendu. Użycie: eval "$(task llm:env)"'
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.BACKEND_PY}} env --backend {{.LLM_BACKEND}}"
|
|
|
|
llm:up:
|
|
desc: Uruchamia backend LLM (autodetekcja platformy) i czeka na gotowość endpointu
|
|
deps: [install]
|
|
cmds:
|
|
- mkdir -p {{.LLM_RUN_DIR}}
|
|
- |
|
|
set -e
|
|
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
|
|
MODEL="$({{.BACKEND_PY}} get serve.model --backend "$BACKEND")"
|
|
BASE_URL="$({{.BACKEND_PY}} get base_url --backend "$BACKEND")"
|
|
echo "backend: $BACKEND | model: $MODEL | endpoint: $BASE_URL"
|
|
|
|
case "$BACKEND" in
|
|
vllm-gpu)
|
|
command -v docker >/dev/null || { echo "brak dockera - zainstaluj albo: LLM_BACKEND=ollama task llm:up" >&2; exit 1; }
|
|
LLM_SERVE_MODEL="$MODEL" {{.COMPOSE}} --profile vllm-gpu up -d
|
|
LOG=""
|
|
;;
|
|
vllm-metal)
|
|
LOG="{{.LLM_RUN_DIR}}/vllm-metal.log"
|
|
llm/scripts/vllm-metal.sh up "$MODEL" 8000 "$LOG" "{{.LLM_RUN_DIR}}/vllm-metal.pid"
|
|
;;
|
|
ollama)
|
|
if command -v ollama >/dev/null 2>&1; then
|
|
# macOS: natywna Ollama korzysta z Metala; w kontenerze liczyłaby na CPU
|
|
curl -fsS --max-time 3 http://localhost:11434/api/version >/dev/null 2>&1 || {
|
|
{{.PY}} llm/scripts/daemonize.py --pidfile "{{.LLM_RUN_DIR}}/ollama.pid" \
|
|
--log "{{.LLM_RUN_DIR}}/ollama.log" -- ollama serve
|
|
sleep 2
|
|
}
|
|
ollama pull "$MODEL"
|
|
else
|
|
{{.COMPOSE}} --profile ollama up -d
|
|
{{.COMPOSE}} exec -T ollama ollama pull "$MODEL"
|
|
fi
|
|
LOG="{{.LLM_RUN_DIR}}/ollama.log"
|
|
;;
|
|
mock)
|
|
LOG="{{.LLM_RUN_DIR}}/mock.log"
|
|
{{.PY}} llm/scripts/daemonize.py --pidfile "{{.LLM_RUN_DIR}}/mock.pid" --log "$LOG" -- \
|
|
{{.PY}} llm/mock_server.py --port 8077
|
|
;;
|
|
*)
|
|
echo "nieznany backend: $BACKEND" >&2; exit 1
|
|
;;
|
|
esac
|
|
|
|
echo "podgląd logów w drugim oknie: task llm:logs"
|
|
llm/scripts/wait-for-endpoint.sh "$BASE_URL" "${LLM_WAIT_TIMEOUT:-900}" "$LOG"
|
|
echo
|
|
echo "gotowe. Wyeksportuj zmienne do swojej powłoki:"
|
|
echo ' eval "$(task llm:env)"'
|
|
|
|
llm:down:
|
|
desc: Zatrzymuje backend LLM
|
|
deps: [install]
|
|
cmds:
|
|
- |
|
|
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
|
|
case "$BACKEND" in
|
|
vllm-gpu) {{.COMPOSE}} --profile vllm-gpu down ;;
|
|
ollama)
|
|
if [ -f "{{.LLM_RUN_DIR}}/ollama.pid" ]; then
|
|
{{.PY}} llm/scripts/daemonize.py --pidfile "{{.LLM_RUN_DIR}}/ollama.pid" --log /dev/null --stop
|
|
else
|
|
{{.COMPOSE}} --profile ollama down
|
|
fi
|
|
;;
|
|
vllm-metal) llm/scripts/vllm-metal.sh down "{{.LLM_RUN_DIR}}/vllm-metal.pid" ;;
|
|
mock)
|
|
{{.PY}} llm/scripts/daemonize.py --pidfile "{{.LLM_RUN_DIR}}/mock.pid" --log /dev/null --stop
|
|
;;
|
|
esac
|
|
|
|
llm:status:
|
|
desc: Czy endpoint LLM odpowiada i jakie modele wystawia
|
|
deps: [install]
|
|
cmds:
|
|
- |
|
|
BASE_URL="$({{.BACKEND_PY}} get base_url --backend {{.LLM_BACKEND}})"
|
|
echo "endpoint: $BASE_URL"
|
|
curl -fsS --max-time 5 "$BASE_URL/models" || { echo "brak odpowiedzi - uruchom: task llm:up" >&2; exit 1; }
|
|
|
|
llm:smoke:
|
|
desc: Jedno zapytanie do endpointu - sprawdza, czy model w ogóle odpowiada
|
|
deps: [install]
|
|
cmds:
|
|
- |
|
|
BASE_URL="$({{.BACKEND_PY}} get base_url --backend {{.LLM_BACKEND}})"
|
|
MODEL="$({{.BACKEND_PY}} get serve.model --backend {{.LLM_BACKEND}})"
|
|
curl -fsS --max-time 120 "$BASE_URL/chat/completions" \
|
|
-H 'Content-Type: application/json' \
|
|
-d "{\"model\":\"$MODEL\",\"messages\":[{\"role\":\"user\",\"content\":\"Odpowiedz jednym slowem: dziala\"}],\"max_tokens\":16}"
|
|
echo
|
|
|
|
llm:logs:
|
|
desc: 'Logi backendu LLM na żywo. Warianty: TAIL=500, FOLLOW=false, GREP=wzorzec'
|
|
deps: [install]
|
|
vars:
|
|
TAIL: '{{.TAIL | default "100"}}'
|
|
FOLLOW: '{{.FOLLOW | default "true"}}'
|
|
GREP: '{{.GREP | default ""}}'
|
|
cmds:
|
|
- |
|
|
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
|
|
llm/scripts/logs.sh \
|
|
--backend "$BACKEND" \
|
|
--run-dir "{{.LLM_RUN_DIR}}" \
|
|
--tail "{{.TAIL}}" \
|
|
{{if eq .FOLLOW "false"}}--no-follow{{else}}--follow{{end}} \
|
|
{{if .GREP}}--grep "{{.GREP}}"{{end}}
|
|
|
|
llm:logs:errors:
|
|
desc: Same błędy i ostrzeżenia z logów backendu (jednorazowy zrzut)
|
|
deps: [install]
|
|
vars:
|
|
TAIL: '{{.TAIL | default "2000"}}'
|
|
cmds:
|
|
- |
|
|
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
|
|
llm/scripts/logs.sh --backend "$BACKEND" --run-dir "{{.LLM_RUN_DIR}}" \
|
|
--tail "{{.TAIL}}" --no-follow \
|
|
--grep "error|warning|traceback|exception|failed|out of memory|cuda|refused"
|
|
|
|
llm:logs:save:
|
|
desc: 'Zrzut logów do pliku, do dołączenia w zgłoszeniu. Domyślnie .runs/llm/dump.log'
|
|
deps: [install]
|
|
vars:
|
|
TAIL: '{{.TAIL | default "5000"}}'
|
|
OUT: '{{.OUT | default ".runs/llm/dump.log"}}'
|
|
cmds:
|
|
- |
|
|
BACKEND="$({{.BACKEND_PY}} detect --requested {{.LLM_BACKEND}})"
|
|
llm/scripts/logs.sh --backend "$BACKEND" --run-dir "{{.LLM_RUN_DIR}}" \
|
|
--tail "{{.TAIL}}" --save "{{.OUT}}"
|
|
|
|
llm:mock:
|
|
desc: Atrapa serwera OpenAI na pierwszym planie (Ctrl-C kończy)
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.PY}} llm/mock_server.py --port 8077 --verbose"
|
|
|
|
# ------------------------------------------------------- przebiegi z LLM
|
|
demo:llm:
|
|
desc: Przebieg agentowy na fixture z aktualnym backendem LLM (domyślnie atrapa - deterministyczny)
|
|
deps: [install]
|
|
vars:
|
|
RUN_DIR: '{{.RUN_DIR | default ".runs/demo-llm"}}'
|
|
cmds:
|
|
- task: llm:up
|
|
vars: {LLM_BACKEND: "{{.LLM_BACKEND}}"}
|
|
- |
|
|
eval "$({{.BACKEND_PY}} env --backend {{.LLM_BACKEND}})"
|
|
{{.CLI}} run \
|
|
--repo {{.FIXTURE}} \
|
|
--package acme-sdk --module acme --to-version 2.1.0 \
|
|
--run-dir "{{.RUN_DIR}}"
|
|
|
|
test:llm:
|
|
desc: Testy ścieżki agentowej na atrapie LLM (bez GPU i bez sieci)
|
|
deps: [install]
|
|
cmds:
|
|
- "{{.BIN}}/pytest -q tests/test_llm_path.py"
|
|
|
|
# -------------------------------------------------------------- sprzątanie
|
|
clean:
|
|
desc: Usuwa artefakty przebiegów i cache narzędzi
|
|
cmds:
|
|
- rm -rf .runs workspace .pytest_cache .ruff_cache .coverage htmlcov
|
|
- find . -type d -name __pycache__ -prune -exec rm -rf {} +
|
|
|
|
distclean:
|
|
desc: Jak clean, plus .venv i zainstalowany kontekst APM
|
|
deps: [clean]
|
|
cmds:
|
|
- rm -rf {{.VENV}} apm_modules *.egg-info src/*.egg-info
|