Skip to content

feat(scoring): TypeSafe (jev) zero-shot description scorer - #350

Draft
voorhs wants to merge 10 commits into
devfrom
feat/typesafe-scorer
Draft

voorhs wants to merge 10 commits into
devfrom
feat/typesafe-scorer

Conversation

@voorhs

@voorhs voorhs commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • TypeSafeDescriptionScorer (description_typesafe): zero-shot intent scoring via TypeSafe's System One model jev — one Choice over all intent descriptions (multiclass) or one Noul per description (multilabel), probabilities mapped to log/logit similarities so temperature=1 reproduces the model's distribution; non-retryable 4xx errors propagate, transient failures fall back to a uniform row.
  • New optional extra typesafe (typesafe-sdk>=0.7,<1); the SDK is imported lazily inside _create_clients, so nothing else needs it (a typesafe leg in the test-scorers CI matrix exercises the real import).
  • PydanticDiskCache extracted from StructuredOutputCache (behaviour-preserving); the new scorer caches answers in its own subdir keyed on model, base URL, questions and utterance.
  • Preset zero-shot-typesafe (HPO over question_type and temperature; a sampled choice is coerced to noul on multilabel data), advisor registration, docs.
  • chore(typing): one cast in _dump_tools/unit_dumpers.py for torch-stub drift that had made mypy red on a fresh lock.

Design: docs/superpowers/specs/2026-09-18-typesafe-zero-shot-scorer-design.md (local, gitignored).

Validation

Comparison experiment (full pipeline, hold-out, 3 seeds) in Darinochka/AutoIntent-experiments#43: jev matches description_llm/GPT-6 within 95% CIs on banking77 and hwu64 (0.830 vs 0.840, 0.870 vs 0.885 accuracy) at ≈300× lower cost, trails supervised classic-light by 2–9 points, and gives a usable OOS signal on clinc150 (0.924 accuracy, OOS recall 0.77 vs 0.77 for classic-light).

Follow-ups (not in this PR)

  • Generator should catch openai.APIStatusError (an HTTP 402 mid-run crashed the experiment's GPT-6 stage and lost ≈$17 of calls).
  • OptimizationConfig.seed is PositiveInt — rejects seed=0.
  • Both API scorers: predict inside an already-running event loop (server/notebook) fails; LLMDescriptionScorer leaks a loop per trial on 3.14; its max_per_second limiter also throttles cache hits; its prompt puts the utterance before the descriptions, defeating prefix caching.
  • Advisor treats API scorers as embedder consumers; BaseAPIDescriptionScorer extraction once the duplication settles.

Test plan

🤖 Generated with Claude Code

voorhs and others added 10 commits September 18, 2026 22:19
…ucturedOutputCache

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…peSafeDescriptionScorer

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…orer

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…h stub drift)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ilent 4xx, and weak cache keys

Post-review fixes for the zero-shot-typesafe preset:
- from_context downgrades an HPO-sampled question_type="choice" to "noul" on a
  multilabel dataset (with a warning) instead of raising, so a single bad trial
  no longer aborts Pipeline.fit(); the explicit constructor ValueError is
  unchanged. get_implicit_initialization_params() now records the effective
  question_type.
- _ask_one_sync/_ask_one_async re-raise non-retryable 4xx errors (auth, bad
  model, malformed request) via a new _is_fatal() helper instead of degrading
  them to a uniform row; 408/429 still fall back like a transient failure.
- _cache_key now hashes resolved model, resolved base URL, and the full
  question payload (question_type + descriptions + instructions), replacing
  the narrower question_type/descriptions inputs; TypeSafeAnswer/_unpack carry
  the response's model name through to the cache and the INFO log line.
- Cheap minors: _init_event_loop installs a freshly created loop via
  asyncio.set_event_loop; StructuredOutputCache.get/set(_async) skip computing
  the cache key entirely when the cache is disabled; choice fit rejects more
  than MAX_CHOICE_OPTIONS (255) descriptions up front; docstrings added to
  from_context, load, _fit_implementation, _ask_one_sync, _ask_one_async.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…, and cheap-minor fixes

- fake_response derives its "winning" option from the utterance (via
  expected_best_index) when best_index is omitted, so predict() result order
  is actually observable instead of every case landing on index 0; updated
  the tests that assumed index 0 and extended the async/sync parity test to
  assert per-utterance argmax.
- test_from_context_defaults_question_type_by_task asserts the new
  implicit-params shape; test_from_context_coerces_hpo_sampled_choice_to_noul_on_multilabel
  covers the choice->noul downgrade and its warning.
- test_fatal_status_propagates_but_transient_status_falls_back (parametrized
  over max_concurrent) covers _is_fatal: a 401-shaped error propagates instead
  of producing a uniform row, a 503-shaped one still falls back.
- test_cache_key_changes_with_model covers the widened cache key.
- test_fit_rejects_too_many_choice_options covers the new MAX_CHOICE_OPTIONS
  guard; test_failed_request_yields_uniform_row_and_warns is parametrized over
  question_type too, exercising both the softmax and sigmoid fallback paths;
  test_dump_and_load_roundtrip is parametrized over max_concurrent.
- test_create_clients_builds_real_sdk_clients exercises the real typesafe_sdk
  seam (skipped unless the extra is installed, no network call).
- test_zero_shot_typesafe_preset_on_multilabel runs the zero-shot-typesafe
  preset's full HPO loop against a multilabel dataset end to end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add "typesafe" to the test-scorers dependency-group matrix so
test_create_clients_builds_real_sdk_clients runs against the real
typesafe_sdk types (not just the fakes) on every PR; the extra is light
(httpx/pydantic).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant