mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 09:50:21 +08:00
refactor: 统一任务框架 Phase 3 - 用 TaskService/FailoverEngine 替代 FallbackOrchestrator
核心重构:
- 移除 FallbackOrchestrator,用 TaskService + FailoverEngine 替代
- TaskService 作为统一入口,支持 SYNC/ASYNC 两种任务模式
- FailoverEngine 实现候选遍历、重试、故障转移逻辑
- 新增 AttemptFunc/AttemptResult 协议,统一尝试结果表示
功能改进:
- 流式响应首字节探测(30s 超时,空流触发故障转移)
- 流式取消归因优化(区分客户端断连 vs 服务端中断)
- 新增 OpenAI Sora 视频取消路由 POST /v1/videos/{task_id}/cancel
- OpenAI 流式请求自动添加 stream_options.include_usage
代码规范:
- 修复 loguru 日志格式(%s → {})
- 新增 FORMAT_CONVERSION_ENABLED 环境变量说明
测试覆盖:
- test_failover_engine.py: FailoverEngine 单元测试
- test_task_service_async_execute.py: TaskService ASYNC 模式测试
- test_video_cancel_e2e.py: 视频取消端到端测试
This commit is contained in:
@@ -7,7 +7,7 @@ import pytest
|
||||
from src.config.settings import config
|
||||
from src.core.api_format.conversion.internal_video import VideoStatus
|
||||
from src.services.billing.formula_engine import BillingIncompleteError
|
||||
from src.services.task.application import TaskApplicationService
|
||||
from src.services.task.service import TaskService
|
||||
|
||||
|
||||
def _make_task(**overrides: Any) -> SimpleNamespace:
|
||||
@@ -81,18 +81,18 @@ async def test_video_finalize_failed_records_cost_zero(monkeypatch: pytest.Monke
|
||||
lambda _self, **_kwargs: {"duration_seconds": 4},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"src.services.task.application.BillingRuleService.find_rule",
|
||||
"src.services.billing.rule_service.BillingRuleService.find_rule",
|
||||
lambda *_args, **_kwargs: None,
|
||||
)
|
||||
# Mock update_settled_billing (used by finalize_video_task)
|
||||
update_settled = MagicMock(return_value=True)
|
||||
monkeypatch.setattr(
|
||||
"src.services.task.application.UsageService.update_settled_billing",
|
||||
"src.services.usage.service.UsageService.update_settled_billing",
|
||||
update_settled,
|
||||
)
|
||||
|
||||
app = TaskApplicationService(db)
|
||||
await app.finalize_video_task(task)
|
||||
svc = TaskService(db)
|
||||
await svc.finalize_video_task(task)
|
||||
|
||||
# billing_snapshot should be written back to task.request_metadata
|
||||
assert task.request_metadata["billing_snapshot"]["cost"] == 0.0
|
||||
@@ -113,18 +113,18 @@ async def test_video_finalize_completed_no_rule(monkeypatch: pytest.MonkeyPatch)
|
||||
lambda _self, **_kwargs: {"duration_seconds": 4},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"src.services.task.application.BillingRuleService.find_rule",
|
||||
"src.services.billing.rule_service.BillingRuleService.find_rule",
|
||||
lambda *_args, **_kwargs: None,
|
||||
)
|
||||
# Mock update_settled_billing (used by finalize_video_task)
|
||||
update_settled = MagicMock(return_value=True)
|
||||
monkeypatch.setattr(
|
||||
"src.services.task.application.UsageService.update_settled_billing",
|
||||
"src.services.usage.service.UsageService.update_settled_billing",
|
||||
update_settled,
|
||||
)
|
||||
|
||||
app = TaskApplicationService(db)
|
||||
await app.finalize_video_task(task)
|
||||
svc = TaskService(db)
|
||||
await svc.finalize_video_task(task)
|
||||
|
||||
assert task.request_metadata["billing_snapshot"]["status"] == "no_rule"
|
||||
|
||||
@@ -161,7 +161,7 @@ async def test_video_finalize_strict_mode_missing_required_marks_failed(
|
||||
# Mock update_settled_billing (used by finalize_video_task)
|
||||
update_settled = MagicMock(return_value=True)
|
||||
monkeypatch.setattr(
|
||||
"src.services.task.application.UsageService.update_settled_billing",
|
||||
"src.services.usage.service.UsageService.update_settled_billing",
|
||||
update_settled,
|
||||
)
|
||||
|
||||
@@ -169,15 +169,15 @@ async def test_video_finalize_strict_mode_missing_required_marks_failed(
|
||||
try:
|
||||
config.billing_strict_mode = True
|
||||
monkeypatch.setattr(
|
||||
"src.services.task.application.FormulaEngine.evaluate",
|
||||
"src.services.billing.formula_engine.FormulaEngine.evaluate",
|
||||
MagicMock(
|
||||
side_effect=BillingIncompleteError(
|
||||
"Missing required dimensions", missing_required=["duration_seconds"]
|
||||
)
|
||||
),
|
||||
)
|
||||
app = TaskApplicationService(db)
|
||||
await app.finalize_video_task(task)
|
||||
svc = TaskService(db)
|
||||
await svc.finalize_video_task(task)
|
||||
finally:
|
||||
config.billing_strict_mode = old
|
||||
|
||||
|
||||
Reference in New Issue
Block a user