Chat Completions API
Types and functions for the Chat Completions API.
Chat Object
UniLM.Chat — Type
chat = Chat()Creates a new Chat object with default settings:
modelis set togpt-5.6-solmessagesis set to an emptyVector{Message}historyis set totrue
Messages
UniLM.Message — Type
Message(; role, content=nothing, name=nothing, finish_reason=nothing, refusal_message=nothing, tool_calls=nothing, tool_call_id=nothing, provider_content=nothing)Represents a single message in a Chat Completions conversation.
Fields
role::String: One ofRoleSystem,RoleUser,RoleAssistant, orRoleTool.content::Union{String,Nothing}: The text content of the message.name::Union{String,Nothing}: Optional name for the participant.finish_reason::Union{String,Nothing}: Why the model stopped generating (e.g."stop","tool_calls").refusal_message::Union{String,Nothing}: Refusal text when content is filtered.tool_calls::Union{Nothing,Vector{ToolCall}}: Tool calls requested by the assistant.tool_call_id::Union{String,Nothing}: Required whenroleis"tool"— the ID of the tool call being responded to.provider_content::Union{Nothing,ProviderContent}: Provider-native content blocks captured for verbatim round-trip (seeProviderContent); set by the Anthropic/Gemini decoders,nothingotherwise. Never serialized on the OpenAI wire.
Validation
- At least one of
content,tool_calls, orrefusal_messagemust be non-nothing. tool_call_idis required whenrole == "tool".
Convenience Constructors
Message(Val(:system), "You are a helpful assistant")
Message(Val(:user), "Hello!")UniLM.ProviderContent — Type
ProviderContent(provider::Symbol, blocks::Vector{Any})Provider-native assistant content captured verbatim at decode time and echoed verbatim at encode time when the SAME provider encodes the turn again.
The neutral Message carries content::String + tool_calls, but some providers attach blocks that must round-trip byte-faithfully for multi-turn flows to work: Anthropic thinking/redacted_thinking blocks (their signature must be echoed unmodified or tool round-trips on thinking models are rejected with HTTP 400), and Gemini text-part thoughtSignatures. provider tags the wire dialect (:anthropic or :gemini); blocks is the provider's content/parts array exactly as decoded (String-keyed JSON).
Encoders ignore a ProviderContent tagged for a different provider — a conversation moved across providers falls back to the neutral reconstruction (the standard "other models drop thinking" semantics). The field never serializes on the OpenAI wire (excluded from JSON.lower(::Message)).
Convenience Constructors
using UniLM
sys = Message(Val(:system), "You are a helpful assistant")
usr = Message(Val(:user), "Hello!")
println("System role: ", sys.role)
println("User role: ", usr.role)
println("User content: ", usr.content)System role: system
User role: user
User content: Hello!Conversation Management
UniLM.issendvalid — Function
issendvalid(chat::Chat)::Bool
Check if the conversation is valid for sending to the API.
Returns `true` only when the conversation has at least two messages, the first
is a system message, the LAST is a user message, and no two adjacent messages
share a role. Note the last two clauses are stricter than [`push!`](@ref),
which permits consecutive `tool` messages — this check has no such exemption,
and a conversation ending in a tool result is `false` here.
This is a heuristic, not a proof: it cannot catch every malformed shape (a
second system message in the middle passes the adjacency test). It never
throws — a `false` is a verdict, not an error.UniLM.update! — Function
update!(chat::Chat, msg::Message)
Update the chat with a new message.UniLM.fork — Function
fork(chat::Chat) -> ChatCreate an independent copy of a Chat: messages is deep-copied, the cumulative-cost Ref is fresh (copied by value), and EVERY other field is copied verbatim by construction (a fieldnames loop), so new Chat fields survive forking automatically. fork itself applies no normalization or rewrite — a fork is configuration-identical to its source.
fork(chat::Chat, n::Int) -> Vector{Chat}Create n independent forks of a Chat.
UniLM.InvalidConversationError — Type
InvalidConversationError <: ExceptionThrown when a conversation violates structural rules (e.g., missing system message, consecutive messages from the same role).
Fields
reason::String: Human-readable explanation of why the conversation is invalid.
Building a Conversation
chat = Chat(model="gpt-5.2")
push!(chat, Message(Val(:system), "You are a helpful assistant"))
push!(chat, Message(Val(:user), "What is Julia?"))
println("Messages: ", length(chat))
println("Valid for sending: ", issendvalid(chat))Messages: 2
Valid for sending: trueTools
UniLM.Tool — Type
Tool(; type="function", func)Wraps a FunctionSignature for use in the tools parameter of a Chat.
Example
tool = Tool(func=FunctionSignature(
name="get_weather",
description="Get the current weather",
parameters=Dict("type" => "object", "properties" => Dict())
))
chat = Chat(tools=[tool])UniLM.FunctionSignature — Type
FunctionSignature(; name, description=nothing, parameters=nothing, strict=nothing)Describes a function that can be called by the model in the Chat Completions API.
Fields
name::String: The name of the function.description::Union{String,Nothing}: A description of what the function does.parameters::Union{AbstractDict,Nothing}: JSON Schema object describing the function parameters.strict::Union{Bool,Nothing}: Enable strict schema adherence for function arguments (structured outputs).nothing(default) omits the field from the request — the API default, non-strict.truerequiresparametersto be a strict-valid schema (additionalProperties: falseon every object, all propertiesrequired); UniLM does not validate this — the API rejects strict-invalid schemas with a 400.
Example
sig = FunctionSignature(
name="get_weather",
description="Get the current weather in a given location",
parameters=Dict(
"type" => "object",
"properties" => Dict(
"location" => Dict("type" => "string", "description" => "The city")
),
"required" => ["location"],
"additionalProperties" => false
),
strict=true
)UniLM.ToolCall — Type
ToolCall(; id, type="function", func)Represents a tool call returned by the model. Contains the call id (used to match results back), the tool type, and the [GPTFunction] with name and parsed arguments.
Also carries an optional thought_signature::Union{Nothing,String} field holding Gemini-3's opaque tool-call signature, which must be echoed verbatim on the next turn; it is set by the Gemini decoder, ignored (nothing) by every other provider, and deliberately excluded from JSON.lower so OpenAI-wire serialization is unaffected.
UniLM.FunctionCallResult — Type
FunctionCallResult{T}Holds the result of executing a function that was requested by the model via a tool call.
Fields
name::Union{String,Symbol}: The function name.origincall::GPTFunction: The original [GPTFunction] call from the model.result::T: The result of executing the function.
Legacy aliases
The provider-neutral names above are canonical. These GPT* consts are exported aliases that keep pre-rename code working unchanged (silent, no deprecation warning; retained until the 1.0 stability boundary).
UniLM.GPTTool — Type
GPTToolLegacy alias for Tool.
UniLM.GPTToolCall — Type
GPTToolCallLegacy alias for ToolCall.
UniLM.GPTFunctionSignature — Type
GPTFunctionSignatureLegacy alias for FunctionSignature.
UniLM.GPTFunctionCallResult — Type
GPTFunctionCallResultLegacy alias for FunctionCallResult.
Output Format
UniLM.ResponseFormat — Type
ResponseFormat(; type="json_object", json_schema=nothing)
ResponseFormat(json_schema)Specifies the output format for Chat Completions.
Fields
type::String:"json_object"or"json_schema".json_schema::Union{JsonSchemaAPI,AbstractDict,Nothing}: Schema definition whentypeis"json_schema".
Examples
# Free-form JSON
fmt = ResponseFormat()
# Structured JSON via schema
fmt = ResponseFormat(JsonSchemaAPI(
name="result",
description="A structured result",
schema=Dict("type" => "object", "properties" => Dict())
))using JSON
# JSON object format
fmt = ResponseFormat()
println("Type: ", fmt.type)
println("JSON: ", JSON.json(fmt))Type: json_object
JSON: {"type":"json_object"}Request Function
UniLM.chatrequest! — Function
chatrequest!(chat::Chat; config=nothing, callback=nothing, on_tool_call=nothing)Send chat to its provider and return a typed result.
Non-streaming (chat.stream !== true): returns LLMSuccess, LLMFailure, or LLMCallError. Transient statuses (408/429/500/502/503/504/529) are retried with backoff and jitter under the resolved RequestConfig (max_attempts, total_deadline; Retry-After honored). Timeouts surface as LLMCallError with status = nothing and the UniLMTimeout in cause — no fabricated HTTP statuses.
Streaming (chat.stream === true): returns a Task whose fetch yields the same typed results. callback(chunk::Union{String,Message}, close::Ref{Bool}) receives text deltas then the final assembled Message; on_tool_call(tc::ToolCall) fires once per completed streamed tool call. A user InterruptException is never converted into a result value: it propagates, so fetch on the streaming task throws a TaskFailedException whose task.exception is the InterruptException.
config::Union{Nothing,RequestConfig}: per-call timeout/retry budget; nothing resolves the ambient configuration (with_request_config scope, else the process default set via set_default_config!).
Throws ArgumentError before any network I/O when chat.service is an endpoint type that declares its capabilities and does not list :chat. A custom endpoint declares none and is dispatched unvalidated.
Flexible keyword arguments usage
chatrequest!(; kwargs...) Send a request to the OpenAI API to generate a response to the messages in conv.
Keyword Arguments
service::ServiceEndpointSpec = OPENAIServiceEndpoint: The provider endpoint type or instance.model::String = "gpt-5.6-sol": The model to use for the chat completion.systemprompt::Union{Message,String}: The system prompt message.userprompt::Union{Message,String}: The user prompt message.messages::Conversation = Message[]: The conversation history or the system/prompt messages.history::Bool = true: Whether to include the conversation history in the request.tools::Union{Vector{Tool},Nothing} = nothing: A list of tools the model may call.tool_choice::Union{String,GPTToolChoice,Nothing} = nothing: Controls which (if any) function is called by the model. e.g. "auto", "none",GPTToolChoice.parallel_tool_calls::Union{Bool,Nothing} = false: Whether to enable parallel function calling.temperature::Union{Float64,Nothing} = nothing: Sampling temperature (0.0-2.0). Higher values make output more random. Mutually exclusive withtop_p.top_p::Union{Float64,Nothing} = nothing: Nucleus sampling parameter (0.0-1.0). Mutually exclusive withtemperature.n::Union{Int64,Nothing} = nothing: How many chat completion choices to generate for each input message (1-10).stream::Union{Bool,Nothing} = nothing: If set, partial message deltas will be sent, like in ChatGPT.stop::Union{Vector{String},String,Nothing} = nothing: Up to 4 sequences where the API will stop generating further tokens.max_tokens::Union{Int64,Nothing} = nothing: The maximum number of tokens to generate in the chat completion.presence_penalty::Union{Float64,Nothing} = nothing: Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far.response_format::Union{ResponseFormat,Nothing} = nothing: An object specifying the format that the model must output. e.g.,ResponseFormat(type="json_object").frequency_penalty::Union{Float64,Nothing} = nothing: Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far.logit_bias::Union{AbstractDict{String,Float64},Nothing} = nothing: Modify the likelihood of specified tokens appearing in the completion.user::Union{String,Nothing} = nothing: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.seed::Union{Int64,Nothing} = nothing: This feature is in Beta. If specified, the system will make a best effort to sample deterministically.config::Union{Nothing,RequestConfig} = nothing: Per-call timeout/retry budget;nothingresolves the ambient configuration (scoped, else process default).
Role Constants
UniLM.RoleSystem — Constant
RoleSystemRole constant "system" — used for system-level instructions.
UniLM.RoleUser — Constant
RoleUserRole constant "user" — used for user messages.
UniLM.RoleAssistant — Constant
RoleAssistantRole constant "assistant" — used for model-generated messages.
Tool Loop
UniLM.CallableTool — Type
CallableTool{T}(tool, callable)Wraps a tool schema T (Tool or FunctionTool) with a callable. JSON serialization delegates to the inner tool, preserving backward compatibility.
Fields
tool::T: The tool schema.callable::Function:(name::String, args::Dict{String,Any}) -> String
Example
tool = Tool(func=FunctionSignature(name="add", description="Add two numbers",
parameters=Dict("type"=>"object","properties"=>Dict("a"=>Dict("type"=>"number"),"b"=>Dict("type"=>"number")))))
ct = CallableTool(tool, (name, args) -> string(args["a"] + args["b"]))UniLM.ToolCallOutcome — Type
ToolCallOutcomePer-call record from a tool dispatch.
Fields
tool_name::String: Name of the tool that was called.arguments::Dict{String,Any}: Arguments passed to the tool.result::Union{FunctionCallResult,Nothing}: The result wrapper, ornothingon failure.success::Bool: Whether the dispatch succeeded.error::Union{String,Nothing}: Error message on failure.
UniLM.ToolLoopResult — Type
ToolLoopResultResult of a tool dispatch loop.
Fields
response::LLMRequestResponse: The final API response.tool_calls::Vector{ToolCallOutcome}: History of all tool dispatches.turns_used::Int: Number of API round-trips.completed::Bool: Whether the loop terminated normally (text response). Truncated output or a pending server action leaves thisfalse.llm_error::Union{String,Nothing}: Error message if not completed.
UniLM.tool_loop! — Function
tool_loop!(chat::Chat, dispatcher::Function; max_turns=10, config=nothing, callback=nothing, on_tool_call=nothing) -> ToolLoopResultRun a tool-calling loop on a Chat. Repeatedly calls chatrequest!, dispatches tool calls via dispatcher(name, args), pushes tool-role messages back, and repeats until a text response, API error, or max_turns.
Arguments
dispatcher:(name::String, args::Dict{String,Any}) -> Stringmax_turns: Maximum API round-trips (default 10).config: Per-requestRequestConfigpassed tochatrequest!— each turn gets its own attempt/deadline budget.callback: Streaming callback passed tochatrequest!.on_tool_call: Tool call notification callback passed tochatrequest!.
Example
chat = Chat(model="gpt-5-mini", tools=[tool])
push!(chat, Message(Val(:system), "You are a calculator"))
push!(chat, Message(Val(:user), "What is 3+5?"))
result = tool_loop!(chat, (name, args) -> string(args["a"] + args["b"]))tool_loop!(chat::Chat; tools::Vector{<:CallableTool}, kwargs...) -> ToolLoopResultNo-dispatcher variant: builds a dispatcher from CallableTool entries.
UniLM.tool_loop — Function
tool_loop(r::Respond, dispatcher::Function; max_turns=10, config=nothing) -> ToolLoopResultRun a tool-calling loop on a Respond request. Dispatches function calls via dispatcher(name, args), builds function_call_output input items, and chains via previous_response_id.
Per-call config::RequestConfig overrides timeouts/retry budget.
tool_loop(r::Respond; max_turns=10, config=nothing) -> ToolLoopResultNo-dispatcher variant: extracts callables from CallableTool entries in r.tools.
Per-call config::RequestConfig overrides timeouts/retry budget.
tool_loop(input, dispatcher::Function; tools, kwargs...) -> ToolLoopResultConvenience form: creates a Respond and runs the tool loop.
Per-call config::RequestConfig overrides timeouts/retry budget.
tool_loop(input::String; tools, max_turns=10, config=nothing, kwargs...) -> ToolLoopResultNo-dispatcher convenience form of the Responses-API tool loop for a plain-string prompt. Wraps input and tools in a Respond and delegates to tool_loop(::Respond), which dispatches each model-requested function call to the matching CallableTool callable.
Keyword routing is explicit: max_turns and config drive the loop (config::RequestConfig overrides timeouts/retry budget), while every other keyword is forwarded verbatim to the Respond constructor — an unknown keyword raises there rather than being silently dropped. tools is required and must hold CallableTool entries (e.g. from mcp_tools_respond).
Example
session = mcp_connect("https://mcp.example.com/mcp")
tools = mcp_tools_respond(session)
result = tool_loop("List files in /tmp"; tools=tools)UniLM.to_tool — Function
to_tool(x)Overloadable conversion protocol. Identity for Tool, FunctionTool, CallableTool. Converts AbstractDict to Tool. Package extensions can add methods for other types.
Convert an MCPServerTool to a FunctionTool for use with the Responses API.
Model Constants
println("GPT5_2: ", UniLM.GPT5_2)GPT5_2: gpt-5.2