Current section

Files

Jump to
codex_sdk examples live_rate_limits.exs
Raw

examples/live_rate_limits.exs

Mix.Task.run("app.start")
Code.require_file(Path.expand("support/example_helper.exs", __DIR__))
alias CodexExamples.Support
Support.init!()
alias Codex.{Error, Events, Items, RunResultStreaming, Thread, TransportError}
alias Codex.Protocol.RateLimit, as: RateLimitSnapshot
defmodule LiveRateLimits do
@moduledoc false
@default_prompt "Summarize the top-level structure of this repository in 3 bullets."
def main(argv) do
prompt = parse_prompt(argv)
codex_opts = Support.codex_options!()
{:ok, thread} =
Codex.start_thread(
codex_opts,
Support.thread_opts!(%{working_directory: Support.example_working_directory()})
)
case Thread.run_streamed(thread, prompt) do
{:ok, result} ->
try do
state =
result
|> RunResultStreaming.raw_events()
|> Enum.reduce(%{last_snapshot: nil, final_response: nil}, &handle_event/2)
IO.puts("\nFinal response:\n#{state.final_response || "<none>"}")
IO.puts("\nLatest rate limit snapshot:")
IO.inspect(state.last_snapshot || "<none>")
if is_nil(state.last_snapshot) do
IO.puts(
"No rate-limit update event was emitted during this turn; this can happen for short/local runs."
)
end
rescue
error in [Error, TransportError] ->
render_transport_error(error)
end
{:error, reason} ->
IO.puts("Failed to start streamed turn: #{inspect(reason)}")
end
end
defp handle_event(
%Events.ThreadTokenUsageUpdated{rate_limits: %RateLimitSnapshot.Snapshot{} = snapshot},
state
) do
IO.puts("thread/tokenUsage/updated rate limits: #{inspect(snapshot)}")
%{state | last_snapshot: snapshot}
end
defp handle_event(
%Events.AccountRateLimitsUpdated{rate_limits: %RateLimitSnapshot.Snapshot{} = snapshot},
state
) do
IO.puts("account/rateLimits/updated: #{inspect(snapshot)}")
%{state | last_snapshot: snapshot}
end
defp handle_event(%Events.ItemCompleted{item: %Items.AgentMessage{text: text}}, state) do
%{state | final_response: text}
end
defp handle_event(%Events.TurnCompleted{final_response: response}, state) do
%{state | final_response: extract_text(response) || state.final_response}
end
defp handle_event(_event, state), do: state
defp extract_text(%Items.AgentMessage{text: text}) when is_binary(text), do: text
defp extract_text(%{"type" => "text", "text" => text}) when is_binary(text), do: text
defp extract_text(%{type: "text", text: text}) when is_binary(text), do: text
defp extract_text(other) when is_binary(other), do: other
defp extract_text(_), do: nil
defp parse_prompt([prompt | _]), do: prompt
defp parse_prompt(_argv), do: @default_prompt
defp render_transport_error(%Error{} = error) do
status = error.details[:exit_status]
stderr = error.details[:stderr]
IO.puts("""
Failed to run codex#{format_exit_status(status)}.
#{error.message}
Ensure the codex CLI is installed on PATH and you're logged in (or set CODEX_API_KEY).
stderr: #{String.trim(to_string(stderr || ""))}
""")
end
defp render_transport_error(%TransportError{exit_status: status, stderr: stderr}) do
IO.puts("""
Failed to run codex (exit #{inspect(status)}).
Ensure the codex CLI is installed on PATH and you're logged in (or set CODEX_API_KEY).
stderr: #{String.trim(to_string(stderr || ""))}
""")
end
defp format_exit_status(nil), do: ""
defp format_exit_status(status), do: " (exit #{inspect(status)})"
end
LiveRateLimits.main(System.argv())