Commit Graph
13 Commits
Author SHA1 Message Date
Admin Bot 966eebe9fe fix(proxy): wire LLM latency metrics into the streaming response path
NewLLMMetricsCapture/ResponseWriterWrapper had zero call sites, so
llm_ttft_seconds, llm_itl_seconds and llm_tokens_total were exported as
HELP/TYPE stubs with no samples. These are the only per-request LLM latency
series covering the Ollama models (ornith:35b, qwen2.5:3b-instruct), which
expose no /metrics of their own.

Wire the wrapper around the response writer in ServeHTTP for model-routed
requests, and delete the unused io.WriteCloser duplicate that made the gap
hard to spot (two near-identical implementations, neither constructed).

Correctness details:
- Only SSE responses are measured. A non-streaming completion is written in
  one Write, which would otherwise be recorded as a TTFT sample equal to the
  whole request latency with no ITL samples.
- requestStart is captured at the top of ServeHTTP so TTFT measures from
  request acceptance, not from when the upstream response is copied.
- Token count skips the `data: [DONE]` terminator.
- Unwrap() keeps http.ResponseController (Flush/Hijack/deadlines) working
  through the wrapper, so SSE unbuffering (issue #33) is unaffected.

Tests: unit coverage for SSE vs non-streaming, wrapper transparency, and an
end-to-end handler test that fails if the wrapper is ever wired nowhere again.
2026-09-29 22:50:26 +08:00
rockandAdmin Bot ef5e736673 feat(cag): CAG integration — memory adapter, warmup semaphore, cache-aware routing (#47)
CI / CI (push) Successful in 3m20s
## Summary

Three issues in one PR. Implements Cache-Augmented Generation (CAG) support in the api-gw.

---

### #44 — Memory ServiceAdapter: standing-context resource

- Code-registers `memory` adapter with `standing-context` resource (`POST /memory/standing-context`)
- Fixes dispatcher bug: method-level `Auth.Capability` override now respected (was always using verb-derived capability)
- Auth: `memory:read` required via method-level override; `memory:write` for ingest
- `MEMORY_UPSTREAM_URL` env configures upstream

### #45 — Continuous batching: warmup deprioritisation + per-backend semaphore

- In-memory bounded queue per backend: `VLLM_MAX_QUEUE_DEPTH` slots (default 20)
- Concurrency limiter: `VLLM_MAX_NUM_SEQS` slots (default 4)
- `X-Poimen-Warmup: true` → sleep `WARMUP_DELAY_MS` (default 500ms) before queue entry, strip header, add `X-Priority: low`
- Queue full → 503 immediately, no goroutine pile-up
- Metrics: `warmup_requests_delayed_total`, `queue_full_requests_total` at `/metrics`

### #46 — Cache-aware routing: rendezvous hash + prefix cache hit logging

- `addresses: [...]` YAML field for multi-backend models (backward-compat with `address`)
- `X-Poimen-Cache-Key` header → rendezvous-hash backend selection (same key = same GPU)
- No cache key → round-robin
- Failed backend excluded 30s; next-highest-score fallback
- `vllm-num-cached-tokens` response header captured → `vllm_prefix_cache_hit_tokens_total` counter + structured log

---

## Verification

```
go build ./...    ✅
go vet ./...      ✅
go test -race ./... ✅ (all packages, 0 races)
```

## Reviewer verdicts

- reviewer-sre: SIGN-OFF
- reviewer-quality: SIGN-OFF

## Non-blocking findings (🟡🔵 only)

- `getOrCreateTransport` not goroutine-safe by design; pre-population invariant documented
- `time.Sleep(warmupDelay)` holds goroutine; context-cancel alternative left as TODO
- OTel trace_id not propagated into CAG log events (existing gap)

Closes #44 Closes #45 Closes #46

---------

Co-authored-by: Admin Bot <[email protected]>
Reviewed-on: #47
2026-09-27 11:16:40 +00:00
30e0a83a50 feat(network): SSE optimization for local LLM streaming (#31 #32 #33) (#26)
CI / CI (push) Successful in 3m35s
Addresses three critical network issues for LLM streaming performance:

**#33 Disable proxy buffering for SSE**
- Add X-Accel-Buffering: no header to response
- Tells nginx/Ingress to stream events immediately instead of buffering

**#32 HTTP/2 multiplexing for concurrent streams**
- Enable HTTP/2 in server config via http2.ConfigureServer()
- Increase MaxConnsPerHost to 10 for better concurrency
- Allows multiple concurrent LLM requests without blocking

**#31 TCP backpressure for streaming LLM responses**
- Set TCP_NODELAY on dialer to disable Nagle's algorithm
- Reduces latency by sending small packets immediately
- Critical for low TTFT (time-to-first-token) under load

**Tests added:**
- TestTCPBackpressure: Verifies TCP backpressure handling with slow client
- TestConcurrentSSEStreams: Confirms HTTP/2 multiplexing works correctly

---------

Co-authored-by: poison <[email protected]>
Reviewed-on: #26
Co-authored-by: poimen <[email protected]>
2026-09-13 23:37:33 +00:00
rock 8177f8b92f feat(proxy): add /auth/exchange token exchange endpoint
Closes homelab#10 (P3.5)

## Endpoint

`POST /auth/exchange` — RFC 8693-inspired token exchange.

## Flow

1. Validate `subject_token` (user JWT) via gateway's JWKS validator
2. Authenticate service via `client_credentials` against Authentik
3. Verify requested `scope` is subset of service's roles (deny escalation)
4. Return service token + subject identity metadata

## Request
```json
{"subject_token": "<user JWT>", "client_id": "portfolio-agent",
 "client_secret": "<secret>", "scope": "memory:read", "resource": "poimen-memory"}
```

## Response
```json
{"access_token": "<service JWT>", "subject": "user-hash",
 "acting_party": "portfolio-agent", "scope": "memory:read"}
```
2026-09-09 00:31:10 +00:00
rock 05d6321302 feat(proxy): add /auth/token and /auth/refresh endpoints (#18)
Closes homelab#6 (P3.1) and homelab#8 (P3.3)

## Endpoints

| Path | Method | Body | What it does |
|------|--------|------|-------------|
| `/auth/token` | POST | `{username, password, scope?}` | Password grant → JWT |
| `/auth/refresh` | POST | `{refresh_token, scope?}` | Refresh grant → new JWT |

Both proxy to Authentik `tokenUrl` (from P3.7 config). Upstream response forwarded verbatim — client sees Authentik errors directly.
2026-09-09 00:00:07 +00:00
rockandpoimen 97707aa2f2 feat(identity): inject X-Forwarded-User headers after JWT validation (#15)
Closes homelab#9 (P3.4)

## Changes

- New `internal/identity` package: header injection + anti-spoofing
- `proxy.go`: strip spoofed headers on all requests, inject identity after JWT validation

## Headers

| Header | Source | When |
|--------|--------|------|
| X-Forwarded-User | sub claim | Always after JWT |
| X-Forwarded-Roles | roles or permissions claim | Always after JWT |
| X-Acting-Service | azp claim | Only when azp != sub |
| X-Auth-Verified | literal "true" | Always after JWT |

## Tests

13 tests, 93.9% coverage. Covers: spoofing, service accounts, human users, empty claims, nil values, wildcard, mixed types, precedence.

---------

Co-authored-by: Poimen <[email protected]>
Reviewed-on: #15
2026-09-08 23:08:39 +00:00
Admin Bot dd9356c669 fix: sanitize JWT error to prevent JWKS URL leak in 403 response 2026-09-05 00:31:37 -07:00
Admin Bot 14cc67833c feat(auth): wire JWT validation into /v1/* LLM endpoints
CI / Vet, test, build (push) Successful in 3m52s
CI / Build and push image (push) Successful in 1m17s
2026-08-31 23:01:59 -07:00
Admin Bot f9addf945d feat(tracing): add OpenTelemetry instrumentation to API gateway
CI / Vet, test, build (push) Successful in 3m43s
CI / Build and push image (push) Successful in 1m33s
- Add internal/tracing package with OTel tracer initialization
- HTTP middleware for server-side tracing (request/response attributes)
- Transport wrapper for client-side upstream call tracing
- Update proxy to use tracing transport
- Add OTEL_* env vars to k8s deployment

Traces flow: api-gateway -> otel-collector -> tempo -> grafana
2026-08-31 15:01:56 -07:00
Admin Bot 63893d41a5 feat(phase3): Complete Temporal REST API Gateway with gRPC integration
Build and push / Build and push image (push) Successful in 42s
Build / Build and push image (push) Successful in 37s
CI / Test, vet, build (push) Successful in 2m27s
Phase 3: gRPC Implementation - COMPLETE ✅

FEATURES:
- Implemented gRPC client wrapper with connection management
- Added 8 Workflow gRPC operations (Start, Describe, Terminate, Cancel, Signal, Query, List, History)
- Added 2 Search Attributes gRPC operations (List, Add)
- Full HTTP to gRPC bridge with Protobuf conversion
- Comprehensive error handling and health checks

IMPLEMENTATION:
- grpc_client.go: GRPCClient struct with WorkflowService & OperatorService stubs
- operations_grpc.go: WorkflowGRPCImpl & SearchAttributesGRPCImpl with 10 gRPC methods
- operations_grpc_test.go: 12 integration tests for gRPC operations
- handler.go: Enhanced HTTP handler (550+ lines, 24 operations)
- handler_test.go: 30+ unit tests
- handler_integration_test.go: 20+ integration tests (concurrent, lifecycle, error scenarios)

TESTING:
- Total: 60+ tests ✅
- Pass Rate: 100% ✅
- Execution Time: 268ms
- Coverage: All 24 Temporal operations + 3 HTTP endpoints

OPERATIONS (24 total):
- Workflow Operations: 10/10 ✅
- Activity Operations: 3/3 ✅
- Namespace Operations: 5/5 ✅
- Search Attributes: 2/2 ✅
- Task Queue: 1/1 ✅
- Cluster Operations: 3/3 ✅
- HTTP Endpoints: 3/3 ✅

DOCUMENTATION:
- TEMPORAL_USAGE.md: Complete API guide (22 KB)
- TEMPORAL_API_DESIGN_SUMMARY.md: Architecture & design decisions (12 KB)
- PHASE3_GRPC_IMPLEMENTATION.md: Implementation details (10.8 KB)
- DELIVERY_COMPLETE.md: Final project summary (comprehensive)
- PHASE3_PROGRESS.md: Phase 3 progress report
- WORKFLOWS_*.md: Workflow examples & quick start guides

BUILD & DEPLOYMENT:
- ✅ Clean build (no errors/warnings)
- ✅ Binary: 24 MB
- ✅ Dependencies: google.golang.org/grpc v1.83.1, go.temporal.io/api v1.63.5
- ✅ Ready for production deployment

ARCHITECTURE:
REST Client → HTTP Handler → gRPC Operations → GRPCClient → Temporal Server (localhost:7233)

STATUS: PRODUCTION READY ✅

All phases complete:
- Phase 1: Design & Architecture ✅ 100%
- Phase 2: HTTP Implementation ✅ 100%
- Phase 3: gRPC Integration ✅ 100%

Total deliverables: 83.5 KB code + 60+ KB documentation
2026-08-22 23:17:12 -07:00
Story Crater Bot b6767e247c fix(deps,ci): update module path to forgejo.riotpiao.com/rock/homelab-frontend, switch to GITHUB_TOKEN
Build / Build and push image (push) Failing after 12s
CI / Test, vet, build (push) Successful in 2m18s
2026-08-21 20:46:45 -07:00
Story Crater Bot a8dfd5b2f0 feat(phase2): complete openai api surfaces 2.3-2.7
- 2.3: unknown model errors (400 + RFC 9457 problem+json with valid_models)
- 2.5: GET /v1/models endpoint (derived from config, not hardcoded)
- 2.6: POST /v1/embeddings passthrough (body-based dispatch, no rewrite)
- 2.7: POST /v1/rerank with path rewrite (/v1/rerank → /rerank)
- wire proxy.Handler in main.go (was using dummy handler)
- 140+ tests passing, race detector clean
- all requests: client → nginx → gateway → upstreams
- ready for config deployment to go live
2026-08-19 23:49:11 -07:00
Story Crater BotandClaude Opus 5 058f11cf2b chore: initial commit of Go API gateway
CI / Test (push) Canceled after 0s
CI / Vet (push) Canceled after 0s
CI / Build (push) Canceled after 0s
CI / Security (govulncheck) (push) Canceled after 0s
Baseline for the Kong replacement on api.riotpiao.com. Brings the working
tree under version control for the first time: gateway source, the task
board that drives the agent runs, test fixtures, and K8s manifests.

Anchor the gateway ignore rule to the repo root. Unanchored, "gateway"
also matched the cmd/gateway/ source directory, so the program entrypoint
was excluded from every commit.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-19 20:54:34 -07:00