Commit Graph

10 Commits

  • Add support for a separate chatgpt auth endpoint (#1712)
    Adds a `CodexAuth` type that encapsulates information about available
    auth modes and logic for refreshing the token.
    Changes `Responses` API to send requests to different endpoints based on
    the auth type.
    Updates login_with_chatgpt to support API-less mode and skip the key
    exchange.
  • Refactor env settings into config (#1601)
    ## Summary
    - add OpenAI retry and timeout fields to Config
    - inject these settings in tests instead of mutating env vars
    - plumb Config values through client and chat completions logic
    - document new configuration options
    
    ## Testing
    - `cargo test -p codex-core --no-run`
    
    ------
    https://chatgpt.com/codex/tasks/task_i_68792c5b04cc832195c03050c8b6ea94
    
    ---------
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • feat: honor OPENAI_BASE_URL for the built-in openai provider (#1487)
    Some users have proxies or other setups where they are ultimately
    hitting OpenAI endpoints, but need a custom `base_url` rather than the
    default value of `"https://api.openai.com/v1"`. This PR makes it
    possible to override the `base_url` for the `openai` provider via the
    `OPENAI_BASE_URL` environment variable.
  • feat: support custom HTTP headers for model providers (#1473)
    This adds support for two new model provider config options:
    
    - `http_headers` for hardcoded (key, value) pairs
    - `env_http_headers` for headers whose values should be read from
    environment variables
    
    This also updates the built-in `openai` provider to use this feature to
    set the following headers:
    
    - `originator` => `codex_cli_rs`
    - `version` => [CLI version]
    - `OpenAI-Organization` => `OPENAI_ORGANIZATION` env var
    - `OpenAI-Project` => `OPENAI_PROJECT` env var
    
    for consistency with the TypeScript implementation:
    
    
    https://github.com/openai/codex/blob/bd5a9e8ba96c7d9c58ecaf5e61ec62d14ac6378d/codex-cli/src/utils/agent/agent-loop.ts#L321-L329
    
    While here, this also consolidates some logic that was duplicated across
    `client.rs` and `chat_completions.rs` by introducing
    `ModelProviderInfo.create_request_builder()`.
    
    Resolves https://github.com/openai/codex/discussions/1152
  • feat: add query_params option to ModelProviderInfo to support Azure (#1435)
    As discovered in https://github.com/openai/codex/issues/1365, the Azure
    provider needs to be able to specify `api-version` as a query param, so
    this PR introduces a generic `query_params` option to the
    `model_providers` config so that an Azure provider can be defined as
    follows:
    
    ```toml
    [model_providers.azure]
    name = "Azure"
    base_url = "https://YOUR_PROJECT_NAME.openai.azure.com/openai"
    env_key = "AZURE_OPENAI_API_KEY"
    query_params = { api-version = "2025-04-01-preview" }
    ```
    
    This PR also updates the docs with this example.
    
    While here, we also update `wire_api` to default to `"chat"`, as that is
    likely the common case for someone defining an external provider.
    
    Fixes https://github.com/openai/codex/issues/1365.
  • chore: change built_in_model_providers so "openai" is the only "bundled" provider (#1407)
    As we are [close to releasing the Rust CLI
    beta](https://github.com/openai/codex/discussions/1405), for the moment,
    let's take a more neutral stance on what it takes to be a "built-in"
    provider.
    
    * For example, there seems to be a discrepancy around what the "right"
    configuration for Gemini is: https://github.com/openai/codex/pull/881
    * And while the current list of "built-in" providers are all arguably
    "well-known" names, this raises a question of what to do about
    potentially less familiar providers, such as
    https://github.com/openai/codex/pull/1142. Do we just accept every pull
    request like this, or is there some criteria a provider has to meet to
    "qualify" to be bundled with Codex CLI?
    
    I think that if we can establish clear ground rules for being a built-in
    provider, then we can bring this back. But until then, I would rather
    take a minimalist approach because if we decided to reverse our position
    later, it would break folks who were depending on the presence of the
    built-in providers.
  • feat: add support for login with ChatGPT (#1212)
    This does not implement the full Login with ChatGPT experience, but it
    should unblock people.
    
    **What works**
    
    * The `codex` multitool now has a `login` subcommand, so you can run
    `codex login`, which should write `CODEX_HOME/auth.json` if you complete
    the flow successfully. The TUI will now read the `OPENAI_API_KEY` from
    `auth.json`.
    * The TUI should refresh the token if it has expired and the necessary
    information is in `auth.json`.
    * There is a `LoginScreen` in the TUI that tells you to run `codex
    login` if both (1) your model provider expects to use `OPENAI_API_KEY`
    as its env var, and (2) `OPENAI_API_KEY` is not set.
    
    **What does not work**
    
    * The `LoginScreen` does not support the login flow from within the TUI.
    Instead, it tells you to quit, run `codex login`, and then run `codex`
    again.
    * `codex exec` does read from `auth.json` yet, nor does it direct the
    user to go through the login flow if `OPENAI_API_KEY` is not be found.
    * The `maybeRedeemCredits()` function from `get-api-key.tsx` has not
    been ported from TypeScript to `login_with_chatgpt.py` yet:
    
    
    https://github.com/openai/codex/blob/a67a67f3258fc21e147b6786a143fe3e15e6d5ba/codex-cli/src/utils/get-api-key.tsx#L84-L89
    
    **Implementation**
    
    Currently, the OAuth flow requires running a local webserver on
    `127.0.0.1:1455`. It seemed wasteful to incur the additional binary cost
    of a webserver dependency in the Rust CLI just to support login, so
    instead we implement this logic in Python, as Python has a `http.server`
    module as part of its standard library. Specifically, we bundle the
    contents of a single Python file as a string in the Rust CLI and then
    use it to spawn a subprocess as `python3 -c
    {{SOURCE_FOR_PYTHON_SERVER}}`.
    
    As such, the most significant files in this PR are:
    
    ```
    codex-rs/login/src/login_with_chatgpt.py
    codex-rs/login/src/lib.rs
    ```
    
    Now that the CLI may load `OPENAI_API_KEY` from the environment _or_
    `CODEX_HOME/auth.json`, we need a new abstraction for reading/writing
    this variable, so we introduce:
    
    ```
    codex-rs/core/src/openai_api_key.rs
    ```
    
    Note that `std::env::set_var()` is [rightfully] `unsafe` in Rust 2024,
    so we use a LazyLock<RwLock<Option<String>>> to store `OPENAI_API_KEY`
    so it is read in a thread-safe manner.
    
    Ultimately, it should be possible to go through the entire login flow
    from the TUI. This PR introduces a placeholder `LoginScreen` UI for that
    right now, though the new `codex login` subcommand introduced in this PR
    should be a viable workaround until the UI is ready.
    
    **Testing**
    
    Because the login flow is currently implemented in a standalone Python
    file, you can test it without building any Rust code as follows:
    
    ```
    rm -rf /tmp/codex_home && mkdir /tmp/codex_home
    CODEX_HOME=/tmp/codex_home python3 codex-rs/login/src/login_with_chatgpt.py
    ```
    
    For reference:
    
    * the original TypeScript implementation was introduced in
    https://github.com/openai/codex/pull/963
    * support for redeeming credits was later added in
    https://github.com/openai/codex/pull/974
  • feat: introduce --profile for Rust CLI (#921)
    This introduces a much-needed "profile" concept where users can specify
    a collection of options under one name and then pass that via
    `--profile` to the CLI.
    
    This PR introduces the `ConfigProfile` struct and makes it a field of
    `CargoToml`. It further updates
    `Config::load_from_base_config_with_overrides()` to respect
    `ConfigProfile`, overriding default values where appropriate. A detailed
    unit test is added at the end of `config.rs` to verify this behavior.
    
    Details on how to use this feature have also been added to
    `codex-rs/README.md`.
  • feat: support the chat completions API in the Rust CLI (#862)
    This is a substantial PR to add support for the chat completions API,
    which in turn makes it possible to use non-OpenAI model providers (just
    like in the TypeScript CLI):
    
    * It moves a number of structs from `client.rs` to `client_common.rs` so
    they can be shared.
    * It introduces support for the chat completions API in
    `chat_completions.rs`.
    * It updates `ModelProviderInfo` so that `env_key` is `Option<String>`
    instead of `String` (for e.g., ollama) and adds a `wire_api` field
    * It updates `client.rs` to choose between `stream_responses()` and
    `stream_chat_completions()` based on the `wire_api` for the
    `ModelProviderInfo`
    * It updates the `exec` and TUI CLIs to no longer fail if the
    `OPENAI_API_KEY` environment variable is not set
    * It updates the TUI so that `EventMsg::Error` is displayed more
    prominently when it occurs, particularly now that it is important to
    alert users to the `CodexErr::EnvVar` variant.
    * `CodexErr::EnvVar` was updated to include an optional `instructions`
    field so we can preserve the behavior where we direct users to
    https://platform.openai.com if `OPENAI_API_KEY` is not set.
    * Cleaned up the "welcome message" in the TUI to ensure the model
    provider is displayed.
    * Updated the docs in `codex-rs/README.md`.
    
    To exercise the chat completions API from OpenAI models, I added the
    following to my `config.toml`:
    
    ```toml
    model = "gpt-4o"
    model_provider = "openai-chat-completions"
    
    [model_providers.openai-chat-completions]
    name = "OpenAI using Chat Completions"
    base_url = "https://api.openai.com/v1"
    env_key = "OPENAI_API_KEY"
    wire_api = "chat"
    ```
    
    Though to test a non-OpenAI provider, I installed ollama with mistral
    locally on my Mac because ChatGPT said that would be a good match for my
    hardware:
    
    ```shell
    brew install ollama
    ollama serve
    ollama pull mistral
    ```
    
    Then I added the following to my `~/.codex/config.toml`:
    
    ```toml
    model = "mistral"
    model_provider = "ollama"
    ```
    
    Note this code could certainly use more test coverage, but I want to get
    this in so folks can start playing with it.
    
    For reference, I believe https://github.com/openai/codex/pull/247 was
    roughly the comparable PR on the TypeScript side.
  • feat: read model_provider and model_providers from config.toml (#853)
    This is the first step in supporting other model providers in the Rust
    CLI. Specifically, this PR adds support for the new entries in `Config`
    and `ConfigOverrides` to specify a `ModelProviderInfo`, which is the
    basic config needed for an LLM provider. This PR does not get us all the
    way there yet because `client.rs` still categorically appends
    `/responses` to the URL and expects the endpoint to support the OpenAI
    Responses API. Will fix that next!