Current section

Files

Jump to
codex_sdk lib codex runtime exec profile.ex
Raw

lib/codex/runtime/exec/profile.ex

defmodule Codex.Runtime.Exec.Profile do
@moduledoc false
@behaviour CliSubprocessCore.ProviderProfile
alias CliSubprocessCore.ProviderProfiles.Codex, as: CoreCodex
alias CliSubprocessCore.ProviderProfiles.Shared
@impl true
def id, do: CoreCodex.id()
@impl true
def capabilities, do: CoreCodex.capabilities()
@impl true
def build_invocation(opts) when is_list(opts) do
with {:ok, command_spec} <- Shared.resolve_command_spec(opts, :codex, "codex", [:binary_path]) do
args =
["exec", "--json"]
|> Shared.maybe_add_pair("--profile", Keyword.get(opts, :cli_profile))
|> Shared.maybe_add_flag("--full-auto", Keyword.get(opts, :full_auto))
|> Shared.maybe_add_flag(
"--dangerously-bypass-approvals-and-sandbox",
Keyword.get(opts, :dangerously_bypass_approvals_and_sandbox)
)
|> Shared.maybe_add_pair("--model", model_value(opts))
|> Shared.maybe_add_pair("--color", Keyword.get(opts, :color))
|> Shared.maybe_add_pair("--output-last-message", Keyword.get(opts, :output_last_message))
|> Shared.maybe_add_pair("--sandbox", Keyword.get(opts, :sandbox))
|> Shared.maybe_add_pair("--cd", Keyword.get(opts, :working_directory))
|> Shared.maybe_add_repeat("--add-dir", Keyword.get(opts, :additional_directories, []))
|> Shared.maybe_add_flag("--skip-git-repo-check", Keyword.get(opts, :skip_git_repo_check))
|> Shared.maybe_add_repeat("--config", config_values(opts))
|> Kernel.++(normalize_string_list(Keyword.get(opts, :subcommand_args, [])))
|> Shared.maybe_add_pair("--continuation-token", Keyword.get(opts, :continuation_token))
|> Shared.maybe_add_pair("--cancellation-token", Keyword.get(opts, :cancellation_token))
|> Shared.maybe_add_repeat("--image", Keyword.get(opts, :images, []))
|> maybe_add_output_schema(Keyword.get(opts, :output_schema))
|> maybe_add_prompt(Keyword.get(opts, :prompt))
{:ok,
Shared.command(command_spec, args,
cwd: Keyword.get(opts, :cwd),
env: normalize_env(Keyword.get(opts, :env, %{}))
)}
end
end
@impl true
def init_parser_state(opts), do: CoreCodex.init_parser_state(opts)
@impl true
def decode_stdout(line, state), do: CoreCodex.decode_stdout(line, state)
@impl true
def decode_stderr(chunk, state), do: CoreCodex.decode_stderr(chunk, state)
@impl true
def handle_exit(reason, state), do: CoreCodex.handle_exit(reason, state)
@impl true
def transport_options(opts), do: CoreCodex.transport_options(opts)
defp maybe_add_output_schema(args, nil), do: args
defp maybe_add_output_schema(args, schema) when is_binary(schema),
do: args ++ ["--output-schema", schema]
defp maybe_add_output_schema(args, schema) when is_map(schema) or is_list(schema) do
Shared.maybe_add_json_pair(args, "--output-schema", schema)
end
defp maybe_add_output_schema(args, _other), do: args
defp maybe_add_prompt(args, value) when is_binary(value) and value != "", do: args ++ [value]
defp maybe_add_prompt(args, _value), do: args
defp normalize_string_list(values) when is_list(values) do
Enum.flat_map(values, fn
value when is_binary(value) and value != "" -> [value]
_value -> []
end)
end
defp normalize_string_list(_values), do: []
defp model_value(opts) do
if local_provider_value(opts) do
nil
else
Keyword.get(opts, :model) || model_payload_value(opts, :resolved_model)
end
end
defp local_provider_value(opts) do
Keyword.get(opts, :local_provider) ||
Map.get(model_payload_backend_metadata(opts), "oss_provider")
end
defp config_values(opts) do
payload_values =
model_payload_backend_metadata(opts)
|> Map.get("config_values", [])
|> List.wrap()
|> Enum.filter(&(is_binary(&1) and &1 != ""))
(local_model_provider_config_values(opts) ++
Keyword.get(opts, :config_values, []) ++
payload_values)
|> Enum.uniq()
end
defp local_model_provider_config_values(opts) do
model = Keyword.get(opts, :model) || model_payload_value(opts, :resolved_model)
case {local_provider_value(opts), model} do
{provider, model}
when is_binary(provider) and provider != "" and is_binary(model) and model != "" ->
[~s(model_provider="#{provider}"), ~s(model="#{model}")]
_other ->
[]
end
end
defp model_payload_backend_metadata(opts) do
opts
|> Keyword.get(:model_payload, %{})
|> case do
payload when is_map(payload) ->
Map.get(payload, :backend_metadata, Map.get(payload, "backend_metadata", %{}))
_ ->
%{}
end
|> case do
metadata when is_map(metadata) -> metadata
_ -> %{}
end
end
defp model_payload_value(opts, key) when is_atom(key) do
opts
|> Keyword.get(:model_payload, %{})
|> case do
payload when is_map(payload) -> Map.get(payload, key, Map.get(payload, Atom.to_string(key)))
_ -> nil
end
end
defp normalize_env(nil), do: %{}
defp normalize_env(env) when is_map(env) do
Map.new(env, fn {key, value} -> {to_string(key), to_string(value)} end)
end
defp normalize_env(env) when is_list(env) do
env
|> Enum.filter(&match?({_, _}, &1))
|> Map.new(fn {key, value} -> {to_string(key), to_string(value)} end)
end
defp normalize_env(_env), do: %{}
end