feat(proxy): add full URL mode and refactor endpoint rewriting (#1561)

* feat(proxy): add full URL mode and refactor endpoint rewriting

- Add `isFullUrl` provider meta to treat base_url as complete API endpoint
- Remove hardcoded `?beta=true` from Claude adapter, pass through from client
- Refactor forwarder endpoint rewriting with proper query string handling
- Block provider switching when proxy is required but not running
- Add full URL toggle UI in endpoint field with i18n (zh/en/ja)

* refactor(proxy): remove beta query handling

* fix(proxy): strip beta query when rewriting Claude endpoints

* feat(codex): complete full URL support

* refactor(ui): refine full URL endpoint hint
This commit is contained in:
Dex Miller
2026-03-28 15:52:02 +08:00
committed by GitHub
parent eaf83f4fbe
commit 8cae7b7b73
16 changed files with 454 additions and 103 deletions
+23 -4
View File
@@ -61,12 +61,18 @@ pub async fn get_status(State(state): State<ProxyState>) -> Result<Json<ProxySta
/// - 现在 OpenRouter 已推出 Claude Code 兼容接口,默认不再启用该转换(逻辑保留以备回退)
pub async fn handle_messages(
State(state): State<ProxyState>,
uri: axum::http::Uri,
headers: axum::http::HeaderMap,
Json(body): Json<Value>,
) -> Result<axum::response::Response, ProxyError> {
let mut ctx =
RequestContext::new(&state, &body, &headers, AppType::Claude, "Claude", "claude").await?;
let endpoint = uri
.path_and_query()
.map(|path_and_query| path_and_query.as_str())
.unwrap_or(uri.path());
let is_stream = body
.get("stream")
.and_then(|s| s.as_bool())
@@ -77,7 +83,7 @@ pub async fn handle_messages(
let result = match forwarder
.forward_with_retry(
&AppType::Claude,
"/v1/messages",
endpoint,
body.clone(),
headers,
ctx.get_providers(),
@@ -280,6 +286,13 @@ async fn handle_claude_transform(
})
}
fn endpoint_with_query(uri: &axum::http::Uri, endpoint: &str) -> String {
match uri.query() {
Some(query) => format!("{endpoint}?{query}"),
None => endpoint.to_string(),
}
}
// ============================================================================
// Codex API 处理器
// ============================================================================
@@ -287,11 +300,13 @@ async fn handle_claude_transform(
/// 处理 /v1/chat/completions 请求(OpenAI Chat Completions API - Codex CLI
pub async fn handle_chat_completions(
State(state): State<ProxyState>,
uri: axum::http::Uri,
headers: axum::http::HeaderMap,
Json(body): Json<Value>,
) -> Result<axum::response::Response, ProxyError> {
let mut ctx =
RequestContext::new(&state, &body, &headers, AppType::Codex, "Codex", "codex").await?;
let endpoint = endpoint_with_query(&uri, "/chat/completions");
let is_stream = body
.get("stream")
@@ -302,7 +317,7 @@ pub async fn handle_chat_completions(
let result = match forwarder
.forward_with_retry(
&AppType::Codex,
"/chat/completions",
&endpoint,
body,
headers,
ctx.get_providers(),
@@ -328,11 +343,13 @@ pub async fn handle_chat_completions(
/// 处理 /v1/responses 请求(OpenAI Responses API - Codex CLI 透传)
pub async fn handle_responses(
State(state): State<ProxyState>,
uri: axum::http::Uri,
headers: axum::http::HeaderMap,
Json(body): Json<Value>,
) -> Result<axum::response::Response, ProxyError> {
let mut ctx =
RequestContext::new(&state, &body, &headers, AppType::Codex, "Codex", "codex").await?;
let endpoint = endpoint_with_query(&uri, "/responses");
let is_stream = body
.get("stream")
@@ -343,7 +360,7 @@ pub async fn handle_responses(
let result = match forwarder
.forward_with_retry(
&AppType::Codex,
"/responses",
&endpoint,
body,
headers,
ctx.get_providers(),
@@ -369,11 +386,13 @@ pub async fn handle_responses(
/// 处理 /v1/responses/compact 请求(OpenAI Responses Compact API - Codex CLI 透传)
pub async fn handle_responses_compact(
State(state): State<ProxyState>,
uri: axum::http::Uri,
headers: axum::http::HeaderMap,
Json(body): Json<Value>,
) -> Result<axum::response::Response, ProxyError> {
let mut ctx =
RequestContext::new(&state, &body, &headers, AppType::Codex, "Codex", "codex").await?;
let endpoint = endpoint_with_query(&uri, "/responses/compact");
let is_stream = body
.get("stream")
@@ -384,7 +403,7 @@ pub async fn handle_responses_compact(
let result = match forwarder
.forward_with_retry(
&AppType::Codex,
"/responses/compact",
&endpoint,
body,
headers,
ctx.get_providers(),