feat(hosted): add use_kv_cache, fix estimator type and non-finite JSON - #372
Merged
Conversation
## 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
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
ggprior
approved these changes
Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
use_kv_cache(off by default). A fitted estimator re-shipped the training set on everypredict*, so the endpoint's KV cache was unreachable unless the caller threadedmodel_idthrough by hand, and SHAP / permutation importance / partial dependence paid a full re-fit per call. Ported from thesagemakerandfoundrybackends, which already carry it. Measured on a container: 0.51s -> 0.06s per predict, 8.6s -> 1.4s forpermutation_importance, identical output.model_idcan disappear between predicts; a 404 now falls back to the training data we still hold.(_HostedBase, ClassifierMixin)letBaseEstimator's tags win, leavingestimator_typeunset, sois_classifier()was False and sklearn's partial dependence refused the estimator. Now ordered astabpfn_client/estimator.pyalready does it.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.dumpswrites the literals Python's own decoder reads back; output for finite data is unchanged.fit_mode, refit invalidation, eviction fallback, the sklearn contract, and non-finite payloads.Reviewer notes
hosted/only.sagemakerandfoundryshare the base-class ordering bug and are deliberately left alone.is_classifier()now returning True flips sklearn's default CV splitter for classifiers fromKFoldtoStratifiedKFold, so existingcross_val_score/ SFS numbers will shift. More correct, but not reproducible against earlier runs.use_kv_cachehelps many-predicts-per-fit workloads and costs a little in clone-heavy ones (SFS builds a cache per clone and uses it once).🤖 Generated with Claude Code
https://claude.ai/code/session_01AsHuhQhqWz3EkVVtPbdBBa