Current section

Files

Jump to
codex_sdk examples live_thread_management.exs
Raw

examples/live_thread_management.exs

Mix.Task.run("app.start")
Code.require_file(Path.expand("support/example_helper.exs", __DIR__))
alias CodexExamples.Support
Support.init!()
alias Codex.{AppServer, Items, Thread}
alias Codex.Models
defmodule LiveThreadManagement do
@moduledoc false
@default_prompt "Say hello in one sentence."
def main(argv) do
prompt = parse_prompt(argv)
case Support.ensure_remote_working_directory() do
:ok ->
:ok
{:skip, reason} ->
IO.puts("SKIPPED: #{reason}")
System.halt(0)
end
codex_opts = Support.codex_options!()
:ok = Support.ensure_app_server_supported(codex_opts)
{:ok, conn} = AppServer.connect(codex_opts, experimental_api: true, init_timeout_ms: 30_000)
try do
{:ok, thread} =
Codex.start_thread(
codex_opts,
Support.thread_opts!(%{
transport: {:app_server, conn},
working_directory: Support.example_working_directory()
})
)
result =
case Thread.run(thread, prompt, %{timeout_ms: 120_000}) do
{:ok, result} ->
result
{:error, reason} ->
Mix.raise("thread run failed: #{inspect(reason)}")
end
thread_id = result.thread.thread_id
IO.puts("""
Thread started: #{inspect(thread_id)}
Final response: #{extract_text(result.final_response)}
""")
IO.puts("thread/read:")
IO.inspect(AppServer.thread_read(conn, thread_id, include_turns: true))
IO.puts("\nthread/inject_items:")
inject_items = [
%{
"type" => "message",
"role" => "assistant",
"content" => [
%{
"type" => "output_text",
"text" => "Synthetic checkpoint from the SDK example."
}
]
}
]
print_result_or_warning(
AppServer.thread_inject_items(conn, thread_id, inject_items),
"thread/inject_items"
)
IO.puts("\nthread/memoryMode/set disabled:")
print_result_or_warning(
AppServer.thread_memory_mode_set(conn, thread_id, :disabled),
"thread/memoryMode/set"
)
IO.puts("\nthread/fork:")
fork_params =
%{}
|> maybe_put(:model, codex_opts.model)
|> maybe_put(:config, fork_config(codex_opts.reasoning_effort))
print_result_or_warning(AppServer.thread_fork(conn, thread_id, fork_params), "thread/fork")
IO.puts("\nthread/rollback (1 turn):")
print_result_or_warning(AppServer.thread_rollback(conn, thread_id, 1), "thread/rollback")
IO.puts("\nthread/loaded/list:")
IO.inspect(AppServer.thread_loaded_list(conn))
after
:ok = AppServer.disconnect(conn)
end
end
defp print_result_or_warning({:error, %{"code" => -32600, "message" => message}}, name) do
IO.puts("#{name} is not supported by this codex app-server build.")
IO.puts("Raw error: #{message}")
end
defp print_result_or_warning(other, _name), do: IO.inspect(other)
defp fork_config(nil), do: nil
defp fork_config(effort) do
%{"model_reasoning_effort" => Models.reasoning_effort_to_string(effort)}
end
defp parse_prompt([prompt | _]), do: prompt
defp parse_prompt(_argv), do: @default_prompt
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(other), do: inspect(other)
defp maybe_put(map, _key, nil), do: map
defp maybe_put(map, key, value), do: Map.put(map, key, value)
end
LiveThreadManagement.main(System.argv())