mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/ - 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层 - 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构 - 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations) - 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image - 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
This commit is contained in:
11
_deprecated_py_src/api/handlers/claude_cli/__init__.py
Normal file
11
_deprecated_py_src/api/handlers/claude_cli/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
"""
|
||||
Claude CLI 透传处理器
|
||||
"""
|
||||
|
||||
from src.api.handlers.claude_cli.adapter import ClaudeCliAdapter
|
||||
from src.api.handlers.claude_cli.handler import ClaudeCliMessageHandler
|
||||
|
||||
__all__ = [
|
||||
"ClaudeCliAdapter",
|
||||
"ClaudeCliMessageHandler",
|
||||
]
|
||||
105
_deprecated_py_src/api/handlers/claude_cli/adapter.py
Normal file
105
_deprecated_py_src/api/handlers/claude_cli/adapter.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Claude CLI Adapter - 基于通用 CLI Adapter 基类的简化实现
|
||||
|
||||
继承 CliAdapterBase,只需配置 FORMAT_ID 和 HANDLER_CLASS。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from src.api.handlers.base.cli_adapter_base import CliAdapterBase, register_cli_adapter
|
||||
from src.api.handlers.base.cli_handler_base import CliMessageHandlerBase
|
||||
from src.api.handlers.claude.adapter import ClaudeCapabilityDetector
|
||||
from src.config.settings import config
|
||||
from src.core.api_format import ApiFamily
|
||||
|
||||
|
||||
@register_cli_adapter
|
||||
class ClaudeCliAdapter(CliAdapterBase):
|
||||
"""
|
||||
Claude CLI API 适配器
|
||||
|
||||
处理 Claude CLI 格式的请求(/v1/messages 端点,使用 Bearer 认证)。
|
||||
"""
|
||||
|
||||
FORMAT_ID = "claude:cli"
|
||||
API_FAMILY = ApiFamily.CLAUDE
|
||||
name = "claude.cli"
|
||||
|
||||
@property
|
||||
def HANDLER_CLASS(self) -> type[CliMessageHandlerBase]:
|
||||
"""延迟导入 Handler 类避免循环依赖"""
|
||||
from src.api.handlers.claude_cli.handler import ClaudeCliMessageHandler
|
||||
|
||||
return ClaudeCliMessageHandler
|
||||
|
||||
def __init__(self, allowed_api_formats: list[str] | None = None):
|
||||
super().__init__(allowed_api_formats)
|
||||
|
||||
def detect_capability_requirements(
|
||||
self,
|
||||
headers: dict[str, str],
|
||||
request_body: dict[str, Any] | None = None,
|
||||
) -> dict[str, bool]:
|
||||
"""检测 Claude CLI 请求中隐含的能力需求"""
|
||||
return ClaudeCapabilityDetector.detect_from_headers(headers, request_body)
|
||||
|
||||
def _extract_message_count(self, payload: dict[str, Any]) -> int:
|
||||
"""Claude CLI 使用 messages 字段"""
|
||||
messages = payload.get("messages", [])
|
||||
return len(messages) if isinstance(messages, list) else 0
|
||||
|
||||
def _build_audit_metadata(
|
||||
self,
|
||||
payload: dict[str, Any],
|
||||
path_params: dict[str, Any] | None = None, # noqa: ARG002
|
||||
) -> dict[str, Any]:
|
||||
"""Claude CLI 特定的审计元数据"""
|
||||
model = payload.get("model", "unknown")
|
||||
stream = payload.get("stream", False)
|
||||
messages = payload.get("messages", [])
|
||||
|
||||
role_counts = {}
|
||||
for msg in messages:
|
||||
role = msg.get("role", "unknown")
|
||||
role_counts[role] = role_counts.get(role, 0) + 1
|
||||
|
||||
return {
|
||||
"action": "claude_cli_request",
|
||||
"model": model,
|
||||
"stream": bool(stream),
|
||||
"max_tokens": payload.get("max_tokens"),
|
||||
"messages_count": len(messages),
|
||||
"message_roles": role_counts,
|
||||
"temperature": payload.get("temperature"),
|
||||
"top_p": payload.get("top_p"),
|
||||
"tool_count": len(payload.get("tools") or []),
|
||||
"system_present": bool(payload.get("system")),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def build_endpoint_url(
|
||||
cls,
|
||||
base_url: str,
|
||||
request_data: dict[str, Any],
|
||||
model_name: str | None = None,
|
||||
*,
|
||||
provider_type: str | None = None,
|
||||
) -> str:
|
||||
"""构建Claude CLI API端点URL"""
|
||||
base_url = base_url.rstrip("/")
|
||||
if base_url.endswith("/v1"):
|
||||
return f"{base_url}/messages"
|
||||
else:
|
||||
return f"{base_url}/v1/messages"
|
||||
|
||||
# build_request_body 使用基类实现,通过 format_conversion_registry 自动转换 OPENAI -> CLAUDE_CLI
|
||||
|
||||
@classmethod
|
||||
def get_cli_user_agent(cls) -> str | None:
|
||||
"""获取Claude CLI User-Agent"""
|
||||
return config.internal_user_agent_claude_cli
|
||||
|
||||
|
||||
__all__ = ["ClaudeCliAdapter"]
|
||||
217
_deprecated_py_src/api/handlers/claude_cli/handler.py
Normal file
217
_deprecated_py_src/api/handlers/claude_cli/handler.py
Normal file
@@ -0,0 +1,217 @@
|
||||
"""
|
||||
Claude CLI Message Handler - 基于通用 CLI Handler 基类的简化实现
|
||||
|
||||
继承 CliMessageHandlerBase,只需覆盖格式特定的配置和事件处理逻辑。
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from src.api.handlers.base.cli_handler_base import (
|
||||
CliMessageHandlerBase,
|
||||
StreamContext,
|
||||
)
|
||||
from src.core.api_format import ApiFamily, EndpointKind
|
||||
from src.core.usage_tokens import extract_cache_creation_tokens_detail
|
||||
|
||||
|
||||
class ClaudeCliMessageHandler(CliMessageHandlerBase):
|
||||
"""
|
||||
Claude CLI Message Handler - 处理 Claude CLI API 格式
|
||||
|
||||
使用新三层架构 (Provider -> ProviderEndpoint -> ProviderAPIKey)
|
||||
通过 TaskService/FailoverEngine 实现自动故障转移、健康监控和并发控制
|
||||
|
||||
响应格式特点:
|
||||
- 使用 content[] 数组
|
||||
- 使用 text 类型
|
||||
- 流式事件:message_start, content_block_delta, message_delta, message_stop
|
||||
- 支持 cache_creation_input_tokens 和 cache_read_input_tokens
|
||||
|
||||
模型字段:请求体顶级 model 字段
|
||||
"""
|
||||
|
||||
FORMAT_ID = "claude:cli"
|
||||
API_FAMILY = ApiFamily.CLAUDE
|
||||
ENDPOINT_KIND = EndpointKind.CLI
|
||||
|
||||
def extract_model_from_request(
|
||||
self,
|
||||
request_body: dict[str, Any],
|
||||
path_params: dict[str, Any] | None = None, # noqa: ARG002
|
||||
) -> str:
|
||||
"""
|
||||
从请求中提取模型名 - Claude 格式实现
|
||||
|
||||
Claude API 的 model 在请求体顶级字段。
|
||||
|
||||
Args:
|
||||
request_body: 请求体
|
||||
path_params: URL 路径参数(Claude 不使用)
|
||||
|
||||
Returns:
|
||||
模型名
|
||||
"""
|
||||
model = request_body.get("model")
|
||||
return str(model) if model else "unknown"
|
||||
|
||||
def apply_mapped_model(
|
||||
self,
|
||||
request_body: dict[str, Any],
|
||||
mapped_model: str,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Claude API 的 model 在请求体顶级
|
||||
|
||||
Args:
|
||||
request_body: 原始请求体
|
||||
mapped_model: 映射后的模型名
|
||||
|
||||
Returns:
|
||||
更新了 model 字段的请求体
|
||||
"""
|
||||
result = dict(request_body)
|
||||
result["model"] = mapped_model
|
||||
return result
|
||||
|
||||
def _process_event_data(
|
||||
self,
|
||||
ctx: StreamContext,
|
||||
event_type: str,
|
||||
data: dict[str, Any],
|
||||
) -> None:
|
||||
"""
|
||||
处理 Claude CLI 格式的 SSE 事件
|
||||
|
||||
事件类型:
|
||||
- message_start: 消息开始,包含初始 usage(含缓存 tokens)
|
||||
- content_block_delta: 文本增量
|
||||
- message_delta: 消息增量,包含最终 usage
|
||||
- message_stop: 消息结束
|
||||
|
||||
跨格式转换时(如 provider=openai:cli),原始事件数据是 Provider 格式而非 Claude 格式。
|
||||
此时委托基类方法通过 Provider 格式解析器提取 usage。
|
||||
"""
|
||||
# 跨格式转换时:原始事件是 Provider 格式,
|
||||
# 基类 _process_event_data 会自动选择正确的 Provider 解析器提取 usage/text
|
||||
if ctx.provider_api_format and ctx.provider_api_format != ctx.client_api_format:
|
||||
super()._process_event_data(ctx, event_type, data)
|
||||
return
|
||||
|
||||
# 以下是同格式(claude:cli / claude:chat)的处理逻辑
|
||||
|
||||
# 处理 message_start 事件
|
||||
if event_type == "message_start":
|
||||
message = data.get("message", {})
|
||||
if message.get("id"):
|
||||
ctx.response_id = message["id"]
|
||||
|
||||
# 提取初始 usage(包含缓存 tokens)
|
||||
usage = message.get("usage", {})
|
||||
if usage:
|
||||
ctx.input_tokens = usage.get("input_tokens", 0)
|
||||
|
||||
cache_read = usage.get("cache_read_input_tokens", 0)
|
||||
if cache_read:
|
||||
ctx.cached_tokens = cache_read
|
||||
|
||||
total, t5m, t1h = extract_cache_creation_tokens_detail(usage)
|
||||
if total:
|
||||
ctx.cache_creation_tokens = total
|
||||
ctx.cache_creation_tokens_5m = t5m
|
||||
ctx.cache_creation_tokens_1h = t1h
|
||||
|
||||
# 处理文本增量
|
||||
elif event_type == "content_block_delta":
|
||||
delta = data.get("delta", {})
|
||||
if delta.get("type") == "text_delta":
|
||||
text = delta.get("text", "")
|
||||
if text:
|
||||
ctx.append_text(text)
|
||||
|
||||
# 处理消息增量(包含最终 usage)
|
||||
elif event_type == "message_delta":
|
||||
usage = data.get("usage", {})
|
||||
if usage:
|
||||
if "input_tokens" in usage:
|
||||
ctx.input_tokens = usage["input_tokens"]
|
||||
if "output_tokens" in usage:
|
||||
ctx.output_tokens = usage["output_tokens"]
|
||||
|
||||
# 更新缓存读取 tokens
|
||||
if "cache_read_input_tokens" in usage:
|
||||
ctx.cached_tokens = usage["cache_read_input_tokens"]
|
||||
|
||||
# 更新缓存创建 tokens
|
||||
total, t5m, t1h = extract_cache_creation_tokens_detail(usage)
|
||||
if total > 0:
|
||||
ctx.cache_creation_tokens = total
|
||||
ctx.cache_creation_tokens_5m = t5m
|
||||
ctx.cache_creation_tokens_1h = t1h
|
||||
|
||||
# 检查是否结束
|
||||
delta = data.get("delta", {})
|
||||
if delta.get("stop_reason"):
|
||||
ctx.has_completion = True
|
||||
ctx.final_response = data
|
||||
|
||||
# 处理消息结束
|
||||
elif event_type == "message_stop":
|
||||
ctx.has_completion = True
|
||||
|
||||
def _extract_response_metadata(
|
||||
self,
|
||||
response: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
从 Claude 响应中提取元数据
|
||||
|
||||
提取 model、stop_reason 等字段作为元数据。
|
||||
|
||||
Args:
|
||||
response: Claude API 响应
|
||||
|
||||
Returns:
|
||||
提取的元数据字典
|
||||
"""
|
||||
metadata: dict[str, Any] = {}
|
||||
|
||||
# 提取模型名称(实际使用的模型)
|
||||
if "model" in response:
|
||||
metadata["model"] = response["model"]
|
||||
|
||||
# 提取停止原因
|
||||
if "stop_reason" in response:
|
||||
metadata["stop_reason"] = response["stop_reason"]
|
||||
|
||||
# 提取消息 ID
|
||||
if "id" in response:
|
||||
metadata["message_id"] = response["id"]
|
||||
|
||||
# 提取消息类型
|
||||
if "type" in response:
|
||||
metadata["type"] = response["type"]
|
||||
|
||||
return metadata
|
||||
|
||||
def _finalize_stream_metadata(self, ctx: StreamContext) -> None:
|
||||
"""
|
||||
从流上下文中提取最终元数据
|
||||
|
||||
在流传输完成后调用,从收集的事件中提取元数据。
|
||||
|
||||
Args:
|
||||
ctx: 流上下文
|
||||
"""
|
||||
# 从 response_id 提取消息 ID
|
||||
if ctx.response_id:
|
||||
ctx.response_metadata["message_id"] = ctx.response_id
|
||||
|
||||
# 从 final_response 提取停止原因(message_delta 事件中的 delta.stop_reason)
|
||||
if ctx.final_response:
|
||||
delta = ctx.final_response.get("delta", {})
|
||||
if "stop_reason" in delta:
|
||||
ctx.response_metadata["stop_reason"] = delta["stop_reason"]
|
||||
|
||||
# 记录模型名称
|
||||
if ctx.model:
|
||||
ctx.response_metadata["model"] = ctx.model
|
||||
Reference in New Issue
Block a user