diff --git a/AGENTS.md b/AGENTS.md index a9f8828..64f1655 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,3 +24,10 @@ uv run python -m wov_node_echo - Python 环境统一使用 uv 管理。 - 测试必须达到 100% 行覆盖率,且只能通过调用真实代码路径覆盖。 + +## 代码注释规范 + +- 本仓库所有源码(Python、TOML 等支持注释的文件)必须配有详细中文注释,说明模块职责、核心函数的作用以及关键逻辑,确保后续维护人员可以快速理解代码工作原理。 +- 新增或修改代码时,必须同步补充或更新对应注释;不得删除已有注释。 +- 测试代码同样必须配有中文注释,说明每条测试验证的行为。 +- JSON 数据文件(`node.manifest.json`)不支持注释,字段语义以 `wov-sdk` 的 `NodeManifest` 模型注释和本文档输入/输出说明为准。 diff --git a/pyproject.toml b/pyproject.toml index a902e40..9c6eb3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,4 @@ +# WOV Echo 节点配置:使用 uv 管理环境与依赖。 [project] name = "wov-node-echo" version = "0.1.0" @@ -5,16 +6,20 @@ description = "WOV example echo node" requires-python = ">=3.11" dependencies = ["wov-sdk"] +# 本地路径依赖 wov-sdk,不发布到公共包索引。 [tool.uv.sources] wov-sdk = { path = "../wov-sdk" } +# 开发依赖:pytest 与覆盖率工具。 [dependency-groups] dev = ["pytest", "pytest-cov"] +# pytest 配置:强制 100% 行覆盖率,并允许直接 import 包源码。 [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["."] addopts = "--cov=wov_node_echo --cov-report=term-missing --cov-fail-under=100" +# 仅打包节点包本身。 [tool.setuptools] packages = ["wov_node_echo"] diff --git a/tests/test_echo_node.py b/tests/test_echo_node.py index 1120570..a45e43b 100644 --- a/tests/test_echo_node.py +++ b/tests/test_echo_node.py @@ -1,3 +1,8 @@ +"""Echo 节点测试。 + +覆盖直接文本、绝对/相对文件 URI、默认文本和入口点启动等真实代码路径。 +""" + import runpy from pathlib import Path @@ -6,6 +11,7 @@ from wov_sdk.models import InvokeRequest def test_invoke_text(tmp_path) -> None: + """验证直接传入 text 时节点原样写出文本。""" response = invoke( InvokeRequest( run_id="run_1", @@ -20,6 +26,7 @@ def test_invoke_text(tmp_path) -> None: def test_invoke_absolute_file(tmp_path) -> None: + """验证绝对路径 file_uri 的文件内容被读取为输入。""" source = tmp_path / "input.txt" source.write_text("from file", encoding="utf-8") response = invoke( @@ -35,6 +42,7 @@ def test_invoke_absolute_file(tmp_path) -> None: def test_invoke_relative_file(tmp_path, monkeypatch) -> None: + """验证相对路径 file_uri 以节点仓库根目录为基准解析。""" node_root = Path(__file__).resolve().parent.parent source = node_root / "relative_input.txt" source.write_text("relative", encoding="utf-8") @@ -55,6 +63,7 @@ def test_invoke_relative_file(tmp_path, monkeypatch) -> None: def test_invoke_default_text(tmp_path) -> None: + """验证无任何输入时节点返回默认文本。""" response = invoke( InvokeRequest( run_id="run_4", @@ -68,6 +77,7 @@ def test_invoke_default_text(tmp_path) -> None: def test_main_entrypoint(monkeypatch, tmp_path) -> None: + """验证 python -m wov_node_echo 会加载 echo manifest 并启动服务。""" module_path = Path(__file__).resolve().parent.parent / "wov_node_echo" / "__main__.py" captured = {} diff --git a/wov_node_echo/__init__.py b/wov_node_echo/__init__.py index 9bd0e76..39fcb4e 100644 --- a/wov_node_echo/__init__.py +++ b/wov_node_echo/__init__.py @@ -1 +1,5 @@ -"""WOV example echo node.""" +"""WOV Echo 示例节点。 + +该节点用于验证节点协议、独立 Git 仓库管理和生命周期回收机制。它把输入文本 +原样写入产物文件,作为最简的端到端链路验证。 +""" diff --git a/wov_node_echo/__main__.py b/wov_node_echo/__main__.py index eaa971f..c55c14d 100644 --- a/wov_node_echo/__main__.py +++ b/wov_node_echo/__main__.py @@ -1,3 +1,9 @@ +"""Echo 节点入口。 + +通过 wov_sdk.server.run_node 启动标准节点 HTTP 服务,并注册 invoke 处理器。 +节点启动后打印 WOV_NODE_READY 行,等待 wov-api 的 NodeManager 调用。 +""" + from __future__ import annotations import json @@ -8,10 +14,13 @@ from wov_sdk.server import run_node def _resolve_input_text(request: InvokeRequest, node_root: Path) -> str: + """按优先级解析输入文本:直接文本 > 文件 URI > 默认字符串。""" + # 优先使用请求中直接携带的 text 字段。 text = request.inputs.get("text") if text is not None: return str(text) + # 其次读取 file_uri 指向的文件;相对路径以节点仓库根目录为基准。 file_uri = request.inputs.get("file_uri") if file_uri: path = Path(file_uri) @@ -19,15 +28,19 @@ def _resolve_input_text(request: InvokeRequest, node_root: Path) -> str: path = node_root / path return path.read_text(encoding="utf-8") + # 都没有时返回固定文本,保证节点总有可演示的输出。 return "echo" def invoke(request: InvokeRequest) -> InvokeResponse: + """处理节点调用:把解析出的文本写入产物并返回 URI。""" + # 节点仓库根目录用于解析相对文件路径。 node_root = Path(__file__).resolve().parent.parent output_dir = Path(request.output_dir) output_dir.mkdir(parents=True, exist_ok=True) text = _resolve_input_text(request, node_root) + # 产物必须落在请求给定的 output_dir,调度器按 run 与节点组织目录。 output_path = output_dir / "echo.txt" output_path.write_text(text, encoding="utf-8") @@ -41,6 +54,7 @@ def invoke(request: InvokeRequest) -> InvokeResponse: def main() -> None: + """加载节点清单并以本模块的 invoke 处理器启动服务。""" manifest_path = Path(__file__).resolve().parent.parent / "node.manifest.json" with open(manifest_path, "r", encoding="utf-8") as f: manifest = NodeManifest.from_dict(json.load(f))