Machine-readable answers from chat models, shared by PhoenixKitAI.ask/3,
PhoenixKitAI.complete/3 and PhoenixKitAI.describe_image/3.
Two options turn it on:
schema:— a JSON Schema map; the answer is requested as that object (response_format: json_schema, strict) and parsed.json: true— any JSON object (response_format: json_object).
The schema also rides in the prompt, so a model that rejects
response_format (the request is retried once without it on 400/422 —
any 400, so an unrelated bad request costs one extra call) still knows
the shape. The parsed object comes back under "json" on a chat
response and :json on a vision result; an answer that does not parse
is {:error, {:no_json_in_response, text}} and is never cached, though
the provider call is logged with its usage. Nothing validates the
object against the schema locally: strict schemas are enforced by the
provider on the first attempt and advisory on the retry, so check the
keys you depend on.
Summary
Functions
Appends suffix to the last user message (string content or content
parts). Messages with atom or string keys are both handled.
Parses the model's text as a JSON object (or array): the whole text
first, then a fenced block anywhere in it, then the outermost {…} —
models wrap answers in prose and fences however they were told. With
requested? false returns {:ok, nil}.
The sentence to append to the user's prompt and the response_format
to send, or {nil, nil} when no JSON was asked for.
Whether the caller asked for a JSON answer.
Runs fun.(response_format); when a JSON answer was requested and the
provider answers 400/422 (a model that takes no response_format), runs
it once more with nil.
Types
Functions
Appends suffix to the last user message (string content or content
parts). Messages with atom or string keys are both handled.
@spec parse(String.t() | nil, boolean()) :: {:ok, map() | list() | nil} | {:error, {:no_json_in_response, String.t() | nil}}
Parses the model's text as a JSON object (or array): the whole text
first, then a fenced block anywhere in it, then the outermost {…} —
models wrap answers in prose and fences however they were told. With
requested? false returns {:ok, nil}.
The sentence to append to the user's prompt and the response_format
to send, or {nil, nil} when no JSON was asked for.
Whether the caller asked for a JSON answer.
@spec with_fallback(format(), (format() -> {:ok, term()} | {:error, term()})) :: {:ok, term()} | {:error, term()}
Runs fun.(response_format); when a JSON answer was requested and the
provider answers 400/422 (a model that takes no response_format), runs
it once more with nil.