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,175 @@
"""Helpers for synthesizing stable prompt cache keys."""
from __future__ import annotations
import re
import uuid
from collections.abc import Mapping
from typing import Any
from src.core.provider_types import ProviderType, normalize_provider_type
from src.utils.url_utils import is_official_openai_api_url
_OFFICIAL_OPENAI_PROMPT_CACHE_FORMATS: frozenset[str] = frozenset({"openai:chat", "openai:cli"})
_PROMPT_CACHE_NAMESPACE_VERSION = "v3"
_USER_AGENT_CLIENT_FAMILY_PATTERNS: tuple[tuple[tuple[str, ...], str], ...] = (
(("codex desktop",), "codex_desktop"),
(("asyncopenai/python", "openai/python"), "openai_python"),
(("openai-node", "openai/javascript"), "openai_node"),
(("openai-go",), "openai_go"),
(("openai-java",), "openai_java"),
(("openai-ruby",), "openai_ruby"),
(("postmanruntime/",), "postman"),
(("curl/",), "curl"),
(("mozilla/",), "browser"),
)
def _get_header_value(headers: Mapping[str, Any] | None, header_name: str) -> str | None:
if not isinstance(headers, Mapping):
return None
target = str(header_name or "").strip().lower()
if not target:
return None
for name, value in headers.items():
if str(name or "").strip().lower() != target:
continue
normalized = str(value or "").strip()
return normalized or None
return None
def normalize_prompt_cache_client_family(user_agent: str | None) -> str:
"""Reduce a raw User-Agent string to a stable client-family token."""
raw = str(user_agent or "").strip().lower()
if not raw:
return "generic"
for patterns, family in _USER_AGENT_CLIENT_FAMILY_PATTERNS:
if any(pattern in raw for pattern in patterns):
return family
normalized = re.sub(r"[^a-z0-9]+", "_", raw.split()[0].split("/")[0]).strip("_")
return normalized[:48] if normalized else "generic"
def resolve_prompt_cache_client_family(request_headers: Mapping[str, Any] | None) -> str:
"""Best-effort client-family extraction from request headers."""
return normalize_prompt_cache_client_family(_get_header_value(request_headers, "user-agent"))
def _build_stable_prompt_cache_key(
user_api_key_id: str | None,
*,
scope: str,
client_family: str | None = None,
) -> str | None:
normalized = str(user_api_key_id or "").strip()
if not normalized:
return None
# Keep the optional argument for call-site compatibility, but ignore it.
# Prompt cache reuse is more valuable than splitting namespaces by User-Agent.
_ = client_family
namespace = f"aether:{scope}:prompt-cache:{_PROMPT_CACHE_NAMESPACE_VERSION}:user:{normalized}"
return str(uuid.uuid5(uuid.NAMESPACE_OID, namespace))
def build_stable_openai_prompt_cache_key(
user_api_key_id: str | None,
*,
client_family: str | None = None,
) -> str | None:
"""Build a deterministic official OpenAI prompt cache key from the caller's user API key id."""
return _build_stable_prompt_cache_key(
user_api_key_id,
scope="openai",
client_family=client_family,
)
def build_stable_codex_prompt_cache_key(
user_api_key_id: str | None,
*,
client_family: str | None = None,
) -> str | None:
"""Build a deterministic Codex prompt cache key from the caller's user API key id."""
return _build_stable_prompt_cache_key(
user_api_key_id,
scope="codex",
client_family=client_family,
)
def resolve_prompt_cache_key_scope(
*,
provider_api_format: str | None,
provider_type: str | None = None,
base_url: str | None = None,
) -> str | None:
"""Resolve which prompt cache strategy should be used for this request."""
fmt = str(provider_api_format or "").strip().lower()
pt = normalize_provider_type(provider_type)
if pt == ProviderType.CODEX.value and fmt in {"openai:cli", "openai:compact"}:
return "codex"
if fmt == "openai:compact":
return None
if fmt in _OFFICIAL_OPENAI_PROMPT_CACHE_FORMATS and is_official_openai_api_url(base_url):
return "openai"
return None
def maybe_patch_request_with_prompt_cache_key(
request_body: Any,
*,
provider_api_format: str | None,
provider_type: str | None = None,
base_url: str | None = None,
user_api_key_id: str | None = None,
request_headers: Mapping[str, Any] | None = None,
) -> Any:
"""Inject a stable prompt_cache_key when the target upstream supports deterministic reuse."""
if not isinstance(request_body, dict):
return request_body
scope = resolve_prompt_cache_key_scope(
provider_api_format=provider_api_format,
provider_type=provider_type,
base_url=base_url,
)
if not scope:
return request_body
prompt_cache_key = str(request_body.get("prompt_cache_key") or "").strip()
if prompt_cache_key:
return request_body
if scope == "codex":
stable_key = build_stable_codex_prompt_cache_key(
user_api_key_id,
)
else:
stable_key = build_stable_openai_prompt_cache_key(
user_api_key_id,
)
if not stable_key:
return request_body
out = dict(request_body)
out["prompt_cache_key"] = stable_key
return out
__all__ = [
"build_stable_codex_prompt_cache_key",
"build_stable_openai_prompt_cache_key",
"maybe_patch_request_with_prompt_cache_key",
"normalize_prompt_cache_client_family",
"resolve_prompt_cache_client_family",
"resolve_prompt_cache_key_scope",
]