mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
CXC-410 Emit Env Var Status with `/feedback` report Add more observability on top of #14611 [Unset](https://openai.sentry.io/issues/7340419168/?project=4510195390611458&query=019cfa8d-c1ba-7002-96fa-e35fc340551d&referrer=issue-stream) [Set](https://openai.sentry.io/issues/7340426331/?project=4510195390611458&query=019cfa91-aba1-7823-ab7e-762edfbc0ed4&referrer=issue-stream) <img width="1063" height="610" alt="image" src="https://github.com/user-attachments/assets/937ab026-1c2d-4757-81d5-5f31b853113e" /> ###### Summary - Adds auth-env telemetry that records whether key auth-related env overrides were present on session start and request paths. - Threads those auth-env fields through `/responses`, websocket, and `/models` telemetry and feedback metadata. - Buckets custom provider `env_key` configuration to a safe `"configured"` value instead of emitting raw config text. - Keeps the slice observability-only: no raw token values or raw URLs are emitted. ###### Rationale (from spec findings) - 401 and auth-path debugging needs a way to distinguish env-driven auth paths from sessions with no auth env override. - Startup and model-refresh failures need the same auth-env diagnostics as normal request failures. - Feedback and Sentry tags need the same auth-env signal as OTel events so reports can be triaged consistently. - Custom provider config is user-controlled text, so the telemetry contract must stay presence-only / bucketed. ###### Scope - Adds a small `AuthEnvTelemetry` bundle for env presence collection and threads it through the main request/session telemetry paths. - Does not add endpoint/base-url/provider-header/geo routing attribution or broader telemetry API redesign. ###### Trade-offs - `provider_env_key_name` is bucketed to `"configured"` instead of preserving the literal configured env var name. - `/models` is included because startup/model-refresh auth failures need the same diagnostics, but broader parity work remains out of scope. - This slice keeps the existing telemetry APIs and layers auth-env fields onto them rather than redesigning the metadata model. ###### Client follow-up - Add the separate endpoint/base-url attribution slice if routing-source diagnosis is still needed. - Add provider-header or residency attribution only if auth-env presence proves insufficient in real reports. - Revisit whether any additional auth-related env inputs need safe bucketing after more 401 triage data. ###### Testing - `cargo test -p codex-core emit_feedback_request_tags -- --nocapture` - `cargo test -p codex-core collect_auth_env_telemetry_buckets_provider_env_key_name -- --nocapture` - `cargo test -p codex-core models_request_telemetry_emits_auth_env_feedback_tags_on_failure -- --nocapture` - `cargo test -p codex-otel otel_export_routing_policy_routes_api_request_auth_observability -- --nocapture` - `cargo test -p codex-otel otel_export_routing_policy_routes_websocket_connect_auth_observability -- --nocapture` - `cargo test -p codex-otel otel_export_routing_policy_routes_websocket_request_transport_observability -- --nocapture` - `cargo test -p codex-core --no-run --message-format short` - `cargo test -p codex-otel --no-run --message-format short` --------- Co-authored-by: Codex <noreply@openai.com>
86 lines
3.1 KiB
Rust
86 lines
3.1 KiB
Rust
use codex_otel::AuthEnvTelemetryMetadata;
|
|
|
|
use crate::auth::CODEX_API_KEY_ENV_VAR;
|
|
use crate::auth::OPENAI_API_KEY_ENV_VAR;
|
|
use crate::auth::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
|
|
use crate::model_provider_info::ModelProviderInfo;
|
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
|
pub(crate) struct AuthEnvTelemetry {
|
|
pub(crate) openai_api_key_env_present: bool,
|
|
pub(crate) codex_api_key_env_present: bool,
|
|
pub(crate) codex_api_key_env_enabled: bool,
|
|
pub(crate) provider_env_key_name: Option<String>,
|
|
pub(crate) provider_env_key_present: Option<bool>,
|
|
pub(crate) refresh_token_url_override_present: bool,
|
|
}
|
|
|
|
impl AuthEnvTelemetry {
|
|
pub(crate) fn to_otel_metadata(&self) -> AuthEnvTelemetryMetadata {
|
|
AuthEnvTelemetryMetadata {
|
|
openai_api_key_env_present: self.openai_api_key_env_present,
|
|
codex_api_key_env_present: self.codex_api_key_env_present,
|
|
codex_api_key_env_enabled: self.codex_api_key_env_enabled,
|
|
provider_env_key_name: self.provider_env_key_name.clone(),
|
|
provider_env_key_present: self.provider_env_key_present,
|
|
refresh_token_url_override_present: self.refresh_token_url_override_present,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn collect_auth_env_telemetry(
|
|
provider: &ModelProviderInfo,
|
|
codex_api_key_env_enabled: bool,
|
|
) -> AuthEnvTelemetry {
|
|
AuthEnvTelemetry {
|
|
openai_api_key_env_present: env_var_present(OPENAI_API_KEY_ENV_VAR),
|
|
codex_api_key_env_present: env_var_present(CODEX_API_KEY_ENV_VAR),
|
|
codex_api_key_env_enabled,
|
|
// Custom provider `env_key` is arbitrary config text, so emit only a safe bucket.
|
|
provider_env_key_name: provider.env_key.as_ref().map(|_| "configured".to_string()),
|
|
provider_env_key_present: provider.env_key.as_deref().map(env_var_present),
|
|
refresh_token_url_override_present: env_var_present(REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR),
|
|
}
|
|
}
|
|
|
|
fn env_var_present(name: &str) -> bool {
|
|
match std::env::var(name) {
|
|
Ok(value) => !value.trim().is_empty(),
|
|
Err(std::env::VarError::NotUnicode(_)) => true,
|
|
Err(std::env::VarError::NotPresent) => false,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[test]
|
|
fn collect_auth_env_telemetry_buckets_provider_env_key_name() {
|
|
let provider = ModelProviderInfo {
|
|
name: "Custom".to_string(),
|
|
base_url: None,
|
|
env_key: Some("sk-should-not-leak".to_string()),
|
|
env_key_instructions: None,
|
|
experimental_bearer_token: None,
|
|
wire_api: crate::model_provider_info::WireApi::Responses,
|
|
query_params: None,
|
|
http_headers: None,
|
|
env_http_headers: None,
|
|
request_max_retries: None,
|
|
stream_max_retries: None,
|
|
stream_idle_timeout_ms: None,
|
|
requires_openai_auth: false,
|
|
supports_websockets: false,
|
|
};
|
|
|
|
let telemetry = collect_auth_env_telemetry(&provider, false);
|
|
|
|
assert_eq!(
|
|
telemetry.provider_env_key_name,
|
|
Some("configured".to_string())
|
|
);
|
|
}
|
|
}
|