Skip to main content

L2S1 for Python

Python 3.11+ async SDK for the resident Rust engine. Requests and responses use the same HTTP v1 field names, decision kinds, policies and evidence as @l2s1/node. The package includes Pydantic runtime validation and PEP 561 type information. l2s1-sdk 0.1.4 is published on PyPI; the import remains import l2s1.

Install

From PyPI:

pip install l2s1-sdk==0.1.4

From a source checkout:

python -m pip install ./sdks/python

From a built package:

python -m pip install ./l2s1_sdk-0.1.4-py3-none-any.whl

The wheel contains the Python SDK. Supply the Rust engine and GGUF weights separately. load() accepts binary_path, finds l2s1 on PATH, or accepts runtime_dir pointing to an extracted @l2s1/runtime-<platform> package. This reuses the TypeScript bundle's executable and matching native libraries without Node.js. Bundle version, OS/architecture, device and file checksums are checked. There are no model downloads, native builds or install scripts in the wheel.

Repeat a decision with new data

import asyncio
from pydantic import BaseModel, ConfigDict
from l2s1 import BinaryKind, BinaryValue, Decision, L2S1, LoadOptions

class Temperature(BaseModel):
    model_config = ConfigDict(strict=True)
    temperature_c: float

async def main() -> None:
    async with await L2S1.load(LoadOptions(
        model="/path/to/model.gguf",
        runtime_dir="./package",
        execution_mode="parallel",  # Native batching; transport defaults to stdio.
    )) as engine:
        plan = engine.prepare([Decision(
            id="cold",
            instruction="Is temperature_c below 10?",
            kind=BinaryKind(false_label="At least 10.", true_label="Below 10."),
        )], state_type=Temperature)

        first = await plan.decide(Temperature(temperature_c=6))
        second = await plan.decide(Temperature(temperature_c=15))
        results = await plan.decide_batch([
            Temperature(temperature_c=2), Temperature(temperature_c=20),
        ])
        result = first.results[0]
        if isinstance(result.value, BinaryValue):
            print(result.value.value)  # bool | None; preserve abstention

asyncio.run(main())

prepare() snapshots fixed definitions. Passing state_type returns PreparedDecision[Temperature]: mypy checks calls and the SDK checks the model type at runtime. Without state_type, the plan accepts JSON values. Input schema checks do not establish model correctness. Preparation here does not precompile model tokens or retain a KV prefix across requests.

load() defaults to transport="stdio": it launches the compiled Rust executable once and exchanges JSON lines through stdin/stdout. No port, HTTP server or Node.js is needed. This is a resident native process, not an in-process PyO3 extension. Use transport="http" explicitly for a local HTTP server, or connect() for a shared remote server. Both use the same typed JSON contract.

decide_batch() sends the entire array once to Rust's native parallel path. Load with execution_mode="parallel"; parallel_width controls wave width. For HTTP connections the endpoint is POST /v1/decision-batches with {"requests": [...]}. Responses preserve request order and independent state, IDs, media and policy; IDs may repeat across requests. The batch envelope advertises execution: "native_parallel". timeout_ms applies to the whole call. There is no serial fallback or automatic retry. Unsupported backends report batch_unsupported, and engines outside parallel mode report batch_not_enabled. Validate capabilities().batch before selecting optional batch features.

The native wire batch accepts at most 128 requests and 128 total decisions. Current llama.cpp parallel execution supports direct reasoning and at most 26 options per decision. A batch can be all text or use one image for every decision with a matching projector; mixing text and image groups is rejected. All wire requests are validated before inference. An execution error fails the whole batch; already executed waves are not rolled back or replayed. Separate asyncio.gather(engine.decide(...)) calls are queued individually and are not implicitly combined. See the batching review.

Connect, send existing JSON, or adapt another transport

from l2s1 import DecisionRequest, L2S1

async with L2S1.connect("http://127.0.0.1:8080") as engine:
    capabilities = await engine.capabilities()
    # payload can be the exact JSON object used by the TypeScript SDK.
    request = DecisionRequest.model_validate(payload)
    response = await engine.decide(request, timeout_ms=180_000)
    payload_for_typescript = response.model_dump(exclude_unset=True)

load(), connect() and from_backend() expose the same application API. DecisionBackend is a typed protocol with async decide() and capabilities(). BatchDecisionBackend adds async decide_batch() for custom native adapters. An optional synchronous or asynchronous close() hook transfers resource ownership to the facade. L2S1Client is also available as a standalone transport.

TypeScript Python
L2S1.load({ model, binaryPath }) await L2S1.load(LoadOptions(model=..., binary_path=...))
L2S1.connect({ baseUrl }) L2S1.connect(base_url)
L2S1.fromBackend(backend) L2S1.from_backend(backend)
engine.decide(request) await engine.decide(request)
engine.prepare<State>(decisions) engine.prepare(decisions, state_type=State)
plan.decide(state) await plan.decide(state)
plan.decideBatch(states) await plan.decide_batch(states)
engine.decideBatch(requests) await engine.decide_batch(requests)
close() / async disposal await close() / async with
AbortSignal asyncio task cancellation

Both SDKs preserve false, null selections, ordered result IDs and model_scored versus selection_only evidence. Python uses snake case for application methods and options; JSON field names are unchanged. Use model_dump(exclude_unset=True) for round trips so absent extension fields stay absent. Decision-kind discriminator defaults are always serialized.

Inspect capabilities() before selecting image, reasoning or request-policy extensions. Unsupported controls are never silently dropped. Model scores and target_error_rate are not correctness guarantees. L2S1Error retains server code, status, request_id and user_reason; network errors and task cancellation retain their HTTPX/asyncio exceptions. There are no automatic retries.

close() is idempotent. Closing an HTTP client cancels its local HTTP tasks and leaves the remote server running. Closing an owned engine terminates and waits for the Rust child. Cancelling or timing out a call does not guarantee that Rust inference stopped. Local stdio startup opens no socket. Explicit HTTP startup uses an OS-selected loopback port. Native diagnostic output is continuously drained. The stdio client admits at most 16 outstanding calls; timed-out calls occupy their slot until the native reply arrives.

Build and verify

python -m pip install './sdks/python[dev]'
python -m mypy --config-file sdks/python/pyproject.toml sdks/python/src sdks/python/examples sdks/python/tests/typecheck.py
python -m unittest discover -s sdks/python/tests -v
python -m build sdks/python

# Actual Rust stdio/HTTP/scoring boundary and Python ↔ TypeScript JSON equality:
cargo build --locked --example typescript_fixture
npm --prefix sdks/typescript ci
npm --prefix sdks/typescript run build
L2S1_TEST_BINARY="$PWD/target/debug/examples/typescript_fixture" \
  python -m unittest discover -s sdks/python/tests -v

On Windows use typescript_fixture.exe. The Rust fixture exercises resident process lifetime, stdio/HTTP batch validation and scoring without a model; it is not native GGUF, CUDA or Metal quality/performance evidence. The CI matrix targets Python 3.11 and 3.14 on Linux, macOS and Windows; local verification covers only the actual host. The release pipeline publishes validated wheel/sdist artifacts to PyPI.

The release pipeline publishes validated GitHub Release, npm, PyPI and native Cargo packages using configured trusted publishers. v0.1.4 is available.

Resident prefix reuse

Compatible resident text servers (--listen / --stdio, including SDK load) automatically use bounded fixed-schema prefix KV reuse when no execution mode is specified. One-shot CLI calls and the low-level Rust backend retain fresh. Explicit execution modes, vision/projector settings, calibration/output heads and compact evidence preserve their existing paths; recurrent/hybrid models fall back to fresh with a startup message. Use --execution-mode fresh to opt out, or --fixed-schema to require support. Native parallel batching still requires --execution-mode parallel. This changes the split plan and can change scores; check capabilities and usage.reused_prefix_tokens.

Omit fixed_schema to inherit the runtime default. Set it to true to require support, or false to select fresh when no execution mode is specified. An explicit execution mode takes precedence over false.

This default is available in v0.1.4. Published v0.1.3 requires explicit fixedSchema: true / fixed_schema=True.

Metadata

Release files for l2s1-sdk 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for l2s1-sdk 0.2.0
File Size Uploaded
l2s1_sdk-0.2.0.tar.gz 27.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for l2s1-sdk 0.2.0
File Interpreter ABI Platform
l2s1_sdk-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 47.3 kB

Release files / l2s1_sdk-0.2.0.tar.gz

Download URL l2s1_sdk-0.2.0.tar.gz
Size 27.1 kB
Tags Source
SHA-256 checksum
How to use checksums
3db4d0aff7265eabf7f2f981da61c31d7584a317ecefa1b36c6b3672de5837d2
BLAKE2b-256 checksum
How to use checksums
7a79a6333613bbfe60913cd51d3f08153cff0a22c4c4b857fa41818b2108b3cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / l2s1_sdk-0.2.0-py3-none-any.whl

Download URL l2s1_sdk-0.2.0-py3-none-any.whl
Size 20.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
08a7dbbe8f1a71e487a1b8dbb1251526956a04818986a5e557611697cbbc9ade
BLAKE2b-256 checksum
How to use checksums
f6145daf91897439a76a6db50d2752aa5ac789f1afbb415045ee6337e59b8b87
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

This release

0.2.0 This release

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page