Understand the AgentApp runtime

An AgentApp contains the control flow for an agent: what the model should do, which tools it can use, and when its work is complete. Flower executes the app and provides an OpenAI-compatible endpoint for model access. Connectors and frontend-visible events are available through an AgentSession.

Where your app meets the runtime

For example, if your AgentApp is defined in your_package/agent_app.py, declare it in pyproject.toml:

[tool.flwr.app.components]
agentapp = "your_package.agent_app:app"

The <module>:<attribute> value tells Flower where to import the AgentApp object. Flower packages the project as a Flower App Bundle (FAB). A run can resolve an app by app spec, local project, or specific FAB hash.

When a run starts, Flower installs the FAB and its declared dependencies, loads the object, and calls the function registered with AgentApp.main:

@app.main()
def main(agent: AgentSession, context: Context) -> None:
    ...

The function is synchronous and returns when the app has completed its work. An unhandled exception marks the run as failed and records the error in its details and logs.

AgentSession

Flower creates an AgentSession for each AgentApp run and passes it to your main function. It exposes three capabilities:

  • agent.responses provides a lower-level JSON model API

  • agent.connectors returns connector tools and executes function calls

  • agent.events publishes structured events selected by the AgentApp

Provider credentials and connector implementations remain outside the FAB. New AgentApps normally make model requests with the OpenAI SDK and use AgentSession for connectors and frontend-visible events.

Model responses

Flower 1.35.0 exposes an OpenAI-compatible Responses endpoint inside the AgentApp process. The runtime injects its URL and credential as FLWR_RUNTIME_BASE_URL and FLWR_RUNTIME_API_KEY. Pass them to the OpenAI client, then use its standard typed Responses API:

import os

from openai import OpenAI

client = OpenAI(
    base_url=os.environ["FLWR_RUNTIME_BASE_URL"],
    api_key=os.environ["FLWR_RUNTIME_API_KEY"],
    max_retries=0,
)
stream = client.responses.create(
    model="openai/gpt-5.6-sol",
    input="Explain federated AI.",
    stream=True,
)

The runtime recognizes these request fields:

  • model and input

  • stream

  • tools and tool_choice

  • instructions and previous_response_id

  • reasoning and max_output_tokens

  • metadata and text

model must be a non-empty string. input can be text or a sequence of input items. Streaming calls yield typed SDK events. The AgentApp decides which of those events to publish and which output to persist in Context.

The endpoint is authenticated for the current AgentApp task. It is not a public model API for an external client. See Use the OpenAI SDK in an AgentApp for a complete example.

agent.responses.create(request) remains available as a lower-level interface for JSON-based workflows. It returns a JSON response object and automatically appends its model output items to the Flower Context. New AgentApps should prefer the OpenAI SDK when they need typed responses or streaming events.

The default model provider at api.flower.ai does not currently support continuing with previous_response_id. Rebuild input from stored messages for a follow-up request instead. See Rebuild conversation input in Build a collaborative research agent for a complete example.

Connectors

agent.connectors.tools(refs) returns model-facing tool definitions. A built-in reference normally yields one tool. An account connector such as slack can yield several related action tools.

When a model returns a function_call, pass that item to agent.connectors.call(tool_call). Flower resolves the action, runs the connector, records its activity, and returns a function_call_output item for the next model request.

The AgentApp owns the tool loop and must bound it. See Use connectors.

Run events

agent.events.emit(event) publishes one structured event to the run-event stream consumed by Flower Chat and other clients. An SDK stream stays private to the model task until the AgentApp republishes its events:

for event in stream:
    agent.events.emit(event.to_dict())

Publishing an event does not append it to Context. This lets the AgentApp separate frontend-visible progress from conversation state.

These operations have distinct destinations:

Operation

Destination

print(...)

AgentApp logs

agent.events.emit(...)

Run-event stream consumed by Flower Chat and other clients

Store an assistant message in Context

Persistent conversation state

See Publish AgentApp-generated text for the event sequence used to present text that does not come from an SDK stream.

Context

Alongside the AgentSession, your main function receives a Flower Context:

  • context.run_config contains defaults from pyproject.toml fused with per-run overrides

  • context.state stores records persisted for the run series

  • context.run_id identifies the current run

The runtime stores conversation items in a ConfigRecord named items. A ConfigRecord is a specialized Python dictionary, so you can use methods such as get when reading it through context.state.config_records.

If agent.input is a non-empty string, the runtime records it as an Open Responses user-message item before calling the AgentApp. Connector calls append their outputs and built-in activity. The lower-level agent.responses API also appends model output, while SDK responses and events emitted with agent.events are persisted only when the app stores them explicitly.

Runs in the same series can receive the persisted context. The app chooses what to send to the model. A safe conversation loader selects only message items:

import json

messages = []
items_record = context.state.config_records.get("items")
items = items_record.get("json", []) if items_record is not None else []
for item_json in items:
    item = json.loads(item_json)
    if item.get("type") == "message":
        messages.append(item)

Connector activity types such as response.tool_call.started are useful for inspection but are not valid model conversation messages.

The current default Flower Agent converts stored user and assistant messages back into model input. A simple custom AgentApp that forwards only context.run_config["agent.input"] treats every run independently even when the runs share a series.

Run series and federations

A run belongs to one federation. A run series groups runs within that federation and carries their persisted context. Browser chat presents a series as a conversation. flwr chat reuses its current series ID until /new, an agent change, or a federation change. /history can restore an earlier series in the active federation.

Run lifecycle

  1. The CLI or browser resolves an AgentApp and submits a run to a federation

  2. SuperGrid validates account membership, app configuration, and selected account connectors

  3. SuperGrid creates the run and a run series when needed

  4. An executor starts the isolated AgentApp process and loads its FAB

  5. Flower initializes AgentSession and the persisted Context

  6. The main function sends model requests and calls connectors as needed

  7. The AgentApp publishes the model and connector events clients should see

  8. During shutdown, Flower pushes the resulting Context once and records whether the run completed, failed, or stopped

AgentApp and other Flower Apps

A FAB currently supports either:

  • one agentapp component

  • a serverapp and a clientapp

Do not combine an agentapp with a serverapp or clientapp in the same bundle. AgentApp runs execute agent logic rather than federated-learning simulations.