mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
Refactor provider transport modules
This commit is contained in:
@@ -12,6 +12,7 @@ use aether_scheduler_core::{
|
||||
ClientSessionAffinity, SchedulerMinimalCandidateSelectionCandidate, SchedulerRankingOutcome,
|
||||
};
|
||||
|
||||
use crate::ai_serving::transport::provider_types::provider_runtime_policy;
|
||||
use crate::ai_serving::{
|
||||
candidate_common_transport_skip_reason, candidate_transport_pair_skip_reason,
|
||||
CandidateTransportPolicyFacts, GatewayAuthApiKeySnapshot, GatewayProviderTransportSnapshot,
|
||||
@@ -398,6 +399,8 @@ pub(crate) fn candidate_auth_channel_skip_reason(
|
||||
let request_auth_channel = normalize_request_auth_channel(request_auth_channel?)?;
|
||||
let upstream_auth_channel = resolve_transport_request_auth_channel(transport)?;
|
||||
if request_auth_channel == upstream_auth_channel
|
||||
|| provider_runtime_policy(&transport.provider.provider_type)
|
||||
.allow_auth_channel_mismatch_by_default
|
||||
|| allow_auth_channel_mismatch_for_format(transport)
|
||||
{
|
||||
None
|
||||
@@ -418,12 +421,11 @@ fn resolve_transport_request_auth_channel(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
) -> Option<&'static str> {
|
||||
let auth_type = resolve_transport_auth_type_for_endpoint_format(transport);
|
||||
let provider_policy = provider_runtime_policy(&transport.provider.provider_type);
|
||||
match auth_type.as_str() {
|
||||
"api_key" => Some("api_key"),
|
||||
"bearer" => Some("bearer_like"),
|
||||
"oauth" if provider_uses_bearer_like_oauth(&transport.provider.provider_type) => {
|
||||
Some("bearer_like")
|
||||
}
|
||||
"oauth" if provider_policy.oauth_is_bearer_like => Some("bearer_like"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -450,13 +452,6 @@ fn resolve_transport_auth_type_for_endpoint_format(
|
||||
.unwrap_or(default_auth_type)
|
||||
}
|
||||
|
||||
fn provider_uses_bearer_like_oauth(provider_type: &str) -> bool {
|
||||
matches!(
|
||||
provider_type.trim().to_ascii_lowercase().as_str(),
|
||||
"claude_code" | "chatgpt_web" | "gemini_cli" | "antigravity" | "kiro"
|
||||
)
|
||||
}
|
||||
|
||||
fn allow_auth_channel_mismatch_for_format(transport: &GatewayProviderTransportSnapshot) -> bool {
|
||||
let api_format = crate::ai_serving::normalize_api_format_alias(&transport.endpoint.api_format);
|
||||
transport
|
||||
@@ -622,6 +617,16 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_channel_gate_allows_kiro_provider_mismatch_by_default() {
|
||||
let mut transport = sample_transport("oauth");
|
||||
transport.provider.provider_type = "kiro".to_string();
|
||||
assert_eq!(
|
||||
candidate_auth_channel_skip_reason(&transport, Some("api_key")),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_group_common_gate_ignores_representative_key_model_policy() {
|
||||
let candidate = sample_candidate();
|
||||
|
||||
@@ -2008,7 +2008,7 @@ fn ai_serving_video_routes_request_preparation_through_request_payload_seams() {
|
||||
}
|
||||
|
||||
let provider_transport_video =
|
||||
read_workspace_file("crates/aether-provider-transport/src/video.rs");
|
||||
read_workspace_file("crates/aether-provider-transport/src/video/mod.rs");
|
||||
for pattern in [
|
||||
"pub enum ProviderVideoCreateFamily",
|
||||
"pub fn video_create_transport_unsupported_reason(",
|
||||
@@ -2083,7 +2083,7 @@ fn ai_serving_files_routes_request_preparation_through_request_payload_seams() {
|
||||
}
|
||||
|
||||
let provider_transport_files =
|
||||
read_workspace_file("crates/aether-provider-transport/src/gemini_files.rs");
|
||||
read_workspace_file("crates/aether-provider-transport/src/gemini_files/mod.rs");
|
||||
for pattern in [
|
||||
"pub fn gemini_files_transport_unsupported_reason(",
|
||||
"pub fn resolve_gemini_files_auth(",
|
||||
@@ -2169,7 +2169,7 @@ fn ai_serving_image_routes_split_surface_normalization_and_transport_policy() {
|
||||
}
|
||||
|
||||
let provider_transport_image =
|
||||
read_workspace_file("crates/aether-provider-transport/src/openai_image.rs");
|
||||
read_workspace_file("crates/aether-provider-transport/src/openai_image/mod.rs");
|
||||
for pattern in [
|
||||
"pub fn openai_image_transport_unsupported_reason(",
|
||||
"pub fn resolve_openai_image_auth(",
|
||||
@@ -2622,7 +2622,7 @@ fn ai_serving_standard_attempts_consume_eligible_local_candidates_without_transp
|
||||
}
|
||||
|
||||
let provider_transport_standard =
|
||||
read_workspace_file("crates/aether-provider-transport/src/standard.rs");
|
||||
read_workspace_file("crates/aether-provider-transport/src/standard/mod.rs");
|
||||
for pattern in [
|
||||
"pub struct StandardProviderRequestHeadersInput",
|
||||
"pub struct StandardProviderRequestHeaders",
|
||||
@@ -2642,7 +2642,7 @@ fn ai_serving_standard_attempts_consume_eligible_local_candidates_without_transp
|
||||
}
|
||||
|
||||
let provider_transport_request_url =
|
||||
read_workspace_file("crates/aether-provider-transport/src/request_url.rs");
|
||||
read_workspace_file("crates/aether-provider-transport/src/request_url/mod.rs");
|
||||
assert!(
|
||||
provider_transport_request_url.contains("pub fn build_kiro_cross_format_upstream_url("),
|
||||
"provider-transport request_url.rs should own Kiro cross-format URL hook"
|
||||
@@ -2704,7 +2704,7 @@ fn ai_serving_standard_plan_builders_delegate_fallback_transport_policy() {
|
||||
}
|
||||
|
||||
let provider_transport_standard =
|
||||
read_workspace_file("crates/aether-provider-transport/src/standard.rs");
|
||||
read_workspace_file("crates/aether-provider-transport/src/standard/mod.rs");
|
||||
for pattern in [
|
||||
"pub enum StandardPlanFallbackAcceptPolicy",
|
||||
"pub struct StandardPlanFallbackHeadersInput",
|
||||
@@ -2972,7 +2972,7 @@ fn ai_serving_same_format_provider_request_policy_owns_provider_type_behavior()
|
||||
"apps/aether-gateway/src/ai_serving/planner/passthrough/provider/family/request/policy.rs",
|
||||
);
|
||||
let provider_transport_policy =
|
||||
read_workspace_file("crates/aether-provider-transport/src/same_format_provider.rs");
|
||||
read_workspace_file("crates/aether-provider-transport/src/same_format_provider/mod.rs");
|
||||
for pattern in [
|
||||
"pub struct SameFormatProviderRequestBehavior {",
|
||||
"pub struct SameFormatProviderRequestBodyInput",
|
||||
|
||||
@@ -7,6 +7,22 @@ mod refresh;
|
||||
mod request;
|
||||
mod url;
|
||||
|
||||
use crate::provider_types::{
|
||||
ProviderApiFormatInheritance, ProviderLocalEmbeddingSupport, ProviderRuntimePolicy,
|
||||
};
|
||||
|
||||
pub const RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
fixed_provider: true,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::OAuthOrConfiguredBearer,
|
||||
enable_format_conversion_by_default: true,
|
||||
allow_auth_channel_mismatch_by_default: true,
|
||||
oauth_is_bearer_like: true,
|
||||
supports_model_fetch: false,
|
||||
supports_local_openai_chat_transport: false,
|
||||
supports_local_same_format_transport: false,
|
||||
local_embedding_support: ProviderLocalEmbeddingSupport::None,
|
||||
};
|
||||
|
||||
pub use auth::{
|
||||
build_kiro_request_auth_from_config, is_kiro_claude_messages_transport,
|
||||
is_kiro_provider_transport, resolve_local_kiro_bearer_auth, resolve_local_kiro_request_auth,
|
||||
@@ -42,12 +42,117 @@ pub struct FixedProviderEndpointTemplate {
|
||||
pub config_defaults: &'static [FixedProviderEndpointConfigDefault],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProviderApiFormatInheritance {
|
||||
None,
|
||||
OAuth,
|
||||
OAuthOrBearer,
|
||||
OAuthOrConfiguredBearer,
|
||||
}
|
||||
|
||||
impl ProviderApiFormatInheritance {
|
||||
pub fn key_inherits_api_formats(
|
||||
self,
|
||||
auth_type: &str,
|
||||
decrypted_auth_config: Option<&str>,
|
||||
) -> bool {
|
||||
let auth_type = auth_type.trim().to_ascii_lowercase();
|
||||
match self {
|
||||
Self::None => false,
|
||||
Self::OAuth => auth_type == "oauth",
|
||||
Self::OAuthOrBearer => auth_type == "oauth" || auth_type == "bearer",
|
||||
Self::OAuthOrConfiguredBearer => {
|
||||
auth_type == "oauth"
|
||||
|| auth_type == "bearer"
|
||||
&& decrypted_auth_config
|
||||
.map(str::trim)
|
||||
.is_some_and(|value| !value.is_empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProviderLocalEmbeddingSupport {
|
||||
None,
|
||||
AnyKnown,
|
||||
OpenAi,
|
||||
Gemini,
|
||||
Jina,
|
||||
Doubao,
|
||||
}
|
||||
|
||||
impl ProviderLocalEmbeddingSupport {
|
||||
pub fn supports_api_format(self, api_format: &str) -> bool {
|
||||
let api_format = aether_ai_formats::normalize_api_format_alias(api_format);
|
||||
match self {
|
||||
Self::None => false,
|
||||
Self::AnyKnown => matches!(
|
||||
api_format.as_str(),
|
||||
"openai:embedding"
|
||||
| "openai:rerank"
|
||||
| "gemini:embedding"
|
||||
| "jina:embedding"
|
||||
| "jina:rerank"
|
||||
| "doubao:embedding"
|
||||
),
|
||||
Self::OpenAi => matches!(api_format.as_str(), "openai:embedding" | "openai:rerank"),
|
||||
Self::Gemini => api_format == "gemini:embedding",
|
||||
Self::Jina => matches!(api_format.as_str(), "jina:embedding" | "jina:rerank"),
|
||||
Self::Doubao => api_format == "doubao:embedding",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ProviderRuntimePolicy {
|
||||
pub fixed_provider: bool,
|
||||
pub api_format_inheritance: ProviderApiFormatInheritance,
|
||||
pub enable_format_conversion_by_default: bool,
|
||||
pub allow_auth_channel_mismatch_by_default: bool,
|
||||
pub oauth_is_bearer_like: bool,
|
||||
pub supports_model_fetch: bool,
|
||||
pub supports_local_openai_chat_transport: bool,
|
||||
pub supports_local_same_format_transport: bool,
|
||||
pub local_embedding_support: ProviderLocalEmbeddingSupport,
|
||||
}
|
||||
|
||||
impl ProviderRuntimePolicy {
|
||||
pub const fn standard() -> Self {
|
||||
Self {
|
||||
fixed_provider: false,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::None,
|
||||
enable_format_conversion_by_default: false,
|
||||
allow_auth_channel_mismatch_by_default: false,
|
||||
oauth_is_bearer_like: false,
|
||||
supports_model_fetch: true,
|
||||
supports_local_openai_chat_transport: true,
|
||||
supports_local_same_format_transport: true,
|
||||
local_embedding_support: ProviderLocalEmbeddingSupport::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_inherits_api_formats(
|
||||
self,
|
||||
auth_type: &str,
|
||||
decrypted_auth_config: Option<&str>,
|
||||
) -> bool {
|
||||
self.api_format_inheritance
|
||||
.key_inherits_api_formats(auth_type, decrypted_auth_config)
|
||||
}
|
||||
|
||||
pub fn supports_local_embedding_transport(self, api_format: &str) -> bool {
|
||||
self.local_embedding_support.supports_api_format(api_format)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct FixedProviderTemplate {
|
||||
pub provider_type: &'static str,
|
||||
pub version: u32,
|
||||
pub base_url: &'static str,
|
||||
pub endpoints: &'static [FixedProviderEndpointTemplate],
|
||||
pub runtime_policy: ProviderRuntimePolicy,
|
||||
}
|
||||
|
||||
const EMPTY_ENDPOINT_CONFIG_DEFAULTS: &[FixedProviderEndpointConfigDefault] = &[];
|
||||
@@ -57,6 +162,83 @@ const FORCE_STREAM_ENDPOINT_CONFIG_DEFAULTS: &[FixedProviderEndpointConfigDefaul
|
||||
value: FixedProviderEndpointConfigValue::String("force_stream"),
|
||||
}];
|
||||
|
||||
const STANDARD_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy::standard();
|
||||
const CUSTOM_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
local_embedding_support: ProviderLocalEmbeddingSupport::AnyKnown,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const OPENAI_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
local_embedding_support: ProviderLocalEmbeddingSupport::OpenAi,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const GEMINI_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
local_embedding_support: ProviderLocalEmbeddingSupport::Gemini,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const JINA_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
local_embedding_support: ProviderLocalEmbeddingSupport::Jina,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const DOUBAO_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
local_embedding_support: ProviderLocalEmbeddingSupport::Doubao,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
|
||||
const CLAUDE_CODE_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
fixed_provider: true,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::OAuth,
|
||||
enable_format_conversion_by_default: true,
|
||||
oauth_is_bearer_like: true,
|
||||
supports_model_fetch: false,
|
||||
supports_local_openai_chat_transport: false,
|
||||
supports_local_same_format_transport: false,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const CODEX_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
fixed_provider: true,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::OAuth,
|
||||
enable_format_conversion_by_default: true,
|
||||
supports_model_fetch: false,
|
||||
supports_local_openai_chat_transport: false,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const CHATGPT_WEB_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
fixed_provider: true,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::OAuthOrBearer,
|
||||
enable_format_conversion_by_default: true,
|
||||
oauth_is_bearer_like: true,
|
||||
supports_model_fetch: false,
|
||||
supports_local_openai_chat_transport: false,
|
||||
supports_local_same_format_transport: false,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const GEMINI_CLI_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
fixed_provider: true,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::OAuth,
|
||||
oauth_is_bearer_like: true,
|
||||
supports_local_openai_chat_transport: false,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const VERTEX_AI_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
fixed_provider: true,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::OAuth,
|
||||
enable_format_conversion_by_default: true,
|
||||
supports_model_fetch: false,
|
||||
supports_local_openai_chat_transport: false,
|
||||
supports_local_same_format_transport: false,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const ANTIGRAVITY_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
fixed_provider: true,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::OAuth,
|
||||
enable_format_conversion_by_default: true,
|
||||
oauth_is_bearer_like: true,
|
||||
supports_model_fetch: false,
|
||||
supports_local_openai_chat_transport: false,
|
||||
supports_local_same_format_transport: false,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
|
||||
const CLAUDE_CODE_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
provider_type: "claude_code",
|
||||
version: 1,
|
||||
@@ -67,6 +249,7 @@ const CLAUDE_CODE_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProvider
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
}],
|
||||
runtime_policy: CLAUDE_CODE_RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
const CODEX_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
@@ -93,6 +276,7 @@ const CODEX_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTempla
|
||||
config_defaults: FORCE_STREAM_ENDPOINT_CONFIG_DEFAULTS,
|
||||
},
|
||||
],
|
||||
runtime_policy: CODEX_RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
const CHATGPT_WEB_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
@@ -105,6 +289,7 @@ const CHATGPT_WEB_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProvider
|
||||
custom_path: None,
|
||||
config_defaults: FORCE_STREAM_ENDPOINT_CONFIG_DEFAULTS,
|
||||
}],
|
||||
runtime_policy: CHATGPT_WEB_RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
const KIRO_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
@@ -117,6 +302,7 @@ const KIRO_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplat
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
}],
|
||||
runtime_policy: crate::kiro::RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
const GEMINI_CLI_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
@@ -129,6 +315,7 @@ const GEMINI_CLI_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderT
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
}],
|
||||
runtime_policy: GEMINI_CLI_RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
const VERTEX_AI_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
@@ -149,6 +336,7 @@ const VERTEX_AI_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTe
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
},
|
||||
],
|
||||
runtime_policy: VERTEX_AI_RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
const ANTIGRAVITY_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
@@ -161,19 +349,11 @@ const ANTIGRAVITY_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProvider
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
}],
|
||||
runtime_policy: ANTIGRAVITY_RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
pub fn provider_type_is_fixed(provider_type: &str) -> bool {
|
||||
matches!(
|
||||
provider_type.trim().to_ascii_lowercase().as_str(),
|
||||
"claude_code"
|
||||
| "kiro"
|
||||
| "codex"
|
||||
| "chatgpt_web"
|
||||
| "gemini_cli"
|
||||
| "antigravity"
|
||||
| "vertex_ai"
|
||||
)
|
||||
provider_runtime_policy(provider_type).fixed_provider
|
||||
}
|
||||
|
||||
pub fn fixed_provider_key_inherits_api_formats(
|
||||
@@ -181,23 +361,35 @@ pub fn fixed_provider_key_inherits_api_formats(
|
||||
auth_type: &str,
|
||||
decrypted_auth_config: Option<&str>,
|
||||
) -> bool {
|
||||
let provider_type = provider_type.trim().to_ascii_lowercase();
|
||||
let auth_type = auth_type.trim().to_ascii_lowercase();
|
||||
provider_type_is_fixed(&provider_type)
|
||||
&& (auth_type == "oauth"
|
||||
|| provider_type == "chatgpt_web" && auth_type == "bearer"
|
||||
|| provider_type == "kiro"
|
||||
&& auth_type == "bearer"
|
||||
&& decrypted_auth_config
|
||||
.map(str::trim)
|
||||
.is_some_and(|value| !value.is_empty()))
|
||||
provider_runtime_policy(provider_type)
|
||||
.key_inherits_api_formats(auth_type, decrypted_auth_config)
|
||||
}
|
||||
|
||||
pub fn provider_type_enables_format_conversion_by_default(provider_type: &str) -> bool {
|
||||
matches!(
|
||||
provider_type.trim().to_ascii_lowercase().as_str(),
|
||||
"claude_code" | "kiro" | "codex" | "chatgpt_web" | "antigravity" | "vertex_ai"
|
||||
)
|
||||
provider_runtime_policy(provider_type).enable_format_conversion_by_default
|
||||
}
|
||||
|
||||
pub fn provider_type_allows_auth_channel_mismatch_by_default(provider_type: &str) -> bool {
|
||||
provider_runtime_policy(provider_type).allow_auth_channel_mismatch_by_default
|
||||
}
|
||||
|
||||
pub fn provider_type_oauth_is_bearer_like(provider_type: &str) -> bool {
|
||||
provider_runtime_policy(provider_type).oauth_is_bearer_like
|
||||
}
|
||||
|
||||
pub fn provider_runtime_policy(provider_type: &str) -> ProviderRuntimePolicy {
|
||||
if let Some(template) = fixed_provider_template(provider_type) {
|
||||
return template.runtime_policy;
|
||||
}
|
||||
|
||||
match provider_type.trim().to_ascii_lowercase().as_str() {
|
||||
"custom" => CUSTOM_RUNTIME_POLICY,
|
||||
"openai" => OPENAI_RUNTIME_POLICY,
|
||||
"gemini" | "google" => GEMINI_RUNTIME_POLICY,
|
||||
"jina" => JINA_RUNTIME_POLICY,
|
||||
"doubao" | "volcengine" => DOUBAO_RUNTIME_POLICY,
|
||||
_ => STANDARD_RUNTIME_POLICY,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fixed_provider_template(provider_type: &str) -> Option<&'static FixedProviderTemplate> {
|
||||
@@ -225,48 +417,22 @@ pub fn fixed_provider_endpoint_template_by_api_format(
|
||||
}
|
||||
|
||||
pub fn provider_type_supports_model_fetch(provider_type: &str) -> bool {
|
||||
!matches!(
|
||||
provider_type.trim().to_ascii_lowercase().as_str(),
|
||||
"vertex_ai" | "antigravity" | "codex" | "chatgpt_web" | "kiro" | "claude_code"
|
||||
)
|
||||
provider_runtime_policy(provider_type).supports_model_fetch
|
||||
}
|
||||
|
||||
pub fn provider_type_supports_local_openai_chat_transport(provider_type: &str) -> bool {
|
||||
!matches!(
|
||||
provider_type.trim().to_ascii_lowercase().as_str(),
|
||||
"antigravity"
|
||||
| "claude_code"
|
||||
| "codex"
|
||||
| "chatgpt_web"
|
||||
| "gemini_cli"
|
||||
| "kiro"
|
||||
| "vertex_ai"
|
||||
)
|
||||
provider_runtime_policy(provider_type).supports_local_openai_chat_transport
|
||||
}
|
||||
|
||||
pub fn provider_type_supports_local_same_format_transport(provider_type: &str) -> bool {
|
||||
!matches!(
|
||||
provider_type.trim().to_ascii_lowercase().as_str(),
|
||||
"antigravity" | "chatgpt_web" | "claude_code" | "kiro" | "vertex_ai"
|
||||
)
|
||||
provider_runtime_policy(provider_type).supports_local_same_format_transport
|
||||
}
|
||||
|
||||
pub fn provider_type_supports_local_embedding_transport(
|
||||
provider_type: &str,
|
||||
api_format: &str,
|
||||
) -> bool {
|
||||
let provider_type = provider_type.trim().to_ascii_lowercase();
|
||||
let api_format = aether_ai_formats::normalize_api_format_alias(api_format);
|
||||
|
||||
match api_format.as_str() {
|
||||
"openai:embedding" => matches!(provider_type.as_str(), "custom" | "openai"),
|
||||
"openai:rerank" => matches!(provider_type.as_str(), "custom" | "openai"),
|
||||
"gemini:embedding" => matches!(provider_type.as_str(), "custom" | "gemini" | "google"),
|
||||
"jina:embedding" => matches!(provider_type.as_str(), "custom" | "jina"),
|
||||
"jina:rerank" => matches!(provider_type.as_str(), "custom" | "jina"),
|
||||
"doubao:embedding" => matches!(provider_type.as_str(), "custom" | "doubao" | "volcengine"),
|
||||
_ => false,
|
||||
}
|
||||
provider_runtime_policy(provider_type).supports_local_embedding_transport(api_format)
|
||||
}
|
||||
|
||||
pub fn is_codex_cli_backend_url(url: &str) -> bool {
|
||||
@@ -361,7 +527,9 @@ pub const ADMIN_PROVIDER_OAUTH_TEMPLATE_TYPES: &[&str] = &[
|
||||
mod tests {
|
||||
use super::{
|
||||
fixed_provider_endpoint_template_by_api_format, fixed_provider_key_inherits_api_formats,
|
||||
fixed_provider_template, provider_type_supports_local_embedding_transport,
|
||||
fixed_provider_template, provider_runtime_policy,
|
||||
provider_type_allows_auth_channel_mismatch_by_default, provider_type_oauth_is_bearer_like,
|
||||
provider_type_supports_local_embedding_transport,
|
||||
provider_type_supports_local_same_format_transport, FixedProviderEndpointConfigValue,
|
||||
};
|
||||
|
||||
@@ -453,6 +621,52 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kiro_allows_auth_channel_mismatch_by_default() {
|
||||
let policy = provider_runtime_policy("kiro");
|
||||
assert!(policy.fixed_provider);
|
||||
assert!(policy.enable_format_conversion_by_default);
|
||||
assert!(policy.oauth_is_bearer_like);
|
||||
assert!(!policy.supports_model_fetch);
|
||||
assert!(!policy.supports_local_openai_chat_transport);
|
||||
assert!(!policy.supports_local_same_format_transport);
|
||||
assert!(policy.key_inherits_api_formats("oauth", None));
|
||||
assert!(policy.key_inherits_api_formats("bearer", Some("{}")));
|
||||
assert!(!policy.key_inherits_api_formats("bearer", None));
|
||||
|
||||
assert!(provider_type_allows_auth_channel_mismatch_by_default(
|
||||
"kiro"
|
||||
));
|
||||
assert!(provider_type_allows_auth_channel_mismatch_by_default(
|
||||
" KIRO "
|
||||
));
|
||||
assert!(!provider_type_allows_auth_channel_mismatch_by_default(
|
||||
"claude_code"
|
||||
));
|
||||
assert!(!provider_type_allows_auth_channel_mismatch_by_default(
|
||||
"custom"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_policy_preserves_other_fixed_provider_behavior() {
|
||||
let codex = provider_runtime_policy("codex");
|
||||
assert!(codex.fixed_provider);
|
||||
assert!(codex.enable_format_conversion_by_default);
|
||||
assert!(!codex.oauth_is_bearer_like);
|
||||
assert!(!codex.supports_model_fetch);
|
||||
assert!(!codex.supports_local_openai_chat_transport);
|
||||
assert!(codex.supports_local_same_format_transport);
|
||||
|
||||
let gemini_cli = provider_runtime_policy("gemini_cli");
|
||||
assert!(gemini_cli.fixed_provider);
|
||||
assert!(!gemini_cli.enable_format_conversion_by_default);
|
||||
assert!(provider_type_oauth_is_bearer_like("gemini_cli"));
|
||||
assert!(gemini_cli.supports_model_fetch);
|
||||
assert!(!gemini_cli.supports_local_openai_chat_transport);
|
||||
assert!(gemini_cli.supports_local_same_format_transport);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chatgpt_web_does_not_use_generic_same_format_transport() {
|
||||
assert!(!provider_type_supports_local_same_format_transport(
|
||||
|
||||
Reference in New Issue
Block a user