Skip to content

Scientific providers and SDK adapters v1

Apizr 0.4.6

These contracts are implemented in Apizr 0.4.6. The official built-in recognizers ship in the core and remain active by default. Explicit selection reports exact versions, ownership, limits and diagnostics. Runs retain that inventory through their digest-bound Plans; historical records remain readable without a provider.

Static recognition and in-process observation have different authority. A static provider sees bounded source and returns candidates. An SDK adapter receives an explicitly selected object inside the user's existing scientific process and reports selected observations through the tracking SDK. Neither owns the evidence store, experiment digests or publication.

flowchart LR
    S[Core admitted source] --> V[Bounded source and lexical view]
    V --> P[Static provider]
    P --> A[Core result admission]
    A --> E[Canonical experiment evidence]
    W[Existing scientific process] --> D[Explicit SDK adapter]
    D --> R[Bounded SDK reporter]
    R --> T[Ordinary tracking session]
    T --> E
    E --> C[Core digests and immutable store]

Static contract and source authority

apizr.scientific_providers exports ProviderDescriptor, ProviderRequest, ProviderResult and the structural StaticProvider protocol. A provider declares a stable lowercase identity, an opaque bounded implementation version and one or more capabilities. A version identifies the producer implementation; it is not proof that a scientific distribution was installed or executed.

Capability Existing result type Meaning
metric_signals MetricDiscoveryResult Located lexical metric-call signals, without metric values
output_candidates OutputDiscoveryResult Located output declarations, without observed file bytes
randomness_controls RandomnessResult Static literal controls or explicit unknown values
input_reads InputResult Static input references or unknown candidates, without content observations

experiments.provider_source.SourceView admits project-relative references and UTF-8 Python source through the existing _lexical.py parser. Its digest is computed by the core from the exact UTF-8 bytes. tree() supplies a fresh bounded AST, so a provider cannot mutate a shared compiler tree. SourceLexicalVisitor reuses the existing conservative scope and alias authority; each provider supplies explicit import-name allowlists. Shadowing, star imports and unsupported generic scopes do not establish call authority. No scientific package is imported.

This v1 view deliberately reparses when producing a detached AST. Shared parsing optimization belongs to the later performance work; no global mutable cache is introduced. The source text is transient input to explicitly selected trusted recognizers, not an additional stored evidence artifact. It grants no project root, file-read, network or execution authority.

Core hard ceiling Value
Source UTF-8 bytes 1 MiB
AST nodes / depth 100,000 / 128
Aggregate returned evidence items 256, with the existing 128-control randomness ceiling
Aggregate diagnostics 512
Complete serialized result 1 MiB

ProviderLimits may lower these bounds. The request applies the declared source ceilings before recognition. Strict models reject coercions, unknown fields, incompatible schema versions, duplicate capabilities and non-finite values.

Result admission, origins and unknowns

Every result, including a result from a built-in provider, goes through admit_result(result, request=request). Constructing ProviderResult alone does not admit evidence. Admission checks producer identity and version, the exact source reference and its core-computed digest, declared capabilities, aggregate bounds and canonical evidence types. It revalidates already constructed or tampered model instances.

Static providers cannot supply runtime metrics, input digests, file sizes or observed input-content origins. Existing static metric and output signals retain static origin. Randomness and input candidates retain static or unknown as appropriate. Unknown is not a successful observation of absence and never gains an invented value. Literal controls do not prove execution, determinism, reproducibility or scientific causality. See the experiment evidence contract.

ProviderDiagnostic carries a fixed code, unknown origin, optional declared capability and ProviderLocation. The source reference is required; line and column are either both present or both unknown. Lines are one-based and columns are zero-based UTF-8 byte offsets. Admission checks all returned signal and diagnostic locations against the selected source. Locations follow Python's LF, CRLF and CR physical lines; other Unicode separators inside strings or comments do not create positions on additional source lines. Admission checks correspondence and evidence bounds, without independently proving a provider's scientific interpretation. Existing section diagnostics may have an unknown location; a claimed position must name the selected source. Raw exception messages, stderr, library objects and free-form provider messages do not enter these diagnostics. Admission failures expose only provider_result_invalid.

Out-of-tree extension protocol

The payload schemas are apizr.scientific-provider/v1, apizr.scientific-request/v1, apizr.scientific-source/v1 and apizr.scientific-result/v1. They are operation payloads inside the existing apizr.extension/v1 envelope, not a second plugin protocol or loader.

Existing envelope operation Operation-specific payload
scientific.describe Empty arguments; a static ProviderDescriptor or SDKAdapterDescriptor result, distinguished by its schema
scientific.recognize extension_arguments(request); a ProviderResult result

The caller chooses an already installed, explicitly approved extension through plugins.local.run_extension, then calls admit_extension_response(response, request=request). Low-level explicitly trusted callers may use extension_runtime.invoke_extension. Request ids, operation correlation, finite JSON, output bounds, deadlines, cancellation and process cleanup remain the existing runtime's responsibility. Choose envelope request limits large enough for the serialized source and descriptor, and output limits no larger than the admitted result ceiling. No install, discovery or activation occurs on import or during admission.

An in-process provider is trusted core code; AST bounds do not stop it from looping. An out-of-tree process uses existing supervision, which is not a sandbox. Extensions run with the user's permissions. Selection, precedence and diagnostics for unavailable or incompatible providers belong to the separate provider selection work; these contracts add no implicit fallback.

In-process adapter contract

SDKAdapterDescriptor has the separate schema apizr.scientific-adapter/v1 and declares parameter, metric, input and/or artifact reporting capabilities. Its observation mode is exactly explicit-in-process-values. SDKAdapter.observe receives one explicitly supplied object and the structural SDKReporter surface. Adapters may import the scientific library in that existing workload environment. They choose library-supported observations; the core never traverses the object, pickles it, calls its representation or searches notebook namespaces.

from apizr import tracking
from apizr.scientific_providers import ProducerIdentity, SDKAdapterDescriptor
from apizr.tracking.adapters import AdapterReporter

reporter = AdapterReporter(
    SDKAdapterDescriptor(
        identity=ProducerIdentity(name="example.adapter", version="1.0"),
        capabilities=("metric",),
    )
)
with tracking.session(root=project_root):
    reporter.log_metric("accuracy", explicitly_selected_score)

The reporter exposes only the SDK's four logging calls. It has no store, project root, Run creation or finalize method. Undeclared reporting capabilities are refused. Each call uses the current ordinary session, including context ownership, runner/serving suppression, item and capture bounds, failed-capture behavior and experiments.values validation. Recursive JSON remains bounded to depth 16, 4,096 nodes and 64 KiB per value, with finite numbers and no arbitrary objects. The session retains its 256-item section bounds and 1 MiB aggregate capture bound. Only the core fingerprints explicitly selected input/output files and publishes the completed canonical record.

Optional adapters are separately packaged extensions. Their metadata can use scientific.describe under the existing extension lifecycle. The user explicitly imports and invokes their observation code in the scientific process; an isolated extension child cannot inspect live in-memory workload objects. The compiler never imports that code, and this contract introduces no in-process entrypoint discovery. A narrow reporter is an API boundary, not a security sandbox for trusted Python code. Producer inventory in stored Runs is subsequent selection work.

Ownership and qualification

experiments.provider_source owns source admission and the supported lexical view, including the supported bounded parser, limits and parse-limit diagnostic. scientific_providers/contracts owns descriptors, requests, untrusted candidate results and admission over canonical evidence. tracking.adapters owns the reporter delegating to the ordinary SDK. plugins/* continues to own installation, activation and lifecycle; extension_runtime continues to own the process envelope. The dependency inventory declares these edges without moving plugin management into an optional scientific SDK or allowing evidence-store publication by providers.

Official built-in provider

scientific_providers/builtin owns all existing library-specific recognition as whole units: sklearn metric occurrences, joblib dump candidates, pandas/numpy/joblib literal input reads, seed/random-state controls and scientific import candidates. The occurrence-location observers move with those rules so notebook cells keep their original positions. The provider uses the core's public lexical view and canonical result types without importing scientific packages, reading candidate files or executing workload code.

BuiltinProvider implements StaticProvider with identity apizr.builtin, implementation version 1, and all four static capabilities. Its recognize method accepts a request for any declared subset and returns admitted results, enforcing the request's aggregate bounds. It rejects another producer identity. The compatibility experiments.discover_* operations delegate to the same rules and retain their historical individual limits (256 inputs, metrics and outputs; 128 randomness controls), invalid-source diagnostics and Python-encoded byte input support. They do not acquire the aggregate limits of a new combined request. Provider selection will compose these contract requests in the subsequent work.

The core retains explicit declarations and metric values, input/output byte observations, literal parameters, main guards and immutable publication. Closed v1 callable/distribution refinements stay in their canonical result models. Filename suffix hints stay with generic input selection: a .npy or .joblib hint describes a file format without recognizing or importing a library API.

Ten byte-for-byte JSON/text golden artifacts were frozen before extraction at 62f3ec7b and retained under tests/fixtures/experiments/builtin-baseline, with their sizes and SHA-256 manifest. Relocated Python/notebook fixtures and the fraud-detection example must reproduce those exact bytes. The existing coverage gate includes every module under both experiments/ and scientific_providers/: moving the rules does not remove their 100% line / 95% branch requirement.

The contract tests exercise canonical type reuse, source/identity mismatches, tampered instances, strict capabilities, UTF-8 and AST limits, aggregate limits, located unknown diagnostics and the existing extension envelope. Adapter tests exercise ordinary publication, failed non-finite/oversized values and opaque objects. scripts/scientific_contract_proof.py also runs with the installed candidate wheel from an external directory in the existing wheel qualification. It proves contract admission, SDK reporting and default built-in inspection with all four capabilities, without a scientific-library integration or extension selection policy.