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:
fawney19
2026-04-03 16:26:16 +08:00
parent 8f26e1a31f
commit 1d9c77522a
868 changed files with 1735 additions and 2433 deletions

View File

@@ -0,0 +1,125 @@
"""
Usage 事件定义与序列化工具(用于 Redis Streams
"""
from __future__ import annotations
import json
import time
from dataclasses import dataclass
from enum import Enum
from typing import Any
import msgpack
from msgpack.exceptions import OutOfData
USAGE_EVENT_VERSION = 1
class UsageEventType(str, Enum):
STREAMING = "streaming"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
def now_ms() -> int:
return int(time.time() * 1000)
def _sanitize_value(value: Any) -> Any:
if value is None or isinstance(value, (str, int, float, bool)):
return value
if isinstance(value, dict):
return {str(k): _sanitize_value(v) for k, v in value.items()}
if isinstance(value, list):
return [_sanitize_value(item) for item in value]
return str(value)
def sanitize_payload(data: dict[str, Any]) -> dict[str, Any]:
return {str(k): _sanitize_value(v) for k, v in data.items()}
def _decode_payload(raw: Any) -> dict[str, Any]:
"""兼容解码:优先 msgpack回退旧 JSON。
统一将输入归一化为 bytes 后走单一解码路径msgpack → JSON fallback。
str 输入来自 decode_responses=True + surrogateescape 的 Redis 客户端,
通过 surrogateescape 可无损还原回原始 bytes。
"""
if isinstance(raw, str):
raw = raw.encode("utf-8", errors="surrogateescape")
if not isinstance(raw, (bytes, bytearray, memoryview)):
raise ValueError("Invalid payload field in usage event")
payload_bytes = bytes(raw)
# 新格式msgpack
try:
payload = msgpack.unpackb(payload_bytes, raw=False)
except (ValueError, OutOfData, TypeError):
# 兼容旧格式JSON bytes含 surrogateescape 还原后的纯 UTF-8 JSON
try:
payload = json.loads(payload_bytes.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError, TypeError) as exc:
raise ValueError("Invalid payload field in usage event") from exc
if not isinstance(payload, dict):
raise ValueError("Invalid payload field in usage event")
return payload
@dataclass
class UsageEvent:
event_type: UsageEventType
request_id: str
timestamp_ms: int
data: dict[str, Any]
def to_stream_fields(self) -> dict[str, bytes]:
"""序列化为 Redis Stream 字段。
该函数返回 bytes payload要求读写 usage queue 的 Redis 客户端使用
decode_responses=True并以 surrogateescape 做 UTF-8 编解码,
以保证 bytes <-> str 往返无损。
"""
payload = {
"v": USAGE_EVENT_VERSION,
"type": self.event_type.value,
"request_id": self.request_id,
"timestamp_ms": self.timestamp_ms,
# 兜底清洗,避免 metadata 中混入非 JSON 类型导致队列写入失败。
"data": sanitize_payload(self.data),
}
return {"payload": msgpack.packb(payload, use_bin_type=True)}
@classmethod
def from_stream_fields(cls, fields: dict[str, Any]) -> UsageEvent:
raw = fields.get("payload")
if not raw:
raise ValueError("Missing payload field in usage event")
payload = _decode_payload(raw)
event_type = UsageEventType(payload["type"])
return cls(
event_type=event_type,
request_id=payload["request_id"],
timestamp_ms=int(payload.get("timestamp_ms", 0)),
data=payload.get("data", {}) or {},
)
def build_usage_event(
*,
event_type: UsageEventType,
request_id: str,
data: dict[str, Any],
timestamp_ms: int | None = None,
) -> UsageEvent:
return UsageEvent(
event_type=event_type,
request_id=request_id,
timestamp_ms=timestamp_ms or now_ms(),
data=data,
)