Skip to main content

pydantic-open-inference

image image image Actions status uv Ruff Mypy

What is it?

Provides a client-side python interface for calling open inference servers. Define pydantic models for the model inputs and outputs, and it gets automatically converted to and from the open inference protocol, removing the need for boilerplate coding handling raw tensors and their shapes.

Features

  • ✅ Simple, type-safe API toward an open inference server using user-defined pydantic models
  • ✅ Automatic shape inference from pydantic models
  • ✅ Automatic datatype mapping (int→INT64, float→FP32, str→BYTES)
  • ✅ Datatype override for fine-grained control of model inputs
  • ✅ Automatically request only a subset of model outputs by omitting fields in your pydantic model
  • ✅ Take advantage of the full power of pydantic to validate and type-coerce your model calls
  • ✅ Multi-input/output models - each input/output maps to a pydantic field
  • ✅ Custom timeout configuration - configurable per model
  • ✅ Support calling inference for versioned and unversioned models
  • ✅ Error handling with custom exceptions - handle, e.g., specific error codes from the inference server differently
  • ✅ Fully typed - use, e.g., mypy and its pydantic plugin to type check your code
  • ✅ Thread safe
  • ✅ Async support
  • ✅ Also supports the predecessor to open inference, the predict protocol, which is very similar.

Installation

Installation using pip:

pip install pydantic-open-inference

Adding it to your project dependencies using uv:

uv add pydantic-open-inference

How to use it

Assume we have an inference server serving an Open Inference V2 REST API at http://localhost:8080 and that the server contains a model named example_model, which has the following model inputs

name shape datatype description example data
text [1] BYTES A single string of text. ["This is a text"]

and the following model outputs:

name shape datatype description example data
entities [-1, 4] BYTES
A list of entities extracted from the text. Each entity is made up of (in order) a label (either "order-id" or "invoice-id"), a confidence score, a start index for the entity in the text, and an end index.
[["order-id", "0.9", "23", "30"],["invoice-id","0.8","34","46"]]

We then define a pydantic model for the inputs, inheriting from InputsBaseModel. Each model input name needs to correspond to a field in the pydantic model, and the type of the field needs to make sense given the shape and datatype of the input. In this case, there is just a single input named "text" that is supposed to hold a single text, so we simply define

from pydantic_open_inference import InputsBaseModel


class ExampleModelInputs(InputsBaseModel):
    text: str

In the same way, we define a pydantic model for the outputs, inheriting from OutputsBaseModel. There is one model outputs named "entities", so that will be the only field we define. There can be any number of entities (corresponding to the -1 in the shape), so we define it to be a list. A list of what? Here we have some choices. We could simply define entities: list[list[str]] and get each entity as a list of strings, e.g., ["order-id", "0.98", "12", "24"], but this is not very useful to us, so we instead define, e.g., a named tuple with ordered, named, and typed fields:

from typing import NamedTuple
from enum import StrEnum
from pydantic_open_inference import OutputsBaseModel


class Label(StrEnum):
    ORDER_ID = "order-id"
    INVOICE_ID = "invoice-id"


class Entity(NamedTuple):
    label: Label
    score: float
    start: int
    end: int


class ExampleModelOutputs(OutputsBaseModel):
    entities: list[Entity]

Note that we could also have used typing.Literal instead of enum.StrEnum, and even added further validation, e.g., that the score is between 0.0 and 1.0, and that the start index is not negative etc. Pydantic offers many possibilities, but for now, let us stick with this.

We now create a RemoteModel instance. The RemoteModel class represents a model in the inference server, in this case our "example_model". Instantiation requires the URL of the inference server, the name of the model, and the pydantic models we defined for the input and output:

from pydantic_open_inference import RemoteModel

INFERENCE_SERVER_URL = "http://localhost:8080"

example_model = RemoteModel(
    server_url=INFERENCE_SERVER_URL,
    model_name="example_model",
    inputs_model=ExampleModelInputs,
    outputs_model=ExampleModelOutputs,
)

In order to call the inference server, we simply call the RemoteModel.infer method, giving it an instance of ExampleModelInputs, and get back an instance of ExampleModelOutputs:

example_input = ExampleModelInputs(text="Your order id is 123456.")
example_output = example_model.infer(example_input)
# ExampleModelOutputs(
#    entities=[
#        Entity(label=Label.ORDER_ID, score=0.95, start=17, end=23),
#    ]
# )

We have thus not only made a perfectly type-safe and validated call to the inference server, we have also gotten the response back in a useful format! We never had to bother with handling the shape of the data, parsing it in row-major order etc, as all of that is handled under the hood!

The only time you need to think about "shape" is when defining the fields of the pydantic models. Use types like, e.g., list, tuple, and typing.NamedTuple, to type the pydantic-model fields so that they map sensibly to their corresponding model input/output. Each input/output name must match to a pydantic field and the shape must correspond to the structure of the field, e.g., list[tuple[int, int]] for shape [-1, 2], or tuple[tuple[float, float], tuple[float, float]] for shape [2, 2], etc.

The "datatype" of the model inputs is automatically set based on the innermost type of the corresponding field in the InputsBaseModel subclass, according to:

python type datatype
bool BOOL
int INT64
float FP32
str BYTES

However, you can always override the datatype of the model inputs to whatever you want using pydantic_open_inference.DatatypeOverride; see its docstring for more information. The datatypes of the model outputs are disregarded; all that matters is that the data we got back from the model can be mapped to the defined OutputsBaseModel subclass.

When an inference call to a model is made, it automatically requests only the specific outputs for which you have defined fields in your OutputsBaseModel subclass. So if you are not interested in an output, simply do not define a pydantic field for it.

Examples

See the examples directory for more examples.

Metadata

Release files for pydantic-open-inference 1.4.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 pydantic-open-inference 1.4.0
File Size Uploaded Provenance Actions
pydantic_open_inference-1.4.0.tar.gz 15.0 kB Without provenance Details for pydantic_open_inference-1.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pydantic-open-inference 1.4.0
File Interpreter ABI Platform Actions
pydantic_open_inference-1.4.0-py3-none-any.whl Python 3 none any Details for pydantic_open_inference-1.4.0-py3-none-any.whl

Total release size: 30.0 kB

Release files / pydantic_open_inference-1.4.0.tar.gz

Download URL pydantic_open_inference-1.4.0.tar.gz
Size 15.0 kB
Tags Source
SHA-256 checksum
How to use checksums
d18266b236a857a196582f9f66b57ff49889a157deafaa4d85d561ed1c362fd7
BLAKE2b-256 checksum
How to use checksums
04ecf8654e21b0510d3bbeebfc1b84a94a9e7dfd535cf47bfa60ec3e531a9e52
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / pydantic_open_inference-1.4.0-py3-none-any.whl

Download URL pydantic_open_inference-1.4.0-py3-none-any.whl
Size 15.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
023866e95194cf626d6619e97ecf7af106dbfad6377a58be1c3053b370ce7f32
BLAKE2b-256 checksum
How to use checksums
796e02fad58b714b997e6a8d7e1ddd718298cdd098e12659ef4951af8d1c303b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for pydantic-open-inference 1.4.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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