Tips for Defining Tesseract APIs¶
Advanced Pydantic features¶
Warning
Pydantic V2 metadata and transformations like AfterValidator, Field, model_validator, and field_validator are generally supported for all inputs named inputs (first argument of various endpoints), and outputs of apply. They are silently stripped in all other cases (except in abstract_eval).
Tesseract uses Pydantic to define and validate endpoint signatures. Pydantic is a powerful library that allows for complex type definition and validation, but not all of its features are supported by Tesseract.
One core feature of Tesseract is that only the input and output schema for apply is user-specified, while all other endpoint schemas are inferred from them, which cannot preserve all features of the original schema.
Tesseract supports almost all Pydantic features for endpoint inputs named inputs (that is, the first argument to apply, jacobian, jacobian_vector_product, vector_jacobian_product):
class InputSchema(BaseModel):
# ✅ Field metadata + validators
field: int = Field(..., description="Field description", ge=0, le=10)
# ✅ Nested models
nested: NestedModel
# ✅ Default values
default: int = 10
# ✅ Union types
union: Union[int, str]
another_union: int | str
# ✅ Generic containers
list_of_ints: List[int]
dict_of_strs: Dict[str, str]
# ✅ Field validators
validated_field: Annotated[int, AfterValidator(my_validator)]
# ✅ Model validators
@model_validator
def check_something(self):
if self.field > 10:
raise ValueError("Field must be less than 10")
return self
# ❌ Recursive models, will raise a build error
itsame: "InputSchema"
# ❌ Custom types with __get_pydantic_core_schema__, will raise runtime errors
custom: CustomType
Note
In case you run into issues with Pydantic features not listed here, please open an issue.
Building Tesseracts with private dependencies¶
Tesseracts can depend on packages that are not publicly available: private git repositories, authenticated package indices, or internal wheels. There are three ways to supply these at build time, depending on how the dependency is fetched.
Private git repositories over SSH¶
For dependencies pulled over SSH (e.g. git+ssh://git@github.com/org/repo.git),
forward your SSH agent to the build with --forward-ssh-agent:
$ tesseract build . --forward-ssh-agent
The agent is mounted into the build only for the dependency install step, so your keys are never written into an image layer.
Authenticated hosts over HTTPS¶
For anything fetched over HTTPS that needs credentials (a private package index,
a pkg @ https://host/...whl direct reference, or a git+https://host/...
dependency), declare the host under build_config.host_credentials and supply the
token out-of-band at build time.
Credentials are keyed by host, i.e., the domain name alone without any path. A single entry therefore authenticates every request to that host during the build (all indices, wheels, and repositories served from it), so you cannot pair different tokens with different paths on the same host.
# tesseract_config.yaml
build_config:
host_credentials:
- host: pkgs.dev.azure.com
secret_id: azure_token # matches the --secret id below
$ tesseract build . --secret id=azure_token,env=AZURE_TOKEN
The --secret value follows BuildKit’s secret syntax:
id=<id>,env=<VAR> reads the token from an environment variable, and
id=<id>,src=<file> reads it from a file. The id must match the secret_id
of an entry in host_credentials. The token is mounted into the build as a secret
and assembled into netrc and git credential entries on an in-memory mount for the
install step, so it never lands in the config, the build context, an image layer,
or the build cache. On shared machines, prefer src=<file> over env=<VAR>, since
an environment variable is visible to other processes of the same user.
By default the credential uses the username __token__, which works for
personal-access-token style feeds. Set username on the entry for hosts that
require a real username.
Warning
Do not put tokens directly in tesseract_config.yaml or tesseract_requirements.txt.
Both are part of the committed source and the build context. Always pass credentials
through --secret.
Vendoring a dependency¶
If you would rather not authenticate at build time at all, use pip download to
fetch a dependency on the build machine and add it as a
local dependency.
Tuning dependency resolution¶
To pass options to the package resolver during the build, set build_config.build_env.
These environment variables apply only to the build stage, not the final image, which
makes them a good fit for uv resolver settings.
For example, when drawing from multiple indices that share package names, let uv pick
the best match across all of them:
# tesseract_config.yaml
build_config:
build_env:
UV_INDEX_STRATEGY: unsafe-best-match
Warning
build_env values are written into the build context, so they must not contain
secrets. Use host_credentials and --secret for credentials.
Setting environment variables¶
Use the top-level env field in tesseract_config.yaml to set environment variables in the container. These are baked into the Docker image as ENV directives and available at both build time (during RUN steps after injection) and runtime.
# tesseract_config.yaml
env:
XLA_PYTHON_CLIENT_PREALLOCATE: "false"
OMP_NUM_THREADS: "4"
This is useful for tuning framework behavior (e.g., JAX memory allocation, OpenMP thread counts) without modifying your code. You can also override or extend these at runtime with tesseract serve --env or tesseract run --env.
Warning
env values are baked into the image and visible via docker inspect, so they must not contain secrets. For build-time credentials use host_credentials with --secret. For build-only, non-secret settings use build_config.build_env (see Tuning dependency resolution).
Customizing the build process¶
The build_config section of tesseract_config.yaml controls how the Tesseract image is built. Common reasons to customize it:
Your code needs system libraries (e.g.,
gfortran,libgomp1) — useextra_packagesto install them viaapt-get.You need a specific Python version or GPU drivers — override
base_image(must be Debian-based).You’re deploying to a different architecture (e.g., ARM64 on AWS Graviton) — set
target_platform.Your Tesseract needs data files at runtime (model weights, config files) — use
package_datato copy them into the image.None of the above cover your case — use
custom_build_stepsto inject arbitrary Dockerfile commands. See the Dockerfile template for where these are injected.
See also
For the full list of options and their defaults, see the Configuration reference.
For worked examples, see the Package Data, Pyvista on ARM64, and Fortran Integration building blocks.
Creating a Tesseract from a Python package¶
Sometimes it is useful to create a Tesseract from an already-existing
Python package. In order to do so, you can run tesseract init in the root folder of
your package (i.e., where setup.py and requirements.txt would be). Import your package
as needed in tesseract_api.py, and specify the dependencies you need at runtime in
tesseract_requirements.txt.