Model Library
Browse and deploy state-of-the-art AI models through the DEVUP Gateway.
Browse and deploy state-of-the-art AI models through the DEVUP Gateway.
GPT-5.4 Mini brings the strengths of the full GPT-5.4 model into a faster, more efficient form built for high-volume workloads. OpenAI positions it as its strongest mini model yet for coding, computer use, and subagents — the roles where a model runs many times rather than once, and where latency and cost per call decide whether a design is viable at all. It shares the same five reasoning effort levels as the tier above it, defaults to no reasoning for speed, and adds a verbosity control that shapes answer length independently of thinking depth. Text and images in, text out, across a 400,000-token context window.

OpenAI's strongest mini model for coding, computer use, and subagents — the full GPT-5.4 capability set at a size built to run often.
A mini model is not a worse model. It is a model chosen for a different constraint.
The full tier is picked when a single answer has to be right. The mini tier is picked when the same operation runs thousands of times: classification across a queue, extraction across a document set, the routine steps inside a longer agent run, or a subagent called by another model.
In those roles, latency and cost per call determine whether a design works at all. A pipeline that is correct but too slow or too expensive per item is not a pipeline.
The interface is identical to the tier above. Same request shape, same effort levels, same verbosity control, same tools. Moving between them is a change to the model field, which makes testing the trade on your own workload nearly free.
The context window is not identical. This model holds 400,000 tokens against roughly a million on the full tier. For most high-volume work that is far more than enough; for whole-repository or whole-corpus passes it is the constraint that decides the tier.
GPT-5.4 Mini defaults to reasoning_effort: "none".
That default is well matched to what this model is for. Classification, extraction, routing, and formatting do not benefit from a deliberation pass, and on a high-volume path that pass is pure cost.
It is also easy to inherit by accident. If a task needs reasoning, it will not get it unless you ask:
{
"model": "openai/gpt-5.4-mini",
"messages": [{ "role": "user", "content": "..." }],
"reasoning_effort": "medium"
}Set the level explicitly on every request. Not because the default is wrong, but because relying on it means behaviour changes silently when the model does.
| Level | Behaviour |
|---|---|
none (default) | Fast, low-latency responses with no reasoning pass |
low | Light reasoning, minimal overhead |
medium | Balanced reasoning and speed |
high | Thorough multi-step reasoning |
xhigh | Maximum depth |
Validate the level in your own code before sending. Accepted values differ across models, and a value that works elsewhere may not work here.
Raise max_tokens when you raise effort. The reasoning pass consumes output budget. With a
tight ceiling and a high effort level, every token can be spent thinking and none left for the
answer — producing an empty response rather than a short one.
Do not assume the ladder is monotonic. More reasoning is not reliably better on every workload.
Test none and medium against your own evaluation set before settling on something in between.
A practical rule for this tier: if medium is not enough on this model, the answer is usually
the full tier rather than a higher effort level here.
Independent of reasoning effort.
| Value | Output |
|---|---|
low | Concise, to the point |
medium (default) | Balanced |
high | Detailed and comprehensive |
Reasoning effort governs how much the model thinks. Verbosity governs how much it writes. On a
high-volume path, verbosity: low is frequently the single cheapest improvement available — the
answer was already correct, it was just longer than it needed to be.
| Model ID | openai/gpt-5.4-mini |
| Context window | 400,000 tokens |
| Max output | 128,000 tokens |
| Knowledge cutoff | 31 August 2025 |
| Input | Text, image |
| Output | Text |
| Reasoning effort | none (default) · low · medium · high · xhigh |
| Verbosity | low · medium (default) · high |
| Tools | Functions, web search, file search, computer use |
| Latency | Faster than the full tier |
Specifications as published by OpenAI. Architecture is not published — no parameter counts, no layer structure, no training details, no weights.
| Capability | Value |
|---|---|
input_types | text, image |
output_types | text |
audio_input | Not supported |
video_input | Not supported |
context_window | 400000 |
max_output_tokens | 128000 |
reasoning | none, low, medium, high, xhigh — default none |
verbosity | low, medium, high |
streaming | Supported |
tool_calling | Supported |
structured_output | Supported — JSON schema |
requires_prompt | Yes — text prompt required, image optional |
Base URL: https://api.devupai.com/v1 · Model ID: openai/gpt-5.4-mini
The configuration this tier exists for.
curl https://api.devupai.com/v1/chat/completions \
-H "Authorization: Bearer $DEVUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.4-mini",
"messages": [
{
"role": "system",
"content": "Classify the support ticket. Reply with exactly one word: billing, technical, shipping, or other."
},
{ "role": "user", "content": "My order never arrived and the tracking page is blank." }
],
"reasoning_effort": "none",
"verbosity": "low",
"max_tokens": 16
}'No deliberation, minimal verbosity, a 16-token ceiling. On a queue of thousands, those three settings together are the difference between a viable pipeline and an expensive one.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEVUP_API_KEY"],
base_url="https://api.devupai.com/v1",
)
SCHEMA = {
"type": "json_schema",
"json_schema": {
"name": "invoice",
"strict": True,
"schema": {
"type": "object",
"properties": {
"invoice_number": {"type": ["string", "null"]},
"total": {"type": ["number", "null"]},
"currency": {"type": ["string", "null"]},
"due_date": {"type": ["string", "null"]},
},
"required": ["invoice_number", "total", "currency", "due_date"],
"additionalProperties": False,
},
},
}
def extract(document: str) -> str:
"""Extract typed fields from one document."""
response = client.chat.completions.create(
model="openai/gpt-5.4-mini",
messages=[
{
"role": "system",
"content": "Extract only fields present in the source. Use null for anything absent — never infer.",
},
{"role": "user", "content": document},
],
response_format=SCHEMA,
max_tokens=2048,
extra_body={"reasoning_effort": "none", "verbosity": "low"},
)
return response.choices[0].message.contentNullable types throughout, and an instruction never to infer. A schema that forbids null invites the model to produce a value where the document has none.
One of the roles OpenAI names for this tier: a cheaper model called by a more capable one to do a bounded piece of work.
def summarise_tool_output(raw: str) -> str:
"""Condense a large tool result before it re-enters an expensive model's context."""
response = client.chat.completions.create(
model="openai/gpt-5.4-mini",
messages=[
{
"role": "system",
"content": (
"Condense this tool output to the facts a downstream agent needs. "
"Preserve every identifier, number, and error message exactly. "
"Drop formatting, repetition, and anything purely decorative."
),
},
{"role": "user", "content": raw},
],
max_tokens=2048,
extra_body={"reasoning_effort": "low", "verbosity": "low"},
)
return response.choices[0].message.contentThis pattern is where the mini tier earns most. A verbose tool result entering a large model's context on every turn of a long loop is one of the more expensive habits in agent design, and condensing it with a cheaper model first costs a fraction of what it saves.
Note reasoning_effort: "low" rather than none here — deciding what to keep is a judgment, not a
transformation.
Defaults differ across models. Passing the level on every call is what makes a model swap safe.
VALID_EFFORT = {"none", "low", "medium", "high", "xhigh"}
def ask(prompt: str, *, effort: str, verbosity: str = "medium", max_tokens: int = 8192) -> str:
"""Send a request with an explicit effort level, validated before it is sent."""
if effort not in VALID_EFFORT:
raise ValueError(f"reasoning_effort must be one of {sorted(VALID_EFFORT)}, got {effort!r}")
response = client.chat.completions.create(
model="openai/gpt-5.4-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tokens,
extra_body={"reasoning_effort": effort, "verbosity": verbosity},
)
return response.choices[0].message.contentValidating in your own code gives you an error that names the problem, at the point where you can fix it.
Computer use is one of the roles OpenAI names for this model.
import base64
with open("screenshot.png", "rb") as handle:
encoded = base64.b64encode(handle.read()).decode("utf-8")
response = client.chat.completions.create(
model="openai/gpt-5.4-mini",
messages=[
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {"url": f"data:image/png;base64,{encoded}"},
},
{
"type": "text",
"text": (
"List every interactive element in this interface with its label, type, "
"and approximate position. Report nothing you cannot actually see."
),
},
],
}
],
max_tokens=4096,
extra_body={"reasoning_effort": "low", "verbosity": "low"},
)npm install devupaiimport DevupAI from "devupai";
const client = new DevupAI({
apiKey: process.env.DEVUP_API_KEY,
});
const response = await client.chat.completions.create({
model: "openai/gpt-5.4-mini",
messages: [
{
role: "system",
content: "Classify the support ticket. Reply with exactly one word: billing, technical, shipping, or other.",
},
{ role: "user", content: ticket },
],
max_tokens: 16,
reasoning_effort: "none",
verbosity: "low",
});
console.log(response.choices[0]?.message?.content);import json
for _ in range(20): # bounded loop — always give an agent an iteration ceiling
response = client.chat.completions.create(
model="openai/gpt-5.4-mini",
messages=messages,
tools=TOOLS,
max_tokens=16384,
extra_body={"reasoning_effort": "medium"},
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
print(message.content)
break
for call in message.tool_calls:
handler = HANDLERS.get(call.function.name)
if handler is None:
result = {"error": "unknown_tool", "name": call.function.name}
else:
try:
result = handler(**json.loads(call.function.arguments or "{}"))
except Exception as exc: # describe the failure — do not crash the loop
result = {"error": type(exc).__name__, "detail": str(exc)}
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
}
)
else:
print("Agent loop exceeded its iteration limit.")An agent loop is one place the none default is actively wrong — tool selection benefits from
deliberation. Set at least medium.
Note the tighter iteration ceiling than you would give a larger model. A cheap model in a long loop is still a long loop.
stream = client.chat.completions.create(
model="openai/gpt-5.4-mini",
messages=[{"role": "user", "content": "Summarise this incident log."}],
max_tokens=8192,
stream=True,
extra_body={"reasoning_effort": "low", "verbosity": "low"},
)
for chunk in stream:
if not chunk.choices:
if getattr(chunk, "usage", None):
print(f"\n\nTokens — in: {chunk.usage.prompt_tokens}, out: {chunk.usage.completion_tokens}")
continue
delta = chunk.choices[0].delta
if getattr(delta, "content", None):
print(delta.content, end="", flush=True)Strong fit for classification, extraction, ranking, routing, and formatting at volume; for subagents inside a larger system; for computer-use and screenshot work; and for the routine steps inside a longer agent run.
Move to the full tier when your input exceeds 400,000 tokens, or when a specific case has already failed here. A real failure on a real input is better evidence than a benchmark.
Escalate rather than over-tune. If medium effort on this model is not enough, the larger model
is usually the answer — a higher effort level here rarely closes a capability gap.