mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-12 14:10:19 +08:00
增加 Codex 重置次数功能
This commit is contained in:
@@ -601,6 +601,47 @@ fn codex_find_spark_rate_limit(
|
||||
.and_then(serde_json::Value::as_object)
|
||||
}
|
||||
|
||||
fn codex_reset_credits_container<'a>(
|
||||
root: &'a serde_json::Map<String, serde_json::Value>,
|
||||
) -> Option<&'a serde_json::Map<String, serde_json::Value>> {
|
||||
[
|
||||
"rate_limit_reset_credits",
|
||||
"rateLimitResetCredits",
|
||||
"reset_credits",
|
||||
"resetCredits",
|
||||
]
|
||||
.iter()
|
||||
.find_map(|key| root.get(*key).and_then(serde_json::Value::as_object))
|
||||
}
|
||||
|
||||
fn codex_reset_credits_available_count(
|
||||
root: &serde_json::Map<String, serde_json::Value>,
|
||||
) -> Option<u64> {
|
||||
let container = codex_reset_credits_container(root)?;
|
||||
[
|
||||
"available_count",
|
||||
"availableCount",
|
||||
"available",
|
||||
"remaining",
|
||||
"count",
|
||||
]
|
||||
.iter()
|
||||
.find_map(|key| container.get(*key).and_then(coerce_json_u64))
|
||||
}
|
||||
|
||||
fn codex_reset_credits_count_snapshot(
|
||||
available_count: u64,
|
||||
updated_at_unix_secs: u64,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"available_count": available_count,
|
||||
"updated_at": updated_at_unix_secs,
|
||||
"detail_source": "wham_usage",
|
||||
"detail_status": "not_requested",
|
||||
"credits": [],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_codex_wham_usage_response(
|
||||
value: &serde_json::Value,
|
||||
updated_at_unix_secs: u64,
|
||||
@@ -668,6 +709,13 @@ pub fn parse_codex_wham_usage_response(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(available_count) = codex_reset_credits_available_count(root) {
|
||||
result.insert(
|
||||
"reset_credits".to_string(),
|
||||
codex_reset_credits_count_snapshot(available_count, updated_at_unix_secs),
|
||||
);
|
||||
}
|
||||
|
||||
if result.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -675,6 +723,189 @@ pub fn parse_codex_wham_usage_response(
|
||||
Some(serde_json::Value::Object(result))
|
||||
}
|
||||
|
||||
fn parse_codex_reset_credit_timestamp(value: Option<&serde_json::Value>) -> Option<u64> {
|
||||
let value = value?;
|
||||
if let Some(timestamp) = coerce_json_u64(value) {
|
||||
return Some(if timestamp > 1_000_000_000_000 {
|
||||
timestamp / 1000
|
||||
} else {
|
||||
timestamp
|
||||
});
|
||||
}
|
||||
let raw = value.as_str()?.trim();
|
||||
if raw.is_empty() {
|
||||
return None;
|
||||
}
|
||||
chrono::DateTime::parse_from_rfc3339(raw)
|
||||
.ok()
|
||||
.and_then(|timestamp| u64::try_from(timestamp.timestamp()).ok())
|
||||
}
|
||||
|
||||
fn codex_reset_credit_detail_items(value: &serde_json::Value) -> Option<&Vec<serde_json::Value>> {
|
||||
first_json_value_by_paths(
|
||||
value,
|
||||
&[
|
||||
&["credits"],
|
||||
&["data"],
|
||||
&["items"],
|
||||
&["rate_limit_reset_credits", "credits"],
|
||||
&["rate_limit_reset_credits", "data"],
|
||||
&["rateLimitResetCredits", "credits"],
|
||||
&["rateLimitResetCredits", "data"],
|
||||
&["reset_credits", "credits"],
|
||||
&["resetCredits", "credits"],
|
||||
],
|
||||
)
|
||||
.and_then(serde_json::Value::as_array)
|
||||
}
|
||||
|
||||
fn codex_reset_credit_id(object: &serde_json::Map<String, serde_json::Value>) -> Option<String> {
|
||||
[
|
||||
"id",
|
||||
"credit_id",
|
||||
"creditId",
|
||||
"key",
|
||||
"idempotency_key",
|
||||
"idempotencyKey",
|
||||
]
|
||||
.iter()
|
||||
.find_map(|key| coerce_json_string(object.get(*key)))
|
||||
}
|
||||
|
||||
fn codex_reset_credit_display_key(id: &str) -> Option<String> {
|
||||
id.split('-')
|
||||
.next()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
fn codex_reset_credit_status(
|
||||
object: &serde_json::Map<String, serde_json::Value>,
|
||||
) -> Option<String> {
|
||||
["status", "state"]
|
||||
.iter()
|
||||
.find_map(|key| coerce_json_string(object.get(*key)))
|
||||
}
|
||||
|
||||
fn parse_codex_reset_credit_detail_item(item: &serde_json::Value) -> Option<serde_json::Value> {
|
||||
let object = item.as_object()?;
|
||||
let id = codex_reset_credit_id(object)?;
|
||||
let display_key = codex_reset_credit_display_key(&id)?;
|
||||
let expires_at = parse_codex_reset_credit_timestamp(
|
||||
object
|
||||
.get("expires_at")
|
||||
.or_else(|| object.get("expiresAt"))
|
||||
.or_else(|| object.get("expiration_time"))
|
||||
.or_else(|| object.get("expirationTime")),
|
||||
)?;
|
||||
let granted_at = parse_codex_reset_credit_timestamp(
|
||||
object
|
||||
.get("granted_at")
|
||||
.or_else(|| object.get("grantedAt"))
|
||||
.or_else(|| object.get("created_at"))
|
||||
.or_else(|| object.get("createdAt")),
|
||||
);
|
||||
|
||||
let mut out = serde_json::Map::new();
|
||||
out.insert("id".to_string(), json!(id));
|
||||
out.insert("display_key".to_string(), json!(display_key));
|
||||
if let Some(status) = codex_reset_credit_status(object) {
|
||||
out.insert("status".to_string(), json!(status));
|
||||
}
|
||||
if let Some(granted_at) = granted_at {
|
||||
out.insert("granted_at".to_string(), json!(granted_at));
|
||||
}
|
||||
out.insert("expires_at".to_string(), json!(expires_at));
|
||||
Some(serde_json::Value::Object(out))
|
||||
}
|
||||
|
||||
pub fn parse_codex_wham_reset_credits_detail_response(
|
||||
value: &serde_json::Value,
|
||||
updated_at_unix_secs: u64,
|
||||
) -> Option<serde_json::Value> {
|
||||
value.as_object()?;
|
||||
let mut credits = codex_reset_credit_detail_items(value)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(parse_codex_reset_credit_detail_item)
|
||||
.collect::<Vec<_>>();
|
||||
credits.sort_by_key(|item| {
|
||||
item.get("expires_at")
|
||||
.and_then(coerce_json_u64)
|
||||
.unwrap_or(u64::MAX)
|
||||
});
|
||||
|
||||
let detail_status = if credits.is_empty() {
|
||||
"empty"
|
||||
} else {
|
||||
"available"
|
||||
};
|
||||
let mut reset_credits = serde_json::Map::new();
|
||||
reset_credits.insert("updated_at".to_string(), json!(updated_at_unix_secs));
|
||||
reset_credits.insert("detail_source".to_string(), json!("wham_readonly"));
|
||||
reset_credits.insert("detail_status".to_string(), json!(detail_status));
|
||||
reset_credits.insert("credits".to_string(), serde_json::Value::Array(credits));
|
||||
|
||||
if let Some(root) = value.as_object() {
|
||||
if let Some(available_count) = codex_reset_credits_available_count(root).or_else(|| {
|
||||
[
|
||||
"available_count",
|
||||
"availableCount",
|
||||
"available",
|
||||
"remaining",
|
||||
"count",
|
||||
]
|
||||
.iter()
|
||||
.find_map(|key| root.get(*key).and_then(coerce_json_u64))
|
||||
}) {
|
||||
reset_credits.insert("available_count".to_string(), json!(available_count));
|
||||
}
|
||||
}
|
||||
|
||||
Some(json!({ "reset_credits": reset_credits }))
|
||||
}
|
||||
|
||||
pub fn normalize_codex_reset_credit_consume_outcome(
|
||||
value: Option<&serde_json::Value>,
|
||||
) -> Option<String> {
|
||||
let object = value.and_then(serde_json::Value::as_object)?;
|
||||
let raw = ["outcome", "status", "result", "code"]
|
||||
.iter()
|
||||
.find_map(|key| coerce_json_string(object.get(*key)));
|
||||
if let Some(raw) = raw {
|
||||
let normalized = raw
|
||||
.trim()
|
||||
.replace('-', "_")
|
||||
.replace(' ', "_")
|
||||
.to_ascii_lowercase();
|
||||
return match normalized.as_str() {
|
||||
"reset" | "success" | "redeemed" => Some("reset".to_string()),
|
||||
"alreadyredeemed" | "already_redeemed" => Some("already_redeemed".to_string()),
|
||||
"nothingtoreset" | "nothing_to_reset" => Some("nothing_to_reset".to_string()),
|
||||
"nocredit" | "no_credit" => Some("no_credit".to_string()),
|
||||
"error" | "failed" => Some("error".to_string()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
for (field, outcome) in [
|
||||
("reset", "reset"),
|
||||
("alreadyRedeemed", "already_redeemed"),
|
||||
("already_redeemed", "already_redeemed"),
|
||||
("nothingToReset", "nothing_to_reset"),
|
||||
("nothing_to_reset", "nothing_to_reset"),
|
||||
("noCredit", "no_credit"),
|
||||
("no_credit", "no_credit"),
|
||||
] {
|
||||
if object.get(field).and_then(coerce_json_bool) == Some(true) {
|
||||
return Some(outcome.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn codex_json_object<'a>(
|
||||
root: &'a serde_json::Map<String, serde_json::Value>,
|
||||
keys: &[&str],
|
||||
@@ -1772,7 +2003,8 @@ pub fn parse_chatgpt_web_conversation_init_response(
|
||||
mod tests {
|
||||
use super::{
|
||||
codex_build_invalid_state, codex_runtime_invalid_reason,
|
||||
parse_chatgpt_web_conversation_init_response, parse_codex_backend_me_response,
|
||||
normalize_codex_reset_credit_consume_outcome, parse_chatgpt_web_conversation_init_response,
|
||||
parse_codex_backend_me_response, parse_codex_wham_reset_credits_detail_response,
|
||||
parse_codex_wham_usage_response, parse_gemini_cli_retrieve_user_quota_response,
|
||||
parse_gemini_cli_v1internal_credits_response, parse_windsurf_model_configs_response,
|
||||
parse_windsurf_rate_limit_response, parse_windsurf_user_status_response,
|
||||
@@ -2187,6 +2419,95 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_codex_reset_credit_count_from_wham_usage() {
|
||||
let parsed = parse_codex_wham_usage_response(
|
||||
&json!({
|
||||
"plan_type": "plus",
|
||||
"rate_limit": {
|
||||
"primary_window": {
|
||||
"used_percent": 25.0,
|
||||
"reset_after_seconds": 604800
|
||||
}
|
||||
},
|
||||
"rate_limit_reset_credits": {
|
||||
"available_count": 2
|
||||
}
|
||||
}),
|
||||
1_777_000_000,
|
||||
)
|
||||
.expect("codex wham usage should parse");
|
||||
|
||||
assert_eq!(
|
||||
parsed.pointer("/reset_credits/available_count"),
|
||||
Some(&json!(2u64))
|
||||
);
|
||||
assert_eq!(
|
||||
parsed.pointer("/reset_credits/detail_status"),
|
||||
Some(&json!("not_requested"))
|
||||
);
|
||||
assert_eq!(
|
||||
parsed.pointer("/reset_credits/updated_at"),
|
||||
Some(&json!(1_777_000_000u64))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_codex_reset_credit_detail_sorted_by_expiry() {
|
||||
let parsed = parse_codex_wham_reset_credits_detail_response(
|
||||
&json!({
|
||||
"credits": [
|
||||
{
|
||||
"idempotencyKey": "bbbbbbbb-1111-2222-3333-444444444444",
|
||||
"status": "available",
|
||||
"expiresAt": "2030-01-04T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"idempotencyKey": "aaaaaaaa-1111-2222-3333-444444444444",
|
||||
"status": "available",
|
||||
"grantedAt": 1_893_456_000_000u64,
|
||||
"expiresAt": "2030-01-02T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}),
|
||||
1_777_000_000,
|
||||
)
|
||||
.expect("detail should parse");
|
||||
|
||||
assert_eq!(
|
||||
parsed.pointer("/reset_credits/detail_status"),
|
||||
Some(&json!("available"))
|
||||
);
|
||||
assert_eq!(
|
||||
parsed.pointer("/reset_credits/credits/0/display_key"),
|
||||
Some(&json!("aaaaaaaa"))
|
||||
);
|
||||
assert_eq!(
|
||||
parsed.pointer("/reset_credits/credits/0/granted_at"),
|
||||
Some(&json!(1_893_456_000u64))
|
||||
);
|
||||
assert_eq!(
|
||||
parsed.pointer("/reset_credits/credits/1/display_key"),
|
||||
Some(&json!("bbbbbbbb"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalizes_codex_reset_credit_consume_outcome() {
|
||||
assert_eq!(
|
||||
normalize_codex_reset_credit_consume_outcome(Some(&json!({
|
||||
"outcome": "alreadyRedeemed"
|
||||
}))),
|
||||
Some("already_redeemed".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_codex_reset_credit_consume_outcome(Some(&json!({
|
||||
"noCredit": true
|
||||
}))),
|
||||
Some("no_credit".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_codex_backend_me_identity_metadata_without_quota_windows() {
|
||||
let parsed = parse_codex_backend_me_response(
|
||||
|
||||
@@ -16,7 +16,8 @@ pub use presets::{
|
||||
pub use provider::{ProviderPoolAdapter, ProviderPoolMemberInput};
|
||||
pub use providers::{
|
||||
build_antigravity_pool_quota_request, build_chatgpt_web_pool_quota_request,
|
||||
build_codex_pool_quota_request, build_gemini_cli_pool_quota_request,
|
||||
build_codex_pool_quota_request, build_codex_pool_reset_credit_consume_request,
|
||||
build_codex_pool_reset_credits_request, build_gemini_cli_pool_quota_request,
|
||||
build_kiro_pool_quota_request, build_windsurf_pool_model_configs_request,
|
||||
build_windsurf_pool_model_configs_request_with_base_url, build_windsurf_pool_quota_request,
|
||||
build_windsurf_pool_quota_request_with_base_url, build_windsurf_pool_rate_limit_request,
|
||||
@@ -27,7 +28,8 @@ pub use providers::{
|
||||
DefaultProviderPoolAdapter, GeminiCliProviderPoolAdapter, GrokProviderPoolAdapter,
|
||||
KiroPoolQuotaAuthInput, KiroProviderPoolAdapter, UnsupportedQuotaProviderPoolAdapter,
|
||||
ANTIGRAVITY_FETCH_AVAILABLE_MODELS_PATH, CHATGPT_WEB_CONVERSATION_INIT_PATH,
|
||||
CHATGPT_WEB_DEFAULT_BASE_URL, CODEX_WHAM_USAGE_URL, GEMINI_CLI_RETRIEVE_USER_QUOTA_PATH,
|
||||
CHATGPT_WEB_DEFAULT_BASE_URL, CODEX_WHAM_RESET_CREDITS_CONSUME_URL,
|
||||
CODEX_WHAM_RESET_CREDITS_URL, CODEX_WHAM_USAGE_URL, GEMINI_CLI_RETRIEVE_USER_QUOTA_PATH,
|
||||
GEMINI_CLI_USER_AGENT, KIRO_USAGE_LIMITS_PATH, KIRO_USAGE_SDK_VERSION,
|
||||
WINDSURF_MODEL_CONFIGS_PATH, WINDSURF_RATE_LIMIT_PATH, WINDSURF_USER_STATUS_PATH,
|
||||
};
|
||||
@@ -215,6 +217,65 @@ mod tests {
|
||||
assert_eq!(spec.model_name.as_deref(), Some("codex-wham-usage"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_reset_credits_request_uses_wham_detail_endpoint() {
|
||||
let spec = build_codex_pool_reset_credits_request(
|
||||
"key-1",
|
||||
Some(("authorization".to_string(), "Bearer access".to_string())),
|
||||
None,
|
||||
Some(&json!({
|
||||
"plan_type": "plus",
|
||||
"account_id": "acct-1"
|
||||
})),
|
||||
)
|
||||
.expect("spec should build");
|
||||
|
||||
assert_eq!(spec.method, "GET");
|
||||
assert_eq!(spec.url, CODEX_WHAM_RESET_CREDITS_URL);
|
||||
assert_eq!(
|
||||
spec.headers.get("authorization").map(String::as_str),
|
||||
Some("Bearer access")
|
||||
);
|
||||
assert_eq!(
|
||||
spec.headers.get("chatgpt-account-id").map(String::as_str),
|
||||
Some("acct-1")
|
||||
);
|
||||
assert_eq!(spec.model_name.as_deref(), Some("codex-wham-reset-credits"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_reset_credit_consume_request_posts_redeem_request_id() {
|
||||
let spec = build_codex_pool_reset_credit_consume_request(
|
||||
"key-1",
|
||||
Some(("authorization".to_string(), "Bearer access".to_string())),
|
||||
None,
|
||||
None,
|
||||
"8ae6f1c7-7e9e-4f5d-9b8a-000000000000",
|
||||
)
|
||||
.expect("spec should build");
|
||||
|
||||
assert_eq!(spec.method, "POST");
|
||||
assert_eq!(spec.url, CODEX_WHAM_RESET_CREDITS_CONSUME_URL);
|
||||
assert_eq!(spec.content_type.as_deref(), Some("application/json"));
|
||||
assert_eq!(
|
||||
spec.json_body
|
||||
.as_ref()
|
||||
.and_then(|body| body.get("redeem_request_id")),
|
||||
Some(&json!("8ae6f1c7-7e9e-4f5d-9b8a-000000000000"))
|
||||
);
|
||||
assert_eq!(
|
||||
spec.json_body
|
||||
.as_ref()
|
||||
.and_then(|body| body.as_object())
|
||||
.map(|body| body.len()),
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(
|
||||
spec.model_name.as_deref(),
|
||||
Some("codex-wham-reset-credit-consume")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_quota_request_skips_account_header_for_free_accounts() {
|
||||
let spec = build_codex_pool_quota_request(
|
||||
|
||||
@@ -17,6 +17,10 @@ use crate::quota::{
|
||||
use crate::quota_refresh::ProviderPoolQuotaRequestSpec;
|
||||
|
||||
pub const CODEX_WHAM_USAGE_URL: &str = "https://chatgpt.com/backend-api/wham/usage";
|
||||
pub const CODEX_WHAM_RESET_CREDITS_URL: &str =
|
||||
"https://chatgpt.com/backend-api/wham/rate-limit-reset-credits";
|
||||
pub const CODEX_WHAM_RESET_CREDITS_CONSUME_URL: &str =
|
||||
"https://chatgpt.com/backend-api/wham/rate-limit-reset-credits/consume";
|
||||
const PLACEHOLDER_API_KEY: &str = "__placeholder__";
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -68,12 +72,11 @@ impl ProviderPoolAdapter for CodexProviderPoolAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_codex_pool_quota_request(
|
||||
key_id: &str,
|
||||
fn build_codex_wham_headers(
|
||||
resolved_oauth_auth: Option<(String, String)>,
|
||||
decrypted_api_key: Option<&str>,
|
||||
auth_config: Option<&Value>,
|
||||
) -> Result<ProviderPoolQuotaRequestSpec, String> {
|
||||
) -> Result<BTreeMap<String, String>, String> {
|
||||
let mut headers = BTreeMap::new();
|
||||
headers.insert("accept".to_string(), "application/json".to_string());
|
||||
let auth_config_headers = auth_config
|
||||
@@ -120,6 +123,17 @@ pub fn build_codex_pool_quota_request(
|
||||
);
|
||||
}
|
||||
|
||||
Ok(headers)
|
||||
}
|
||||
|
||||
pub fn build_codex_pool_quota_request(
|
||||
key_id: &str,
|
||||
resolved_oauth_auth: Option<(String, String)>,
|
||||
decrypted_api_key: Option<&str>,
|
||||
auth_config: Option<&Value>,
|
||||
) -> Result<ProviderPoolQuotaRequestSpec, String> {
|
||||
let headers = build_codex_wham_headers(resolved_oauth_auth, decrypted_api_key, auth_config)?;
|
||||
|
||||
Ok(ProviderPoolQuotaRequestSpec {
|
||||
request_id: format!("codex-quota:{key_id}"),
|
||||
provider_name: "codex".to_string(),
|
||||
@@ -136,6 +150,64 @@ pub fn build_codex_pool_quota_request(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build_codex_pool_reset_credits_request(
|
||||
key_id: &str,
|
||||
resolved_oauth_auth: Option<(String, String)>,
|
||||
decrypted_api_key: Option<&str>,
|
||||
auth_config: Option<&Value>,
|
||||
) -> Result<ProviderPoolQuotaRequestSpec, String> {
|
||||
let headers = build_codex_wham_headers(resolved_oauth_auth, decrypted_api_key, auth_config)?;
|
||||
|
||||
Ok(ProviderPoolQuotaRequestSpec {
|
||||
request_id: format!("codex-reset-credits:{key_id}"),
|
||||
provider_name: "codex".to_string(),
|
||||
quota_kind: "codex_reset_credits".to_string(),
|
||||
method: "GET".to_string(),
|
||||
url: CODEX_WHAM_RESET_CREDITS_URL.to_string(),
|
||||
headers,
|
||||
content_type: None,
|
||||
json_body: None,
|
||||
client_api_format: "openai:responses".to_string(),
|
||||
provider_api_format: "openai:responses".to_string(),
|
||||
model_name: Some("codex-wham-reset-credits".to_string()),
|
||||
accept_invalid_certs: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build_codex_pool_reset_credit_consume_request(
|
||||
key_id: &str,
|
||||
resolved_oauth_auth: Option<(String, String)>,
|
||||
decrypted_api_key: Option<&str>,
|
||||
auth_config: Option<&Value>,
|
||||
redeem_request_id: &str,
|
||||
) -> Result<ProviderPoolQuotaRequestSpec, String> {
|
||||
let redeem_request_id = redeem_request_id.trim();
|
||||
if redeem_request_id.is_empty() {
|
||||
return Err("缺少 Codex reset credit 幂等请求 ID".to_string());
|
||||
}
|
||||
|
||||
let mut headers =
|
||||
build_codex_wham_headers(resolved_oauth_auth, decrypted_api_key, auth_config)?;
|
||||
headers.insert("content-type".to_string(), "application/json".to_string());
|
||||
|
||||
Ok(ProviderPoolQuotaRequestSpec {
|
||||
request_id: format!("codex-reset-credit-consume:{key_id}:{redeem_request_id}"),
|
||||
provider_name: "codex".to_string(),
|
||||
quota_kind: "codex_reset_credit_consume".to_string(),
|
||||
method: "POST".to_string(),
|
||||
url: CODEX_WHAM_RESET_CREDITS_CONSUME_URL.to_string(),
|
||||
headers,
|
||||
content_type: Some("application/json".to_string()),
|
||||
json_body: Some(serde_json::json!({
|
||||
"redeem_request_id": redeem_request_id,
|
||||
})),
|
||||
client_api_format: "openai:responses".to_string(),
|
||||
provider_api_format: "openai:responses".to_string(),
|
||||
model_name: Some("codex-wham-reset-credit-consume".to_string()),
|
||||
accept_invalid_certs: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn codex_window_reset_elapsed(bucket: &Map<String, Value>, prefix: &str) -> bool {
|
||||
let Some(now_unix_secs) = provider_pool_current_unix_secs() else {
|
||||
return false;
|
||||
|
||||
@@ -19,7 +19,11 @@ pub use chatgpt_web::{
|
||||
CHATGPT_WEB_DEFAULT_BASE_URL,
|
||||
};
|
||||
pub use codex::CodexProviderPoolAdapter;
|
||||
pub use codex::{build_codex_pool_quota_request, CODEX_WHAM_USAGE_URL};
|
||||
pub use codex::{
|
||||
build_codex_pool_quota_request, build_codex_pool_reset_credit_consume_request,
|
||||
build_codex_pool_reset_credits_request, CODEX_WHAM_RESET_CREDITS_CONSUME_URL,
|
||||
CODEX_WHAM_RESET_CREDITS_URL, CODEX_WHAM_USAGE_URL,
|
||||
};
|
||||
pub use default::DefaultProviderPoolAdapter;
|
||||
pub use gemini_cli::GeminiCliProviderPoolAdapter;
|
||||
pub use gemini_cli::{
|
||||
|
||||
Reference in New Issue
Block a user