Commit Graph

3 Commits

Author SHA1 Message Date
Alpamys f9e6d20962 feat(serve): structured-output + auto-quant live (v0.33.0 Part D)
Closes #49, #53, #54.

#53 Wire --structured-output into transformers generation loop:
- New utils/structured_output.build_logits_processors(constraint, tok)
  returns a HF LogitsProcessor list. Tries outlines first (broader
  coverage), falls back to lm-format-enforcer, returns [] if neither
  installed or factory crashes — server degrades to free-form rather
  than 500 on a missing dep.
- _generate_response gains logits_processor kwarg, forwarded to
  model.generate(...). Chat-completions handler builds the processor
  list per request (cheap; per-request build keeps the descriptor
  mutable for future /v1/output_constraint endpoints) and passes it
  down. Empty list path is unchanged from v0.30.0 free-form behaviour.

#54 --auto-quant live eval loop:
- New utils/auto_quant.evaluate_candidate(name, eval_fn, prompts):
  times mean per-prompt latency, scores correctness, marks ok=False
  when any prompt crashes or score < min_correct_fraction.
- New utils/auto_quant.run_auto_quant_picker(candidate_specs, prompts,
  min_score): evaluates each candidate, calls pick_best, soft-falls-
  back to highest-scored ok candidate if no candidate clears the
  threshold so the server still binds.
- serve.py replaces the v0.30.0 deferral warning with a real picker
  run over a fixed 3-prompt set across default_candidate_order().
  Logs the picked (name, score, latency) on stdout.

#49 End-to-end --push-as integration test (mocked HF):
- New tests/test_part_d.py::TestPushAsResumeIntegration uses a fake
  huggingface_hub module via patch.dict to verify HFPushCallback
  constructs cleanly with a token, exposes the _repo_failed sticky
  flag (v0.29.0 review fix), and that prepare_hf_resume rejects
  output_dir outside cwd. The full HF Hub network roundtrip needs a
  paid sandbox repo — keeping it mocked-only is a deliberate trade
  (prevents flaky CI on rate limits / token rotation).

Tests: +15 in tests/test_part_d.py covering build_logits_processors
graceful-degrade paths (None / off / unknown / no-libs / factory
crash), generate_response logits_processor plumbing, evaluate_candidate
(empty / all-correct / crash / below-threshold), run_auto_quant_picker
(threshold pass + soft fallback), HF push smoke. One existing test in
test_inference_advanced.py updated: TestAutoQuantCLIWarning no longer
expects the v0.30.1 deferral message — it now expects --auto-quant to
actually run.

Known limitations:
- #49: full HF Hub roundtrip is mocked-only; live integration test
  requires a paid sandbox repo and rotating token, deferred to a
  separate end-to-end CI job.
- #54: live re-loading of the model at the picked quant is NOT done
  in this commit — the picker logs the choice but the already-loaded
  model is served. Live re-load needs an additional bnb / awq round-
  trip per candidate, which is heavy for a startup-time decision;
  follow-up tracked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:43:07 +05:00
Alpamys 4faf5fc933 test(inference): skip 3 CLI tests when FastAPI is absent
The ubuntu-latest py3.11 matrix cell doesn't install the [serve] extra,
so `soup serve` exits early with a FastAPI-missing message before
reaching --structured-output / --auto-quant / --json-schema validation.
Add `pytest.importorskip("fastapi")` to the three tests that exercise
those CLI-level validation paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 23:47:19 +05:00
Alpamys e509614a9f feat(inference): v0.30.0 — Inference Excellence
- Prefix caching (--prefix-cache) wired to vLLM enable_prefix_caching.
- Speculative decoding auto-pairing (--auto-spec) with curated target→draft
  map for Llama 3.1/3.3/4, Qwen 2.5/3, Mistral Large, Mixtral, DeepSeek
  V3/R1, Gemma 2/3. Targets without a known draft (≤8B) fall through.
- Dynamic LoRA hot-swap: POST /v1/adapters/activate/{name} + /deactivate.
  Name regex enforced by FastAPI Path(pattern=…); active state protected
  by threading.Lock; CORS restricted to loopback origins (hot-swap mutates
  state without auth).
- Structured output: --structured-output json|regex with --json-schema
  (cwd-confined via is_under_cwd; 64KB cap; top-level type required) and
  --regex-pattern (2048-char cap, null-byte reject, must compile).
  Constrained token sampling deferred to v0.30.1; constraint descriptor
  exposed on app.state.
- Continuous-batching dashboard: --dashboard + /metrics endpoint. Thread
  safe ServerMetrics with bounded deque for latencies; record_latency
  runs in finally so failure paths are not a blind spot.
- OpenTelemetry tracing: --trace --trace-endpoint. OTLP endpoint SSRF
  hardened matching v0.29.0 HF_ENDPOINT (scheme allowlist, 0.0.0.0
  rejected, RFC1918/link-local/cloud-metadata via ipaddress.ip_address,
  plain HTTP loopback-only). build_tracer idempotent — only installs
  provider when current is ProxyTracerProvider/NoOpTracerProvider. Span
  context via contextlib.ExitStack so __exit__ receives real exc info.
- Auto-quant picker API: --auto-quant flag + Candidate dataclass +
  pick_best() shipped (generator-safe, first-wins tie-break, matches
  v0.28.0 kernel_picker precedent). Live eval loop deferred to v0.30.1;
  flag prints a yellow deferral warning so it is never a silent no-op.

Tests: 2801 → 2886 (+85). New tests/test_inference_advanced.py covers
all 7 parts plus review-driven negatives: Llama 3.3/4 pairing, Mistral
Large, DeepSeek V3/R1, Gemma 3 targets; /v1/adapters/deactivate;
activate-when-no-adapters → 404; metrics concurrent track_request;
OTLP private-IP / 0.0.0.0 / missing-host; pick_best empty-list and
all-failed; NaN score/latency rejection; --json-schema outside cwd;
--structured-output json without schema; --auto-quant deferral warning.

Review agents run (python-review, security-review, code-review, tdd-guide
+ manual verification-loop) and every finding addressed:
- HIGH: pick_best iterator exhaustion (materialise to list).
- HIGH: --auto-quant silent no-op (yellow warning).
- HIGH: --structured-output json silent no-op (fail-fast requires schema).
- HIGH: --json-schema path traversal (is_under_cwd containment).
- HIGH: OTel span swallowed HTTPException (ExitStack migration).
- HIGH: wildcard CORS on unauthed POST (loopback-only regex).
- MEDIUM: OTLP RFC1918/link-local rejection.
- MEDIUM: tracer provider idempotency.
- MEDIUM: record_latency in finally (no failure-path blind spot).
- LOW: active_state threading.Lock; deque replaces O(n) pop(0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 23:39:04 +05:00