mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 16:07:01 +08:00
refactor: split api dependencies by domain
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
"""按业务领域拆分的 FastAPI 依赖工厂。"""
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
"""Agent 与消息查询依赖。"""
|
||||||
|
|
||||||
|
from fastapi import Depends
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.api.context import get_agent_chat_repository, get_agent_chat_transaction
|
||||||
|
from app.api.data import get_async_db
|
||||||
|
from app.api.dependencies.data import repository
|
||||||
|
from app.application.messaging.chat import (
|
||||||
|
AgentChatService,
|
||||||
|
AsyncAgentChatRepository,
|
||||||
|
AsyncUnitOfWork,
|
||||||
|
)
|
||||||
|
from app.application.messaging.message import MessageQueryService
|
||||||
|
|
||||||
|
|
||||||
|
def get_agent_chat_service(
|
||||||
|
chat_repository: AsyncAgentChatRepository = Depends(get_agent_chat_repository),
|
||||||
|
unit_of_work: AsyncUnitOfWork = Depends(get_agent_chat_transaction),
|
||||||
|
) -> AgentChatService:
|
||||||
|
"""组装类型化 Agent 会话历史查询和删除服务。"""
|
||||||
|
return AgentChatService(chat_repository, unit_of_work)
|
||||||
|
|
||||||
|
|
||||||
|
def get_message_query_service(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> MessageQueryService:
|
||||||
|
"""组装消息历史异步查询服务。"""
|
||||||
|
return MessageQueryService(repository=repository("message", db))
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
"""用户身份、授权与认证服务依赖。"""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from fastapi import Depends, HTTPException
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_token
|
||||||
|
from app.api.data import get_async_db, get_db
|
||||||
|
from app.api.dependencies.data import repository, standalone_repository
|
||||||
|
from app.application.security.auth import AuthService
|
||||||
|
from app.application.security.passkeys import PasskeyService
|
||||||
|
from app.application.security.user import UserService
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_service(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> UserService:
|
||||||
|
"""组装用户管理应用服务。"""
|
||||||
|
return UserService(repository=repository("user", db))
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_service() -> AuthService:
|
||||||
|
"""组装同步认证应用服务。"""
|
||||||
|
return AuthService(
|
||||||
|
users=standalone_repository("user"),
|
||||||
|
config=standalone_repository("system_config"),
|
||||||
|
passkeys=standalone_repository("passkey"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_passkey_service() -> PasskeyService:
|
||||||
|
"""组装 PassKey 应用服务。"""
|
||||||
|
return PasskeyService(repository=standalone_repository("passkey"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_user(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
token_data: _SchemaTokenPayload = Depends(verify_token),
|
||||||
|
) -> Any:
|
||||||
|
"""读取令牌对应用户,不存在时返回 403。"""
|
||||||
|
user = repository("user", db).get_by_id(token_data.sub)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=403, detail="用户不存在")
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
async def get_current_user_async(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
token_data: _SchemaTokenPayload = Depends(verify_token),
|
||||||
|
) -> Any:
|
||||||
|
"""异步读取令牌对应用户,不存在时返回 403。"""
|
||||||
|
user = await repository("user", db).async_get_by_id(token_data.sub)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=403, detail="用户不存在")
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_active_user(
|
||||||
|
current_user: Any = Depends(get_current_user),
|
||||||
|
) -> Any:
|
||||||
|
"""校验并返回当前激活用户。"""
|
||||||
|
if not current_user.is_active:
|
||||||
|
raise HTTPException(status_code=403, detail="用户未激活")
|
||||||
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
|
async def get_current_active_user_async(
|
||||||
|
current_user: Any = Depends(get_current_user_async),
|
||||||
|
) -> Any:
|
||||||
|
"""异步校验并返回当前激活用户。"""
|
||||||
|
if not current_user.is_active:
|
||||||
|
raise HTTPException(status_code=403, detail="用户未激活")
|
||||||
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_manage_user(current_user: Any) -> Any:
|
||||||
|
"""校验用户具备全局管理权限。"""
|
||||||
|
permissions = current_user.permissions or {}
|
||||||
|
if not current_user.is_superuser and not bool(permissions.get("manage")):
|
||||||
|
raise HTTPException(status_code=400, detail="用户权限不足")
|
||||||
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_active_manage_user(
|
||||||
|
current_user: Any = Depends(get_current_active_user),
|
||||||
|
) -> Any:
|
||||||
|
"""返回当前拥有管理权限的激活用户。"""
|
||||||
|
return _ensure_manage_user(current_user)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_current_active_manage_user_async(
|
||||||
|
current_user: Any = Depends(get_current_active_user_async),
|
||||||
|
) -> Any:
|
||||||
|
"""异步返回当前拥有管理权限的激活用户。"""
|
||||||
|
return _ensure_manage_user(current_user)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_active_superuser(
|
||||||
|
current_user: Any = Depends(get_current_user),
|
||||||
|
) -> Any:
|
||||||
|
"""校验并返回当前激活超级管理员。"""
|
||||||
|
if not current_user.is_superuser:
|
||||||
|
raise HTTPException(status_code=400, detail="用户权限不足")
|
||||||
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
|
async def get_current_active_superuser_async(
|
||||||
|
current_user: Any = Depends(get_current_user_async),
|
||||||
|
) -> Any:
|
||||||
|
"""异步校验并返回当前激活超级管理员。"""
|
||||||
|
if not current_user.is_superuser:
|
||||||
|
raise HTTPException(status_code=400, detail="用户权限不足")
|
||||||
|
return current_user
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
"""未迁移领域共用的 API 数据兼容 Facade。"""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from app.api.data import get_api_data_ports
|
||||||
|
|
||||||
|
|
||||||
|
def repository(name: str, session: Any) -> Any:
|
||||||
|
"""按旧能力名构造绑定当前请求会话的仓储。"""
|
||||||
|
return get_api_data_ports().repository(name, session)
|
||||||
|
|
||||||
|
|
||||||
|
def standalone_repository(name: str) -> Any:
|
||||||
|
"""按旧能力名构造无需绑定请求会话的仓储。"""
|
||||||
|
return get_api_data_ports().standalone_repository(name)
|
||||||
|
|
||||||
|
|
||||||
|
def transaction(name: str, session: Any) -> Any:
|
||||||
|
"""按旧能力名构造绑定当前请求会话的事务端口。"""
|
||||||
|
return get_api_data_ports().transaction(name, session)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
"""历史、媒体服务器与 Dashboard 查询依赖。"""
|
||||||
|
|
||||||
|
from fastapi import Depends
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.api.data import get_async_db, get_db
|
||||||
|
from app.api.dependencies.data import repository, transaction
|
||||||
|
from app.application.dashboard import DashboardQueryService
|
||||||
|
from app.application.history import (
|
||||||
|
DownloadHistoryMutationCommand,
|
||||||
|
HistoryQueryService,
|
||||||
|
TransferHistoryLookupService,
|
||||||
|
TransferHistoryMutationCommand,
|
||||||
|
clear_transfer_failures,
|
||||||
|
)
|
||||||
|
from app.application.mediaserver import MediaServerQueryService
|
||||||
|
from app.chain.storage import StorageChain
|
||||||
|
from app.runtime.events import eventmanager
|
||||||
|
from app.schemas.types import EventType
|
||||||
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
|
|
||||||
|
|
||||||
|
def get_mediaserver_query_service(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> MediaServerQueryService:
|
||||||
|
"""组装媒体服务器本地条目异步查询服务。"""
|
||||||
|
return MediaServerQueryService(repository=repository("media_server", db))
|
||||||
|
|
||||||
|
|
||||||
|
def get_dashboard_query_service(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> DashboardQueryService:
|
||||||
|
"""组装 Dashboard 媒体与整理历史统计查询服务。"""
|
||||||
|
from app.chain.dashboard import DashboardChain
|
||||||
|
|
||||||
|
return DashboardQueryService(
|
||||||
|
repository=repository("transfer_history", db),
|
||||||
|
media_statistics=DashboardChain().media_statistic,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_download_history_mutation_command(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> DownloadHistoryMutationCommand:
|
||||||
|
"""组装下载历史删除用例及其请求级事务。"""
|
||||||
|
return DownloadHistoryMutationCommand(
|
||||||
|
repository=repository("download_history", db),
|
||||||
|
unit_of_work=transaction("sync", db),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_history_query_service(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> HistoryQueryService:
|
||||||
|
"""组装历史列表和详情异步查询服务。"""
|
||||||
|
return HistoryQueryService(
|
||||||
|
download_repository=repository("download_history", db),
|
||||||
|
transfer_repository=repository("transfer_history", db),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_transfer_history_lookup_service(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> TransferHistoryLookupService:
|
||||||
|
"""组装手动整理使用的同步历史投影服务。"""
|
||||||
|
return TransferHistoryLookupService(repository("transfer_history", db))
|
||||||
|
|
||||||
|
|
||||||
|
def get_transfer_history_mutation_command(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> TransferHistoryMutationCommand:
|
||||||
|
"""组装整理历史删除、文件处理和事件发布用例。"""
|
||||||
|
storage_chain = StorageChain()
|
||||||
|
return TransferHistoryMutationCommand(
|
||||||
|
repository=repository("transfer_history", db),
|
||||||
|
download_repository=repository("download_history", db),
|
||||||
|
unit_of_work=transaction("sync", db),
|
||||||
|
file_item_factory=lambda payload: _SchemaFileItem(**payload),
|
||||||
|
delete_media_file=storage_chain.delete_media_file,
|
||||||
|
publish_download_file_deleted=lambda payload: eventmanager.send_event(
|
||||||
|
EventType.DownloadFileDeleted,
|
||||||
|
payload,
|
||||||
|
),
|
||||||
|
clear_failures=clear_transfer_failures,
|
||||||
|
)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
"""插件配置与运行态刷新依赖。"""
|
||||||
|
|
||||||
|
from app.application.commands import init_commands
|
||||||
|
from app.application.plugin.config import PluginConfigCommand
|
||||||
|
from app.application.plugin.routes import register_plugin_api
|
||||||
|
from app.application.plugin.runtime import get_plugin_manager
|
||||||
|
from app.application.scheduling import update_plugin_job
|
||||||
|
from app.runtime.events import eventmanager
|
||||||
|
from app.schemas.event import PluginDataResetEventData
|
||||||
|
from app.schemas.types import ChainEventType
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin_config_command() -> PluginConfigCommand:
|
||||||
|
"""组装插件配置更新与重置用例,隔离 API 对运行时写操作的编排。"""
|
||||||
|
manager = get_plugin_manager()
|
||||||
|
|
||||||
|
def publish_reset(plugin_id: str) -> None:
|
||||||
|
"""在清理持久化数据前通知目标插件执行补偿。"""
|
||||||
|
eventmanager.send_event(
|
||||||
|
ChainEventType.PluginDataReset,
|
||||||
|
PluginDataResetEventData(
|
||||||
|
plugin_id=plugin_id,
|
||||||
|
reset_config=True,
|
||||||
|
reset_data=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def refresh_registrations(plugin_id: str) -> None:
|
||||||
|
"""按服务、命令、动态路由顺序刷新插件宿主注册。"""
|
||||||
|
update_plugin_job(plugin_id)
|
||||||
|
init_commands(plugin_id)
|
||||||
|
register_plugin_api(plugin_id)
|
||||||
|
|
||||||
|
return PluginConfigCommand(
|
||||||
|
save_config=manager.save_plugin_config,
|
||||||
|
initialize=manager.init_plugin,
|
||||||
|
stop=manager.stop,
|
||||||
|
delete_config=manager.delete_plugin_config,
|
||||||
|
delete_data=manager.delete_plugin_data,
|
||||||
|
reload_runtime=manager.reload_plugin,
|
||||||
|
publish_reset=publish_reset,
|
||||||
|
refresh_registrations=refresh_registrations,
|
||||||
|
)
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
"""站点领域的请求级 command/query 依赖。"""
|
||||||
|
|
||||||
|
from fastapi import Depends
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.api.data import get_async_db, get_db
|
||||||
|
from app.api.dependencies.data import repository, transaction
|
||||||
|
from app.application.site.mutation import SiteMutationCommand
|
||||||
|
from app.application.site.query import SiteQueryService
|
||||||
|
from app.application.site.sites import SitesHelper # pylint: disable=no-name-in-module
|
||||||
|
from app.domain import site as site_rules
|
||||||
|
from app.foundation import url as url_tools
|
||||||
|
from app.runtime.events import eventmanager
|
||||||
|
from app.schemas.types import EventType
|
||||||
|
|
||||||
|
|
||||||
|
async def _publish_site_updated(payload: dict) -> None:
|
||||||
|
"""发布已提交的站点更新事件。"""
|
||||||
|
await eventmanager.async_send_event(EventType.SiteUpdated, payload)
|
||||||
|
|
||||||
|
|
||||||
|
async def _publish_site_deleted(payload: dict) -> None:
|
||||||
|
"""发布已提交的站点删除事件。"""
|
||||||
|
await eventmanager.async_send_event(EventType.SiteDeleted, payload)
|
||||||
|
|
||||||
|
|
||||||
|
def get_site_mutation_command(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> SiteMutationCommand:
|
||||||
|
"""组装请求级站点写用例及其事务和外部目录依赖。"""
|
||||||
|
sites_helper = SitesHelper()
|
||||||
|
|
||||||
|
def normalize_url(value: str) -> str:
|
||||||
|
"""沿用站点接口的 scheme/netloc 规范化格式。"""
|
||||||
|
scheme, netloc = url_tools.split_netloc(value)
|
||||||
|
return f"{scheme}://{netloc}/"
|
||||||
|
|
||||||
|
return SiteMutationCommand(
|
||||||
|
repository=repository("site", db),
|
||||||
|
unit_of_work=transaction("async", db),
|
||||||
|
auth_level_provider=lambda: sites_helper.auth_level,
|
||||||
|
indexer_loader=sites_helper.async_get_indexer,
|
||||||
|
domain_extractor=site_rules.extract_domain,
|
||||||
|
url_normalizer=normalize_url,
|
||||||
|
publish_updated=_publish_site_updated,
|
||||||
|
publish_deleted=_publish_site_deleted,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_site_query_service(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> SiteQueryService:
|
||||||
|
"""组装站点异步查询服务。"""
|
||||||
|
return SiteQueryService(repository=repository("site", db))
|
||||||
|
|
||||||
|
|
||||||
|
def get_site_sync_query_service(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> SiteQueryService:
|
||||||
|
"""组装站点同步查询服务,用于同步 Chain 路由。"""
|
||||||
|
return SiteQueryService(repository=repository("site", db))
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
"""订阅领域的请求级 command/query 依赖。"""
|
||||||
|
|
||||||
|
from fastapi import BackgroundTasks, Depends
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
|
from app.api.data import get_async_db, get_db
|
||||||
|
from app.api.dependencies.data import repository, transaction
|
||||||
|
from app.application.scheduling import Scheduler
|
||||||
|
from app.application.servarr import ServarrSubscriptionService
|
||||||
|
from app.application.subscription.delete import DeleteSubscribeCommand
|
||||||
|
from app.application.subscription.identity import DeleteSubscriptionsByIdentityCommand
|
||||||
|
from app.application.subscription.mutation import SubscriptionMutationService
|
||||||
|
from app.application.subscription.query import SubscriptionQueryService
|
||||||
|
from app.application.subscription.search import SearchSubscriptionsCommand
|
||||||
|
from app.runtime.events import eventmanager
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.schemas.types import EventType
|
||||||
|
|
||||||
|
|
||||||
|
async def _publish_subscribe_deleted(
|
||||||
|
subscribe_id: int,
|
||||||
|
subscribe_info: dict,
|
||||||
|
) -> None:
|
||||||
|
"""通过宿主事件总线发布已提交的订阅删除事件。"""
|
||||||
|
await eventmanager.async_send_event(
|
||||||
|
EventType.SubscribeDeleted,
|
||||||
|
{"subscribe_id": subscribe_id, "subscribe_info": subscribe_info},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_delete_subscribe_command(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> DeleteSubscribeCommand:
|
||||||
|
"""组装请求级订阅删除用例及其具体适配器。"""
|
||||||
|
return DeleteSubscribeCommand(
|
||||||
|
repository=repository("subscribe", db),
|
||||||
|
unit_of_work=transaction("async", db),
|
||||||
|
publish_deleted=_publish_subscribe_deleted,
|
||||||
|
report_deleted=MoviePilotServerHelper.sub_done_async,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _log_subscribe_deleted_event_error(
|
||||||
|
subscribe_id: int,
|
||||||
|
error: Exception,
|
||||||
|
) -> None:
|
||||||
|
"""记录按媒体身份删除时的单条事件失败并允许后续事件继续。"""
|
||||||
|
logger.error(
|
||||||
|
f"发送订阅删除事件失败:{subscribe_id} - {error}",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_delete_subscriptions_by_identity_command(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> DeleteSubscriptionsByIdentityCommand:
|
||||||
|
"""组装请求级按媒体身份删除订阅用例。"""
|
||||||
|
return DeleteSubscriptionsByIdentityCommand(
|
||||||
|
repository=repository("subscribe", db),
|
||||||
|
unit_of_work=transaction("async", db),
|
||||||
|
publish_deleted=_publish_subscribe_deleted,
|
||||||
|
handle_event_error=_log_subscribe_deleted_event_error,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_search_subscriptions_command(
|
||||||
|
background_tasks: BackgroundTasks,
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> SearchSubscriptionsCommand:
|
||||||
|
"""组装手工订阅搜索用例,并把调度延迟到响应后的后台任务。"""
|
||||||
|
def schedule_search(subscribe_id: int | None, state: str | None) -> None:
|
||||||
|
"""按历史参数提交订阅搜索调度任务。"""
|
||||||
|
background_tasks.add_task(
|
||||||
|
Scheduler().start,
|
||||||
|
job_id="subscribe_search",
|
||||||
|
sid=subscribe_id,
|
||||||
|
state=state,
|
||||||
|
manual=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return SearchSubscriptionsCommand(
|
||||||
|
repository=repository("subscribe", db),
|
||||||
|
schedule_search=schedule_search,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_subscription_query_service(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> SubscriptionQueryService:
|
||||||
|
"""组装订阅和订阅历史异步查询服务。"""
|
||||||
|
return SubscriptionQueryService(
|
||||||
|
repository=repository("subscribe", db),
|
||||||
|
async_repository=repository("subscribe", db),
|
||||||
|
history_repository=repository("subscribe_history", db),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_subscription_mutation_service(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> SubscriptionMutationService:
|
||||||
|
"""组装异步订阅写服务。"""
|
||||||
|
return SubscriptionMutationService(
|
||||||
|
repository=repository("subscribe", db),
|
||||||
|
history_repository=repository("subscribe_history", db),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_subscription_sync_mutation_service(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> SubscriptionMutationService:
|
||||||
|
"""组装同步订阅查询服务,供文件信息接口使用。"""
|
||||||
|
return SubscriptionMutationService(repository=repository("subscribe", db))
|
||||||
|
|
||||||
|
|
||||||
|
def get_servarr_subscription_service(
|
||||||
|
async_db: AsyncSession = Depends(get_async_db),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> ServarrSubscriptionService:
|
||||||
|
"""组装 Servarr 兼容路由的请求级订阅数据用例。"""
|
||||||
|
return ServarrSubscriptionService(
|
||||||
|
async_repository=repository("subscribe", async_db),
|
||||||
|
sync_repository=repository("subscribe", db),
|
||||||
|
)
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
"""工作流领域的请求级 command/query 依赖。"""
|
||||||
|
|
||||||
|
from fastapi import Depends
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
|
from app.api.data import get_async_db, get_db
|
||||||
|
from app.api.dependencies.data import repository, standalone_repository, transaction
|
||||||
|
from app.application.scheduling import Scheduler
|
||||||
|
from app.application.workflow import (
|
||||||
|
WorkflowDefinitionCommand,
|
||||||
|
WorkflowMutationCommand,
|
||||||
|
WorkflowQueryService,
|
||||||
|
)
|
||||||
|
from app.runtime.config import global_vars
|
||||||
|
from app.workflow import WorkFlowManager
|
||||||
|
|
||||||
|
|
||||||
|
def get_workflow_mutation_command(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> WorkflowMutationCommand:
|
||||||
|
"""组装请求级工作流写用例和提交后的调度副作用。"""
|
||||||
|
scheduler = Scheduler()
|
||||||
|
workflow_manager = WorkFlowManager()
|
||||||
|
return WorkflowMutationCommand(
|
||||||
|
repository=repository("workflow", db),
|
||||||
|
unit_of_work=transaction("sync", db),
|
||||||
|
add_timer=scheduler.update_workflow_job,
|
||||||
|
remove_timer=scheduler.remove_workflow_job,
|
||||||
|
load_event=workflow_manager.load_workflow_events,
|
||||||
|
remove_event=workflow_manager.remove_workflow_event,
|
||||||
|
refresh_event=workflow_manager.update_workflow_event,
|
||||||
|
stop_running=global_vars.stop_workflow,
|
||||||
|
delete_cache=lambda workflow_id: standalone_repository(
|
||||||
|
"system_config"
|
||||||
|
).delete(f"WorkflowCache-{workflow_id}"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_workflow_definition_command(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> WorkflowDefinitionCommand:
|
||||||
|
"""组装工作流创建、复用和重置的异步写用例。"""
|
||||||
|
return WorkflowDefinitionCommand(
|
||||||
|
repository=repository("workflow", db),
|
||||||
|
unit_of_work=transaction("async", db),
|
||||||
|
stop_running=global_vars.stop_workflow,
|
||||||
|
delete_cache=lambda workflow_id: standalone_repository(
|
||||||
|
"system_config"
|
||||||
|
).delete(f"WorkflowCache-{workflow_id}"),
|
||||||
|
report_fork=MoviePilotServerHelper.async_workflow_fork_by_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_workflow_query_service(
|
||||||
|
db: AsyncSession = Depends(get_async_db),
|
||||||
|
) -> WorkflowQueryService:
|
||||||
|
"""组装工作流只读查询用例,避免端点直接持有数据库操作器。"""
|
||||||
|
return WorkflowQueryService(repository=repository("workflow", db))
|
||||||
+81
-517
@@ -1,524 +1,88 @@
|
|||||||
|
"""FastAPI 依赖兼容聚合入口。
|
||||||
|
|
||||||
|
新代码按领域从 ``app.api.dependencies`` 导入;本模块保留全部历史名字,避免端点、测试和
|
||||||
|
旧 SDK 在依赖拆分期间同步改动导入路径。
|
||||||
"""
|
"""
|
||||||
API 层的公共依赖。
|
|
||||||
|
|
||||||
这些是 FastAPI 的路由依赖:从令牌解出用户、校验激活状态与权限,失败一律以
|
from app.api.dependencies.agent import (
|
||||||
HTTPException 表达。它们此前住在 app/db/oper/user.py 里,与数据访问混在一处——
|
get_agent_chat_service,
|
||||||
鉴权是 HTTP 层的关注点,产出的是 403/400 而不是数据。放在 db 包里既让数据层反向
|
get_message_query_service,
|
||||||
依赖了 fastapi,也使这部分逻辑无法与数据访问分开度量。
|
|
||||||
"""
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from fastapi import BackgroundTasks, Depends, HTTPException
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.application.subscription.delete import DeleteSubscribeCommand
|
|
||||||
from app.application.subscription.identity import (
|
|
||||||
DeleteSubscriptionsByIdentityCommand,
|
|
||||||
)
|
)
|
||||||
from app.application.subscription.search import SearchSubscriptionsCommand
|
from app.api.dependencies.auth import (
|
||||||
from app.application.subscription.query import SubscriptionQueryService
|
get_auth_service,
|
||||||
from app.application.subscription.mutation import SubscriptionMutationService
|
get_current_active_manage_user,
|
||||||
from app.application.site.mutation import SiteMutationCommand
|
get_current_active_manage_user_async,
|
||||||
from app.application.site.query import SiteQueryService
|
get_current_active_superuser,
|
||||||
from app.application.workflow import (
|
get_current_active_superuser_async,
|
||||||
WorkflowDefinitionCommand,
|
get_current_active_user,
|
||||||
WorkflowMutationCommand,
|
get_current_active_user_async,
|
||||||
WorkflowQueryService,
|
get_current_user,
|
||||||
|
get_current_user_async,
|
||||||
|
get_passkey_service,
|
||||||
|
get_user_service,
|
||||||
)
|
)
|
||||||
from app.application.messaging.message import MessageQueryService
|
from app.api.dependencies.history import (
|
||||||
from app.application.messaging.chat import AgentChatService
|
get_dashboard_query_service,
|
||||||
from app.api.context import (
|
get_download_history_mutation_command,
|
||||||
get_agent_chat_repository,
|
get_history_query_service,
|
||||||
get_agent_chat_transaction,
|
get_mediaserver_query_service,
|
||||||
|
get_transfer_history_lookup_service,
|
||||||
|
get_transfer_history_mutation_command,
|
||||||
)
|
)
|
||||||
from app.application.messaging.chat import (
|
from app.api.dependencies.plugin import get_plugin_config_command
|
||||||
AsyncAgentChatRepository,
|
from app.api.dependencies.site import (
|
||||||
AsyncUnitOfWork as AgentChatUnitOfWork,
|
get_site_mutation_command,
|
||||||
|
get_site_query_service,
|
||||||
|
get_site_sync_query_service,
|
||||||
)
|
)
|
||||||
from app.application.mediaserver import MediaServerQueryService
|
from app.api.dependencies.subscription import (
|
||||||
from app.application.servarr import ServarrSubscriptionService
|
get_delete_subscribe_command,
|
||||||
from app.application.dashboard import DashboardQueryService
|
get_delete_subscriptions_by_identity_command,
|
||||||
from app.application.history import (
|
get_search_subscriptions_command,
|
||||||
DownloadHistoryMutationCommand,
|
get_servarr_subscription_service,
|
||||||
HistoryQueryService,
|
get_subscription_mutation_service,
|
||||||
TransferHistoryLookupService,
|
get_subscription_query_service,
|
||||||
TransferHistoryMutationCommand,
|
get_subscription_sync_mutation_service,
|
||||||
clear_transfer_failures,
|
|
||||||
)
|
)
|
||||||
from app.application.plugin.config import PluginConfigCommand
|
from app.api.dependencies.workflow import (
|
||||||
from app.application.commands import init_commands
|
get_workflow_definition_command,
|
||||||
from app.application.plugin.routes import register_plugin_api
|
get_workflow_mutation_command,
|
||||||
from app.application.scheduling import update_plugin_job
|
get_workflow_query_service,
|
||||||
from app.adapters.web.security.access import verify_token
|
)
|
||||||
from app.application.security.user import UserService
|
|
||||||
from app.application.security.auth import AuthService
|
# 兼容聚合入口只显式列出既有 FastAPI 依赖,不向插件制造新的动态导出规则。
|
||||||
from app.application.security.passkeys import PasskeyService
|
__all__ = [
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
"get_agent_chat_service",
|
||||||
from app.api.data import get_api_data_ports, get_async_db, get_db
|
"get_auth_service",
|
||||||
from app.runtime.events import eventmanager
|
"get_current_active_manage_user",
|
||||||
from app.application.plugin.runtime import get_plugin_manager as PluginManager
|
"get_current_active_manage_user_async",
|
||||||
from app.runtime.log import logger
|
"get_current_active_superuser",
|
||||||
from app.schemas.event import PluginDataResetEventData
|
"get_current_active_superuser_async",
|
||||||
from app.schemas.types import ChainEventType, EventType
|
"get_current_active_user",
|
||||||
from app.application.scheduling import Scheduler
|
"get_current_active_user_async",
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=no-name-in-module
|
"get_current_user",
|
||||||
from app.domain import site as site_rules
|
"get_current_user_async",
|
||||||
from app.foundation import url as url_tools
|
"get_dashboard_query_service",
|
||||||
from app.runtime.config import global_vars
|
"get_delete_subscribe_command",
|
||||||
from app.workflow import WorkFlowManager
|
"get_delete_subscriptions_by_identity_command",
|
||||||
from app.chain.storage import StorageChain
|
"get_download_history_mutation_command",
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
"get_history_query_service",
|
||||||
|
"get_mediaserver_query_service",
|
||||||
|
"get_message_query_service",
|
||||||
def _repository(name: str, session: Any) -> Any:
|
"get_passkey_service",
|
||||||
"""构造绑定当前请求会话的数据仓储。"""
|
"get_plugin_config_command",
|
||||||
return get_api_data_ports().repository(name, session)
|
"get_search_subscriptions_command",
|
||||||
|
"get_servarr_subscription_service",
|
||||||
|
"get_site_mutation_command",
|
||||||
def _standalone_repository(name: str) -> Any:
|
"get_site_query_service",
|
||||||
"""构造无需绑定请求会话的数据端口。"""
|
"get_site_sync_query_service",
|
||||||
return get_api_data_ports().standalone_repository(name)
|
"get_subscription_mutation_service",
|
||||||
|
"get_subscription_query_service",
|
||||||
|
"get_subscription_sync_mutation_service",
|
||||||
def _transaction(name: str, session: Any) -> Any:
|
"get_transfer_history_lookup_service",
|
||||||
"""构造绑定当前请求会话的事务端口。"""
|
"get_transfer_history_mutation_command",
|
||||||
return get_api_data_ports().transaction(name, session)
|
"get_user_service",
|
||||||
|
"get_workflow_definition_command",
|
||||||
|
"get_workflow_mutation_command",
|
||||||
async def _publish_subscribe_deleted(
|
"get_workflow_query_service",
|
||||||
subscribe_id: int,
|
]
|
||||||
subscribe_info: dict,
|
|
||||||
) -> None:
|
|
||||||
"""通过宿主事件总线发布已提交的订阅删除事件。"""
|
|
||||||
await eventmanager.async_send_event(
|
|
||||||
EventType.SubscribeDeleted,
|
|
||||||
{"subscribe_id": subscribe_id, "subscribe_info": subscribe_info},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_delete_subscribe_command(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> DeleteSubscribeCommand:
|
|
||||||
"""组装请求级订阅删除用例及其具体适配器。"""
|
|
||||||
return DeleteSubscribeCommand(
|
|
||||||
repository=_repository("subscribe", db),
|
|
||||||
unit_of_work=_transaction("async", db),
|
|
||||||
publish_deleted=_publish_subscribe_deleted,
|
|
||||||
report_deleted=MoviePilotServerHelper.sub_done_async,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _log_subscribe_deleted_event_error(
|
|
||||||
subscribe_id: int,
|
|
||||||
error: Exception,
|
|
||||||
) -> None:
|
|
||||||
"""记录按媒体身份删除时的单条事件失败并允许后续事件继续。"""
|
|
||||||
logger.error(
|
|
||||||
f"发送订阅删除事件失败:{subscribe_id} - {error}",
|
|
||||||
exc_info=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_delete_subscriptions_by_identity_command(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> DeleteSubscriptionsByIdentityCommand:
|
|
||||||
"""组装请求级按媒体身份删除订阅用例。"""
|
|
||||||
return DeleteSubscriptionsByIdentityCommand(
|
|
||||||
repository=_repository("subscribe", db),
|
|
||||||
unit_of_work=_transaction("async", db),
|
|
||||||
publish_deleted=_publish_subscribe_deleted,
|
|
||||||
handle_event_error=_log_subscribe_deleted_event_error,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_search_subscriptions_command(
|
|
||||||
background_tasks: BackgroundTasks,
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> SearchSubscriptionsCommand:
|
|
||||||
"""组装手工订阅搜索用例,并把调度延迟到响应后的后台任务。"""
|
|
||||||
def schedule_search(subscribe_id: int | None, state: str | None) -> None:
|
|
||||||
"""按历史参数提交订阅搜索调度任务。"""
|
|
||||||
background_tasks.add_task(
|
|
||||||
Scheduler().start,
|
|
||||||
job_id="subscribe_search",
|
|
||||||
sid=subscribe_id,
|
|
||||||
state=state,
|
|
||||||
manual=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
return SearchSubscriptionsCommand(
|
|
||||||
repository=_repository("subscribe", db),
|
|
||||||
schedule_search=schedule_search,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_subscription_query_service(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> SubscriptionQueryService:
|
|
||||||
"""组装订阅和订阅历史异步查询服务。"""
|
|
||||||
return SubscriptionQueryService(
|
|
||||||
repository=_repository("subscribe", db),
|
|
||||||
async_repository=_repository("subscribe", db),
|
|
||||||
history_repository=_repository("subscribe_history", db),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_service(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> UserService:
|
|
||||||
"""组装用户管理应用服务。"""
|
|
||||||
return UserService(repository=_repository("user", db))
|
|
||||||
|
|
||||||
|
|
||||||
def get_auth_service() -> AuthService:
|
|
||||||
"""组装同步认证应用服务。"""
|
|
||||||
return AuthService(
|
|
||||||
users=_standalone_repository("user"),
|
|
||||||
config=_standalone_repository("system_config"),
|
|
||||||
passkeys=_standalone_repository("passkey"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_passkey_service() -> PasskeyService:
|
|
||||||
"""组装 PassKey 应用服务。"""
|
|
||||||
return PasskeyService(repository=_standalone_repository("passkey"))
|
|
||||||
|
|
||||||
|
|
||||||
def get_subscription_mutation_service(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> SubscriptionMutationService:
|
|
||||||
"""组装异步订阅写服务。"""
|
|
||||||
return SubscriptionMutationService(
|
|
||||||
repository=_repository("subscribe", db),
|
|
||||||
history_repository=_repository("subscribe_history", db),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_subscription_sync_mutation_service(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
) -> SubscriptionMutationService:
|
|
||||||
"""组装同步订阅查询服务,供文件信息接口使用。"""
|
|
||||||
return SubscriptionMutationService(repository=_repository("subscribe", db))
|
|
||||||
|
|
||||||
|
|
||||||
def get_servarr_subscription_service(
|
|
||||||
async_db: AsyncSession = Depends(get_async_db),
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
) -> ServarrSubscriptionService:
|
|
||||||
"""组装 Servarr 兼容路由的请求级订阅数据用例。"""
|
|
||||||
return ServarrSubscriptionService(
|
|
||||||
async_repository=_repository("subscribe", async_db),
|
|
||||||
sync_repository=_repository("subscribe", db),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def _publish_site_updated(payload: dict) -> None:
|
|
||||||
"""发布已提交的站点更新事件。"""
|
|
||||||
await eventmanager.async_send_event(EventType.SiteUpdated, payload)
|
|
||||||
|
|
||||||
|
|
||||||
async def _publish_site_deleted(payload: dict) -> None:
|
|
||||||
"""发布已提交的站点删除事件。"""
|
|
||||||
await eventmanager.async_send_event(EventType.SiteDeleted, payload)
|
|
||||||
|
|
||||||
|
|
||||||
def get_site_mutation_command(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> SiteMutationCommand:
|
|
||||||
"""组装请求级站点写用例及其事务和外部目录依赖。"""
|
|
||||||
sites_helper = SitesHelper()
|
|
||||||
|
|
||||||
def normalize_url(value: str) -> str:
|
|
||||||
"""沿用站点接口的 scheme/netloc 规范化格式。"""
|
|
||||||
scheme, netloc = url_tools.split_netloc(value)
|
|
||||||
return f"{scheme}://{netloc}/"
|
|
||||||
|
|
||||||
return SiteMutationCommand(
|
|
||||||
repository=_repository("site", db),
|
|
||||||
unit_of_work=_transaction("async", db),
|
|
||||||
auth_level_provider=lambda: sites_helper.auth_level,
|
|
||||||
indexer_loader=sites_helper.async_get_indexer,
|
|
||||||
domain_extractor=site_rules.extract_domain,
|
|
||||||
url_normalizer=normalize_url,
|
|
||||||
publish_updated=_publish_site_updated,
|
|
||||||
publish_deleted=_publish_site_deleted,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_site_query_service(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> SiteQueryService:
|
|
||||||
"""组装站点异步查询服务。"""
|
|
||||||
return SiteQueryService(repository=_repository("site", db))
|
|
||||||
|
|
||||||
|
|
||||||
def get_site_sync_query_service(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
) -> SiteQueryService:
|
|
||||||
"""组装站点同步查询服务,用于同步 Chain 路由。"""
|
|
||||||
return SiteQueryService(repository=_repository("site", db))
|
|
||||||
|
|
||||||
|
|
||||||
def get_workflow_mutation_command(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
) -> WorkflowMutationCommand:
|
|
||||||
"""组装请求级工作流写用例和提交后的调度副作用。"""
|
|
||||||
scheduler = Scheduler()
|
|
||||||
workflow_manager = WorkFlowManager()
|
|
||||||
return WorkflowMutationCommand(
|
|
||||||
repository=_repository("workflow", db),
|
|
||||||
unit_of_work=_transaction("sync", db),
|
|
||||||
add_timer=scheduler.update_workflow_job,
|
|
||||||
remove_timer=scheduler.remove_workflow_job,
|
|
||||||
load_event=workflow_manager.load_workflow_events,
|
|
||||||
remove_event=workflow_manager.remove_workflow_event,
|
|
||||||
refresh_event=workflow_manager.update_workflow_event,
|
|
||||||
stop_running=global_vars.stop_workflow,
|
|
||||||
delete_cache=lambda workflow_id: _standalone_repository("system_config").delete(
|
|
||||||
f"WorkflowCache-{workflow_id}"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_workflow_definition_command(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> WorkflowDefinitionCommand:
|
|
||||||
"""组装工作流创建、复用和重置的异步写用例。"""
|
|
||||||
return WorkflowDefinitionCommand(
|
|
||||||
repository=_repository("workflow", db),
|
|
||||||
unit_of_work=_transaction("async", db),
|
|
||||||
stop_running=global_vars.stop_workflow,
|
|
||||||
delete_cache=lambda workflow_id: _standalone_repository("system_config").delete(
|
|
||||||
f"WorkflowCache-{workflow_id}"
|
|
||||||
),
|
|
||||||
report_fork=MoviePilotServerHelper.async_workflow_fork_by_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_workflow_query_service(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> WorkflowQueryService:
|
|
||||||
"""组装工作流只读查询用例,避免端点直接持有数据库操作器。"""
|
|
||||||
return WorkflowQueryService(repository=_repository("workflow", db))
|
|
||||||
|
|
||||||
|
|
||||||
def get_message_query_service(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> MessageQueryService:
|
|
||||||
"""组装消息历史异步查询服务。"""
|
|
||||||
return MessageQueryService(repository=_repository("message", db))
|
|
||||||
|
|
||||||
|
|
||||||
def get_agent_chat_service(
|
|
||||||
repository: AsyncAgentChatRepository = Depends(get_agent_chat_repository),
|
|
||||||
unit_of_work: AgentChatUnitOfWork = Depends(get_agent_chat_transaction),
|
|
||||||
) -> AgentChatService:
|
|
||||||
"""组装 Agent 会话历史查询和删除服务。"""
|
|
||||||
return AgentChatService(
|
|
||||||
repository=repository,
|
|
||||||
unit_of_work=unit_of_work,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_mediaserver_query_service(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> MediaServerQueryService:
|
|
||||||
"""组装媒体服务器本地条目异步查询服务。"""
|
|
||||||
return MediaServerQueryService(repository=_repository("media_server", db))
|
|
||||||
|
|
||||||
|
|
||||||
def get_dashboard_query_service(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
) -> DashboardQueryService:
|
|
||||||
"""组装 Dashboard 媒体与整理历史统计查询服务。"""
|
|
||||||
from app.chain.dashboard import DashboardChain
|
|
||||||
|
|
||||||
return DashboardQueryService(
|
|
||||||
repository=_repository("transfer_history", db),
|
|
||||||
media_statistics=DashboardChain().media_statistic,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_download_history_mutation_command(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
) -> DownloadHistoryMutationCommand:
|
|
||||||
"""组装下载历史删除用例及其请求级事务。"""
|
|
||||||
return DownloadHistoryMutationCommand(
|
|
||||||
repository=_repository("download_history", db),
|
|
||||||
unit_of_work=_transaction("sync", db),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_history_query_service(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
) -> HistoryQueryService:
|
|
||||||
"""组装历史列表和详情异步查询服务。"""
|
|
||||||
return HistoryQueryService(
|
|
||||||
download_repository=_repository("download_history", db),
|
|
||||||
transfer_repository=_repository("transfer_history", db),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_transfer_history_lookup_service(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
) -> TransferHistoryLookupService:
|
|
||||||
"""组装手动整理使用的同步历史投影服务。"""
|
|
||||||
return TransferHistoryLookupService(_repository("transfer_history", db))
|
|
||||||
|
|
||||||
|
|
||||||
def get_transfer_history_mutation_command(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
) -> TransferHistoryMutationCommand:
|
|
||||||
"""组装整理历史删除、文件处理和事件发布用例。"""
|
|
||||||
storage_chain = StorageChain()
|
|
||||||
return TransferHistoryMutationCommand(
|
|
||||||
repository=_repository("transfer_history", db),
|
|
||||||
download_repository=_repository("download_history", db),
|
|
||||||
unit_of_work=_transaction("sync", db),
|
|
||||||
file_item_factory=lambda payload: _SchemaFileItem(**payload),
|
|
||||||
delete_media_file=storage_chain.delete_media_file,
|
|
||||||
publish_download_file_deleted=lambda payload: eventmanager.send_event(
|
|
||||||
EventType.DownloadFileDeleted,
|
|
||||||
payload,
|
|
||||||
),
|
|
||||||
clear_failures=clear_transfer_failures,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_config_command() -> PluginConfigCommand:
|
|
||||||
"""组装插件配置更新与重置用例,隔离 API 对运行时写操作的编排。"""
|
|
||||||
manager = PluginManager()
|
|
||||||
|
|
||||||
def publish_reset(plugin_id: str) -> None:
|
|
||||||
"""在清理持久化数据前通知目标插件执行补偿。"""
|
|
||||||
eventmanager.send_event(
|
|
||||||
ChainEventType.PluginDataReset,
|
|
||||||
PluginDataResetEventData(
|
|
||||||
plugin_id=plugin_id,
|
|
||||||
reset_config=True,
|
|
||||||
reset_data=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def refresh_registrations(plugin_id: str) -> None:
|
|
||||||
"""按服务、命令、动态路由顺序刷新插件宿主注册。"""
|
|
||||||
update_plugin_job(plugin_id)
|
|
||||||
init_commands(plugin_id)
|
|
||||||
register_plugin_api(plugin_id)
|
|
||||||
|
|
||||||
return PluginConfigCommand(
|
|
||||||
save_config=manager.save_plugin_config,
|
|
||||||
initialize=manager.init_plugin,
|
|
||||||
stop=manager.stop,
|
|
||||||
delete_config=manager.delete_plugin_config,
|
|
||||||
delete_data=manager.delete_plugin_data,
|
|
||||||
reload_runtime=manager.reload_plugin,
|
|
||||||
publish_reset=publish_reset,
|
|
||||||
refresh_registrations=refresh_registrations,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_user(
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
token_data: _SchemaTokenPayload = Depends(verify_token)
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
获取当前用户
|
|
||||||
"""
|
|
||||||
user = _repository("user", db).get_by_id(token_data.sub)
|
|
||||||
if not user:
|
|
||||||
raise HTTPException(status_code=403, detail="用户不存在")
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
async def get_current_user_async(
|
|
||||||
db: AsyncSession = Depends(get_async_db),
|
|
||||||
token_data: _SchemaTokenPayload = Depends(verify_token)
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
异步获取当前用户
|
|
||||||
"""
|
|
||||||
user = await _repository("user", db).async_get_by_id(token_data.sub)
|
|
||||||
if not user:
|
|
||||||
raise HTTPException(status_code=403, detail="用户不存在")
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_active_user(
|
|
||||||
current_user: Any = Depends(get_current_user),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
获取当前激活用户
|
|
||||||
"""
|
|
||||||
if not current_user.is_active:
|
|
||||||
raise HTTPException(status_code=403, detail="用户未激活")
|
|
||||||
return current_user
|
|
||||||
|
|
||||||
|
|
||||||
async def get_current_active_user_async(
|
|
||||||
current_user: Any = Depends(get_current_user_async),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
异步获取当前激活用户
|
|
||||||
"""
|
|
||||||
if not current_user.is_active:
|
|
||||||
raise HTTPException(status_code=403, detail="用户未激活")
|
|
||||||
return current_user
|
|
||||||
|
|
||||||
|
|
||||||
def _ensure_manage_user(current_user: Any) -> Any:
|
|
||||||
"""
|
|
||||||
校验用户具备全局管理权限。
|
|
||||||
"""
|
|
||||||
permissions = current_user.permissions or {}
|
|
||||||
if not current_user.is_superuser and not bool(permissions.get("manage")):
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400, detail="用户权限不足"
|
|
||||||
)
|
|
||||||
return current_user
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_active_manage_user(
|
|
||||||
current_user: Any = Depends(get_current_active_user),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
获取当前拥有管理权限的激活用户。
|
|
||||||
"""
|
|
||||||
return _ensure_manage_user(current_user)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_current_active_manage_user_async(
|
|
||||||
current_user: Any = Depends(get_current_active_user_async),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
异步获取当前拥有管理权限的激活用户。
|
|
||||||
"""
|
|
||||||
return _ensure_manage_user(current_user)
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_active_superuser(
|
|
||||||
current_user: Any = Depends(get_current_user),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
获取当前激活超级管理员
|
|
||||||
"""
|
|
||||||
if not current_user.is_superuser:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400, detail="用户权限不足"
|
|
||||||
)
|
|
||||||
return current_user
|
|
||||||
|
|
||||||
|
|
||||||
async def get_current_active_superuser_async(
|
|
||||||
current_user: Any = Depends(get_current_user_async),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
异步获取当前激活超级管理员
|
|
||||||
"""
|
|
||||||
if not current_user.is_superuser:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400, detail="用户权限不足"
|
|
||||||
)
|
|
||||||
return current_user
|
|
||||||
|
|||||||
+41
-78
@@ -33,6 +33,7 @@ from app.schemas.message import AgentWebChoiceRequest as _SchemaAgentWebChoiceRe
|
|||||||
from app.schemas.message import Message as _SchemaMessage
|
from app.schemas.message import Message as _SchemaMessage
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.api.presentation.sse import build_sse_error_response, build_sse_response
|
||||||
from app.agent.contracts import ReplyMode, build_display_message
|
from app.agent.contracts import ReplyMode, build_display_message
|
||||||
from app.agent.llm.capability import AgentCapabilityManager
|
from app.agent.llm.capability import AgentCapabilityManager
|
||||||
from app.agent.mcp import agent_mcp_manager
|
from app.agent.mcp import agent_mcp_manager
|
||||||
@@ -45,7 +46,8 @@ from app.command import Command
|
|||||||
from app.runtime.config import global_vars, settings
|
from app.runtime.config import global_vars, settings
|
||||||
from app.runtime.events import Event, EventManager
|
from app.runtime.events import Event, EventManager
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.principal import ApiPrincipal
|
||||||
from app.api.deps import get_agent_chat_service, get_current_active_user
|
from app.api.dependencies.agent import get_agent_chat_service
|
||||||
|
from app.api.dependencies.auth import get_current_active_user
|
||||||
from app.application.messaging.chat import (
|
from app.application.messaging.chat import (
|
||||||
AgentChatRecord,
|
AgentChatRecord,
|
||||||
AgentChatService,
|
AgentChatService,
|
||||||
@@ -694,6 +696,21 @@ def _build_web_agent_sse(
|
|||||||
return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
||||||
|
|
||||||
|
|
||||||
|
def _build_web_agent_error_response(
|
||||||
|
message: str,
|
||||||
|
*,
|
||||||
|
locale: Optional[str],
|
||||||
|
) -> StreamingResponse:
|
||||||
|
"""Map a rejected WebAgent request to one terminal SSE error event."""
|
||||||
|
return build_sse_error_response(
|
||||||
|
_build_web_agent_sse(
|
||||||
|
"error",
|
||||||
|
{"message": message},
|
||||||
|
locale=locale,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _sanitize_web_agent_upload_name(
|
def _sanitize_web_agent_upload_name(
|
||||||
filename: Optional[str], mime_type: Optional[str] = None
|
filename: Optional[str], mime_type: Optional[str] = None
|
||||||
) -> str:
|
) -> str:
|
||||||
@@ -1965,15 +1982,9 @@ async def web_agent_stream(
|
|||||||
getattr(request, "headers", {}).get("X-MoviePilot-Agent-Interaction") == "1"
|
getattr(request, "headers", {}).get("X-MoviePilot-Agent-Interaction") == "1"
|
||||||
)
|
)
|
||||||
if is_secret_confirmation_control and not protected_transport_supported:
|
if is_secret_confirmation_control and not protected_transport_supported:
|
||||||
return StreamingResponse(
|
return _build_web_agent_error_response(
|
||||||
iter([
|
"当前客户端不支持安全交付敏感设置,未执行操作。",
|
||||||
_build_web_agent_sse(
|
locale=locale,
|
||||||
"error",
|
|
||||||
{"message": "当前客户端不支持安全交付敏感设置,未执行操作。"},
|
|
||||||
locale=locale,
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
media_type="text/event-stream",
|
|
||||||
)
|
)
|
||||||
is_traditional_message = (
|
is_traditional_message = (
|
||||||
_is_web_agent_traditional_message(prompt)
|
_is_web_agent_traditional_message(prompt)
|
||||||
@@ -1982,27 +1993,15 @@ async def web_agent_stream(
|
|||||||
if is_traditional_message:
|
if is_traditional_message:
|
||||||
denied_message = _ensure_web_agent_command_allowed(current_user)
|
denied_message = _ensure_web_agent_command_allowed(current_user)
|
||||||
if denied_message:
|
if denied_message:
|
||||||
return StreamingResponse(
|
return _build_web_agent_error_response(
|
||||||
iter([
|
denied_message,
|
||||||
_build_web_agent_sse(
|
locale=locale,
|
||||||
"error",
|
|
||||||
{"message": denied_message},
|
|
||||||
locale=locale,
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
media_type="text/event-stream",
|
|
||||||
)
|
)
|
||||||
unknown_command_message = _get_web_agent_unknown_command_message(prompt)
|
unknown_command_message = _get_web_agent_unknown_command_message(prompt)
|
||||||
if unknown_command_message:
|
if unknown_command_message:
|
||||||
return StreamingResponse(
|
return _build_web_agent_error_response(
|
||||||
iter([
|
unknown_command_message,
|
||||||
_build_web_agent_sse(
|
locale=locale,
|
||||||
"error",
|
|
||||||
{"message": unknown_command_message},
|
|
||||||
locale=locale,
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
media_type="text/event-stream",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
user_attachments = _build_web_agent_input_attachments(
|
user_attachments = _build_web_agent_input_attachments(
|
||||||
@@ -2089,39 +2088,19 @@ async def web_agent_stream(
|
|||||||
return
|
return
|
||||||
yield _build_web_agent_sse("done", {}, locale=locale)
|
yield _build_web_agent_sse("done", {}, locale=locale)
|
||||||
|
|
||||||
return StreamingResponse(
|
return build_sse_response(traditional_event_generator())
|
||||||
traditional_event_generator(),
|
|
||||||
media_type="text/event-stream",
|
|
||||||
headers={
|
|
||||||
"Cache-Control": "no-cache, no-transform",
|
|
||||||
"Connection": "keep-alive",
|
|
||||||
"X-Accel-Buffering": "no",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if not settings.AI_AGENT_ENABLE:
|
if not settings.AI_AGENT_ENABLE:
|
||||||
return StreamingResponse(
|
return _build_web_agent_error_response(
|
||||||
iter([
|
"智能助手未启用,请先在系统设置中开启。",
|
||||||
_build_web_agent_sse(
|
locale=locale,
|
||||||
"error",
|
|
||||||
{"message": "智能助手未启用,请先在系统设置中开启。"},
|
|
||||||
locale=locale,
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
media_type="text/event-stream",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
manager = get_running_agent_manager()
|
manager = get_running_agent_manager()
|
||||||
if manager is None:
|
if manager is None:
|
||||||
return StreamingResponse(
|
return _build_web_agent_error_response(
|
||||||
iter([
|
"智能助手服务尚未就绪,请稍后重试。",
|
||||||
_build_web_agent_sse(
|
locale=locale,
|
||||||
"error",
|
|
||||||
{"message": "智能助手服务尚未就绪,请稍后重试。"},
|
|
||||||
locale=locale,
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
media_type="text/event-stream",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
transcript = _transcribe_web_agent_audio_refs(payload.audio_refs or [])
|
transcript = _transcribe_web_agent_audio_refs(payload.audio_refs or [])
|
||||||
@@ -2129,26 +2108,14 @@ async def web_agent_stream(
|
|||||||
display_prompt = _merge_web_agent_prompt_with_transcript(display_prompt, transcript)
|
display_prompt = _merge_web_agent_prompt_with_transcript(display_prompt, transcript)
|
||||||
has_audio_input = bool(transcript)
|
has_audio_input = bool(transcript)
|
||||||
if not prompt and payload.audio_refs and not payload.images and not payload.files:
|
if not prompt and payload.audio_refs and not payload.images and not payload.files:
|
||||||
return StreamingResponse(
|
return _build_web_agent_error_response(
|
||||||
iter([
|
"语音识别失败,请稍后重试。",
|
||||||
_build_web_agent_sse(
|
locale=locale,
|
||||||
"error",
|
|
||||||
{"message": "语音识别失败,请稍后重试。"},
|
|
||||||
locale=locale,
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
media_type="text/event-stream",
|
|
||||||
)
|
)
|
||||||
if not prompt and not payload.images and not payload.files and not payload.audio_refs:
|
if not prompt and not payload.images and not payload.files and not payload.audio_refs:
|
||||||
return StreamingResponse(
|
return _build_web_agent_error_response(
|
||||||
iter([
|
"请输入要发送给智能助手的内容或选择附件。",
|
||||||
_build_web_agent_sse(
|
locale=locale,
|
||||||
"error",
|
|
||||||
{"message": "请输入要发送给智能助手的内容或选择附件。"},
|
|
||||||
locale=locale,
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
media_type="text/event-stream",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
MessageChain().bind_user_session(str(current_user.id), session_id)
|
MessageChain().bind_user_session(str(current_user.id), session_id)
|
||||||
@@ -2314,13 +2281,9 @@ async def web_agent_stream(
|
|||||||
await event_publisher.aclose()
|
await event_publisher.aclose()
|
||||||
# 客户端断线后保留 Agent 继续执行;发布器关闭后不再接受受保护结果。
|
# 客户端断线后保留 Agent 继续执行;发布器关闭后不再接受受保护结果。
|
||||||
|
|
||||||
return StreamingResponse(
|
return build_sse_response(
|
||||||
event_generator(),
|
event_generator(),
|
||||||
media_type="text/event-stream",
|
|
||||||
headers={
|
headers={
|
||||||
"Cache-Control": "no-cache, no-transform",
|
|
||||||
"Connection": "keep-alive",
|
|
||||||
"X-Accel-Buffering": "no",
|
|
||||||
**(
|
**(
|
||||||
{"X-MoviePilot-Agent-Control": "secret-confirmation"}
|
{"X-MoviePilot-Agent-Control": "secret-confirmation"}
|
||||||
if is_secret_confirmation_control
|
if is_secret_confirmation_control
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
|
||||||
import uuid
|
import uuid
|
||||||
from typing import AsyncIterator, List, Optional
|
from typing import AsyncIterator, List, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Header, Security
|
from fastapi import APIRouter, Header, Security
|
||||||
from fastapi.responses import JSONResponse, StreamingResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from app.schemas.openai import AnthropicErrorDetail as _SchemaAnthropicErrorDetail
|
from app.schemas.openai import AnthropicErrorDetail as _SchemaAnthropicErrorDetail
|
||||||
from app.schemas.openai import AnthropicErrorResponse as _SchemaAnthropicErrorResponse
|
from app.schemas.openai import AnthropicErrorResponse as _SchemaAnthropicErrorResponse
|
||||||
@@ -21,6 +20,7 @@ from app.api.openai_utils import (
|
|||||||
build_prompt,
|
build_prompt,
|
||||||
build_session_id,
|
build_session_id,
|
||||||
)
|
)
|
||||||
|
from app.api.presentation.sse import build_sse_response, encode_named_event
|
||||||
from app.agent.runtime_loader import get_running_agent_manager
|
from app.agent.runtime_loader import get_running_agent_manager
|
||||||
from app.runtime.config import settings
|
from app.runtime.config import settings
|
||||||
from app.adapters.web.security.access import anthropic_api_key_header
|
from app.adapters.web.security.access import anthropic_api_key_header
|
||||||
@@ -97,26 +97,71 @@ async def _stream_anthropic_response(
|
|||||||
|
|
||||||
task = asyncio.create_task(_run_agent())
|
task = asyncio.create_task(_run_agent())
|
||||||
try:
|
try:
|
||||||
yield f"event: message_start\ndata: {json.dumps({'type': 'message_start', 'message': {'id': message_id, 'type': 'message', 'role': 'assistant', 'content': [], 'model': MODEL_ID, 'stop_reason': None, 'stop_sequence': None, 'usage': {'input_tokens': 0, 'output_tokens': 0}}}, ensure_ascii=False)}\n\n"
|
yield encode_named_event(
|
||||||
yield f"event: content_block_start\ndata: {json.dumps({'type': 'content_block_start', 'index': 0, 'content_block': {'type': 'text', 'text': ''}}, ensure_ascii=False)}\n\n"
|
"message_start",
|
||||||
|
{
|
||||||
|
"type": "message_start",
|
||||||
|
"message": {
|
||||||
|
"id": message_id,
|
||||||
|
"type": "message",
|
||||||
|
"role": "assistant",
|
||||||
|
"content": [],
|
||||||
|
"model": MODEL_ID,
|
||||||
|
"stop_reason": None,
|
||||||
|
"stop_sequence": None,
|
||||||
|
"usage": {"input_tokens": 0, "output_tokens": 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
yield encode_named_event(
|
||||||
|
"content_block_start",
|
||||||
|
{
|
||||||
|
"type": "content_block_start",
|
||||||
|
"index": 0,
|
||||||
|
"content_block": {"type": "text", "text": ""},
|
||||||
|
},
|
||||||
|
)
|
||||||
while True:
|
while True:
|
||||||
item = await event_queue.get()
|
item = await event_queue.get()
|
||||||
if item is None:
|
if item is None:
|
||||||
break
|
break
|
||||||
if isinstance(item, dict) and item.get("error"):
|
if isinstance(item, dict) and item.get("error"):
|
||||||
yield (
|
yield encode_named_event(
|
||||||
"event: error\n"
|
"error",
|
||||||
f"data: {json.dumps({'type': 'error', 'error': {'type': 'api_error', 'message': str(item['error'])}}, ensure_ascii=False)}\n\n"
|
{
|
||||||
|
"type": "error",
|
||||||
|
"error": {
|
||||||
|
"type": "api_error",
|
||||||
|
"message": str(item["error"]),
|
||||||
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
yield f"event: message_stop\ndata: {json.dumps({'type': 'message_stop'}, ensure_ascii=False)}\n\n"
|
yield encode_named_event("message_stop", {"type": "message_stop"})
|
||||||
return
|
return
|
||||||
text = str(item or "")
|
text = str(item or "")
|
||||||
if not text:
|
if not text:
|
||||||
continue
|
continue
|
||||||
yield f"event: content_block_delta\ndata: {json.dumps({'type': 'content_block_delta', 'index': 0, 'delta': {'type': 'text_delta', 'text': text}}, ensure_ascii=False)}\n\n"
|
yield encode_named_event(
|
||||||
yield f"event: content_block_stop\ndata: {json.dumps({'type': 'content_block_stop', 'index': 0}, ensure_ascii=False)}\n\n"
|
"content_block_delta",
|
||||||
yield f"event: message_delta\ndata: {json.dumps({'type': 'message_delta', 'delta': {'stop_reason': 'end_turn', 'stop_sequence': None}, 'usage': {'output_tokens': 0}}, ensure_ascii=False)}\n\n"
|
{
|
||||||
yield f"event: message_stop\ndata: {json.dumps({'type': 'message_stop'}, ensure_ascii=False)}\n\n"
|
"type": "content_block_delta",
|
||||||
|
"index": 0,
|
||||||
|
"delta": {"type": "text_delta", "text": text},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
yield encode_named_event(
|
||||||
|
"content_block_stop",
|
||||||
|
{"type": "content_block_stop", "index": 0},
|
||||||
|
)
|
||||||
|
yield encode_named_event(
|
||||||
|
"message_delta",
|
||||||
|
{
|
||||||
|
"type": "message_delta",
|
||||||
|
"delta": {"stop_reason": "end_turn", "stop_sequence": None},
|
||||||
|
"usage": {"output_tokens": 0},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
yield encode_named_event("message_stop", {"type": "message_stop"})
|
||||||
finally:
|
finally:
|
||||||
await manager.clear_session(session_id=session_id, user_id=user_id)
|
await manager.clear_session(session_id=session_id, user_id=user_id)
|
||||||
if not task.done():
|
if not task.done():
|
||||||
@@ -172,7 +217,7 @@ async def messages(
|
|||||||
session_seed = anthropic_version or "anthropic"
|
session_seed = anthropic_version or "anthropic"
|
||||||
session_id = build_session_id(f"{session_seed}:{uuid.uuid4().hex}", SESSION_PREFIX)
|
session_id = build_session_id(f"{session_seed}:{uuid.uuid4().hex}", SESSION_PREFIX)
|
||||||
if payload.stream:
|
if payload.stream:
|
||||||
return StreamingResponse(
|
return build_sse_response(
|
||||||
_stream_anthropic_response(
|
_stream_anthropic_response(
|
||||||
manager=manager,
|
manager=manager,
|
||||||
session_id=session_id,
|
session_id=session_id,
|
||||||
@@ -180,12 +225,6 @@ async def messages(
|
|||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
images=images,
|
images=images,
|
||||||
),
|
),
|
||||||
media_type="text/event-stream",
|
|
||||||
headers={
|
|
||||||
"Cache-Control": "no-cache",
|
|
||||||
"Connection": "keep-alive",
|
|
||||||
"X-Accel-Buffering": "no",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
collected_messages = []
|
collected_messages = []
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from app.schemas.user import AuthProviderInfo as _SchemaAuthProviderInfo
|
|||||||
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
||||||
from app.application.security.auth import AuthService, consume_plugin_auth_ticket
|
from app.application.security.auth import AuthService, consume_plugin_auth_ticket
|
||||||
from app.application.plugin.runtime import get_plugin_manager as PluginManager
|
from app.application.plugin.runtime import get_plugin_manager as PluginManager
|
||||||
from app.api.deps import get_auth_service
|
from app.api.dependencies.auth import get_auth_service
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ from app.chain.dashboard import DashboardChain
|
|||||||
from app.chain.storage import StorageChain
|
from app.chain.storage import StorageChain
|
||||||
from app.runtime.config import settings
|
from app.runtime.config import settings
|
||||||
from app.adapters.web.security.access import verify_apitoken
|
from app.adapters.web.security.access import verify_apitoken
|
||||||
from app.api.deps import get_current_active_superuser, get_dashboard_query_service
|
from app.api.dependencies.auth import get_current_active_superuser
|
||||||
|
from app.api.dependencies.history import get_dashboard_query_service
|
||||||
from app.application.dashboard import DashboardQueryService
|
from app.application.dashboard import DashboardQueryService
|
||||||
from app.schemas.types import StorageAction
|
from app.schemas.types import StorageAction
|
||||||
from app.application.directory import DirectoryHelper
|
from app.application.directory import DirectoryHelper
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ from app.application.site.query import (
|
|||||||
SiteQueryService,
|
SiteQueryService,
|
||||||
get_configured_site_query_service,
|
get_configured_site_query_service,
|
||||||
)
|
)
|
||||||
from app.api.deps import get_current_active_user, get_site_sync_query_service
|
from app.api.dependencies.auth import get_current_active_user
|
||||||
|
from app.api.dependencies.site import get_site_sync_query_service
|
||||||
from app.application.directory import DirectoryHelper
|
from app.application.directory import DirectoryHelper
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_RECORDING,
|
MUSIC_ENTITY_RECORDING,
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ from app.agent.prompt.transfer_redo import (
|
|||||||
)
|
)
|
||||||
from app.runtime.config import settings, global_vars
|
from app.runtime.config import settings, global_vars
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_manage_user,
|
get_current_active_manage_user,
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
|
)
|
||||||
|
from app.api.dependencies.history import (
|
||||||
get_download_history_mutation_command,
|
get_download_history_mutation_command,
|
||||||
get_history_query_service,
|
get_history_query_service,
|
||||||
get_transfer_history_mutation_command,
|
get_transfer_history_mutation_command,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from fastapi.responses import HTMLResponse
|
|||||||
from app.schemas.common import ManageRequest as _SchemaManageRequest
|
from app.schemas.common import ManageRequest as _SchemaManageRequest
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.api.deps import get_current_active_superuser_async
|
from app.api.dependencies.auth import get_current_active_superuser_async
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ from app.domain.meta.metabase import MetaBase
|
|||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
from app.domain.metainfo import MetaInfo, MetaInfoPath
|
from app.domain.metainfo import MetaInfo, MetaInfoPath
|
||||||
from app.adapters.web.security.access import verify_token, verify_apitoken
|
from app.adapters.web.security.access import verify_token, verify_apitoken
|
||||||
from app.api.deps import get_current_active_user, get_current_active_superuser
|
from app.api.dependencies.auth import (
|
||||||
|
get_current_active_superuser,
|
||||||
|
get_current_active_user,
|
||||||
|
)
|
||||||
from app.schemas.category import CategoryConfig
|
from app.schemas.category import CategoryConfig
|
||||||
from app.schemas.event import MediaSourceInfo as _SchemaMediaSourceInfo
|
from app.schemas.event import MediaSourceInfo as _SchemaMediaSourceInfo
|
||||||
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaSource, MediaType
|
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaSource, MediaType
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from app.domain.metainfo import MetaInfo
|
|||||||
from app.adapters.web.security.access import verify_token
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.application.mediaserver import MediaServerHelper, MediaServerQueryService
|
from app.application.mediaserver import MediaServerHelper, MediaServerQueryService
|
||||||
from app.api.deps import get_mediaserver_query_service
|
from app.api.dependencies.history import get_mediaserver_query_service
|
||||||
from app.schemas.mediaserver import NotExistMediaInfo
|
from app.schemas.mediaserver import NotExistMediaInfo
|
||||||
from app.schemas.types import MediaSource, MediaType, SystemConfigKey
|
from app.schemas.types import MediaSource, MediaType, SystemConfigKey
|
||||||
from app.schemas.media import build_media_key, resolve_media_identity
|
from app.schemas.media import build_media_key, resolve_media_identity
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ from app.runtime.config import settings, global_vars
|
|||||||
from app.adapters.web.security.access import verify_token, verify_apitoken
|
from app.adapters.web.security.access import verify_token, verify_apitoken
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.principal import ApiPrincipal
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.api.deps import get_current_active_superuser, get_message_query_service
|
from app.api.dependencies.agent import get_message_query_service
|
||||||
|
from app.api.dependencies.auth import get_current_active_superuser
|
||||||
from app.application.messaging.message import MessageQueryService
|
from app.application.messaging.message import MessageQueryService
|
||||||
from app.runtime.extensions.service_config import ServiceConfigHelper
|
from app.runtime.extensions.service_config import ServiceConfigHelper
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from app.application.security.passkeys import (
|
|||||||
PasskeyService,
|
PasskeyService,
|
||||||
)
|
)
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.principal import ApiPrincipal
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_user,
|
get_current_active_user,
|
||||||
get_current_active_user_async,
|
get_current_active_user_async,
|
||||||
get_user_service,
|
get_user_service,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from app.chain.recommend import RecommendChain
|
|||||||
from app.schemas.types import MediaSource, MediaType
|
from app.schemas.types import MediaSource, MediaType
|
||||||
from app.domain.context import MusicAlbumInfo, MusicArtistInfo, MusicInfo
|
from app.domain.context import MusicAlbumInfo, MusicArtistInfo, MusicInfo
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.api.deps import get_current_active_superuser_async
|
from app.api.dependencies.auth import get_current_active_superuser_async
|
||||||
from app.chain.listenbrainz import (
|
from app.chain.listenbrainz import (
|
||||||
LISTENBRAINZ_CHART_RANGES,
|
LISTENBRAINZ_CHART_RANGES,
|
||||||
LISTENBRAINZ_FRESH_MAX_DAYS,
|
LISTENBRAINZ_FRESH_MAX_DAYS,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from app.schemas.common import ManageRequest as _SchemaManageRequest
|
|||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.chain.notification import NotificationChain
|
from app.chain.notification import NotificationChain
|
||||||
from app.api.deps import get_current_active_superuser
|
from app.api.dependencies.auth import get_current_active_superuser
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import AsyncIterator, List, Optional, Tuple
|
from typing import AsyncIterator, List, Optional, Tuple
|
||||||
|
|
||||||
from fastapi import APIRouter, Request, Security
|
from fastapi import APIRouter, Request, Security
|
||||||
from fastapi.responses import JSONResponse, StreamingResponse
|
from fastapi.responses import JSONResponse
|
||||||
from fastapi.security import HTTPAuthorizationCredentials
|
from fastapi.security import HTTPAuthorizationCredentials
|
||||||
|
|
||||||
from app.schemas.openai import OpenAIChatCompletionResponse as _SchemaOpenAIChatCompletionResponse
|
from app.schemas.openai import OpenAIChatCompletionResponse as _SchemaOpenAIChatCompletionResponse
|
||||||
@@ -26,6 +25,7 @@ from app.api.openai_utils import (
|
|||||||
build_responses_input,
|
build_responses_input,
|
||||||
build_session_id,
|
build_session_id,
|
||||||
)
|
)
|
||||||
|
from app.api.presentation.sse import build_sse_response, encode_data_event
|
||||||
from app.agent.runtime_loader import (
|
from app.agent.runtime_loader import (
|
||||||
get_moviepilot_agent_type,
|
get_moviepilot_agent_type,
|
||||||
get_running_agent_manager,
|
get_running_agent_manager,
|
||||||
@@ -216,7 +216,8 @@ def _get_collecting_agent_type() -> type:
|
|||||||
|
|
||||||
|
|
||||||
def _sse_payload(data: dict) -> str:
|
def _sse_payload(data: dict) -> str:
|
||||||
return f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
|
"""保留旧测试入口并委托独立 OpenAI SSE wire mapper。"""
|
||||||
|
return encode_data_event(data)
|
||||||
|
|
||||||
|
|
||||||
async def _stream_response(
|
async def _stream_response(
|
||||||
@@ -519,7 +520,7 @@ async def chat_completions(
|
|||||||
session_id = build_session_id(session_key, SESSION_PREFIX)
|
session_id = build_session_id(session_key, SESSION_PREFIX)
|
||||||
username = str(payload.user or "openai-client")
|
username = str(payload.user or "openai-client")
|
||||||
if payload.stream:
|
if payload.stream:
|
||||||
return StreamingResponse(
|
return build_sse_response(
|
||||||
_stream_response(
|
_stream_response(
|
||||||
manager=manager,
|
manager=manager,
|
||||||
session_id=session_id,
|
session_id=session_id,
|
||||||
@@ -529,12 +530,6 @@ async def chat_completions(
|
|||||||
images=images,
|
images=images,
|
||||||
cleanup_session=not use_server_session,
|
cleanup_session=not use_server_session,
|
||||||
),
|
),
|
||||||
media_type="text/event-stream",
|
|
||||||
headers={
|
|
||||||
"Cache-Control": "no-cache",
|
|
||||||
"Connection": "keep-alive",
|
|
||||||
"X-Accel-Buffering": "no",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
collected_messages = []
|
collected_messages = []
|
||||||
|
|||||||
@@ -46,9 +46,11 @@ from app.adapters.web.security.access import (
|
|||||||
)
|
)
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.principal import ApiPrincipal
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
get_current_active_superuser_async,
|
get_current_active_superuser_async,
|
||||||
|
)
|
||||||
|
from app.api.dependencies.plugin import (
|
||||||
get_plugin_config_command,
|
get_plugin_config_command,
|
||||||
)
|
)
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
|
|||||||
@@ -26,11 +26,13 @@ from app.application.plugin.runtime import get_plugin_manager as PluginManager
|
|||||||
from app.adapters.web.security.access import verify_token
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.principal import ApiPrincipal
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_manage_user,
|
get_current_active_manage_user,
|
||||||
get_current_active_manage_user_async,
|
get_current_active_manage_user_async,
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
get_current_active_superuser_async,
|
get_current_active_superuser_async,
|
||||||
|
)
|
||||||
|
from app.api.dependencies.site import (
|
||||||
get_site_mutation_command,
|
get_site_mutation_command,
|
||||||
get_site_query_service,
|
get_site_query_service,
|
||||||
get_site_sync_query_service,
|
get_site_sync_query_service,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from app.chain.storage import StorageChain
|
|||||||
from app.chain.transfer import TransferChain
|
from app.chain.transfer import TransferChain
|
||||||
from app.runtime.config import settings
|
from app.runtime.config import settings
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.principal import ApiPrincipal
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_manage_user,
|
get_current_active_manage_user,
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -36,9 +36,11 @@ from app.application.subscription.mutation import (
|
|||||||
SubscriptionMutationService,
|
SubscriptionMutationService,
|
||||||
)
|
)
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_user,
|
get_current_active_user,
|
||||||
get_current_active_user_async,
|
get_current_active_user_async,
|
||||||
|
)
|
||||||
|
from app.api.dependencies.subscription import (
|
||||||
get_delete_subscribe_command,
|
get_delete_subscribe_command,
|
||||||
get_delete_subscriptions_by_identity_command,
|
get_delete_subscriptions_by_identity_command,
|
||||||
get_search_subscriptions_command,
|
get_search_subscriptions_command,
|
||||||
|
|||||||
@@ -42,7 +42,11 @@ from app.application.module import ModuleManager
|
|||||||
from app.adapters.web.security.access import verify_apitoken, verify_resource_token, verify_token
|
from app.adapters.web.security.access import verify_apitoken, verify_resource_token, verify_token
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.principal import ApiPrincipal
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.api.deps import get_current_active_superuser, get_current_active_superuser_async, get_current_active_user_async
|
from app.api.dependencies.auth import (
|
||||||
|
get_current_active_superuser,
|
||||||
|
get_current_active_superuser_async,
|
||||||
|
get_current_active_user_async,
|
||||||
|
)
|
||||||
from app.application.image import ImageHelper
|
from app.application.image import ImageHelper
|
||||||
from app.runtime.localization import LocaleHelper
|
from app.runtime.localization import LocaleHelper
|
||||||
from app.adapters.external.market import (
|
from app.adapters.external.market import (
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from app.chain.tmdb import TmdbChain
|
|||||||
from app.runtime.config import settings
|
from app.runtime.config import settings
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.api.deps import get_current_active_superuser_async
|
from app.api.dependencies.auth import get_current_active_superuser_async
|
||||||
from app.schemas.types import MediaType, SystemConfigKey
|
from app.schemas.types import MediaType, SystemConfigKey
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ from app.runtime.config import settings
|
|||||||
from app.domain.context import MediaInfo, MusicInfo
|
from app.domain.context import MediaInfo, MusicInfo
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
from app.domain.metainfo import MetaInfo
|
from app.domain.metainfo import MetaInfo
|
||||||
from app.api.deps import get_current_active_superuser, get_current_active_superuser_async
|
from app.api.dependencies.auth import (
|
||||||
|
get_current_active_superuser,
|
||||||
|
get_current_active_superuser_async,
|
||||||
|
)
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_RECORDING,
|
MUSIC_ENTITY_RECORDING,
|
||||||
MediaSource,
|
MediaSource,
|
||||||
|
|||||||
@@ -19,10 +19,8 @@ from app.chain.media import MediaChain
|
|||||||
from app.chain.transfer import TransferChain
|
from app.chain.transfer import TransferChain
|
||||||
from app.runtime.config import settings, global_vars
|
from app.runtime.config import settings, global_vars
|
||||||
from app.adapters.web.security.access import verify_token, verify_apitoken
|
from app.adapters.web.security.access import verify_token, verify_apitoken
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import get_current_active_manage_user
|
||||||
get_current_active_manage_user,
|
from app.api.dependencies.history import get_transfer_history_lookup_service
|
||||||
get_transfer_history_lookup_service,
|
|
||||||
)
|
|
||||||
from app.application.directory import DirectoryHelper
|
from app.application.directory import DirectoryHelper
|
||||||
from app.application.history import TransferHistoryLookupService
|
from app.application.history import TransferHistoryLookupService
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
@@ -304,12 +302,26 @@ def manual_transfer(
|
|||||||
_: object = Depends(get_current_active_manage_user),
|
_: object = Depends(get_current_active_manage_user),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
手动转移,文件或历史记录,支持自定义剧集识别格式
|
解析手动整理 HTTP 请求并委托兼容用例处理器。
|
||||||
|
|
||||||
:param transer_item: 手工整理项
|
:param transer_item: 手工整理项
|
||||||
:param background: 后台运行
|
:param background: 后台运行
|
||||||
:param history_query: 整理历史投影服务
|
:param history_query: 整理历史投影服务
|
||||||
:param _: Token校验
|
:param _: Token校验
|
||||||
"""
|
"""
|
||||||
|
return _execute_manual_transfer(
|
||||||
|
transer_item=transer_item,
|
||||||
|
background=background,
|
||||||
|
history_query=history_query,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _execute_manual_transfer(
|
||||||
|
transer_item: ManualTransferItem,
|
||||||
|
background: Optional[bool],
|
||||||
|
history_query: TransferHistoryLookupService,
|
||||||
|
) -> Any:
|
||||||
|
"""执行历史恢复、批量预览与 TransferChain 兼容编排。"""
|
||||||
force = False
|
force = False
|
||||||
downloader = None
|
downloader = None
|
||||||
download_hash = None
|
download_hash = None
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from app.schemas.user import UserUpdate as _SchemaUserUpdate
|
|||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.application.security.token import PasswordTooLongError, get_password_hash
|
from app.application.security.token import PasswordTooLongError, get_password_hash
|
||||||
from app.application.security.user import UserService
|
from app.application.security.user import UserService
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_superuser_async,
|
get_current_active_superuser_async,
|
||||||
get_current_active_user_async,
|
get_current_active_user_async,
|
||||||
get_current_active_user,
|
get_current_active_user,
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ from app.application.workflow import (
|
|||||||
from app.chain.workflow import WorkflowChain
|
from app.chain.workflow import WorkflowChain
|
||||||
from app.application.plugin.runtime import get_plugin_manager as PluginManager
|
from app.application.plugin.runtime import get_plugin_manager as PluginManager
|
||||||
from app.workflow import WorkFlowManager
|
from app.workflow import WorkFlowManager
|
||||||
from app.api.deps import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_manage_user,
|
get_current_active_manage_user,
|
||||||
get_current_active_manage_user_async,
|
get_current_active_manage_user_async,
|
||||||
|
)
|
||||||
|
from app.api.dependencies.workflow import (
|
||||||
get_workflow_definition_command,
|
get_workflow_definition_command,
|
||||||
get_workflow_mutation_command,
|
get_workflow_mutation_command,
|
||||||
get_workflow_query_service,
|
get_workflow_query_service,
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
"""API presentation helpers isolated from domain dependency assembly."""
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"""SSE wire mapping and response lifecycle helpers."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from collections.abc import AsyncIterable, Iterable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from fastapi.responses import StreamingResponse
|
||||||
|
|
||||||
|
|
||||||
|
SSE_HEADERS = {
|
||||||
|
"Cache-Control": "no-cache, no-transform",
|
||||||
|
"Connection": "keep-alive",
|
||||||
|
"X-Accel-Buffering": "no",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def encode_data_event(payload: dict[str, Any]) -> str:
|
||||||
|
"""Encode an OpenAI-style unnamed SSE data event."""
|
||||||
|
return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
||||||
|
|
||||||
|
|
||||||
|
def encode_named_event(event: str, payload: dict[str, Any]) -> str:
|
||||||
|
"""Encode a named SSE event without applying a response envelope."""
|
||||||
|
return (
|
||||||
|
f"event: {event}\n"
|
||||||
|
f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_sse_response(
|
||||||
|
content: AsyncIterable[str] | Iterable[str],
|
||||||
|
*,
|
||||||
|
headers: dict[str, str] | None = None,
|
||||||
|
) -> StreamingResponse:
|
||||||
|
"""Build a non-buffered SSE response and merge protocol-specific headers."""
|
||||||
|
return StreamingResponse(
|
||||||
|
content,
|
||||||
|
media_type="text/event-stream",
|
||||||
|
headers={**SSE_HEADERS, **(headers or {})},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_sse_error_response(payload: str) -> StreamingResponse:
|
||||||
|
"""Build a one-event SSE error response using the common transport policy."""
|
||||||
|
return build_sse_response(iter([payload]))
|
||||||
+1
-1
@@ -19,7 +19,7 @@ from app.domain.context import MediaInfo
|
|||||||
from app.domain.metainfo import MetaInfo
|
from app.domain.metainfo import MetaInfo
|
||||||
from app.application.servarr import ServarrSubscription, ServarrSubscriptionService
|
from app.application.servarr import ServarrSubscription, ServarrSubscriptionService
|
||||||
from app.adapters.web.security.access import verify_apikey
|
from app.adapters.web.security.access import verify_apikey
|
||||||
from app.api.deps import get_servarr_subscription_service
|
from app.api.dependencies.subscription import get_servarr_subscription_service
|
||||||
from app.schemas.servarr import RadarrMovie
|
from app.schemas.servarr import RadarrMovie
|
||||||
from app.schemas.servarr import SonarrSeries
|
from app.schemas.servarr import SonarrSeries
|
||||||
from app.schemas.types import MediaSource, MediaType
|
from app.schemas.types import MediaSource, MediaType
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
> 审计范围:宿主后端;排除 `app/plugins/**` 运行时插件副本
|
> 审计范围:宿主后端;排除 `app/plugins/**` 运行时插件副本
|
||||||
> 规范优先级:`AGENTS.md` 与 `docs/rules/` 高于本文
|
> 规范优先级:`AGENTS.md` 与 `docs/rules/` 高于本文
|
||||||
> 相关文档:`docs/architecture-overview.md`、`docs/refactor/backend-architecture-governance.md`、`docs/refactor/backend-module-refactor-compatibility.md`
|
> 相关文档:`docs/architecture-overview.md`、`docs/refactor/backend-architecture-governance.md`、`docs/refactor/backend-module-refactor-compatibility.md`
|
||||||
> 实施进度:阶段 0(ARCH-201~203)、阶段 1(ARCH-210~212)、阶段 2(ARCH-220~222)与 ARCH-230 已完成,后续任务按 ID 独立提交和回滚
|
> 实施进度:阶段 0(ARCH-201~203)、阶段 1(ARCH-210~212)、阶段 2(ARCH-220~222)与 ARCH-230~231 已完成,后续任务按 ID 独立提交和回滚
|
||||||
|
|
||||||
## 1. 结论先行
|
## 1. 结论先行
|
||||||
|
|
||||||
@@ -473,6 +473,22 @@ app/api/dependencies/ # 按领域拆分依赖工厂
|
|||||||
|
|
||||||
**优先切片**:`manual_transfer()`、`web_agent_stream()`、OpenAI/Anthropic streaming adapter。
|
**优先切片**:`manual_transfer()`、`web_agent_stream()`、OpenAI/Anthropic streaming adapter。
|
||||||
|
|
||||||
|
**实施记录(2026-08-21)**:
|
||||||
|
|
||||||
|
- `app/api/deps.py` 已由 524 行集中装配点收敛为 88 行兼容聚合入口;认证、Agent、订阅、站点、
|
||||||
|
工作流、历史和插件依赖分别由 `app/api/dependencies/` 下的领域模块拥有。宿主 API 端点全部改为
|
||||||
|
直接导入领域依赖,旧入口只为外部兼容消费者保留。
|
||||||
|
- 新增 `app/api/presentation/sse.py`,统一 non-buffered SSE transport 策略,并分别提供 unnamed data
|
||||||
|
与 named event wire mapper。WebAgent、OpenAI 和 Anthropic 继续保留各自协议 payload 与错误结构,
|
||||||
|
不进入通用 `Response` 包装。
|
||||||
|
- `manual_transfer()` 已缩为 HTTP/鉴权/依赖入口,历史恢复、批量预览和旧 `TransferChain` 参数兼容
|
||||||
|
由内部处理器承接;WebAgent 的拒绝响应、stream headers 与协议映射已从主控制流抽离。长生命周期
|
||||||
|
generator 仍保留在端点模块,因为它直接拥有 request disconnect、后台 task cancel 与敏感结果关闭时序,
|
||||||
|
后续只能在保持现有取消测试的前提下继续下沉。
|
||||||
|
- 125 个鉴权、手动整理、WebAgent、OpenAI/Anthropic 生命周期、API 响应和 typed runtime 专项测试通过;
|
||||||
|
61 个架构/基线 CLI 测试通过。依赖基线变化只反映集中边拆为领域边,runtime contract 变化只反映
|
||||||
|
dependency callable 的新模块路径;禁止边与插件 raw API 均未变化。
|
||||||
|
|
||||||
#### ARCH-232:配置快照与窄配置端口
|
#### ARCH-232:配置快照与窄配置端口
|
||||||
|
|
||||||
**目标**:阻止 `settings` 和 `SystemConfigOper()` 继续扩散,不要求一次清除 180 个文件。
|
**目标**:阻止 `settings` 和 `SystemConfigOper()` 继续扩散,不要求一次清除 180 个文件。
|
||||||
|
|||||||
+188
-79
@@ -13,8 +13,8 @@
|
|||||||
"runtime_to_db": [],
|
"runtime_to_db": [],
|
||||||
"workflow_to_db": []
|
"workflow_to_db": []
|
||||||
},
|
},
|
||||||
"edge_count": 6105,
|
"edge_count": 6203,
|
||||||
"edge_sha256": "47fd6792530be771c9854d3f2951097d3b110cd46b1cab11643a070f3b11299c",
|
"edge_sha256": "75ac7a20854abb707ea6c851326a183873a64e93f67053ffcd79ca5b01a105f6",
|
||||||
"edges": [
|
"edges": [
|
||||||
"app -> app.runtime",
|
"app -> app.runtime",
|
||||||
"app -> app.runtime.compat",
|
"app -> app.runtime.compat",
|
||||||
@@ -1480,60 +1480,119 @@
|
|||||||
"app.api.context -> app.application.messaging.chat",
|
"app.api.context -> app.application.messaging.chat",
|
||||||
"app.api.context -> app.startup",
|
"app.api.context -> app.startup",
|
||||||
"app.api.context -> app.startup.context",
|
"app.api.context -> app.startup.context",
|
||||||
"app.api.deps -> app.adapters",
|
"app.api.dependencies.agent -> app.api",
|
||||||
"app.api.deps -> app.adapters.external",
|
"app.api.dependencies.agent -> app.api.context",
|
||||||
"app.api.deps -> app.adapters.external.server",
|
"app.api.dependencies.agent -> app.api.data",
|
||||||
"app.api.deps -> app.adapters.web",
|
"app.api.dependencies.agent -> app.api.dependencies",
|
||||||
"app.api.deps -> app.adapters.web.security",
|
"app.api.dependencies.agent -> app.api.dependencies.data",
|
||||||
"app.api.deps -> app.adapters.web.security.access",
|
"app.api.dependencies.agent -> app.application",
|
||||||
|
"app.api.dependencies.agent -> app.application.messaging",
|
||||||
|
"app.api.dependencies.agent -> app.application.messaging.chat",
|
||||||
|
"app.api.dependencies.agent -> app.application.messaging.message",
|
||||||
|
"app.api.dependencies.auth -> app.adapters",
|
||||||
|
"app.api.dependencies.auth -> app.adapters.web",
|
||||||
|
"app.api.dependencies.auth -> app.adapters.web.security",
|
||||||
|
"app.api.dependencies.auth -> app.adapters.web.security.access",
|
||||||
|
"app.api.dependencies.auth -> app.api",
|
||||||
|
"app.api.dependencies.auth -> app.api.data",
|
||||||
|
"app.api.dependencies.auth -> app.api.dependencies",
|
||||||
|
"app.api.dependencies.auth -> app.api.dependencies.data",
|
||||||
|
"app.api.dependencies.auth -> app.application",
|
||||||
|
"app.api.dependencies.auth -> app.application.security",
|
||||||
|
"app.api.dependencies.auth -> app.application.security.auth",
|
||||||
|
"app.api.dependencies.auth -> app.application.security.passkeys",
|
||||||
|
"app.api.dependencies.auth -> app.application.security.user",
|
||||||
|
"app.api.dependencies.auth -> app.schemas",
|
||||||
|
"app.api.dependencies.auth -> app.schemas.token",
|
||||||
|
"app.api.dependencies.data -> app.api",
|
||||||
|
"app.api.dependencies.data -> app.api.data",
|
||||||
|
"app.api.dependencies.history -> app.api",
|
||||||
|
"app.api.dependencies.history -> app.api.data",
|
||||||
|
"app.api.dependencies.history -> app.api.dependencies",
|
||||||
|
"app.api.dependencies.history -> app.api.dependencies.data",
|
||||||
|
"app.api.dependencies.history -> app.application",
|
||||||
|
"app.api.dependencies.history -> app.application.dashboard",
|
||||||
|
"app.api.dependencies.history -> app.application.history",
|
||||||
|
"app.api.dependencies.history -> app.application.mediaserver",
|
||||||
|
"app.api.dependencies.history -> app.chain",
|
||||||
|
"app.api.dependencies.history -> app.chain.dashboard",
|
||||||
|
"app.api.dependencies.history -> app.chain.storage",
|
||||||
|
"app.api.dependencies.history -> app.runtime",
|
||||||
|
"app.api.dependencies.history -> app.runtime.events",
|
||||||
|
"app.api.dependencies.history -> app.schemas",
|
||||||
|
"app.api.dependencies.history -> app.schemas.types",
|
||||||
|
"app.api.dependencies.history -> app.schemas.workflow",
|
||||||
|
"app.api.dependencies.plugin -> app.application",
|
||||||
|
"app.api.dependencies.plugin -> app.application.commands",
|
||||||
|
"app.api.dependencies.plugin -> app.application.plugin",
|
||||||
|
"app.api.dependencies.plugin -> app.application.plugin.config",
|
||||||
|
"app.api.dependencies.plugin -> app.application.plugin.routes",
|
||||||
|
"app.api.dependencies.plugin -> app.application.plugin.runtime",
|
||||||
|
"app.api.dependencies.plugin -> app.application.scheduling",
|
||||||
|
"app.api.dependencies.plugin -> app.runtime",
|
||||||
|
"app.api.dependencies.plugin -> app.runtime.events",
|
||||||
|
"app.api.dependencies.plugin -> app.schemas",
|
||||||
|
"app.api.dependencies.plugin -> app.schemas.event",
|
||||||
|
"app.api.dependencies.plugin -> app.schemas.types",
|
||||||
|
"app.api.dependencies.site -> app.api",
|
||||||
|
"app.api.dependencies.site -> app.api.data",
|
||||||
|
"app.api.dependencies.site -> app.api.dependencies",
|
||||||
|
"app.api.dependencies.site -> app.api.dependencies.data",
|
||||||
|
"app.api.dependencies.site -> app.application",
|
||||||
|
"app.api.dependencies.site -> app.application.site",
|
||||||
|
"app.api.dependencies.site -> app.application.site.mutation",
|
||||||
|
"app.api.dependencies.site -> app.application.site.query",
|
||||||
|
"app.api.dependencies.site -> app.domain",
|
||||||
|
"app.api.dependencies.site -> app.domain.site",
|
||||||
|
"app.api.dependencies.site -> app.foundation",
|
||||||
|
"app.api.dependencies.site -> app.foundation.url",
|
||||||
|
"app.api.dependencies.site -> app.runtime",
|
||||||
|
"app.api.dependencies.site -> app.runtime.events",
|
||||||
|
"app.api.dependencies.site -> app.schemas",
|
||||||
|
"app.api.dependencies.site -> app.schemas.types",
|
||||||
|
"app.api.dependencies.subscription -> app.adapters",
|
||||||
|
"app.api.dependencies.subscription -> app.adapters.external",
|
||||||
|
"app.api.dependencies.subscription -> app.adapters.external.server",
|
||||||
|
"app.api.dependencies.subscription -> app.api",
|
||||||
|
"app.api.dependencies.subscription -> app.api.data",
|
||||||
|
"app.api.dependencies.subscription -> app.api.dependencies",
|
||||||
|
"app.api.dependencies.subscription -> app.api.dependencies.data",
|
||||||
|
"app.api.dependencies.subscription -> app.application",
|
||||||
|
"app.api.dependencies.subscription -> app.application.scheduling",
|
||||||
|
"app.api.dependencies.subscription -> app.application.servarr",
|
||||||
|
"app.api.dependencies.subscription -> app.application.subscription",
|
||||||
|
"app.api.dependencies.subscription -> app.application.subscription.delete",
|
||||||
|
"app.api.dependencies.subscription -> app.application.subscription.identity",
|
||||||
|
"app.api.dependencies.subscription -> app.application.subscription.mutation",
|
||||||
|
"app.api.dependencies.subscription -> app.application.subscription.query",
|
||||||
|
"app.api.dependencies.subscription -> app.application.subscription.search",
|
||||||
|
"app.api.dependencies.subscription -> app.runtime",
|
||||||
|
"app.api.dependencies.subscription -> app.runtime.events",
|
||||||
|
"app.api.dependencies.subscription -> app.runtime.log",
|
||||||
|
"app.api.dependencies.subscription -> app.schemas",
|
||||||
|
"app.api.dependencies.subscription -> app.schemas.types",
|
||||||
|
"app.api.dependencies.workflow -> app.adapters",
|
||||||
|
"app.api.dependencies.workflow -> app.adapters.external",
|
||||||
|
"app.api.dependencies.workflow -> app.adapters.external.server",
|
||||||
|
"app.api.dependencies.workflow -> app.api",
|
||||||
|
"app.api.dependencies.workflow -> app.api.data",
|
||||||
|
"app.api.dependencies.workflow -> app.api.dependencies",
|
||||||
|
"app.api.dependencies.workflow -> app.api.dependencies.data",
|
||||||
|
"app.api.dependencies.workflow -> app.application",
|
||||||
|
"app.api.dependencies.workflow -> app.application.scheduling",
|
||||||
|
"app.api.dependencies.workflow -> app.application.workflow",
|
||||||
|
"app.api.dependencies.workflow -> app.runtime",
|
||||||
|
"app.api.dependencies.workflow -> app.runtime.config",
|
||||||
|
"app.api.dependencies.workflow -> app.workflow",
|
||||||
"app.api.deps -> app.api",
|
"app.api.deps -> app.api",
|
||||||
"app.api.deps -> app.api.context",
|
"app.api.deps -> app.api.dependencies",
|
||||||
"app.api.deps -> app.api.data",
|
"app.api.deps -> app.api.dependencies.agent",
|
||||||
"app.api.deps -> app.application",
|
"app.api.deps -> app.api.dependencies.auth",
|
||||||
"app.api.deps -> app.application.commands",
|
"app.api.deps -> app.api.dependencies.history",
|
||||||
"app.api.deps -> app.application.dashboard",
|
"app.api.deps -> app.api.dependencies.plugin",
|
||||||
"app.api.deps -> app.application.history",
|
"app.api.deps -> app.api.dependencies.site",
|
||||||
"app.api.deps -> app.application.mediaserver",
|
"app.api.deps -> app.api.dependencies.subscription",
|
||||||
"app.api.deps -> app.application.messaging",
|
"app.api.deps -> app.api.dependencies.workflow",
|
||||||
"app.api.deps -> app.application.messaging.chat",
|
|
||||||
"app.api.deps -> app.application.messaging.message",
|
|
||||||
"app.api.deps -> app.application.plugin",
|
|
||||||
"app.api.deps -> app.application.plugin.config",
|
|
||||||
"app.api.deps -> app.application.plugin.routes",
|
|
||||||
"app.api.deps -> app.application.plugin.runtime",
|
|
||||||
"app.api.deps -> app.application.scheduling",
|
|
||||||
"app.api.deps -> app.application.security",
|
|
||||||
"app.api.deps -> app.application.security.auth",
|
|
||||||
"app.api.deps -> app.application.security.passkeys",
|
|
||||||
"app.api.deps -> app.application.security.user",
|
|
||||||
"app.api.deps -> app.application.servarr",
|
|
||||||
"app.api.deps -> app.application.site",
|
|
||||||
"app.api.deps -> app.application.site.mutation",
|
|
||||||
"app.api.deps -> app.application.site.query",
|
|
||||||
"app.api.deps -> app.application.subscription",
|
|
||||||
"app.api.deps -> app.application.subscription.delete",
|
|
||||||
"app.api.deps -> app.application.subscription.identity",
|
|
||||||
"app.api.deps -> app.application.subscription.mutation",
|
|
||||||
"app.api.deps -> app.application.subscription.query",
|
|
||||||
"app.api.deps -> app.application.subscription.search",
|
|
||||||
"app.api.deps -> app.application.workflow",
|
|
||||||
"app.api.deps -> app.chain",
|
|
||||||
"app.api.deps -> app.chain.dashboard",
|
|
||||||
"app.api.deps -> app.chain.storage",
|
|
||||||
"app.api.deps -> app.domain",
|
|
||||||
"app.api.deps -> app.domain.site",
|
|
||||||
"app.api.deps -> app.foundation",
|
|
||||||
"app.api.deps -> app.foundation.url",
|
|
||||||
"app.api.deps -> app.runtime",
|
|
||||||
"app.api.deps -> app.runtime.config",
|
|
||||||
"app.api.deps -> app.runtime.events",
|
|
||||||
"app.api.deps -> app.runtime.log",
|
|
||||||
"app.api.deps -> app.schemas",
|
|
||||||
"app.api.deps -> app.schemas.event",
|
|
||||||
"app.api.deps -> app.schemas.token",
|
|
||||||
"app.api.deps -> app.schemas.types",
|
|
||||||
"app.api.deps -> app.schemas.workflow",
|
|
||||||
"app.api.deps -> app.workflow",
|
|
||||||
"app.api.endpoints.agent -> app.agent",
|
"app.api.endpoints.agent -> app.agent",
|
||||||
"app.api.endpoints.agent -> app.agent.callback",
|
"app.api.endpoints.agent -> app.agent.callback",
|
||||||
"app.api.endpoints.agent -> app.agent.contracts",
|
"app.api.endpoints.agent -> app.agent.contracts",
|
||||||
@@ -1542,7 +1601,11 @@
|
|||||||
"app.api.endpoints.agent -> app.agent.mcp",
|
"app.api.endpoints.agent -> app.agent.mcp",
|
||||||
"app.api.endpoints.agent -> app.agent.runtime_loader",
|
"app.api.endpoints.agent -> app.agent.runtime_loader",
|
||||||
"app.api.endpoints.agent -> app.api",
|
"app.api.endpoints.agent -> app.api",
|
||||||
"app.api.endpoints.agent -> app.api.deps",
|
"app.api.endpoints.agent -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.agent -> app.api.dependencies.agent",
|
||||||
|
"app.api.endpoints.agent -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.agent -> app.api.presentation",
|
||||||
|
"app.api.endpoints.agent -> app.api.presentation.sse",
|
||||||
"app.api.endpoints.agent -> app.api.principal",
|
"app.api.endpoints.agent -> app.api.principal",
|
||||||
"app.api.endpoints.agent -> app.api.response",
|
"app.api.endpoints.agent -> app.api.response",
|
||||||
"app.api.endpoints.agent -> app.application",
|
"app.api.endpoints.agent -> app.application",
|
||||||
@@ -1589,12 +1652,15 @@
|
|||||||
"app.api.endpoints.anthropic -> app.api.endpoints",
|
"app.api.endpoints.anthropic -> app.api.endpoints",
|
||||||
"app.api.endpoints.anthropic -> app.api.endpoints.openai",
|
"app.api.endpoints.anthropic -> app.api.endpoints.openai",
|
||||||
"app.api.endpoints.anthropic -> app.api.openai_utils",
|
"app.api.endpoints.anthropic -> app.api.openai_utils",
|
||||||
|
"app.api.endpoints.anthropic -> app.api.presentation",
|
||||||
|
"app.api.endpoints.anthropic -> app.api.presentation.sse",
|
||||||
"app.api.endpoints.anthropic -> app.runtime",
|
"app.api.endpoints.anthropic -> app.runtime",
|
||||||
"app.api.endpoints.anthropic -> app.runtime.config",
|
"app.api.endpoints.anthropic -> app.runtime.config",
|
||||||
"app.api.endpoints.anthropic -> app.schemas",
|
"app.api.endpoints.anthropic -> app.schemas",
|
||||||
"app.api.endpoints.anthropic -> app.schemas.openai",
|
"app.api.endpoints.anthropic -> app.schemas.openai",
|
||||||
"app.api.endpoints.auth -> app.api",
|
"app.api.endpoints.auth -> app.api",
|
||||||
"app.api.endpoints.auth -> app.api.deps",
|
"app.api.endpoints.auth -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.auth -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.auth -> app.api.response",
|
"app.api.endpoints.auth -> app.api.response",
|
||||||
"app.api.endpoints.auth -> app.application",
|
"app.api.endpoints.auth -> app.application",
|
||||||
"app.api.endpoints.auth -> app.application.plugin",
|
"app.api.endpoints.auth -> app.application.plugin",
|
||||||
@@ -1625,7 +1691,9 @@
|
|||||||
"app.api.endpoints.dashboard -> app.adapters.web.security",
|
"app.api.endpoints.dashboard -> app.adapters.web.security",
|
||||||
"app.api.endpoints.dashboard -> app.adapters.web.security.access",
|
"app.api.endpoints.dashboard -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.dashboard -> app.api",
|
"app.api.endpoints.dashboard -> app.api",
|
||||||
"app.api.endpoints.dashboard -> app.api.deps",
|
"app.api.endpoints.dashboard -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.dashboard -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.dashboard -> app.api.dependencies.history",
|
||||||
"app.api.endpoints.dashboard -> app.api.response",
|
"app.api.endpoints.dashboard -> app.api.response",
|
||||||
"app.api.endpoints.dashboard -> app.application",
|
"app.api.endpoints.dashboard -> app.application",
|
||||||
"app.api.endpoints.dashboard -> app.application.dashboard",
|
"app.api.endpoints.dashboard -> app.application.dashboard",
|
||||||
@@ -1677,7 +1745,9 @@
|
|||||||
"app.api.endpoints.download -> app.adapters.web.security",
|
"app.api.endpoints.download -> app.adapters.web.security",
|
||||||
"app.api.endpoints.download -> app.adapters.web.security.access",
|
"app.api.endpoints.download -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.download -> app.api",
|
"app.api.endpoints.download -> app.api",
|
||||||
"app.api.endpoints.download -> app.api.deps",
|
"app.api.endpoints.download -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.download -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.download -> app.api.dependencies.site",
|
||||||
"app.api.endpoints.download -> app.api.principal",
|
"app.api.endpoints.download -> app.api.principal",
|
||||||
"app.api.endpoints.download -> app.api.response",
|
"app.api.endpoints.download -> app.api.response",
|
||||||
"app.api.endpoints.download -> app.application",
|
"app.api.endpoints.download -> app.application",
|
||||||
@@ -1718,7 +1788,9 @@
|
|||||||
"app.api.endpoints.history -> app.agent.prompt.transfer_redo",
|
"app.api.endpoints.history -> app.agent.prompt.transfer_redo",
|
||||||
"app.api.endpoints.history -> app.agent.runtime_loader",
|
"app.api.endpoints.history -> app.agent.runtime_loader",
|
||||||
"app.api.endpoints.history -> app.api",
|
"app.api.endpoints.history -> app.api",
|
||||||
"app.api.endpoints.history -> app.api.deps",
|
"app.api.endpoints.history -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.history -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.history -> app.api.dependencies.history",
|
||||||
"app.api.endpoints.history -> app.api.response",
|
"app.api.endpoints.history -> app.api.response",
|
||||||
"app.api.endpoints.history -> app.application",
|
"app.api.endpoints.history -> app.application",
|
||||||
"app.api.endpoints.history -> app.application.history",
|
"app.api.endpoints.history -> app.application.history",
|
||||||
@@ -1735,7 +1807,8 @@
|
|||||||
"app.api.endpoints.llm -> app.agent.llm",
|
"app.api.endpoints.llm -> app.agent.llm",
|
||||||
"app.api.endpoints.llm -> app.agent.llm.provider",
|
"app.api.endpoints.llm -> app.agent.llm.provider",
|
||||||
"app.api.endpoints.llm -> app.api",
|
"app.api.endpoints.llm -> app.api",
|
||||||
"app.api.endpoints.llm -> app.api.deps",
|
"app.api.endpoints.llm -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.llm -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.llm -> app.api.response",
|
"app.api.endpoints.llm -> app.api.response",
|
||||||
"app.api.endpoints.llm -> app.schemas",
|
"app.api.endpoints.llm -> app.schemas",
|
||||||
"app.api.endpoints.llm -> app.schemas.common",
|
"app.api.endpoints.llm -> app.schemas.common",
|
||||||
@@ -1779,7 +1852,8 @@
|
|||||||
"app.api.endpoints.media -> app.adapters.web.security",
|
"app.api.endpoints.media -> app.adapters.web.security",
|
||||||
"app.api.endpoints.media -> app.adapters.web.security.access",
|
"app.api.endpoints.media -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.media -> app.api",
|
"app.api.endpoints.media -> app.api",
|
||||||
"app.api.endpoints.media -> app.api.deps",
|
"app.api.endpoints.media -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.media -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.media -> app.api.response",
|
"app.api.endpoints.media -> app.api.response",
|
||||||
"app.api.endpoints.media -> app.application",
|
"app.api.endpoints.media -> app.application",
|
||||||
"app.api.endpoints.media -> app.application.plugin",
|
"app.api.endpoints.media -> app.application.plugin",
|
||||||
@@ -1811,7 +1885,8 @@
|
|||||||
"app.api.endpoints.mediaserver -> app.adapters.web.security",
|
"app.api.endpoints.mediaserver -> app.adapters.web.security",
|
||||||
"app.api.endpoints.mediaserver -> app.adapters.web.security.access",
|
"app.api.endpoints.mediaserver -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.mediaserver -> app.api",
|
"app.api.endpoints.mediaserver -> app.api",
|
||||||
"app.api.endpoints.mediaserver -> app.api.deps",
|
"app.api.endpoints.mediaserver -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.mediaserver -> app.api.dependencies.history",
|
||||||
"app.api.endpoints.mediaserver -> app.api.response",
|
"app.api.endpoints.mediaserver -> app.api.response",
|
||||||
"app.api.endpoints.mediaserver -> app.application",
|
"app.api.endpoints.mediaserver -> app.application",
|
||||||
"app.api.endpoints.mediaserver -> app.application.configuration",
|
"app.api.endpoints.mediaserver -> app.application.configuration",
|
||||||
@@ -1837,7 +1912,9 @@
|
|||||||
"app.api.endpoints.message -> app.adapters.web.security",
|
"app.api.endpoints.message -> app.adapters.web.security",
|
||||||
"app.api.endpoints.message -> app.adapters.web.security.access",
|
"app.api.endpoints.message -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.message -> app.api",
|
"app.api.endpoints.message -> app.api",
|
||||||
"app.api.endpoints.message -> app.api.deps",
|
"app.api.endpoints.message -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.message -> app.api.dependencies.agent",
|
||||||
|
"app.api.endpoints.message -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.message -> app.api.principal",
|
"app.api.endpoints.message -> app.api.principal",
|
||||||
"app.api.endpoints.message -> app.api.response",
|
"app.api.endpoints.message -> app.api.response",
|
||||||
"app.api.endpoints.message -> app.application",
|
"app.api.endpoints.message -> app.application",
|
||||||
@@ -1861,7 +1938,8 @@
|
|||||||
"app.api.endpoints.mfa -> app.adapters.web.security",
|
"app.api.endpoints.mfa -> app.adapters.web.security",
|
||||||
"app.api.endpoints.mfa -> app.adapters.web.security.access",
|
"app.api.endpoints.mfa -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.mfa -> app.api",
|
"app.api.endpoints.mfa -> app.api",
|
||||||
"app.api.endpoints.mfa -> app.api.deps",
|
"app.api.endpoints.mfa -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.mfa -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.mfa -> app.api.principal",
|
"app.api.endpoints.mfa -> app.api.principal",
|
||||||
"app.api.endpoints.mfa -> app.api.response",
|
"app.api.endpoints.mfa -> app.api.response",
|
||||||
"app.api.endpoints.mfa -> app.application",
|
"app.api.endpoints.mfa -> app.application",
|
||||||
@@ -1884,7 +1962,8 @@
|
|||||||
"app.api.endpoints.music -> app.adapters.web.security",
|
"app.api.endpoints.music -> app.adapters.web.security",
|
||||||
"app.api.endpoints.music -> app.adapters.web.security.access",
|
"app.api.endpoints.music -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.music -> app.api",
|
"app.api.endpoints.music -> app.api",
|
||||||
"app.api.endpoints.music -> app.api.deps",
|
"app.api.endpoints.music -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.music -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.music -> app.api.response",
|
"app.api.endpoints.music -> app.api.response",
|
||||||
"app.api.endpoints.music -> app.chain",
|
"app.api.endpoints.music -> app.chain",
|
||||||
"app.api.endpoints.music -> app.chain.listenbrainz",
|
"app.api.endpoints.music -> app.chain.listenbrainz",
|
||||||
@@ -1900,7 +1979,8 @@
|
|||||||
"app.api.endpoints.music -> app.schemas.transfer",
|
"app.api.endpoints.music -> app.schemas.transfer",
|
||||||
"app.api.endpoints.music -> app.schemas.types",
|
"app.api.endpoints.music -> app.schemas.types",
|
||||||
"app.api.endpoints.notification -> app.api",
|
"app.api.endpoints.notification -> app.api",
|
||||||
"app.api.endpoints.notification -> app.api.deps",
|
"app.api.endpoints.notification -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.notification -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.notification -> app.api.response",
|
"app.api.endpoints.notification -> app.api.response",
|
||||||
"app.api.endpoints.notification -> app.chain",
|
"app.api.endpoints.notification -> app.chain",
|
||||||
"app.api.endpoints.notification -> app.chain.notification",
|
"app.api.endpoints.notification -> app.chain.notification",
|
||||||
@@ -1917,6 +1997,8 @@
|
|||||||
"app.api.endpoints.openai -> app.agent.runtime_loader",
|
"app.api.endpoints.openai -> app.agent.runtime_loader",
|
||||||
"app.api.endpoints.openai -> app.api",
|
"app.api.endpoints.openai -> app.api",
|
||||||
"app.api.endpoints.openai -> app.api.openai_utils",
|
"app.api.endpoints.openai -> app.api.openai_utils",
|
||||||
|
"app.api.endpoints.openai -> app.api.presentation",
|
||||||
|
"app.api.endpoints.openai -> app.api.presentation.sse",
|
||||||
"app.api.endpoints.openai -> app.runtime",
|
"app.api.endpoints.openai -> app.runtime",
|
||||||
"app.api.endpoints.openai -> app.runtime.config",
|
"app.api.endpoints.openai -> app.runtime.config",
|
||||||
"app.api.endpoints.openai -> app.schemas",
|
"app.api.endpoints.openai -> app.schemas",
|
||||||
@@ -1933,7 +2015,9 @@
|
|||||||
"app.api.endpoints.plugin -> app.adapters.web.security",
|
"app.api.endpoints.plugin -> app.adapters.web.security",
|
||||||
"app.api.endpoints.plugin -> app.adapters.web.security.access",
|
"app.api.endpoints.plugin -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.plugin -> app.api",
|
"app.api.endpoints.plugin -> app.api",
|
||||||
"app.api.endpoints.plugin -> app.api.deps",
|
"app.api.endpoints.plugin -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.plugin -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.plugin -> app.api.dependencies.plugin",
|
||||||
"app.api.endpoints.plugin -> app.api.principal",
|
"app.api.endpoints.plugin -> app.api.principal",
|
||||||
"app.api.endpoints.plugin -> app.api.response",
|
"app.api.endpoints.plugin -> app.api.response",
|
||||||
"app.api.endpoints.plugin -> app.application",
|
"app.api.endpoints.plugin -> app.application",
|
||||||
@@ -2005,7 +2089,9 @@
|
|||||||
"app.api.endpoints.site -> app.adapters.web.security",
|
"app.api.endpoints.site -> app.adapters.web.security",
|
||||||
"app.api.endpoints.site -> app.adapters.web.security.access",
|
"app.api.endpoints.site -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.site -> app.api",
|
"app.api.endpoints.site -> app.api",
|
||||||
"app.api.endpoints.site -> app.api.deps",
|
"app.api.endpoints.site -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.site -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.site -> app.api.dependencies.site",
|
||||||
"app.api.endpoints.site -> app.api.endpoints",
|
"app.api.endpoints.site -> app.api.endpoints",
|
||||||
"app.api.endpoints.site -> app.api.endpoints.plugin",
|
"app.api.endpoints.site -> app.api.endpoints.plugin",
|
||||||
"app.api.endpoints.site -> app.api.principal",
|
"app.api.endpoints.site -> app.api.principal",
|
||||||
@@ -2035,7 +2121,8 @@
|
|||||||
"app.api.endpoints.site -> app.schemas.types",
|
"app.api.endpoints.site -> app.schemas.types",
|
||||||
"app.api.endpoints.site -> app.schemas.workflow",
|
"app.api.endpoints.site -> app.schemas.workflow",
|
||||||
"app.api.endpoints.storage -> app.api",
|
"app.api.endpoints.storage -> app.api",
|
||||||
"app.api.endpoints.storage -> app.api.deps",
|
"app.api.endpoints.storage -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.storage -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.storage -> app.api.principal",
|
"app.api.endpoints.storage -> app.api.principal",
|
||||||
"app.api.endpoints.storage -> app.api.response",
|
"app.api.endpoints.storage -> app.api.response",
|
||||||
"app.api.endpoints.storage -> app.chain",
|
"app.api.endpoints.storage -> app.chain",
|
||||||
@@ -2059,7 +2146,9 @@
|
|||||||
"app.api.endpoints.subscribe -> app.adapters.web.security",
|
"app.api.endpoints.subscribe -> app.adapters.web.security",
|
||||||
"app.api.endpoints.subscribe -> app.adapters.web.security.access",
|
"app.api.endpoints.subscribe -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.subscribe -> app.api",
|
"app.api.endpoints.subscribe -> app.api",
|
||||||
"app.api.endpoints.subscribe -> app.api.deps",
|
"app.api.endpoints.subscribe -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.subscribe -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.subscribe -> app.api.dependencies.subscription",
|
||||||
"app.api.endpoints.subscribe -> app.api.principal",
|
"app.api.endpoints.subscribe -> app.api.principal",
|
||||||
"app.api.endpoints.subscribe -> app.api.response",
|
"app.api.endpoints.subscribe -> app.api.response",
|
||||||
"app.api.endpoints.subscribe -> app.application",
|
"app.api.endpoints.subscribe -> app.application",
|
||||||
@@ -2103,7 +2192,8 @@
|
|||||||
"app.api.endpoints.system -> app.agent.llm",
|
"app.api.endpoints.system -> app.agent.llm",
|
||||||
"app.api.endpoints.system -> app.agent.llm.server_tools",
|
"app.api.endpoints.system -> app.agent.llm.server_tools",
|
||||||
"app.api.endpoints.system -> app.api",
|
"app.api.endpoints.system -> app.api",
|
||||||
"app.api.endpoints.system -> app.api.deps",
|
"app.api.endpoints.system -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.system -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.system -> app.api.principal",
|
"app.api.endpoints.system -> app.api.principal",
|
||||||
"app.api.endpoints.system -> app.api.response",
|
"app.api.endpoints.system -> app.api.response",
|
||||||
"app.api.endpoints.system -> app.application",
|
"app.api.endpoints.system -> app.application",
|
||||||
@@ -2147,7 +2237,8 @@
|
|||||||
"app.api.endpoints.tmdb -> app.adapters.web.security",
|
"app.api.endpoints.tmdb -> app.adapters.web.security",
|
||||||
"app.api.endpoints.tmdb -> app.adapters.web.security.access",
|
"app.api.endpoints.tmdb -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.tmdb -> app.api",
|
"app.api.endpoints.tmdb -> app.api",
|
||||||
"app.api.endpoints.tmdb -> app.api.deps",
|
"app.api.endpoints.tmdb -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.tmdb -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.tmdb -> app.api.response",
|
"app.api.endpoints.tmdb -> app.api.response",
|
||||||
"app.api.endpoints.tmdb -> app.application",
|
"app.api.endpoints.tmdb -> app.application",
|
||||||
"app.api.endpoints.tmdb -> app.application.configuration",
|
"app.api.endpoints.tmdb -> app.application.configuration",
|
||||||
@@ -2163,7 +2254,8 @@
|
|||||||
"app.api.endpoints.tmdb -> app.schemas.types",
|
"app.api.endpoints.tmdb -> app.schemas.types",
|
||||||
"app.api.endpoints.tmdb -> app.schemas.workflow",
|
"app.api.endpoints.tmdb -> app.schemas.workflow",
|
||||||
"app.api.endpoints.torrent -> app.api",
|
"app.api.endpoints.torrent -> app.api",
|
||||||
"app.api.endpoints.torrent -> app.api.deps",
|
"app.api.endpoints.torrent -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.torrent -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.torrent -> app.api.response",
|
"app.api.endpoints.torrent -> app.api.response",
|
||||||
"app.api.endpoints.torrent -> app.chain",
|
"app.api.endpoints.torrent -> app.chain",
|
||||||
"app.api.endpoints.torrent -> app.chain.media",
|
"app.api.endpoints.torrent -> app.chain.media",
|
||||||
@@ -2188,7 +2280,9 @@
|
|||||||
"app.api.endpoints.transfer -> app.adapters.web.security",
|
"app.api.endpoints.transfer -> app.adapters.web.security",
|
||||||
"app.api.endpoints.transfer -> app.adapters.web.security.access",
|
"app.api.endpoints.transfer -> app.adapters.web.security.access",
|
||||||
"app.api.endpoints.transfer -> app.api",
|
"app.api.endpoints.transfer -> app.api",
|
||||||
"app.api.endpoints.transfer -> app.api.deps",
|
"app.api.endpoints.transfer -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.transfer -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.transfer -> app.api.dependencies.history",
|
||||||
"app.api.endpoints.transfer -> app.api.response",
|
"app.api.endpoints.transfer -> app.api.response",
|
||||||
"app.api.endpoints.transfer -> app.application",
|
"app.api.endpoints.transfer -> app.application",
|
||||||
"app.api.endpoints.transfer -> app.application.directory",
|
"app.api.endpoints.transfer -> app.application.directory",
|
||||||
@@ -2208,7 +2302,8 @@
|
|||||||
"app.api.endpoints.transfer -> app.schemas.types",
|
"app.api.endpoints.transfer -> app.schemas.types",
|
||||||
"app.api.endpoints.transfer -> app.schemas.workflow",
|
"app.api.endpoints.transfer -> app.schemas.workflow",
|
||||||
"app.api.endpoints.user -> app.api",
|
"app.api.endpoints.user -> app.api",
|
||||||
"app.api.endpoints.user -> app.api.deps",
|
"app.api.endpoints.user -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.user -> app.api.dependencies.auth",
|
||||||
"app.api.endpoints.user -> app.api.response",
|
"app.api.endpoints.user -> app.api.response",
|
||||||
"app.api.endpoints.user -> app.application",
|
"app.api.endpoints.user -> app.application",
|
||||||
"app.api.endpoints.user -> app.application.security",
|
"app.api.endpoints.user -> app.application.security",
|
||||||
@@ -2233,7 +2328,9 @@
|
|||||||
"app.api.endpoints.workflow -> app.adapters.external",
|
"app.api.endpoints.workflow -> app.adapters.external",
|
||||||
"app.api.endpoints.workflow -> app.adapters.external.server",
|
"app.api.endpoints.workflow -> app.adapters.external.server",
|
||||||
"app.api.endpoints.workflow -> app.api",
|
"app.api.endpoints.workflow -> app.api",
|
||||||
"app.api.endpoints.workflow -> app.api.deps",
|
"app.api.endpoints.workflow -> app.api.dependencies",
|
||||||
|
"app.api.endpoints.workflow -> app.api.dependencies.auth",
|
||||||
|
"app.api.endpoints.workflow -> app.api.dependencies.workflow",
|
||||||
"app.api.endpoints.workflow -> app.api.response",
|
"app.api.endpoints.workflow -> app.api.response",
|
||||||
"app.api.endpoints.workflow -> app.application",
|
"app.api.endpoints.workflow -> app.application",
|
||||||
"app.api.endpoints.workflow -> app.application.plugin",
|
"app.api.endpoints.workflow -> app.application.plugin",
|
||||||
@@ -2289,7 +2386,8 @@
|
|||||||
"app.api.servarr -> app.adapters.web.security",
|
"app.api.servarr -> app.adapters.web.security",
|
||||||
"app.api.servarr -> app.adapters.web.security.access",
|
"app.api.servarr -> app.adapters.web.security.access",
|
||||||
"app.api.servarr -> app.api",
|
"app.api.servarr -> app.api",
|
||||||
"app.api.servarr -> app.api.deps",
|
"app.api.servarr -> app.api.dependencies",
|
||||||
|
"app.api.servarr -> app.api.dependencies.subscription",
|
||||||
"app.api.servarr -> app.api.response",
|
"app.api.servarr -> app.api.response",
|
||||||
"app.api.servarr -> app.application",
|
"app.api.servarr -> app.application",
|
||||||
"app.api.servarr -> app.application.servarr",
|
"app.api.servarr -> app.application.servarr",
|
||||||
@@ -6122,7 +6220,7 @@
|
|||||||
"app.workflow.actions.transfer_file -> app.workflow",
|
"app.workflow.actions.transfer_file -> app.workflow",
|
||||||
"app.workflow.actions.transfer_file -> app.workflow.actions"
|
"app.workflow.actions.transfer_file -> app.workflow.actions"
|
||||||
],
|
],
|
||||||
"module_count": 760,
|
"module_count": 771,
|
||||||
"modules": [
|
"modules": [
|
||||||
"app",
|
"app",
|
||||||
"app.adapters",
|
"app.adapters",
|
||||||
@@ -6312,6 +6410,15 @@
|
|||||||
"app.api.apiv1",
|
"app.api.apiv1",
|
||||||
"app.api.context",
|
"app.api.context",
|
||||||
"app.api.data",
|
"app.api.data",
|
||||||
|
"app.api.dependencies",
|
||||||
|
"app.api.dependencies.agent",
|
||||||
|
"app.api.dependencies.auth",
|
||||||
|
"app.api.dependencies.data",
|
||||||
|
"app.api.dependencies.history",
|
||||||
|
"app.api.dependencies.plugin",
|
||||||
|
"app.api.dependencies.site",
|
||||||
|
"app.api.dependencies.subscription",
|
||||||
|
"app.api.dependencies.workflow",
|
||||||
"app.api.deps",
|
"app.api.deps",
|
||||||
"app.api.endpoints",
|
"app.api.endpoints",
|
||||||
"app.api.endpoints.agent",
|
"app.api.endpoints.agent",
|
||||||
@@ -6348,6 +6455,8 @@
|
|||||||
"app.api.endpoints.webhook",
|
"app.api.endpoints.webhook",
|
||||||
"app.api.endpoints.workflow",
|
"app.api.endpoints.workflow",
|
||||||
"app.api.openai_utils",
|
"app.api.openai_utils",
|
||||||
|
"app.api.presentation",
|
||||||
|
"app.api.presentation.sse",
|
||||||
"app.api.principal",
|
"app.api.principal",
|
||||||
"app.api.response",
|
"app.api.response",
|
||||||
"app.api.router_specs",
|
"app.api.router_specs",
|
||||||
|
|||||||
@@ -1317,7 +1317,7 @@
|
|||||||
"consumers": [],
|
"consumers": [],
|
||||||
"producers": [
|
"producers": [
|
||||||
{
|
{
|
||||||
"caller": "app.api.deps",
|
"caller": "app.api.dependencies.plugin",
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1514,7 +1514,7 @@
|
|||||||
],
|
],
|
||||||
"producers": [
|
"producers": [
|
||||||
{
|
{
|
||||||
"caller": "app.api.deps",
|
"caller": "app.api.dependencies.history",
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1598,7 +1598,7 @@
|
|||||||
],
|
],
|
||||||
"producers": [
|
"producers": [
|
||||||
{
|
{
|
||||||
"caller": "app.api.deps",
|
"caller": "app.api.dependencies.site",
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1625,7 +1625,7 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"caller": "app.api.deps",
|
"caller": "app.api.dependencies.site",
|
||||||
"count": 1
|
"count": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1660,7 +1660,7 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"caller": "app.api.deps",
|
"caller": "app.api.dependencies.subscription",
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user