Use the OpenAI SDK in an AgentApp¶
The OpenAI Python SDK is the standard way to make model requests from a Flower AgentApp. Flower provides an OpenAI-compatible Responses endpoint and its credentials while the AgentApp is running, so your code can use the SDK without a model-provider API key.
This guide targets Flower 1.35.0.
Start from the AgentApp template¶
Create a project from the AgentApp published on Flower Hub:
$ uvx --from flwr==1.35.0 flwr new @flwrlabs/agent
$ cd agent
$ uv sync
The template already includes compatible Flower and OpenAI SDK dependencies:
dependencies = ["flwr>=1.35.0,<2.0", "openai>=2.16.0,<3.0.0"]
For an existing AgentApp, set its Flower target in pyproject.toml:
[tool.flwr.app]
flwr-version-target = "1.35.0"
Then update its dependencies:
$ uv add 'flwr>=1.35.0,<2.0' 'openai>=2.16.0,<3.0.0'
Do not add a model-provider API key to the project or its configuration.
Create the client inside the AgentApp¶
Flower starts the AgentApp process with two environment variables:
FLWR_RUNTIME_BASE_URLis the base URL of its internal Runtime APIFLWR_RUNTIME_API_KEYauthenticates requests from that AgentApp process
Pass both values to OpenAI without modifying them. The SDK adds the
/responses path when it creates a response.
Create the client inside the main function so commands such as flwr build can
import the module without requiring a running Flower runtime:
client = OpenAI(
base_url=os.environ["FLWR_RUNTIME_BASE_URL"],
api_key=os.environ["FLWR_RUNTIME_API_KEY"],
max_retries=0,
)
Set max_retries=0 because each Responses request creates a Flower model task.
An automatic SDK retry could create the task more than once.
Important
FLWR_RUNTIME_BASE_URL is not FLWR_MODEL_API_ENDPOINT. The first is available
only inside a running AgentApp. The second configures the upstream model
provider for a self-hosted SuperLink and belongs outside AgentApp code.
Stream the response to Flower clients¶
The runtime keeps model-task events private until the AgentApp chooses to
publish them. Iterate over the SDK stream and pass each event to
agent.events.emit so Flower Chat and the browser can render the response:
import os
from flwr.agentapp import AgentApp, AgentSession
from flwr.app import Context
from openai import OpenAI
MODEL = "openai/gpt-5.6-sol"
app = AgentApp()
@app.main()
def main(agent: AgentSession, context: Context) -> None:
"""Send the configured input to the model."""
prompt = context.run_config.get("agent.input")
if not isinstance(prompt, str) or not prompt.strip():
raise ValueError("agent.input must be a non-empty string")
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=MODEL,
input=prompt.strip(),
stream=True,
)
output_text = []
for event in stream:
agent.events.emit(event.to_dict())
if event.type in {"error", "response.failed"}:
raise RuntimeError(f"Model response failed: {event}")
if event.type == "response.output_text.delta":
output_text.append(event.delta)
print("".join(output_text))
event.to_dict() converts the typed SDK event into the JSON object expected by
Flower. The app also collects text deltas so the completed answer appears in
its logs.
Publish AgentApp-generated text¶
print(...) writes to the AgentApp logs. It does not publish an assistant
response to Flower Chat.
When the AgentApp already has final user-facing text that did not come from an SDK stream, publish an output delta followed by a completion event:
assistant_text = "Hello from the AgentApp!"
agent.events.emit(
{
"type": "response.output_text.delta",
"delta": assistant_text,
}
)
agent.events.emit({"type": "response.completed"})
The output delta adds assistant text to the run-event stream. The completion event tells Flower Chat and other run-event clients that the response has finished. For model-generated output, prefer republishing the original SDK events so clients receive the complete response event sequence.
Publishing these events does not add an assistant message to the conversation
state. When later runs need to replay it, see
Persist the final answer for the complete Context update.
Use the SDK with connectors¶
Model requests use client.responses.create. Connector discovery and
execution remain on the AgentSession:
agent.connectors.tools(...)returns tool schemas to pass to the SDKagent.connectors.call(...)executes a model-requested function callagent.events.emit(...)publishes events to the run-event stream
The SDK returns typed output items. Convert a function-call item with
item.to_dict() before passing it to agent.connectors.call. See Build a
collaborative research
agent for a complete bounded tool
loop.
Persist only the state you need¶
The runtime records a non-empty agent.input as a user message before calling
the AgentApp. Responses created through the SDK are not automatically appended
to the Flower Context. Store the final assistant message yourself when later
runs in the same series need to replay it.
Publishing an event with agent.events.emit makes it visible to run-event
clients but does not persist it in the conversation state. See
Persist the final answer for an implementation that stores the
final assistant message safely.
Build and run the AgentApp¶
$ uv run flwr build
$ uv run flwr login supergrid
$ uv run flwr run . supergrid --stream
The runtime injects both environment variables when it starts the AgentApp. Do
not set, log, or persist FLWR_RUNTIME_API_KEY yourself.
Troubleshoot the SDK client¶
ModuleNotFoundError: openai: runuv syncor add the SDK dependencyMissing runtime URL or key: run the app through Flower instead of starting the Python module directly
Authentication failure: start a new run and use its injected credentials
Unsupported request field: compare the request with the supported model fields in The AgentApp runtime
The endpoint is scoped to the running AgentApp. It is not a public API for browsers, external services, or independently launched SDK clients.