mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 08:27:46 +08:00
style: format quota and response changes
This commit is contained in:
@@ -4,8 +4,7 @@ use serde_json::{json, Map, Value};
|
|||||||
|
|
||||||
use crate::formats::openai::namespace::NamespaceToolAliases;
|
use crate::formats::openai::namespace::NamespaceToolAliases;
|
||||||
use crate::formats::openai::responses::{
|
use crate::formats::openai::responses::{
|
||||||
encode_gemini_tool_signature_carrier_with_direction,
|
encode_gemini_tool_signature_carrier_with_direction, openai_responses_message_item_id,
|
||||||
openai_responses_message_item_id,
|
|
||||||
openai_responses_synthetic_reasoning_item_id,
|
openai_responses_synthetic_reasoning_item_id,
|
||||||
response::{
|
response::{
|
||||||
ensure_modern_openai_responses_response_fields, openai_responses_current_timestamp,
|
ensure_modern_openai_responses_response_fields, openai_responses_current_timestamp,
|
||||||
|
|||||||
@@ -318,8 +318,7 @@ mod tests {
|
|||||||
use super::{
|
use super::{
|
||||||
decode_gemini_tool_signature_carrier, encode_gemini_tool_signature_carrier_with_direction,
|
decode_gemini_tool_signature_carrier, encode_gemini_tool_signature_carrier_with_direction,
|
||||||
normalize_openai_responses_message_item_ids, openai_responses_message_item_id,
|
normalize_openai_responses_message_item_ids, openai_responses_message_item_id,
|
||||||
openai_responses_request_operation,
|
openai_responses_request_operation, openai_responses_synthetic_reasoning_item_id,
|
||||||
openai_responses_synthetic_reasoning_item_id,
|
|
||||||
strip_incompatible_openai_responses_reasoning_items,
|
strip_incompatible_openai_responses_reasoning_items,
|
||||||
strip_incompatible_openai_responses_reasoning_items_with_policy,
|
strip_incompatible_openai_responses_reasoning_items_with_policy,
|
||||||
GeminiToolSignatureCarrierDirection, OpenAiResponsesReasoningReplayPolicy,
|
GeminiToolSignatureCarrierDirection, OpenAiResponsesReasoningReplayPolicy,
|
||||||
@@ -414,14 +413,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn synthetic_message_item_ids_are_stable_and_start_with_msg() {
|
fn synthetic_message_item_ids_are_stable_and_start_with_msg() {
|
||||||
let first = openai_responses_message_item_id(
|
let first = openai_responses_message_item_id("1c938e58-32a8-4d28-9c34-538d78076895", 0);
|
||||||
"1c938e58-32a8-4d28-9c34-538d78076895",
|
let second = openai_responses_message_item_id("1c938e58-32a8-4d28-9c34-538d78076895", 0);
|
||||||
0,
|
|
||||||
);
|
|
||||||
let second = openai_responses_message_item_id(
|
|
||||||
"1c938e58-32a8-4d28-9c34-538d78076895",
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
let other = openai_responses_message_item_id("chatcmpl-123", 1);
|
let other = openai_responses_message_item_id("chatcmpl-123", 1);
|
||||||
|
|
||||||
assert!(first.starts_with("msg_"));
|
assert!(first.starts_with("msg_"));
|
||||||
@@ -442,7 +435,9 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(normalize_openai_responses_message_item_ids(&mut body), 1);
|
assert_eq!(normalize_openai_responses_message_item_ids(&mut body), 1);
|
||||||
let input = body["input"].as_array().expect("input array");
|
let input = body["input"].as_array().expect("input array");
|
||||||
assert!(input[0]["id"].as_str().is_some_and(|id| id.starts_with("msg_")));
|
assert!(input[0]["id"]
|
||||||
|
.as_str()
|
||||||
|
.is_some_and(|id| id.starts_with("msg_")));
|
||||||
assert_eq!(input[1]["id"], "msg_provider_123");
|
assert_eq!(input[1]["id"], "msg_provider_123");
|
||||||
assert_eq!(input[2].get("id"), Some(&json!("legacy_call")));
|
assert_eq!(input[2].get("id"), Some(&json!("legacy_call")));
|
||||||
assert!(input[3].get("id").is_none());
|
assert!(input[3].get("id").is_none());
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ use aether_ai_formats::formats::conversion::response::{
|
|||||||
use aether_ai_formats::formats::openai::responses::{
|
use aether_ai_formats::formats::openai::responses::{
|
||||||
openai_responses_message_item_id, openai_responses_synthetic_reasoning_item_id,
|
openai_responses_message_item_id, openai_responses_synthetic_reasoning_item_id,
|
||||||
};
|
};
|
||||||
use aether_ai_formats::formats::openai::responses::response::ensure_modern_openai_responses_response_fields;
|
|
||||||
use aether_ai_formats::formats::registry::{convert_response, FormatContext, FormatError};
|
use aether_ai_formats::formats::registry::{convert_response, FormatContext, FormatError};
|
||||||
use aether_ai_formats::{
|
use aether_ai_formats::{
|
||||||
canonical_response_unknown_block_count, canonical_to_claude_response,
|
canonical_response_unknown_block_count, canonical_to_claude_response,
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ pub use formats::openai::responses::request::{
|
|||||||
};
|
};
|
||||||
pub use formats::openai::responses::{
|
pub use formats::openai::responses::{
|
||||||
normalize_openai_responses_message_item_ids, openai_responses_message_item_id,
|
normalize_openai_responses_message_item_ids, openai_responses_message_item_id,
|
||||||
openai_responses_request_operation,
|
openai_responses_request_operation, openai_responses_synthetic_reasoning_item_id,
|
||||||
openai_responses_synthetic_reasoning_item_id,
|
|
||||||
strip_incompatible_openai_responses_reasoning_items,
|
strip_incompatible_openai_responses_reasoning_items,
|
||||||
strip_incompatible_openai_responses_reasoning_items_with_policy,
|
strip_incompatible_openai_responses_reasoning_items_with_policy,
|
||||||
OpenAiResponsesReasoningReplayPolicy, OPENAI_RESPONSES_OPERATION_COMPACT,
|
OpenAiResponsesReasoningReplayPolicy, OPENAI_RESPONSES_OPERATION_COMPACT,
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
|
use crate::formats::openai::responses::openai_responses_message_item_id;
|
||||||
use crate::formats::openai::responses::{
|
use crate::formats::openai::responses::{
|
||||||
decode_gemini_tool_signature_carrier, GeminiToolSignatureCarrierDirection,
|
decode_gemini_tool_signature_carrier, GeminiToolSignatureCarrierDirection,
|
||||||
};
|
};
|
||||||
use crate::formats::openai::shared::map_thinking_budget_to_openai_reasoning_effort;
|
use crate::formats::openai::shared::map_thinking_budget_to_openai_reasoning_effort;
|
||||||
use crate::formats::openai::responses::openai_responses_message_item_id;
|
|
||||||
use crate::formats::shared::model_directives::ReasoningEffort;
|
use crate::formats::shared::model_directives::ReasoningEffort;
|
||||||
use crate::formats::shared::response::remove_empty_pages_from_tool_input_value;
|
use crate::formats::shared::response::remove_empty_pages_from_tool_input_value;
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ pub use providers::{
|
|||||||
WINDSURF_MODEL_CONFIGS_PATH, WINDSURF_RATE_LIMIT_PATH, WINDSURF_USER_STATUS_PATH,
|
WINDSURF_MODEL_CONFIGS_PATH, WINDSURF_RATE_LIMIT_PATH, WINDSURF_USER_STATUS_PATH,
|
||||||
};
|
};
|
||||||
pub use quota::{
|
pub use quota::{
|
||||||
provider_pool_key_account_quota_exhausted, provider_pool_key_quota_hard_blocked,
|
provider_pool_key_account_quota_exhausted, provider_pool_key_model_quota_exhausted,
|
||||||
provider_pool_key_model_quota_exhausted, provider_pool_key_model_quota_hard_blocked,
|
provider_pool_key_model_quota_hard_blocked, provider_pool_key_quota_hard_blocked,
|
||||||
provider_pool_key_scheduling_label, provider_pool_member_quota_snapshot,
|
provider_pool_key_scheduling_label, provider_pool_member_quota_snapshot,
|
||||||
provider_pool_quota_metadata_provider_type, provider_pool_quota_metadata_updated_at,
|
provider_pool_quota_metadata_provider_type, provider_pool_quota_metadata_updated_at,
|
||||||
provider_pool_quota_snapshot_updated_at,
|
provider_pool_quota_snapshot_updated_at,
|
||||||
@@ -1005,14 +1005,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let alpha = service.member_signals(
|
let alpha = service.member_signals("codex", &key, None, Some("vendor-alpha-model"));
|
||||||
"codex",
|
|
||||||
&key,
|
|
||||||
None,
|
|
||||||
Some("vendor-alpha-model"),
|
|
||||||
);
|
|
||||||
let beta = service.member_signals("codex", &key, None, Some("vendor-beta-model"));
|
let beta = service.member_signals("codex", &key, None, Some("vendor-beta-model"));
|
||||||
assert!(!alpha.quota_exhausted, "one available alpha window must keep it usable");
|
assert!(
|
||||||
|
!alpha.quota_exhausted,
|
||||||
|
"one available alpha window must keep it usable"
|
||||||
|
);
|
||||||
assert!(beta.quota_exhausted);
|
assert!(beta.quota_exhausted);
|
||||||
assert!(!beta.quota_hard_blocked);
|
assert!(!beta.quota_hard_blocked);
|
||||||
}
|
}
|
||||||
@@ -1062,12 +1060,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let signals = service.member_signals(
|
let signals = service.member_signals("codex", &key, None, Some("gpt-5.3-codex-spark"));
|
||||||
"codex",
|
|
||||||
&key,
|
|
||||||
None,
|
|
||||||
Some("gpt-5.3-codex-spark"),
|
|
||||||
);
|
|
||||||
assert!(
|
assert!(
|
||||||
!signals.quota_exhausted,
|
!signals.quota_exhausted,
|
||||||
"a compact bucket name should match a token in the selected model"
|
"a compact bucket name should match a token in the selected model"
|
||||||
@@ -1093,12 +1086,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let signals = service.member_signals(
|
let signals = service.member_signals("codex", &key, None, Some("gpt-5.4-codex-spark"));
|
||||||
"codex",
|
|
||||||
&key,
|
|
||||||
None,
|
|
||||||
Some("gpt-5.4-codex-spark"),
|
|
||||||
);
|
|
||||||
assert!(signals.quota_exhausted);
|
assert!(signals.quota_exhausted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use serde_json::{Map, Value};
|
|||||||
use crate::capability::{ProviderPoolCapabilities, ProviderPoolCapability};
|
use crate::capability::{ProviderPoolCapabilities, ProviderPoolCapability};
|
||||||
use crate::plan::{derive_plan_tier, normalize_provider_plan_tier};
|
use crate::plan::{derive_plan_tier, normalize_provider_plan_tier};
|
||||||
use crate::quota::{
|
use crate::quota::{
|
||||||
provider_pool_account_blocked, provider_pool_quota_reset_seconds,
|
provider_pool_account_blocked, provider_pool_model_quota_exhausted,
|
||||||
provider_pool_model_quota_exhausted, provider_pool_quota_snapshot_exhausted_decision,
|
provider_pool_quota_reset_seconds, provider_pool_quota_snapshot_exhausted_decision,
|
||||||
provider_pool_quota_usage_ratio,
|
provider_pool_quota_usage_ratio,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ use crate::provider::{
|
|||||||
};
|
};
|
||||||
use crate::quota::{
|
use crate::quota::{
|
||||||
provider_pool_current_unix_secs, provider_pool_json_bool, provider_pool_json_f64,
|
provider_pool_current_unix_secs, provider_pool_json_bool, provider_pool_json_f64,
|
||||||
provider_pool_metadata_bucket, provider_pool_quota_snapshot_exhausted_decision,
|
provider_pool_metadata_bucket, provider_pool_model_quota_exhausted,
|
||||||
provider_pool_model_quota_exhausted, provider_pool_reset_deadline_elapsed,
|
provider_pool_quota_snapshot_exhausted_decision, provider_pool_reset_deadline_elapsed,
|
||||||
provider_pool_timestamp_unix_secs,
|
provider_pool_timestamp_unix_secs,
|
||||||
};
|
};
|
||||||
use crate::quota_refresh::ProviderPoolQuotaRequestSpec;
|
use crate::quota_refresh::ProviderPoolQuotaRequestSpec;
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use crate::provider::{
|
|||||||
};
|
};
|
||||||
use crate::quota::{
|
use crate::quota::{
|
||||||
provider_pool_current_unix_secs, provider_pool_json_bool, provider_pool_json_f64,
|
provider_pool_current_unix_secs, provider_pool_json_bool, provider_pool_json_f64,
|
||||||
provider_pool_metadata_bucket, provider_pool_quota_snapshot_exhausted_decision,
|
provider_pool_metadata_bucket, provider_pool_model_quota_exhausted,
|
||||||
provider_pool_model_quota_exhausted, provider_pool_reset_deadline_elapsed,
|
provider_pool_quota_snapshot_exhausted_decision, provider_pool_reset_deadline_elapsed,
|
||||||
provider_pool_timestamp_unix_secs,
|
provider_pool_timestamp_unix_secs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -106,10 +106,8 @@ pub(crate) fn provider_pool_model_quota_exhausted(
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if !model_matches.is_empty() {
|
if !model_matches.is_empty() {
|
||||||
let exhausted = provider_pool_explicit_model_windows_exhausted(
|
let exhausted =
|
||||||
model_matches,
|
provider_pool_explicit_model_windows_exhausted(model_matches, observed_at);
|
||||||
observed_at,
|
|
||||||
);
|
|
||||||
if resolved.is_none()
|
if resolved.is_none()
|
||||||
|| provider_pool_should_replace_model_quota_resolution(
|
|| provider_pool_should_replace_model_quota_resolution(
|
||||||
resolved.as_ref().and_then(|(observed_at, _)| *observed_at),
|
resolved.as_ref().and_then(|(observed_at, _)| *observed_at),
|
||||||
@@ -130,10 +128,7 @@ pub(crate) fn provider_pool_model_quota_exhausted(
|
|||||||
.filter(|window| provider_pool_window_family_matches_model(window, &requested))
|
.filter(|window| provider_pool_window_family_matches_model(window, &requested))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if !family_matches.is_empty() {
|
if !family_matches.is_empty() {
|
||||||
let exhausted = provider_pool_any_window_exhausted(
|
let exhausted = provider_pool_any_window_exhausted(family_matches, observed_at);
|
||||||
family_matches,
|
|
||||||
observed_at,
|
|
||||||
);
|
|
||||||
if resolved.is_none()
|
if resolved.is_none()
|
||||||
|| provider_pool_should_replace_model_quota_resolution(
|
|| provider_pool_should_replace_model_quota_resolution(
|
||||||
resolved.as_ref().and_then(|(observed_at, _)| *observed_at),
|
resolved.as_ref().and_then(|(observed_at, _)| *observed_at),
|
||||||
@@ -234,7 +229,10 @@ fn provider_pool_window_is_generic(window: &Map<String, Value>) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = window.get("code").and_then(Value::as_str).unwrap_or_default();
|
let code = window
|
||||||
|
.get("code")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.unwrap_or_default();
|
||||||
let family = code
|
let family = code
|
||||||
.split_once(['_', ':', '/'])
|
.split_once(['_', ':', '/'])
|
||||||
.map(|(prefix, _)| prefix)
|
.map(|(prefix, _)| prefix)
|
||||||
@@ -248,8 +246,17 @@ fn provider_pool_window_is_generic(window: &Map<String, Value>) -> bool {
|
|||||||
.is_some_and(|scope| scope.trim().eq_ignore_ascii_case("account"));
|
.is_some_and(|scope| scope.trim().eq_ignore_ascii_case("account"));
|
||||||
}
|
}
|
||||||
[
|
[
|
||||||
"weekly", "5h", "daily", "monthly", "primary", "secondary", "account", "quota",
|
"weekly",
|
||||||
"window", "rate", "reset",
|
"5h",
|
||||||
|
"daily",
|
||||||
|
"monthly",
|
||||||
|
"primary",
|
||||||
|
"secondary",
|
||||||
|
"account",
|
||||||
|
"quota",
|
||||||
|
"window",
|
||||||
|
"rate",
|
||||||
|
"reset",
|
||||||
]
|
]
|
||||||
.contains(&family.as_str())
|
.contains(&family.as_str())
|
||||||
}
|
}
|
||||||
@@ -301,8 +308,7 @@ fn provider_pool_window_family_matches_model(
|
|||||||
.map(|scope| scope.trim().to_ascii_lowercase());
|
.map(|scope| scope.trim().to_ascii_lowercase());
|
||||||
// A model-scoped window without an explicit model must not accidentally
|
// A model-scoped window without an explicit model must not accidentally
|
||||||
// match a token from its opaque code.
|
// match a token from its opaque code.
|
||||||
if explicit_scope.as_deref() == Some("model")
|
if explicit_scope.as_deref() == Some("model") || provider_pool_window_has_explicit_model(window)
|
||||||
|| provider_pool_window_has_explicit_model(window)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -373,9 +379,26 @@ fn provider_pool_is_specific_model_token(token: &str) -> bool {
|
|||||||
token.len() >= 4
|
token.len() >= 4
|
||||||
&& !token.chars().all(|character| character.is_ascii_digit())
|
&& !token.chars().all(|character| character.is_ascii_digit())
|
||||||
&& ![
|
&& ![
|
||||||
"auto", "base", "claude", "codex", "default", "fast", "flash", "free",
|
"auto",
|
||||||
"gemini", "gpt", "latest", "mini", "model", "plus", "pro", "reasoning",
|
"base",
|
||||||
"team", "think", "thinking", "vendor",
|
"claude",
|
||||||
|
"codex",
|
||||||
|
"default",
|
||||||
|
"fast",
|
||||||
|
"flash",
|
||||||
|
"free",
|
||||||
|
"gemini",
|
||||||
|
"gpt",
|
||||||
|
"latest",
|
||||||
|
"mini",
|
||||||
|
"model",
|
||||||
|
"plus",
|
||||||
|
"pro",
|
||||||
|
"reasoning",
|
||||||
|
"team",
|
||||||
|
"think",
|
||||||
|
"thinking",
|
||||||
|
"vendor",
|
||||||
]
|
]
|
||||||
.contains(&token)
|
.contains(&token)
|
||||||
}
|
}
|
||||||
@@ -394,9 +417,9 @@ fn provider_pool_window_has_explicit_model(window: &Map<String, Value>) -> bool
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|key| match window.get(*key) {
|
.any(|key| match window.get(*key) {
|
||||||
Some(Value::String(value)) => !value.trim().is_empty(),
|
Some(Value::String(value)) => !value.trim().is_empty(),
|
||||||
Some(Value::Array(values)) => values.iter().any(|value| {
|
Some(Value::Array(values)) => values
|
||||||
value.as_str().is_some_and(|value| !value.trim().is_empty())
|
.iter()
|
||||||
}),
|
.any(|value| value.as_str().is_some_and(|value| !value.trim().is_empty())),
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -610,8 +633,12 @@ fn provider_pool_quota_window_is_exhausted(window: &Map<String, Value>) -> bool
|
|||||||
provider_pool_json_bool(window.get("is_exhausted"))
|
provider_pool_json_bool(window.get("is_exhausted"))
|
||||||
.or_else(|| provider_pool_json_bool(window.get("exhausted")))
|
.or_else(|| provider_pool_json_bool(window.get("exhausted")))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
provider_pool_json_f64(window.get("used_ratio").or_else(|| window.get("usage_ratio")))
|
provider_pool_json_f64(
|
||||||
.map(|value| value >= 1.0 - 1e-6)
|
window
|
||||||
|
.get("used_ratio")
|
||||||
|
.or_else(|| window.get("usage_ratio")),
|
||||||
|
)
|
||||||
|
.map(|value| value >= 1.0 - 1e-6)
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
provider_pool_json_f64(window.get("used_percent")).map(|value| value >= 100.0 - 1e-6)
|
provider_pool_json_f64(window.get("used_percent")).map(|value| value >= 100.0 - 1e-6)
|
||||||
@@ -625,8 +652,7 @@ fn provider_pool_quota_window_is_exhausted(window: &Map<String, Value>) -> bool
|
|||||||
.map(|value| value <= 1e-6)
|
.map(|value| value <= 1e-6)
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
provider_pool_json_f64(window.get("remaining_percent"))
|
provider_pool_json_f64(window.get("remaining_percent")).map(|value| value <= 1e-6)
|
||||||
.map(|value| value <= 1e-6)
|
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let remaining = provider_pool_json_f64(
|
let remaining = provider_pool_json_f64(
|
||||||
|
|||||||
Reference in New Issue
Block a user