OptixLog Docs
Python SDKGenerated Bindings

Type-Safety Guarantees

How the generated overloads make passing a wrong node type to a project a static error in pyright and mypy.

The primary purpose of optixlog generate is type safety: if you pass a node type to a project whose schema does not include that node type, your type checker catches it before you run the code.

The mechanism

Each generated project wrapper has ingest overloads whose data parameter is typed to only that project's node union:

_GratingCouplerLabNodes = SimulationNode | MeasurementNode
_ModulatorProgramNodes  = SimulationNode | LayoutNode


class _Project_proj_grating_7f3a:
    @overload
    def ingest(self, *, data: _GratingCouplerLabNodes, prompt: str) -> IngestResult: ...
    @overload
    def ingest(
        self, *, data: _GratingCouplerLabNodes,
        origin_nodes: Sequence[str], forward_nodes: Sequence[str],
    ) -> IngestResult: ...

OptixClient.project() is overloaded once per project Literal id:

class OptixClient:
    @overload
    def project(self, id: Literal["proj_grating_7f3a"]) -> _Project_proj_grating_7f3a: ...
    @overload
    def project(self, id: Literal["proj_modulator_22b1"]) -> _Project_proj_modulator_22b1: ...

When you write client.project(Projects.GRATING_COUPLER_LAB), the type checker sees Projects.GRATING_COUPLER_LAB: Literal["proj_grating_7f3a"], selects the matching overload, and knows the return type is _Project_proj_grating_7f3a. It then applies that wrapper's ingest overloads to validate data.

Correct call

SimulationNode is in proj_grating_7f3a's union, so this call typechecks cleanly:

from optixlog_gen import OptixClient, Projects, SimulationNode

client = OptixClient()
result = client.project(Projects.GRATING_COUPLER_LAB).ingest(
    data=SimulationNode(config={"mesh": "fine"}, solver="fdtd"),
    prompt="Simulated a grating coupler.",
)

Rejected call

LayoutNode is in the _ModulatorProgramNodes union but not in _GratingCouplerLabNodes. Passing it to GRATING_COUPLER_LAB is a static type error:

from optixlog_gen import OptixClient, Projects, LayoutNode

client = OptixClient()
client.project(Projects.GRATING_COUPLER_LAB).ingest(
    data=LayoutNode(gds_path="coupler.gds"),  # type error: not in this project's schema
    prompt="Layout for grating coupler.",
)
# pyright: reportArgumentType
# mypy:    [call-overload]

Neither pyright nor mypy will accept this call. The error is reported at the data= argument.

Why this matters

Without generated bindings you would use the raw Pipeline API, which accepts any IngestibleNode:

from optixlog.pipeline import Pipeline

pipeline = Pipeline(client)
project = pipeline.project("proj_grating_7f3a")
project.ingest(data=LayoutNode(gds_path="coupler.gds"), prompt="...")  # runtime error, not caught statically

The raw API has no per-project type knowledge, so a node/project mismatch becomes a runtime failure (the server rejects the payload). Generated bindings move the failure to type-check time.

Checker-specific error messages

Prop

Type

Both checkers are verified against the fixture tests in python-sdk-spec/tests/. You can run them with pyright or mypy on any file that imports from optixlog_gen.

Projects with a shared node type

When a node type is shared by two projects (e.g. SimulationNode is in both GRATING_COUPLER_LAB and MODULATOR_PROGRAM), it is a member of both union aliases. Passing it to either project typechecks correctly:

# Both of these are valid.
client.project(Projects.GRATING_COUPLER_LAB).ingest(
    data=SimulationNode(config={}, solver="fdtd"),
    prompt="Grating coupler run.",
)
client.project(Projects.MODULATOR_PROGRAM).ingest(
    data=SimulationNode(config={}, solver="eme"),
    prompt="Modulator simulation.",
)

MeasurementNode is only in GRATING_COUPLER_LAB, so passing it to MODULATOR_PROGRAM is a static error — even though SimulationNode is shared.

See Node Classes for the full type mapping from schema fields to Python annotations.

On this page