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,945 @@
from __future__ import annotations
import base64
import time
import uuid
from dataclasses import asdict
from datetime import datetime, timezone
from typing import Any
from fastapi import BackgroundTasks, HTTPException, Request
from fastapi.responses import JSONResponse, Response
from sqlalchemy.orm import Session
from src.api.base.context import ApiRequestContext
from src.api.base.pipeline import get_pipeline
from src.core.api_format.headers import extract_client_api_key_for_endpoint_with_query
from src.core.api_format.metadata import get_auth_config_for_endpoint
from src.core.crypto import crypto_service
from src.core.http_compression import normalize_content_encoding
from src.core.logger import logger
from src.database import create_session, get_db
from src.models.database import ApiKey, RequestCandidate, User
from src.services.auth.service import AuthService
from src.utils.async_utils import safe_create_task
from .common import ensure_loopback
from .gateway_contract import (
_GEMINI_FILES_DOWNLOAD_ROUTE_RE,
_GEMINI_FILES_RESOURCE_ROUTE_RE,
_GEMINI_MODEL_OPERATION_CANCEL_RE,
_GEMINI_OPERATION_CANCEL_RE,
_GEMINI_SYNC_ROUTE_RE,
_GEMINI_VIDEO_CREATE_ROUTE_RE,
_GEMINI_VIDEO_MODEL_OPERATION_ANY_RE,
_OPENAI_VIDEO_CANCEL_ROUTE_RE,
_OPENAI_VIDEO_CONTENT_ROUTE_RE,
_OPENAI_VIDEO_REMIX_ROUTE_RE,
_OPENAI_VIDEO_TASK_ROUTE_RE,
CONTROL_ACTION_HEADER,
CONTROL_ACTION_PROXY_PUBLIC,
CONTROL_EXECUTED_HEADER,
GatewayAuthContext,
GatewayExecuteRequest,
GatewayExecutionDecisionResponse,
GatewayExecutionPlanResponse,
GatewayResolveRequest,
GatewayRouteDecision,
GatewayStreamReportRequest,
GatewaySyncReportRequest,
classify_gateway_route,
)
class _GatewayProxy:
def __getattr__(self, name: str) -> Any:
from . import gateway as gateway_module
return getattr(gateway_module, name)
gateway_module = _GatewayProxy()
async def _build_chat_sync_decision(
*,
request: Request,
payload: GatewayExecuteRequest,
db: Session,
auth_context: GatewayAuthContext,
decision: GatewayRouteDecision,
expected_api_format: str,
decision_kind: str,
report_kind: str,
finalize_kind: str,
) -> GatewayExecutionDecisionResponse | None:
from src.api.handlers.base.chat_adapter_base import ChatAdapterBase
from src.api.handlers.base.chat_sync_executor import ChatSyncExecutor
from src.services.task.request_state import MutableRequestBodyState
if str(payload.method or "").strip().upper() != "POST":
return None
adapter, path_params = gateway_module._resolve_gateway_sync_adapter(decision, payload.path)
if not isinstance(adapter, ChatAdapterBase):
return None
if gateway_module._is_stream_request_payload(payload.body_json, path_params):
return None
user, api_key = gateway_module._load_gateway_auth_models(db, auth_context)
pipeline = gateway_module.get_pipeline()
await pipeline._check_user_rate_limit(request, db, user, api_key)
context = gateway_module._build_gateway_request_context(
request=request,
payload=payload,
db=db,
user=user,
api_key=api_key,
adapter=adapter,
path_params=path_params,
balance_remaining=auth_context.balance_remaining,
)
authorize_result = adapter.authorize(context)
if hasattr(authorize_result, "__await__"):
await authorize_result
original_request_body = await context.ensure_json_body_async()
if context.path_params:
original_request_body = adapter._merge_path_params(
original_request_body,
context.path_params,
)
request_obj = adapter._validate_request_body(original_request_body, context.path_params)
if isinstance(request_obj, JSONResponse):
return None
handler = adapter._create_handler(
db=db,
user=user,
api_key=api_key,
request_id=context.request_id,
client_ip=context.client_ip,
user_agent=context.user_agent,
start_time=context.start_time,
api_family=adapter.API_FAMILY.value if adapter.API_FAMILY else None,
endpoint_kind=adapter.ENDPOINT_KIND.value if adapter.ENDPOINT_KIND else None,
)
await handler._convert_request(request_obj)
model = handler.extract_model_from_request(
original_request_body,
context.path_params,
)
api_format = handler.allowed_api_formats[0]
capability_requirements = handler._resolve_capability_requirements(
model_name=model,
request_headers=context.original_headers,
request_body=original_request_body,
)
preferred_key_ids = await handler._resolve_preferred_key_ids(
model_name=model,
request_body=original_request_body,
)
candidate = await gateway_module._select_gateway_direct_candidate(
db=db,
redis_client=getattr(handler, "redis", None),
api_format=str(api_format),
model_name=str(model or "unknown"),
user_api_key=api_key,
request_id=context.request_id,
is_stream=False,
capability_requirements=capability_requirements or None,
preferred_key_ids=preferred_key_ids or None,
request_body=original_request_body,
)
if candidate is None:
return None
sync_executor = ChatSyncExecutor(handler)
prepared_plan = await sync_executor._build_sync_execution_plan(
candidate.provider,
candidate.endpoint,
candidate.key,
candidate,
model=str(model or "unknown"),
api_format=api_format,
original_headers=context.original_headers,
request_state=MutableRequestBodyState(original_request_body),
query_params=context.query_params,
client_content_encoding=context.client_content_encoding,
)
contract = prepared_plan.contract
contract_provider_api_format = str(contract.provider_api_format or "").strip().lower()
contract_client_api_format = str(contract.client_api_format or "").strip().lower()
if not prepared_plan.remote_eligible or contract_client_api_format != expected_api_format:
return None
provider_request_headers = {
str(header_name).strip().lower(): str(header_value).strip()
for header_name, header_value in dict(prepared_plan.headers or {}).items()
if str(header_name).strip() and str(header_value).strip()
}
provider_request_body = dict(prepared_plan.payload or {})
auth_header, auth_value = gateway_module._extract_gateway_upstream_auth(
provider_request_headers,
provider_api_format=contract_provider_api_format,
key=candidate.key,
)
prompt_cache_key = str(provider_request_body.get("prompt_cache_key") or "").strip() or None
mapped_model = getattr(sync_executor._ctx, "mapped_model_result", None)
has_envelope = prepared_plan.envelope is not None
needs_conversion = bool(prepared_plan.needs_conversion)
selected_report_kind = (
finalize_kind
if (
prepared_plan.upstream_is_stream
or needs_conversion
or has_envelope
or contract_provider_api_format != expected_api_format
)
else report_kind
)
decision_extra_headers: dict[str, str] = {}
decision_provider_request_headers = provider_request_headers or None
decision_provider_request_body = provider_request_body
report_context = {
"user_id": str(user.id),
"api_key_id": str(api_key.id),
"request_id": str(context.request_id),
"model": str(model or "unknown"),
"provider_name": str(contract.provider_name or "unknown"),
"provider_id": str(contract.provider_id or ""),
"endpoint_id": str(contract.endpoint_id or ""),
"key_id": str(contract.key_id or ""),
"candidate_id": str(contract.candidate_id or "") or None,
"provider_api_format": str(contract.provider_api_format or ""),
"client_api_format": str(contract.client_api_format or ""),
"mapped_model": str(mapped_model or "").strip() or None,
"original_headers": dict(context.original_headers),
"original_request_body": original_request_body,
"proxy_info": prepared_plan.proxy_info,
"has_envelope": has_envelope,
"envelope_name": gateway_module._gateway_report_context_envelope_name(
prepared_plan.envelope
),
"needs_conversion": needs_conversion,
}
report_context["provider_request_headers"] = provider_request_headers
report_context["provider_request_body"] = provider_request_body
return GatewayExecutionDecisionResponse(
action="executor_sync_decision",
decision_kind=decision_kind,
request_id=str(contract.request_id or context.request_id or ""),
candidate_id=str(contract.candidate_id or "").strip() or None,
provider_name=str(contract.provider_name or candidate.provider.name or ""),
provider_id=str(contract.provider_id or candidate.provider.id or ""),
endpoint_id=str(contract.endpoint_id or candidate.endpoint.id or ""),
key_id=str(contract.key_id or candidate.key.id or ""),
upstream_base_url=(
str(
prepared_plan.selected_base_url or getattr(candidate.endpoint, "base_url", "") or ""
).strip()
),
upstream_url=str(contract.url or "").strip() or None,
auth_header=auth_header,
auth_value=str(auth_value or "").strip(),
provider_api_format=contract_provider_api_format,
client_api_format=contract_client_api_format,
model_name=str(contract.model_name or model or "unknown"),
mapped_model=str(mapped_model or "").strip() or None,
prompt_cache_key=prompt_cache_key,
extra_headers=decision_extra_headers,
provider_request_headers=decision_provider_request_headers,
provider_request_body=decision_provider_request_body,
content_type=(
str(contract.content_type or provider_request_headers.get("content-type") or "").strip()
or "application/json"
),
proxy=gateway_module._serialize_gateway_sync_proxy(contract.proxy),
tls_profile=str(contract.tls_profile or "").strip() or None,
timeouts=gateway_module._serialize_gateway_sync_timeouts(contract.timeouts),
upstream_is_stream=prepared_plan.upstream_is_stream or None,
report_kind=selected_report_kind,
report_context=report_context,
auth_context=auth_context,
)
async def _build_openai_chat_sync_decision(
*,
request: Request,
payload: GatewayExecuteRequest,
db: Session,
auth_context: GatewayAuthContext,
decision: GatewayRouteDecision,
) -> GatewayExecutionDecisionResponse | None:
from src.api.handlers.base.chat_adapter_base import ChatAdapterBase
from src.config.settings import config
from src.services.provider.auth import get_provider_auth
from src.services.provider.behavior import get_provider_behavior
from src.services.provider.prompt_cache import maybe_patch_request_with_prompt_cache_key
from src.services.provider.stream_policy import (
get_upstream_stream_policy,
resolve_upstream_is_stream,
)
from src.services.provider.upstream_headers import build_upstream_extra_headers
from src.services.proxy_node.resolver import (
build_proxy_url_async,
get_system_proxy_config_async,
resolve_delegate_config_async,
resolve_effective_proxy,
resolve_proxy_info_async,
)
from src.services.request.execution_runtime_plan import (
ExecutionPlanTimeouts,
ExecutionProxySnapshot,
)
if str(payload.method or "").strip().upper() != "POST":
return None
adapter, path_params = gateway_module._resolve_gateway_sync_adapter(decision, payload.path)
if not isinstance(adapter, ChatAdapterBase):
return None
if gateway_module._is_stream_request_payload(payload.body_json, path_params):
return None
if not isinstance(payload.body_json, dict):
return None
user, api_key = gateway_module._load_gateway_auth_models(db, auth_context)
pipeline = gateway_module.get_pipeline()
await pipeline._check_user_rate_limit(request, db, user, api_key)
context = gateway_module._build_gateway_request_context(
request=request,
payload=payload,
db=db,
user=user,
api_key=api_key,
adapter=adapter,
path_params=path_params,
balance_remaining=auth_context.balance_remaining,
)
authorize_result = adapter.authorize(context)
if hasattr(authorize_result, "__await__"):
await authorize_result
original_request_body = await context.ensure_json_body_async()
if context.path_params:
original_request_body = adapter._merge_path_params(
original_request_body,
context.path_params,
)
request_obj = adapter._validate_request_body(original_request_body, context.path_params)
if isinstance(request_obj, JSONResponse):
return None
handler = adapter._create_handler(
db=db,
user=user,
api_key=api_key,
request_id=context.request_id,
client_ip=context.client_ip,
user_agent=context.user_agent,
start_time=context.start_time,
api_family=adapter.API_FAMILY.value if adapter.API_FAMILY else None,
endpoint_kind=adapter.ENDPOINT_KIND.value if adapter.ENDPOINT_KIND else None,
)
model = handler.extract_model_from_request(
original_request_body,
context.path_params,
)
client_api_format = str(handler.allowed_api_formats[0] or "").strip().lower()
if client_api_format != "openai:chat":
return None
capability_requirements = handler._resolve_capability_requirements(
model_name=model,
request_headers=context.original_headers,
request_body=original_request_body,
)
preferred_key_ids = await handler._resolve_preferred_key_ids(
model_name=model,
request_body=original_request_body,
)
candidate = await gateway_module._select_gateway_direct_candidate(
db=db,
redis_client=getattr(handler, "redis", None),
api_format=client_api_format,
model_name=str(model or "unknown"),
user_api_key=api_key,
request_id=context.request_id,
is_stream=False,
capability_requirements=capability_requirements or None,
preferred_key_ids=preferred_key_ids or None,
request_body=original_request_body,
)
if candidate is None:
return None
provider = candidate.provider
endpoint = candidate.endpoint
key = candidate.key
provider_api_format = str(endpoint.api_format or client_api_format or "").strip().lower()
if provider_api_format != "openai:chat":
return None
if getattr(endpoint, "custom_path", None):
return None
if getattr(endpoint, "header_rules", None) or getattr(endpoint, "body_rules", None):
return None
provider_type = str(getattr(provider, "provider_type", "") or "").strip().lower()
behavior = get_provider_behavior(
provider_type=provider_type,
endpoint_sig=provider_api_format,
)
if (
behavior.envelope is not None
or behavior.same_format_variant is not None
or behavior.cross_format_variant is not None
):
return None
upstream_policy = get_upstream_stream_policy(
endpoint,
provider_type=provider_type,
endpoint_sig=provider_api_format,
)
upstream_is_stream = resolve_upstream_is_stream(
client_is_stream=False,
policy=upstream_policy,
)
mapped_model = candidate.mapping_matched_model if candidate else None
if not mapped_model:
mapped_model = await handler._get_mapped_model(
source_model=model,
provider_id=str(provider.id),
api_format=provider_api_format,
)
provider_request_body = dict(original_request_body)
if mapped_model:
provider_request_body = handler.apply_mapped_model(provider_request_body, mapped_model)
if upstream_is_stream:
provider_request_body["stream"] = True
provider_request_body = maybe_patch_request_with_prompt_cache_key(
provider_request_body,
provider_api_format=provider_api_format,
provider_type=provider_type,
base_url=getattr(endpoint, "base_url", None),
user_api_key_id=str(getattr(api_key, "id", "") or ""),
request_headers=context.original_headers,
)
prompt_cache_key = str(provider_request_body.get("prompt_cache_key") or "").strip() or None
auth_info = await get_provider_auth(endpoint, key)
if auth_info is not None:
auth_header, auth_value = auth_info.as_tuple()
decrypted_auth_config = auth_info.decrypted_auth_config
else:
auth_header, auth_type = get_auth_config_for_endpoint(provider_api_format)
decrypted_key = crypto_service.decrypt(key.api_key)
auth_value = f"Bearer {decrypted_key}" if auth_type == "bearer" else decrypted_key
decrypted_auth_config = None
extra_headers = build_upstream_extra_headers(
provider_type=provider_type,
endpoint_sig=provider_api_format,
request_body=provider_request_body,
original_headers=context.original_headers,
decrypted_auth_config=decrypted_auth_config,
)
effective_proxy = resolve_effective_proxy(provider.proxy, getattr(key, "proxy", None))
proxy_info = await resolve_proxy_info_async(effective_proxy)
delegate_cfg = await resolve_delegate_config_async(effective_proxy)
is_tunnel_delegate = bool(delegate_cfg and delegate_cfg.get("tunnel"))
effective_proxy_for_contract = effective_proxy
if not effective_proxy_for_contract or not effective_proxy_for_contract.get("enabled", True):
effective_proxy_for_contract = await get_system_proxy_config_async()
proxy_url: str | None = None
if effective_proxy_for_contract and not is_tunnel_delegate:
proxy_url = await build_proxy_url_async(effective_proxy_for_contract)
proxy_snapshot = ExecutionProxySnapshot.from_proxy_info(
proxy_info,
proxy_url=proxy_url,
mode_override="tunnel" if is_tunnel_delegate else None,
node_id_override=(
str(delegate_cfg.get("node_id") or "").strip() or None if is_tunnel_delegate else None
),
)
request_timeout = provider.request_timeout or config.http_request_timeout
timeouts = ExecutionPlanTimeouts(
connect_ms=int(config.http_connect_timeout * 1000),
read_ms=int(config.http_read_timeout * 1000),
write_ms=int(config.http_write_timeout * 1000),
pool_ms=int(config.http_pool_timeout * 1000),
total_ms=int(request_timeout * 1000),
)
return GatewayExecutionDecisionResponse(
action="executor_sync_decision",
decision_kind="openai_chat_sync",
request_id=str(context.request_id),
candidate_id=str(
getattr(candidate, "request_candidate_id", "") or getattr(candidate, "id", "") or ""
)
or None,
provider_name=str(provider.name),
provider_id=str(provider.id),
endpoint_id=str(endpoint.id),
key_id=str(key.id),
upstream_base_url=str(getattr(endpoint, "base_url", "") or "").strip(),
auth_header=str(auth_header or "").strip() or "authorization",
auth_value=str(auth_value or "").strip(),
provider_api_format=provider_api_format,
client_api_format=client_api_format,
model_name=str(model or "unknown"),
mapped_model=str(mapped_model or "").strip() or None,
prompt_cache_key=prompt_cache_key,
extra_headers={str(k): str(v) for k, v in (extra_headers or {}).items()},
content_type="application/json",
proxy=gateway_module._serialize_gateway_sync_proxy(proxy_snapshot),
timeouts=gateway_module._serialize_gateway_sync_timeouts(timeouts),
upstream_is_stream=upstream_is_stream or None,
report_kind=(
"openai_chat_sync_finalize" if upstream_is_stream else "openai_chat_sync_success"
),
report_context={
"user_id": str(user.id),
"api_key_id": str(api_key.id),
"request_id": str(context.request_id),
"candidate_id": str(
getattr(candidate, "request_candidate_id", "") or getattr(candidate, "id", "") or ""
)
or None,
"model": str(model or "unknown"),
"provider_name": str(provider.name or "unknown"),
"provider_id": str(provider.id),
"endpoint_id": str(endpoint.id),
"key_id": str(key.id),
"provider_api_format": provider_api_format,
"client_api_format": client_api_format,
"mapped_model": str(mapped_model or "").strip() or None,
"original_headers": dict(context.original_headers),
"original_request_body": original_request_body,
"proxy_info": proxy_info,
"has_envelope": False,
"needs_conversion": False,
},
auth_context=auth_context,
)
async def _build_chat_stream_decision(
*,
request: Request,
payload: GatewayExecuteRequest,
db: Session,
auth_context: GatewayAuthContext,
decision: GatewayRouteDecision,
expected_api_format: str,
decision_kind: str,
report_kind: str,
) -> GatewayExecutionDecisionResponse | None:
from src.api.handlers.base.chat_adapter_base import ChatAdapterBase
from src.config.settings import config
from src.core.api_format.headers import set_accept_if_absent
from src.services.provider.auth import get_provider_auth
from src.services.provider.transport import build_provider_url
from src.services.proxy_node.resolver import (
build_proxy_url_async,
get_system_proxy_config_async,
resolve_delegate_config_async,
resolve_effective_proxy,
resolve_proxy_info_async,
)
from src.services.request.execution_runtime_plan import (
ExecutionPlan,
ExecutionPlanTimeouts,
ExecutionProxySnapshot,
build_execution_plan_body,
is_remote_execution_runtime_contract_eligible,
)
if str(payload.method or "").strip().upper() != "POST":
return None
adapter, path_params = gateway_module._resolve_gateway_sync_adapter(decision, payload.path)
if not isinstance(adapter, ChatAdapterBase):
return None
if not gateway_module._is_stream_request_payload(payload.body_json, path_params):
return None
user, api_key = gateway_module._load_gateway_auth_models(db, auth_context)
pipeline = gateway_module.get_pipeline()
await pipeline._check_user_rate_limit(request, db, user, api_key)
context = gateway_module._build_gateway_request_context(
request=request,
payload=payload,
db=db,
user=user,
api_key=api_key,
adapter=adapter,
path_params=path_params,
balance_remaining=auth_context.balance_remaining,
)
authorize_result = adapter.authorize(context)
if hasattr(authorize_result, "__await__"):
await authorize_result
original_request_body = await context.ensure_json_body_async()
if context.path_params:
original_request_body = adapter._merge_path_params(
original_request_body,
context.path_params,
)
request_obj = adapter._validate_request_body(original_request_body, context.path_params)
if isinstance(request_obj, JSONResponse):
return None
handler = adapter._create_handler(
db=db,
user=user,
api_key=api_key,
request_id=context.request_id,
client_ip=context.client_ip,
user_agent=context.user_agent,
start_time=context.start_time,
api_family=adapter.API_FAMILY.value if adapter.API_FAMILY else None,
endpoint_kind=adapter.ENDPOINT_KIND.value if adapter.ENDPOINT_KIND else None,
)
converted_request = await handler._convert_request(request_obj)
model = getattr(converted_request, "model", original_request_body.get("model", "unknown"))
api_format = str(handler.allowed_api_formats[0] or "").strip().lower()
if api_format != expected_api_format:
return None
capability_requirements = handler._resolve_capability_requirements(
model_name=model,
request_headers=context.original_headers,
request_body=original_request_body,
)
preferred_key_ids = await handler._resolve_preferred_key_ids(
model_name=model,
request_body=original_request_body,
)
candidate = await gateway_module._select_gateway_direct_candidate(
db=db,
redis_client=getattr(handler, "redis", None),
api_format=api_format,
model_name=str(model or "unknown"),
user_api_key=api_key,
request_id=context.request_id,
is_stream=True,
capability_requirements=capability_requirements or None,
preferred_key_ids=preferred_key_ids or None,
request_body=original_request_body,
)
if candidate is None:
return None
provider = candidate.provider
endpoint = candidate.endpoint
key = candidate.key
provider_api_format = str(endpoint.api_format or api_format or "").strip().lower()
prep = await handler._prepare_provider_request(
model=str(model or "unknown"),
provider=provider,
endpoint=endpoint,
key=key,
working_request_body=dict(original_request_body),
original_headers=context.original_headers,
client_api_format=api_format,
provider_api_format=provider_api_format,
candidate=candidate,
client_is_stream=True,
)
provider_api_format = str(prep.provider_api_format or "").strip().lower()
client_api_format = str(prep.client_api_format or "").strip().lower()
if (
not prep.upstream_is_stream
or gateway_module._stream_executor_requires_python_rewrite(
envelope=prep.envelope,
needs_conversion=bool(prep.needs_conversion),
provider_api_format=provider_api_format,
client_api_format=client_api_format,
)
or client_api_format != expected_api_format
):
return None
if provider_api_format != expected_api_format and not (
expected_api_format == "openai:chat"
and provider_api_format in {"claude:chat", "gemini:chat"}
):
return None
auth_info = prep.auth_info or await get_provider_auth(endpoint, key)
provider_payload, provider_headers = handler._request_builder.build(
prep.request_body,
context.original_headers,
endpoint,
key,
is_stream=prep.upstream_is_stream,
extra_headers=prep.extra_headers if prep.extra_headers else None,
pre_computed_auth=auth_info.as_tuple() if auth_info else None,
envelope=prep.envelope,
provider_api_format=prep.provider_api_format,
)
if not isinstance(provider_payload, dict):
return None
if prep.upstream_is_stream:
set_accept_if_absent(provider_headers)
provider_request_headers = {
str(k).lower(): str(v)
for k, v in dict(provider_headers or {}).items()
if str(k).strip() and str(v).strip()
}
provider_request_body = dict(provider_payload)
auth_header, auth_value = gateway_module._extract_gateway_upstream_auth(
provider_request_headers,
provider_api_format=provider_api_format,
key=key,
)
upstream_url = build_provider_url(
endpoint,
query_params=context.query_params,
path_params={"model": prep.url_model},
is_stream=prep.upstream_is_stream,
key=key,
decrypted_auth_config=auth_info.decrypted_auth_config if auth_info else None,
)
effective_proxy = resolve_effective_proxy(provider.proxy, getattr(key, "proxy", None))
proxy_info = await resolve_proxy_info_async(effective_proxy)
delegate_cfg = await resolve_delegate_config_async(effective_proxy)
effective_proxy_for_contract = effective_proxy
if not effective_proxy_for_contract or not effective_proxy_for_contract.get("enabled", True):
effective_proxy_for_contract = await get_system_proxy_config_async()
is_tunnel_delegate = bool(delegate_cfg and delegate_cfg.get("tunnel"))
proxy_url: str | None = None
if effective_proxy_for_contract and not is_tunnel_delegate:
proxy_url = await build_proxy_url_async(effective_proxy_for_contract)
proxy_snapshot = ExecutionProxySnapshot.from_proxy_info(
proxy_info,
proxy_url=proxy_url,
mode_override="tunnel" if is_tunnel_delegate else None,
node_id_override=(
str(delegate_cfg.get("node_id") or "").strip() or None if is_tunnel_delegate else None
),
)
timeouts = ExecutionPlanTimeouts(
connect_ms=int(config.http_connect_timeout * 1000),
read_ms=int(config.http_read_timeout * 1000),
write_ms=int(config.http_write_timeout * 1000),
pool_ms=int(config.http_pool_timeout * 1000),
total_ms=None,
)
contract = ExecutionPlan(
request_id=str(context.request_id or ""),
candidate_id=str(
getattr(candidate, "request_candidate_id", "") or getattr(candidate, "id", "") or ""
)
or None,
provider_name=str(provider.name),
provider_id=str(provider.id),
endpoint_id=str(endpoint.id),
key_id=str(key.id),
method="POST",
url=str(upstream_url),
headers=dict(provider_request_headers),
body=build_execution_plan_body(
provider_request_body,
content_type=str(provider_request_headers.get("content-type") or "").strip() or None,
),
stream=True,
provider_api_format=provider_api_format,
client_api_format=str(prep.client_api_format or api_format),
model_name=str(model or ""),
content_type=str(provider_request_headers.get("content-type") or "").strip() or None,
content_encoding=context.client_content_encoding,
proxy=proxy_snapshot,
tls_profile=prep.tls_profile,
timeouts=timeouts,
)
if not is_remote_execution_runtime_contract_eligible(contract):
return None
prompt_cache_key = str(provider_request_body.get("prompt_cache_key") or "").strip() or None
has_envelope = prep.envelope is not None
needs_conversion = bool(prep.needs_conversion)
decision_extra_headers: dict[str, str] = {}
decision_provider_request_headers = provider_request_headers or None
decision_provider_request_body = provider_request_body
report_context = {
"user_id": str(user.id),
"api_key_id": str(api_key.id),
"request_id": str(context.request_id),
"candidate_id": str(contract.candidate_id or "") or None,
"model": str(model or "unknown"),
"provider_name": str(contract.provider_name or provider.name or "unknown"),
"provider_id": str(contract.provider_id or provider.id or ""),
"endpoint_id": str(contract.endpoint_id or endpoint.id or ""),
"key_id": str(contract.key_id or key.id or ""),
"provider_api_format": provider_api_format,
"client_api_format": str(prep.client_api_format or api_format),
"mapped_model": str(prep.mapped_model or "").strip() or None,
"original_headers": dict(context.original_headers),
"original_request_body": original_request_body,
"proxy_info": proxy_info,
"has_envelope": has_envelope,
"envelope_name": gateway_module._gateway_report_context_envelope_name(prep.envelope),
"needs_conversion": needs_conversion,
}
report_context["provider_request_headers"] = provider_request_headers
report_context["provider_request_body"] = provider_request_body
return GatewayExecutionDecisionResponse(
action="executor_stream_decision",
decision_kind=decision_kind,
request_id=str(context.request_id),
candidate_id=str(contract.candidate_id or "") or None,
provider_name=str(contract.provider_name or provider.name or ""),
provider_id=str(contract.provider_id or provider.id or ""),
endpoint_id=str(contract.endpoint_id or endpoint.id or ""),
key_id=str(contract.key_id or key.id or ""),
upstream_base_url=str(getattr(endpoint, "base_url", "") or "").strip(),
upstream_url=str(upstream_url),
auth_header=auth_header,
auth_value=str(auth_value or "").strip(),
provider_api_format=provider_api_format,
client_api_format=str(prep.client_api_format or api_format),
model_name=str(model or "unknown"),
mapped_model=str(prep.mapped_model or "").strip() or None,
prompt_cache_key=prompt_cache_key,
extra_headers=decision_extra_headers,
provider_request_headers=decision_provider_request_headers,
provider_request_body=decision_provider_request_body,
content_type=(
str(provider_request_headers.get("content-type") or "").strip() or "application/json"
),
proxy=gateway_module._serialize_gateway_sync_proxy(proxy_snapshot),
tls_profile=str(prep.tls_profile or "").strip() or None,
timeouts=gateway_module._serialize_gateway_sync_timeouts(timeouts),
report_kind=report_kind,
report_context=report_context,
auth_context=auth_context,
)
async def _build_openai_chat_stream_decision(
*,
request: Request,
payload: GatewayExecuteRequest,
db: Session,
auth_context: GatewayAuthContext,
decision: GatewayRouteDecision,
) -> GatewayExecutionDecisionResponse | None:
return await _build_chat_stream_decision(
request=request,
payload=payload,
db=db,
auth_context=auth_context,
decision=decision,
expected_api_format="openai:chat",
decision_kind="openai_chat_stream",
report_kind="openai_chat_stream_success",
)
async def _build_claude_chat_stream_decision(
*,
request: Request,
payload: GatewayExecuteRequest,
db: Session,
auth_context: GatewayAuthContext,
decision: GatewayRouteDecision,
) -> GatewayExecutionDecisionResponse | None:
return await _build_chat_stream_decision(
request=request,
payload=payload,
db=db,
auth_context=auth_context,
decision=decision,
expected_api_format="claude:chat",
decision_kind="claude_chat_stream",
report_kind="claude_chat_stream_success",
)
async def _build_gemini_chat_stream_decision(
*,
request: Request,
payload: GatewayExecuteRequest,
db: Session,
auth_context: GatewayAuthContext,
decision: GatewayRouteDecision,
) -> GatewayExecutionDecisionResponse | None:
return await _build_chat_stream_decision(
request=request,
payload=payload,
db=db,
auth_context=auth_context,
decision=decision,
expected_api_format="gemini:chat",
decision_kind="gemini_chat_stream",
report_kind="gemini_chat_stream_success",
)
async def _build_claude_chat_sync_decision(
*,
request: Request,
payload: GatewayExecuteRequest,
db: Session,
auth_context: GatewayAuthContext,
decision: GatewayRouteDecision,
) -> GatewayExecutionDecisionResponse | None:
return await _build_chat_sync_decision(
request=request,
payload=payload,
db=db,
auth_context=auth_context,
decision=decision,
expected_api_format="claude:chat",
decision_kind="claude_chat_sync",
report_kind="claude_chat_sync_success",
finalize_kind="claude_chat_sync_finalize",
)
async def _build_gemini_chat_sync_decision(
*,
request: Request,
payload: GatewayExecuteRequest,
db: Session,
auth_context: GatewayAuthContext,
decision: GatewayRouteDecision,
) -> GatewayExecutionDecisionResponse | None:
return await _build_chat_sync_decision(
request=request,
payload=payload,
db=db,
auth_context=auth_context,
decision=decision,
expected_api_format="gemini:chat",
decision_kind="gemini_chat_sync",
report_kind="gemini_chat_sync_success",
finalize_kind="gemini_chat_sync_finalize",
)