mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
refactor: 全局适配 ApiFamily/EndpointKind 结构化标识体系
将新的 (ApiFamily, EndpointKind) / `family:kind` 签名体系应用到整个代码库: - API Handlers: 所有 adapter/handler 使用新的签名格式 - Services: provider, model, usage, cache, auth 等服务层适配 - Database: ProviderEndpoint 新增 api_family/endpoint_kind 字段 - Frontend: Provider 管理、Usage 表格等组件适配 - Tests: 更新所有相关测试用例
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -132,13 +133,15 @@ async def get_model_supported_capabilities(
|
||||
for cap_name in supported_caps:
|
||||
if cap_name in all_caps:
|
||||
cap = all_caps[cap_name]
|
||||
capability_details.append({
|
||||
"name": cap.name,
|
||||
"display_name": cap.display_name,
|
||||
"description": cap.description,
|
||||
"match_mode": cap.match_mode.value,
|
||||
"config_mode": cap.config_mode.value,
|
||||
})
|
||||
capability_details.append(
|
||||
{
|
||||
"name": cap.name,
|
||||
"display_name": cap.display_name,
|
||||
"description": cap.description,
|
||||
"match_mode": cap.match_mode.value,
|
||||
"config_mode": cap.config_mode.value,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"model": model_name,
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, Request
|
||||
from sqlalchemy import and_, or_
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
from src.api.base.adapter import ApiAdapter, ApiMode
|
||||
from src.api.base.context import ApiRequestContext
|
||||
from src.api.base.pipeline import ApiRequestPipeline
|
||||
from src.core.logger import logger
|
||||
from src.database import get_db
|
||||
@@ -38,7 +39,6 @@ from src.models.endpoint_models import (
|
||||
PublicHealthEvent,
|
||||
)
|
||||
from src.services.health.endpoint import EndpointHealthService
|
||||
from src.api.base.context import ApiRequestContext
|
||||
|
||||
router = APIRouter(prefix="/api/public", tags=["System Catalog"])
|
||||
pipeline = ApiRequestPipeline()
|
||||
@@ -376,9 +376,17 @@ class PublicModelsAdapter(PublicApiAdapter):
|
||||
provider_name=provider.name,
|
||||
name=unified_name,
|
||||
display_name=display_name,
|
||||
description=global_model.config.get("description") if global_model and global_model.config else None,
|
||||
description=(
|
||||
global_model.config.get("description")
|
||||
if global_model and global_model.config
|
||||
else None
|
||||
),
|
||||
tags=None,
|
||||
icon_url=global_model.config.get("icon_url") if global_model and global_model.config else None,
|
||||
icon_url=(
|
||||
global_model.config.get("icon_url")
|
||||
if global_model and global_model.config
|
||||
else None
|
||||
),
|
||||
input_price_per_1m=model.get_effective_input_price(),
|
||||
output_price_per_1m=model.get_effective_output_price(),
|
||||
cache_creation_price_per_1m=model.get_effective_cache_creation_price(),
|
||||
@@ -469,9 +477,17 @@ class PublicSearchModelsAdapter(PublicApiAdapter):
|
||||
provider_name=provider.name,
|
||||
name=unified_name,
|
||||
display_name=display_name,
|
||||
description=global_model.config.get("description") if global_model and global_model.config else None,
|
||||
description=(
|
||||
global_model.config.get("description")
|
||||
if global_model and global_model.config
|
||||
else None
|
||||
),
|
||||
tags=None,
|
||||
icon_url=global_model.config.get("icon_url") if global_model and global_model.config else None,
|
||||
icon_url=(
|
||||
global_model.config.get("icon_url")
|
||||
if global_model and global_model.config
|
||||
else None
|
||||
),
|
||||
input_price_per_1m=model.get_effective_input_price(),
|
||||
output_price_per_1m=model.get_effective_output_price(),
|
||||
cache_creation_price_per_1m=model.get_effective_cache_creation_price(),
|
||||
@@ -612,13 +628,9 @@ class PublicApiFormatHealthMonitorAdapter(PublicApiAdapter):
|
||||
)
|
||||
|
||||
# 获取本站入口路径
|
||||
from src.core.api_format import APIFormat, get_local_path
|
||||
from src.core.api_format import get_local_path_for_endpoint
|
||||
|
||||
try:
|
||||
api_format_enum = APIFormat(api_format)
|
||||
local_path = get_local_path(api_format_enum)
|
||||
except ValueError:
|
||||
local_path = "/"
|
||||
local_path = get_local_path_for_endpoint(api_format)
|
||||
|
||||
monitors.append(
|
||||
PublicApiFormatHealthMonitor(
|
||||
|
||||
@@ -8,6 +8,7 @@ Claude API 端点
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -16,11 +17,9 @@ from src.api.handlers.claude import (
|
||||
ClaudeTokenCountAdapter,
|
||||
build_claude_adapter,
|
||||
)
|
||||
from src.core.api_format import APIFormat, get_api_format_definition
|
||||
from src.database import get_db
|
||||
|
||||
_claude_def = get_api_format_definition(APIFormat.CLAUDE)
|
||||
router = APIRouter(tags=["Claude API"], prefix=_claude_def.path_prefix)
|
||||
router = APIRouter(tags=["Claude API"])
|
||||
pipeline = ApiRequestPipeline()
|
||||
|
||||
|
||||
|
||||
@@ -11,19 +11,16 @@ Gemini API 专属端点
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.api.base.pipeline import ApiRequestPipeline
|
||||
from src.api.handlers.gemini import build_gemini_adapter
|
||||
from src.api.handlers.gemini_cli import build_gemini_cli_adapter
|
||||
from src.core.api_format import APIFormat, get_api_format_definition
|
||||
from src.database import get_db
|
||||
|
||||
# 从配置获取路径前缀
|
||||
_gemini_def = get_api_format_definition(APIFormat.GEMINI)
|
||||
|
||||
router = APIRouter(tags=["Gemini API"], prefix=_gemini_def.path_prefix)
|
||||
router = APIRouter(tags=["Gemini API"])
|
||||
pipeline = ApiRequestPipeline()
|
||||
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@ from fastapi.responses import JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
from src.core.api_format import APIFormat, get_auth_handler, get_default_auth_method
|
||||
from src.core.api_format.metadata import get_api_format_definition
|
||||
from src.core.api_format import get_auth_handler, get_default_auth_method_for_endpoint
|
||||
from src.core.crypto import crypto_service
|
||||
from src.core.logger import logger
|
||||
from src.database import get_db
|
||||
@@ -38,10 +37,7 @@ from src.services.cache.aware_scheduler import CacheAwareScheduler, ProviderCand
|
||||
from src.services.gemini_files_mapping import delete_file_key_mapping, store_file_key_mapping
|
||||
from src.services.provider.transport import redact_url_for_log
|
||||
|
||||
# 从配置获取路径前缀
|
||||
_gemini_def = get_api_format_definition(APIFormat.GEMINI)
|
||||
|
||||
router = APIRouter(tags=["Gemini Files API"], prefix=_gemini_def.path_prefix)
|
||||
router = APIRouter(tags=["Gemini Files API"])
|
||||
|
||||
# Gemini Files API 基础 URL
|
||||
GEMINI_FILES_BASE_URL = "https://generativelanguage.googleapis.com"
|
||||
@@ -70,7 +66,7 @@ def _extract_gemini_api_key(request: Request) -> str | None:
|
||||
1. URL 参数 ?key=
|
||||
2. x-goog-api-key 请求头
|
||||
"""
|
||||
auth_method = get_default_auth_method(APIFormat.GEMINI)
|
||||
auth_method = get_default_auth_method_for_endpoint("gemini:chat")
|
||||
handler = get_auth_handler(auth_method)
|
||||
return handler.extract_credentials(request)
|
||||
|
||||
@@ -175,7 +171,7 @@ def _resolve_files_model_name(
|
||||
Model.is_active == True,
|
||||
Provider.is_active == True,
|
||||
ProviderEndpoint.is_active == True,
|
||||
ProviderEndpoint.api_format == APIFormat.GEMINI.value,
|
||||
ProviderEndpoint.api_family == "gemini",
|
||||
)
|
||||
.distinct()
|
||||
.order_by(GlobalModel.name.asc())
|
||||
@@ -193,7 +189,7 @@ async def _select_provider_candidate(
|
||||
scheduler = CacheAwareScheduler()
|
||||
candidates, _global_model_id = await scheduler.list_all_candidates(
|
||||
db=db,
|
||||
api_format=APIFormat.GEMINI,
|
||||
api_format="gemini:chat",
|
||||
model_name=model_name,
|
||||
affinity_key=str(user_api_key.id),
|
||||
user_api_key=user_api_key,
|
||||
|
||||
@@ -20,12 +20,7 @@ from src.api.base.models_service import (
|
||||
list_available_models,
|
||||
)
|
||||
from src.core.api_format import (
|
||||
API_FORMAT_DEFINITIONS,
|
||||
APIFormat,
|
||||
ApiFormatDefinition,
|
||||
detect_request_context,
|
||||
get_auth_handler,
|
||||
get_default_auth_method,
|
||||
)
|
||||
from src.core.api_format.conversion import (
|
||||
format_conversion_registry,
|
||||
@@ -35,32 +30,23 @@ from src.core.logger import logger
|
||||
from src.database import get_db
|
||||
from src.models.database import ApiKey, User
|
||||
from src.services.auth.service import AuthService
|
||||
from src.services.provider.format import normalize_endpoint_signature
|
||||
|
||||
router = APIRouter(tags=["System Catalog"])
|
||||
|
||||
# 各格式对应的 API 格式列表(包括对应的 CLI 格式)
|
||||
_CLAUDE_FORMATS = [APIFormat.CLAUDE.value, APIFormat.CLAUDE_CLI.value]
|
||||
_OPENAI_FORMATS = [APIFormat.OPENAI.value, APIFormat.OPENAI_CLI.value]
|
||||
_GEMINI_FORMATS = [APIFormat.GEMINI.value, APIFormat.GEMINI_CLI.value]
|
||||
_CLAUDE_FORMATS = ["claude:chat", "claude:cli"]
|
||||
_OPENAI_FORMATS = ["openai:chat", "openai:cli"]
|
||||
_GEMINI_FORMATS = ["gemini:chat", "gemini:cli"]
|
||||
|
||||
# 所有格式(用于格式转换时的查询)
|
||||
_ALL_CHAT_FORMATS = [
|
||||
APIFormat.CLAUDE.value,
|
||||
APIFormat.CLAUDE_CLI.value,
|
||||
APIFormat.OPENAI.value,
|
||||
APIFormat.OPENAI_CLI.value,
|
||||
APIFormat.GEMINI.value,
|
||||
APIFormat.GEMINI_CLI.value,
|
||||
*_CLAUDE_FORMATS,
|
||||
*_OPENAI_FORMATS,
|
||||
*_GEMINI_FORMATS,
|
||||
]
|
||||
|
||||
|
||||
def _extract_api_key_from_request(request: Request, definition: ApiFormatDefinition) -> str | None:
|
||||
"""根据格式定义从请求中提取 API Key"""
|
||||
auth_method = get_default_auth_method(definition.api_format)
|
||||
handler = get_auth_handler(auth_method)
|
||||
return handler.extract_credentials(request)
|
||||
|
||||
|
||||
def _detect_api_format_and_key(request: Request) -> tuple[str, str | None]:
|
||||
"""
|
||||
根据请求头检测 API 格式并提取 API Key
|
||||
@@ -74,17 +60,17 @@ def _detect_api_format_and_key(request: Request) -> tuple[str, str | None]:
|
||||
(api_format, api_key) 元组
|
||||
"""
|
||||
context = detect_request_context(request)
|
||||
return context.data_format.value.lower(), context.credentials
|
||||
return context.endpoint.key, context.credentials
|
||||
|
||||
|
||||
def _get_formats_for_api(api_format: str) -> list[str]:
|
||||
"""获取对应 API 格式的端点格式列表"""
|
||||
if api_format == "claude":
|
||||
fam = (api_format.split(":", 1)[0] if api_format else "").strip().lower()
|
||||
if fam == "claude":
|
||||
return _CLAUDE_FORMATS
|
||||
elif api_format == "gemini":
|
||||
if fam == "gemini":
|
||||
return _GEMINI_FORMATS
|
||||
else:
|
||||
return _OPENAI_FORMATS
|
||||
return _OPENAI_FORMATS
|
||||
|
||||
|
||||
def _is_format_conversion_enabled() -> bool:
|
||||
@@ -101,30 +87,32 @@ def _get_convertible_formats(client_format: str, global_conversion_enabled: bool
|
||||
当启用格式转换时,返回所有可以转换的格式;
|
||||
否则只返回客户端格式本身(不包括同族的其他格式)。
|
||||
"""
|
||||
client_format_upper = client_format.upper()
|
||||
client_format_norm = normalize_endpoint_signature(client_format)
|
||||
|
||||
# 格式转换关闭时,只返回客户端格式本身
|
||||
if not global_conversion_enabled:
|
||||
return [client_format_upper]
|
||||
return [client_format_norm]
|
||||
|
||||
# 收集所有可转换的格式
|
||||
register_default_normalizers()
|
||||
convertible_formats = []
|
||||
convertible_formats: list[str] = []
|
||||
for target_format in _ALL_CHAT_FORMATS:
|
||||
target_norm = normalize_endpoint_signature(target_format)
|
||||
# 相同格式始终可用
|
||||
if target_format == client_format_upper:
|
||||
convertible_formats.append(target_format)
|
||||
if target_norm == client_format_norm:
|
||||
convertible_formats.append(target_norm)
|
||||
continue
|
||||
|
||||
# 检查是否有双向转换器
|
||||
if format_conversion_registry.can_convert_full(
|
||||
client_format_upper,
|
||||
target_format,
|
||||
client_format_norm,
|
||||
target_norm,
|
||||
require_stream=False,
|
||||
):
|
||||
convertible_formats.append(target_format)
|
||||
convertible_formats.append(target_norm)
|
||||
|
||||
return convertible_formats if convertible_formats else [client_format_upper]
|
||||
# 去重并保持稳定顺序
|
||||
return list(dict.fromkeys(convertible_formats)) if convertible_formats else [client_format_norm]
|
||||
|
||||
|
||||
def _flatten_provider_formats(provider_to_formats: dict[str, set[str]]) -> list[str]:
|
||||
@@ -137,11 +125,17 @@ def _flatten_provider_formats(provider_to_formats: dict[str, set[str]]) -> list[
|
||||
return sorted(all_formats)
|
||||
|
||||
|
||||
def _get_family(api_format: str) -> str:
|
||||
"""从 endpoint signature 提取协议族(如 'openai:chat' -> 'openai')。"""
|
||||
return (str(api_format).split(":", 1)[0] if api_format else "").strip().lower()
|
||||
|
||||
|
||||
def _build_empty_list_response(api_format: str) -> dict:
|
||||
"""根据 API 格式构建空列表响应"""
|
||||
if api_format == "claude":
|
||||
fam = _get_family(api_format)
|
||||
if fam == "claude":
|
||||
return {"data": [], "has_more": False, "first_id": None, "last_id": None}
|
||||
elif api_format == "gemini":
|
||||
elif fam == "gemini":
|
||||
return {"models": []}
|
||||
else:
|
||||
return {"object": "list", "data": []}
|
||||
@@ -159,9 +153,7 @@ def _filter_formats_by_restrictions(
|
||||
"""
|
||||
if restrictions.allowed_api_formats is None:
|
||||
return formats, None
|
||||
# 统一转为大写比较,兼容数据库中存储的大小写
|
||||
allowed_upper = {f.upper() for f in restrictions.allowed_api_formats}
|
||||
filtered = [f for f in formats if f.upper() in allowed_upper]
|
||||
filtered = [f for f in formats if restrictions.is_api_format_allowed(f)]
|
||||
if not filtered:
|
||||
logger.info(f"[Models] API Key 不允许访问格式 {api_format}")
|
||||
return [], _build_empty_list_response(api_format)
|
||||
@@ -191,7 +183,8 @@ def _authenticate(db: Session, api_key: str | None) -> tuple[User | None, ApiKey
|
||||
|
||||
def _build_auth_error_response(api_format: str) -> JSONResponse:
|
||||
"""根据 API 格式构建认证错误响应"""
|
||||
if api_format == "claude":
|
||||
fam = _get_family(api_format)
|
||||
if fam == "claude":
|
||||
return JSONResponse(
|
||||
status_code=401,
|
||||
content={
|
||||
@@ -202,7 +195,7 @@ def _build_auth_error_response(api_format: str) -> JSONResponse:
|
||||
},
|
||||
},
|
||||
)
|
||||
elif api_format == "gemini":
|
||||
elif fam == "gemini":
|
||||
return JSONResponse(
|
||||
status_code=401,
|
||||
content={
|
||||
@@ -383,7 +376,8 @@ def _build_gemini_model_response(model_info: ModelInfo) -> dict:
|
||||
|
||||
def _build_404_response(model_id: str, api_format: str) -> JSONResponse:
|
||||
"""根据 API 格式构建 404 响应"""
|
||||
if api_format == "claude":
|
||||
fam = _get_family(api_format)
|
||||
if fam == "claude":
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={
|
||||
@@ -391,7 +385,7 @@ def _build_404_response(model_id: str, api_format: str) -> JSONResponse:
|
||||
"error": {"type": "not_found_error", "message": f"Model '{model_id}' not found"},
|
||||
},
|
||||
)
|
||||
elif api_format == "gemini":
|
||||
elif fam == "gemini":
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={
|
||||
@@ -533,9 +527,9 @@ async def list_models(
|
||||
)
|
||||
logger.debug(f"[Models] 返回 {len(models)} 个模型")
|
||||
|
||||
if api_format == "claude":
|
||||
if _get_family(api_format) == "claude":
|
||||
return _build_claude_list_response(models, before_id, after_id, limit)
|
||||
elif api_format == "gemini":
|
||||
elif _get_family(api_format) == "gemini":
|
||||
return _build_gemini_list_response(models, page_size, page_token)
|
||||
else:
|
||||
return _build_openai_list_response(models)
|
||||
@@ -596,7 +590,7 @@ async def retrieve_model(
|
||||
api_format, api_key = _detect_api_format_and_key(request)
|
||||
|
||||
# Gemini 格式的 name 带 "models/" 前缀,需要移除
|
||||
if api_format == "gemini" and model_id.startswith("models/"):
|
||||
if _get_family(api_format) == "gemini" and model_id.startswith("models/"):
|
||||
model_id = model_id[7:]
|
||||
|
||||
logger.info(f"[Models] GET /v1/models/{model_id} | format={api_format}")
|
||||
@@ -635,9 +629,9 @@ async def retrieve_model(
|
||||
if not model_info:
|
||||
return _build_404_response(model_id, api_format)
|
||||
|
||||
if api_format == "claude":
|
||||
if _get_family(api_format) == "claude":
|
||||
return _build_claude_model_response(model_info)
|
||||
elif api_format == "gemini":
|
||||
elif _get_family(api_format) == "gemini":
|
||||
return _build_gemini_model_response(model_info)
|
||||
else:
|
||||
return _build_openai_model_response(model_info)
|
||||
@@ -682,29 +676,27 @@ async def list_models_gemini(
|
||||
"""
|
||||
logger.info("[Models] GET /v1beta/models | format=gemini")
|
||||
|
||||
# 从 x-goog-api-key 或 ?key= 提取 API Key
|
||||
gemini_def = API_FORMAT_DEFINITIONS[APIFormat.GEMINI]
|
||||
api_key = _extract_api_key_from_request(request, gemini_def)
|
||||
api_format, api_key = _detect_api_format_and_key(request)
|
||||
|
||||
# 认证
|
||||
user, key_record = _authenticate(db, api_key)
|
||||
if not user:
|
||||
return _build_auth_error_response("gemini")
|
||||
return _build_auth_error_response(api_format)
|
||||
|
||||
# 构建访问限制
|
||||
restrictions = AccessRestrictions.from_api_key_and_user(key_record, user)
|
||||
|
||||
# 获取可用格式(包括可转换的格式)
|
||||
global_conversion_enabled = _is_format_conversion_enabled()
|
||||
candidate_formats = _get_convertible_formats("gemini", global_conversion_enabled)
|
||||
candidate_formats = _get_convertible_formats(api_format, global_conversion_enabled)
|
||||
candidate_formats, empty_response = _filter_formats_by_restrictions(
|
||||
candidate_formats, restrictions, "gemini"
|
||||
candidate_formats, restrictions, api_format
|
||||
)
|
||||
if empty_response is not None:
|
||||
return empty_response
|
||||
|
||||
provider_to_formats = get_compatible_provider_formats(
|
||||
db, "gemini", candidate_formats, global_conversion_enabled
|
||||
db, api_format, candidate_formats, global_conversion_enabled
|
||||
)
|
||||
formats = _flatten_provider_formats(provider_to_formats)
|
||||
|
||||
@@ -718,7 +710,7 @@ async def list_models_gemini(
|
||||
formats,
|
||||
restrictions,
|
||||
provider_to_formats=provider_to_formats,
|
||||
client_format="gemini",
|
||||
client_format=api_format,
|
||||
)
|
||||
logger.debug(f"[Models] 返回 {len(models)} 个模型")
|
||||
response = _build_gemini_list_response(models, page_size, page_token)
|
||||
@@ -763,30 +755,28 @@ async def get_model_gemini(
|
||||
model_id = model_name[7:] if model_name.startswith("models/") else model_name
|
||||
logger.info(f"[Models] GET /v1beta/models/{model_id} | format=gemini")
|
||||
|
||||
# 从 x-goog-api-key 或 ?key= 提取 API Key
|
||||
gemini_def = API_FORMAT_DEFINITIONS[APIFormat.GEMINI]
|
||||
api_key = _extract_api_key_from_request(request, gemini_def)
|
||||
api_format, api_key = _detect_api_format_and_key(request)
|
||||
|
||||
# 认证
|
||||
user, key_record = _authenticate(db, api_key)
|
||||
if not user:
|
||||
return _build_auth_error_response("gemini")
|
||||
return _build_auth_error_response(api_format)
|
||||
|
||||
# 构建访问限制
|
||||
restrictions = AccessRestrictions.from_api_key_and_user(key_record, user)
|
||||
|
||||
# 获取可用格式(包括可转换的格式)
|
||||
global_conversion_enabled = _is_format_conversion_enabled()
|
||||
candidate_formats = _get_convertible_formats("gemini", global_conversion_enabled)
|
||||
candidate_formats = _get_convertible_formats(api_format, global_conversion_enabled)
|
||||
candidate_formats, _ = _filter_formats_by_restrictions(
|
||||
candidate_formats, restrictions, "gemini"
|
||||
candidate_formats, restrictions, api_format
|
||||
)
|
||||
provider_to_formats = get_compatible_provider_formats(
|
||||
db, "gemini", candidate_formats, global_conversion_enabled
|
||||
db, api_format, candidate_formats, global_conversion_enabled
|
||||
)
|
||||
formats = _flatten_provider_formats(provider_to_formats)
|
||||
if not formats:
|
||||
return _build_404_response(model_id, "gemini")
|
||||
return _build_404_response(model_id, api_format)
|
||||
|
||||
available_provider_ids = get_available_provider_ids(db, formats, provider_to_formats)
|
||||
model_info = find_model_by_id(
|
||||
@@ -799,6 +789,6 @@ async def get_model_gemini(
|
||||
)
|
||||
|
||||
if not model_info:
|
||||
return _build_404_response(model_id, "gemini")
|
||||
return _build_404_response(model_id, api_format)
|
||||
|
||||
return _build_gemini_model_response(model_info)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""公开模块状态 API(供登录页等使用)"""
|
||||
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -8,17 +8,16 @@ OpenAI API 端点
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.api.base.pipeline import ApiRequestPipeline
|
||||
from src.api.handlers.openai import OpenAIChatAdapter
|
||||
from src.api.handlers.openai_cli import OpenAICliAdapter
|
||||
from src.core.api_format import APIFormat, get_api_format_definition
|
||||
from src.database import get_db
|
||||
|
||||
_openai_def = get_api_format_definition(APIFormat.OPENAI)
|
||||
router = APIRouter(tags=["OpenAI API"], prefix=_openai_def.path_prefix)
|
||||
router = APIRouter(tags=["OpenAI API"])
|
||||
pipeline = ApiRequestPipeline()
|
||||
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ async def test_connection(
|
||||
}
|
||||
|
||||
# 确定 API 格式
|
||||
format_value = api_format or "CLAUDE"
|
||||
format_value = api_format or "claude:chat"
|
||||
|
||||
# 创建 FallbackOrchestrator
|
||||
redis_client = get_redis_client_sync()
|
||||
@@ -279,7 +279,11 @@ async def test_connection(
|
||||
|
||||
request_builder = PassthroughRequestBuilder()
|
||||
provider_payload, provider_headers = request_builder.build(
|
||||
payload, {}, endpoint, key, is_stream=False,
|
||||
payload,
|
||||
{},
|
||||
endpoint,
|
||||
key,
|
||||
is_stream=False,
|
||||
pre_computed_auth=auth_info.as_tuple() if auth_info else None,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user