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:
@@ -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认证插件
|
||||
|
||||
@@ -17,7 +17,6 @@ from src.services.auth.service import AuthService
|
||||
from .base import AuthContext, AuthPlugin
|
||||
|
||||
|
||||
|
||||
class JwtAuthPlugin(AuthPlugin):
|
||||
"""
|
||||
JWT认证插件
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
"""
|
||||
增加计数器
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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] = []
|
||||
|
||||
@@ -10,7 +10,6 @@ import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import time
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user