mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Prefer websockets when providers support them (#13592)
Remove all flags and model settings. --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
+14
-30
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! `ModelClient` is intended to live for the lifetime of a Codex session and holds the stable
|
||||
//! configuration and state needed to talk to a provider (auth, provider selection, conversation id,
|
||||
//! and feature-gated request behavior).
|
||||
//! and transport fallback state).
|
||||
//!
|
||||
//! Per-turn settings (model selection, reasoning controls, telemetry context, and turn metadata)
|
||||
//! are passed explicitly to streaming and unary methods so that the turn lifetime is visible at the
|
||||
@@ -94,7 +94,6 @@ use crate::auth::RefreshTokenError;
|
||||
use crate::client_common::Prompt;
|
||||
use crate::client_common::ResponseEvent;
|
||||
use crate::client_common::ResponseStream;
|
||||
use crate::config::Config;
|
||||
use crate::default_client::build_reqwest_client;
|
||||
use crate::error::CodexErr;
|
||||
use crate::error::Result;
|
||||
@@ -122,14 +121,6 @@ const MEMORIES_SUMMARIZE_ENDPOINT: &str = "/memories/trace_summarize";
|
||||
#[cfg(test)]
|
||||
pub(crate) const WEBSOCKET_CONNECT_TIMEOUT: Duration =
|
||||
Duration::from_millis(crate::model_provider_info::DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS);
|
||||
pub fn ws_version_from_features(config: &Config) -> bool {
|
||||
config
|
||||
.features
|
||||
.enabled(crate::features::Feature::ResponsesWebsockets)
|
||||
|| config
|
||||
.features
|
||||
.enabled(crate::features::Feature::ResponsesWebsocketsV2)
|
||||
}
|
||||
|
||||
/// Session-scoped state shared by all [`ModelClient`] clones.
|
||||
///
|
||||
@@ -143,7 +134,6 @@ struct ModelClientState {
|
||||
auth_env_telemetry: AuthEnvTelemetry,
|
||||
session_source: SessionSource,
|
||||
model_verbosity: Option<VerbosityConfig>,
|
||||
responses_websockets_enabled_by_feature: bool,
|
||||
enable_request_compression: bool,
|
||||
include_timing_metrics: bool,
|
||||
beta_features_header: Option<String>,
|
||||
@@ -175,8 +165,7 @@ impl RequestRouteTelemetry {
|
||||
/// A session-scoped client for model-provider API calls.
|
||||
///
|
||||
/// This holds configuration and state that should be shared across turns within a Codex session
|
||||
/// (auth, provider selection, conversation id, feature-gated request behavior, and transport
|
||||
/// fallback state).
|
||||
/// (auth, provider selection, conversation id, and transport fallback state).
|
||||
///
|
||||
/// WebSocket fallback is session-scoped: once a turn activates the HTTP fallback, subsequent turns
|
||||
/// will also use HTTP for the remainder of the session.
|
||||
@@ -265,7 +254,6 @@ impl ModelClient {
|
||||
provider: ModelProviderInfo,
|
||||
session_source: SessionSource,
|
||||
model_verbosity: Option<VerbosityConfig>,
|
||||
responses_websockets_enabled_by_feature: bool,
|
||||
enable_request_compression: bool,
|
||||
include_timing_metrics: bool,
|
||||
beta_features_header: Option<String>,
|
||||
@@ -282,7 +270,6 @@ impl ModelClient {
|
||||
auth_env_telemetry,
|
||||
session_source,
|
||||
model_verbosity,
|
||||
responses_websockets_enabled_by_feature,
|
||||
enable_request_compression,
|
||||
include_timing_metrics,
|
||||
beta_features_header,
|
||||
@@ -324,9 +311,9 @@ impl ModelClient {
|
||||
pub(crate) fn force_http_fallback(
|
||||
&self,
|
||||
session_telemetry: &SessionTelemetry,
|
||||
model_info: &ModelInfo,
|
||||
_model_info: &ModelInfo,
|
||||
) -> bool {
|
||||
let websocket_enabled = self.responses_websocket_enabled(model_info);
|
||||
let websocket_enabled = self.responses_websocket_enabled();
|
||||
let activated =
|
||||
websocket_enabled && !self.state.disable_websockets.swap(true, Ordering::Relaxed);
|
||||
if activated {
|
||||
@@ -517,19 +504,16 @@ impl ModelClient {
|
||||
|
||||
/// Returns whether the Responses-over-WebSocket transport is active for this session.
|
||||
///
|
||||
/// This combines provider capability and feature gating; both must be true for websocket paths
|
||||
/// to be eligible.
|
||||
///
|
||||
/// If websockets are only enabled via model preference (no explicit feature flag), prefer the
|
||||
/// current v2 behavior.
|
||||
pub fn responses_websocket_enabled(&self, model_info: &ModelInfo) -> bool {
|
||||
/// WebSocket use is controlled by provider capability and session-scoped fallback state.
|
||||
pub fn responses_websocket_enabled(&self) -> bool {
|
||||
if !self.state.provider.supports_websockets
|
||||
|| self.state.disable_websockets.load(Ordering::Relaxed)
|
||||
|| (*CODEX_RS_SSE_FIXTURE).is_some()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
self.state.responses_websockets_enabled_by_feature || model_info.prefer_websockets
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns auth + provider configuration resolved from the current session auth state.
|
||||
@@ -868,9 +852,9 @@ impl ModelClientSession {
|
||||
pub async fn preconnect_websocket(
|
||||
&mut self,
|
||||
session_telemetry: &SessionTelemetry,
|
||||
model_info: &ModelInfo,
|
||||
_model_info: &ModelInfo,
|
||||
) -> std::result::Result<(), ApiError> {
|
||||
if !self.client.responses_websocket_enabled(model_info) {
|
||||
if !self.client.responses_websocket_enabled() {
|
||||
return Ok(());
|
||||
}
|
||||
if self.websocket_session.connection.is_some() {
|
||||
@@ -1248,7 +1232,7 @@ impl ModelClientSession {
|
||||
service_tier: Option<ServiceTier>,
|
||||
turn_metadata_header: Option<&str>,
|
||||
) -> Result<()> {
|
||||
if !self.client.responses_websocket_enabled(model_info) {
|
||||
if !self.client.responses_websocket_enabled() {
|
||||
return Ok(());
|
||||
}
|
||||
if self.websocket_session.last_request.is_some() {
|
||||
@@ -1292,8 +1276,8 @@ impl ModelClientSession {
|
||||
///
|
||||
/// The caller is responsible for passing per-turn settings explicitly (model selection,
|
||||
/// reasoning settings, telemetry context, and turn metadata). This method will prefer the
|
||||
/// Responses WebSocket transport when enabled and healthy, and will fall back to the HTTP
|
||||
/// Responses API transport otherwise.
|
||||
/// Responses WebSocket transport when the provider supports it and it remains healthy, and will
|
||||
/// fall back to the HTTP Responses API transport otherwise.
|
||||
pub async fn stream(
|
||||
&mut self,
|
||||
prompt: &Prompt,
|
||||
@@ -1307,7 +1291,7 @@ impl ModelClientSession {
|
||||
let wire_api = self.client.state.provider.wire_api;
|
||||
match wire_api {
|
||||
WireApi::Responses => {
|
||||
if self.client.responses_websocket_enabled(model_info) {
|
||||
if self.client.responses_websocket_enabled() {
|
||||
match self
|
||||
.stream_responses_websocket(
|
||||
prompt,
|
||||
|
||||
@@ -23,7 +23,6 @@ fn test_model_client(session_source: SessionSource) -> ModelClient {
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ use crate::terminal;
|
||||
use crate::truncate::TruncationPolicy;
|
||||
use crate::turn_metadata::TurnMetadataState;
|
||||
use crate::util::error_or_panic;
|
||||
use crate::ws_version_from_features;
|
||||
use async_channel::Receiver;
|
||||
use async_channel::Sender;
|
||||
use chrono::Local;
|
||||
@@ -1807,7 +1806,6 @@ impl Session {
|
||||
session_configuration.provider.clone(),
|
||||
session_configuration.session_source.clone(),
|
||||
config.model_verbosity,
|
||||
ws_version_from_features(config.as_ref()),
|
||||
config.features.enabled(Feature::EnableRequestCompression),
|
||||
config.features.enabled(Feature::RuntimeMetrics),
|
||||
Self::build_model_client_beta_features_header(config.as_ref()),
|
||||
@@ -6239,10 +6237,7 @@ async fn run_sampling_request(
|
||||
// transient reconnect messages. In debug builds, keep full visibility for diagnosis.
|
||||
let report_error = retries > 1
|
||||
|| cfg!(debug_assertions)
|
||||
|| !sess
|
||||
.services
|
||||
.model_client
|
||||
.responses_websocket_enabled(&turn_context.model_info);
|
||||
|| !sess.services.model_client.responses_websocket_enabled();
|
||||
if report_error {
|
||||
// Surface retry information to any UI/front‑end so the
|
||||
// user understands what is happening instead of staring
|
||||
|
||||
@@ -239,7 +239,6 @@ fn test_model_client_session() -> crate::client::ModelClientSession {
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.new_session()
|
||||
@@ -2513,7 +2512,6 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
session_configuration.provider.clone(),
|
||||
session_configuration.session_source.clone(),
|
||||
config.model_verbosity,
|
||||
ws_version_from_features(config.as_ref()),
|
||||
config.features.enabled(Feature::EnableRequestCompression),
|
||||
config.features.enabled(Feature::RuntimeMetrics),
|
||||
Session::build_model_client_beta_features_header(config.as_ref()),
|
||||
@@ -3308,7 +3306,6 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
||||
session_configuration.provider.clone(),
|
||||
session_configuration.session_source.clone(),
|
||||
config.model_verbosity,
|
||||
ws_version_from_features(config.as_ref()),
|
||||
config.features.enabled(Feature::EnableRequestCompression),
|
||||
config.features.enabled(Feature::RuntimeMetrics),
|
||||
Session::build_model_client_beta_features_header(config.as_ref()),
|
||||
|
||||
@@ -184,9 +184,9 @@ pub enum Feature {
|
||||
TuiAppServer,
|
||||
/// Prevent idle system sleep while a turn is actively running.
|
||||
PreventIdleSleep,
|
||||
/// Use the Responses API WebSocket transport for OpenAI by default.
|
||||
/// Legacy rollout flag for Responses API WebSocket transport experiments.
|
||||
ResponsesWebsockets,
|
||||
/// Enable Responses API websocket v2 mode.
|
||||
/// Legacy rollout flag for Responses API WebSocket transport v2 experiments.
|
||||
ResponsesWebsocketsV2,
|
||||
}
|
||||
|
||||
@@ -860,13 +860,13 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
FeatureSpec {
|
||||
id: Feature::ResponsesWebsockets,
|
||||
key: "responses_websockets",
|
||||
stage: Stage::UnderDevelopment,
|
||||
stage: Stage::Removed,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::ResponsesWebsocketsV2,
|
||||
key: "responses_websockets_v2",
|
||||
stage: Stage::UnderDevelopment,
|
||||
stage: Stage::Removed,
|
||||
default_enabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -162,7 +162,6 @@ pub(crate) use codex_shell_command::powershell;
|
||||
pub use client::ModelClient;
|
||||
pub use client::ModelClientSession;
|
||||
pub use client::X_CODEX_TURN_METADATA_HEADER;
|
||||
pub use client::ws_version_from_features;
|
||||
pub use client_common::Prompt;
|
||||
pub use client_common::REVIEW_PROMPT;
|
||||
pub use client_common::ResponseEvent;
|
||||
|
||||
@@ -88,7 +88,6 @@ pub(crate) fn model_info_from_slug(slug: &str) -> ModelInfo {
|
||||
effective_context_window_percent: 95,
|
||||
experimental_supported_tools: Vec::new(),
|
||||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: true, // this is the fallback model metadata
|
||||
supports_search_tool: false,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user