Skip to content

feat(hosted): add use_kv_cache, fix estimator type and non-finite JSON - #372

Merged
safaricd merged 4 commits into
mainfrom
hosted-estimator-extensions-fix
Sep 1, 2026
Merged

feat(hosted): add use_kv_cache, fix estimator type and non-finite JSON#372
safaricd merged 4 commits into
mainfrom
hosted-estimator-extensions-fix

Conversation

@safaricd

@safaricd safaricd commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Changes

  • use_kv_cache (off by default). A fitted estimator re-shipped the training set on every predict*, so the endpoint's KV cache was unreachable unless the caller threaded model_id through by hand, and SHAP / permutation importance / partial dependence paid a full re-fit per call. Ported from the sagemaker and foundry backends, which already carry it. Measured on a container: 0.51s -> 0.06s per predict, 8.6s -> 1.4s for permutation_importance, identical output.
  • Evicted ids no longer break a fitted estimator. The endpoint's cache is bounded, so a model_id can disappear between predicts; a 404 now falls back to the training data we still hold.
  • Base-class order. (_HostedBase, ClassifierMixin) let BaseEstimator's tags win, leaving estimator_type unset, so is_classifier() was False and sklearn's partial dependence refused the estimator. Now ordered as tabpfn_client/estimator.py already does it.
  • Non-finite JSON. httpx's json= rejects non-finite floats, so missing values could not cross the JSON path at all and Parquet was the only option for them. json.dumps writes the literals Python's own decoder reads back; output for finite data is unchanged.
  • Tests for each: cache on/off, implied fit_mode, refit invalidation, eviction fallback, the sklearn contract, and non-finite payloads.

Reviewer notes

  • Scoped to hosted/ only. sagemaker and foundry share the base-class ordering bug and are deliberately left alone.
  • Behaviour change: is_classifier() now returning True flips sklearn's default CV splitter for classifiers from KFold to StratifiedKFold, so existing cross_val_score / SFS numbers will shift. More correct, but not reproducible against earlier runs.
  • use_kv_cache helps many-predicts-per-fit workloads and costs a little in clone-heavy ones (SFS builds a cache per clone and uses it once).
  • 241 unit tests pass; verified end to end against a self-hosted container on an H100.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AsHuhQhqWz3EkVVtPbdBBa

## Changes

- **`use_kv_cache` (off by default).** A fitted estimator re-shipped the
  training set on every `predict*`, so the endpoint's KV cache was
  unreachable unless the caller threaded `model_id` through by hand, and
  SHAP / permutation importance / partial dependence paid a full re-fit per
  call. Ported from the `sagemaker` and `foundry` backends, which already
  carry it. Measured on a container: 0.51s -> 0.06s per predict, 8.6s ->
  1.4s for `permutation_importance`, identical output.
- **Evicted ids no longer break a fitted estimator.** The endpoint's cache
  is bounded, so a `model_id` can disappear between predicts; a 404 now
  falls back to the training data we still hold.
- **Base-class order.** `(_HostedBase, ClassifierMixin)` let
  `BaseEstimator`'s tags win, leaving `estimator_type` unset, so
  `is_classifier()` was False and sklearn's partial dependence refused the
  estimator. Now ordered as `tabpfn_client/estimator.py` already does it.
  This changes the default CV splitter for classifiers to StratifiedKFold.
- **Non-finite JSON.** httpx's `json=` rejects non-finite floats, so missing
  values could not cross the JSON path at all and Parquet was the only
  option for them. `json.dumps` writes the literals Python's own decoder
  reads back; output for finite data is unchanged.
- **Tests** for each: cache on/off, implied `fit_mode`, refit invalidation,
  eviction fallback, the sklearn contract, and non-finite payloads.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AsHuhQhqWz3EkVVtPbdBBa
safaricd and others added 3 commits September 1, 2026 11:28
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AsHuhQhqWz3EkVVtPbdBBa
sklearn's `clone` is typed with overloads returning a union, so the
attribute access tripped trunk's basedpyright check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AsHuhQhqWz3EkVVtPbdBBa
The `sagemaker` and `foundry` backends leave an evicted cache entry to the
caller, and the endpoint's own 404 already says how to recover. Silently
re-fitting also hid a large latency cost behind a flag sold as a
performance option, so drop it for parity and document the consequence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AsHuhQhqWz3EkVVtPbdBBa
@safaricd
safaricd merged commit 98197b7 into main Sep 1, 2026
11 checks passed
@safaricd
safaricd deleted the hosted-estimator-extensions-fix branch September 1, 2026 12:16
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.

2 participants