OpenAI

Using OpenAI GPT and o-series models with Flux — setup, API keys, model strings, structured output, and the parameters that behave differently.

Flux talks to OpenAI through the openai Python SDK’s async client. The integration lives in flux/tasks/ai/openai.py and works against the Chat Completions API.

Install

The OpenAI SDK is an optional dependency. Install it directly:

pip install openai

Or take the full LLM bundle:

pip install flux-core[ai]

There is no per-provider extra in 0.56.0. The ai extra installs ollama, openai, anthropic, and google-genai together.

API key

The OpenAI SDK reads OPENAI_API_KEY from the environment by default. Flux constructs AsyncOpenAI() with no arguments, so the same lookup applies:

export OPENAI_API_KEY="sk-..."

The Flux integration does not currently wire up OPENAI_BASE_URL, organization IDs, or project IDs. If you need any of those, instantiate your own client in a custom provider module — see Adding a new provider.

Model string

Use the openai/ prefix:

from flux.tasks.ai import agent

assistant = await agent(
    "You are a helpful assistant.",
    model="openai/gpt-4o",
)

Anything after the slash is forwarded as the OpenAI model identifier. The integration is tested against gpt-4o, gpt-4o-mini, and the o-series (o3, o3-mini). Newer models work as long as they speak the Chat Completions wire format.

Streaming

Streaming is on by default. The OpenAI formatter uses client.chat.completions.create(stream=True) and yields tokens through Flux’s progress() task. Streaming is disabled automatically whenever response_format is set.

assistant = await agent(
    "You are a helpful assistant.",
    model="openai/gpt-4o",
    stream=True,
)

max_tokens

The OpenAI formatter passes max_tokens through to the API. When set, build_messages() forwards it as max_completion_tokens — the unified token cap in the OpenAI v2 SDK, accepted by both reasoning and non-reasoning models:

researcher = await agent(
    "Write concise summaries.",
    model="openai/gpt-4o",
    max_tokens=1024,
)

When max_tokens is left unset, output length falls back to the model’s default cap.

Reasoning effort

The OpenAI formatter passes reasoning_effort directly to the Chat Completions API. Valid values are "low", "medium", "high", or None (omitted from the call):

researcher = await agent(
    "You are a careful researcher.",
    model="openai/o3-mini",
    reasoning_effort="high",
)

Only the o-series models accept reasoning_effort. On GPT-4 family models the parameter is rejected by the API. The Flux layer does no model-family check before forwarding, so it’s the caller’s responsibility to pair reasoning_effort only with reasoning-capable models.

The OpenAI formatter sets supports_reasoning_stream = True and surfaces reasoning_content deltas when the API returns them. See Reasoning models for the agent-loop details.

Structured output

OpenAI structured output uses the native response_format API parameter. The Flux formatter builds the request like this (openai.py::apply_structured_output):

call_kwargs["response_format"] = {
    "type": "json_schema",
    "json_schema": {
        "name": self._response_format.__name__,
        "schema": self._response_format.model_json_schema(),
    },
}

This is API-enforced for the gpt-4o family and o-series. The model is guaranteed to return JSON matching the supplied schema or to refuse.

from pydantic import BaseModel

class Extraction(BaseModel):
    name: str
    age: int

extractor = await agent(
    "Extract structured data from text.",
    model="openai/gpt-4o",
    response_format=Extraction,
)
result: Extraction = await extractor("Alice is 32 years old.")

The agent loop still calls response_format.model_validate_json() on the response, so you get Pydantic-level validation on top of the API-level enforcement.

Combining tools and structured output

response_format with tools works for OpenAI. The final, post-tool-loop answer is the JSON object. See Tools and structured output for the loop behavior.

What can go wrong

See also