Installing local Python modules into a Tesseract

Context

Sometimes it might be necessary to bundle local Python modules into a Tesseract.

There are 2 ways to do this:

  1. Make them a proper Python package with pyproject.toml and add the local path to the tesseract_requirements.txt file. Both absolute and relative paths work, but in case they are relative paths, they should be relative to the Tesseract’s root folder (i.e., the one which contains the tesseract_api.py file).

  2. Just put them as .py files next to tesseract_api.py and add them to build_config.package_data (see also [packagedata.md]) in tesseract_config.yaml to make sure they’re being included in container builds.

Example Tesseract

Here is an example Tesseract that highlights both ways to include dependencies.

# Copyright 2025 Pasteur Labs. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

from goodbyeworld import ungreet
from helloworld import greet
from pydantic import BaseModel, Field


class InputSchema(BaseModel):
    name: str = Field(description="Name of the person you want to greet.")


class OutputSchema(BaseModel):
    message: str = Field(description="A message!")


def apply(inputs: InputSchema) -> OutputSchema:
    """Greets and ungreets a person whose name is given as input."""
    message = f"{greet(inputs.name)}\n{ungreet(inputs.name)}"
    return OutputSchema(message=message)

The custom package helloworld is shipped with a file helloworld.py and a pyproject.toml that ensures it can be built as a Python package.

# Copyright 2025 Pasteur Labs. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0


def greet(name):
    return f"Hello {name}!"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "helloworld"
version = "0.1.0"

Then, it can be added as a local dependency to tesseract_requirements.txt by passing a relative path:

./helloworld

The local module goodbyeworld.py is just a single Python file:

# Copyright 2025 Pasteur Labs. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0


def ungreet(name):
    return f"Goodbye {name}!"

To ensure it gets added to built Tesseracts, we have to register it as package_data:

name: "localpackage"
version: "0.0.1"
description: ""

build_config:
    package_data:
      - ["goodbyeworld.py", "goodbyeworld.py"]

Now we are ready to build the Tesseract. This Tesseract will accept a name like “Tessie” as an input and return a message:

$ tesseract build examples/localpackage
$ tesseract run localpackage apply '{"inputs": {"name": "Tessie"}}'
{"message":"Hello Tessie!\nGoodbye Tessie!"}

This confirms that both custom dependencies are available to the Tesseract container.

To run the Tesseract without containerization, we have to make sure that helloworld is installed into our dev environment:

$ pip install examples/localpackage/helloworld
$ TESSERACT_API_PATH=examples/localpackage/tesseract_api.py tesseract-runtime apply '{"inputs": {"name": "Tessie"}}'
{"message":"Hello Tessie!\nGoodbye Tessie!"}

Advanced pattern: injecting private dependencies as local wheels

In case a Tesseract depends on private packages that are not accessible from the public Python package index (PyPI), injecting them as local files can be a useful way to side-step authentication issues.

This is especially powerful in conjunction with pip download (e.g. from a private PyPI registry) to obtain a pre-built wheel:

$ pip download cowsay==6.1
Collecting cowsay==6.1
  Obtaining dependency information for cowsay==6.1 from https://files.pythonhosted.org/packages/f1/13/63c0a02c44024ee16f664e0b36eefeb22d54e93531630bd99e237986f534/cowsay-6.1-py3-none-any.whl.metadata
  Downloading cowsay-6.1-py3-none-any.whl.metadata (5.6 kB)
Downloading cowsay-6.1-py3-none-any.whl (25 kB)
Saved ./cowsay-6.1-py3-none-any.whl
Successfully downloaded cowsay

We can then specify it as a local dependency in tesseract_requirements.txt by adding the following line:

./cowsay-6.1-py3-none-any.whl

Finally, let’s build the Tesseract, and verify it works

$ tesseract build mytess
 [i] Building image ...
 [i] Built image sha256:7d024, ['mytess:latest']

$ tesseract run mytess apply '{"inputs": {"message": "Hello, World!"}}'
{"out":"  _____________\n| Hello, World! |\n  =============\n             \\\n              \\\n                ^__^\n                (oo)\\_______\n                (__)\\       )\\/\\\n                    ||----w |\n                    ||     ||"}