View Source ExOpenAI.Codegen.ResponseConverter (ex_openai.ex v2.0.0-beta2)
Converts raw JSON API responses into Elixir structs based on the OpenAPI response schema and the generated component modules.
At a high level this module:
- Takes the decoded JSON response (string-keyed maps/lists from
Jason) - Uses the response
Schemato find the top-level response component - Uses the generated typespecs (
@type t() :: ...) to understand field types - Builds the top-level response struct (e.g.
Response,CreateChatCompletionResponse) - Recursively converts nested maps into component structs where the schema
and typespecs provide enough information (including
anyOf/oneOfunions) - Deep-atomizes keys for remaining nested maps/lists to keep ergonomics high
This keeps the runtime behavior in sync with the generated typespecs under
ExOpenAI.Components.*, including complex unions like OutputItem.t/0.
Summary
Functions
Converts an API response into the expected struct type based on the response schema.
Extracts the type definition for a specific field from a typespec AST.
Parses a value based on its typespec.
Functions
@spec convert_response( {:ok, any()} | {:error, any()}, ExOpenAI.Codegen.DocsParser.Schema.t() | nil ) :: {:ok, any()} | {:error, any()}
Converts an API response into the expected struct type based on the response schema.
Takes the response and the expected response schema, and converts the response into the expected top-level struct type if they match. Nested maps and lists are not converted into their own component structs; instead, their keys are recursively converted to atoms for easier access.
Parameters
response- The API response tuple{:ok, map}or{:error, any}response_schema- The Schema struct describing the expected response type
Behavior Details
- Handles responses with different patterns, including those with "response" and "type" keys
- Processes reference values directly (when
is_reference(ref)is true) - Converts responses to top-level component structs when the response schema is known
- For
oneOfandanyOfresponse schemas, selects the best matching component based on key overlap - Nested maps and lists are recursively converted to atom-keyed maps/lists, but are not wrapped into their own component structs
- Returns the original response when no conversion is needed or possible
- Preserves error tuples, passing them through unchanged
Return Values
{:ok, converted_value}for successful conversions- Original error tuples are passed through unchanged
Extracts the type definition for a specific field from a typespec AST.
Takes a typespec AST (typically from Code.Typespec.fetch_types/1) and a field name, then searches through the AST structure to find the type definition for that field.
Parameters
typespec_ast- The typespec AST structure, typically a list containing type definitionsfield_name- The atom name of the field to look up
Returns
- The type AST for the field if found
nilif the field is not found in the typespec
Examples
iex> ast = [type: {:t, {:type, 1, :map, [...]}, []}]
iex> get_field_type_from_ast(ast, :id)
{:remote_type, 1, [{:atom, 0, String}, {:atom, 0, :t}, []]}
Parses a value based on its typespec.
Handles primitive and enum-like fields, as well as remote component types and unions. For component modules, it can recursively convert nested maps to the appropriate structs based on the component's typespec and the shape of the data. Nested maps and lists are atomized recursively when no more specific type information is available.
Parameters
type_spec- The typespec AST for the fieldvalue- The actual value to parse
Returns
- The parsed/converted value
Examples
iex> parse_remote_type({:type, 1, :boolean, []}, true)
true
iex> parse_remote_type({:remote_type, 1, [{:atom, 0, ExOpenAI.Components.Reasoning}, {:atom, 0, :t}, []]}, %{"effort" => nil})
%ExOpenAI.Components.Reasoning{effort: nil}