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:
fawney19
2026-02-01 17:28:00 +08:00
parent c246ccfc91
commit 7b66505634
219 changed files with 4732 additions and 2545 deletions

View File

@@ -3,7 +3,6 @@ API Key认证插件
支持从header中提取API Key进行认证
"""
from __future__ import annotations
from fastapi import Request
@@ -16,7 +15,6 @@ from src.services.usage.service import UsageService
from .base import AuthContext, AuthPlugin
class ApiKeyAuthPlugin(AuthPlugin):
"""
API Key认证插件

View File

@@ -17,7 +17,6 @@ from src.services.auth.service import AuthService
from .base import AuthContext, AuthPlugin
class JwtAuthPlugin(AuthPlugin):
"""
JWT认证插件

View File

@@ -30,7 +30,6 @@ from src.core.logger import logger
from .base import LoadBalancerStrategy, ProviderCandidate, SelectionResult
class StickyPriorityStrategy(LoadBalancerStrategy):
"""
粘性优先级策略
@@ -154,14 +153,18 @@ class StickyPriorityStrategy(LoadBalancerStrategy):
)
# 粘性提供商不健康,选择备用提供商
logger.warning(f"Sticky provider {sticky_candidate.provider.name} is unhealthy, selecting backup")
logger.warning(
f"Sticky provider {sticky_candidate.provider.name} is unhealthy, selecting backup"
)
# 从同一优先级组中选择健康的备用提供商
backup_candidate = self._select_backup_provider(highest_group)
if not backup_candidate:
# 如果没有健康的备用,降级使用不健康的粘性提供商
logger.warning("No healthy backup provider available, falling back to unhealthy sticky provider")
logger.warning(
"No healthy backup provider available, falling back to unhealthy sticky provider"
)
backup_candidate = sticky_candidate
self._record_selection(backup_candidate.provider, is_sticky=False)

View File

@@ -25,7 +25,6 @@ from src.plugins.rate_limit.base import RateLimitStrategy
from src.plugins.token.base import TokenCounterPlugin
class PluginManager:
"""
统一的插件管理器
@@ -140,9 +139,11 @@ class PluginManager:
# 检查 API 版本兼容性
plugin_api_version = getattr(plugin_instance.metadata, "api_version", "1.0")
if not self._is_api_version_compatible(plugin_api_version):
logger.warning(f"Plugin {plugin_instance.name} has incompatible API version "
logger.warning(
f"Plugin {plugin_instance.name} has incompatible API version "
f"{plugin_api_version} (supported: {self.SUPPORTED_API_VERSION}), "
f"plugin will be disabled")
f"plugin will be disabled"
)
plugin_instance.enabled = False
self._incompatible_plugins.append(plugin_instance.name)
@@ -378,12 +379,16 @@ class PluginManager:
else:
# 初始化失败,禁用插件
plugin.enabled = False
logger.error(f"Failed to initialize plugin: {plugin.name}, plugin has been disabled")
logger.error(
f"Failed to initialize plugin: {plugin.name}, plugin has been disabled"
)
except Exception as e:
results[f"{plugin.name}"] = False
# 初始化异常,禁用插件
plugin.enabled = False
logger.error(f"Error initializing plugin {plugin.name}: {e}, plugin has been disabled")
logger.error(
f"Error initializing plugin {plugin.name}: {e}, plugin has been disabled"
)
return results
@@ -458,8 +463,10 @@ class PluginManager:
if len(result) != len(plugins):
remaining = [p for p in plugins if p not in result]
circular_names = [p.name for p in remaining]
logger.error(f"Circular dependency detected among plugins: {circular_names}. "
f"These plugins will be disabled.")
logger.error(
f"Circular dependency detected among plugins: {circular_names}. "
f"These plugins will be disabled."
)
# 禁用存在循环依赖的插件,而不是继续加载
for plugin in remaining:
plugin.enabled = False

View File

@@ -83,7 +83,9 @@ class MonitorPlugin(BasePlugin):
pass
@abstractmethod
async def increment(self, name: str, value: float = 1, labels: dict[str, str] | None = None) -> Any:
async def increment(
self, name: str, value: float = 1, labels: dict[str, str] | None = None
) -> Any:
"""
增加计数器

View File

@@ -17,10 +17,10 @@ except ImportError:
PROMETHEUS_AVAILABLE = False
Counter = Gauge = Histogram = Summary = REGISTRY = generate_latest = None
from .base import Metric, MetricType, MonitorPlugin
from src.core.logger import logger
from .base import Metric, MetricType, MonitorPlugin
class PrometheusPlugin(MonitorPlugin):
"""
@@ -138,7 +138,9 @@ class PrometheusPlugin(MonitorPlugin):
# 如果没有运行的事件循环,任务将在后续创建
logger.warning("No event loop available for Prometheus flush task")
def _get_or_create_metric(self, name: str, metric_type: MetricType, labels: list[str] | None = None) -> Any:
def _get_or_create_metric(
self, name: str, metric_type: MetricType, labels: list[str] | None = None
) -> Any:
"""获取或创建指标"""
if name not in self._metrics:
labels = labels or []
@@ -171,7 +173,9 @@ class PrometheusPlugin(MonitorPlugin):
if len(self._buffer) >= self.batch_size:
await self.flush()
async def increment(self, name: str, value: float = 1, labels: dict[str, str] | None = None) -> Any:
async def increment(
self, name: str, value: float = 1, labels: dict[str, str] | None = None
) -> Any:
"""增加计数器"""
try:
if name in self._metrics:

View File

@@ -44,18 +44,14 @@ class EmailNotificationPlugin(NotificationPlugin):
# 邮件配置
self.from_email = config.get("from_email") if config else None
self.from_name = (
config.get("from_name", "Aether") if config else "Aether"
)
self.from_name = config.get("from_name", "Aether") if config else "Aether"
self.to_emails = config.get("to_emails", []) if config else []
self.cc_emails = config.get("cc_emails", []) if config else []
self.bcc_emails = config.get("bcc_emails", []) if config else []
# 模板配置
self.use_html = config.get("use_html", True) if config else True
self.subject_prefix = (
config.get("subject_prefix", "[Aether]") if config else "[Aether]"
)
self.subject_prefix = config.get("subject_prefix", "[Aether]") if config else "[Aether]"
# 缓冲配置
self._buffer: list[Notification] = []

View File

@@ -10,7 +10,6 @@ import hashlib
import hmac
import json
import time
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:

View File

@@ -25,8 +25,8 @@ from datetime import datetime, timezone
from typing import Any
from src.core.logger import logger
from .base import RateLimitResult, RateLimitStrategy
from .base import RateLimitResult, RateLimitStrategy
class SlidingWindow:

View File

@@ -8,10 +8,10 @@ import time
from datetime import datetime, timedelta, timezone
from typing import Any
from ...clients.redis_client import get_redis_client_sync
from src.core.logger import logger
from .base import RateLimitResult, RateLimitStrategy
from ...clients.redis_client import get_redis_client_sync
from .base import RateLimitResult, RateLimitStrategy
class TokenBucket:

View File

@@ -4,6 +4,7 @@ Token计数插件基类
"""
from __future__ import annotations
from abc import abstractmethod
from dataclasses import dataclass
from typing import Any
@@ -71,9 +72,7 @@ class TokenCounterPlugin(BasePlugin):
pass
@abstractmethod
async def count_messages(
self, messages: list[dict[str, Any]], model: str | None = None
) -> int:
async def count_messages(self, messages: list[dict[str, Any]], model: str | None = None) -> int:
"""计算消息列表的Token数量"""
pass

View File

@@ -163,9 +163,7 @@ class ClaudeTokenCounterPlugin(TokenCounterPlugin):
model = model or self.default_model or "claude-3-5-sonnet-20241022"
return self._estimate_tokens_from_text(text, model)
async def count_messages(
self, messages: list[dict[str, Any]], model: str | None = None
) -> int:
async def count_messages(self, messages: list[dict[str, Any]], model: str | None = None) -> int:
"""计算消息列表的Token数量"""
if not self.enabled:
return 0

View File

@@ -138,9 +138,7 @@ class TiktokenCounterPlugin(TokenCounterPlugin):
# 简单估算: 平均每个字符0.75个token
return int(len(text) * 0.75)
async def count_messages(
self, messages: list[dict[str, Any]], model: str | None = None
) -> int:
async def count_messages(self, messages: list[dict[str, Any]], model: str | None = None) -> int:
"""计算消息列表的Token数量"""
if not self.enabled:
return 0