mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
feat(core): add websocket preference and rate-limit metadata
This commit is contained in:
@@ -1018,7 +1018,11 @@ pub struct ChatgptAuthTokensRefreshResponse {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct GetAccountRateLimitsResponse {
|
||||
/// Backward-compatible single-bucket view.
|
||||
pub rate_limits: RateLimitSnapshot,
|
||||
/// Multi-bucket view keyed by metered `limit_id`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub rate_limits_by_limit_id: Option<HashMap<String, RateLimitSnapshot>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
@@ -3112,6 +3116,8 @@ pub struct AccountRateLimitsUpdatedNotification {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct RateLimitSnapshot {
|
||||
pub limit_id: Option<String>,
|
||||
pub limit_name: Option<String>,
|
||||
pub primary: Option<RateLimitWindow>,
|
||||
pub secondary: Option<RateLimitWindow>,
|
||||
pub credits: Option<CreditsSnapshot>,
|
||||
@@ -3121,6 +3127,8 @@ pub struct RateLimitSnapshot {
|
||||
impl From<CoreRateLimitSnapshot> for RateLimitSnapshot {
|
||||
fn from(value: CoreRateLimitSnapshot) -> Self {
|
||||
Self {
|
||||
limit_id: value.limit_id,
|
||||
limit_name: value.limit_name,
|
||||
primary: value.primary.map(RateLimitWindow::from),
|
||||
secondary: value.secondary.map(RateLimitWindow::from),
|
||||
credits: value.credits.map(CreditsSnapshot::from),
|
||||
|
||||
@@ -103,7 +103,11 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
],
|
||||
supported_text_verbosity: &[TextVerbosityConfig::Medium],
|
||||
is_default: true,
|
||||
upgrade: None,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
show_in_picker: true,
|
||||
},
|
||||
ModelPreset {
|
||||
@@ -140,7 +144,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
supported_text_verbosity: ALL_TEXT_VERBOSITY,
|
||||
is_default: false,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.2-codex".to_string(),
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
@@ -236,7 +240,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
supported_text_verbosity: &[TextVerbosityConfig::Medium],
|
||||
is_default: false,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.2-codex".to_string(),
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
@@ -266,7 +270,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
supported_text_verbosity: ALL_TEXT_VERBOSITY,
|
||||
is_default: false,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.2-codex".to_string(),
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
@@ -292,7 +296,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
supported_text_verbosity: ALL_TEXT_VERBOSITY,
|
||||
is_default: false,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.2-codex".to_string(),
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
@@ -325,7 +329,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
supported_text_verbosity: ALL_TEXT_VERBOSITY,
|
||||
is_default: false,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.2-codex".to_string(),
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
@@ -355,7 +359,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
supported_text_verbosity: ALL_TEXT_VERBOSITY,
|
||||
is_default: false,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.2-codex".to_string(),
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
@@ -380,7 +384,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
supported_text_verbosity: ALL_TEXT_VERBOSITY,
|
||||
is_default: false,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.2-codex".to_string(),
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
@@ -417,7 +421,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
|
||||
supported_text_verbosity: ALL_TEXT_VERBOSITY,
|
||||
is_default: false,
|
||||
upgrade: Some(ModelUpgrade {
|
||||
id: "gpt-5.2-codex".to_string(),
|
||||
id: "gpt-5.3-codex".to_string(),
|
||||
reasoning_effort_mapping: None,
|
||||
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
|
||||
}),
|
||||
|
||||
@@ -455,7 +455,34 @@ impl ModelClient {
|
||||
.as_deref()
|
||||
.or(prompt.log_tag.as_deref());
|
||||
match self.provider.wire_api {
|
||||
WireApi::Responses => self.stream_responses(prompt, log_tag).await,
|
||||
WireApi::Responses => {
|
||||
let prefer_websockets = prompt
|
||||
.model_family_override
|
||||
.as_ref()
|
||||
.map(|family| family.prefer_websockets)
|
||||
.or_else(|| {
|
||||
prompt
|
||||
.model_override
|
||||
.as_deref()
|
||||
.and_then(find_family_for_model)
|
||||
.map(|family| family.prefer_websockets)
|
||||
})
|
||||
.unwrap_or(self.config.model_family.prefer_websockets);
|
||||
|
||||
if prefer_websockets {
|
||||
match self.stream_responses_websocket(prompt, log_tag).await {
|
||||
Ok(stream) => Ok(stream),
|
||||
Err(err) => {
|
||||
warn!(
|
||||
"preferred websocket transport failed; falling back to responses HTTP stream: {err}"
|
||||
);
|
||||
self.stream_responses(prompt, log_tag).await
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.stream_responses(prompt, log_tag).await
|
||||
}
|
||||
}
|
||||
WireApi::ResponsesWebsocket => self.stream_responses_websocket(prompt, log_tag).await,
|
||||
WireApi::Chat => {
|
||||
let effective_family = prompt
|
||||
|
||||
@@ -68,6 +68,9 @@ pub struct ModelFamily {
|
||||
/// Responses API.
|
||||
pub supports_parallel_tool_calls: bool,
|
||||
|
||||
/// Prefer websocket transport for this model when supported by the provider.
|
||||
pub prefer_websockets: bool,
|
||||
|
||||
// This should be set to true when the model expects a tool named
|
||||
// "local_shell" to be provided. Its contract must be understood natively by
|
||||
// the model such that its description can be omitted.
|
||||
@@ -122,6 +125,7 @@ macro_rules! model_family {
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_effort: None,
|
||||
supports_parallel_tool_calls: false,
|
||||
prefer_websockets: false,
|
||||
uses_local_shell_tool: false,
|
||||
apply_patch_tool_type: None,
|
||||
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
||||
@@ -349,6 +353,7 @@ pub fn derive_default_model_family(model: &str) -> ModelFamily {
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_effort: None,
|
||||
supports_parallel_tool_calls: false,
|
||||
prefer_websockets: false,
|
||||
uses_local_shell_tool: false,
|
||||
apply_patch_tool_type: None,
|
||||
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
||||
|
||||
@@ -676,6 +676,8 @@ fn rate_limit_snapshot_to_protocol(
|
||||
resets_at: None,
|
||||
};
|
||||
code_protocol::protocol::RateLimitSnapshot {
|
||||
limit_id: None,
|
||||
limit_name: None,
|
||||
primary: Some(primary),
|
||||
secondary: Some(secondary),
|
||||
credits: None,
|
||||
|
||||
@@ -383,6 +383,7 @@ fn apply_model_info_overrides_with_personality(
|
||||
|
||||
family.supports_reasoning_summaries = info.supports_reasoning_summaries;
|
||||
family.supports_parallel_tool_calls = info.supports_parallel_tool_calls;
|
||||
family.prefer_websockets = info.prefer_websockets;
|
||||
if let Some(effort) = info.default_reasoning_level {
|
||||
family.default_reasoning_effort = Some(map_reasoning_effort(effort));
|
||||
}
|
||||
|
||||
@@ -249,6 +249,9 @@ pub struct ModelInfo {
|
||||
/// Input modalities accepted by the backend for this model.
|
||||
#[serde(default = "default_input_modalities")]
|
||||
pub input_modalities: Vec<InputModality>,
|
||||
/// When true, this model should prefer websocket transport when available.
|
||||
#[serde(default)]
|
||||
pub prefer_websockets: bool,
|
||||
}
|
||||
|
||||
impl ModelInfo {
|
||||
@@ -506,6 +509,7 @@ mod tests {
|
||||
effective_context_window_percent: 95,
|
||||
experimental_supported_tools: vec![],
|
||||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,4 +667,3 @@ mod tests {
|
||||
assert_eq!(personality_variables.get_personality_message(None), None);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
Approvals are your mechanism to get user consent to run shell commands without the sandbox. `approval_policy` is `never`: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is paired with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.
|
||||
|
||||
Approval policy is currently never. Do not provide the `sandbox_permissions` for any reason, commands will be rejected.
|
||||
|
||||
@@ -1270,6 +1270,10 @@ pub struct TokenCountEvent {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, TS)]
|
||||
pub struct RateLimitSnapshot {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub limit_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub limit_name: Option<String>,
|
||||
pub primary: Option<RateLimitWindow>,
|
||||
pub secondary: Option<RateLimitWindow>,
|
||||
pub credits: Option<CreditsSnapshot>,
|
||||
|
||||
Reference in New Issue
Block a user