Production Deployment – to_app() with Middleware Stack¶
Real-world use case: E-commerce order processing with retry middleware for transient failures. Production systems need resilience – this shows how middleware and pipelines compose to handle validation, payment, and fulfillment with automatic retries and structured logging.
In other frameworks: LangGraph handles retries via custom node wrappers that must be applied to each node individually. adk-fluent uses middleware composition with the M module, applying cross-cutting concerns uniformly across the entire pipeline.
Tip
What you’ll learn How to compose agents into a sequential pipeline.
Source: 15_production_runtime.py
from adk_fluent import Agent, RetryMiddleware, StructuredLogMiddleware
# to_app() compiles through IR to a production-ready ADK App.
# Middleware wraps every agent invocation with cross-cutting concerns.
pipeline = (
Agent("order_validator")
.model("gemini-2.5-flash")
.instruct("Validate the incoming order: check required fields, verify pricing, confirm inventory.")
>> Agent("payment_processor")
.model("gemini-2.5-flash")
.instruct("Process payment for the validated order. Apply discounts and calculate tax.")
>> Agent("fulfillment")
.model("gemini-2.5-flash")
.instruct("Create shipping label and dispatch order to the nearest warehouse.")
)
# Add production middleware
pipeline.middleware(RetryMiddleware(max_attempts=3))
pipeline.middleware(StructuredLogMiddleware())
# Compile to native App -- ready for Runner
app = pipeline.to_app()
# Also build the sequential agent directly for comparison
built_fluent = pipeline.build()
from google.adk.agents.llm_agent import LlmAgent
from google.adk.agents.sequential_agent import SequentialAgent
# Native ADK production setup: manually assemble agent, runner, session
order_validator = LlmAgent(
name="order_validator",
model="gemini-2.5-flash",
instruction="Validate the incoming order: check required fields, verify pricing, confirm inventory.",
)
payment_processor = LlmAgent(
name="payment_processor",
model="gemini-2.5-flash",
instruction="Process payment for the validated order. Apply discounts and calculate tax.",
)
fulfillment = LlmAgent(
name="fulfillment",
model="gemini-2.5-flash",
instruction="Create shipping label and dispatch order to the nearest warehouse.",
)
pipeline_native = SequentialAgent(
name="order_pipeline",
sub_agents=[order_validator, payment_processor, fulfillment],
)
graph TD
n1[["order_validator_then_payment_processor_then_fulfillment (sequence)"]]
n2["order_validator"]
n3["payment_processor"]
n4["fulfillment"]
n2 --> n3
n3 --> n4
Equivalence¶
from google.adk.apps.app import App
# to_app() produces a native ADK App
assert isinstance(app, App)
# build() produces the same type as native
assert type(pipeline_native) == type(built_fluent)
assert len(built_fluent.sub_agents) == 3
assert built_fluent.sub_agents[0].name == "order_validator"
assert built_fluent.sub_agents[1].name == "payment_processor"
assert built_fluent.sub_agents[2].name == "fulfillment"
# Middleware is attached
assert len(pipeline._middlewares) == 2
assert isinstance(pipeline._middlewares[0], RetryMiddleware)
assert isinstance(pipeline._middlewares[1], StructuredLogMiddleware)
See also
API reference: Runner