cadrumo.application.provisioning module

Typed dependency probes and CLI startup dependency provisioning.

The doctor probes in this module are read-only: each asks whether one external service or optional package extra is usable on this workstation and returns a typed DependencyStatus. The startup coordinator is the separate, explicit composition boundary that provisions application-owned directories and validates operator-owned and shipped dependencies before command dispatch.

The on-host readers are probed per role by probe_local_reader(), so a down server or an unpulled model becomes an instructive refusal instead of a raw stack trace. The aeat config check command renders this module’s statuses as CheckDependencyPayload rows beside the active profile’s capability posture from resolve_active_capability(). Optional-extra probes walk the core OPTIONAL_EXTRAS catalogue of OptionalExtra records, so CLI diagnostics and adapter import guards share one registry.

ensure_cli_startup_dependencies()[source]

Provision owned storage defaults and validate the shipped authority.

Explicit storage overrides are dependencies and are validated by the materializer rather than created. Authority admission validates the published descriptor and content-addressed SQLite generation without hydrating registry components or reaching development authoring inputs.

Return type:

None

class DependencyStatus(**data)[source]

Bases: ProvisioningOutcome

Availability result for one external dependency.

service is the stable row id shown by the configuration check. facts records the measured state and precondition_verdict closes every unavailable outcome without embedding presentation or executable text. The model is intentionally generic so Ollama, subprocess CLIs, Playwright browser binaries, and OptionalExtra package extras all render through the same payload shape and can be validated into CheckDependencyPayload.

Parameters:

data (Any)

service: str
available: bool
class SystemMemoryReading(**data)[source]

Bases: BaseModel

Measured total and free physical system memory, either of which may be unreadable.

Both figures are carried because they answer different questions and the provisioning decision separates them deliberately: total_bytes is what a diagnostic row reports about the machine, free_bytes is the only figure a load decision may act on. A machine with 64 GiB installed and 2 GiB free cannot host a 4 GiB model, and reading the total as headroom is exactly the admission error the contention check exists to prevent.

None means “not measured”, never “zero”. See AcceleratorKind for the same distinction on the device side.

Parameters:

data (Any)

total_bytes: int | None
free_bytes: int | None
read_system_memory()[source]

Measure total and free physical system memory, reporting either as None when unreadable.

Dependency-free on every supported platform: POSIX reads the sysconf page-size pair, Windows calls GlobalMemoryStatusEx through ctypes. The free counterpart costs nothing to obtain – the Windows struct already carries ullAvailPhys in the same call, and POSIX exposes SC_AVPHYS_PAGES beside the total – so both figures come from one measurement and cannot disagree about the moment they describe.

A platform that answers nothing yields None, never a guess: an unknown quantity must not be reported as a shortfall by probe_model_runtime_hardware_floor(), nor mistaken for headroom by assess_model_load_contention().

Return type:

SystemMemoryReading

read_total_system_memory_bytes()[source]

Return total physical system memory in bytes, or None when unreadable.

A narrowing of read_system_memory() for the hardware-floor diagnostic, which judges the machine’s installed capacity rather than its present headroom. Kept as the one total-only reader so there is no second platform branch to drift.

Return type:

int | None

probe_model_runtime_hardware_floor(settings=None, *, total_memory_bytes=None)[source]

Report whether this machine meets the local model runtime’s memory floor.

The third capability axis. The product already distinguishes installed (OptionalExtra) from permitted (ServiceCapability); this answers capable – and is named for the floor it measures rather than for the word “capability”, which already denotes four unrelated concepts in this tree (modelo-revision capability, terminal capability, operator service capability, optional-extra capability).

Below the floor the local runtime does not refuse. It loads and thrashes, or is killed mid-read, and the operator sees an unexplained timeout. This probe turns that into a typed row naming the shortfall and the accepted floor.

Unknown memory reports available true with a detail saying so. That is deliberate and is the safe direction for a diagnostic: an unreadable platform must not manufacture a shortfall that blocks a machine which may be perfectly adequate. The refusal is raised only on a measured shortfall.

Parameters:
Return type:

DependencyStatus

Returns:

A DependencyStatus on the model-runtime-hardware-floor row.

probe_optional_extra(extra)[source]

Probe whether an optional package extra is importable, never raising.

Wraps the core optional_extra_available() spec-only check for one OptionalExtra (no import, no side effects) in the doctor’s DependencyStatus, retaining only the extra’s machine identity when absent. The feature-boundary guard is the sibling core require_optional_extra(), which raises the typed MissingOptionalExtraError when a command actually requires the feature.

Return type:

DependencyStatus

Parameters:

extra (OptionalExtra)

class AcceleratorDevice(**data)[source]

Bases: BaseModel

One enumerated accelerator device and its measured VRAM figures.

Per-device rather than aggregated because a model loads onto ONE device: two cards with 3 GiB free each are not 6 GiB of headroom for a 4 GiB model. The aggregation rules that follow from that live on HardwareProfile.

Parameters:

data (Any)

index: NonNegativeInt
name: str
total_vram_bytes: int | None
free_vram_bytes: int | None
class AcceleratorReading(**data)[source]

Bases: BaseModel

What the accelerator measurement found: a kind, and the devices behind it.

kind is AcceleratorKind; an empty devices tuple is consistent with both NONE (measured: there are none) and UNKNOWN (not measured), which is precisely why the kind is carried separately rather than inferred from the tuple being empty.

Parameters:

data (Any)

kind: AcceleratorKind
devices: tuple[AcceleratorDevice, ...]
detail: str
class HardwareProfile(**data)[source]

Bases: BaseModel

The measured local-inference capacity of this machine.

Carries totals AND free amounts on both the system-memory and the device axis, and the two are not interchangeable: totals are for reporting, free amounts are for acting. A diagnostic row may say “16 GiB card”; a load decision may only consult free_vram_bytes. Deciding on a total is the concrete defect this model exists to prevent – the machine this was built for has a 16 GiB card with under 4 GiB free, shared with a resident search service, and a load admitted against the total takes the host down with every running process on it.

Every figure is independently optional. None means the measurement was not obtainable, and per the provisioning decision the two directions differ: diagnostic rows report an unreadable figure as unverified and stay available, while assess_model_load_contention() treats it as a refusal input. See probe_hardware_profile().

Parameters:

data (Any)

memory: SystemMemoryReading
accelerator: AcceleratorReading
property total_vram_bytes: int | None

Return summed device VRAM for REPORTING, or None when any device is unreadable.

Summed because the reporting question is “what is installed in this machine”, which is additive. Deliberately NOT the figure any load decision consults – see free_vram_bytes.

property free_vram_bytes: int | None

Return the largest single-device free VRAM for ACTING, or None when unreadable.

The maximum, never the sum: a model is resident on one device, so the headroom that decides admission is the best single device’s, and summing would manufacture capacity that no allocation can reach. Devices whose free figure is unreadable are skipped rather than counted as zero – they cannot host the load either way, and treating them as zero would not change the maximum.

None when no device reported a free figure at all, which fails closed at the act.

read_accelerator()[source]

Measure the local accelerator through NVML, never raising and never loading a model.

NVML (the nvidia-ml-py binding declared in the llm extra) is preferred over a nvidia-smi shell-out because it yields per-device total and free figures from an in-process query with no subprocess. The distinction is load-bearing for attribution: a whole-device free figure is contaminated by every process on the card, which is exactly the quantity assess_model_load_contention() must split between this runtime’s residents and peer processes – so NVML is authoritative for the free-VRAM figure, and the runtime’s own /api/ps report (see read_runtime_residents()) is authoritative for how much of it is ours.

Returns UNKNOWN when NVML is absent or uninitialisable, and NONE only on the positive reading that NVML initialised and enumerated zero devices.

Return type:

AcceleratorReading

probe_hardware_profile(*, memory=None, accelerator=None)[source]

Measure this machine into a HardwareProfile, never raising.

Both measurements are injectable arguments rather than patched module internals, so every branch of the profile and of assess_model_load_contention() is exercised against real model construction with real readings – a test must never depend on this host’s actual device state, which changes minute to minute while an agent fleet runs on it.

Parameters:
Return type:

HardwareProfile

Returns:

The composed HardwareProfile.

binding_free_bytes(profile)[source]

Return free memory in the arena that actually binds a model load, or None.

The one place the arena rule lives, shared by assess_model_load_contention() (which acts on it) and select_model_for_role() (which plans against it), so the two can never disagree about which figure binds. A device load is bound by device memory, a measured-accelerator-free machine by system memory, and an unmeasurable accelerator by nothing that may be trusted – which is None, and is why UNKNOWN does not fall through to system memory: a card this build cannot read may still be holding the memory a load needs.

Return type:

int | None

Parameters:

profile (HardwareProfile)

probe_local_inference_hardware(profile=None)[source]

Report the measured hardware profile as a diagnostic row, unknown shown as unverified.

Follows the shipped direction of probe_model_runtime_hardware_floor(): an unreadable figure keeps available true and is rendered as unverified, because a diagnostic must not manufacture a shortfall on a platform it merely cannot measure. The opposite direction governs assess_model_load_contention(), which acts rather than reports and so refuses on the same unknown.

Parameters:

profile (HardwareProfile | None) – A measured profile; probed via probe_hardware_profile() when omitted.

Return type:

DependencyStatus

Returns:

A DependencyStatus on the local-inference-hardware row.

class ModelSelection(**data)[source]

Bases: ProvisioningOutcome

Which model a role resolved to, and everything the operator should know about it.

A planning result, not an admission decision. It answers “which model should this role use on this machine”; whether that model may be loaded right now is assess_model_load_contention()’s question, asked later and against readings taken at that moment. Keeping the two apart is what lets selection stay useful on a machine whose headroom is momentarily gone.

selected false means no catalogued candidate cleared the bars, and runtime_id is then None rather than a fallback – naming a model that cannot serve the role would push the failure into inference, where it surfaces as a timeout instead of as a refusal.

advisories is never empty for an override that carries a licence, context or headroom concern. An override is honoured, because the operator’s explicit setting outranks the catalogue’s preference – but it is never honoured silently.

Parameters:

data (Any)

role: ModelRole
runtime: ModelRuntime
runtime_id: str | None
candidate: ModelCandidate | None
posture: DeploymentLicencePosture
tier: HardwareTier
binding_free_bytes: int | None
required_context_tokens: int
safety_margin_bytes: NonNegativeInt
override_applied: bool
selected: bool
advisories: tuple[ModelSelectionAdvisory, ...]
property assessable_load: tuple[str, int] | None

Return (runtime_id, required_bytes) when a load can be assessed at all.

None when this role selected nothing, or when the chosen candidate declares no memory requirement. The second case matters as much as the first and is easier to miss: an unknown requirement read as zero flows into assess_model_load_contention() as the amount the model needs, so the check reports the load ADMITTED on evidence nobody has.

Owned here because every caller was re-deriving the same three-part guard – selected, runtime id, candidate – and each one that forgot the fourth part (the requirement itself being optional) fail-opened in its own way. A caller that cannot assess should say so rather than assess against an invented number.

property licence_advisory: str

Return the localised non-commercial licence advisory, or an empty string.

Non-empty exactly when LICENCE_COMMERCIAL_USE_BARRED is present, which automatic selection can never produce – so this string appearing is itself the signal that an override reached past the commercial posture.

select_model_for_role(role, *, profile=None, settings=None, override=None, posture=DeploymentLicencePosture.COMMERCIAL, runtime=ModelRuntime.LOCAL_OLLAMA)[source]

Resolve role to the weakest catalogued model that clears every bar.

Bounded from below, never maximised. Candidates are ordered by ascending memory requirement and the FIRST survivor wins, so a machine with headroom to spare still gets the small model. A larger model is an operator’s explicit choice, which is why nothing here ranks on capability above the floor.

Three bars, applied in this order and each for a different reason:

  • Context window. A model whose window cannot hold the configured request window is excluded on capability, not preference – it cannot do the job at any price.

  • Licence. Under a COMMERCIAL posture a candidate whose publisher text bars commercial use is not eligible. This is the bar that moved the shipped default.

  • Measured headroom. Requirement plus the configured safety margin against free memory in the binding arena.

An unmeasurable machine does not refuse here, and that is deliberate rather than a softening of the fail-closed rule: refusing to name a model because headroom is momentarily unreadable would break provisioning on exactly the machines that most need to pull one, while the load itself is still failed closed by assess_model_load_contention() at the act. The selection says so with FIT_UNVERIFIED.

Parameters:
  • role (ModelRole) – The role to resolve.

  • profile (HardwareProfile | None) – Measured hardware; probed when omitted.

  • settings (Settings | None) – Settings carrying the context window and safety margin; loaded when omitted.

  • override (str | None) – An operator-named runtime id that outranks selection. Honoured even when uncatalogued or licence-barred, always with advisories.

  • posture (DeploymentLicencePosture) – The deployment’s licence posture; commercial by default, because that is what this product is.

  • runtime (ModelRuntime) – Which runtime to resolve against; on-host by default, because the on-host route is the one the product prefers and the one that keeps evidence on the operator’s machine. A hosted runtime skips the headroom bar (nothing runs here) and keeps the capability and licence bars, which apply wherever the prompt is served.

Returns:

an unsatisfiable role returns selected false carrying the measured reason and closed outcome.

Return type:

A ModelSelection. Never raises

LOCAL_MODEL_PROVISIONING_SERVICE

The doctor row id for the local-inference provisioning coherence check.

probe_local_model_provisioning(*, installed=None, installed_measured=True, settings=None)[source]

Detect a PARTIALLY-installed local-inference posture, in both directions.

Local inference has two independent halves: the llm package extra, which an operator installs with pip, and the models themselves, which an operator pulls into the runtime. Either half can be present without the other, and the two broken states are not variations of one problem – they have different causes and different remedies, so a row detecting only one strands every operator who lands in the other with no signal at all.

  • Extra present, no selected model installed. The code path is live and the model it would load is absent. The remedy is a pull.

  • A selected model installed, extra absent. Multi-gigabyte models occupy disk that nothing can use, typically after a core reinstall. The remedy is the extra’s install command – or a removal, to reclaim the disk.

The two conditions are disjoint by construction: each requires the extra state the other forbids, so neither can be satisfied by the other’s evidence and a check for one can never stand in for the other.

Coherent postures – both halves present, or neither – report available. So does the case where the inventory cannot be read while the extra is ALSO absent: nothing indicates an opted-in local-inference posture, and the detail says plainly that the inventory was not readable so the models-without-extra direction could not be ruled out. When the extra IS present, an unreadable inventory is itself reported, because an operator who opted into local inference and cannot be told what is installed has a real problem rather than an absence.

Parameters:
Return type:

DependencyStatus

Returns:

A DependencyStatus naming the direction and its remedy.

probe_optional_extras()[source]

Probe each OptionalExtra into DependencyStatus rows.

The result set is keyed by the same OPTIONAL_EXTRAS catalogue used by require_optional_extra(), keeping aeat config check and runtime feature guards aligned.

Return type:

tuple[DependencyStatus, ...]