Files
Aether/_deprecated_py_src/services/task/submit/execute.py
fawney19 1d9c77522a 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)
2026-04-03 16:26:16 +08:00

105 lines
3.5 KiB
Python

from __future__ import annotations
from collections.abc import Callable
from typing import Any
import httpx
from sqlalchemy.orm import Session
from src.config.settings import config
from src.services.candidate.submit import AllCandidatesFailedError, SubmitOutcome
from src.services.task.submit.attempt import AsyncSubmitAttemptService
from src.services.task.submit.filter import AsyncSubmitFilterService
class AsyncSubmitExecutionService:
"""异步提交候选执行编排服务。"""
def __init__(
self,
db: Session,
*,
sanitize: Callable[[str], str],
extract_response_text: Callable[[httpx.Response], str],
match_provider_failover_rule: Callable[..., str | None],
) -> None:
self.db = db
self._filter_ops = AsyncSubmitFilterService(db)
self._attempt_ops = AsyncSubmitAttemptService(
db,
sanitize=sanitize,
extract_response_text=extract_response_text,
match_provider_failover_rule=match_provider_failover_rule,
)
async def execute_submit_loop(
self,
*,
candidates: list[Any],
record_map: dict[tuple[int, int], str],
task_type: str,
model_name: str,
submit_func: Any,
extract_external_task_id: Any,
supported_auth_types: set[str] | None,
allow_format_conversion: bool,
) -> SubmitOutcome:
candidate_keys: list[dict[str, Any]] = []
eligible_count = 0
last_status_code: int | None = None
for idx, cand in enumerate(candidates):
candidate_info = self._filter_ops.build_candidate_info(idx=idx, candidate=cand)
candidate_keys.append(candidate_info)
attempt_plan = self._filter_ops.prepare_candidate_for_attempt(
idx=idx,
candidate=cand,
record_map=record_map,
candidate_info=candidate_info,
task_type=task_type,
model_name=model_name,
supported_auth_types=supported_auth_types,
allow_format_conversion=allow_format_conversion,
)
if attempt_plan is None:
continue
eligible_count += 1
outcome, status_code = await self._attempt_ops.submit_candidate(
candidate=cand,
record_id=attempt_plan.record_id,
candidate_info=candidate_info,
candidate_keys=candidate_keys,
rule_lookup=attempt_plan.rule_lookup,
submit_func=submit_func,
extract_external_task_id=extract_external_task_id,
)
if status_code is not None:
last_status_code = status_code
if outcome is not None:
return outcome
# Persist candidate records before raising.
try:
self.db.commit()
except Exception:
self.db.rollback()
if eligible_count == 0:
reason = "no_eligible_candidates"
if config.billing_require_rule:
reason = "no_candidate_with_billing_rule"
raise AllCandidatesFailedError(
reason=reason,
candidate_keys=candidate_keys,
last_status_code=last_status_code,
)
raise AllCandidatesFailedError(
reason="all_candidates_failed",
candidate_keys=candidate_keys,
last_status_code=last_status_code,
)