"""应用层 Protocol 端口定义。""" from __future__ import annotations from pathlib import Path # noqa: TC003 — runtime_checkable Protocol 需运行时可见 from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable if TYPE_CHECKING: import numpy as np from app.harness.pools import Pools from app.tree.index import TreeIndex from core.types import GeneratedQuestion, PoolConfig @runtime_checkable class EmbeddingProvider(Protocol): """文本嵌入端口。 属性: dim: 嵌入维度 D。 """ @property def dim(self) -> int: ... def embed(self, texts: str | list[str]) -> np.ndarray: """文本 → 嵌入向量(L2 归一化)。 参数: texts: 单条文本或文本列表。 返回: [N, D] ndarray,每行 L2 范数为 1.0。 """ ... @runtime_checkable class QuestionGenerator(Protocol): """LLM 驱动的题目生成端口(预留接口)。 参数: video_id: 视频标识。 task_type: 题型。 tree: 视频树索引,提供锚节点上下文。 exemplars: 风格示例题目列表。 返回: 生成的单条题目。 """ async def generate( self, video_id: str, task_type: str, tree: TreeIndex, *, exemplars: list[GeneratedQuestion], ) -> GeneratedQuestion: ... @runtime_checkable class OCRProvider(Protocol): """帧文字转录端口。 实现方负责将帧图像发送给 OCR 服务并返回拼接后的文本。 单帧失败应降级跳过,不得抛出异常中断整体流程。 参数: frame_paths: 帧文件路径列表。 返回: "帧1: <行1> | <行2>\\n帧2: ..." 格式文本;无有效结果时空串。 """ async def transcribe_frames(self, frame_paths: list[Path]) -> str: ... @runtime_checkable class ToolDispatchFn(Protocol): """工具调度函数签名。 参数: tool_name: 工具名称。 args: 工具参数字典。 context: 上下文字典(包含 session_id)。 返回: 工具执行结果文本。 """ async def __call__( self, tool_name: str, args: dict[str, Any], *, context: dict[str, Any] ) -> str: ... @runtime_checkable class ToolDispatchFactory(Protocol): """per-version 工具调度工厂。 通过注入 skills_dir 生产对应版本的 ToolDispatchFn。 参数: skills_dir: skill 文件目录(None 则不加载 skill)。 返回: ToolDispatchFn 实例。 """ def __call__(self, *, skills_dir: Path | None = None) -> ToolDispatchFn: ... @runtime_checkable class PromptBuilderFn(Protocol): """Prompt 构建函数签名。 参数: qa: 待构建 prompt 的题目。 返回: (system_prompt, user_prompt) 二元组。 """ def __call__(self, qa: GeneratedQuestion) -> tuple[str, str]: ... @runtime_checkable class PromptBuilderFactory(Protocol): """per-version prompt 构建工厂。 通过注入 skills_dir 和 prompts_dir 生产对应版本的 PromptBuilderFn。 参数: skills_dir: skill 文件目录(None 则不加载 skill)。 prompts_dir: prompt 文件目录(None 则使用默认目录)。 返回: PromptBuilderFn 实例。 """ def __call__( self, *, skills_dir: Path | None = None, prompts_dir: Path | None = None, ) -> PromptBuilderFn: ... @runtime_checkable class PoolStrategy(Protocol): """池构建策略端口。 应用层端口(非 core 层),因为返回类型 Pools 定义在 app/harness/pools.py。 两个具体策略(GlobalPoolStrategy / PerCategoryPoolStrategy)实现此接口。 """ def build( self, questions: list[GeneratedQuestion], correctness: dict[str, bool], config: PoolConfig, ) -> Pools: ... def build_incremental( self, new_task_types: list[str], questions: list[GeneratedQuestion], correctness: dict[str, bool], config: PoolConfig, ) -> dict[str, dict[str, list[str]]]: ...