Skip to main content
Muna supports compiling a tiny-but-growing subset of Python language constructs. Below are requirements and guidelines for compiling a Python function with Muna:

Specifying the Function Signature

The prediction function must be a module-level function, and must have parameter and return type annotations:
from muna import compile

@compile(...)
def greeting(name: str) -> str:
    return f"Hello {name}"
The prediction function must not have any variable-length positional or keyword arguments.

Supported Parameter Types

Muna supports a fixed set of predictor input and output value types. Below are supported type annotations:
Floating-point input and return values should be annotated with the float built-in type.
from muna import compile

@compile(...)
def square(number: float) -> float:
    return number ** 2
Unlike Python which defaults to 64-bit floats, Muna will always lower a Python float to 32 bits.
For control over the binary width of the number, use the numpy.float[16,32,64] types:
from muna import compile
import numpy as np

@compile(...)
def square(number: np.float64) -> float64:
    return number ** 2
Integer input and return values should be annotated with the int built-in type.
from muna import compile

@compile(...)
def square(number: int) -> int:
    return number ** 2
Unlike Python which supports arbitrary-precision integers, Muna will always lower a Python int to 32 bits.
For control over the binary width of the integer, use the numpy.int[8,16,32,64] types:
from muna import compile
import numpy as np

@compile(...)
def square(number: np.int16) -> np.int16:
    return number ** 2
Boolean input and return values must be annotated with the bool built-in type.
from muna import compile

@compile(...)
def invert(on: bool) -> bool:
    return not on
Tensor input and return values must be annotated with the NumPy numpy.typing.NDArray[T] type, where T is the tensor element type.
from muna import compile
import numpy as np
from numpy.typing import NDArray

@compile(...)
def cholesky_decompose(tensor: NDArray[np.float64]) -> np.ndarray:
    return np.linalg.cholesky(tensor).astype("float32")
You can also annotate with the np.ndarray type, but doing so will always assume a float32 element type (following PyTorch semantics).
Below are the supported element types:
Numpy data typeMuna data type
np.float16float16
np.float32float32
np.float64float64
np.int8int8
np.int16int16
np.int32int32
np.int64int64
np.uint8uint8
np.uint16uint16
np.uint32uint32
np.uint64uint64
boolbool
Muna does not yet support complex numbers or tensors.
Muna only supports, and will always assume, little-endian ordering for multi-byte element types.
String input and return values must be annotated with the str built-in type.
from muna import compile

@compile(...)
def uppercase(text: str) -> str:
    return text.upper()
List input and return values must be annotated with the list[T] built-in type, where T is the element type.
from muna import compile

@compile(...)
def slice(items: list[str]) -> list[str]:
    return items[:3]
When the list element type T is a Pydantic BaseModel, a full JSON schema will be generated.
Providing an element type T is optional but strongly recommended because it is used to generate a schema for the parameter or return value.
Dictionary input and return values can be annotated in one of two ways:
  1. Using a Pydantic BaseModel subclass.
  2. Using the dict[str, T] built-in type.
from muna import compile
from pydantic import BaseModel
from typing import Literal

class Person(BaseModel):
    city: str
    age: int

class Pet(BaseModel):
    sound: Literal["bark", "meow"]
    legs: int

@compile(...)
def choose_favorite_pet(person: Person) -> Pet:
    return Pet(sound="meow", legs=6)
We strongly recommend the Pydantic BaseModel annotation, as it allows us to generate a full JSON schema.
When using the dict annotation, they key type must be str. The value type T can be any arbitrary type.
Image input and return values must be annotated with the Pillow PIL.Image.Image type.
from muna import compile
from PIL import Image

@compile(...)
def resize(image: Image.Image) -> Image.Image:
    return image.resize((512, 512))
Binary input and return values can be annotated in one of three ways:
  1. Using the bytes built-in type.
  2. Using the bytearray built-in type.
  3. Using the io.BytesIO type.
from muna import compile
from PIL import Image

def resize_pixels(pixels: bytes) -> bytes
    return Image.frombytes("L", (4,4), pixels).resize((8,8)).tobytes()

Using Parameter Annotations

Muna supports attaching additional annotations to the function’s parameter and return types:
from muna import compile, Parameter
from typing import Annotated

@compile(...)
def area(
    radius: Annotated[
      float,
      Parameter.Generic(description="Radius of the circle.")
    ]
) -> Annotated[float, Parameter.Generic(description="Area of the circle.")]:
    ...
These annotations serve multiple important purposes:
  • They help users know what input data to provide to the predictor and how to use output data from the predictor, via the parameter description.
  • They help users search for predictors using highly detailed queries (e.g. MCP clients).
  • They help the Muna client automatically provide familiar interfaces around your prediction function, e.g. with the OpenAI interface.
  • They help the Muna website automatically create interactive visualizers for your prediction function.
While not required, we very strongly recommend using parameter annotations on your compiled functions.
Below are currently supported annotations:
Use the Parameter.Generic annotation to provide information about a general input or output type:
predictor.py
from muna import Parameter
from typing import Annotated

def area(
    radius: Annotated[
      float,
      Parameter.Generic(description="Radius of the circle.")
    ]
) -> Annotated[float, Parameter.Generic(description="Area of the circle.")]:
    ...
Below is the full Parameter.Generic annotation definition:
@classmethod
def Generic(
    cls,
    *,
    description: str  # Parameter description.
) -> Parameter: ...
Use the Parameter.Numeric annotation to provide information about a numeric input or output type:
predictor.py
from muna import Parameter
from typing import Annotated

def area(
    radius: Annotated[
      float,
      Parameter.Numeric(description="Circle radius.", range=(1., 10.))
    ]
) -> Annotated[float, Parameter.Numeric(description="Circle area.")]:
    ...
Below is the full Parameter.Numeric annotation definition:
@classmethod
def Numeric(
    cls,
    *,
    description: str,                       # Parameter description.
    range: tuple[float, float] | None=None  # Value range.
) -> Parameter: ...
Use the Parameter.Audio annotation to provide information about an audio input or output type:
predictor.py
from muna import Parameter
from numpy import ndarray
from typing import Annotated

def transcribe_audio(
    audio: Annotated[
      ndarray,
      Parameter.Audio(description="Input audio", sample_rate=24_000)
    ]
) -> str:
    ...
Below is the full Parameter.Audio annotation definition:
@classmethod
def Audio(
    cls,
    *,
    description: str, # Parameter description.
    sample_rate: int  # Audio sample rate in Hertz.
) -> Parameter: ...
Use the Parameter.Embedding annotation to provide information about a vector embedding input or output type:
predictor.py
from muna import Parameter
from numpy import ndarray
from typing import Annotated

def embed_text(
    text: str
) -> Annotated[ndarray, Parameter.Embedding(description="Embedding vector.")]:
    ...
The Parameter.Embedding annotation allows the compiled predictor to be used by the OpenAI embedding interface.
Below is the full Parameter.Embedding annotation definition:
@classmethod
def Embedding(
    cls,
    *,
    description: str  # Parameter description.
) -> Parameter: ...

Writing the Function Body

The function body can contain arbitrary Python code. Given that the Muna compiler is currently a proof of concept, it has limited coverage for Python language features. Below is a list of Python language features that we either partially support, or do not support at all:
StatementStatusNotes
Recursive functions🔨Recursive functions must have a return type annotation.
Lambda expressions🚧Lambda expressions can be invoked, but cannot be used as objects.
CollectionStatusNotes
List literals🚧List must contain primitive members (e.g. int, str).
Dictionary literals🚧Dictionary must contain primitive members (e.g. int, str).
Set literals🚧Set must contain primitive members (e.g. int, str).
Tuple literals🚧Tuple must contain primitive members (e.g. int, str).
Tracing through classes is not yet supported.
StatementStatusNotes
raise statements🔨
try..except statement🔨
Over time the list of unsupported language features will shrink and eventually, will be empty.

Library Coverage

We are adding support for popular libraries, across tensor frameworks, scientific computing, and more:
Below are libraries currently supported by our compiler:
If you need a specific library to be supported by the Muna compiler, reach out to us.