mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
- 删除全部 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)
275 lines
8.8 KiB
Python
275 lines
8.8 KiB
Python
"""
|
|
Execution runtime 计划契约
|
|
|
|
用于在 Python 控制面和 Rust execution runtime 之间传递稳定的请求执行信息。
|
|
当前阶段先服务于非流式 chat 路径的计划构建与本地执行拆分。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import json
|
|
from dataclasses import asdict, dataclass
|
|
from typing import Any
|
|
|
|
|
|
def _drop_none(value: Any) -> Any:
|
|
"""递归移除 None 字段,便于序列化为紧凑 payload。"""
|
|
if isinstance(value, dict):
|
|
return {key: _drop_none(item) for key, item in value.items() if item is not None}
|
|
if isinstance(value, list):
|
|
return [_drop_none(item) for item in value]
|
|
return value
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class ExecutionPlanTimeouts:
|
|
connect_ms: int | None = None
|
|
read_ms: int | None = None
|
|
write_ms: int | None = None
|
|
pool_ms: int | None = None
|
|
total_ms: int | None = None
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class ExecutionPlanBody:
|
|
json_body: Any = None
|
|
body_bytes_b64: str | None = None
|
|
body_ref: str | None = None
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class ExecutionProxySnapshot:
|
|
enabled: bool
|
|
mode: str | None = None
|
|
node_id: str | None = None
|
|
label: str | None = None
|
|
url: str | None = None
|
|
extra: dict[str, Any] | None = None
|
|
|
|
@classmethod
|
|
def from_proxy_info(
|
|
cls,
|
|
proxy_info: dict[str, Any] | None,
|
|
*,
|
|
proxy_url: str | None = None,
|
|
mode_override: str | None = None,
|
|
node_id_override: str | None = None,
|
|
extra: dict[str, Any] | None = None,
|
|
) -> ExecutionProxySnapshot | None:
|
|
if not proxy_info and not proxy_url:
|
|
return None
|
|
mode = (
|
|
mode_override
|
|
or str(proxy_info.get("type") or proxy_info.get("mode") or "").strip()
|
|
or None
|
|
)
|
|
if not mode and proxy_url:
|
|
mode = proxy_url.split("://", 1)[0].strip().lower() or None
|
|
return cls(
|
|
enabled=True,
|
|
mode=mode,
|
|
node_id=node_id_override
|
|
or str((proxy_info or {}).get("node_id") or "").strip()
|
|
or None,
|
|
label=str((proxy_info or {}).get("label") or "").strip() or None,
|
|
url=str(proxy_url or (proxy_info or {}).get("url") or "").strip() or None,
|
|
extra=extra or None,
|
|
)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class ExecutionPlan:
|
|
request_id: str
|
|
candidate_id: str | None
|
|
provider_name: str
|
|
provider_id: str
|
|
endpoint_id: str
|
|
key_id: str
|
|
method: str
|
|
url: str
|
|
headers: dict[str, str]
|
|
body: ExecutionPlanBody
|
|
stream: bool
|
|
provider_api_format: str
|
|
client_api_format: str
|
|
model_name: str
|
|
content_type: str | None = None
|
|
content_encoding: str | None = None
|
|
proxy: ExecutionProxySnapshot | None = None
|
|
tls_profile: str | None = None
|
|
timeouts: ExecutionPlanTimeouts | None = None
|
|
|
|
def to_payload(self) -> dict[str, Any]:
|
|
return _drop_none(asdict(self))
|
|
|
|
|
|
_REMOTE_EXECUTION_RUNTIME_BYPASS_FORMATS = {"openai:cli", "openai:compact"}
|
|
|
|
|
|
def should_bypass_remote_execution_runtime_url(
|
|
url: str | None,
|
|
*,
|
|
provider_api_format: str | None = None,
|
|
client_api_format: str | None = None,
|
|
) -> bool:
|
|
normalized_url = str(url or "").strip().lower()
|
|
if not normalized_url:
|
|
return False
|
|
|
|
normalized_provider_api_format = str(provider_api_format or "").strip().lower()
|
|
normalized_client_api_format = str(client_api_format or "").strip().lower()
|
|
if (
|
|
normalized_provider_api_format not in _REMOTE_EXECUTION_RUNTIME_BYPASS_FORMATS
|
|
and normalized_client_api_format not in _REMOTE_EXECUTION_RUNTIME_BYPASS_FORMATS
|
|
):
|
|
return False
|
|
|
|
return "/backend-api/codex" in normalized_url or "/backendapi/codex" in normalized_url
|
|
|
|
|
|
def should_bypass_remote_execution_runtime(contract: ExecutionPlan) -> bool:
|
|
return should_bypass_remote_execution_runtime_url(
|
|
contract.url,
|
|
provider_api_format=contract.provider_api_format,
|
|
client_api_format=contract.client_api_format,
|
|
)
|
|
|
|
|
|
def is_remote_execution_runtime_proxy_supported(proxy: ExecutionProxySnapshot | None) -> bool:
|
|
proxy_mode = str((proxy.mode if proxy else "") or "").strip().lower()
|
|
return (
|
|
proxy is None
|
|
or (str(proxy.url or "").strip() != "" and proxy_mode not in {"tunnel"})
|
|
or (proxy_mode == "tunnel" and str(proxy.node_id or "").strip() != "")
|
|
)
|
|
|
|
|
|
def is_remote_execution_runtime_contract_eligible(contract: ExecutionPlan) -> bool:
|
|
content_encoding = str(contract.content_encoding or "").strip().lower()
|
|
has_json_body = contract.body.json_body is not None
|
|
has_raw_body = bool(str(contract.body.body_bytes_b64 or "").strip())
|
|
has_body = has_json_body or has_raw_body
|
|
return (
|
|
((not has_body and content_encoding == "") or has_body)
|
|
and (not has_json_body or content_encoding in {"", "gzip"} or has_raw_body)
|
|
and is_remote_execution_runtime_proxy_supported(contract.proxy)
|
|
and not should_bypass_remote_execution_runtime(contract)
|
|
)
|
|
|
|
|
|
def build_execution_plan_body(
|
|
payload: Any,
|
|
*,
|
|
content_type: str | None = None,
|
|
) -> ExecutionPlanBody:
|
|
normalized_content_type = str(content_type or "").strip().lower()
|
|
|
|
if isinstance(payload, dict):
|
|
return ExecutionPlanBody(json_body=payload)
|
|
|
|
if isinstance(payload, list) and "json" in normalized_content_type:
|
|
return ExecutionPlanBody(json_body=payload)
|
|
|
|
if isinstance(payload, (bytes, bytearray, memoryview)):
|
|
return ExecutionPlanBody(body_bytes_b64=base64.b64encode(bytes(payload)).decode("ascii"))
|
|
|
|
if isinstance(payload, str):
|
|
return ExecutionPlanBody(
|
|
body_bytes_b64=base64.b64encode(payload.encode("utf-8")).decode("ascii")
|
|
)
|
|
|
|
if payload is None:
|
|
return ExecutionPlanBody()
|
|
|
|
return ExecutionPlanBody(
|
|
body_bytes_b64=base64.b64encode(
|
|
json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
|
).decode("ascii")
|
|
)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class PreparedExecutionPlan:
|
|
"""本地执行所需的运行时上下文;`contract` 是可序列化的稳定边界。"""
|
|
|
|
contract: ExecutionPlan
|
|
payload: dict[str, Any]
|
|
headers: dict[str, str]
|
|
upstream_is_stream: bool
|
|
needs_conversion: bool
|
|
provider_type: str
|
|
request_timeout: float
|
|
delegate_config: dict[str, Any] | None = None
|
|
proxy_config: dict[str, Any] | None = None
|
|
envelope: Any = None
|
|
selected_base_url: str | None = None
|
|
client_content_encoding: str | None = None
|
|
proxy_info: dict[str, Any] | None = None
|
|
|
|
@property
|
|
def remote_eligible(self) -> bool:
|
|
return is_remote_execution_runtime_contract_eligible(self.contract)
|
|
|
|
|
|
async def build_proxy_snapshot(
|
|
proxy_config: dict[str, Any] | None,
|
|
*,
|
|
label: str = "adapter",
|
|
) -> ExecutionProxySnapshot | None:
|
|
if not proxy_config:
|
|
return None
|
|
|
|
try:
|
|
from src.services.proxy_node.resolver import (
|
|
build_proxy_url_async,
|
|
resolve_delegate_config_async,
|
|
resolve_proxy_info_async,
|
|
)
|
|
|
|
delegate_cfg = await resolve_delegate_config_async(proxy_config)
|
|
proxy_url: str | None = None
|
|
if proxy_config and not (delegate_cfg and delegate_cfg.get("tunnel")):
|
|
proxy_url = await build_proxy_url_async(proxy_config)
|
|
proxy_info = await resolve_proxy_info_async(proxy_config)
|
|
return ExecutionProxySnapshot.from_proxy_info(
|
|
proxy_info,
|
|
proxy_url=proxy_url,
|
|
mode_override="tunnel" if delegate_cfg and delegate_cfg.get("tunnel") else None,
|
|
node_id_override=(
|
|
str(delegate_cfg.get("node_id") or "").strip() or None
|
|
if delegate_cfg and delegate_cfg.get("tunnel")
|
|
else None
|
|
),
|
|
)
|
|
except Exception as exc:
|
|
from src.core.logger import logger
|
|
|
|
logger.warning("Build {} proxy snapshot failed: {}", label, exc)
|
|
return None
|
|
|
|
|
|
# Compatibility aliases for older call sites still using executor terminology.
|
|
should_bypass_remote_executor_url = should_bypass_remote_execution_runtime_url
|
|
should_bypass_remote_executor = should_bypass_remote_execution_runtime
|
|
is_remote_proxy_supported = is_remote_execution_runtime_proxy_supported
|
|
is_remote_contract_eligible = is_remote_execution_runtime_contract_eligible
|
|
|
|
__all__ = [
|
|
"ExecutionPlan",
|
|
"ExecutionPlanBody",
|
|
"ExecutionPlanTimeouts",
|
|
"ExecutionProxySnapshot",
|
|
"PreparedExecutionPlan",
|
|
"build_execution_plan_body",
|
|
"build_proxy_snapshot",
|
|
"is_remote_contract_eligible",
|
|
"is_remote_execution_runtime_contract_eligible",
|
|
"is_remote_execution_runtime_proxy_supported",
|
|
"is_remote_proxy_supported",
|
|
"should_bypass_remote_execution_runtime",
|
|
"should_bypass_remote_execution_runtime_url",
|
|
"should_bypass_remote_executor",
|
|
"should_bypass_remote_executor_url",
|
|
]
|