PR 4 du chantier admin. Board de sante temps reel des dependances.
- POST /internal/tech-steps/heartbeat (requireInternalWorker) ->
recordWorkerHeartbeat : upsert WorkerHeartbeat (cle fixe
"tech-step-llm-worker"), lastRunAt/lastResult pour un ping "job".
Schema workerHeartbeatSchema dans packages/shared.
- services/tech-step-llm-worker : api-client.postHeartbeat (best-effort,
ne throw jamais) appele au boot (index.ts), a chaque tick et apres
chaque job (scheduler.ts, avec job/ok/counts).
- admin-monitoring.service.ts + GET /admin/monitoring (requireAdmin) :
sonde active bornee (~2 s) de Postgres (SELECT 1), l'API (uptime/RSS),
tech-step-intent-service (/health), et le worker via son heartbeat.
Statut up/degraded/down/unknown ; une sonde down ne casse ni les autres
ni l'endpoint. Seuils worker : > 8 j degraded, > 21 j down.
- MonitoringView / ServiceHealthView dans packages/shared.
- Front : MonitoringPage (grille de cartes coloree par statut, re-poll
15 s), logique pure monitoring.ts, i18n admin.monitoring.*,
AdminApiClient.getMonitoring.
- Tests : Mocha admin-monitoring.test.ts (heartbeat 401/400/upsert
job+boot ; GET /admin/monitoring 401, board 4 cibles, worker unknown
sans heartbeat puis up apres) ; Cypress monitoring.cy.ts (2 verts).
Worker mocha : 6/6 toujours verts.
- specs/backend-architecture.md : section monitoring. .gitignore :
apps/admin-web/cypress/{screenshots,videos,downloads}.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| src | ||
| test/jobs | ||
| .env.example | ||
| .env.test.example | ||
| .mocharc.json | ||
| Dockerfile | ||
| package.json | ||
| pnpm-lock.yaml | ||
| README.md | ||
| tsconfig.json | ||
tech-step-llm-worker
Standalone scheduled worker for the tech-step detection reliability feature (see the repo root's feature plan). Periodically:
audit-low-confidence— samples clausesapps/api's NLP classifier (tech-step-matcher.ts) itself scored below its own confidence threshold, asks a local LLM for a second opinion, and proposes a new training utterance whenever the LLM disagrees with what the NLP anchor already implied.transform-corrections— drains user-submitted tech-step corrections (StepDescription.tsx's editable mode,apps/web) not yet processed, and asks the LLM to propose new synonyms/example utterances from each one.
Both jobs only ever propose TechStepTrainingSuggestion rows for a maintainer to review — nothing here edits tech-step-training-data.ts automatically. See apps/api/src/scripts/retrain-tech-steps.ts for the maintainer-driven step that actually applies reviewed suggestions.
Why this lives outside the pnpm workspace
Same reasoning as experiments/llm-tech-step-poc: node-llama-cpp's native binding must never end up compiled into apps/api's own install/Docker build. This package has its own package.json/lockfile-less install, entirely separate from pnpm-workspace.yaml (which only covers apps/*/packages/*).
It also has no Prisma client and no direct database access — every read/write goes through apps/api's /internal/tech-steps/* routes (api-client.ts), authenticated with a shared secret (INTERNAL_WORKER_SECRET, must match apps/api's own). This keeps apps/api the single owner of the schema, and keeps this worker a simple "read some text over HTTP, run local inference, POST a suggestion" process with nothing to keep in sync if the schema changes shape.
Setup
cd services/tech-step-llm-worker
pnpm install --ignore-workspace
cp .env.example .env
# edit .env: set INTERNAL_WORKER_SECRET to match apps/api's own
pnpm start # runs the cron loop
# or:
RUN_ONCE=true pnpm start # runs both jobs once and exits
The GGUF model (qwen2.5-1.5b by default, Q4_K_M, ~1GB) downloads on first run into ./models/ (gitignored) and is cached there for subsequent runs — expect the very first run to take noticeably longer than later ones. See src/config.ts for every environment variable this reads, including TECH_STEP_LLM_MODEL_PATH to point at an already-downloaded GGUF file instead (useful offline, or when a mid-deploy network download isn't wanted).
Running via Docker Compose
docker-compose.yml (repo root) defines a tech-step-llm-worker service alongside app/postgres — it's optional: set INTERNAL_WORKER_SECRET in the root .env to enable it, leave it unset and the service simply won't start (its environment: block fails loudly if referenced without a value, same posture as the other required secrets in that file).
Testing
pnpm test
Unit tests (test/jobs/*.test.ts) mock api-client.ts's HTTP calls and a fake TechStepLlmService-shaped object directly — no real network calls, no real model loaded, no real apps/api needed. There is currently no integration test exercising a real model against a real apps/api instance; that would need to be run manually (see "Setup" above) before merging any future change to the prompts/schemas in llm-verdict.ts.
Known limitations (first version of this feature)
- Scheduler cadence (
TECH_STEP_WORKER_CRON, default weekly) is a provisional floor, not a calibrated value — see the feature's plan document for what it should be tuned against (recipe/correction volume, server resources). - Sampling in
audit-low-confidenceonly looks at theAUDIT_SAMPLE_SIZE(apps/api'stech-step-worker.service.ts) most-recently-created steps, not the whole recipe catalog — a smarter sampling strategy (e.g. weighted by how often a recipe is actually viewed/planned) is future work. - No per-key technique definitions are sent to the LLM today — just the bare
TechStep.keylist (GET /reference/tech-steps, e.g."panFry","foldIn"). Adding a short human-readable gloss per technique (a newTechStepView.descriptionfield) would likely improvejudgeClause's accuracy but is out of scope for this version. - Locale is always assumed
"fr"intransform-corrections— no recipe/step in the app carries its own locale field yet (seerecipe.service.ts'sDEFAULT_TECH_STEP_LOCALEcomment on the API side).