Skip to main content

examples/structured_output.exs

# Live structured-output test against Z.AI (Anthropic format) with glm-4.5-air.
#
#   set -a && . ./.env && set +a && mix run examples/structured_output.exs
#
# The model is forced to call the `final_result` output tool whose args match a
# JSON Schema derived from an Ecto schema; we validate them with a changeset.

key = System.get_env("ZAI_API_KEY") || System.get_env("ANTHROPIC_AUTH_TOKEN")

unless key do
  IO.puts("ZAI_API_KEY not set — skipping.")
  System.halt(0)
end

defmodule Extract do
  use Ecto.Schema

  @primary_key false
  embedded_schema do
    field(:name, :string)
    field(:age, :integer)
    field(:mood, Ecto.Enum, values: [:happy, :sad, :neutral])
  end

  def changeset(s, a) do
    s
    |> Ecto.Changeset.cast(a, [:name, :age, :mood])
    |> Ecto.Changeset.validate_required([:name, :age])
    |> Ecto.Changeset.validate_number(:age, greater_than: 0, less_than: 150)
  end
end

alias ExAgent

model =
  ExAgent.Models.Anthropic.new(
    model: System.get_env("ZAI_MODEL", "glm-4.5-air"),
    auth_token: key,
    base_url: "https://api.z.ai/api/anthropic"
  )

agent =
  ExAgent.new(
    model: model,
    output: Extract,
    instructions: "Extract structured data from the user's message.",
    model_settings: [max_tokens: 512, temperature: 0]
  )

{:ok, result} =
  ExAgent.run(agent, "Hi! I'm Mara, I'm 30 years old, and I'm feeling pretty happy today.")

IO.puts("=== STRUCTURED OUTPUT ===")
IO.inspect(result.output, label: "output")
IO.puts("\nname: #{result.output.name}, age: #{result.output.age}, mood: #{result.output.mood}")
IO.puts("\n=== USAGE ===")
IO.inspect(result.usage, label: "usage")