From bfdb15773f768dd0c249fac140991b745dbce290 Mon Sep 17 00:00:00 2001 From: extreme Date: Tue, 14 Apr 2026 17:10:10 +0800 Subject: [PATCH] =?UTF-8?q?[refactor]=20=E9=87=8D=E6=9E=84=E7=94=9F?= =?UTF-8?q?=E6=88=90=E8=83=BD=E5=8A=9B=E7=9B=B8=E5=85=B3=E7=9A=84=E5=A5=91?= =?UTF-8?q?=E7=BA=A6=EF=BC=8C=E7=BB=9F=E4=B8=80=E8=BE=93=E5=85=A5/?= =?UTF-8?q?=E8=BE=93=E5=87=BA=20DTO=20=E5=92=8C=E4=BE=9B=E5=BA=94=E5=95=86?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=EF=BC=8C=E7=AE=80=E5=8C=96=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E4=B8=8E=E9=9B=86=E6=88=90=E6=A8=A1=E5=9D=97=E7=9A=84=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E5=85=B3=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/jellyfish-code-and-api.mdc | 3 +++ AGENTS.md | 3 +++ backend/app/core/contracts/__init__.py | 23 +++++++++++++++++++ .../image_generation.py} | 4 ++-- .../provider.py} | 4 ++-- .../video_generation.py} | 4 ++-- .../app/core/integrations/openai/images.py | 4 ++-- backend/app/core/integrations/openai/video.py | 4 ++-- .../core/integrations/openai/video_payload.py | 2 +- .../core/integrations/volcengine/images.py | 4 ++-- .../app/core/integrations/volcengine/video.py | 4 ++-- .../integrations/volcengine/video_payload.py | 2 +- backend/app/core/tasks/__init__.py | 6 ++--- .../app/core/tasks/image_generation_tasks.py | 4 ++-- .../app/core/tasks/video_generation_tasks.py | 4 ++-- backend/app/services/film/generated_video.py | 9 +++----- .../app/services/studio/image_task_runner.py | 4 +++- backend/app/services/studio/image_tasks.py | 2 +- .../core/integrations/test_image_adapters.py | 4 ++-- .../core/integrations/test_video_adapters.py | 4 ++-- .../docs/architecture/project-structure.md | 6 +++++ 21 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 backend/app/core/contracts/__init__.py rename backend/app/core/{tasks/image_generation_types.py => contracts/image_generation.py} (95%) rename backend/app/core/{tasks/provider_types.py => contracts/provider.py} (71%) rename backend/app/core/{tasks/video_generation_types.py => contracts/video_generation.py} (94%) diff --git a/.cursor/rules/jellyfish-code-and-api.mdc b/.cursor/rules/jellyfish-code-and-api.mdc index 418516e..298606e 100644 --- a/.cursor/rules/jellyfish-code-and-api.mdc +++ b/.cursor/rules/jellyfish-code-and-api.mdc @@ -17,6 +17,9 @@ alwaysApply: true - 后端严格区分层职责: - `api` 层负责收参、鉴权、响应组织。 - `service` 层负责业务逻辑、状态流转、数据编排。 +- 生成能力相关的通用契约(输入/输出 DTO、供应商配置等)统一放在 `app/core/contracts`: + - `tasks` 仅保留任务封装、分派与执行编排,不定义跨层 DTO。 + - `integrations` 仅依赖 `contracts`,不依赖 `tasks` 类型模块。 ## 变更同步要求 diff --git a/AGENTS.md b/AGENTS.md index 37165f1..5574efe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,6 +24,9 @@ - 当前真实实现、当前边界、当前状态流转,放 `architecture`。 - 正在推进或待推进的改造方案、任务拆解、阶段计划,放 `plans`。 - 操作说明、开发流程、接入方式,放 `guide`。 +9. 生成能力相关的通用契约(输入/输出 DTO、供应商配置等)统一放在 `app/core/contracts`: + - `tasks` 仅保留任务封装、分派与执行编排,不定义跨层 DTO。 + - `integrations` 仅依赖 `contracts`,不依赖 `tasks` 类型模块。 ## 前端页面职责 diff --git a/backend/app/core/contracts/__init__.py b/backend/app/core/contracts/__init__.py new file mode 100644 index 0000000..ff4362d --- /dev/null +++ b/backend/app/core/contracts/__init__.py @@ -0,0 +1,23 @@ +"""生成任务共享契约导出。""" + +from app.core.contracts.image_generation import ( + ImageGenerationInput, + ImageGenerationResult, + ImageItem, + InputImageRef, + ResponseFormat, +) +from app.core.contracts.provider import ProviderConfig, ProviderKey +from app.core.contracts.video_generation import VideoGenerationInput, VideoGenerationResult + +__all__ = [ + "ProviderConfig", + "ProviderKey", + "VideoGenerationInput", + "VideoGenerationResult", + "ImageGenerationInput", + "ImageGenerationResult", + "ImageItem", + "InputImageRef", + "ResponseFormat", +] diff --git a/backend/app/core/tasks/image_generation_types.py b/backend/app/core/contracts/image_generation.py similarity index 95% rename from backend/app/core/tasks/image_generation_types.py rename to backend/app/core/contracts/image_generation.py index b81bc25..8dd4f0c 100644 --- a/backend/app/core/tasks/image_generation_types.py +++ b/backend/app/core/contracts/image_generation.py @@ -1,4 +1,4 @@ -"""图片生成任务:输入输出模型(供 tasks 与 integrations 共用,避免循环导入)。""" +"""图片生成共享输入输出契约。""" from __future__ import annotations @@ -6,7 +6,7 @@ from typing import Literal, Optional from pydantic import BaseModel, ConfigDict, Field, model_validator -from app.core.tasks.provider_types import ProviderKey +from app.core.contracts.provider import ProviderKey ResponseFormat = Literal["url", "b64_json"] diff --git a/backend/app/core/tasks/provider_types.py b/backend/app/core/contracts/provider.py similarity index 71% rename from backend/app/core/tasks/provider_types.py rename to backend/app/core/contracts/provider.py index 5d57a29..209efd5 100644 --- a/backend/app/core/tasks/provider_types.py +++ b/backend/app/core/contracts/provider.py @@ -1,4 +1,4 @@ -"""任务执行层供应商公共类型。""" +"""生成能力共享的供应商类型契约。""" from __future__ import annotations @@ -10,7 +10,7 @@ ProviderKey = Literal["openai", "volcengine"] @dataclass(frozen=True, slots=True) class ProviderConfig: - """任务执行时所需的供应商配置。""" + """执行生成任务时需要的供应商配置。""" provider: ProviderKey api_key: str diff --git a/backend/app/core/tasks/video_generation_types.py b/backend/app/core/contracts/video_generation.py similarity index 94% rename from backend/app/core/tasks/video_generation_types.py rename to backend/app/core/contracts/video_generation.py index a44a33c..22a6f38 100644 --- a/backend/app/core/tasks/video_generation_types.py +++ b/backend/app/core/contracts/video_generation.py @@ -1,4 +1,4 @@ -"""视频生成任务:输入输出模型(供 tasks 与 integrations 共用)。""" +"""视频生成共享输入输出契约。""" from __future__ import annotations @@ -6,7 +6,7 @@ from typing import Optional from pydantic import BaseModel, ConfigDict, Field, model_validator -from app.core.tasks.provider_types import ProviderKey +from app.core.contracts.provider import ProviderKey def _strip_optional_b64(value: str | None) -> str | None: diff --git a/backend/app/core/integrations/openai/images.py b/backend/app/core/integrations/openai/images.py index cf166be..3c08197 100644 --- a/backend/app/core/integrations/openai/images.py +++ b/backend/app/core/integrations/openai/images.py @@ -11,12 +11,12 @@ from app.core.integrations.http_logging import ( log_image_http_response, safe_body_for_log_openai, ) -from app.core.tasks.image_generation_types import ( +from app.core.contracts.image_generation import ( ImageGenerationInput, ImageGenerationResult, ImageItem, ) -from app.core.tasks.provider_types import ProviderConfig +from app.core.contracts.provider import ProviderConfig class OpenAIImageApiAdapter: diff --git a/backend/app/core/integrations/openai/video.py b/backend/app/core/integrations/openai/video.py index a6c907c..3a21247 100644 --- a/backend/app/core/integrations/openai/video.py +++ b/backend/app/core/integrations/openai/video.py @@ -5,8 +5,8 @@ from __future__ import annotations from typing import Any from app.core.integrations.openai.video_payload import build_create_video_body -from app.core.tasks.provider_types import ProviderConfig -from app.core.tasks.video_generation_types import VideoGenerationInput +from app.core.contracts.provider import ProviderConfig +from app.core.contracts.video_generation import VideoGenerationInput class OpenAIVideoApiAdapter: diff --git a/backend/app/core/integrations/openai/video_payload.py b/backend/app/core/integrations/openai/video_payload.py index d38f3bc..63ae32b 100644 --- a/backend/app/core/integrations/openai/video_payload.py +++ b/backend/app/core/integrations/openai/video_payload.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import Any -from app.core.tasks.video_generation_types import VideoGenerationInput, _strip_optional_b64 +from app.core.contracts.video_generation import VideoGenerationInput, _strip_optional_b64 def to_image_data_url(value: str) -> str: diff --git a/backend/app/core/integrations/volcengine/images.py b/backend/app/core/integrations/volcengine/images.py index b4a144a..51ee96b 100644 --- a/backend/app/core/integrations/volcengine/images.py +++ b/backend/app/core/integrations/volcengine/images.py @@ -11,12 +11,12 @@ from app.core.integrations.http_logging import ( log_image_http_response, safe_body_for_log_volcengine_image, ) -from app.core.tasks.image_generation_types import ( +from app.core.contracts.image_generation import ( ImageGenerationInput, ImageGenerationResult, ImageItem, ) -from app.core.tasks.provider_types import ProviderConfig +from app.core.contracts.provider import ProviderConfig class VolcengineImageApiAdapter: diff --git a/backend/app/core/integrations/volcengine/video.py b/backend/app/core/integrations/volcengine/video.py index c8c8603..85e1e1c 100644 --- a/backend/app/core/integrations/volcengine/video.py +++ b/backend/app/core/integrations/volcengine/video.py @@ -5,8 +5,8 @@ from __future__ import annotations from typing import Any from app.core.integrations.volcengine.video_payload import build_create_task_body -from app.core.tasks.provider_types import ProviderConfig -from app.core.tasks.video_generation_types import VideoGenerationInput +from app.core.contracts.provider import ProviderConfig +from app.core.contracts.video_generation import VideoGenerationInput class VolcengineVideoApiAdapter: diff --git a/backend/app/core/integrations/volcengine/video_payload.py b/backend/app/core/integrations/volcengine/video_payload.py index 5ff2ea2..ef7d75a 100644 --- a/backend/app/core/integrations/volcengine/video_payload.py +++ b/backend/app/core/integrations/volcengine/video_payload.py @@ -5,7 +5,7 @@ from __future__ import annotations from typing import Any from app.core.integrations.openai.video_payload import to_image_data_url -from app.core.tasks.video_generation_types import VideoGenerationInput, _strip_optional_b64 +from app.core.contracts.video_generation import VideoGenerationInput, _strip_optional_b64 def volcengine_ratio(size: str | None) -> str: diff --git a/backend/app/core/tasks/__init__.py b/backend/app/core/tasks/__init__.py index 1684c9a..d06a04f 100644 --- a/backend/app/core/tasks/__init__.py +++ b/backend/app/core/tasks/__init__.py @@ -1,10 +1,10 @@ """Tasks for the skills runtime.""" from app.core.tasks.image_generation_tasks import ImageGenerationTask -from app.core.tasks.image_generation_types import ImageGenerationInput, ImageGenerationResult -from app.core.tasks.provider_types import ProviderConfig, ProviderKey +from app.core.contracts.image_generation import ImageGenerationInput, ImageGenerationResult +from app.core.contracts.provider import ProviderConfig, ProviderKey from app.core.tasks.video_generation_tasks import VideoGenerationTask -from app.core.tasks.video_generation_types import VideoGenerationInput, VideoGenerationResult +from app.core.contracts.video_generation import VideoGenerationInput, VideoGenerationResult __all__ = [ "ProviderConfig", diff --git a/backend/app/core/tasks/image_generation_tasks.py b/backend/app/core/tasks/image_generation_tasks.py index 40bcc4d..ca71508 100644 --- a/backend/app/core/tasks/image_generation_tasks.py +++ b/backend/app/core/tasks/image_generation_tasks.py @@ -10,14 +10,14 @@ from typing import Any, AsyncIterator from app.core.integrations.openai.images import OpenAIImageApiAdapter from app.core.integrations.volcengine.images import VolcengineImageApiAdapter -from app.core.tasks.image_generation_types import ( +from app.core.contracts.image_generation import ( ImageGenerationInput, ImageGenerationResult, ImageItem, InputImageRef, ResponseFormat, ) -from app.core.tasks.provider_types import ProviderConfig +from app.core.contracts.provider import ProviderConfig from app.core.tasks.registry import resolve_task_adapter from app.core.task_manager.types import BaseTask diff --git a/backend/app/core/tasks/video_generation_tasks.py b/backend/app/core/tasks/video_generation_tasks.py index b9e908b..885203d 100644 --- a/backend/app/core/tasks/video_generation_tasks.py +++ b/backend/app/core/tasks/video_generation_tasks.py @@ -11,9 +11,9 @@ from typing import Any, AsyncIterator from app.core.integrations.openai.video import OpenAIVideoApiAdapter from app.core.integrations.volcengine.video import VolcengineVideoApiAdapter -from app.core.tasks.provider_types import ProviderConfig +from app.core.contracts.provider import ProviderConfig from app.core.tasks.registry import resolve_task_adapter -from app.core.tasks.video_generation_types import VideoGenerationInput, VideoGenerationResult +from app.core.contracts.video_generation import VideoGenerationInput, VideoGenerationResult from app.core.task_manager.types import BaseTask __all__ = [ diff --git a/backend/app/services/film/generated_video.py b/backend/app/services/film/generated_video.py index 64e9a49..92d26d6 100644 --- a/backend/app/services/film/generated_video.py +++ b/backend/app/services/film/generated_video.py @@ -11,12 +11,9 @@ from app.core import storage from app.core.db import async_session_maker from app.core.task_manager import SqlAlchemyTaskStore from app.core.task_manager.types import TaskStatus -from app.core.tasks import ( - ProviderConfig, - VideoGenerationInput, - VideoGenerationResult, - VideoGenerationTask, -) +from app.core.contracts.provider import ProviderConfig +from app.core.contracts.video_generation import VideoGenerationInput, VideoGenerationResult +from app.core.tasks import VideoGenerationTask from app.models.llm import Model, ModelCategoryKey, ModelSettings from app.models.task_links import GenerationTaskLink from app.models.studio import FileItem, Shot, ShotDetail, ShotFrameType diff --git a/backend/app/services/studio/image_task_runner.py b/backend/app/services/studio/image_task_runner.py index e1a2c17..aab1c17 100644 --- a/backend/app/services/studio/image_task_runner.py +++ b/backend/app/services/studio/image_task_runner.py @@ -6,7 +6,9 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.core.db import async_session_maker from app.core.task_manager import DeliveryMode, SqlAlchemyTaskStore, TaskManager from app.core.task_manager.types import TaskStatus -from app.core.tasks import ImageGenerationInput, ImageGenerationResult, ImageGenerationTask, ProviderConfig +from app.core.contracts.image_generation import ImageGenerationInput, ImageGenerationResult +from app.core.contracts.provider import ProviderConfig +from app.core.tasks import ImageGenerationTask from app.models.studio import ( ActorImage, AssetQualityLevel, diff --git a/backend/app/services/studio/image_tasks.py b/backend/app/services/studio/image_tasks.py index ab75c3e..04a5a3e 100644 --- a/backend/app/services/studio/image_tasks.py +++ b/backend/app/services/studio/image_tasks.py @@ -11,7 +11,7 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core import storage -from app.core.tasks import ProviderConfig +from app.core.contracts.provider import ProviderConfig from app.models.llm import Model, ModelCategoryKey from app.models.studio import AssetViewAngle, FileItem, PromptCategory, PromptTemplate, ShotFrameType from app.services.llm import get_model_by_category diff --git a/backend/tests/core/integrations/test_image_adapters.py b/backend/tests/core/integrations/test_image_adapters.py index e78c939..e869a85 100644 --- a/backend/tests/core/integrations/test_image_adapters.py +++ b/backend/tests/core/integrations/test_image_adapters.py @@ -9,8 +9,8 @@ import pytest from app.core.integrations.openai.images import OpenAIImageApiAdapter from app.core.integrations.volcengine.images import VolcengineImageApiAdapter -from app.core.tasks.image_generation_types import ImageGenerationInput, InputImageRef -from app.core.tasks.provider_types import ProviderConfig +from app.core.contracts.image_generation import ImageGenerationInput, InputImageRef +from app.core.contracts.provider import ProviderConfig def _patch_httpx_client(monkeypatch: pytest.MonkeyPatch, transport: httpx.MockTransport) -> None: diff --git a/backend/tests/core/integrations/test_video_adapters.py b/backend/tests/core/integrations/test_video_adapters.py index b75aaa2..b28db36 100644 --- a/backend/tests/core/integrations/test_video_adapters.py +++ b/backend/tests/core/integrations/test_video_adapters.py @@ -9,8 +9,8 @@ import pytest from app.core.integrations.openai.video import OpenAIVideoApiAdapter from app.core.integrations.volcengine.video import VolcengineVideoApiAdapter -from app.core.tasks.provider_types import ProviderConfig -from app.core.tasks.video_generation_types import VideoGenerationInput +from app.core.contracts.provider import ProviderConfig +from app.core.contracts.video_generation import VideoGenerationInput def _patch_httpx_client(monkeypatch: pytest.MonkeyPatch, transport: httpx.MockTransport) -> None: diff --git a/site/content/docs/architecture/project-structure.md b/site/content/docs/architecture/project-structure.md index 8c7d5af..dc9982b 100644 --- a/site/content/docs/architecture/project-structure.md +++ b/site/content/docs/architecture/project-structure.md @@ -35,6 +35,12 @@ jellyfish/ - 管理数据库模型、文件存储与任务状态 - 通过 `chains / services / core.tasks` 接入 AI 能力 +## 生成能力分层约束 + +- 通用生成契约(输入/输出 DTO、供应商配置等)统一放在 `app/core/contracts`。 +- `app/core/tasks` 仅保留任务封装、分派与执行编排,不定义跨层 DTO。 +- `app/core/integrations` 仅实现供应商协议适配,依赖 `contracts`,不依赖 `tasks` 类型模块。 + ## 站点职责 - 官网首页与产品介绍