bentoml/bentoml

The easiest way to serve AI apps and models - Build Model Inference APIs, Job queues, LLM apps, Multi-model pipelines, and more!

8,591 stars Python 8 components

11 hidden assumptions · 6-stage pipeline · 8 components

Like any codebase, this ml inference makes assumptions it never checks — most are routine. The ones worth your attention are below.

Converts ML models into production-ready REST and gRPC APIs with automatic batching and scaling

HTTP requests enter through the ASGI server, get deserialized by IODescriptors into Python objects, optionally batched by CorkDispatcher, processed by service methods that load and run ML models, then serialized back to HTTP responses. Long-running tasks are stored in SQLite and polled by clients.

Under the hood, the system uses 3 feedback loops, 3 data pools, 3 control points to manage its runtime behavior.

A 8-component ml inference. 438 files analyzed. Data flows through 6 distinct pipeline stages.

Hidden Assumptions

Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here. The rest are minor; they're under "Show everything".

Worth your attention first

If a batch contains requests with different IODescriptor types (e.g., JSON and Image), serialization fails with cryptic errors or produces malformed responses

Worth your attention first

If SQLite corruption or concurrent updates cause a task to transition from SUCCESS back to PENDING, the client never returns results and polls indefinitely

Worth your attention first

Long-running tasks fail silently when SQLite writes fail due to disk full or permission denied, leaving clients polling forever with no error indication

Show everything (8 more)
Scale

ClientEndpoint.input and ClientEndpoint.output dictionaries fit entirely in memory and don't exceed Python's dict size limits, but large multipart uploads or model outputs can exhaust memory

If this fails: When model endpoints accept/return large tensor data (>1GB), the client endpoint metadata causes OOM errors during proxy generation or serialization

src/_bentoml_impl/client/base.py:ClientEndpoint.input/output
Temporal

The ServiceContext remains valid for the entire request lifetime and is never invalidated during request processing, but context can be cleared by other threads or signal handlers

If this fails: If the service context is reset while a request is processing (e.g., during graceful shutdown), middleware loses request tracking and can corrupt response serialization

src/_bentoml_impl/server/app.py:ContextMiddleware
Domain

HTTP status codes from the BentoML server exactly match standard HTTPStatus enum values, but custom or extended status codes cause KeyError in error_mapping lookup

If this fails: When servers return non-standard HTTP codes (e.g., 299 or vendor-specific 5xx codes), the exception mapping fails and raises KeyError instead of a meaningful BentoMLException

src/_bentoml_impl/client/base.py:map_exception
Environment

The BENTOML_RESULT_STORE environment variable, if set, points to a valid SQLite database path that the service has read/write access to

If this fails: If the environment variable points to a non-existent directory, read-only filesystem, or invalid SQLite file, task storage initialization fails at runtime with unclear error messages

src/_bentoml_impl/server/app.py:RESULT_STORE_ENV
Contract

Service method signatures discovered via introspection remain stable between client proxy creation and actual method invocation, but dynamic method modification breaks the contract

If this fails: If a service modifies its API methods dynamically (e.g., adding parameters or changing return types), existing client proxies call with wrong signatures and get unexpected errors

src/_bentoml_impl/client/proxy2.py:RemoteProxy method generation
Scale

Request latencies fall within the exponential bucket ranges defined for Prometheus metrics, but extreme latencies (>10 minutes) overflow the highest bucket

If this fails: Very long-running model inference requests get miscategorized in metrics, making it impossible to monitor or alert on truly stuck requests

src/_bentoml_impl/server/app.py:exponential_buckets metrics
Domain

Image type detection relies on MIME type mappings and file extensions being standard and complete, but custom image formats or missing MIME types cause misclassification

If this fails: Custom medical imaging formats (DICOM, NIFTI) or proprietary image types get treated as generic binary data, bypassing image-specific optimizations and validations

src/_bentoml_impl/client/base.py:is_image_type checking
Resource

The aiohttp ClientSession can handle the expected concurrent request load without hitting connection pool limits or timeout thresholds

If this fails: Under high concurrency, client proxy requests get queued or time out due to connection pool exhaustion, even when the server has capacity

src/_bentoml_impl/client/proxy2.py:aiohttp session management

Open the standalone hidden-assumptions report for bentoml →

How Data Flows Through the System

HTTP requests enter through the ASGI server, get deserialized by IODescriptors into Python objects, optionally batched by CorkDispatcher, processed by service methods that load and run ML models, then serialized back to HTTP responses. Long-running tasks are stored in SQLite and polled by clients.

  1. HTTP Request Receipt — Starlette receives HTTP requests and routes them to the correct service method based on URL patterns defined by @bentoml.api decorators
  2. Request Deserialization — IODescriptor subclasses (JSON, Image, File) convert HTTP payloads into Python objects using Pydantic validation and type coercion [Request/Response Payload → Python Objects]
  3. Request Batching — CorkDispatcher aggregates individual requests into batches when batchable=True, waiting for batch_size threshold or timeout to optimize GPU utilization [Python Objects → BatchRequest]
  4. Model Inference — Service methods execute model inference using framework adapters, loading models lazily and applying framework-specific optimizations [BatchRequest → Inference Results]
  5. Response Serialization — IODescriptors convert Python inference results back into HTTP response formats (JSON, images, files) with appropriate content-type headers [Inference Results → Request/Response Payload]
  6. Async Task Management — Long-running operations are stored as Task objects in Sqlite3Store, returning task IDs immediately while processing continues in background [Task → Task ID]

Data Models

The data structures that flow between stages — the contracts that hold the system together.

ServiceConfig src/_bentoml_sdk/service/config.py
Pydantic model with image: ImageSpec (base container), resources: ResourceConfig (CPU/GPU limits), workers: int (process count), timeout: int (request timeout)
Created from service decorators, validated at import time, used by server factory to configure runtime behavior
Request/Response Payload src/_bentoml_impl/serde/
Union of JSON dict, bytes (multipart), or structured data with content_type: str, headers: dict[str, str], body: bytes
Deserialized from HTTP requests into Python objects, transformed by service methods, serialized back to HTTP responses
ClientEndpoint src/_bentoml_impl/client/base.py
dataclass with name: str, route: str, input_spec: IODescriptor, output_spec: IODescriptor, stream_output: bool, is_task: bool
Discovered from service introspection, cached by clients, used to generate type-safe remote method calls
Task src/_bentoml_impl/tasks/
dataclass with id: str, status: ResultStatus (enum), result: Any, error: str | None, created_at: datetime
Created for long-running requests, persisted in SQLite, polled by clients until completion
BatchRequest src/bentoml/_internal/marshal/
list[Request] with shared batch_size: int, timeout: float, and per-request metadata for batching optimization
Aggregated from individual requests, processed as a batch by batchable APIs, results distributed back to original requests

System Behavior

How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.

Data Pools

Task Results Store (database)
SQLite database that persists task state, results, and errors for async operations, enabling client polling and result retrieval
Model Registry (registry)
Framework-specific model loaders and cached model instances, preventing repeated loading and enabling model versioning
Request Batching Buffer (buffer)
In-memory queue that accumulates requests for dynamic batching, releasing when size or timeout thresholds are met

Feedback Loops

Delays

Control Points

Technology Stack

Starlette (framework)
ASGI web framework that handles HTTP routing, middleware, and request/response lifecycle for service APIs
Pydantic (serialization)
Provides type validation and serialization for request/response payloads and service configuration schemas
Uvicorn (runtime)
ASGI server that runs the Starlette applications in production with async request handling
OpenTelemetry (infra)
Instruments service APIs with distributed tracing and metrics collection for observability
Prometheus Client (infra)
Exposes custom metrics for inference latency, request counts, and system health monitoring
Simple-DI (framework)
Dependency injection container that manages service configuration and component lifecycle
HTTPX (library)
HTTP client library used by RemoteProxy and client implementations for service-to-service communication
aiosqlite (database)
Async SQLite driver for persisting task state and results in the background task system

Key Components

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related Ml Inference Repositories

Frequently Asked Questions

What is BentoML used for?

Converts ML models into production-ready REST and gRPC APIs with automatic batching and scaling bentoml/bentoml is a 8-component ml inference written in Python. Data flows through 6 distinct pipeline stages. The codebase contains 438 files.

How is BentoML architected?

BentoML is organized into 5 architecture layers: SDK Interface, Server Runtime, IO Serialization, Client Libraries, and 1 more. Data flows through 6 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.

How does data flow through BentoML?

Data moves through 6 stages: HTTP Request Receipt → Request Deserialization → Request Batching → Model Inference → Response Serialization → .... HTTP requests enter through the ASGI server, get deserialized by IODescriptors into Python objects, optionally batched by CorkDispatcher, processed by service methods that load and run ML models, then serialized back to HTTP responses. Long-running tasks are stored in SQLite and polled by clients. This pipeline design reflects a complex multi-stage processing system.

What technologies does BentoML use?

The core stack includes Starlette (ASGI web framework that handles HTTP routing, middleware, and request/response lifecycle for service APIs), Pydantic (Provides type validation and serialization for request/response payloads and service configuration schemas), Uvicorn (ASGI server that runs the Starlette applications in production with async request handling), OpenTelemetry (Instruments service APIs with distributed tracing and metrics collection for observability), Prometheus Client (Exposes custom metrics for inference latency, request counts, and system health monitoring), Simple-DI (Dependency injection container that manages service configuration and component lifecycle), and 2 more. A focused set of dependencies that keeps the build manageable.

What system dynamics does BentoML have?

BentoML exhibits 3 data pools (Task Results Store, Model Registry), 3 feedback loops, 3 control points, 3 delays. The feedback loops handle batch-window and polling. These runtime behaviors shape how the system responds to load, failures, and configuration changes.

What design patterns does BentoML use?

4 design patterns detected: Decorator-based Service Definition, Dynamic Request Batching, Framework Adapter Pattern, Mirror Client Proxies.

Analyzed on April 20, 2026 by CodeSea. Written by .