core.inference_policy¶
Versioned inference-policy artifact: model + calibration + reject threshold.
Overview¶
The InferencePolicy is the canonical deployment descriptor for
inference. It binds a specific model bundle to an optional calibrator
store and a reject threshold that was tuned on the (potentially
calibrated) score distribution.
model bundle + calibrator store + reject threshold → InferencePolicy
↓
inference_policy.json
↓
resolve_inference_config()
↓
OnlinePredictor / batch / tray
The policy file lives at models/inference_policy.json and is the
first thing inference resolution checks.
Canonical template (repository)¶
Unlike per-user config.toml, models/inference_policy.json is not
auto-written on first tray startup. The repository holds a stable
checked-in example at
configs/inference_policy.template.json
that matches render_default_inference_policy_template_json() (fixed
created_at / git_commit for reproducibility). See the
Inference policy template guide.
The module also exposes a low-level
write_inference_policy_starter_template() helper for code paths that
explicitly want a placeholder file with live provenance and
_help.paths_are_relative_to set to your install’s TASKCLF_HOME.
Editing from the tray¶
The tray menu Advanced → Edit Inference Policy opens this file in the default
editor. If the file is missing, the tray creates it first only when a
model bundle can be resolved. In that case the seeded policy reuses the
bundle's metadata.json advisory reject_threshold and attaches a
matching calibrator store when artifacts/**/store.json explicitly
binds to the same model/schema. When no model can be resolved, the tray
does not write a placeholder file; it notifies the user to use
Prediction Model or Open Data Folder first, and includes an
optional CLI hint (taskclf policy create --model-dir models/<run_id>)
for environments where the CLI is installed. Prefer
taskclf policy create for a validated policy. Hand-edited JSON that
fails validation is ignored by load_inference_policy, so inference
falls back to active.json resolution until the file is fixed.
Resolution precedence¶
When inference starts:
- Explicit
--model-dirCLI override — bypasses policy entirely. models/inference_policy.json— canonical deployment descriptor.models/active.json+ code defaults — deprecated legacy fallback.- Best-model selection + code defaults — no-config fallback.
InferencePolicy fields¶
| Field | Type | Description |
|---|---|---|
policy_version |
Literal["v1"] |
Schema version of the policy |
model_dir |
str |
Path to model bundle, relative to models_dir.parent |
model_schema_hash |
str |
Must match the bundle's metadata.schema_hash |
model_label_set |
list[str] |
Must match the bundle's metadata.label_set |
calibrator_store_dir |
str \| None |
Path to calibrator store, relative to models_dir.parent |
calibration_method |
str \| None |
"temperature" or "isotonic" |
reject_threshold |
float |
Tuned on calibrated scores when calibrator is present |
created_at |
str |
ISO-8601 timestamp |
source |
str |
"manual", "tune-reject", "retrain", "calibrate", "tray-edit", "starter-template", etc. |
git_commit |
str |
Git commit SHA at creation time |
Functions¶
build_inference_policy¶
build_inference_policy(
*,
model_dir: str,
model_schema_hash: str,
model_label_set: list[str],
reject_threshold: float = DEFAULT_REJECT_THRESHOLD,
calibrator_store_dir: str | None = None,
calibration_method: str | None = None,
source: str = "manual",
) -> InferencePolicy
Convenience builder that fills in created_at and git_commit
automatically.
save_inference_policy¶
Atomically persist the policy as models_dir/inference_policy.json.
Uses temp-file + os.replace so readers never see a partial write.
load_inference_policy¶
Read the policy file. Returns None on missing, invalid JSON, or
validation failure (no exception raised).
remove_inference_policy¶
Delete the policy file. Returns True if removed, False if it
did not exist.
render_default_inference_policy_template_json¶
Return the canonical JSON text for configs/inference_policy.template.json
(stable timestamps for the checked-in file).
write_inference_policy_starter_template¶
Atomically write a placeholder inference_policy.json for manual editing
(same shape as the canonical template, with live created_at /
git_commit and _help.paths_are_relative_to).
validate_policy¶
Validate that the policy's references resolve to compatible artifacts
on disk. Raises PolicyValidationError on failure.
Checks:
model_direxists withmetadata.json.model_schema_hashmatches the bundle.model_label_setmatches the bundle.calibrator_store_dir(if set) exists withstore.json.- Calibrator store's
model_schema_hash(if set) matches the policy.
Usage¶
After tuning¶
taskclf train tune-reject \
--model-dir models/run_001 \
--calibrator-store artifacts/calibrator_store \
--from 2026-01-01 --to 2026-01-31 \
--write-policy
Manual creation¶
taskclf policy create \
--model-dir models/run_001 \
--calibrator-store artifacts/calibrator_store \
--reject-threshold 0.55
Inspect / remove¶
taskclf.core.inference_policy
¶
Versioned inference-policy artifact: model + calibration + reject threshold.
The :class:InferencePolicy is the canonical deployment descriptor. It
binds a specific model bundle to an optional calibrator store and a
reject threshold that was tuned on the (potentially calibrated) score
distribution.
Persistence uses the same atomic-write pattern as
:func:~taskclf.model_registry.write_active_atomic: write to a
temporary file, then :func:os.replace so readers never see a partial
write.
InferencePolicy
¶
Bases: BaseModel
Versioned deployment descriptor binding model + calibration + threshold.
Stored as models/inference_policy.json. Inference resolution
reads this file to determine which model bundle, calibrator store,
and reject threshold to use.
All paths are relative to models_dir.parent (i.e. relative
to TASKCLF_HOME), matching the convention used by
:class:~taskclf.model_registry.ActivePointer.
Source code in src/taskclf/core/inference_policy.py
PolicyValidationError
¶
Bases: Exception
Raised when an inference policy fails validation against disk artifacts.
Source code in src/taskclf/core/inference_policy.py
build_inference_policy(*, model_dir, model_schema_hash, model_label_set, reject_threshold=DEFAULT_REJECT_THRESHOLD, calibrator_store_dir=None, calibration_method=None, source='manual')
¶
Convenience builder that fills in provenance fields automatically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_dir
|
str
|
Path to model bundle, relative to |
required |
model_schema_hash
|
str
|
Schema hash from the model's metadata. |
required |
model_label_set
|
list[str]
|
Label set from the model's metadata. |
required |
reject_threshold
|
float
|
Reject threshold for this model+calibration pair. |
DEFAULT_REJECT_THRESHOLD
|
calibrator_store_dir
|
str | None
|
Path to calibrator store directory,
relative to |
None
|
calibration_method
|
str | None
|
|
None
|
source
|
str
|
How this policy was created ( |
'manual'
|
Returns:
| Type | Description |
|---|---|
InferencePolicy
|
A populated :class: |
Source code in src/taskclf/core/inference_policy.py
render_default_inference_policy_template_json()
¶
Return the canonical checked-in starter JSON text for configs/.
Uses stable created_at / git_commit so the file matches the
repository template byte-for-byte. Runtime starter files written by
:func:write_inference_policy_starter_template use live provenance.
Source code in src/taskclf/core/inference_policy.py
write_inference_policy_starter_template(models_dir)
¶
Atomically write a placeholder inference_policy.json for manual editing.
Same content shape as :func:render_default_inference_policy_template_json,
but with live created_at / git_commit and
_help.paths_are_relative_to set to str(models_dir.parent).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
models_dir
|
Path
|
The |
required |
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written policy file. |
Source code in src/taskclf/core/inference_policy.py
save_inference_policy(policy, models_dir)
¶
Atomically persist policy as models_dir/inference_policy.json.
Uses temp-file + :func:os.replace so readers never see a partial
write.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
policy
|
InferencePolicy
|
The policy to persist. |
required |
models_dir
|
Path
|
The |
required |
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written policy file. |
Source code in src/taskclf/core/inference_policy.py
load_inference_policy(models_dir)
¶
Load the inference policy from models_dir/inference_policy.json.
Returns None (without raising) when the file is missing, contains
invalid JSON, or fails validation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
models_dir
|
Path
|
The |
required |
Returns:
| Type | Description |
|---|---|
InferencePolicy | None
|
A validated :class: |
Source code in src/taskclf/core/inference_policy.py
remove_inference_policy(models_dir)
¶
Delete models_dir/inference_policy.json if it exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
models_dir
|
Path
|
The |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/taskclf/core/inference_policy.py
validate_policy(policy, models_dir)
¶
Validate that policy references artifacts that exist and are compatible.
Checks:
model_dirresolves to an existing directory withmetadata.json.model_schema_hashmatches the bundle's recorded schema hash.model_label_setmatches the bundle's recorded label set.- If
calibrator_store_diris set, it resolves to an existing directory withstore.json. - If the calibrator store has model-binding metadata, it matches the policy's model binding.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
policy
|
InferencePolicy
|
The policy to validate. |
required |
models_dir
|
Path
|
The |
required |
Raises:
| Type | Description |
|---|---|
PolicyValidationError
|
When any check fails. |
Source code in src/taskclf/core/inference_policy.py
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | |