mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 10:27:46 +08:00
feat(claude-code): 支持通过 /api/oauth/usage 查询账号 5H/周额度并在号池展示
This commit is contained in:
@@ -16,26 +16,26 @@ pub use presets::{
|
||||
pub use provider::{ProviderPoolAdapter, ProviderPoolMemberInput};
|
||||
pub use providers::{
|
||||
build_antigravity_pool_quota_request, build_antigravity_pool_quota_summary_request,
|
||||
build_chatgpt_web_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_chatgpt_web_pool_quota_request, build_claude_code_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,
|
||||
build_windsurf_pool_rate_limit_request_with_base_url, build_xai_pool_billing_request,
|
||||
build_xai_pool_user_request, enrich_chatgpt_web_quota_metadata, grok_mode_id_for_model,
|
||||
grok_pool_tier_from_quota_bucket, grok_quota_window_key_for_model,
|
||||
grok_supported_quota_windows_for_tier, normalize_chatgpt_web_image_quota_limit,
|
||||
AntigravityProviderPoolAdapter, ChatGptWebProviderPoolAdapter, CodexProviderPoolAdapter,
|
||||
DefaultProviderPoolAdapter, GeminiCliProviderPoolAdapter, GrokProviderPoolAdapter,
|
||||
KiroPoolQuotaAuthInput, KiroProviderPoolAdapter, UnsupportedQuotaProviderPoolAdapter,
|
||||
XaiProviderPoolAdapter, ANTIGRAVITY_FETCH_AVAILABLE_MODELS_PATH,
|
||||
ANTIGRAVITY_RETRIEVE_USER_QUOTA_SUMMARY_PATH, CHATGPT_WEB_CONVERSATION_INIT_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,
|
||||
XAI_BILLING_PATH, XAI_USER_PATH,
|
||||
AntigravityProviderPoolAdapter, ChatGptWebProviderPoolAdapter, ClaudeCodeProviderPoolAdapter,
|
||||
CodexProviderPoolAdapter, DefaultProviderPoolAdapter, GeminiCliProviderPoolAdapter,
|
||||
GrokProviderPoolAdapter, KiroPoolQuotaAuthInput, KiroProviderPoolAdapter,
|
||||
UnsupportedQuotaProviderPoolAdapter, XaiProviderPoolAdapter,
|
||||
ANTIGRAVITY_FETCH_AVAILABLE_MODELS_PATH, ANTIGRAVITY_RETRIEVE_USER_QUOTA_SUMMARY_PATH,
|
||||
CHATGPT_WEB_CONVERSATION_INIT_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, XAI_BILLING_PATH, XAI_USER_PATH,
|
||||
};
|
||||
pub use quota::{
|
||||
provider_pool_codex_metadata_has_account_quota, provider_pool_key_account_quota_exhausted,
|
||||
@@ -104,6 +104,7 @@ mod tests {
|
||||
[
|
||||
"antigravity",
|
||||
"chatgpt_web",
|
||||
"claude_code",
|
||||
"codex",
|
||||
"gemini_cli",
|
||||
"grok",
|
||||
@@ -112,15 +113,25 @@ mod tests {
|
||||
"xai"
|
||||
]
|
||||
);
|
||||
assert!(service.supports_quota_refresh("claude_code"));
|
||||
assert!(service.supports_quota_refresh("codex"));
|
||||
assert!(service.supports_quota_refresh("antigravity"));
|
||||
assert!(service.supports_quota_refresh("grok"));
|
||||
assert!(service.supports_quota_refresh("gemini_cli"));
|
||||
assert!(service.supports_quota_refresh("windsurf"));
|
||||
assert!(service.supports_quota_refresh("xai"));
|
||||
let claude_spec = build_claude_code_pool_quota_request(
|
||||
"key-1",
|
||||
("authorization".to_string(), "Bearer access".to_string()),
|
||||
);
|
||||
assert_eq!(claude_spec.method, "GET");
|
||||
assert_eq!(claude_spec.url, "https://api.anthropic.com/api/oauth/usage");
|
||||
assert_eq!(
|
||||
service.quota_refresh_unsupported_message("claude_code"),
|
||||
"Claude Code 暂不支持自动刷新额度:上游没有稳定可用的账号额度查询接口"
|
||||
claude_spec
|
||||
.headers
|
||||
.get("anthropic-beta")
|
||||
.map(String::as_str),
|
||||
Some("oauth-2025-04-20")
|
||||
);
|
||||
assert_eq!(
|
||||
service.quota_refresh_unsupported_message("vertex_ai"),
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use aether_data_contracts::repository::provider_catalog::StoredProviderCatalogEndpoint;
|
||||
|
||||
use crate::capability::ProviderPoolCapabilities;
|
||||
use crate::provider::{
|
||||
provider_pool_endpoint_format_matches, provider_pool_matching_endpoint, ProviderPoolAdapter,
|
||||
};
|
||||
use crate::quota_refresh::ProviderPoolQuotaRequestSpec;
|
||||
|
||||
pub const CLAUDE_CODE_OAUTH_USAGE_URL: &str = "https://api.anthropic.com/api/oauth/usage";
|
||||
pub const CLAUDE_CODE_OAUTH_BETA: &str = "oauth-2025-04-20";
|
||||
pub const CLAUDE_CODE_USAGE_USER_AGENT: &str = "claude-code/2.1.284";
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ClaudeCodeProviderPoolAdapter;
|
||||
|
||||
impl ProviderPoolAdapter for ClaudeCodeProviderPoolAdapter {
|
||||
fn provider_type(&self) -> &'static str {
|
||||
"claude_code"
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> ProviderPoolCapabilities {
|
||||
ProviderPoolCapabilities {
|
||||
quota_refresh: true,
|
||||
..ProviderPoolCapabilities::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn quota_refresh_endpoint(
|
||||
&self,
|
||||
endpoints: &[StoredProviderCatalogEndpoint],
|
||||
include_inactive: bool,
|
||||
) -> Option<StoredProviderCatalogEndpoint> {
|
||||
provider_pool_matching_endpoint(endpoints, include_inactive, |endpoint| {
|
||||
provider_pool_endpoint_format_matches(endpoint, "claude:messages")
|
||||
})
|
||||
}
|
||||
|
||||
fn quota_refresh_missing_endpoint_message(&self) -> String {
|
||||
"找不到有效的 claude:messages 端点".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the `GET /api/oauth/usage` request that reports the account's 5h / 7d
|
||||
/// utilization windows. The URL is fixed (the origin allowlist only accepts
|
||||
/// `api.anthropic.com`), independent of the inference endpoint's base URL.
|
||||
pub fn build_claude_code_pool_quota_request(
|
||||
key_id: &str,
|
||||
authorization: (String, String),
|
||||
) -> ProviderPoolQuotaRequestSpec {
|
||||
let headers = BTreeMap::from([
|
||||
("authorization".to_string(), authorization.1),
|
||||
("accept".to_string(), "application/json".to_string()),
|
||||
("content-type".to_string(), "application/json".to_string()),
|
||||
(
|
||||
"anthropic-beta".to_string(),
|
||||
CLAUDE_CODE_OAUTH_BETA.to_string(),
|
||||
),
|
||||
(
|
||||
"user-agent".to_string(),
|
||||
CLAUDE_CODE_USAGE_USER_AGENT.to_string(),
|
||||
),
|
||||
]);
|
||||
|
||||
ProviderPoolQuotaRequestSpec {
|
||||
request_id: format!("claude-code-quota:{key_id}"),
|
||||
provider_name: "claude_code".to_string(),
|
||||
quota_kind: "claude_code".to_string(),
|
||||
method: "GET".to_string(),
|
||||
url: CLAUDE_CODE_OAUTH_USAGE_URL.to_string(),
|
||||
headers,
|
||||
content_type: None,
|
||||
json_body: None,
|
||||
client_api_format: "claude:messages".to_string(),
|
||||
provider_api_format: "claude_code:oauth_usage".to_string(),
|
||||
model_name: Some("oauth_usage".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod antigravity;
|
||||
pub mod chatgpt_web;
|
||||
pub mod claude_code;
|
||||
pub mod codex;
|
||||
pub mod default;
|
||||
pub mod gemini_cli;
|
||||
@@ -20,6 +21,11 @@ pub use chatgpt_web::{
|
||||
normalize_chatgpt_web_image_quota_limit, CHATGPT_WEB_CONVERSATION_INIT_PATH,
|
||||
CHATGPT_WEB_DEFAULT_BASE_URL,
|
||||
};
|
||||
pub use claude_code::ClaudeCodeProviderPoolAdapter;
|
||||
pub use claude_code::{
|
||||
build_claude_code_pool_quota_request, CLAUDE_CODE_OAUTH_BETA, CLAUDE_CODE_OAUTH_USAGE_URL,
|
||||
CLAUDE_CODE_USAGE_USER_AGENT,
|
||||
};
|
||||
pub use codex::CodexProviderPoolAdapter;
|
||||
pub use codex::{
|
||||
build_codex_pool_quota_request, build_codex_pool_reset_credit_consume_request,
|
||||
@@ -40,10 +46,7 @@ pub use kiro::{
|
||||
build_kiro_pool_quota_request, KiroPoolQuotaAuthInput, KIRO_USAGE_LIMITS_PATH,
|
||||
KIRO_USAGE_SDK_VERSION,
|
||||
};
|
||||
pub use unsupported::{
|
||||
UnsupportedQuotaProviderPoolAdapter, CLAUDE_CODE_PROVIDER_POOL_ADAPTER,
|
||||
VERTEX_AI_PROVIDER_POOL_ADAPTER,
|
||||
};
|
||||
pub use unsupported::{UnsupportedQuotaProviderPoolAdapter, VERTEX_AI_PROVIDER_POOL_ADAPTER};
|
||||
pub use windsurf::{
|
||||
build_windsurf_pool_model_configs_request,
|
||||
build_windsurf_pool_model_configs_request_with_base_url, build_windsurf_pool_quota_request,
|
||||
|
||||
@@ -28,12 +28,6 @@ impl ProviderPoolAdapter for UnsupportedQuotaProviderPoolAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
pub const CLAUDE_CODE_PROVIDER_POOL_ADAPTER: UnsupportedQuotaProviderPoolAdapter =
|
||||
UnsupportedQuotaProviderPoolAdapter::new(
|
||||
"claude_code",
|
||||
"Claude Code 暂不支持自动刷新额度:上游没有稳定可用的账号额度查询接口",
|
||||
);
|
||||
|
||||
pub const VERTEX_AI_PROVIDER_POOL_ADAPTER: UnsupportedQuotaProviderPoolAdapter =
|
||||
UnsupportedQuotaProviderPoolAdapter::new(
|
||||
"vertex_ai",
|
||||
|
||||
@@ -11,10 +11,10 @@ use crate::capability::ProviderPoolCapability;
|
||||
use crate::presets::normalize_provider_scheduling_presets;
|
||||
use crate::provider::{ProviderPoolAdapter, ProviderPoolMemberInput};
|
||||
use crate::providers::{
|
||||
AntigravityProviderPoolAdapter, ChatGptWebProviderPoolAdapter, CodexProviderPoolAdapter,
|
||||
DefaultProviderPoolAdapter, GeminiCliProviderPoolAdapter, GrokProviderPoolAdapter,
|
||||
KiroProviderPoolAdapter, WindsurfProviderPoolAdapter, XaiProviderPoolAdapter,
|
||||
CLAUDE_CODE_PROVIDER_POOL_ADAPTER, VERTEX_AI_PROVIDER_POOL_ADAPTER,
|
||||
AntigravityProviderPoolAdapter, ChatGptWebProviderPoolAdapter, ClaudeCodeProviderPoolAdapter,
|
||||
CodexProviderPoolAdapter, DefaultProviderPoolAdapter, GeminiCliProviderPoolAdapter,
|
||||
GrokProviderPoolAdapter, KiroProviderPoolAdapter, WindsurfProviderPoolAdapter,
|
||||
XaiProviderPoolAdapter, VERTEX_AI_PROVIDER_POOL_ADAPTER,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -48,7 +48,7 @@ impl ProviderPoolService {
|
||||
pub fn with_builtin_adapters() -> Self {
|
||||
Self::new()
|
||||
.with_adapter(Arc::new(AntigravityProviderPoolAdapter))
|
||||
.with_adapter(Arc::new(CLAUDE_CODE_PROVIDER_POOL_ADAPTER))
|
||||
.with_adapter(Arc::new(ClaudeCodeProviderPoolAdapter))
|
||||
.with_adapter(Arc::new(CodexProviderPoolAdapter))
|
||||
.with_adapter(Arc::new(GeminiCliProviderPoolAdapter))
|
||||
.with_adapter(Arc::new(GrokProviderPoolAdapter))
|
||||
|
||||
Reference in New Issue
Block a user