feat: add Go client package with sync facade over async /jobs

Adds client/ -- a public Go package providing a synchronous facade over
foreman's async POST /jobs API (Level 1 integration per ADR-0011).

Two delivery modes:
- Webhook receiver (preferred): ephemeral HTTP server on random port,
  pushes results immediately, verifies HMAC when configured
- Polling fallback: polls GET /jobs/{id} at configurable interval

Also includes Tags() and Embed() helpers, bearer auth support, and
comprehensive integration tests against the real foreman HTTP handlers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-23 18:38:16 -04:00
parent daf07fd759
commit 4759a06d1b
4 changed files with 1173 additions and 0 deletions
+38
View File
@@ -165,3 +165,41 @@ with the real SQLite-backed job queue and single worker loop.
no HMAC when no secret, signature format validation.
- Artifacts: small inline, large by URL, empty returns nil.
- TTL pruner: deletes old terminal jobs.
## Phase 5: Go client package + go-llm Foreman() constructor — 2026-05-23
**Level 0 + Level 1 integration complete** (ADR-0011).
- `client/` — public Go client package (sync facade over async `/jobs` API):
- `client.New(baseURL, opts...)`: configurable client with bearer auth,
webhook secret, custom HTTP client, poll interval.
- `client.Submit(ctx, SubmitRequest) (*Result, error)`: synchronous
submission — blocks until the job reaches a terminal state (`done`/`failed`).
- **Two delivery modes:**
- **Webhook receiver (preferred):** starts an ephemeral HTTP server on a
random port, sets `state_webhook_url`, waits for the `done`/`failed`
webhook event. Verifies HMAC signature when `WithWebhookSecret` is set.
Falls back to polling automatically if the listener fails to bind.
- **Polling fallback:** polls `GET /jobs/{id}` at `pollInterval` (default
2s) until terminal state. Forced via `WithPollingMode()`.
- `client.Tags(ctx)`: fetches installed models via `GET /api/tags`.
- `client.Embed(ctx, EmbedRequest)`: sends embedding requests via
`POST /api/embed` (bypasses queue, ADR-0013).
- Both modes respect context cancellation/deadline and clean up resources.
- Tests (all passing with `-race`):
- Happy path (polling): submit, poll, verify completed result + artifacts.
- Happy path (webhook): submit with webhook receiver, verify push delivery.
- Failed job: returns Result with state=failed and error message.
- Context timeout: returns error on deadline exceeded.
- Auth: bearer token sent when configured; 401 without it.
- HMAC webhook verification: signed webhooks verified correctly.
- Tags and Embed endpoints: round-trip through the client.
- Missing model validation: returns error before network call.
- go-llm integration (Level 0):
- `llm.Foreman(baseURL, apiKey, opts...)` constructor added to
`v2/constructors.go` on branch `feat/foreman-constructor`.
- Delegates to existing `ollamaProvider.New()` — zero new code paths.
- DD#9 added to `v2/CLAUDE.md`.
- PR: https://gitea.stevedudenhoeffer.com/steve/go-llm/pulls/4