Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
# Setup Guide - Codex with Runtime Provider Overrides

This fork adds environment variable support for overriding provider configuration at runtime.

## Prerequisites

- **Rust toolchain** (1.70 or newer)
- **Node.js** 16+ (for the CLI wrapper)
- **Git**

Install Rust if needed:
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
```

## Installation Steps

### 1. Clone the Fork

```bash
git clone https://github.com/daniel-salib/codex.git
cd codex
git checkout runtime-provider-overrides
```

Or if you already have it cloned:
```bash
cd /path/to/codex
git fetch origin
git checkout runtime-provider-overrides
git pull
```

### 2. Build the Rust Binary

```bash
cd codex-rs
cargo build --release
# Or if behind a corporate proxy:
# with-proxy cargo build --release
```

This will take several minutes on first build. The binary will be at:
`codex-rs/target/release/codex`

### 3. Install to Vendor Directory

Copy the compiled binary to where the CLI wrapper expects it:

```bash
# From the codex root directory
mkdir -p codex-cli/vendor/x86_64-unknown-linux-musl/codex
cp codex-rs/target/release/codex codex-cli/vendor/x86_64-unknown-linux-musl/codex/codex
chmod +x codex-cli/vendor/x86_64-unknown-linux-musl/codex/codex
```

### 4. Make CLI Wrapper Executable

The Node.js wrapper needs execute permissions:

```bash
# From the codex root directory
chmod +x codex-cli/bin/codex.js
```

### 5. Install Globally (Choose One Method)

#### Option A: Symlink (Recommended for Development)

```bash
# Make sure ~/bin exists and is in your PATH
mkdir -p ~/bin

# Create symlink
ln -sf "$(pwd)/codex-cli/bin/codex.js" ~/bin/codex

# Add to PATH if needed (add to ~/.bashrc or ~/.zshrc)
export PATH="$HOME/bin:$PATH"
```

### 6. Verify Installation

Test that codex is working:

```bash
codex --version
# Should output: codex-cli 0.0.0
```

If you get a "Permission denied" error, make sure you completed Step 4 (making codex.js executable).

### 7. Configure Codex for Local vLLM

Create a codex config file that defines a vLLM provider:

```bash
mkdir -p ~/.codex

cat > ~/.codex/config.toml << 'EOF'
# Default model name - vLLM typically exposes models as "default"
model = "default"

# Default provider to use
model_provider = "vllm-local"

[model_providers.vllm-local]
name = "Local vLLM"
# Fallback values - these can be overridden by environment variables (see next step)
base_url = "http://localhost:8000/v1"
wire_api = "responses"
env_key = "OPENAI_API_KEY"
EOF
```

### 8. Set Up Environment Variables

If you want to easily switch between different vLLM servers without editing config files:

```bash
# Add to ~/.bashrc or ~/.zshrc
export OPENAI_API_KEY="dummy-key-for-local-vllm"

# Runtime provider overrides - these override config.toml values!
export CODEX_PROVIDER_VLLM_LOCAL_BASE_URL="http://localhost:8000/v1"
export CODEX_PROVIDER_VLLM_LOCAL_WIRE_API="responses"

export PATH="$HOME/bin:$PATH"
```

Then reload your shell config:

```bash
source ~/.bashrc # or source ~/.zshrc
```

### 9. Test Your Setup

Make sure your vLLM server is running on port 8000, then test codex:

```bash
# Simple test
codex exec --dangerously-bypass-approvals-and-sandbox "what's 2+2?"

# You should see output like:
# --------
# workdir: /your/current/directory
# model: default
# provider: vllm-local
# approval: never
# sandbox: danger-full-access
# --------
```

### 10. Changing vLLM Server Ports (Optional)

**If you used Option A (Simple Setup):**

Edit your config file to change the port:
```bash
nano ~/.codex/config.toml
# Change: base_url = "http://localhost:9000/v1"
```

**If you used Option B (Runtime Overrides):**

Just update the environment variable (no config file editing needed!):
```bash
export CODEX_PROVIDER_VLLM_LOCAL_BASE_URL="http://localhost:9000/v1"
# That's it! This demonstrates the power of runtime overrides.
```

## Usage

### Environment Variable Overrides (This Fork's Feature)

This fork allows you to override provider settings via environment variables **without modifying config files**:

```bash
# Format: CODEX_PROVIDER_<PROVIDER_ID>_BASE_URL and CODEX_PROVIDER_<PROVIDER_ID>_WIRE_API
# The PROVIDER_ID is the kebab-case provider name converted to SCREAMING_SNAKE_CASE
# Example: "vllm-local" becomes "VLLM_LOCAL"

export CODEX_PROVIDER_VLLM_LOCAL_BASE_URL="http://localhost:8000/v1"
export CODEX_PROVIDER_VLLM_LOCAL_WIRE_API="responses"

# Now any codex command automatically uses these overrides
codex exec --dangerously-bypass-approvals-and-sandbox "your prompt here"
```

**Benefits:**
- Change provider URL without editing config files
- Switch between different vLLM servers easily
- Great for testing and development

**Note:** The provider must already exist in your `~/.codex/config.toml`. These environment variables only override the `base_url` and `wire_api` fields.

### Alternative: Config Overrides with -c Flag

You can also override settings directly on the command line (works with any codex version):

```bash
# Override specific fields (must be on one line, no line breaks in the TOML)
codex exec -c "model_providers.vllm-local.base_url=http://localhost:9000/v1" -c "model_providers.vllm-local.wire_api=responses" --dangerously-bypass-approvals-and-sandbox "what's 2+2?"

# Or define a completely new provider inline (all on one line!)
codex exec -c "model_providers.my-vllm={ name = 'My vLLM', base_url = 'http://localhost:8000/v1', wire_api = 'responses', env_key = 'OPENAI_API_KEY' }" -c model_provider="my-vllm" -c model="default" --dangerously-bypass-approvals-and-sandbox "what's 2+2?"
```

**Important:** When using `-c` with inline TOML tables (`{ ... }`), the entire command must be on **one line** with no line breaks inside the TOML string, or the parser will fail.

## Troubleshooting

### Error: "Missing environment variable: OPENAI_API_KEY"
Set a dummy value:
```bash
export OPENAI_API_KEY="dummy-key-for-local-vllm"
```

### Error: "The model `gpt-5-codex` does not exist"
Update your config to use the correct model name (usually "default" for vLLM):
```bash
# Add to ~/.codex/config.toml
model = "default"
```

### Error: "unexpected status 404 Not Found"
Check that your vLLM server is running and accessible:
```bash
curl http://localhost:8000/v1/models
```

### Error: "invalid type: string ... expected struct ModelProviderInfo"
You have line breaks in your `-c` TOML inline table. Put the entire command on one line.
75 changes: 74 additions & 1 deletion codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,75 @@ pub struct Config {
pub otel: crate::config::types::OtelConfig,
}

/// Apply environment variable overrides to a model provider configuration.
///
/// This function checks for provider-specific environment variables that can
/// override the base_url and wire_api settings at runtime without modifying
/// config files. The environment variables follow this pattern:
///
/// - `CODEX_PROVIDER_<PROVIDER_ID>_BASE_URL` - Override the base URL
/// - `CODEX_PROVIDER_<PROVIDER_ID>_WIRE_API` - Override the wire API ("chat" or "responses")
///
/// Where `<PROVIDER_ID>` is the uppercased provider ID with hyphens converted to underscores.
/// For example, for provider "vllm-local":
/// - `CODEX_PROVIDER_VLLM_LOCAL_BASE_URL`
/// - `CODEX_PROVIDER_VLLM_LOCAL_WIRE_API`
fn apply_provider_env_overrides(provider: &mut ModelProviderInfo, provider_id: &str) {
use crate::model_provider_info::WireApi;

// Convert provider ID to environment variable suffix
// e.g., "vllm-local" -> "VLLM_LOCAL"
let provider_suffix = provider_id.to_uppercase().replace('-', "_");

// Check for base URL override
let base_url_env = format!("CODEX_PROVIDER_{}_BASE_URL", provider_suffix);
if let Ok(base_url) = std::env::var(&base_url_env) {
let trimmed = base_url.trim();
if !trimmed.is_empty() {
tracing::info!(
"Overriding provider '{}' base_url from environment variable {}: {}",
provider_id,
base_url_env,
trimmed
);
provider.base_url = Some(trimmed.to_string());
}
}

// Check for wire API override
let wire_api_env = format!("CODEX_PROVIDER_{}_WIRE_API", provider_suffix);
if let Ok(wire_api_str) = std::env::var(&wire_api_env) {
let trimmed = wire_api_str.trim().to_lowercase();
if !trimmed.is_empty() {
match trimmed.as_str() {
"responses" => {
tracing::info!(
"Overriding provider '{}' wire_api from environment variable {}: responses",
provider_id,
wire_api_env
);
provider.wire_api = WireApi::Responses;
}
"chat" => {
tracing::info!(
"Overriding provider '{}' wire_api from environment variable {}: chat",
provider_id,
wire_api_env
);
provider.wire_api = WireApi::Chat;
}
_ => {
tracing::warn!(
"Invalid value for {}: '{}' (expected 'chat' or 'responses')",
wire_api_env,
trimmed
);
}
}
}
}
}

impl Config {
pub async fn load_with_cli_overrides(
cli_overrides: Vec<(String, TomlValue)>,
Expand Down Expand Up @@ -1070,7 +1139,7 @@ impl Config {
.or(config_profile.model_provider)
.or(cfg.model_provider)
.unwrap_or_else(|| "openai".to_string());
let model_provider = model_providers
let mut model_provider = model_providers
.get(&model_provider_id)
.ok_or_else(|| {
std::io::Error::new(
Expand All @@ -1080,6 +1149,10 @@ impl Config {
})?
.clone();

// Apply environment variable overrides for provider configuration
// This allows runtime override of base_url and wire_api without modifying config files
apply_provider_env_overrides(&mut model_provider, &model_provider_id);

let shell_environment_policy = cfg.shell_environment_policy.into();

let history = cfg.history.unwrap_or_default();
Expand Down
Loading