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,124 @@
"""
WebSocket \u96a7\u9053\u4e8c\u8fdb\u5236\u5e27\u534f\u8bae
\u5e27\u683c\u5f0f:
| stream_id (4B) | msg_type (1B) | flags (1B) | payload_len (4B) | payload (NB) |
\u7528\u4e8e Aether \u4e0e aether-proxy \u4e4b\u95f4\u7684 WebSocket \u96a7\u9053\u591a\u8def\u590d\u7528\u901a\u4fe1\u3002
"""
import struct
from enum import IntEnum
from typing import Self
_U64_MAX = (1 << 64) - 1
HEADER_SIZE = 10 # 4 + 1 + 1 + 4 bytes
class MsgType(IntEnum):
"""\u6d88\u606f\u7c7b\u578b"""
REQUEST_HEADERS = 0x01 # Aether -> Proxy: \u8bf7\u6c42\u5143\u6570\u636e (JSON)
REQUEST_BODY = 0x02 # Aether -> Proxy: \u8bf7\u6c42\u4f53
RESPONSE_HEADERS = 0x03 # Proxy -> Aether: \u54cd\u5e94\u72b6\u6001\u7801 + headers (JSON)
RESPONSE_BODY = 0x04 # Proxy -> Aether: \u54cd\u5e94\u4f53\uff08\u6d41\u5f0f\u5206\u5757\uff09
STREAM_END = 0x05 # \u53cc\u5411: \u6d41\u7ed3\u675f
STREAM_ERROR = 0x06 # \u53cc\u5411: \u6d41\u9519\u8bef
PING = 0x10 # \u53cc\u5411: \u5fc3\u8df3 (stream_id=0)
PONG = 0x11 # \u53cc\u5411: \u5fc3\u8df3\u54cd\u5e94 (stream_id=0)
GOAWAY = 0x12 # \u53cc\u5411: \u4f18\u96c5\u5173\u95ed (stream_id=0)
HEARTBEAT_DATA = 0x13 # Proxy -> Aether: \u6307\u6807\u4e0a\u62a5
HEARTBEAT_ACK = 0x14 # Aether -> Proxy: \u5fc3\u8df3\u786e\u8ba4 + \u8fdc\u7a0b\u914d\u7f6e
NODE_STATUS = 0x15 # Tunnel relay -> Worker: \u8282\u70b9\u8fde\u63a5\u72b6\u6001\u5e7f\u64ad
class FrameFlags:
"""\u5e27\u6807\u5fd7\u4f4d"""
END_STREAM = 0x01
GZIP_COMPRESSED = 0x02
class Frame:
"""WebSocket \u96a7\u9053\u5e27"""
__slots__ = ("stream_id", "msg_type", "flags", "payload")
def __init__(
self,
stream_id: int,
msg_type: MsgType,
flags: int = 0,
payload: bytes = b"",
) -> None:
self.stream_id = stream_id
self.msg_type = msg_type
self.flags = flags
self.payload = payload
def encode(self) -> bytes:
header = struct.pack(
"!IBBI",
self.stream_id,
self.msg_type,
self.flags,
len(self.payload),
)
return header + self.payload
@classmethod
def decode(cls, data: bytes) -> Self:
if len(data) < HEADER_SIZE:
raise ValueError(
f"\u5e27\u6570\u636e\u592a\u77ed: \u9700\u8981 {HEADER_SIZE} \u5b57\u8282, \u5b9e\u9645 {len(data)}"
)
stream_id, msg_type_raw, flags, payload_len = struct.unpack("!IBBI", data[:HEADER_SIZE])
expected_total = HEADER_SIZE + payload_len
if len(data) < expected_total:
raise ValueError(
f"\u5e27\u6570\u636e\u4e0d\u5b8c\u6574: \u9700\u8981 {expected_total} \u5b57\u8282, \u5b9e\u9645 {len(data)}"
)
try:
msg_type = MsgType(msg_type_raw)
except ValueError:
raise ValueError(f"\u672a\u77e5\u6d88\u606f\u7c7b\u578b: 0x{msg_type_raw:02x}")
payload = data[HEADER_SIZE:expected_total]
return cls(stream_id, msg_type, flags, payload)
@property
def is_end_stream(self) -> bool:
return bool(self.flags & FrameFlags.END_STREAM)
@property
def is_gzip(self) -> bool:
return bool(self.flags & FrameFlags.GZIP_COMPRESSED)
def __repr__(self) -> str:
return (
f"Frame(stream={self.stream_id}, type={self.msg_type.name}, "
f"flags=0x{self.flags:02x}, payload_len={len(self.payload)})"
)
def normalize_heartbeat_id(value: object) -> int | None:
"""Normalize heartbeat_id to u64-compatible int for Rust ACK parsing."""
if isinstance(value, bool) or value is None:
return None
parsed: int | None = None
if isinstance(value, int):
parsed = value
elif isinstance(value, float):
if value.is_integer():
parsed = int(value)
elif isinstance(value, str):
stripped = value.strip()
if stripped.isdigit():
try:
parsed = int(stripped)
except ValueError:
parsed = None
if parsed is None or parsed < 0 or parsed > _U64_MAX:
return None
return parsed