From 94b19b80d97b54775da468b4816dc82c6bca2411 Mon Sep 17 00:00:00 2001 From: chamsechan Date: Mon, 5 Oct 2026 13:32:28 +0800 Subject: [PATCH] chore(comments): translate English code comments into concise Chinese --- .codex/config.toml | 6 +- .github/actions/ccache-restore/action.yml | 4 +- .github/actions/ccache-save/action.yml | 3 +- .github/actions/setup-build-env/action.yml | 2 +- .github/workflows/ci.yml | 27 ++- .github/workflows/pr-cache-cleanup.yml | 4 +- .gitignore | 2 +- CMakeLists.txt | 11 +- cmake_ext/BuildAcceleration.cmake | 2 +- cmake_ext/KiteLlm.cmake | 14 +- cmake_ext/LayerHeaderViews.cmake | 16 +- cmake_ext/Pcre2.cmake | 13 +- cmake_ext/StbImage.cmake | 2 +- cmake_ext/ThirdPartyCacheMetadata.cmake | 8 +- cmake_ext/ThirdPartyEngines.cmake | 7 +- demo/common/demo_options.h | 16 +- demo/common/demo_profile.py | 2 +- demo/common/demo_profile_defaults.h | 2 +- demo/json_prompt_demo.py | 12 +- dev_support/CMakeLists.txt | 4 +- dev_support/benchmarks/control_snapshots.cpp | 7 +- dev_support/benchmarks/control_snapshots.py | 4 +- .../benchmarks/traceable_batch_operations.cpp | 6 +- .../inference/test_causal_lm_backend.cpp | 4 +- .../inference/test_causal_lm_backend.h | 4 +- .../node_authoring/benchmark/CMakeLists.txt | 2 +- dev_support/node_authoring/benchmark/run.py | 4 +- dev_support/node_authoring/legacy_node_base.h | 7 +- .../starter_batch_group_node.cpp | 2 +- .../node_authoring/starter_batch_node.cpp | 2 +- .../starter_batch_select_scatter_node.cpp | 12 +- .../node_authoring/starter_control_node.cpp | 4 +- .../node_authoring/starter_llm_node.cpp | 5 +- include/adapter/biz_blackboard_keys.h | 7 +- include/adapter/biz_input_constraints.h | 5 +- include/adapter/converter_authoring.h | 43 ++--- include/adapter/io_binding.h | 11 +- include/adapter/io_binding_registry.h | 15 +- include/adapter/io_converter.h | 16 +- include/adapter/operator_io_contracts.h | 6 +- include/adapter/operator_output_config.h | 11 +- include/adapter/operator_value_type.h | 37 ++-- include/adapter/result_validation.h | 4 +- include/contracts/config_schema.h | 9 +- include/contracts/config_schema_validation.h | 4 +- include/contracts/control_payload.h | 13 +- include/contracts/diagnostic.h | 4 +- include/contracts/json_structure.h | 5 +- include/contracts/path_utils.h | 8 +- include/contracts/registry_conflicts.h | 6 +- include/core/node_definition.h | 4 +- include/core/node_interface.h | 2 +- include/core/pipeline.h | 11 +- include/core/pipeline_validator.h | 9 +- include/core/port_definition.h | 2 +- include/core/session_context.h | 2 +- include/core/thread_pool.h | 4 +- include/edgeflow/log.h | 16 +- include/edgeflow/operator/types.h | 2 +- include/engine/backend_identity.h | 9 +- include/engine/backend_interface.h | 24 ++- include/engine/fixed_batch_executor.h | 7 +- include/engine/inference_definition.h | 8 +- include/engine/model_identity.h | 11 +- include/engine/model_interface.h | 9 +- include/engine/tensor.h | 3 +- include/nodes/configuration_snapshot.h | 21 +-- include/nodes/function_node.h | 41 ++--- include/nodes/generate_options_config.h | 4 +- include/nodes/model_calls.h | 4 +- include/nodes/node_config_parser.h | 16 +- include/nodes/node_error_codes.h | 2 +- include/nodes/node_result.h | 7 +- include/nodes/parameter_binding.h | 10 +- include/nodes/session_resources.h | 9 +- include/nodes/text_template.h | 5 +- include/nodes/traceable_batch_operations.h | 11 +- include/nodes/traceable_batch_validation.h | 6 +- include/platform_mock/error_codes.h | 4 +- include/platform_mock/operator_data_types.h | 4 +- include/platform_mock/operator_types.h | 6 +- scripts/check_doc_links.py | 12 +- scripts/check_layer_dependencies.py | 14 +- scripts/check_layer_isolation.sh | 52 +++--- scripts/configure_build.sh | 2 +- scripts/fetch_real_test_models.sh | 2 +- scripts/generate_test_onnx_model.py | 14 +- scripts/render_architecture_diagrams.sh | 12 +- scripts/run_all_tests.sh | 6 +- scripts/run_sanitizers.sh | 9 +- scripts/ubsan_suppressions.txt | 4 +- src/adapter/CMakeLists.txt | 3 +- src/adapter/deployment_model_resolver.h | 9 +- src/adapter/deployment_preparation.cpp | 5 +- src/adapter/deployment_preparation.h | 5 +- src/adapter/deployment_structure.h | 2 +- src/adapter/input/rerank_input.cpp | 5 +- src/adapter/io_binding_resolver.h | 2 +- src/adapter/io_catalog.cpp | 2 +- src/adapter/io_converter_registry.cpp | 4 +- .../operator/json_output_config_reader.h | 4 +- .../operator/operator_config_resolver.h | 4 +- .../operator/operator_error_mapping.cpp | 5 +- src/adapter/operator/operator_error_mapping.h | 17 +- src/adapter/operator/operator_output_pool.h | 2 +- src/adapter/output/rule_match_response.h | 4 +- src/adapter/shared_algorithm_runtime.h | 1 - src/cli/CMakeLists.txt | 2 +- src/cli/alg_pipeline_tool.cpp | 5 +- src/cli/pipeline_authoring.cpp | 15 +- src/cli/pipeline_authoring.h | 19 +- src/cli/pipeline_json_schema.h | 4 +- src/cli/pipeline_remediation.cpp | 4 +- src/cli/pipeline_remediation.h | 11 +- src/common_nodes/CMakeLists.txt | 2 +- .../structured_json_parse_node.cpp | 7 +- .../support/compiled_text_regex.h | 2 +- src/common_nodes/text_embedding_node.cpp | 4 +- src/common_nodes/text_rule_match_node.cpp | 2 +- src/common_nodes/text_template_node.cpp | 3 +- src/core/name_suggestions.h | 4 +- src/core/pipeline.cpp | 12 +- src/core/pipeline_config.cpp | 8 +- src/core/pipeline_config_structure.h | 2 +- src/core/pipeline_validator.cpp | 42 ++--- src/custom_nodes/CMakeLists.txt | 4 +- src/custom_nodes/prompt_guided_llm_node.cpp | 12 +- .../backends/kite_llm/kite_llm_backend.cpp | 9 +- .../backends/llama_cpp/llama_cpp_backend.h | 7 +- .../onnxruntime/onnxruntime_backend.h | 4 +- .../whisper_cpp/whisper_cpp_backend.cpp | 4 +- .../bge_embedding/bge_embedding_model.cpp | 2 +- .../bge_reranker/bge_reranker_model.cpp | 2 +- .../models/common/embedding_numeric_support.h | 16 +- .../generated_text_embedding_model.h | 5 +- .../qwen_causal_lm/qwen_causal_lm_model.h | 7 +- .../vision_document/vision_document_model.cpp | 2 +- .../models/whisper_asr/whisper_asr_model.cpp | 2 +- src/engine/text/utf8.h | 10 +- .../common_autoregressive_generator.h | 8 +- tests/CMakeLists.txt | 13 +- tests/RuntimeTests.cmake | 26 ++- tests/ScaffoldFixtures.cmake | 4 +- .../abi/test_adapter_contract_security.cpp | 68 ++++--- tests/contract/abi/test_cpp_operator_sdk.cpp | 25 ++- tests/contract/abi/test_operator_safety.cpp | 14 +- .../architecture/test_diagram_render_gate.sh | 6 +- .../test_layer_header_views.cmake | 16 +- .../architecture/test_llama_cache.cmake | 10 +- .../test_quality_gate_contract.py | 14 +- .../architecture/test_test_labels_contract.py | 72 ++++---- .../test_third_party_cache_metadata.cmake | 6 +- .../test_spec_signature_diagnostics.cmake | 6 +- .../catalog/test_catalog_contract_ssot.cpp | 2 +- .../catalog/test_registry_conflict.cpp | 4 +- tests/integration/demo/test_demo_runner.cpp | 18 +- .../operator/test_operator_api.cpp | 79 ++++---- .../operator/test_operator_golden.cpp | 21 +-- .../pipeline/test_model_backend_pipeline.cpp | 68 ++++--- .../test_pipeline_catalog_validator.cpp | 45 ++--- .../test_runtime_control_and_hot_swap.cpp | 4 +- .../adapter_examples/tagged_union_adapter.h | 2 +- tests/support/control_test_utils.h | 2 +- tests/support/model_registration.h | 3 +- tests/support/node_process_pause.h | 5 +- tests/support/node_test_utils.h | 3 +- .../support/operator_nested_output_fixture.h | 6 +- tests/support/operator_test_fixture.h | 6 +- tests/support/registry_test_access.h | 4 +- tests/support/scoped_allocation_failure.cpp | 2 +- tests/support/scoped_allocation_failure.h | 20 +- tests/tooling/generate_scaffold_fixtures.py | 10 +- tests/tooling/studio_browser_test.mjs | 22 +-- tests/tooling/studio_editor_test.mjs | 4 +- tests/tooling/studio_fix_workflow_test.mjs | 16 +- tests/tooling/studio_graph_test.mjs | 6 +- tests/tooling/test_dev_recipe.py | 6 +- tests/tooling/test_json_prompt_demo.py | 10 +- tests/tooling/test_pipeline_studio.py | 152 ++++++++------- tests/tooling/test_scaffold_custom_node.py | 26 +-- tests/unit/adapter/test_adapter_purity.cpp | 95 +++++----- .../unit/adapter/test_complex_converters.cpp | 18 +- .../unit/adapter/test_io_binding_registry.cpp | 68 ++++--- tests/unit/adapter/test_io_converters.cpp | 25 ++- tests/unit/adapter/test_text_converters.cpp | 12 +- tests/unit/core/test_dag_pipeline.cpp | 6 +- .../test_definition_schema_validation.cpp | 51 +++--- tests/unit/core/test_framework_core.cpp | 8 +- tests/unit/core/test_node_base_contracts.cpp | 18 +- .../core/test_node_ownership_and_reuse.cpp | 7 +- tests/unit/core/test_pipeline_config.cpp | 8 +- tests/unit/core/test_registry_reentrant.cpp | 48 +++-- .../core/test_validated_pipeline_plan.cpp | 17 +- tests/unit/engine/test_batch_executor.cpp | 2 +- ...t_engine_fault_tolerance_and_lifecycle.cpp | 10 +- tests/unit/engine/test_llama_cpp_backend.cpp | 14 +- .../engine/test_model_backend_decoupling.cpp | 59 +++--- .../engine/test_onnx_and_embedding_model.cpp | 25 ++- .../engine/test_onnx_and_reranker_model.cpp | 16 +- .../unit/engine/test_whisper_cpp_backend.cpp | 29 ++- tests/unit/nodes/test_asr_transcribe_node.cpp | 4 +- tests/unit/nodes/test_common_nodes.cpp | 107 ++++++----- tests/unit/nodes/test_function_node.cpp | 168 +++++++++-------- tests/unit/nodes/test_llm_generate_node.cpp | 4 +- tests/unit/nodes/test_ocr_detect_node.cpp | 2 - tests/unit/nodes/test_parameter_binding.cpp | 16 +- .../nodes/test_structured_json_parse_node.cpp | 9 +- tests/unit/nodes/test_text_chunk_node.cpp | 36 ++-- .../nodes/test_text_corpus_source_node.cpp | 2 - tests/unit/nodes/test_text_embedding_node.cpp | 27 +-- tests/unit/nodes/test_text_rerank_node.cpp | 18 +- .../unit/nodes/test_text_rule_match_node.cpp | 17 +- tests/unit/nodes/test_text_template_node.cpp | 23 +-- .../nodes/test_traceable_batch_operations.cpp | 173 +++++++++--------- tests/unit/nodes/test_vector_top_k_node.cpp | 3 - .../operator/test_operator_output_pool.cpp | 18 +- .../operator/test_operator_value_registry.cpp | 30 ++- tools/dev_recipe.py | 12 +- tools/pipeline_studio/server.py | 18 +- tools/pipeline_studio/web/api.js | 2 +- tools/pipeline_studio/web/app.js | 10 +- tools/pipeline_studio/web/editor.js | 16 +- tools/pipeline_studio/web/graph.js | 14 +- tools/pipeline_studio/web/workbench.js | 4 +- tools/pipeline_studio/web/workflow.js | 4 +- tools/scaffold_custom_node.py | 51 +++--- tools/verify_selection.py | 10 +- 227 files changed, 1550 insertions(+), 1750 deletions(-) diff --git a/.codex/config.toml b/.codex/config.toml index 53e1fc26..a53d0f55 100644 --- a/.codex/config.toml +++ b/.codex/config.toml @@ -1,6 +1,6 @@ -# Codex-only runtime and model mapping. -# Shared roles and delegation-context policy are defined once in ../AGENTS.md. -# Keep this file compatible with Codex CLI 0.144.0-alpha.4 strict config. +# 仅供 Codex 使用的运行时与模型映射。 +# 共享角色与委派上下文策略统一定义在 ../AGENTS.md。 +# 本文件须兼容 Codex CLI 0.144.0-alpha.4 的严格配置校验。 [features] multi_agent = true diff --git a/.github/actions/ccache-restore/action.yml b/.github/actions/ccache-restore/action.yml index a1343af9..8ef3f8f3 100644 --- a/.github/actions/ccache-restore/action.yml +++ b/.github/actions/ccache-restore/action.yml @@ -24,8 +24,8 @@ runs: key: ${{ inputs.key }} restore-keys: ${{ inputs.restore-keys }} - # Restored files keep their saved mtimes; ccache refreshes an entry's mtime on every hit - # and writes misses as new files, so anything newer than this mark is the job's working set. + # 恢复的文件保留保存时的 mtime;ccache 每次命中都会刷新条目的 mtime,未命中则写入新文件, + # 因此比此标记更新的文件即为本作业的工作集。 - name: Mark ccache working-set start shell: bash run: echo "CCACHE_WORKING_SET_START=$(date +%s)" >> "${GITHUB_ENV}" diff --git a/.github/actions/ccache-save/action.yml b/.github/actions/ccache-save/action.yml index 94243f14..73d8196c 100644 --- a/.github/actions/ccache-save/action.yml +++ b/.github/actions/ccache-save/action.yml @@ -53,8 +53,7 @@ runs: echo "::warning::Cannot list Actions caches; skipped pruning." exit 0 fi - # Never prune unless the snapshot just saved is visible; otherwise the ref would lose - # its only warm cache. + # 仅当刚保存的快照可见时才清理,否则该 ref 会失去唯一的热缓存。 if ! awk -v key="${CACHE_KEY}" '$2 == key { found = 1 } END { exit !found }' <<< "${entries}"; then echo "::notice::Saved snapshot ${CACHE_KEY} is not listed yet; skipped pruning." exit 0 diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index 57d3eb2d..34f6f6d5 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -34,7 +34,7 @@ runs: echo "All requested build tools are preinstalled." else echo "Installing: ${missing[*]}" - # Package triggers rebuilding the man database add minutes on hosted runners. + # 安装软件包会触发重建 man 数据库,在托管 runner 上要多花几分钟。 sudo rm -f /var/lib/man-db/auto-update sudo apt-get -o Acquire::Retries=3 update sudo apt-get -o Acquire::Retries=3 install -y --no-install-recommends "${missing[@]}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee8be336..72aad193 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: name: Build & GTest Regression Suite (CMake 3.19) runs-on: ubuntu-latest timeout-minutes: 45 - # actions: write lets ccache-save prune superseded snapshots for this ref. + # actions: write 允许 ccache-save 清理本 ref 已被取代的快照。 permissions: contents: read actions: write @@ -35,7 +35,7 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 - # CMake comes from the pinned 3.19 archive below; Java only needs to run PlantUML. + # CMake 来自下方固定的 3.19 归档;Java 仅用于运行 PlantUML。 - name: Set up Build Environment uses: ./.github/actions/setup-build-env with: @@ -94,7 +94,7 @@ jobs: name: Production Backends ASan & UBSan runs-on: ubuntu-latest timeout-minutes: 60 - # actions: write lets ccache-save prune superseded snapshots for this ref. + # actions: write 允许 ccache-save 清理本 ref 已被取代的快照。 permissions: contents: read actions: write @@ -113,7 +113,7 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 - # sanitizer-runtime excludes the tooling tests that need Java. + # sanitizer-runtime 不包含需要 Java 的工具测试。 - name: Set up Build Environment uses: ./.github/actions/setup-build-env with: @@ -134,7 +134,7 @@ jobs: uses: ./.github/actions/ccache-restore with: path: ${{ env.CCACHE_DIR }} - # Restore the latest accessible snapshot; save a new one scoped to ref and commit sha + # 恢复最新的可访问快照;保存按 ref 和 commit sha 划分的新快照 key: sanitizer-ccache-v2-${{ runner.os }}-${{ env.TOOLCHAIN_ID }}-${{ github.ref }}-${{ github.sha }} restore-keys: | sanitizer-ccache-v2-${{ runner.os }}-${{ env.TOOLCHAIN_ID }}-${{ github.ref }}- @@ -158,7 +158,7 @@ jobs: name: Real GGUF Operator & Profile runs-on: ubuntu-latest timeout-minutes: 30 - # actions: write lets ccache-save prune superseded snapshots for this ref. + # actions: write 允许 ccache-save 清理本 ref 已被取代的快照。 permissions: contents: read actions: write @@ -196,8 +196,7 @@ jobs: restore-keys: | 3rdparty-release-v1-${{ runner.os }}-${{ env.TOOLCHAIN_ID }}- - # Fall back to this job's main snapshot before the default gate's differently - # configured cache. + # 先回退到本作业的 main 快照,再回退到默认门禁那份配置不同的缓存。 - name: Restore CCache uses: ./.github/actions/ccache-restore with: @@ -233,7 +232,7 @@ jobs: name: Whisper ASR Backend & Real Profile runs-on: ubuntu-latest timeout-minutes: 30 - # actions: write lets ccache-save prune superseded snapshots for this ref. + # actions: write 允许 ccache-save 清理本 ref 已被取代的快照。 permissions: contents: read actions: write @@ -308,15 +307,15 @@ jobs: kite-llm: name: kiteLLM Private Release & Real GGUF - # Fork PRs and Dependabot do not receive repository secrets. Keep this job - # on trusted events; do not switch it to pull_request_target. + # Fork PR 和 Dependabot 拿不到仓库 secrets。本作业只在可信事件上运行, + # 不要改为 pull_request_target。 if: >- github.actor != 'dependabot[bot]' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) runs-on: ubuntu-24.04 timeout-minutes: 20 - # actions: write lets ccache-save prune superseded snapshots for this ref. + # actions: write 允许 ccache-save 清理本 ref 已被取代的快照。 permissions: contents: read actions: write @@ -364,8 +363,8 @@ jobs: kite-ccache-${{ runner.os }}-${{ github.ref }}- kite-ccache-${{ runner.os }}- - # Only the public model artifacts are cached. Private kiteLLM headers, libraries and - # linked executables must not enter this public repository's caches/artifacts. + # 只缓存公开的模型文件。私有 kiteLLM 头文件、库和链接后的可执行文件 + # 不得进入本公开仓库的缓存或制品。 - name: Cache Pinned Kite Demo Models uses: actions/cache@v4 with: diff --git a/.github/workflows/pr-cache-cleanup.yml b/.github/workflows/pr-cache-cleanup.yml index dfa6eece..d5ec4da1 100644 --- a/.github/workflows/pr-cache-cleanup.yml +++ b/.github/workflows/pr-cache-cleanup.yml @@ -1,7 +1,7 @@ name: PR Cache Cleanup -# Pull request caches are only reachable from that pull request. Delete them once it closes so -# they do not push main snapshots out of the repository's Actions cache quota. +# PR 缓存只能被该 PR 访问。PR 关闭后即删除, +# 避免占用仓库 Actions 缓存配额而把 main 快照挤出去。 on: pull_request: types: [ closed ] diff --git a/.gitignore b/.gitignore index 2403e42b..e061a037 100644 --- a/.gitignore +++ b/.gitignore @@ -68,7 +68,7 @@ models/*_vocab.txt # 4. 测试报告与运行日志 (Test Reports, GTest Outputs & Logs) # ------------------------------------------------------------------------------ results/ -# Demo run outputs; anchored so src/adapter/output/ converters stay tracked. +# Demo 运行输出;以 / 锚定,确保 src/adapter/output/ 下的 Converter 仍被跟踪。 /output/ *.log *.tlog diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bd46ee7..6ccba630 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ target_include_directories(edgeflow_public_headers INTERFACE $ $) -# Source extensions use authoring contracts; they are not the dynamic SDK ABI. +# 源码扩展使用编写契约,不属于动态 SDK ABI。 add_library(edgeflow_extension_headers INTERFACE) target_include_directories(edgeflow_extension_headers INTERFACE "${PROJECT_SOURCE_DIR}/include") @@ -114,9 +114,8 @@ include(cmake_ext/Pcre2.cmake) # 引入第三方开源推理引擎 (ONNX Runtime & llama.cpp) 自动下载配置 include(cmake_ext/ThirdPartyEngines.cmake) -# Keep repository executables at build/ even though their CMake ownership is -# delegated to source subdirectories. Public commands and CI scripts rely on -# this stable artifact layout. +# 即使 CMake 归属已下放到各源码子目录,仓库可执行文件仍统一输出到 build/。 +# 公开命令和 CI 脚本依赖这一稳定的产物布局。 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") # 轻量运行时契约位于所有实现层下方;各层依赖 interface 只向下传播。 @@ -166,8 +165,8 @@ endfunction() edgeflow_add_runtime_object_target(edgeflow_model_execution_objects edgeflow_model_execution_dependencies) -# Vendor compilation stays inside the Backend implementations, including -# headers and definitions carried by imported runtime targets. +# 厂商编译只发生在 Backend 实现内部, +# 包括导入运行时目标携带的头文件和编译定义。 edgeflow_add_runtime_object_target(edgeflow_model_execution_backends_objects edgeflow_model_execution_dependencies) target_include_directories(edgeflow_model_execution_backends_objects PRIVATE diff --git a/cmake_ext/BuildAcceleration.cmake b/cmake_ext/BuildAcceleration.cmake index 83dd9682..77f10a8f 100644 --- a/cmake_ext/BuildAcceleration.cmake +++ b/cmake_ext/BuildAcceleration.cmake @@ -1,4 +1,4 @@ -# Developer build acceleration options shared by first-party and fetched targets. +# 第一方目标与拉取的依赖目标共用的开发构建加速选项。 set(LLM_EDGEFLOW_LINKER "auto" CACHE STRING "Linker selection: auto, mold, lld, or system") diff --git a/cmake_ext/KiteLlm.cmake b/cmake_ext/KiteLlm.cmake index ef9267cc..5e571741 100644 --- a/cmake_ext/KiteLlm.cmake +++ b/cmake_ext/KiteLlm.cmake @@ -1,5 +1,5 @@ -# User-authorized private GitHub dependency. Authentication remains in gh's -# credential store or GH_TOKEN; never materialize tokens in CMake/cache files. +# 经用户授权的私有 GitHub 依赖。认证信息保留在 gh 凭据存储或 GH_TOKEN 中; +# 绝不能把 token 写入 CMake 文件或缓存。 option(ENABLE_KITELLM "Download and link the pinned kiteLLM static release" OFF) set(LLM_EDGEFLOW_HAS_KITELLM OFF) if(NOT ENABLE_KITELLM) @@ -29,7 +29,7 @@ set(_kite_asset "kiteLLM-${_kite_platform}.tar.gz") set(_kite_cache "${LLM_EDGEFLOW_3RDPARTY_DIR}/kite_llm/${_kite_release}/${_kite_platform}") set(_kite_archive "${_kite_cache}/${_kite_asset}") file(MAKE_DIRECTORY "${_kite_cache}") -# Separate build directories share this archive cache. +# 不同构建目录共享此归档缓存。 file(LOCK "${_kite_cache}/.download.lock" GUARD PROCESS TIMEOUT 60) if(NOT EXISTS "${_kite_archive}") find_program(KITELLM_GH_EXECUTABLE NAMES gh) @@ -39,8 +39,8 @@ if(NOT EXISTS "${_kite_archive}") "with repository read access (CI: set GH_TOKEN).") endif() message(STATUS "[kiteLLM] Downloading chamsechan/kiteLLM ${_kite_release}/${_kite_asset}") - # Download into a staging directory so interrupted downloads never become a - # reusable archive. The checksum below is authoritative even if a tag moves. + # 先下载到暂存目录,避免中断的下载变成可复用的归档。 + # 即使 tag 被移动,也以下方校验和为准。 set(_kite_download "${_kite_cache}/download") file(MAKE_DIRECTORY "${_kite_download}") execute_process( @@ -66,8 +66,8 @@ if(NOT _kite_actual_sha256 STREQUAL _kite_sha256) "Cached kiteLLM SHA-256 mismatch. Remove ${_kite_archive} and configure again.") endif() -# Re-extract only verified bytes into this build, avoiding stale/tampered headers -# or libraries and machine-specific absolute paths from previous environments. +# 只把已校验的字节重新解压到本构建,避免残留或被篡改的头文件/库, +# 以及旧环境遗留的机器相关绝对路径。 set(_kite_extract "${CMAKE_BINARY_DIR}/_deps/kite_llm_release") file(MAKE_DIRECTORY "${_kite_extract}") execute_process(COMMAND "${CMAKE_COMMAND}" -E tar xzf "${_kite_archive}" diff --git a/cmake_ext/LayerHeaderViews.cmake b/cmake_ext/LayerHeaderViews.cmake index 1d906fcd..48d68d86 100644 --- a/cmake_ext/LayerHeaderViews.cmake +++ b/cmake_ext/LayerHeaderViews.cmake @@ -1,8 +1,8 @@ -# Keep conventional include spellings while withholding other layers' headers -# from compiler search paths. LayerGuard still checks relative/absolute includes. +# 保留常规的 include 写法,同时不把其他层的头文件放进编译器搜索路径。 +# LayerGuard 仍会检查相对/绝对路径 include。 function(edgeflow_header_view view_name) set(view_dir "${PROJECT_BINARY_DIR}/layer_includes/${view_name}") - # Reconfiguration after a header removal must not retain a stale visible file. + # 删除头文件后重新配置时,不得残留过期的可见文件。 file(REMOVE_RECURSE "${view_dir}") file(MAKE_DIRECTORY "${view_dir}") foreach(header IN LISTS ARGN) @@ -15,8 +15,8 @@ function(edgeflow_header_view view_name) file(CREATE_LINK "${PROJECT_SOURCE_DIR}/${header}" "${view_dir}/${include_path}" SYMBOLIC RESULT link_result) if(NOT link_result STREQUAL "0") - # Platforms without symlink support track copies as configure dependencies; - # edits then trigger CMake before the next incremental compilation. + # 不支持符号链接的平台把副本登记为配置依赖; + # 编辑后会在下次增量编译前触发 CMake。 configure_file("${PROJECT_SOURCE_DIR}/${header}" "${view_dir}/${include_path}" COPYONLY) endif() @@ -52,7 +52,7 @@ edgeflow_collect_headers(model_api_headers "${PROJECT_SOURCE_DIR}/include/engine edgeflow_collect_headers(model_private_headers "${PROJECT_SOURCE_DIR}/src/engine") edgeflow_header_view(model_execution ${model_api_headers} ${model_private_headers}) -# Shared Node authoring contracts have one manifest for CMake and LayerGuard. +# 共享的 Node 编写契约只有一份清单,供 CMake 和 LayerGuard 共用。 set(node_contract_manifest "${PROJECT_SOURCE_DIR}/cmake_ext/node_core_contracts.txt") set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${node_contract_manifest}") file(STRINGS "${node_contract_manifest}" node_core_contracts) @@ -74,8 +74,8 @@ edgeflow_collect_headers(integration_headers "${PROJECT_SOURCE_DIR}/include/adap edgeflow_header_view(integration ${integration_headers} ${core_headers} ${model_api_headers} ${platform_mock_headers}) -# Capture the actual evaluated target include paths, including transitive usage -# requirements, so the existing LayerGuard gate detects accidental broadening. +# 记录目标实际求值后的 include 路径 (含传递的使用要求), +# 以便现有 LayerGuard 门禁发现意外放宽。 function(edgeflow_generate_layer_compile_manifest) set(content "set(layer_cxx [==[${CMAKE_CXX_COMPILER}]==])\n") set(layer_cxx_flags "") diff --git a/cmake_ext/Pcre2.cmake b/cmake_ext/Pcre2.cmake index fb4fe4a7..013d13d6 100644 --- a/cmake_ext/Pcre2.cmake +++ b/cmake_ext/Pcre2.cmake @@ -39,8 +39,8 @@ if(_PCRE2_CACHE_VALID AND else() include(FetchContent) - # TextRuleMatchNode uses PCRE2 for Unicode-aware lookbehind and named captures. - # Keep the dependency private, source-built, pinned, and checksum-verified. + # TextRuleMatchNode 使用 PCRE2 实现 Unicode 感知的后行断言和命名捕获。 + # 该依赖须保持私有、源码构建、版本固定并经校验和验证。 set(PCRE2_BUILD_PCRE2_8 ON CACHE BOOL "Build PCRE2 8-bit library" FORCE) set(PCRE2_BUILD_PCRE2_16 OFF CACHE BOOL "Disable PCRE2 16-bit library" FORCE) set(PCRE2_BUILD_PCRE2_32 OFF CACHE BOOL "Disable PCRE2 32-bit library" FORCE) @@ -60,8 +60,8 @@ else() FetchContent_MakeAvailable(pcre2) - # PCRE2 is an implementation detail of alg_sdk. Do not leak its C symbols from - # the public shared-library surface when statically linking on ELF/Mach-O. + # PCRE2 是 alg_sdk 的实现细节。在 ELF/Mach-O 上静态链接时, + # 不得从公开共享库导出其 C 符号。 if(TARGET pcre2-8-static AND CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") target_compile_options(pcre2-8-static PRIVATE -fvisibility=hidden) endif() @@ -69,9 +69,8 @@ else() if(NOT TARGET edgeflow_pcre2) add_library(edgeflow_pcre2 INTERFACE) if(APPLE) - # Apple ld can mark every symbol pulled from a static archive as private. - # Link the archive by name so -hidden-l applies to the complete PCRE2 object - # set instead of exporting it through company_alg_sdk.dylib. + # Apple ld 可能把从静态归档拉入的符号全部设为私有。按名称链接该归档, + # 让 -hidden-l 作用于整个 PCRE2 目标文件集,而不是经由 company_alg_sdk.dylib 导出。 add_dependencies(edgeflow_pcre2 pcre2-8-static) target_include_directories( edgeflow_pcre2 SYSTEM INTERFACE "${pcre2_BINARY_DIR}/interface") diff --git a/cmake_ext/StbImage.cmake b/cmake_ext/StbImage.cmake index 7e7ecba6..d8d02de9 100644 --- a/cmake_ext/StbImage.cmake +++ b/cmake_ext/StbImage.cmake @@ -1,4 +1,4 @@ -# Pinned, verified image decoder. No third-party source is committed. +# 版本固定且经校验的图像解码器。不提交任何第三方源码。 set(_stb_commit "013ac3beddff3dbffafd5177e7972067cd2b5083") set(_stb_sha "594c2fe35d49488b4382dbfaec8f98366defca819d916ac95becf3e75f4200b3") set(_stb_dir "${CMAKE_SOURCE_DIR}/3rdparty/stb_image/${_stb_commit}") diff --git a/cmake_ext/ThirdPartyCacheMetadata.cmake b/cmake_ext/ThirdPartyCacheMetadata.cmake index 4d8ef578..3d9eebee 100644 --- a/cmake_ext/ThirdPartyCacheMetadata.cmake +++ b/cmake_ext/ThirdPartyCacheMetadata.cmake @@ -2,15 +2,15 @@ include_guard(GLOBAL) include(CMakeParseArguments) -# Older CMake already preserves archive timestamps; the explicit switch was -# introduced in 3.24. Keep the same extraction behavior on every supported version. +# 较旧的 CMake 本就保留归档时间戳;显式开关在 3.24 才引入。 +# 在所有支持的版本上保持相同的解压行为。 set(EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24") set(EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS DOWNLOAD_EXTRACT_TIMESTAMP TRUE) endif() -# Prepare an inspectable cache marker and report whether an existing cache was -# produced for the same pinned source and ABI-relevant build environment. +# 准备可检查的缓存标记,并报告现有缓存是否由相同的固定源码 +# 和 ABI 相关构建环境生成。 function(edgeflow_prepare_third_party_cache) set(_one_value_args NAME VERSION SOURCE_SHA256 CACHE_DIR KIND MARKER_ROOT OUT_VALID OUT_MARKER) diff --git a/cmake_ext/ThirdPartyEngines.cmake b/cmake_ext/ThirdPartyEngines.cmake index da0193bd..f6414db7 100644 --- a/cmake_ext/ThirdPartyEngines.cmake +++ b/cmake_ext/ThirdPartyEngines.cmake @@ -23,8 +23,7 @@ if(ENABLE_ONNXRUNTIME) set(ORT_LIB_NAME "libonnxruntime.so") endif() - # Select the pinned official package before inspecting the persistent cache, - # so the cache marker is tied to the exact platform archive. + # 先选定固定的官方包,再检查持久缓存,使缓存标记与具体平台归档对应。 if(APPLE) if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64") set(ORT_URL "https://github.com/microsoft/onnxruntime/releases/download/v1.17.3/onnxruntime-osx-arm64-1.17.3.tgz") @@ -113,8 +112,8 @@ if(ENABLE_LLAMACPP) "70adb1b4cea5ee39f867792c78dc59320921eda7") set(_LLAMA_SOURCE_SHA256 "94d215f1fd85ded40f4674eccdbd3caf4a9b0daa00b6d72255efec922c6d94a4") - # Resolve the pinned upstream BLAS defaults before inspecting the cache. - # These options must describe both source builds and imported archives. + # 先解析固定上游版本的 BLAS 默认值,再检查缓存。 + # 这些选项须同时描述源码构建和导入的归档。 if(APPLE) set(_llama_blas_default ON) set(_llama_blas_vendor_default Apple) diff --git a/demo/common/demo_options.h b/demo/common/demo_options.h index 4fd5f7fd..c2db68de 100644 --- a/demo/common/demo_options.h +++ b/demo/common/demo_options.h @@ -13,10 +13,10 @@ namespace alg_demo { /** - * @brief Apply the optional LLMEDGEFLOW_LEVEL environment setting. + * @brief 应用可选的 LLMEDGEFLOW_LEVEL 环境变量。 * - * Valid decimal values are 0 (Fatal) through 5 (Verbose). Missing or invalid - * values silently preserve the current process-wide log level. + * 合法值为十进制 0 (Fatal) 到 5 (Verbose)。缺失或非法时静默保留当前的 + * 进程级日志级别。 */ void ConfigureLogLevelFromEnvironment() noexcept; @@ -24,14 +24,14 @@ void ConfigureLogLevelFromEnvironment() noexcept; * @brief Demo 运行参数对象 (由命令行参数、Profile 配置与默认安全值合并而成) */ struct DemoOptions { - std::string profiles_file; // Optional deployment-specific profile document + std::string profiles_file; // 可选的部署专属 Profile 文档 std::string profile; // 预定义运行配置 Profile 标识 std::string biz; // SDK 从所选配置解析出的业务身份 std::string config_path; // Operator .conf 路径 std::string dataset_path; // 业务测试集文件路径 std::string output_dir = "./results"; // 结果输出根目录 - // Execution settings are configured only by Profile JSON (or defaults). + // 执行参数只能由 Profile JSON 配置 (或使用默认值)。 int batch_size = alg_demo::kDemoBatchSize; // 最大批大小 (支持按批分块分发) int device_id = alg_demo::kDemoDeviceId; // 设备 ID std::string chip = alg_demo::kDemoChip; // 计算平台芯片类型字符串 @@ -41,7 +41,7 @@ struct DemoOptions { std::optional control_file; // 运行时 Control JSON 文件路径 std::optional control_cmd; // 节点命令 ID;必须配合 control_file std::string suite; // 执行套件 ("smoke", "real", "all") - bool example_control = false; // Explicitly apply the Demo example update. + bool example_control = false; // 显式应用 Demo 示例更新。 bool append = false; // 结果文件是否追加模式 bool allow_fallback_sample = false; // 测试集缺失时是否允许使用内置样例 bool list_only = false; // 是否仅列出可用 Business 和 Profile @@ -91,8 +91,8 @@ int LoadAndValidateProfilesDocument(const std::string& profiles_path, nlohmann::json* out_root, std::string* error_msg); -// Select and merge only documents returned by LoadAndValidateProfilesDocument. -// These operations reuse the same validated snapshot without reopening files. +// 只选择并合并 LoadAndValidateProfilesDocument 返回的文档。 +// 这些操作复用同一份已校验快照,不会重新打开文件。 std::vector SelectProfilesForSuite(const nlohmann::json& root, const std::string& suite_name); int MergeProfileOptions(const nlohmann::json& root, diff --git a/demo/common/demo_profile.py b/demo/common/demo_profile.py index 79a95d3e..35f13da4 100644 --- a/demo/common/demo_profile.py +++ b/demo/common/demo_profile.py @@ -1,4 +1,4 @@ -"""Current Demo Profile fields shared with the native build.""" +"""与原生构建共享的当前 Demo Profile 字段。""" import json from pathlib import Path diff --git a/demo/common/demo_profile_defaults.h b/demo/common/demo_profile_defaults.h index 00c93522..263c9923 100644 --- a/demo/common/demo_profile_defaults.h +++ b/demo/common/demo_profile_defaults.h @@ -4,7 +4,7 @@ namespace alg_demo { -// Shared Demo/Profile defaults for the executable and tooling projections. +// 可执行程序与工具投影共用的 Demo/Profile 默认值。 inline constexpr int kDemoBatchSize = 1; inline constexpr int kDemoDeviceId = 0; inline constexpr char kDemoChip[] = "cpu"; diff --git a/demo/json_prompt_demo.py b/demo/json_prompt_demo.py index fadb4063..215ec0b0 100644 --- a/demo/json_prompt_demo.py +++ b/demo/json_prompt_demo.py @@ -24,8 +24,8 @@ def encode_request(payload): request = json.loads(payload) if not isinstance(request, dict): raise ValueError("Input must be a JSON object") - # Only normalize whitespace for the line-based dataset reader. All fields - # reach the SDK; query selection and validation belong to its Adapter. + # 仅为按行读取的数据集读取器规整空白。所有字段都会送达 SDK; + # query 的选择与校验归其 Adapter 负责。 return dump_compact_json(request) @@ -50,7 +50,7 @@ def collect_responses(result_file, count): document = record.get("output", {}).get("entities") if not isinstance(document, dict): raise ValueError("Demo result must contain a JSON response object") - # Forward the complete SDK response. No business field projection here. + # 原样转发完整的 SDK 响应,此处不做业务字段投影。 responses[request_id] = dump_compact_json(document) return [responses[30001 + i] for i in range(count)] @@ -63,7 +63,7 @@ def _run_demo_impl(requests, config, work_dir, executable): str(executable), "--config", str(config), "--dataset", str(dataset), "--output-dir", str(output_dir), ] - # Keep native diagnostic output out of the JSON string response stream. + # 原生诊断输出不混入 JSON 字符串响应流。 with (work_dir / "demo.log").open("w", encoding="utf-8") as log: result = subprocess.run(command, cwd=ROOT, stdout=log, stderr=subprocess.STDOUT) if result.returncode: @@ -94,14 +94,14 @@ def main(argv=None): payloads = args.dataset.read_text(encoding="utf-8").splitlines() else: payloads = [args.input if args.input is not None else sys.stdin.read()] - # Reject invalid requests before creating run artifacts or starting a model. + # 在创建运行产物或启动模型前拒绝非法请求。 texts = prepare_requests(payloads) args.output_dir.mkdir(parents=True, exist_ok=True) work_dir = Path(tempfile.mkdtemp(prefix="run-", dir=args.output_dir.resolve())) print(f"Demo artifacts: {work_dir}", file=sys.stderr) responses = _run_demo_impl(texts, args.config, work_dir, args.demo_bin.resolve()) - # Validate the entire run before publishing any response. + # 整次运行校验通过后才发布任何响应。 sys.stdout.write("\n".join(responses) + "\n") return 0 except (OSError, ValueError, TypeError, KeyError, AttributeError) as error: diff --git a/dev_support/CMakeLists.txt b/dev_support/CMakeLists.txt index c4d07575..46f7627c 100644 --- a/dev_support/CMakeLists.txt +++ b/dev_support/CMakeLists.txt @@ -1,5 +1,5 @@ -# Deterministic runtime registrations used by mock Demo profiles and tests. -# OBJECT libraries keep every registration translation unit in each consumer. +# mock Demo Profile 和测试使用的确定性运行时注册。 +# OBJECT 库确保每个消费者都包含全部注册翻译单元。 add_library(edgeflow_test_backend_fixtures OBJECT inference/test_tensor_backend.cpp inference/test_causal_lm_backend.cpp) diff --git a/dev_support/benchmarks/control_snapshots.cpp b/dev_support/benchmarks/control_snapshots.cpp index 98fe4f10..f29dc1ce 100644 --- a/dev_support/benchmarks/control_snapshots.cpp +++ b/dev_support/benchmarks/control_snapshots.cpp @@ -13,9 +13,8 @@ #include "core/node_registry.h" #include "core/session_context.h" #include "tests/support/node_test_utils.h" -// Counts ordinary C++ new/new[] on the calling thread only. Aligned -// allocations, direct malloc calls, and allocations on other threads are not -// included. +// 仅统计调用线程上普通的 C++ new/new[]。 +// 不包括对齐分配、直接调用 malloc 以及其他线程上的分配。 thread_local bool count_on = false; thread_local size_t alloc_count = 0, alloc_bytes = 0; void* operator new(size_t n) { @@ -58,7 +57,7 @@ int main(int argc, char** argv) { std::string update = tpl ? R"({"template":"V1: {{primary}} / {{role}}","prompt_id":"pid_1"})" : R"({"categories":{"GREETING":["hello","hi"],"EXTRA":["absent"]}})"; - node->Control(cmd, update); // warm schema statics + node->Control(cmd, update); // 预热 schema 静态变量 for (int a = 0; a < 5; ++a) { alloc_count = alloc_bytes = 0; count_on = true; diff --git a/dev_support/benchmarks/control_snapshots.py b/dev_support/benchmarks/control_snapshots.py index edc159a7..593831c7 100644 --- a/dev_support/benchmarks/control_snapshots.py +++ b/dev_support/benchmarks/control_snapshots.py @@ -30,7 +30,7 @@ def positive_integer(value): def runner_link_command(commands): - """Keep compiler/launcher arguments while stripping Ninja shell wrappers.""" + """保留编译器/启动器参数,去掉 Ninja 的 shell 包装。""" for line in reversed(commands.splitlines()): tokens = shlex.split(line) if "-o" not in tokens or not all( @@ -114,7 +114,7 @@ def run(command, cwd=ROOT): )] executable = output / "bench_current" command[command.index("-o") + 1] = str(executable) - # Put replacement objects ahead of static libraries for normal linkers. + # 对普通链接器,把替换的目标文件放在静态库之前。 command[command.index("-o"):command.index("-o")] = objects + [str(benchmark_object)] run(command, cwd=build) diff --git a/dev_support/benchmarks/traceable_batch_operations.cpp b/dev_support/benchmarks/traceable_batch_operations.cpp index 88bba311..8d4c767d 100644 --- a/dev_support/benchmarks/traceable_batch_operations.cpp +++ b/dev_support/benchmarks/traceable_batch_operations.cpp @@ -1,5 +1,5 @@ -// Standalone Linux measurement of traceable batch operation construction. -// Checks output counts and reports median duration and process peak RSS. +// 在 Linux 上独立测量 traceable 批处理操作的构造开销。 +// 校验输出数量,并报告中位耗时和进程峰值 RSS。 #include "nodes/traceable_batch_operations.h" #include @@ -24,7 +24,7 @@ double Measure(Fn fn, size_t expected_size) { if (!result.ok() || result.value().size() != expected_size) { throw std::runtime_error("Unexpected benchmark output"); } - if (round > 0) { // One warmup, then seven measured constructions. + if (round > 0) { // 一次预热,随后测量七次构造。 times.push_back( std::chrono::duration(end - start).count()); } diff --git a/dev_support/inference/test_causal_lm_backend.cpp b/dev_support/inference/test_causal_lm_backend.cpp index 275c3fdc..bebf2179 100644 --- a/dev_support/inference/test_causal_lm_backend.cpp +++ b/dev_support/inference/test_causal_lm_backend.cpp @@ -28,8 +28,8 @@ int TestCausalLmSession::Generate(const std::string& formatted_prompt, if (diagnostic) *diagnostic = "Prompt is empty"; return -1; } - // The fixture proves protocol composition and lifetime only. Business test - // Models that bind it own their deterministic response semantics. + // 该夹具只验证协议组合与生命周期。 + // 绑定它的业务测试 Model 自行负责确定性的响应语义。 *output = "test-generation"; return 0; } catch (...) { diff --git a/dev_support/inference/test_causal_lm_backend.h b/dev_support/inference/test_causal_lm_backend.h index 83c082b9..6600c380 100644 --- a/dev_support/inference/test_causal_lm_backend.h +++ b/dev_support/inference/test_causal_lm_backend.h @@ -12,8 +12,8 @@ namespace llm_edgeflow { namespace test { -// The historic fixture name is kept to avoid changing persisted test profiles; -// its execution protocol is the current text-generation contract. +// 保留历史夹具名,避免改动已持久化的测试 Profile; +// 其执行协议即当前的文本生成契约。 class TestCausalLmSession : public ITextGenerationSession { public: explicit TestCausalLmSession(std::string model_path); diff --git a/dev_support/node_authoring/benchmark/CMakeLists.txt b/dev_support/node_authoring/benchmark/CMakeLists.txt index d545b22c..f99d6954 100644 --- a/dev_support/node_authoring/benchmark/CMakeLists.txt +++ b/dev_support/node_authoring/benchmark/CMakeLists.txt @@ -8,7 +8,7 @@ set(ENABLE_KITELLM OFF CACHE BOOL "" FORCE) set(ENABLE_WHISPERCPP OFF CACHE BOOL "" FORCE) add_subdirectory("${EDGEFLOW_SOURCE}" edgeflow EXCLUDE_FROM_ALL) add_executable(node_authoring_probe probe.cpp) -# Retired authoring helpers belong only to the historical comparison baseline. +# 已退役的编写辅助只用于历史对比基线。 target_include_directories(node_authoring_probe PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/baseline_include") target_link_libraries(node_authoring_probe PRIVATE llm_edgeflow::internal_runtime) set_target_properties(node_authoring_probe PROPERTIES CXX_VISIBILITY_PRESET hidden) diff --git a/dev_support/node_authoring/benchmark/run.py b/dev_support/node_authoring/benchmark/run.py index 9b24c6fe..6391b724 100644 --- a/dev_support/node_authoring/benchmark/run.py +++ b/dev_support/node_authoring/benchmark/run.py @@ -49,8 +49,8 @@ def run(command, **kwargs): header = run(["git", "-C", str(root), "show", BASELINE_HELPER_REVISION + ":include/nodes/" + name], capture_output=True).stdout - # The retired base relied on class-style port helpers that NodeBase no - # longer carries; LegacyNodeBase keeps them for this comparison only. + # 已退役的基类依赖 NodeBase 不再提供的类式端口辅助函数; + # LegacyNodeBase 仅为此对比保留它们。 header = header.replace('#include "nodes/node_base.h"', '#include "dev_support/node_authoring/legacy_node_base.h"') header = header.replace("public NodeBase", "public LegacyNodeBase") diff --git a/dev_support/node_authoring/legacy_node_base.h b/dev_support/node_authoring/legacy_node_base.h index e7e6752e..dba1869b 100644 --- a/dev_support/node_authoring/legacy_node_base.h +++ b/dev_support/node_authoring/legacy_node_base.h @@ -9,9 +9,8 @@ namespace llm_edgeflow { -// Retired class-style port helpers. Tests that drive the NodeBase runtime -// directly and the authoring benchmark's historical baseline use them; -// production Nodes use Specs, whose AuthorNode owns port binding. +// 已退役的类式端口辅助函数。直接驱动 NodeBase 运行时的测试和编写基准的 +// 历史基线会用到它们;生产 Node 使用 Spec,端口绑定由 AuthorNode 负责。 class LegacyNodeBase : public NodeBase { public: using NodeBase::NodeBase; @@ -58,7 +57,7 @@ class LegacyNodeBase : public NodeBase { return port; } - // Preserve BindPort's validation and left-to-right error ordering. + // 保留 BindPort 的校验及从左到右的错误顺序。 template void BindPorts(const NodeInitContext& init_ctx, Ports&... ports) const { (BindPort(init_ctx, ports), ...); diff --git a/dev_support/node_authoring/starter_batch_group_node.cpp b/dev_support/node_authoring/starter_batch_group_node.cpp index 0281e54d..b3bc3fc2 100644 --- a/dev_support/node_authoring/starter_batch_group_node.cpp +++ b/dev_support/node_authoring/starter_batch_group_node.cpp @@ -35,7 +35,7 @@ NodeResult Run(const Inputs& inputs, const Options& /*options*/, TextBatch prompts; prompts.reserve(inputs.queries->size()); - // Preserve anchor order for 1:1 PreservedOutput alignment + // 保持锚点顺序,以便 PreservedOutput 按 1:1 对齐 for (size_t i = 0; i < inputs.queries->size(); ++i) { const auto& query_item = (*inputs.queries)[i]; const auto& group = view.GroupByAnchorIndex(i); diff --git a/dev_support/node_authoring/starter_batch_node.cpp b/dev_support/node_authoring/starter_batch_node.cpp index e7b7ea52..087ef779 100644 --- a/dev_support/node_authoring/starter_batch_node.cpp +++ b/dev_support/node_authoring/starter_batch_node.cpp @@ -19,7 +19,7 @@ struct Models { LlmCall generator; }; -// All request values stay local to this ordinary business function. +// 所有请求值都只在这个普通业务函数内使用。 NodeResult Run(const Inputs& inputs, const Options& options, const Models& models) { TextBatch prompts; diff --git a/dev_support/node_authoring/starter_batch_select_scatter_node.cpp b/dev_support/node_authoring/starter_batch_select_scatter_node.cpp index 975dfa4f..3ec060e2 100644 --- a/dev_support/node_authoring/starter_batch_select_scatter_node.cpp +++ b/dev_support/node_authoring/starter_batch_select_scatter_node.cpp @@ -25,7 +25,7 @@ NodeResult Run(const Inputs& inputs, const Options& options, return NodeResult::Success(TextBatch{}); } - // Round 1: Generate initial drafts for all inputs + // 第 1 轮:为所有输入生成初稿 auto first_res = models.generator.Generate(*inputs.input); if (!first_res.ok()) { return first_res; @@ -33,7 +33,7 @@ NodeResult Run(const Inputs& inputs, const Options& options, const auto& drafts = first_res.value(); - // Step 2: Select items that require polishing (contain polish_tag) + // 第 2 步:选出需要润色的条目 (包含 polish_tag) auto selection_res = SelectBatch(drafts, [&](const std::string& text) { return text.find(options.polish_tag) != std::string::npos; }); @@ -44,20 +44,20 @@ NodeResult Run(const Inputs& inputs, const Options& options, const auto& selection = selection_res.value(); if (selection.empty()) { - // Skip second model call when no items need polishing + // 没有条目需要润色时跳过第二次模型调用 return first_res; } - // Materialize owned sub-batch for the second model call + // 为第二次模型调用物化自有子批次 TextBatch sub_batch = selection.Materialize(); - // Round 2: Call polisher model only on selected sub-batch + // 第 2 轮:只对选中的子批次调用润色模型 auto second_res = models.polisher.Generate(sub_batch); if (!second_res.ok()) { return second_res; } - // Step 3: Scatter-replace polished items back into full draft batch + // 第 3 步:把润色后的条目散回完整的初稿批次 return ScatterReplace(selection, second_res.value()); } diff --git a/dev_support/node_authoring/starter_control_node.cpp b/dev_support/node_authoring/starter_control_node.cpp index 1d231697..08ec6ede 100644 --- a/dev_support/node_authoring/starter_control_node.cpp +++ b/dev_support/node_authoring/starter_control_node.cpp @@ -10,10 +10,10 @@ struct StarterControlNodeParams { std::string prefix; }; -// Choose a stable, unused custom ID using the current Catalog. +// 根据当前 Catalog 选择一个稳定且未被占用的自定义 ID。 inline constexpr int kUpdatePrefix = 1001; -// Business logic works on ordinary data, not platform structures. +// 业务逻辑处理普通数据,而非平台结构。 static std::string ApplyPrefix(const std::string& input, const StarterControlNodeParams& params) { return params.prefix + input; diff --git a/dev_support/node_authoring/starter_llm_node.cpp b/dev_support/node_authoring/starter_llm_node.cpp index d3af6631..7c6d3fd9 100644 --- a/dev_support/node_authoring/starter_llm_node.cpp +++ b/dev_support/node_authoring/starter_llm_node.cpp @@ -6,9 +6,8 @@ namespace llm_edgeflow { namespace custom_nodes { namespace { -// LLM starter: see doc/dev_guide/first_custom_node.md for the authoring -// walkthrough. -// Start here: these two functions work on text, not platform structures. +// LLM 入门模板:编写流程见 doc/dev_guide/first_custom_node.md。 +// 从这里开始:这两个函数处理文本,而非平台结构。 static std::string BuildPrompt(const std::string& text) { return text; } static std::string FormatAnswer(const std::string& text) { return text; } diff --git a/include/adapter/biz_blackboard_keys.h b/include/adapter/biz_blackboard_keys.h index daab3368..8624e918 100644 --- a/include/adapter/biz_blackboard_keys.h +++ b/include/adapter/biz_blackboard_keys.h @@ -7,9 +7,8 @@ namespace llm_edgeflow { -// Business-facing request and response slots belong to the Integration -// adapters. Lower layers consume logical port bindings and neutral value -// contracts only. +// 业务请求/响应槽位归接入适配层所有; +// 下层只消费逻辑端口绑定和中性值契约。 inline constexpr auto kInputSentences = MakeBlackboardKey("input_sentences"); inline constexpr auto kRuleMatches = @@ -31,7 +30,7 @@ inline constexpr auto kStructuredVerdicts = MakeBlackboardKey("structured_verdicts"); inline constexpr auto kMatchedPolicy = MakeBlackboardKey("matched_policy"); -// The output converter's logical port differs from the business blackboard key. +// 输出 Converter 的逻辑端口名与业务 Blackboard 键不同。 inline constexpr auto kMatchedPolicies = MakeBlackboardKey("matched_policies"); inline constexpr auto kImagePaths = diff --git a/include/adapter/biz_input_constraints.h b/include/adapter/biz_input_constraints.h index e9db8c5c..44ee9a90 100644 --- a/include/adapter/biz_input_constraints.h +++ b/include/adapter/biz_input_constraints.h @@ -5,8 +5,7 @@ namespace llm_edgeflow::biz_input { -// Shared semantic limits; each entry point validates its own pointer/length -// representation. +// 共享语义上限;各入口自行校验指针、长度和表示形式。 inline constexpr size_t kMaxTextBytes = 64 * 1024; inline constexpr size_t kMaxDocTextBytes = 10 * 1024 * 1024; inline constexpr size_t kMaxImageUriBytes = 4096; @@ -15,7 +14,7 @@ inline constexpr int32_t kMaxAudioPcmSamples = 16000 * 60; inline constexpr size_t kMaxAudioPcmBytes = 10 * 1024 * 1024; inline constexpr int32_t kMinSampleRate = 8000; inline constexpr int32_t kMaxSampleRate = 192000; -// Generic carriers without business field semantics. +// 通用载体,不含业务字段语义。 inline constexpr size_t kMaxBufferBytes = 10 * 1024 * 1024; inline constexpr size_t kMaxAnyBytes = 10 * 1024 * 1024; diff --git a/include/adapter/converter_authoring.h b/include/adapter/converter_authoring.h index cdec44d7..9e70949b 100644 --- a/include/adapter/converter_authoring.h +++ b/include/adapter/converter_authoring.h @@ -17,7 +17,7 @@ namespace llm_edgeflow { -// Both sides must carry the same C++ value type. Identity mapping is explicit. +// 两侧必须是同一 C++ 值类型;恒等映射需显式声明。 template inline std::pair BindIoPort( const BlackboardKey& logical_port, const BlackboardKey& actual_key) { @@ -30,9 +30,8 @@ inline std::pair BindIoPort( return BindIoPort(port, port); } -// Common convention: required slot, type_suffix = slot_name, and an empty -// key_suffix that falls back to type_suffix. For other suffixes or optional -// slots, use ExternalSlotDefinition's explicit fields. +// 常用约定:必填槽位,type_suffix = slot_name,key_suffix 为空时回退到 +// type_suffix。其他后缀或可选槽位请使用 ExternalSlotDefinition 的显式字段。 template inline ExternalSlotDefinition ExternalInputSlot( std::string slot_name, @@ -45,8 +44,8 @@ inline ExternalSlotDefinition ExternalInputSlot( slot_name}; } -// Omitted capacity fields are derived from the registered ValueType; an -// explicit list must match that ValueType. +// 省略容量字段时由已注册的 ValueType 推导; +// 显式列表必须与该 ValueType 一致。 template inline ExternalSlotDefinition ExternalOutputSlot( std::string slot_name, std::vector capacity_fields = {}) { @@ -59,8 +58,8 @@ inline ExternalSlotDefinition ExternalOutputSlot( std::move(capacity_fields)}; } -// Operator checks carriers and the effective batch limit before decoding and -// passes that limit in options; this repeats the bound defensively. +// Operator 在解码前已检查载体和有效批大小上限,并通过 options 传入; +// 此处防御性地再检查一次。 inline bool ValidateDecodeRequest(const ExternalInputBatchView& source, const InputDecodeOptions& options, AlgContext* context, AdapterStatus* status) { @@ -102,20 +101,19 @@ inline const T* ReadInputSlot(const ExternalInputBatchView& source, return value; } -// Structural safety only; size limits and optional-field semantics stay at the -// call site so existing diagnostics and validation order remain unchanged. +// 仅做结构安全检查;大小上限和可选字段语义留在调用处, +// 以保持现有诊断信息和校验顺序不变。 inline bool IsValidInputString(const CompanyString* value) { return value && value->length >= 0 && (value->length == 0 || value->data); } -// Call after validation. Empty strings may legitimately have a null data -// pointer. +// 须在校验后调用。空字符串的 data 指针可以为空。 inline std::string CopyInputString(const CompanyString& value) { return value.length == 0 ? std::string{} : std::string(value.data, value.length); } -// Call after every row passes validation, before publishing business values. +// 所有行校验通过后、发布业务值前调用。 inline bool PublishRequestIds(const InputDecodeOptions& options, std::vector ids, AdapterStatus* status) { @@ -187,8 +185,8 @@ inline int CopyToOperatorString(std::string_view src, CompanyString* dest, } } -// The view must describe the actual leased storage. Never infer capacity from -// CompanyString.length (content length) or a converter-local fallback. +// view 必须描述实际租用的存储。不得从 CompanyString.length (内容长度) +// 或 Converter 本地的回退值推断容量。 inline bool WriteOutputString(const ExternalOutputBatchView& view, const char* slot, CompanyString* destination, const char* field, std::string_view value, @@ -219,8 +217,8 @@ inline bool WriteOutputString(const ExternalOutputBatchView& view, return false; } -// Add runtime location to a business error without making the business function -// depend on converter IDs, batch indices, or port bindings. +// 为业务错误补充运行时位置,使业务函数无需依赖 Converter ID、批内索引或 +// 端口绑定。 inline int ReturnRowStatus(const AdapterStatus& result, const std::string& id, size_t index, AdapterStatus* status) { if (status) { @@ -230,7 +228,7 @@ inline int ReturnRowStatus(const AdapterStatus& result, const std::string& id, return result.Code(); } -// A synchronous borrowed writer. Do not retain it or any destination pointers. +// 同步借用的写入器,不得保留它或任何目标指针。 class OutputStringWriter { public: OutputStringWriter(const ExternalOutputBatchView& view, const char* slot, @@ -252,9 +250,8 @@ class OutputStringWriter { size_t index_; }; -// One required host slot -> one owned payload per request. Callback validates -// and copies borrowed host fields; publication starts only after every row -// passes. +// 每个请求:一个必填宿主槽位 -> 一个自有 payload。回调负责校验并复制借用的 +// 宿主字段;所有行校验通过后才开始发布。 template int DecodeRequestRows( const ExternalInputBatchView& source, const InputDecodeOptions& options, @@ -286,8 +283,8 @@ int DecodeRequestRows( return COMPANY_ALG_SUCCESS; } -// Exactly one result (sub_id == 0) per request, in any internal order. The -// framework restores external IDs; callback owns business fields/serialization. +// 每个请求恰好一个结果 (sub_id == 0),内部顺序不限。框架负责恢复外部 ID, +// 回调负责业务字段与序列化。 template int EncodeResultRows( AlgContext* context, const OutputPortBindings& bindings, diff --git a/include/adapter/io_binding.h b/include/adapter/io_binding.h index e1ae1c20..5db49536 100644 --- a/include/adapter/io_binding.h +++ b/include/adapter/io_binding.h @@ -7,7 +7,7 @@ namespace llm_edgeflow { -// Standard Operator batch bound applied when a binding does not override it. +// binding 未覆盖时采用的 Operator 标准批大小上限。 inline constexpr size_t kDefaultIoBindingMaxBatchSize = 64; /** @@ -19,13 +19,12 @@ struct IoBindingDefinition { std::string input_converter_id; std::string output_converter_id; - // logical_name -> blackboard_key. Converter logical ports without an entry - // map to the same name; list only renamed ports. + // logical_name -> blackboard_key。未列出的 Converter 逻辑端口映射到同名键, + // 只需列出改名的端口。 std::unordered_map input_ports; std::unordered_map output_ports; - // Defaults to the standard Operator bound. Zero adds no bound; the effective - // limit is the smallest positive value among the binding and its converters, - // and at least one must be positive. + // 默认使用 Operator 标准上限;0 表示不设上限。有效上限取 binding 及其 + // Converter 中最小的正值,且至少一方须为正。 size_t max_batch_size = kDefaultIoBindingMaxBatchSize; }; diff --git a/include/adapter/io_binding_registry.h b/include/adapter/io_binding_registry.h index 0a5e556c..61223b00 100644 --- a/include/adapter/io_binding_registry.h +++ b/include/adapter/io_binding_registry.h @@ -11,21 +11,18 @@ namespace llm_edgeflow { -// Declared entries are kept as written; converter logical ports without an -// entry map to the same key. Computed on use, independent of registration -// order. +// 已声明的条目原样保留;未声明的 Converter 逻辑端口映射到同名键。 +// 使用时计算,与注册顺序无关。 std::unordered_map EffectivePortMapping( const std::unordered_map& declared, const std::vector& logical_ports); -// Output slots with no capacity_fields inherit the string capacity fields of -// their registered ValueType, in lexicographic order. Input slots and unknown -// ValueTypes return the declared list unchanged. +// 未声明 capacity_fields 的输出槽位,按字典序继承其已注册 ValueType 的 +// 字符串容量字段。输入槽位和未知 ValueType 原样返回声明列表。 std::vector EffectiveCapacityFields( const ExternalSlotDefinition& slot); -// Smallest positive limit among the binding and its converters; zero when none -// of them declares one. +// binding 及其 Converter 中最小的正上限;均未声明时为 0。 size_t EffectiveMaxBatchSize(const IoBindingDefinition& binding, const InputConverterDefinition& input, const OutputConverterDefinition& output); @@ -40,7 +37,7 @@ class IoBindingRegistry { std::vector AllBindings() const; - // A biz identifies one complete external contract across its bindings. + // 一个 biz 通过其全部 binding 标识一份完整的外部契约。 bool ValidateBizContract(const std::string& biz_name, std::string* error = nullptr) const; diff --git a/include/adapter/io_converter.h b/include/adapter/io_converter.h index 24b35283..bee71055 100644 --- a/include/adapter/io_converter.h +++ b/include/adapter/io_converter.h @@ -86,7 +86,7 @@ class ExternalInputBatchView { } }; -/** Synchronous Encode borrows slots and immutable specs from leased pools. */ +/** 同步 Encode 借用租约池中的槽位和不可变规格。 */ class ExternalOutputBatchView { public: size_t count = 0; @@ -128,10 +128,10 @@ class ExternalOutputBatchView { */ struct InputDecodeOptions { std::string converter_id; - // Effective batch limit filled by the Operator; zero skips the upper bound. + // 由 Operator 填入的有效批大小上限;0 表示不检查上限。 size_t max_batch_size = 0; - // Per-call table owned by the Operator. Despite const options, converters - // write each input row's external request ID here, in input order. + // 每次调用独有的表,归 Operator 所有。尽管 options 为 const,Converter + // 仍按输入顺序在此写入每行的外部请求 ID。 std::vector* request_ids = nullptr; }; @@ -140,7 +140,7 @@ struct InputDecodeOptions { */ struct OutputEncodeOptions { std::string converter_id; - // The same per-call table, read-only: index i is input row i's external ID. + // 同一张调用表的只读视图:第 i 项是第 i 个输入行的外部 ID。 const std::vector* request_ids = nullptr; }; @@ -167,7 +167,7 @@ class PortBindings { BlackboardTypeTraits::TypeName()}; } - // The token names a logical port; always resolve the actual key via bindings. + // token 表示逻辑端口;实际键始终通过 bindings 解析。 template BlackboardKey Key(const BlackboardKey& logical_port) const { return Key(logical_port.name); @@ -251,7 +251,7 @@ struct InputConverterDefinition { std::string external_type; std::vector external_slots; std::vector logical_ports; // 发布的内部逻辑输出端口 - // Optional converter-specific limit; zero adds no bound. + // 可选的 Converter 专属上限;0 表示不设上限。 size_t max_batch_size = 0; DecodeInputFn decode_fn = nullptr; @@ -269,7 +269,7 @@ struct OutputConverterDefinition { std::vector logical_ports; // 消费的内部逻辑输入端口 std::vector external_slots; std::string cardinality = "1:1"; - // Optional converter-specific limit; zero adds no bound. + // 可选的 Converter 专属上限;0 表示不设上限。 size_t max_batch_size = 0; std::string capacity_policy = "reject_overflow"; diff --git a/include/adapter/operator_io_contracts.h b/include/adapter/operator_io_contracts.h index fe662551..bd5bac0c 100644 --- a/include/adapter/operator_io_contracts.h +++ b/include/adapter/operator_io_contracts.h @@ -11,8 +11,8 @@ namespace llm_edgeflow { enum class IoDirection { kUnknown, kInput, kOutput }; -// Framework storage for immutable parameters. Structure authors use ordinary -// structs and MakeOutputParameterParser; no inheritance is required. +// 不可变参数的框架存储。结构作者使用普通 struct 和 +// MakeOutputParameterParser,无需继承。 struct OutputAllocationParameters { virtual ~OutputAllocationParameters() = default; }; @@ -34,7 +34,7 @@ struct ResolvedOutputPoolSpec { uint32_t meta_num = 0; int32_t metadata_type_id = 0; std::unordered_map capacities; - std::string allocator; // Empty selects the ValueType default allocator. + std::string allocator; // 为空时使用 ValueType 的默认分配器。 std::shared_ptr params; template diff --git a/include/adapter/operator_output_config.h b/include/adapter/operator_output_config.h index f1351d45..9bc43e44 100644 --- a/include/adapter/operator_output_config.h +++ b/include/adapter/operator_output_config.h @@ -4,8 +4,8 @@ namespace llm_edgeflow { -// Select a field of one output's configuration. These are framework fields, -// distinct from the structure-specific enums inside kParameters. +// 选择某个输出配置中的字段。这些是框架字段, +// 区别于 kParameters 内各结构自有的枚举。 enum class OutputConfigField { kAllocator, kParameters, @@ -14,10 +14,9 @@ enum class OutputConfigField { kMetadataTypeId }; -// Creation-time Integration component, independent of Pipeline execution. -// The JSON implementation returns serialized JSON values (including quotes for -// strings); other configuration carriers can implement the same text boundary. -// Structure allocators receive only the resulting parameter text. +// 创建期的接入适配层组件,独立于 Pipeline 执行。JSON 实现返回序列化后的 +// JSON 值 (字符串含引号);其他配置载体可实现同样的文本边界。 +// 结构分配器只接收最终的参数文本。 class OutputConfigReader { public: virtual ~OutputConfigReader() = default; diff --git a/include/adapter/operator_value_type.h b/include/adapter/operator_value_type.h index f5407f7e..a69e970b 100644 --- a/include/adapter/operator_value_type.h +++ b/include/adapter/operator_value_type.h @@ -28,7 +28,7 @@ struct InputLimits { size_t max_doc_text_bytes = biz_input::kMaxDocTextBytes; size_t max_image_uri_bytes = biz_input::kMaxImageUriBytes; int32_t max_audio_pcm_samples = - biz_input::kMaxAudioPcmSamples; // 960k samples + biz_input::kMaxAudioPcmSamples; // 96 万个采样点 size_t max_audio_pcm_bytes = biz_input::kMaxAudioPcmBytes; // 10 MiB int32_t min_sample_rate = biz_input::kMinSampleRate; int32_t max_sample_rate = biz_input::kMaxSampleRate; @@ -98,7 +98,7 @@ struct OwnedExternalBlock { OwnedExternalBlock(const OwnedExternalBlock&) = delete; OwnedExternalBlock& operator=(const OwnedExternalBlock&) = delete; - // Register each allocation while its unique_ptr still guards failure. + // 在 unique_ptr 仍负责失败回收时登记每次分配。 template T* Own(std::unique_ptr value) { T* raw = value.get(); @@ -146,10 +146,9 @@ using NormalizeOutputParametersFn = std::function* normalized, std::string* error)>; -// Parse only this structure's parameters, once during Create. Parser has the -// signature bool(const std::string&, T*, std::string*). T is an ordinary -// struct; the framework handles immutable ownership and checked access -// thereafter. +// 只解析本结构的参数,且仅在 Create 时解析一次。Parser 签名为 +// bool(const std::string&, T*, std::string*)。T 为普通 struct, +// 此后由框架负责不可变所有权和受检访问。 template NormalizeOutputParametersFn MakeOutputParameterParser(Parser parse) { return [parse = std::move(parse)]( @@ -176,12 +175,11 @@ struct OperatorValueTypeBinding { AllocateExternalFn allocate_external; ResetExternalFn reset_external; DestroyExternalFn destroy_external; - // Empty for a ValueType's default allocation; assigned by allocator - // registration. + // ValueType 默认分配时为空;由分配器注册时赋值。 std::string allocation_name; - // Called at configuration time to create immutable single-object parameters. - // Prefer MakeOutputParameterParser with an ordinary parameter struct. - // Parameter destruction must not allocate; no file or queue access here. + // 配置期调用,创建不可变的单对象参数。建议使用普通参数 struct 和 + // MakeOutputParameterParser。参数析构不得分配内存, + // 此处也不得访问文件或队列。 NormalizeOutputParametersFn normalize_parameters; }; @@ -198,7 +196,7 @@ bool ComputeStandardOutputBlockPayloadBytes(size_t root_bytes, std::string* error) noexcept; } // namespace operator_value_detail -// Keep the external null diagnostic and type erasure at the binding boundary. +// 外部空值诊断和类型擦除保留在绑定边界。 template OperatorValueTypeBinding MakeTypedInputBinding(const char* suffix, const char* type_name, @@ -219,8 +217,8 @@ OperatorValueTypeBinding MakeTypedInputBinding(const char* suffix, return binding; } -// Each pooled string is declared once for capacity validation, allocation and -// reset. Member pointers keep the descriptor tied to its concrete C structure. +// 每个池化字符串只声明一次,用于容量校验、分配和重置。 +// 成员指针使描述符与具体的 C 结构绑定。 template struct OutputStringField { std::string name; @@ -269,8 +267,8 @@ OperatorValueTypeBinding MakePooledOutputBinding( const ResolvedOutputPoolSpec& spec, OwnedExternalBlock* block, std::string*) -> int { - // Reserve before allocating: one root plus a wrapper and data buffer - // for each nested field. OwnedExternalBlock rolls back partial failure. + // 分配前先预留:一个根块,每个嵌套字段再加一个包装和一个数据缓冲区。 + // OwnedExternalBlock 负责回滚部分失败。 block->cleanups.reserve(1 + 2 * string_fields.size() + (metadata_field ? 2 : 0)); auto* raw = block->Own(std::make_unique()); @@ -291,7 +289,7 @@ OperatorValueTypeBinding MakePooledOutputBinding( const ResolvedOutputPoolSpec&) noexcept { if (!ptr) return; auto* raw = static_cast(ptr); - // Reset values only; nested storage and metadata type survive reuse. + // 只重置值;嵌套存储和元数据类型在复用时保留。 reset_scalars(*raw); for (const auto& field : string_fields) { operator_value_detail::ResetNestedCompanyString(raw->*field.member); @@ -305,9 +303,8 @@ OperatorValueTypeBinding MakePooledOutputBinding( return binding; } -// Source-extension registration. All registrations finish before Operator Init. -// A named allocator preserves the registered outer type, while defining its -// own nested layout, parameter normalization, byte accounting and cleanup. +// 源码扩展注册,须在 Operator Init 前全部完成。具名分配器保留已注册的 +// 外层类型,同时自定义嵌套布局、参数归一化、字节计数和清理。 bool RegisterOperatorValueType(const OperatorValueTypeBinding& binding); bool RegisterOperatorOutputAllocator(const std::string& name, const OperatorValueTypeBinding& binding); diff --git a/include/adapter/result_validation.h b/include/adapter/result_validation.h index 367e3236..443ad6d1 100644 --- a/include/adapter/result_validation.h +++ b/include/adapter/result_validation.h @@ -8,8 +8,8 @@ namespace llm_edgeflow { -// req_id is the input batch index, never the external request ID. Validate -// every item before exposing an ordered view to either ABI output path. +// req_id 是输入批内索引,而非外部请求 ID。向任一 ABI 输出路径暴露有序 +// 视图前,须校验每个条目。 template bool IndexResults(const Batch* batch, const std::vector* request_ids, std::vector* ordered, diff --git a/include/contracts/config_schema.h b/include/contracts/config_schema.h index 6c9bb954..2e6c2d53 100644 --- a/include/contracts/config_schema.h +++ b/include/contracts/config_schema.h @@ -71,11 +71,10 @@ inline const char* ConfigValueKindName(ConfigValueKind kind) noexcept { } } -// Reads a declared configuration field: the configured value when present, -// otherwise the default from its declaration, so each default is written once -// in the Definition. Throws for undeclared fields or a configured value of the -// wrong type, like nlohmann::json::value. Null means omitted configuration; -// other non-object values are rejected. +// 读取已声明的配置字段:有配置值时取配置值,否则取声明中的默认值, +// 因此每个默认值只在 Definition 中写一次。字段未声明或配置值类型错误时 +// 抛出异常,与 nlohmann::json::value 一致。null 表示未配置, +// 其他非对象值会被拒绝。 template T ConfigValueOrDefault(const nlohmann::json& config, const std::vector& fields, diff --git a/include/contracts/config_schema_validation.h b/include/contracts/config_schema_validation.h index c4a623fd..46f48b54 100644 --- a/include/contracts/config_schema_validation.h +++ b/include/contracts/config_schema_validation.h @@ -44,8 +44,8 @@ inline bool IsValidConfigValueKind(ConfigValueKind kind) noexcept { namespace detail { -// Exclusive integer upper bounds are exactly representable in double; -// converting INT64_MAX/UINT64_MAX to double rounds up to these values. +// 不含端点的整数上界可在 double 中精确表示; +// INT64_MAX/UINT64_MAX 转为 double 时会向上舍入到这些值。 inline constexpr double kInt64UpperBound = 9223372036854775808.0; // 2^63 inline constexpr double kUint64UpperBound = 18446744073709551616.0; // 2^64 diff --git a/include/contracts/control_payload.h b/include/contracts/control_payload.h index 92693233..12294b2a 100644 --- a/include/contracts/control_payload.h +++ b/include/contracts/control_payload.h @@ -12,10 +12,9 @@ namespace llm_edgeflow { -// Supported validation keywords: type, enum, required, properties, -// minProperties, additionalProperties, homogeneous items, minimum and maximum. -// Documentary annotations do not change validation or apply defaults. Reject -// unsupported or malformed declarations at registration and in this helper. +// 支持的校验关键字:type、enum、required、properties、minProperties、 +// additionalProperties、同构 items、minimum 和 maximum。说明性注解不影响 +// 校验,也不填充默认值。注册时和本函数都会拒绝不支持或格式错误的声明。 inline bool ValidateControlSchema(const nlohmann::json& schema, std::string* error = nullptr) { if (error) error->clear(); @@ -183,7 +182,7 @@ inline bool ValidateValue(const nlohmann::json& payload, } // namespace control_payload_detail -// Structural validation only; Nodes still validate domain semantics. +// 仅做结构校验;Node 仍需校验领域语义。 inline bool ValidateControlPayload(const nlohmann::json& payload, const nlohmann::json& schema, std::string* error = nullptr) { @@ -191,8 +190,8 @@ inline bool ValidateControlPayload(const nlohmann::json& payload, return control_payload_detail::ValidateValue(payload, schema, error); } -// Owns the parsed payload; no borrowed platform buffers survive this call. -// The output is replaced only after parsing and structural validation succeed. +// 持有解析后的 payload;调用返回后不再引用借用的平台缓冲区。 +// 仅在解析和结构校验都成功后才替换输出。 inline bool ParseControlPayload(const std::string& text, const nlohmann::json& schema, nlohmann::json* payload, diff --git a/include/contracts/diagnostic.h b/include/contracts/diagnostic.h index 65707b7d..3ab27bdb 100644 --- a/include/contracts/diagnostic.h +++ b/include/contracts/diagnostic.h @@ -5,8 +5,8 @@ namespace llm_edgeflow { -// Diagnostic allocation must not turn a recoverable failure into terminate. -// Pass exception.what() or a literal in catch blocks, not a temporary string. +// 诊断信息分配失败不得把可恢复错误变成 terminate。catch 块中应传入 +// exception.what() 或字面量,而不是临时 string。 inline void SetDiagnosticNoexcept(std::string* output, std::string_view message) noexcept { if (!output) return; diff --git a/include/contracts/json_structure.h b/include/contracts/json_structure.h index 25633aad..d7b15b16 100644 --- a/include/contracts/json_structure.h +++ b/include/contracts/json_structure.h @@ -7,9 +7,8 @@ namespace llm_edgeflow::json_structure { -// Shallow JSON Schema keyword predicates, shared by the ordered parsers, their -// editor schema and Control payload validation. No recursive validation, -// normalization or diagnostics; each caller keeps its own error contract. +// 浅层 JSON Schema 关键字谓词,供有序解析器、其编辑器 schema 和 Control +// payload 校验共用。不做递归校验、归一化或诊断,错误契约由各调用方负责。 using Json = nlohmann::json; inline Json Object(Json properties, Json required = Json::array()) { diff --git a/include/contracts/path_utils.h b/include/contracts/path_utils.h index 043c2754..4239ed8e 100644 --- a/include/contracts/path_utils.h +++ b/include/contracts/path_utils.h @@ -4,8 +4,8 @@ namespace llm_edgeflow { -// Pure lexical helpers: callers own normalization, platform-specific policies, -// deployment roots, existence checks, and symlink resolution. +// 纯词法辅助函数:归一化、平台策略、部署根目录、存在性检查和符号链接解析 +// 均由调用方负责。 inline bool HasParentPathComponent(const std::filesystem::path& path) { for (const auto& component : path) { if (component == "..") return true; @@ -13,8 +13,8 @@ inline bool HasParentPathComponent(const std::filesystem::path& path) { return false; } -// Compare normalized/canonical paths by components, including the root itself. -// A string prefix is insufficient: /root/models_extra is outside /root/models. +// 按路径分量比较已归一化/规范化的路径 (含根目录本身)。字符串前缀比较不够: +// /root/models_extra 不在 /root/models 内。 inline bool IsPathWithinRoot(const std::filesystem::path& root, const std::filesystem::path& candidate) { auto root_it = root.begin(); diff --git a/include/contracts/registry_conflicts.h b/include/contracts/registry_conflicts.h index c019a77a..2d2d060a 100644 --- a/include/contracts/registry_conflicts.h +++ b/include/contracts/registry_conflicts.h @@ -6,8 +6,8 @@ namespace llm_edgeflow { -// Fail-closed conflict state of one registry. The registry guards it with its -// own mutex; a conflict stays recorded even if its message cannot be stored. +// 单个注册表的 fail-closed 冲突状态,由注册表自身的互斥锁保护; +// 即使消息无法保存,冲突也会被记录。 class RegistryConflicts { public: void Record(std::string message) noexcept { @@ -20,7 +20,7 @@ class RegistryConflicts { bool HasConflict() const noexcept { return has_conflict_; } - // Recorded messages; never empty while a conflict is recorded. + // 已记录的消息;存在冲突时绝不为空。 std::vector Messages() const { if (has_conflict_ && messages_.empty()) { return {"Registry conflict recorded without a stored message"}; diff --git a/include/core/node_definition.h b/include/core/node_definition.h index 9f6ea276..e7fe0f2d 100644 --- a/include/core/node_definition.h +++ b/include/core/node_definition.h @@ -47,7 +47,7 @@ struct ControlCommandDefinition { std::string description; nlohmann::json payload_schema = nlohmann::json::object(); bool supports_hot_swap = false; - // Opt in on both definitions when sharing an identical command across types. + // 跨类型共享同一命令时,两个 Definition 都需显式开启。 bool shared_id = false; ControlCommandDefinition() = default; @@ -80,7 +80,7 @@ struct NodeDefinition { std::vector port_constraints; std::vector control_commands; std::vector config_fields; - // Pure semantic validation: no model/session allocation or external I/O. + // 纯语义校验:不分配模型/会话,不做外部 I/O。 NodeConfigValidator validate_config; std::vector model_dependencies; bool parallel_safe = false; diff --git a/include/core/node_interface.h b/include/core/node_interface.h index 73eb1f05..84992acd 100644 --- a/include/core/node_interface.h +++ b/include/core/node_interface.h @@ -47,7 +47,7 @@ struct NodeControlResult { struct NodeInitContext { const ValidatedNodePlan* plan = nullptr; SessionContext* session_ctx = nullptr; - // Borrowed only for Init; do not store this pointer in a Node. + // 仅在 Init 期间借用;Node 不得保存此指针。 std::string* diagnostic = nullptr; bool Fail(std::string_view message) const noexcept { diff --git a/include/core/pipeline.h b/include/core/pipeline.h index 7d71967d..41609c46 100644 --- a/include/core/pipeline.h +++ b/include/core/pipeline.h @@ -66,10 +66,9 @@ class Pipeline { /** * @brief 运行时动态控制 */ - // Calls must be externally serialized with Execute/Control. Broadcast updates - // are not transactional; error identifies failed instances. A JSON envelope - // {"$edgeflow_control":1,"node_id":"id","payload":{...}} targets one - // instance. + // 调用须与 Execute/Control 在外部串行化。广播更新不具事务性,error 会指出 + // 失败的实例。JSON 信封 + // {"$edgeflow_control":1,"node_id":"id","payload":{...}} 只作用于一个实例。 int Control(int cmd, const std::string& json_param, std::string* error = nullptr, ControlFailureStage* failure_stage = nullptr); @@ -102,8 +101,8 @@ class Pipeline { State state_ = State::kEmpty; ExecutionMode execution_mode_ = ExecutionMode::kSequential; - // Heap ownership keeps addresses handed to initialized Nodes stable while a - // fully staged runtime assembly is committed into this façade. + // 使用堆所有权,确保完整暂存的运行时组件提交到本 façade 时, + // 已交给已初始化 Node 的地址保持不变。 std::unique_ptr session_ctx_; std::unique_ptr plan_; diff --git a/include/core/pipeline_validator.h b/include/core/pipeline_validator.h index 76274a29..bc26e41c 100644 --- a/include/core/pipeline_validator.h +++ b/include/core/pipeline_validator.h @@ -20,13 +20,12 @@ struct ValidationFix { std::string title; std::string effect; nlohmann::json patch = nlohmann::json::array(); - std::string verification; // "pipeline_valid" or "target_resolved" + std::string verification; // "pipeline_valid" 或 "target_resolved" nlohmann::json ToJson() const; }; -// Filled by developer tooling (alg_pipeline_tool); the Validator itself only -// reports neutral diagnostics. +// 由开发工具 (alg_pipeline_tool) 填写;Validator 本身只报告中性诊断。 struct ValidationRemediation { int schema_version = 1; RemediationCause cause = RemediationCause::kUnknownConfigField; @@ -47,8 +46,8 @@ struct ValidationDiagnostic { std::vector related_nodes; std::vector suggestions; std::optional remediation; - // Neutral facts established by validation, including inferred item shapes. - // Tooling may explain these facts without repeating Validator rules. + // 校验确立的中性事实,含推断出的 item 形状。 + // 工具可据此解释,无需重复 Validator 规则。 nlohmann::json facts = nlohmann::json::object(); nlohmann::json ToJson() const; diff --git a/include/core/port_definition.h b/include/core/port_definition.h index 74b6fb93..74c7e032 100644 --- a/include/core/port_definition.h +++ b/include/core/port_definition.h @@ -7,7 +7,7 @@ namespace llm_edgeflow { -// Shared flow properties, independent of how a port is named. +// 与端口命名方式无关的共享流属性。 struct PortContract { std::string type_id; bool required = true; diff --git a/include/core/session_context.h b/include/core/session_context.h index aba0d976..a11cbf8f 100644 --- a/include/core/session_context.h +++ b/include/core/session_context.h @@ -256,7 +256,7 @@ class SessionContext { key.Name(), ResourceEntry{created, std::type_index(typeid(T))}); } } catch (...) { - // Share this attempt's failure with its waiters without caching it. + // 将本次尝试的失败共享给等待者,但不缓存。 flight->failure = std::current_exception(); created.reset(); } diff --git a/include/core/thread_pool.h b/include/core/thread_pool.h index c2941700..d6c1082d 100644 --- a/include/core/thread_pool.h +++ b/include/core/thread_pool.h @@ -43,8 +43,8 @@ class ThreadPool { }); } } catch (...) { - // A failed constructor never runs ~ThreadPool. Join any workers before - // their queue, mutex and condition variable are destroyed. + // 构造失败时不会执行 ~ThreadPool,须在队列、互斥锁和条件变量销毁前 + // join 所有工作线程。 StopAndJoin(); throw; } diff --git a/include/edgeflow/log.h b/include/edgeflow/log.h index ed17df86..118d440b 100644 --- a/include/edgeflow/log.h +++ b/include/edgeflow/log.h @@ -32,25 +32,25 @@ typedef enum { #endif /** - * @brief Set the process-wide log level. - * @param name Compatibility name for the AlgBase logging contract. - * @param level Integer log level in the inclusive range [0, 5]. - * @return 0 on success, -1 when level is outside [0, 5]. + * @brief 设置进程级日志级别。 + * @param name 为兼容 AlgBase 日志契约保留的名称。 + * @param level 整数日志级别,闭区间 [0, 5]。 + * @return 成功返回 0;level 超出 [0, 5] 时返回 -1。 */ COMPANY_ALG_API int AlgBase_setLogLevelByName(const char* name, int level) ALG_LOG_NOEXCEPT; /** - * @brief Return the current process-wide log level. - * @param name Compatibility name for the AlgBase logging contract. + * @brief 返回当前进程级日志级别。 + * @param name 为兼容 AlgBase 日志契约保留的名称。 */ COMPANY_ALG_API int AlgBase_getLogLevelByName(const char* name) ALG_LOG_NOEXCEPT; /** - * @brief Write one formatted log record to stderr. + * @brief 向 stderr 写入一条格式化日志。 * - * The function preserves fmt exactly and does not append a newline. + * 原样保留 fmt,不追加换行符。 */ ALG_LOG_PRINTF_ATTRIBUTE(3, 4) COMPANY_ALG_API void AlgBase_logPrint(const char* level_label, const char* name, diff --git a/include/edgeflow/operator/types.h b/include/edgeflow/operator/types.h index 10defc4b..4daba309 100644 --- a/include/edgeflow/operator/types.h +++ b/include/edgeflow/operator/types.h @@ -1,4 +1,4 @@ #pragma once -// Public entrypoint; local platform data declarations live separately. +// 公共入口;本地平台数据声明单独存放。 #include "platform_mock/operator_data_types.h" diff --git a/include/engine/backend_identity.h b/include/engine/backend_identity.h index 97f90249..9fce4b1b 100644 --- a/include/engine/backend_identity.h +++ b/include/engine/backend_identity.h @@ -7,9 +7,8 @@ namespace llm_edgeflow { -// Declares a Backend provider's type once through Derived::kBackendType. The -// runtime factory still compares the provider and every loaded session with -// the requested Definition. +// 通过 Derived::kBackendType 一次性声明 Backend 提供者的类型。运行时工厂 +// 仍会将提供者及每个已加载会话与所请求的 Definition 比对。 template class BackendIdentity : public IInferenceBackend { public: @@ -19,8 +18,8 @@ class BackendIdentity : public IInferenceBackend { } }; -// Definition carrying BackendClass's type; callers add protocols, -// concurrency, configuration fields and validator. +// 携带 BackendClass 类型的 Definition;调用方补充协议、并发度、配置字段 +// 和校验器。 template BackendDefinition MakeBackendDefinition() { BackendDefinition definition; diff --git a/include/engine/backend_interface.h b/include/engine/backend_interface.h index f3ee1ba2..754bf9d4 100644 --- a/include/engine/backend_interface.h +++ b/include/engine/backend_interface.h @@ -41,11 +41,10 @@ class ITensorGraphSession : public IBackendSession { }; /** - * @brief Vendor-neutral synchronous text generation session. + * @brief 与厂商无关的同步文本生成会话。 * - * The Model supplies an already formatted prompt. A concrete Backend may - * delegate the whole operation to a managed engine or use an internal - * autoregressive decoder and the shared sampler. + * Model 提供已格式化的 prompt。具体 Backend 可以把整个操作委托给托管 + * 引擎,也可以使用内部自回归解码器和共享采样器。 */ class ITextGenerationSession : public IBackendSession { public: @@ -56,15 +55,15 @@ class ITextGenerationSession : public IBackendSession { }; /** - * @brief Vendor-neutral execution target selected by the deployment ingress. + * @brief 由部署入口选定的、与厂商无关的执行目标。 */ struct ExecutionTarget { std::optional device_id; std::string platform; }; -// Model-prepared RGB planes. The patch size describes the model's spatial -// input layout; no vendor types or encoded image files cross this boundary. +// Model 预处理好的 RGB 平面。patch 尺寸描述模型的空间输入布局; +// 厂商类型和编码后的图像文件均不跨越此边界。 struct ImageTextInput { std::string prompt; int width = 0; @@ -80,9 +79,9 @@ class IImageTextGenerationSession : public IBackendSession { std::string* diagnostic = nullptr) noexcept = 0; }; -// Owned, unpooled hidden states of generated tokens, in generation order. -// These are not input-token states or a sentence embedding. Each row matches -// one token_id; early EOS may return fewer rows than the requested limit. +// 生成 token 的隐藏状态,自有且不池化,按生成顺序排列。它们既不是输入 +// token 的状态,也不是句向量。每行对应一个 token_id;提前遇到 EOS 时, +// 行数可能少于请求上限。 struct GeneratedTokenEmbeddings { std::vector token_ids; std::vector> values; @@ -90,9 +89,8 @@ struct GeneratedTokenEmbeddings { class IGeneratedTokenEmbeddingSession : public IBackendSession { public: - // Greedy generation, without an implicit chat template. max_tokens: 1..64. - // Clear output on failure. Empty output is valid for immediate EOS; the - // consuming Model decides whether that represents a usable feature. + // 贪心生成,不隐式套用 chat 模板。max_tokens 取 1..64。失败时清空输出。 + // 立即遇到 EOS 时输出为空也合法,由消费方 Model 决定其是否为可用特征。 virtual int GenerateEmbeddings( const std::string& formatted_prompt, bool add_bos, int max_tokens, GeneratedTokenEmbeddings* output, diff --git a/include/engine/fixed_batch_executor.h b/include/engine/fixed_batch_executor.h index d59ba9cc..3b68fdc1 100644 --- a/include/engine/fixed_batch_executor.h +++ b/include/engine/fixed_batch_executor.h @@ -32,10 +32,9 @@ struct BatchSlice { */ class FixedBatchExecutor { public: - // Single-item model semantics on a dynamic session. Tensor/fixed-batch - // models continue to use Execute and explicitly prepare their padded batch. - // The callback owns only one input -> one output; Execute owns provenance and - // rollback. exception_code preserves a model's established exception mapping. + // 动态会话上的单条目模型语义。张量/固定批模型仍使用 Execute,并显式准备 + // 填充后的批。回调只负责一个输入 -> 一个输出;Execute 负责来源追踪和 + // 回滚。exception_code 保留模型既有的异常映射。 template static int ExecuteItems(const std::vector>& inputs, const BatchPolicy& policy, RunItem&& run_item, diff --git a/include/engine/inference_definition.h b/include/engine/inference_definition.h index e1f016b1..b828b06c 100644 --- a/include/engine/inference_definition.h +++ b/include/engine/inference_definition.h @@ -82,9 +82,8 @@ struct ModelDefinition { ExecutionProtocol required_protocol = ExecutionProtocol::kTensorGraph; std::vector config_fields; InferenceConcurrency concurrency = InferenceConcurrency::kSerialized; - // Pure validation of schema-normalized config, shared by preflight and - // runtime materialization. - // No session allocation, model loading or external I/O. + // 对 schema 归一化后的配置做纯校验,由预检和运行时实例化共用。 + // 不分配会话、不加载模型、不做外部 I/O。 std::function validate_config; }; @@ -97,8 +96,7 @@ struct BackendDefinition { std::vector supported_protocols; std::vector config_fields; InferenceConcurrency concurrency = InferenceConcurrency::kSerialized; - // Pure validation of normalized config; no session allocation or external - // I/O. + // 对归一化后的配置做纯校验;不分配会话,不做外部 I/O。 std::function validate_config; }; diff --git a/include/engine/model_identity.h b/include/engine/model_identity.h index 0420ca03..f6393990 100644 --- a/include/engine/model_identity.h +++ b/include/engine/model_identity.h @@ -8,10 +8,9 @@ namespace llm_edgeflow { -// Declares a Model's identity once. Derived supplies kModelType and -// kConcurrency; the capability follows from the implemented interface. The -// registered Definition starts from the same values, and the runtime factory -// still checks every created instance against its Definition. +// 一次性声明 Model 的标识。Derived 提供 kModelType 和 kConcurrency, +// 能力由所实现的接口决定。注册的 Definition 以相同的值为起点,运行时工厂 +// 仍会将每个创建的实例与其 Definition 比对。 template class ModelIdentity : public CapabilityInterface { public: @@ -31,8 +30,8 @@ class ModelIdentity : public CapabilityInterface { } }; -// Definition carrying ModelClass's identity; callers add the description, -// protocol, configuration fields and validator. +// 携带 ModelClass 标识的 Definition;调用方补充描述、协议、配置字段 +// 和校验器。 template ModelDefinition MakeModelDefinition() { ModelDefinition definition; diff --git a/include/engine/model_interface.h b/include/engine/model_interface.h index 91357897..a0045e5e 100644 --- a/include/engine/model_interface.h +++ b/include/engine/model_interface.h @@ -20,14 +20,13 @@ class IModel { virtual const std::string& ModelType() const noexcept = 0; virtual const std::string& Capability() const noexcept = 0; - // Describes only Model semantic reentrancy. Runtime planning combines this - // value with the selected Backend concurrency and applies the stricter one. + // 仅描述 Model 语义上的可重入性。运行时规划会结合所选 Backend 的 + // 并发度,取两者中更严格的一个。 virtual InferenceConcurrency Concurrency() const noexcept = 0; }; -// Capability calls clear the optional diagnostic on entry and return a reason -// on failure. The caller owns it for this invocation; Models retain no mutable -// last-error state. Integer return codes and output rollback remain unchanged. +// 能力调用在入口清空可选的诊断信息,失败时写入原因。诊断信息归本次调用方 +// 所有,Model 不保留可变的 last-error 状态。整数返回码和输出回滚语义不变。 /** * @brief Embedding 向量化模型能力接口 diff --git a/include/engine/tensor.h b/include/engine/tensor.h index a402b696..cf28d4eb 100644 --- a/include/engine/tensor.h +++ b/include/engine/tensor.h @@ -120,8 +120,7 @@ struct NativeTypeTraits { */ class HostTensorBuffer : public ITensorBuffer { public: - // Retain the requested size on failure so a failed allocation is never - // mistaken for a valid zero-length buffer. + // 失败时保留请求的大小,避免把分配失败误认为合法的零长度缓冲区。 explicit HostTensorBuffer(size_t byte_size) : size_(byte_size) { if (byte_size > 0) { size_t alignment = 64; diff --git a/include/nodes/configuration_snapshot.h b/include/nodes/configuration_snapshot.h index 9c0fa6d3..d10a8165 100644 --- a/include/nodes/configuration_snapshot.h +++ b/include/nodes/configuration_snapshot.h @@ -16,10 +16,9 @@ namespace llm_edgeflow { /** - * @brief Connection facts of input ports captured defensively during Init. + * @brief Init 期间防御性捕获的输入端口连接事实。 * - * Immutable after initialization; distinguishes semantic-only parsing from - * validation against explicit input bindings. + * 初始化后不可变;用于区分仅做语义解析与按显式输入绑定校验。 */ struct BindingFacts { bool has_bindings = false; @@ -43,16 +42,14 @@ inline BindingFacts MakeBindingFacts(const NodeInitContext& ctx) { } /** - * @brief Thread-safe configuration snapshot manager for node instances. + * @brief Node 实例的线程安全配置快照管理器。 * - * Enforces: - * - Atomic snapshot acquisition for readers via atomic load acquire (without - * the writer mutex; shared_ptr atomic operations may use internal locks). - * - Serialized, transaction-safe candidate building and atomic publication for - * writers. - * - Readers safely retain old snapshots for arbitrary batch duration. - * - Failed candidate building / validation never overwrites active - * configuration. + * 保证: + * - 读者通过 atomic load acquire 原子获取快照 (不持有写锁;shared_ptr + * 的原子操作可能使用内部锁)。 + * - 写者串行、事务安全地构建候选配置并原子发布。 + * - 读者可在任意批次处理期间安全持有旧快照。 + * - 候选构建或校验失败绝不覆盖当前生效的配置。 */ template class ConfigurationSnapshot { diff --git a/include/nodes/function_node.h b/include/nodes/function_node.h index d88d6356..310d4c5c 100644 --- a/include/nodes/function_node.h +++ b/include/nodes/function_node.h @@ -167,8 +167,8 @@ struct BatchRunSignature { std::is_invocable_v; } }(); - // InvokeBatch returns void when no signature matches, without instantiating - // an invalid call. Its dispatch order is the source of truth for Result. + // 无签名匹配时 InvokeBatch 返回 void,且不会实例化非法调用。 + // Result 以其分派顺序为准。 using Result = decltype(InvokeBatch( std::declval(), std::declval(), std::declval(), std::declval(), @@ -186,8 +186,8 @@ auto InvokeTextHook(const Fn& fn, const std::string& text, } } -// Both LLM hooks accept implicit string conversions and explicit string_view -// copies. Arithmetic results must never become silently truncated characters. +// 两个 LLM 钩子都接受可隐式转换为 string 的值和显式的 string_view 拷贝。 +// 算术结果绝不能被静默截断为字符。 template inline constexpr bool kIsHookText = std::is_convertible_v || @@ -213,7 +213,7 @@ struct TextHookSignature { } // namespace detail // --------------------------------------------------------------------------- -// Map Entrypoint +// Map 入口 // --------------------------------------------------------------------------- template in, Output out, } // --------------------------------------------------------------------------- -// Batch Entrypoint +// Batch 入口 // --------------------------------------------------------------------------- enum class InputFlow { @@ -368,8 +368,7 @@ enum class InputFlow { AggregateByRequest, }; -// Flow metadata describes the existing port contract; it does not transform -// data. +// 流元数据描述现有的端口契约,不转换数据。 struct PortFlow { std::string cardinality = "1:1"; std::string provenance = "preserve"; @@ -603,8 +602,8 @@ inline InputBindingHolder Optional(std::string name, std::move(name), member_ptr, false, flow)); } -// Optional connection whose absent request value is handled by the algorithm -// (e.g. template missing-variable policy or conditionally used context). +// 可选连接:请求中缺失的值由算法处理 +// (例如模板缺失变量策略或按条件使用的上下文)。 template inline InputBindingHolder OptionalValue( std::string name, const BatchT* InputsT::*member_ptr, PortFlow flow = {}) { @@ -1330,7 +1329,7 @@ class BatchSpec { std::vector biz_names_; }; -// Batch overloads +// Batch 重载 template inline auto MakeBatchSpec(InputsOf inputs, @@ -1371,13 +1370,13 @@ inline auto MakeBatchSpec(InputsOf inputs, } // --------------------------------------------------------------------------- -// AuthorNode definition: supports both MapSpec and BatchSpec +// AuthorNode 定义:同时支持 MapSpec 和 BatchSpec // --------------------------------------------------------------------------- template class AuthorNode; -// BatchSpec Specialization +// BatchSpec 特化 template class AuthorNode> @@ -1496,7 +1495,7 @@ class AuthorNode> spec_.Output().Publish(req_ctx, std::move(output)); return 0; } else { - // Unreachable in a valid program; avoid cascading template errors. + // 合法程序中不可达;避免引发连锁模板错误。 return this->Fail(req_ctx, node_error::author_node::kInternalError, "Invalid Batch Run signature"); } @@ -1518,8 +1517,8 @@ struct MapInputs { const InputBatchT* items = nullptr; }; -// Runs a Map on the Batch runtime: one required anchor input, one preserved -// output and a per-item loop that names the failing item. +// 在 Batch 运行时上执行 Map:一个必需的锚点输入、一个保序输出, +// 以及能指出失败条目的逐条循环。 template auto MakeMapRuntimeSpec(std::string node_name, SpecT map) { using InputBatch = typename SpecT::InputBatch; @@ -1568,8 +1567,8 @@ auto MakeMapRuntimeSpec(std::string node_name, SpecT map) { } // namespace detail -// MapSpec Specialization: the Definition comes from MapSpec; execution, -// parameters and Control share the Batch runtime. +// MapSpec 特化:Definition 来自 MapSpec;执行、参数和 Control 共用 +// Batch 运行时。 template class AuthorNode> @@ -1589,7 +1588,7 @@ class AuthorNode> }; // --------------------------------------------------------------------------- -// LLM Text Shortcut Spec Factory +// LLM 文本快捷 Spec 工厂 // --------------------------------------------------------------------------- struct LlmTextInputs { @@ -1661,8 +1660,8 @@ inline auto MakeLlmTextSpec(Input in_port, auto llm_res = models.generator.Generate(prompts, options); if (!llm_res.ok()) return llm_res; - // Copy views before assigning to owned output, including views into - // item.data itself. No partial output is published on hook failure. + // 赋值给自有输出前先复制 view,包括指向 item.data 自身的 view。 + // 钩子失败时不发布部分输出。 auto outputs = std::move(llm_res).value(); for (auto& item : outputs) { auto res = detail::InvokeTextHook(format_answer, diff --git a/include/nodes/generate_options_config.h b/include/nodes/generate_options_config.h index b40bc7c4..c81c3be2 100644 --- a/include/nodes/generate_options_config.h +++ b/include/nodes/generate_options_config.h @@ -12,7 +12,7 @@ namespace llm_edgeflow { -// Shared sampling fields for Nodes; each caller chooses its token default. +// Node 共享的采样字段;各调用方自行选择 token 默认值。 inline std::vector GenerateOptionsFields( int default_max_tokens) { return { @@ -72,7 +72,7 @@ inline std::vector GenerateOptionsFields( "\"\"];命中后输出不包含停止文本。"}}; } -// Consume fields already validated and defaulted by NodeConfigParser. +// 消费已由 NodeConfigParser 校验并填充默认值的字段。 inline bool ParseGenerateOptions(const nlohmann::json& config, GenerateOptions* options, std::string* diagnostic) { diff --git a/include/nodes/model_calls.h b/include/nodes/model_calls.h index 68fbf25c..3dc896e8 100644 --- a/include/nodes/model_calls.h +++ b/include/nodes/model_calls.h @@ -66,8 +66,8 @@ NodeResult InvokeAlignedModel( namespace detail { -// Owns one bound model capability and its slot identity. Calls are move-only, -// so each Node instance keeps exclusive ownership of its bindings. +// 持有一个已绑定的模型能力及其槽位标识。调用对象只能移动, +// 因此每个 Node 实例独占其绑定。 template class ModelCallBase { public: diff --git a/include/nodes/node_config_parser.h b/include/nodes/node_config_parser.h index f5574177..4283c7f7 100644 --- a/include/nodes/node_config_parser.h +++ b/include/nodes/node_config_parser.h @@ -11,8 +11,8 @@ namespace llm_edgeflow { -// Optional authoring helper: one field list and one semantic parser for an -// ordinary parameter struct. It neither reads files nor serializes JSON. +// 可选的编写辅助:为普通参数 struct 提供一份字段列表和一个语义解析器。 +// 不读文件,也不序列化 JSON。 template class NodeConfigParser { public: @@ -26,8 +26,8 @@ class NodeConfigParser { return fields_; } - // For raw Node configuration or an already decoded Control payload. Reuses - // the same field validation/defaults as PipelineValidator and AuthorNode. + // 用于原始 Node 配置或已解码的 Control payload。复用与 PipelineValidator + // 和 AuthorNode 相同的字段校验与默认值。 std::optional Parse(const nlohmann::json& config, std::string* error = nullptr) const noexcept { if (error) error->clear(); @@ -50,11 +50,9 @@ class NodeConfigParser { return std::nullopt; } - // Only for input already validated/defaulted with Fields(), such as a - // Definition's validate_config callback or AuthorNode initialization. - // Forwards the existing JSON object directly; does not copy or normalize it - // again. The parser owns semantic checks and must return owned parameter - // data. + // 仅用于已按 Fields() 校验并填充默认值的输入,如 Definition 的 + // validate_config 回调或 AuthorNode 初始化。直接转发现有 JSON 对象, + // 不再复制或归一化。解析器负责语义检查,并须返回自有的参数数据。 std::optional ParseNormalized( const nlohmann::json& config, std::string* error = nullptr) const noexcept { diff --git a/include/nodes/node_error_codes.h b/include/nodes/node_error_codes.h index 6ef4e53b..c6ea6e6f 100644 --- a/include/nodes/node_error_codes.h +++ b/include/nodes/node_error_codes.h @@ -3,7 +3,7 @@ namespace llm_edgeflow { namespace node_error { -// Shared authoring failures and operation-specific business failures. +// 共享的编写期失败码和各操作专属的业务失败码。 namespace control { inline constexpr int kInvalidRequest = -1; } // namespace control diff --git a/include/nodes/node_result.h b/include/nodes/node_result.h index fab5d812..893f10a3 100644 --- a/include/nodes/node_result.h +++ b/include/nodes/node_result.h @@ -158,8 +158,8 @@ struct NodeFailure { } return false; }; - // Only omit the prefix when the complete key is already present. A request - // alone, or a numeric prefix of another item's key, does not identify it. + // 仅当完整键已出现时才省略前缀。仅有请求号,或另一条目键的数字前缀, + // 都不足以标识该条目。 if (detail.operation.empty() || contains_token(detail.operation)) { if (!detail.key.has_value() || contains_token("req_id=" + std::to_string(detail.key->req_id) + @@ -195,10 +195,9 @@ struct NodeFailure { template class [[nodiscard]] NodeResult { public: - // Disallow default construction to avoid uninitialized / ambiguous states. + // 禁止默认构造,避免未初始化或语义不明的状态。 NodeResult() = delete; - // Construct from value NodeResult(T value) : storage_(std::move(value)) {} // NOLINT static NodeResult Success(T value) { diff --git a/include/nodes/parameter_binding.h b/include/nodes/parameter_binding.h index 4a05b795..f367d941 100644 --- a/include/nodes/parameter_binding.h +++ b/include/nodes/parameter_binding.h @@ -209,7 +209,7 @@ class ConcreteFieldBinding final : public ParameterFieldBinding { if (has_default_) { def.default_value = FieldTypeTraits::ToJson(default_val_); - // Validate that default_value satisfies minimum, maximum, and enum_values + // default_value 必须满足 minimum、maximum 和 enum_values if (minimum_.has_value()) { if constexpr (std::is_arithmetic_v) { if (static_cast(default_val_) < *minimum_) { @@ -271,7 +271,7 @@ class ConcreteFieldBinding final : public ParameterFieldBinding { } return false; } - // Additional bounds validation + // 再校验取值边界 if (minimum_.has_value()) { if constexpr (std::is_arithmetic_v) { if (static_cast(extracted) < *minimum_) { @@ -470,7 +470,7 @@ class Parameters { bindings_.push_back(holder->Clone()); } - // Materialize config field definitions to validate defaults eagerly + // 实例化配置字段定义,以便提前校验默认值 definitions_.reserve(bindings_.size()); for (const auto& binding : bindings_) { definitions_.push_back(binding->ToFieldDefinition()); @@ -590,8 +590,8 @@ class Parameters { return *this; } - // The parser supplies complex owned members; typed bindings then assign - // simple members, and Validate checks the complete parameter object. + // 解析器提供复杂的自有成员;之后由类型化绑定赋值简单成员, + // Validate 再检查完整的参数对象。 Parameters& WithParser(NodeConfigParser parser) { if (complex_parser_) { throw std::invalid_argument( diff --git a/include/nodes/session_resources.h b/include/nodes/session_resources.h index bc174614..9a39810d 100644 --- a/include/nodes/session_resources.h +++ b/include/nodes/session_resources.h @@ -10,8 +10,8 @@ namespace llm_edgeflow { -// Borrowed for the node's session lifetime. Models remain explicit Spec -// dependencies; this facade cannot look up models or access request values. +// 在 Node 的会话生命周期内借用。Model 仍是显式的 Spec 依赖; +// 此 facade 不能查找模型或访问请求值。 class SessionResources { public: SessionResources() = default; @@ -21,9 +21,8 @@ class SessionResources { return Session().GetModelManager().GetModelRevision(model_id); } - // Ordinary factories return a value or NodeFailure. The existing session - // single-flight shares failures with waiters and permits retry on the next - // call. Other exceptions still reach the node runtime's exception barrier. + // 普通工厂返回值或 NodeFailure。现有的会话 single-flight 机制会把失败共享给 + // 等待者,并允许下次调用重试。其他异常仍会到达 Node 运行时的异常屏障。 template NodeResult> GetOrCreateResult( const SessionResourceKey& key, Factory&& factory) const { diff --git a/include/nodes/text_template.h b/include/nodes/text_template.h index 00e9e79b..bd69280a 100644 --- a/include/nodes/text_template.h +++ b/include/nodes/text_template.h @@ -24,9 +24,8 @@ inline bool IsTextTemplateIdentifier(std::string_view name) { return true; } -// Only {{name}} placeholders are variables. Single braces remain literal (for -// example JSON); double braces never escape variables. Only compile the -// original template, never inserted request text. +// 只有 {{name}} 占位符是变量。单花括号保持字面量 (如 JSON);双花括号始终 +// 表示变量,无转义语法。只编译原始模板,绝不编译插入的请求文本。 inline bool ParseTextTemplate(const std::string& pattern, std::vector* tokens, std::string* error = nullptr) { diff --git a/include/nodes/traceable_batch_operations.h b/include/nodes/traceable_batch_operations.h index 9703306b..eef2df76 100644 --- a/include/nodes/traceable_batch_operations.h +++ b/include/nodes/traceable_batch_operations.h @@ -20,8 +20,8 @@ namespace llm_edgeflow { -// TraceableItemKeyHash and std::hash are defined in -// node_result.h for repository-wide availability. +// TraceableItemKeyHash 和 std::hash 定义在 +// node_result.h,以便全仓库使用。 // ============================================================================ // 1. JoinByItem @@ -67,8 +67,7 @@ class ItemJoinView { std::vector rows) : left_(&left), right_(&right), rows_(std::move(rows)) {} - // Disallow construction from temporary / rvalue batches (both non-const and - // const) + // 禁止从临时/右值批次构造 (含非 const 和 const) ItemJoinView(std::vector>&&, const std::vector>&, std::vector) = delete; @@ -153,7 +152,7 @@ NodeResult> JoinByItem( } } - // Check for unknown keys in right (in both kExact and kLeft) + // kExact 和 kLeft 模式都检查 right 中的未知键 for (const auto& r_item : right) { TraceableItemKey r_key{r_item.req_id, r_item.sub_id}; if (left_key_to_idx.find(r_key) == left_key_to_idx.end()) { @@ -478,7 +477,7 @@ void GroupByRequest(const std::vector>&&, const std::vector>&&) = delete; // ============================================================================ -// 3. SelectBatch and ScatterReplace +// 3. SelectBatch 与 ScatterReplace // ============================================================================ template diff --git a/include/nodes/traceable_batch_validation.h b/include/nodes/traceable_batch_validation.h index 7af4ecf5..d4997c24 100644 --- a/include/nodes/traceable_batch_validation.h +++ b/include/nodes/traceable_batch_validation.h @@ -24,10 +24,10 @@ struct TraceableAlignmentResult { }; /** - * @brief Validate a strict 1:1, order-preserving Traceable batch contract. + * @brief 校验严格 1:1、保序的 Traceable 批次契约。 * - * For a count mismatch, mismatch_index is the first index missing from either - * batch. For an aligned result, mismatch_index equals the batch size. + * 数量不一致时,mismatch_index 是任一批次中首个缺失的索引; + * 完全对齐时,mismatch_index 等于批大小。 */ template [[nodiscard]] TraceableAlignmentResult ValidatePreservedTraceableAlignment( diff --git a/include/platform_mock/error_codes.h b/include/platform_mock/error_codes.h index 6d72da98..a7ab6b03 100644 --- a/include/platform_mock/error_codes.h +++ b/include/platform_mock/error_codes.h @@ -1,8 +1,8 @@ #ifndef EDGEFLOW_PLATFORM_MOCK_ERROR_CODES_H_ #define EDGEFLOW_PLATFORM_MOCK_ERROR_CODES_H_ -// Local platform mock declarations for this repository's Demo and tests. -// These are existing external-environment substitutes, not company SDK headers. +// 本仓库 Demo 和测试使用的本地平台 mock 声明。 +// 它们是现有外部环境的替代品,不是公司 SDK 头文件。 #define COMPANY_ALG_SUCCESS (0) // 成功 #define COMPANY_ALG_ERR_INVALID_HANDLE (-1) // 无效句柄 (nullptr 或野指针) diff --git a/include/platform_mock/operator_data_types.h b/include/platform_mock/operator_data_types.h index 749ab5f2..da5c863a 100644 --- a/include/platform_mock/operator_data_types.h +++ b/include/platform_mock/operator_data_types.h @@ -1,8 +1,8 @@ #ifndef EDGEFLOW_PLATFORM_MOCK_OPERATOR_DATA_TYPES_H_ #define EDGEFLOW_PLATFORM_MOCK_OPERATOR_DATA_TYPES_H_ -// Local platform mock declarations for this repository's Demo and tests. -// These are existing external-environment substitutes, not company SDK headers. +// 本仓库 Demo 和测试使用的本地平台 mock 声明。 +// 它们是现有外部环境的替代品,不是公司 SDK 头文件。 #include #include diff --git a/include/platform_mock/operator_types.h b/include/platform_mock/operator_types.h index 6884f2b5..a703a2a3 100644 --- a/include/platform_mock/operator_types.h +++ b/include/platform_mock/operator_types.h @@ -1,8 +1,8 @@ #ifndef EDGEFLOW_PLATFORM_MOCK_OPERATOR_TYPES_H_ #define EDGEFLOW_PLATFORM_MOCK_OPERATOR_TYPES_H_ -// Local platform mock declarations for this repository's Demo and tests. -// These are existing external-environment substitutes, not company SDK headers. +// 本仓库 Demo 和测试使用的本地平台 mock 声明。 +// 它们是现有外部环境的替代品,不是公司 SDK 头文件。 #include #include @@ -17,7 +17,7 @@ namespace llm_edgeflow::operator_api { */ enum class ComputePlatform : int32_t { kUnknown = 0, - kAx650 = 1, // AX650 NPU + kAx650 = 1, // 爱芯元智 AX650 NPU kAscend310P = 2, // 华为昇腾 310P NPU kAscend910B = 3, // 华为昇腾 910B NPU kRk3588 = 4, // 瑞芯微 RK3588 NPU diff --git a/scripts/check_doc_links.py b/scripts/check_doc_links.py index c0f5b68e..2298b95c 100755 --- a/scripts/check_doc_links.py +++ b/scripts/check_doc_links.py @@ -24,7 +24,7 @@ def link_destination(text, position): - """Read an angle-delimited or balanced bare destination and its end offset.""" + """读取尖括号包裹或括号平衡的裸目标地址及其结束偏移。""" angle_delimited = text[position:position + 1] == "<" if angle_delimited: position += 1 @@ -56,7 +56,7 @@ def link_destination(text, position): def inline_link_targets(text): - """Yield single-line inline destinations, excluding optional link titles.""" + """逐个产出单行内联链接目标,不含可选的链接标题。""" position = 0 while match := INLINE_LINK_START.search(text, position): position = match.end() @@ -74,7 +74,7 @@ def inline_link_targets(text): def slugify(heading): - """GitHub heading anchor: lowercase, drop punctuation/symbols, spaces to '-'.""" + """GitHub 标题锚点:转小写,去掉标点/符号,空格转为 '-'。""" text = re.sub(r"`([^`]*)`", r"\1", heading) text = re.sub(r"!?\[([^\]]*)\]\([^)]*\)", r"\1", text) text = re.sub(r"<[^>]+>", "", text) @@ -90,7 +90,7 @@ def slugify(heading): def prose_lines(path): - """Yield (line number, text) outside fenced code blocks.""" + """产出代码围栏之外的 (行号, 文本)。""" fence = None for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): match = FENCE.match(line) @@ -134,7 +134,7 @@ def markdown_files(root): def check(root): - """Return (errors, counters) for every relative link in the Markdown files.""" + """返回 Markdown 文件中所有相对链接的 (errors, counters)。""" root = root.resolve() errors, cache = [], {} counters = {"files": 0, "links": 0, "anchor_links": 0} @@ -194,7 +194,7 @@ def write(root, name, text): "```markdown\n[skip](missing.md)\n```\n## Local\n") errors, counters = check(root) assert errors == [], errors - # Seven inline links and one img src; the https link is skipped. + # 七个内联链接和一个 img src;https 链接会被跳过。 assert counters == {"files": 2, "links": 8, "anchor_links": 4}, counters write(root, "guide/guide_(old).md", "# Heading\n") diff --git a/scripts/check_layer_dependencies.py b/scripts/check_layer_dependencies.py index 03793907..e75fe52e 100644 --- a/scripts/check_layer_dependencies.py +++ b/scripts/check_layer_dependencies.py @@ -7,8 +7,8 @@ import tempfile -# Header-only runtime and authoring contracts intentionally shared with Nodes. -# Additions require an explicit ownership decision, not a directory-wide exemption. +# 有意与 Node 共享的纯头文件运行时及编写契约。 +# 新增条目需要明确的归属决定,而不是按目录整体豁免。 NODE_CORE_CONTRACTS = set( (Path(__file__).resolve().parents[1] / "cmake_ext/node_core_contracts.txt") .read_text(encoding="utf-8").splitlines()) @@ -39,7 +39,7 @@ def forbidden(source, target): src, dst = owner(source), owner(target) if src in {"Contracts", "Integration", "Orchestration", "Capability Nodes", "Model Execution"} and dst == "Support": return True - # Shared contracts must not conceal a dependency on orchestration internals. + # 共享契约不得隐藏对编排内部实现的依赖。 if source in NODE_CORE_PATHS and dst == "Orchestration" and target not in NODE_CORE_PATHS: return True if src == "Contracts" and dst not in {"Contracts", "Support"}: @@ -109,13 +109,13 @@ def check(root): if vendors and not any(relative.startswith(f"src/engine/backends/{vendor}/") for vendor in vendors): errors.append(f"{prefix}: vendor header outside its concrete Backend") continue - # Respect quoted local includes, root-relative paths, public - # include paths and private src paths, including ../ aliases. + # 支持带引号的本地 include、根相对路径、公共 include 路径 + # 和私有 src 路径,包括 ../ 别名。 candidates = (source.parent / include, root / include, root / "include" / include, root / "src" / include) target = next((p.resolve() for p in candidates if p.is_file()), None) if target is None or root not in target.parents: - continue # standard library / fetched dependencies + continue # 标准库 / 拉取的依赖 target_name = target.relative_to(root).as_posix() if forbidden(relative, target_name): errors.append(f"{prefix}: {owner(relative)} -> {owner(target_name)} forbidden ({target_name})") @@ -175,7 +175,7 @@ def write(path, content=""): file = write(path, directive + "\n") assert check(root), (path, directive) file.unlink() - # A local helper must not conceal a reverse dependency. + # 本地辅助文件不得隐藏反向依赖。 write("src/engine/models/demo/model.cpp", '#include "helper.h"\n') helper = write("src/engine/models/demo/helper.h", '#include "adapter/io_converter.h"\n') assert check(root) diff --git a/scripts/check_layer_isolation.sh b/scripts/check_layer_isolation.sh index 25bd4a33..2eef6586 100755 --- a/scripts/check_layer_isolation.sh +++ b/scripts/check_layer_isolation.sh @@ -11,7 +11,7 @@ if [[ "${1:-}" == "--self-test" ]]; then TMP_TEST_DIR=$(mktemp -d /tmp/layerguard_test_XXXXXX) trap 'rm -rf "${TMP_TEST_DIR}"' EXIT - # Test Case 1: Missing directory must cause script to fail + # 用例 1:缺少目录时脚本必须失败 mkdir -p "${TMP_TEST_DIR}/empty_repo" set +e REPO_ROOT="${TMP_TEST_DIR}/empty_repo" bash "${SCRIPT_PATH}" >/dev/null 2>&1 @@ -22,7 +22,7 @@ if [[ "${1:-}" == "--self-test" ]]; then exit 1 fi - # Test Case 2: Injected illegal include must cause script to fail + # 用例 2:注入非法 include 时脚本必须失败 mkdir -p "${TMP_TEST_DIR}/violation_repo/src/common_nodes" mkdir -p "${TMP_TEST_DIR}/violation_repo/src/custom_nodes" mkdir -p "${TMP_TEST_DIR}/violation_repo/src/adapter/biz" @@ -40,7 +40,7 @@ if [[ "${1:-}" == "--self-test" ]]; then exit 1 fi - # Test Case 3: Lower layers must not regain business-owned Blackboard keys. + # 用例 3:下层不得重新引入业务专属的 Blackboard 键。 : > "${TMP_TEST_DIR}/violation_repo/src/common_nodes/bad_node.cpp" mkdir -p "${TMP_TEST_DIR}/violation_repo/src/core" echo '#include "adapter/biz_blackboard_keys.h"' > \ @@ -56,7 +56,7 @@ if [[ "${1:-}" == "--self-test" ]]; then exit 1 fi - # Test Case 4: The direct Kite SDK header must remain in its own Backend. + # 用例 4:Kite SDK 头文件只能直接出现在其自身的 Backend 中。 : > "${TMP_TEST_DIR}/violation_repo/src/core/bad_core.cpp" for KITE_INJECTION_PATH in \ "include/engine/bad_model.h" \ @@ -85,7 +85,7 @@ if [[ "${1:-}" == "--self-test" ]]; then : > "${KITE_INJECTION_FILE}" done - # Test Case 5: The direct whisper.h header must remain in its own Backend. + # 用例 5:whisper.h 头文件只能直接出现在其自身的 Backend 中。 for WHISPER_INJECTION_PATH in \ "include/engine/bad_model.h" \ "src/engine/models/whisper_asr/bad_model.cpp" \ @@ -113,7 +113,7 @@ if [[ "${1:-}" == "--self-test" ]]; then : > "${WHISPER_INJECTION_FILE}" done - # Test Case 6: Custom Nodes obey the same platform boundary as common Nodes. + # 用例 6:自定义 Node 与通用 Node 遵守相同的平台边界。 for CUSTOM_INCLUDE in \ '#include "edgeflow/operator/interface.h"' \ '# include "../adapter/biz_blackboard_keys.h"' \ @@ -134,7 +134,7 @@ if [[ "${1:-}" == "--self-test" ]]; then done : > "${TMP_TEST_DIR}/violation_repo/src/custom_nodes/bad_node.cpp" - # Test Case 7: Reusable framework code must not depend on custom implementations. + # 用例 7:可复用的框架代码不得依赖自定义实现。 for CUSTOM_CONSUMER in \ "src/common_nodes/bad_node.cpp" \ "include/nodes/bad_support.h" \ @@ -162,7 +162,7 @@ if [[ "${1:-}" == "--self-test" ]]; then : > "${CUSTOM_CONSUMER_FILE}" done - # Test Case 8: Custom source ownership is checked even before any nodes exist. + # 用例 8:即使尚无任何 Node,也要检查自定义源码的归属。 mkdir -p "${TMP_TEST_DIR}/violation_repo/include/nodes" cp "${REPO_ROOT}/include/nodes/node_base.h" \ "${TMP_TEST_DIR}/violation_repo/include/nodes/node_base.h" @@ -193,7 +193,7 @@ echo "======================================================================" echo " [LayerGuard] Checking Integration / Orchestration / Capability Nodes / Model Execution Boundaries..." echo "======================================================================" -# Rule 1: All capability nodes must keep platform structs and conversion in Integration. +# 规则 1:所有能力节点都须把平台结构及其转换留在接入适配层。 NODE_SOURCE_PATHS=("$REPO_ROOT/src/common_nodes" "$REPO_ROOT/src/custom_nodes") for NODE_SOURCE_PATH in "${NODE_SOURCE_PATHS[@]}"; do if [ ! -d "$NODE_SOURCE_PATH" ]; then @@ -213,7 +213,7 @@ if [ -n "$VIOLATIONS_NODES_INTEGRATION" ]; then fi echo "✅ [LayerGuard PASS] Zero Capability Nodes -> Integration reverse include violations." -# Rule 2: Integration Adapters (src/adapter/biz/) MUST NEVER directly include Model Execution Engine headers +# 规则 2:接入适配层 (src/adapter/biz/) 绝不能直接 include 模型执行层头文件 VIOLATIONS_INTEGRATION_EXECUTION=$(grep -rnE '#include\s*["<](engine/|src/engine/)' "$REPO_ROOT/src/adapter/biz" || true) if [ -n "$VIOLATIONS_INTEGRATION_EXECUTION" ]; then @@ -224,7 +224,7 @@ if [ -n "$VIOLATIONS_INTEGRATION_EXECUTION" ]; then fi echo "✅ [LayerGuard PASS] Zero Integration -> Model Execution illegal engine include violations." -# Rule 3: Common Nodes (src/common_nodes/) MUST NEVER depend on biz-specific nodes +# 规则 3:通用 Node (src/common_nodes/) 绝不能依赖业务专属 Node VIOLATIONS_COMMON_BIZ=$(grep -rnE '#include\s*["<](biz/|src/biz/|business/|src/business/)' "$REPO_ROOT/src/common_nodes" || true) if [ -n "$VIOLATIONS_COMMON_BIZ" ]; then @@ -234,7 +234,7 @@ if [ -n "$VIOLATIONS_COMMON_BIZ" ]; then fi echo "✅ [LayerGuard PASS] Zero Common Node -> Biz Node reverse include violations." -# Rule 3b: Framework code cannot acquire a dependency on custom Node implementations. +# 规则 3b:框架代码不得依赖自定义 Node 实现。 VIOLATIONS_CUSTOM_DEPENDENCY=$(grep -rnE \ '^[[:space:]]*#[[:space:]]*include[[:space:]]*["<]([^">]*/)?custom_nodes/' \ "$REPO_ROOT/src/common_nodes" "$REPO_ROOT/include/nodes" \ @@ -247,8 +247,8 @@ if [ -n "$VIOLATIONS_CUSTOM_DEPENDENCY" ]; then fi echo "✅ [LayerGuard PASS] Framework code does not depend on custom Node implementations." -# Rule 4: Integration owns business-facing Blackboard key names. Orchestration, Capability Nodes and Model Execution may -# depend only on neutral value contracts and resolved logical port bindings. +# 规则 4:业务 Blackboard 键名归接入适配层所有。流程编排层、能力节点层和 +# 模型执行层只能依赖中性值契约和已解析的逻辑端口绑定。 LOWER_LAYER_PATHS=( "$REPO_ROOT/include/core" "$REPO_ROOT/src/core" "$REPO_ROOT/include/nodes" "${NODE_SOURCE_PATHS[@]}" @@ -264,8 +264,8 @@ if [ -n "$VIOLATIONS_BIZ_KEYS" ]; then fi echo "✅ [LayerGuard PASS] Business Blackboard keys remain owned by Integration." -# Rule 4b: The direct Kite SDK belongs only to its concrete Backend. Check -# before build-specific guards so the isolation self-test needs no SDK/build. +# 规则 4b:Kite SDK 只能直接出现在其具体 Backend 中。在构建相关的守卫之前 +# 检查,使隔离自测无需 SDK 或构建。 KITE_VENDOR_OUTSIDE_BACKEND=$(grep -rnE \ '^[[:space:]]*#[[:space:]]*include[[:space:]]*["<]([^">]*/)?kiteLLM\.h[">]' \ "$REPO_ROOT/include" "$REPO_ROOT/src" "$REPO_ROOT/demo" 2>/dev/null | \ @@ -277,7 +277,7 @@ if [ -n "$KITE_VENDOR_OUTSIDE_BACKEND" ]; then fi echo "✅ [LayerGuard PASS] The kiteLLM vendor header stays inside its concrete Backend." -# Rule 4c: The direct whisper.h header belongs only to its concrete Backend. +# 规则 4c:whisper.h 头文件只能直接出现在其具体 Backend 中。 WHISPER_VENDOR_OUTSIDE_BACKEND=$(grep -rnE \ '^[[:space:]]*#[[:space:]]*include[[:space:]]*["<]([^">]*/)?whisper\.h[">]' \ "$REPO_ROOT/include" "$REPO_ROOT/src" "$REPO_ROOT/demo" 2>/dev/null | \ @@ -289,7 +289,7 @@ if [ -n "$WHISPER_VENDOR_OUTSIDE_BACKEND" ]; then fi echo "✅ [LayerGuard PASS] The whisper.h vendor header stays inside its concrete Backend." -# Rule 5: The neutral TraceableItem contract has one canonical include path. +# 规则 5:中性的 TraceableItem 契约只有一个规范 include 路径。 LEGACY_TRACEABLE_HEADER="$REPO_ROOT/include/core/traceable_item.h" LEGACY_TRACEABLE_INCLUDES=$(grep -rnE \ '#include\s*["<]core/traceable_item\.h[">]' \ @@ -302,8 +302,8 @@ if [ -e "$LEGACY_TRACEABLE_HEADER" ] || [ -n "$LEGACY_TRACEABLE_INCLUDES" ]; the fi echo "✅ [LayerGuard PASS] TraceableItem uses the neutral contracts include path." -# Rule 6: Node support consumes the extracted validated-node plan, not the full -# Orchestration validator implementation contract. +# 规则 6:Node 支持代码只消费抽取出的已校验 Node 计划, +# 而不是完整的编排层 Validator 实现契约。 NODE_SUPPORT_HEADER="$REPO_ROOT/include/nodes/node_base.h" if [ ! -f "$NODE_SUPPORT_HEADER" ] || \ ! grep -q 'core/validated_node_plan.h' "$NODE_SUPPORT_HEADER" || \ @@ -313,8 +313,7 @@ if [ ! -f "$NODE_SUPPORT_HEADER" ] || \ fi echo "✅ [LayerGuard PASS] Node support is decoupled from PipelineValidator." -# Rule 7: Source ownership in CMake must preserve the four compile-time layers -# and the explicit composition root. +# 规则 7:CMake 中的源码归属必须保持四个编译期分层和显式的组合根。 for OWNERSHIP in \ "src/engine/CMakeLists.txt:edgeflow_model_execution_objects" \ "src/common_nodes/CMakeLists.txt:edgeflow_capability_nodes_objects" \ @@ -347,7 +346,7 @@ if ! grep -q 'target_sources(edgeflow_composition_objects' \ fi echo "✅ [LayerGuard PASS] CMake source ownership preserves all four layers and the composition root." -# Rule 8: Pure C11 Syntax & ABI Compliance Check via standard C compiler +# 规则 8:用标准 C 编译器检查纯 C11 语法与 ABI 合规性 GENERATED_VERSION_INCLUDE="$(mktemp -d "${TMPDIR:-/tmp}/edgeflow-version-header.XXXXXX")" cleanup_generated_version() { rm -rf "${GENERATED_VERSION_INCLUDE}" @@ -388,7 +387,7 @@ if [[ -n "${C11_COMPILER}" ]]; then edgeflow/log.h edgeflow/operator/types.h \ platform_mock/error_codes.h \ platform_mock/operator_data_types.h; do - # A macro-only header is valid; provide a translation unit for -pedantic. + # 纯宏头文件也合法;为 -pedantic 提供一个翻译单元。 printf '#include "%s"\nint main(void) { return 0; }\n' "${C11_HEADER}" | \ "${C11_COMPILER}" -std=c11 -pedantic-errors -fsyntax-only -x c \ -I"${GENERATED_VERSION_INCLUDE}" -I"$REPO_ROOT/include" - @@ -398,7 +397,8 @@ else echo "⚠️ [LayerGuard WARN] Neither gcc nor clang found for C11 syntax-only check." fi -# Rule 9: Demo Layer (demo/) MUST NEVER directly include internal SDK headers (adapter/, core/, biz/, business/, engine/, src/) +# 规则 9:Demo 层 (demo/) 绝不能直接 include SDK 内部头文件 +# (adapter/、core/、biz/、business/、engine/、src/) VIOLATIONS_DEMO_INTERNAL=$(grep -rnE '#include\s*["<](adapter/|core/|biz/|business/|engine/|src/)' "$REPO_ROOT/demo" || true) if [ -n "$VIOLATIONS_DEMO_INTERNAL" ]; then @@ -409,7 +409,7 @@ if [ -n "$VIOLATIONS_DEMO_INTERNAL" ]; then fi echo "✅ [LayerGuard PASS] Zero Demo -> Internal SDK header violations." -# Rule 10: LLM vendor runtime and model semantic boundary. +# 规则 10:LLM 厂商运行时与模型语义边界。 LLAMA_VENDOR_OUTSIDE_BACKEND=$(grep -rnE '#include\s*["<]llama\.h[">]' \ "$REPO_ROOT/include" "$REPO_ROOT/src" \ --exclude-dir=backends 2>/dev/null || true) diff --git a/scripts/configure_build.sh b/scripts/configure_build.sh index c6b2d855..ae1907cc 100755 --- a/scripts/configure_build.sh +++ b/scripts/configure_build.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -# Apply a native CMake preset while preserving an existing build's generator. +# 应用原生 CMake preset,同时保留已有构建目录的生成器。 if [[ $# -lt 3 ]]; then echo "Usage: $0 [cmake options...]" >&2 exit 2 diff --git a/scripts/fetch_real_test_models.sh b/scripts/fetch_real_test_models.sh index ee979a00..85d488bd 100755 --- a/scripts/fetch_real_test_models.sh +++ b/scripts/fetch_real_test_models.sh @@ -76,7 +76,7 @@ download_verified() { echo "✓ ${filename} (${expected_sha})" } -# File names, hashes and upstream URLs share the selection manifest. +# 文件名、哈希和上游 URL 共用同一份选型清单。 MANIFEST_ROWS="$(mktemp)" trap 'rm -f "$MANIFEST_ROWS"' EXIT python3 - "$PROJECT_ROOT/models/asset_manifest.json" "$MODE" > "$MANIFEST_ROWS" <<'MANIFEST' diff --git a/scripts/generate_test_onnx_model.py b/scripts/generate_test_onnx_model.py index ed6023c3..3a587646 100644 --- a/scripts/generate_test_onnx_model.py +++ b/scripts/generate_test_onnx_model.py @@ -1,15 +1,15 @@ #!/usr/bin/env python3 -"""Generate a deterministic, dependency-free ONNX Runtime test fixture. +"""生成确定性、无外部依赖的 ONNX Runtime 测试夹具。 -The graph is intentionally small and is not a pretrained BGE model. It proves -the real ONNX Runtime Load/Run boundary used by ``BgeEmbeddingModel``: +该计算图有意保持很小,并非预训练的 BGE 模型。它用于验证 +``BgeEmbeddingModel`` 使用的真实 ONNX Runtime Load/Run 边界: input_ids[int64, batch, sequence] -> Cast -> Unsqueeze -> Mul(scale) -> Add(bias) -> last_hidden_state[float32, batch, sequence, 128] -Only Python's standard library is used. The minimal ONNX protobuf is encoded -directly so a clean checkout does not require the ``onnx`` or ``numpy`` wheels. +只使用 Python 标准库。直接编码最小的 ONNX protobuf, +因此干净的检出无需安装 ``onnx`` 或 ``numpy`` wheel。 """ from __future__ import annotations @@ -161,7 +161,7 @@ def generate_onnx_model(model_path: Path, hidden_dim: int = 128) -> None: model += _int_field(1, 8) # ModelProto.ir_version model += _string_field(2, "edgeflow_test_generator") model += _message_field(7, bytes(graph)) - model += _message_field(8, _int_field(2, 13)) # default opset 13 + model += _message_field(8, _int_field(2, 13)) # 默认 opset 13 model_path.parent.mkdir(parents=True, exist_ok=True) model_path.write_bytes(model) @@ -218,7 +218,7 @@ def generate_rerank_onnx_model(model_path: Path) -> None: model += _int_field(1, 8) # ModelProto.ir_version model += _string_field(2, "edgeflow_test_generator") model += _message_field(7, bytes(graph)) - model += _message_field(8, _int_field(2, 13)) # default opset 13 + model += _message_field(8, _int_field(2, 13)) # 默认 opset 13 model_path.parent.mkdir(parents=True, exist_ok=True) model_path.write_bytes(model) diff --git a/scripts/render_architecture_diagrams.sh b/scripts/render_architecture_diagrams.sh index c826f15b..61799827 100755 --- a/scripts/render_architecture_diagrams.sh +++ b/scripts/render_architecture_diagrams.sh @@ -103,9 +103,8 @@ cleanup() { } trap cleanup EXIT INT TERM -# Render SVGs directly to isolated temporary directory in a single pass with -# parallel threads and fast TieredCompilation startup. Syntax errors are caught -# immediately via -failfast2 without polluting workspace. +# 单次运行、多线程并以快速 TieredCompilation 启动,直接把 SVG 渲染到隔离的 +# 临时目录。语法错误通过 -failfast2 立即捕获,不会污染工作区。 "${JAVA_BIN}" -Djava.awt.headless=true -XX:+TieredCompilation \ -XX:TieredStopAtLevel=1 \ -jar "${PLANTUML_JAR}" -nbthread auto \ @@ -186,10 +185,9 @@ if [[ "${MODE}" == "--generate" ]]; then install -m 0644 "${GENERATED_FLOW}" "${FLOW_ASSET}" echo "✅ Generated source-provenance-locked architecture SVG assets." else - # PlantUML layout coordinates can differ across CPU architectures and font - # stacks even with the same Jar. The committed asset is therefore locked to - # the exact source SHA and generator version, while a fresh render above - # proves syntax/renderability and both outputs are checked semantically. + # 即使使用同一个 Jar,PlantUML 的布局坐标也可能因 CPU 架构和字体栈而不同。 + # 因此提交的资源锁定到确切的源文件 SHA 和生成器版本;上面的全新渲染用于 + # 验证语法和可渲染性,两份输出都会做语义检查。 validate_committed_asset "${CLASS_ASSET}" "${CLASS_PROVENANCE}" \ SharedAlgorithmRuntime Pipeline AlgContext NodeBase FixedBatchExecutor validate_committed_asset "${FLOW_ASSET}" "${FLOW_PROVENANCE}" \ diff --git a/scripts/run_all_tests.sh b/scripts/run_all_tests.sh index dca7154c..fda0e64c 100755 --- a/scripts/run_all_tests.sh +++ b/scripts/run_all_tests.sh @@ -2,7 +2,7 @@ set -euo pipefail # ============================================================================== -# LLM-EdgeFlow canonical quality gate. +# LLM-EdgeFlow 规范质量门禁。 # ============================================================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -45,8 +45,8 @@ echo -e "${BOLD}[ Step 2/6: Configure and build ]${NC}" cmake --build "$BUILD_DIR" -j"$JOBS" echo -e "${GREEN}✓ Build completed.${NC}\n" -# Steps 3-6 share one global scheduler. Labels retain stage ownership while -# allowing slow integration and tooling tests to overlap safely. +# 步骤 3-6 共用一个全局调度器。标签保留各阶段的归属, +# 同时允许较慢的集成测试和工具测试安全地并行。 echo -e "${BOLD}[ Steps 3-6/6: Unified Tier 1-4 CTest scheduler ]${NC}" (cd "$BUILD_DIR" && ctest -j"$JOBS" --output-on-failure --no-tests=error) diff --git a/scripts/run_sanitizers.sh b/scripts/run_sanitizers.sh index 3cf08605..3d9839a0 100755 --- a/scripts/run_sanitizers.sh +++ b/scripts/run_sanitizers.sh @@ -57,9 +57,8 @@ COMMON_CMAKE_ARGS=( -DLLM_EDGEFLOW_SANITIZERS="${SANITIZERS}" -DLLM_EDGEFLOW_LINKER="${LLM_EDGEFLOW_LINKER:-auto}" ) -# Reuse already-fetched source trees when available. This keeps sanitizer -# builds deterministic in restricted/offline development environments while -# preserving FetchContent's normal download behavior on a clean checkout. +# 优先复用已拉取的源码树。这样在受限/离线开发环境中 sanitizer 构建保持确定, +# 在干净检出时仍保留 FetchContent 的正常下载行为。 if [[ -d "${PROJECT_ROOT}/build/_deps/nlohmann_json-src" ]]; then COMMON_CMAKE_ARGS+=( -DFETCHCONTENT_SOURCE_DIR_NLOHMANN_JSON="${PROJECT_ROOT}/build/_deps/nlohmann_json-src" @@ -100,8 +99,8 @@ NCPU="${LLM_EDGEFLOW_JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null if [[ "${MODE}" == "fast" ]]; then cmake --build "${BUILD_DIR}" --target edgeflow_dev_tests -j"${NCPU}" elif [[ "${MODE}" == "ci-runtime" ]]; then - # Build only executables run by sanitizer-runtime tests. Tooling binaries such as - # alg_pipeline_tool and alg_show are tier4-only; a missing target here fails CTest. + # 只构建 sanitizer-runtime 测试会运行的可执行文件。alg_pipeline_tool、 + # alg_show 等工具二进制只属于 tier4;这里缺少目标会导致 CTest 失败。 cmake --build "${BUILD_DIR}" -j"${NCPU}" --target \ alg_demo \ edgeflow_test_core_runner \ diff --git a/scripts/ubsan_suppressions.txt b/scripts/ubsan_suppressions.txt index 90377d7a..fb19b0d5 100644 --- a/scripts/ubsan_suppressions.txt +++ b/scripts/ubsan_suppressions.txt @@ -1,4 +1,4 @@ -# LLM-EdgeFlow UBSan Suppressions File -# Suppress known third-party null pointer offset calculations in ggml.c +# LLM-EdgeFlow UBSan 抑制文件 +# 抑制 ggml.c 中已知的第三方空指针偏移计算 pointer-overflow:ggml.c null:ggml.c diff --git a/src/adapter/CMakeLists.txt b/src/adapter/CMakeLists.txt index 3dc89257..f3975d89 100644 --- a/src/adapter/CMakeLists.txt +++ b/src/adapter/CMakeLists.txt @@ -1,5 +1,4 @@ -# Framework mechanisms stay explicit; business converters and bindings are -# collected recursively and compiled on the next build. +# 框架机制显式列出;业务 Converter 与 binding 递归收集,下次构建时编译。 file(GLOB_RECURSE EDGEFLOW_ADAPTER_BIZ_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/input/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/output/*.cpp" diff --git a/src/adapter/deployment_model_resolver.h b/src/adapter/deployment_model_resolver.h index 7bed8da3..987e274c 100644 --- a/src/adapter/deployment_model_resolver.h +++ b/src/adapter/deployment_model_resolver.h @@ -8,12 +8,11 @@ namespace llm_edgeflow { /** - * @brief Resolve deployment model references before entering Orchestration. + * @brief 进入流程编排层前解析部署中的模型引用。 * - * A non-empty model_root_dir denotes the directory that directly contains - * model artifacts and sidecars. Relative model_path values are resolved under - * that directory and cannot escape it. With an empty root, deployment model - * paths must already be absolute. + * 非空的 model_root_dir 表示直接存放模型文件及其附属文件的目录。相对的 + * model_path 在该目录下解析,且不能越出该目录。根目录为空时,部署中的 + * 模型路径必须已是绝对路径。 */ bool ResolveDeploymentModelPaths( const nlohmann::json& pipeline_json, const std::string& model_root_dir, diff --git a/src/adapter/deployment_preparation.cpp b/src/adapter/deployment_preparation.cpp index 7b3689d8..704133dc 100644 --- a/src/adapter/deployment_preparation.cpp +++ b/src/adapter/deployment_preparation.cpp @@ -265,9 +265,8 @@ void ProjectDeploymentDiagnostics(ValidationReport* report) { [](const ValidationFix& fix) { if (!fix.patch.is_array()) return false; for (const auto& op : fix.patch) { - // Business identity is derived, not editable - // in the external document. Model paths - // remain editable. + // 业务标识由推导得出,外部文档不可编辑; + // 模型路径仍可编辑。 if (op.is_object() && op.contains("path") && op["path"] == "/biz_name") { return true; diff --git a/src/adapter/deployment_preparation.h b/src/adapter/deployment_preparation.h index fa0ee16b..4e3ff4fb 100644 --- a/src/adapter/deployment_preparation.h +++ b/src/adapter/deployment_preparation.h @@ -22,9 +22,8 @@ struct DeploymentPrepareOptions { std::string model_root_dir; }; -// Converter selection, port mappings and output pool budgets resolved from a -// deployment document. Preparation produces it; the validated Operator plan -// keeps it unchanged. +// 从部署文档解析出的 Converter 选择、端口映射和输出池预算。 +// 由准备阶段生成,经校验的 Operator 计划保持不变。 struct IoBindingSelection { IoBindingDefinition binding; const InputConverterDefinition* input_converter = nullptr; diff --git a/src/adapter/deployment_structure.h b/src/adapter/deployment_structure.h index cf88297e..c33f5707 100644 --- a/src/adapter/deployment_structure.h +++ b/src/adapter/deployment_structure.h @@ -4,7 +4,7 @@ namespace llm_edgeflow { -// Owned by Integration. Allocator-specific params remain opaque JSON. +// 归接入适配层所有。分配器专属参数保持为不透明 JSON。 const nlohmann::json& PipelineDocumentStructure(); const nlohmann::json& DeploymentStructure(); const nlohmann::json& OutputAllocationStructure(); diff --git a/src/adapter/input/rerank_input.cpp b/src/adapter/input/rerank_input.cpp index eab9ae21..2e786598 100644 --- a/src/adapter/input/rerank_input.cpp +++ b/src/adapter/input/rerank_input.cpp @@ -17,9 +17,8 @@ namespace { constexpr const char* kInputSlot = "rerank_in"; -// Converter-level business limit. The Operator admits passages up to -// max_doc_text_bytes; this stricter bound is the effective limit today and -// awaits the solution owner's confirmation. +// Converter 级业务上限。Operator 允许的段落上限为 max_doc_text_bytes; +// 当前以这个更严格的上限为准,尚待方案负责人确认。 constexpr size_t kMaxCandidatePassageBytes = biz_input::kMaxTextBytes; int DecodeOperatorRerankInput(const ExternalInputBatchView& source, diff --git a/src/adapter/io_binding_resolver.h b/src/adapter/io_binding_resolver.h index b23d83dc..ba28f5b4 100644 --- a/src/adapter/io_binding_resolver.h +++ b/src/adapter/io_binding_resolver.h @@ -18,7 +18,7 @@ namespace llm_edgeflow { * @brief 已验证的不可变接入计划 (同时包含 I/O 转换器绑定与内部 Pipeline 计划) */ struct ValidatedIoPlan : IoBindingSelection { - // External document snapshot with resolved model paths, without derived biz. + // 外部文档快照,模型路径已解析,不含推导出的 biz。 nlohmann::json resolved_pipeline_json; std::unique_ptr pipeline_plan; diff --git a/src/adapter/io_catalog.cpp b/src/adapter/io_catalog.cpp index 9ca676f8..e8470a66 100644 --- a/src/adapter/io_catalog.cpp +++ b/src/adapter/io_catalog.cpp @@ -60,7 +60,7 @@ nlohmann::json OutputConverterToJson(const OutputConverterDefinition& conv) { } nlohmann::json IoBindingToJson(const IoBindingDefinition& b) { - // Report the effective mappings, including ports mapped to the same name. + // 报告有效映射,包括映射到同名键的端口。 const auto& converters = IoConverterRegistry::Instance(); const auto* input = converters.FindInputConverter(b.input_converter_id); const auto* output = converters.FindOutputConverter(b.output_converter_id); diff --git a/src/adapter/io_converter_registry.cpp b/src/adapter/io_converter_registry.cpp index e8a059f6..391746af 100644 --- a/src/adapter/io_converter_registry.cpp +++ b/src/adapter/io_converter_registry.cpp @@ -4,8 +4,8 @@ namespace llm_edgeflow { namespace { -// Default protocol carrier label: the external slot types in declaration -// order, e.g. "CompanyFrame,CompanyString". +// 默认协议载体标签:按声明顺序列出外部槽位类型, +// 如 "CompanyFrame,CompanyString"。 std::string JoinedSlotTypes(const std::vector& slots) { std::string joined; for (const auto& slot : slots) { diff --git a/src/adapter/operator/json_output_config_reader.h b/src/adapter/operator/json_output_config_reader.h index dc8bac2d..2cbbcfbf 100644 --- a/src/adapter/operator/json_output_config_reader.h +++ b/src/adapter/operator/json_output_config_reader.h @@ -5,8 +5,8 @@ namespace llm_edgeflow { -// Borrows one output object only for the resolver's Create-time work. Returned -// strings own their storage; the document never enters the output pools. +// 仅在解析器 Create 期间借用单个输出对象。返回的字符串自有存储, +// 该文档不会进入输出池。 class JsonOutputConfigReader final : public OutputConfigReader { public: explicit JsonOutputConfigReader(const nlohmann::json& config) diff --git a/src/adapter/operator/operator_config_resolver.h b/src/adapter/operator/operator_config_resolver.h index 46b63edc..ce924b56 100644 --- a/src/adapter/operator/operator_config_resolver.h +++ b/src/adapter/operator/operator_config_resolver.h @@ -20,8 +20,8 @@ struct ResolvedOperatorConfig { std::filesystem::path pipeline_path; std::filesystem::path model_root_path; std::unique_ptr io_plan; - uint32_t effective_frame_depth = 0; // normalized output pool depth - uint32_t effective_process_batch_limit = 0; // min(pool depth, binding limit) + uint32_t effective_frame_depth = 0; // 归一化后的输出池深度 + uint32_t effective_process_batch_limit = 0; // min(池深度, binding 上限) }; /** diff --git a/src/adapter/operator/operator_error_mapping.cpp b/src/adapter/operator/operator_error_mapping.cpp index 0450575c..f675a1e2 100644 --- a/src/adapter/operator/operator_error_mapping.cpp +++ b/src/adapter/operator/operator_error_mapping.cpp @@ -44,8 +44,7 @@ OperatorFailureStage ControlFailureToOperatorStage( int PublicFailureCode(OperatorFailureStage stage, int internal_code) noexcept { switch (stage) { case OperatorFailureStage::kCreatePreparation: - // Adapter-owned categories keep their meaning; every other preparation - // failure is an invalid creation parameter or configuration. + // Adapter 自有类别保持原义;其余准备阶段失败均视为非法的创建参数或配置。 switch (internal_code) { case COMPANY_ALG_ERR_UNSUPPORTED_BIZ: case COMPANY_ALG_ERR_REGISTRY_CONFLICT: @@ -65,7 +64,7 @@ int PublicFailureCode(OperatorFailureStage stage, int internal_code) noexcept { case OperatorFailureStage::kControlRuntime: break; } - // Runtime guards and exception barriers already return public codes. + // 运行时守卫和异常屏障已返回公开错误码。 return internal_code; } diff --git a/src/adapter/operator/operator_error_mapping.h b/src/adapter/operator/operator_error_mapping.h index 2d3e82d0..61b3012c 100644 --- a/src/adapter/operator/operator_error_mapping.h +++ b/src/adapter/operator/operator_error_mapping.h @@ -7,16 +7,15 @@ namespace llm_edgeflow { -// Failures that originate below the Operator facade. Their internal Pipeline, -// Node and Model codes are reported through GetOperatorLastError() only; the -// host receives the public code of the failure stage. +// 源于 Operator facade 之下的失败。其内部 Pipeline、Node 和 Model 错误码 +// 只通过 GetOperatorLastError() 报告;宿主收到的是失败阶段的公开错误码。 enum class OperatorFailureStage { - kCreatePreparation, // configuration, deployment, model and backend loading - kProcessExecution, // a Node or Model failed in Pipeline::Execute - kControlRequest, // Control envelope, target or payload was rejected - kControlUnsupported, // no Node declares or handles the command - kControlExecution, // a Node rejected or failed to apply the update - kControlRuntime, // no ready runtime, or an exception barrier fired + kCreatePreparation, // 配置、部署、模型和 Backend 加载 + kProcessExecution, // Pipeline::Execute 中 Node 或 Model 失败 + kControlRequest, // Control 信封、目标或 payload 被拒绝 + kControlUnsupported, // 没有 Node 声明或处理该命令 + kControlExecution, // Node 拒绝或未能应用更新 + kControlRuntime, // 无就绪运行时,或触发了异常屏障 }; OperatorFailureStage ControlFailureToOperatorStage( diff --git a/src/adapter/operator/operator_output_pool.h b/src/adapter/operator/operator_output_pool.h index 969f2b2a..11b6c4a5 100644 --- a/src/adapter/operator/operator_output_pool.h +++ b/src/adapter/operator/operator_output_pool.h @@ -86,7 +86,7 @@ class OutputPoolState : public std::enable_shared_from_this { std::vector all_blocks_; std::vector free_ring_; - // The FIFO insertion position is (free_head_ + free_count_) % ring size. + // FIFO 插入位置为 (free_head_ + free_count_) % 环大小。 size_t free_head_ = 0; size_t free_count_ = 0; std::unordered_map block_states_; diff --git a/src/adapter/output/rule_match_response.h b/src/adapter/output/rule_match_response.h index 6bf3084a..2cc61c4f 100644 --- a/src/adapter/output/rule_match_response.h +++ b/src/adapter/output/rule_match_response.h @@ -6,8 +6,8 @@ namespace llm_edgeflow { -// Serializes a rule match result as the JSON object used by the keyword -// response (match_result_json) and the audio response (intent_slot_json). +// 将规则匹配结果序列化为 JSON 对象,供关键词响应 (match_result_json) +// 和音频响应 (intent_slot_json) 使用。 std::string SerializeRuleMatchResponse(const RuleMatchItem& result); } // namespace llm_edgeflow diff --git a/src/adapter/shared_algorithm_runtime.h b/src/adapter/shared_algorithm_runtime.h index 9e87478c..d64756d2 100644 --- a/src/adapter/shared_algorithm_runtime.h +++ b/src/adapter/shared_algorithm_runtime.h @@ -44,7 +44,6 @@ class SharedAlgorithmRuntime { std::string* out_error = nullptr, ControlFailureStage* failure_stage = nullptr) noexcept; - // Getters Pipeline* GetPipeline() { return pipeline_.get(); } const Pipeline* GetPipeline() const { return pipeline_.get(); } const ValidatedIoPlan* GetIoPlan() const { return io_plan_.get(); } diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 0f329d74..9238e438 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -2,7 +2,7 @@ add_executable(alg_show alg_show.cpp) target_link_libraries(alg_show PRIVATE edgeflow_internal_headers nlohmann_json::nlohmann_json) -# Developer-tool guidance on Validator diagnostics; not part of the SDK. +# 基于 Validator 诊断的开发工具指引;不属于 SDK。 add_library(edgeflow_pipeline_tooling STATIC pipeline_remediation.cpp) target_link_libraries(edgeflow_pipeline_tooling PUBLIC edgeflow_internal_headers llm_edgeflow::internal_runtime) diff --git a/src/cli/alg_pipeline_tool.cpp b/src/cli/alg_pipeline_tool.cpp index 2816bbc0..b70951b3 100644 --- a/src/cli/alg_pipeline_tool.cpp +++ b/src/cli/alg_pipeline_tool.cpp @@ -55,9 +55,8 @@ nlohmann::json ToolError(const std::string& code, const std::string& message, {"severity", "error"}}})}}; } -// Keep build/test registration guidance in the CLI; SDK/Core diagnostics stay -// tool-neutral. Wrapped deployment errors supply their original code -// separately. +// 构建/测试注册指引保留在 CLI 中;SDK/Core 诊断保持与工具无关。 +// 被包装的部署错误另行提供其原始错误码。 void PrintRegistrationHint(const nlohmann::json& response, const std::string& source_code = {}) { #ifdef LLM_EDGEFLOW_TOOL_HAS_TEST_REGISTRATIONS diff --git a/src/cli/pipeline_authoring.cpp b/src/cli/pipeline_authoring.cpp index b1c30037..551eba0a 100644 --- a/src/cli/pipeline_authoring.cpp +++ b/src/cli/pipeline_authoring.cpp @@ -59,7 +59,7 @@ std::optional GetEffectiveInputKey(const nlohmann::json& node, return std::nullopt; } -// Validate the transient editing protocol, not the persisted Pipeline schema. +// 校验临时编辑协议,而非持久化的 Pipeline schema。 void CheckFields(const nlohmann::json& value, std::initializer_list allowed) { if (!value.is_object()) throw std::invalid_argument("字段必须是对象"); @@ -700,7 +700,7 @@ bool PipelineAuthoring::ApplyOperation(nlohmann::json* pipeline, return false; } - // Check conflict: does any other node produce tgt_port? + // 检查冲突:是否有其他 Node 产出 tgt_port? for (const auto& other : (*pipeline)["pipeline"]) { if (!other.is_object()) continue; std::string o_id = other.value("id", ""); @@ -734,7 +734,7 @@ bool PipelineAuthoring::ApplyOperation(nlohmann::json* pipeline, current_key = (*src_node)["outputs"][src_port].get(); } - // Check if current_key is already mapped to another egress port + // 检查 current_key 是否已映射到其他出口端口 for (const auto& eg : biz->egress) { if (eg.blackboard_key != tgt_port && eg.blackboard_key == current_key) { if (error) { @@ -777,7 +777,7 @@ bool PipelineAuthoring::ApplyOperation(nlohmann::json* pipeline, return true; } - // Node to Node + // Node 到 Node if (src_id == tgt_id) { if (error) *error = "连线会形成环"; return false; @@ -837,7 +837,7 @@ bool PipelineAuthoring::ApplyOperation(nlohmann::json* pipeline, return false; } - // Ordinary fan-out never renames the producer's existing effective key. + // 普通扇出从不重命名生产者现有的有效键。 std::string key = GetEffectiveOutputKey(*src_node, src_port); if (!(*tgt_node).contains("inputs")) { (*tgt_node)["inputs"] = nlohmann::json::object(); @@ -936,7 +936,7 @@ bool PipelineAuthoring::ApplyOperation(nlohmann::json* pipeline, return true; } - // Normal node to Normal node + // 普通 Node 到普通 Node auto* src_node = FindNodeById(pipeline, src_id); auto* tgt_node = FindNodeById(pipeline, tgt_id); if (!src_node) { @@ -997,7 +997,7 @@ bool PipelineAuthoring::ApplyOperation(nlohmann::json* pipeline, } if (IsAncestor(dep_id, node_id, dep_graph)) { - // Already an ancestor; do not add a redundant edge. + // 已是祖先,不再添加冗余边。 if (changes) { changes->push_back({"add_dependency", node_id, @@ -1080,7 +1080,6 @@ AuthoringResult PipelineAuthoring::ApplyRequest(const nlohmann::json& request) { return result; } - // Check the 4 MiB payload limit. if (request.dump().size() > 4 * 1024 * 1024) { result.ok = false; result.diagnostics.push_back( diff --git a/src/cli/pipeline_authoring.h b/src/cli/pipeline_authoring.h index f9116a5c..e5b2e0ab 100644 --- a/src/cli/pipeline_authoring.h +++ b/src/cli/pipeline_authoring.h @@ -33,35 +33,32 @@ struct AuthoringResult { class PipelineAuthoring { public: - // Applies a single authoring request containing {schema_version, pipeline, - // operation/operations, require_valid}. + // 应用单个编辑请求,包含 {schema_version, pipeline, operation/operations, + // require_valid}。 static AuthoringResult ApplyRequest(const nlohmann::json& request); - // Applies one authoring operation to an in-memory pipeline document. + // 将一个编辑操作应用到内存中的 Pipeline 文档。 static bool ApplyOperation(nlohmann::json* pipeline, const nlohmann::json& operation, std::vector* changes, std::string* error); - // Collects all occupied blackboard keys in document (explicit mappings, - // default outputs, ingress, egress). + // 收集文档中所有已占用的 Blackboard 键 + // (显式映射、默认输出、ingress、egress)。 static std::unordered_set GetOccupiedKeys( const nlohmann::json& pipeline); - // Allocates a unique key based on base name without colliding with occupied - // keys. + // 基于 base 名称分配一个不与已占用键冲突的唯一键。 static std::string AllocateKey( const std::string& base, const std::unordered_set& occupied); - // Checks whether potential_ancestor is a direct or transitive dependency of - // node_id. + // 检查 potential_ancestor 是否为 node_id 的直接或传递依赖。 static bool IsAncestor( const std::string& potential_ancestor, const std::string& node_id, const std::unordered_map>& dep_graph); - // Builds a depends_on adjacency graph mapping node_id to its listed - // dependencies. + // 构建 depends_on 邻接图:node_id -> 其列出的依赖。 static std::unordered_map> BuildDependencyGraph(const nlohmann::json& pipeline); }; diff --git a/src/cli/pipeline_json_schema.h b/src/cli/pipeline_json_schema.h index f9509ba3..267bb0e6 100644 --- a/src/cli/pipeline_json_schema.h +++ b/src/cli/pipeline_json_schema.h @@ -4,8 +4,8 @@ namespace llm_edgeflow { -// Offline editor assistance for the capabilities of the selected tool build. -// Semantic validation remains in ValidatePipelineDocument / PipelineValidator. +// 为所选工具构建的能力提供离线编辑辅助。 +// 语义校验仍由 ValidatePipelineDocument / PipelineValidator 负责。 nlohmann::json BuildPipelineJsonSchema(const nlohmann::json& catalog); } // namespace llm_edgeflow diff --git a/src/cli/pipeline_remediation.cpp b/src/cli/pipeline_remediation.cpp index 8e594374..308ee3af 100644 --- a/src/cli/pipeline_remediation.cpp +++ b/src/cli/pipeline_remediation.cpp @@ -553,8 +553,8 @@ void PopulateBasicRemediation(ValidationDiagnostic* diag, } else if (diag->code == DiagnosticCode::kPortCardinalityMismatch || diag->code == DiagnosticCode::kPortProvenanceMismatch || diag->code == DiagnosticCode::kPortLifetimeMismatch) { - // Validator owns direction, effective contracts and transitive shapes. - // A path can identify either a node input, its output or an IO boundary. + // 方向、有效契约和传递形状由 Validator 负责。 + // 路径可指向 Node 输入、其输出或 IO 边界。 if (diag->facts.empty()) return; ValidationRemediation rem; rem.cause = RemediationCause::kPortFlowMismatch; diff --git a/src/cli/pipeline_remediation.h b/src/cli/pipeline_remediation.h index e8f608b4..01702875 100644 --- a/src/cli/pipeline_remediation.h +++ b/src/cli/pipeline_remediation.h @@ -6,19 +6,18 @@ namespace llm_edgeflow { -// Developer-tool guidance layered on Validator diagnostics. Rules still come -// only from PipelineValidator; this code classifies its diagnostics, adds -// facts and a summary, and proposes JSON Patch fixes that the Validator -// confirms on the patched document. It is not compiled into the SDK. +// 建立在 Validator 诊断之上的开发工具指引。规则仍只来自 PipelineValidator; +// 此处对诊断分类,补充事实和摘要,并给出 JSON Patch 修复建议,由 Validator +// 在打补丁后的文档上确认。不编译进 SDK。 -// Adds remediation cause, facts and summary to each diagnostic of report. +// 为 report 中每条诊断补充修复原因、事实和摘要。 void AttachRemediation(const nlohmann::json& root, ValidationReport* report); ValidationReport ValidateWithRemediation( const nlohmann::json& root, const PipelineIoBoundary* io_boundary = nullptr); -// Also keeps up to three verified fixes per diagnostic. +// 每条诊断另外最多保留三个已验证的修复。 ValidationReport ExplainPipeline( const nlohmann::json& root, const PipelineIoBoundary* io_boundary = nullptr); diff --git a/src/common_nodes/CMakeLists.txt b/src/common_nodes/CMakeLists.txt index f1114d0b..cb468390 100644 --- a/src/common_nodes/CMakeLists.txt +++ b/src/common_nodes/CMakeLists.txt @@ -1,4 +1,4 @@ -# Includes support/ and any operation subdirectories. +# 包含 support/ 及各操作子目录。 file(GLOB_RECURSE EDGEFLOW_COMMON_NODE_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") target_sources(edgeflow_capability_nodes_objects PRIVATE diff --git a/src/common_nodes/structured_json_parse_node.cpp b/src/common_nodes/structured_json_parse_node.cpp index a62fcd4a..43c32958 100644 --- a/src/common_nodes/structured_json_parse_node.cpp +++ b/src/common_nodes/structured_json_parse_node.cpp @@ -203,15 +203,14 @@ bool ParseOrExtractJson(const StructuredJsonOptions& options, *out_diag = "Unclosed JSON markdown block"; return false; } - // An invalid fenced value must not fall through to extracting its - // children. + // 围栏内的值非法时,不得转而提取其子元素。 return parse_candidate( input.substr(content_start, code_block_end - content_start), JsonParseStatus::kExtractedFromMarkdown); } - // Preserve the first outer container, including arrays of objects. Never - // extract a valid child from a malformed or truncated parent container. + // 保留第一个外层容器 (含对象数组)。绝不从格式错误或被截断的父容器中 + // 提取合法的子元素。 const size_t start = input.find_first_of("[{"); if (start == std::string::npos) return false; std::vector delimiters; diff --git a/src/common_nodes/support/compiled_text_regex.h b/src/common_nodes/support/compiled_text_regex.h index 9be0507f..6a09e398 100644 --- a/src/common_nodes/support/compiled_text_regex.h +++ b/src/common_nodes/support/compiled_text_regex.h @@ -8,7 +8,7 @@ namespace llm_edgeflow { enum class TextRegexSearchStatus { kMatched, kNotMatched, kError }; -// Move-only RAII wrapper that keeps PCRE2 details out of capability Nodes. +// 仅可移动的 RAII 包装,使 PCRE2 细节不进入能力节点。 class CompiledTextRegex final { public: CompiledTextRegex(); diff --git a/src/common_nodes/text_embedding_node.cpp b/src/common_nodes/text_embedding_node.cpp index 18af4eca..6b2a1dfb 100644 --- a/src/common_nodes/text_embedding_node.cpp +++ b/src/common_nodes/text_embedding_node.cpp @@ -69,8 +69,8 @@ NodeResult EmbedText(const EmbeddingInputs& inputs, "TextEmbeddingNode: single-flight inference failed", node_error::text_embedding::kSessionInferenceFailed); } - // The model facade validated the cached result; PreservedOutput checks the - // returned copy again before publishing it for this request. + // 模型 facade 已校验缓存结果;PreservedOutput 在为本请求发布前 + // 会再次检查返回的副本。 return NodeResult::Success(*cached.value()); } diff --git a/src/common_nodes/text_rule_match_node.cpp b/src/common_nodes/text_rule_match_node.cpp index 825abca0..bb3433d3 100644 --- a/src/common_nodes/text_rule_match_node.cpp +++ b/src/common_nodes/text_rule_match_node.cpp @@ -207,7 +207,7 @@ NodeResult MatchRules(const RuleInputs& inputs, for (const auto& item : *text_items) { const std::string& sentence = item.data; RuleMatchItem result; - // The first hit with a category names the result. + // 首个带类别的命中决定结果名称。 auto record_hit = [&](const std::string& category, const std::string& word, float score, const std::string& rule_id) { result.is_hit = 1; diff --git a/src/common_nodes/text_template_node.cpp b/src/common_nodes/text_template_node.cpp index fa6e2a17..6fbc37ab 100644 --- a/src/common_nodes/text_template_node.cpp +++ b/src/common_nodes/text_template_node.cpp @@ -405,8 +405,7 @@ NodeResult RenderTemplate(const TemplateInputs& inputs, if (var == "primary") { if (p_it != primary_by_sample.end()) value = &primary_str; } else if (var == "context" || var == "context_text") { - // A present aggregate batch may contain no results for this - // request. + // 已存在的聚合批次中可能没有本请求的结果。 if (context_items || context_text_items) value = &context_str; } else if (var == "matches") { if (matches_items) value = &matches_str; diff --git a/src/core/name_suggestions.h b/src/core/name_suggestions.h index 8ccd1a8a..7881fdd1 100644 --- a/src/core/name_suggestions.h +++ b/src/core/name_suggestions.h @@ -9,11 +9,11 @@ namespace llm_edgeflow { size_t LevenshteinDistance(std::string_view s1, std::string_view s2); -// Orders names by edit distance; ties use the name for deterministic output. +// 按编辑距离排序名称;距离相同时按名称排序,保证输出确定。 std::vector RankByEditDistance(std::string_view target, std::vector names); -// Up to `limit` names close enough to `target` to be a likely typo. +// 最多 `limit` 个与 `target` 足够接近、可能是拼写错误的名称。 std::vector NearestNames(std::string_view target, std::vector names, size_t limit = 3); diff --git a/src/core/pipeline.cpp b/src/core/pipeline.cpp index 1ba3640e..96e333ee 100644 --- a/src/core/pipeline.cpp +++ b/src/core/pipeline.cpp @@ -443,8 +443,8 @@ int Pipeline::Execute(AlgContext* req_ctx) { struct SubmittedNodesGuard { std::vector>& futures; ~SubmittedNodesGuard() { - // Diagnostic construction can also throw. Never let submitted tasks - // retain the caller's context after Execute has unwound. + // 构造诊断信息也可能抛异常。Execute 栈展开后, + // 已提交的任务绝不能继续持有调用方的上下文。 for (auto& future : futures) { if (future.valid()) future.wait(); } @@ -566,8 +566,8 @@ int Pipeline::Control(int cmd, const std::string& json_param, } bool target_found = false; size_t node_index = 0; - // Materialization uses this same layer/instance order. Keep diagnostics tied - // to instance IDs even when several instances have the same Node type. + // 实例化使用相同的层/实例顺序。即使多个实例属于同一 Node 类型, + // 诊断也要关联到实例 ID。 for (const auto& layer : plan_->report.topological_layers) { for (const auto& id : layer) { auto* node = nodes_[node_index++].get(); @@ -606,8 +606,8 @@ int Pipeline::Control(int cmd, const std::string& json_param, (target_id.empty() ? "" : " for node '" + target_id + "'")); } - // Broadcast remains best-effort: a later semantic failure does not undo an - // earlier update. Report each failure instead of hiding it behind an int. + // 广播仍是尽力而为:后续语义失败不会撤销之前的更新。 + // 逐个报告失败,而不是只用一个 int 掩盖。 bool handled = false; int first_failure = 0; std::string failures; diff --git a/src/core/pipeline_config.cpp b/src/core/pipeline_config.cpp index f6078c34..5d358b08 100644 --- a/src/core/pipeline_config.cpp +++ b/src/core/pipeline_config.cpp @@ -86,7 +86,7 @@ bool ParsePipelineConfig(const nlohmann::json& root, return false; } - // A single worker budget controls sequential and parallel execution. + // 同一个 worker 预算同时控制串行和并行执行。 if (root.contains("max_parallel_workers")) { if (!shape::HasType(root["max_parallel_workers"], shape::Property(structure, "max_parallel_workers"))) { @@ -367,7 +367,7 @@ bool ParsePipelineConfig(const nlohmann::json& root, return false; } - // Input connections and output names use the same explicit mapping shape. + // 输入连接和输出名称使用相同的显式映射结构。 for (const char* direction : {"inputs", "outputs"}) { if (!node_elem.contains(direction)) continue; const auto& bindings = node_elem[direction]; @@ -438,8 +438,8 @@ bool ParsePipelineConfig(const nlohmann::json& root, } seen_node_ids.insert(node_cfg.id); - // Optional additional ordering constraints. Data dependencies are planned - // by PipelineValidator from input/output bindings. + // 可选的附加顺序约束。数据依赖由 PipelineValidator + // 根据输入/输出绑定规划。 if (node_elem.contains("depends_on")) { if (!shape::HasType(node_elem["depends_on"], shape::Property(node_shape, "depends_on"))) { diff --git a/src/core/pipeline_config_structure.h b/src/core/pipeline_config_structure.h index 706cc6c1..c9e58dc1 100644 --- a/src/core/pipeline_config_structure.h +++ b/src/core/pipeline_config_structure.h @@ -4,7 +4,7 @@ namespace llm_edgeflow { -// Owned by Orchestration. Catalog-specific fields are added only by tooling. +// 归流程编排层所有。Catalog 专属字段只由工具添加。 const nlohmann::json& PipelineConfigStructure(); } // namespace llm_edgeflow diff --git a/src/core/pipeline_validator.cpp b/src/core/pipeline_validator.cpp index 8a8d8a25..d2b69d6c 100644 --- a/src/core/pipeline_validator.cpp +++ b/src/core/pipeline_validator.cpp @@ -43,8 +43,8 @@ void AddPortFlowDiagnostic( const std::string& consumer_id, PortDirection location, const std::string& logical_port, const std::string& bound_key, const PortContract& producer, const PortContract& consumer) { - // A node input diagnostic belongs to the consumer; boundary diagnostics - // belong to the producer. Preserve these locations and related-node IDs. + // Node 输入诊断归属消费者,边界诊断归属生产者。 + // 保留这些位置和相关 Node ID。 const bool boundary = location == PortDirection::kOutput; Add(report, code, path, std::move(message), boundary ? producer_id : consumer_id, logical_port, @@ -79,16 +79,14 @@ NodePortDefinition EffectivePortDefinition( return effective; } -// Number of items one request has under a Blackboard key. Node cardinality -// states the node's own relation: inputs say how items are consumed ("1:1" -// item by item, "N:1" grouped by request, "N:M" arbitrarily) and accept any -// shape; outputs say how produced items relate to the item-wise inputs. The -// shape is derived along the DAG and checked only where it matters: a boundary -// requiring one item per request, and item-wise inputs that must pair up. +// 一个请求在某 Blackboard 键下的条目数。Node 的 cardinality 描述 Node 自身的 +// 关系:输入表示条目的消费方式 ("1:1" 逐条,"N:1" 按请求分组,"N:M" 任意), +// 且接受任意形状;输出表示产出条目与逐条输入之间的关系。形状沿 DAG 推导, +// 只在关键处检查:要求每请求一个条目的边界,以及必须两两配对的逐条输入。 struct KeyShape { enum class Kind { kUnknown, kPerRequest, kMulti }; Kind kind = Kind::kUnknown; - std::string origin; // Fan-out that generated the items of a kMulti shape. + std::string origin; // 生成 kMulti 形状条目的扇出来源。 static KeyShape Unknown() { return {}; } static KeyShape PerRequest() { return {Kind::kPerRequest, {}}; } @@ -126,8 +124,8 @@ nlohmann::json ShapeFacts(const KeyShape& shape) { return {{"kind", "unknown"}}; } -// Biz and IO boundary ports count items per request: "1:1" is exactly one -// item, any other declaration is a collection belonging to the request. +// Biz 和 IO 边界端口按请求计数:"1:1" 恰为一个条目, +// 其他声明均为归属该请求的集合。 KeyShape BoundaryShape(const PortContract& port, std::string origin) { return port.cardinality == "1:1" ? KeyShape::PerRequest() : KeyShape::Multi(std::move(origin)); @@ -173,8 +171,8 @@ int LifetimeRank(const std::string& lifetime) { } bool TraversesParent(const std::filesystem::path& path) { - // Retain the portable leading Windows-parent check even on hosts whose - // native filesystem treats backslashes as ordinary filename characters. + // 即使宿主文件系统把反斜杠视为普通文件名字符, + // 仍保留可移植的 Windows 前导父目录检查。 return HasParentPathComponent(path) || path.string().rfind("..\\", 0) == 0; } @@ -505,8 +503,8 @@ ValidatedPipelinePlan ValidateAndPlanInternal( } } - // 8. Orchestration only performs environment-neutral lexical path checks. - // Deployment roots are resolved by Integration before runtime validation. + // 流程编排层只做与环境无关的词法路径检查。 + // 部署根目录由接入适配层在运行时校验前解析。 const auto normalized_path = std::filesystem::path(model.model_path).lexically_normal(); if (!normalized_path.is_absolute() && TraversesParent(normalized_path)) { @@ -664,7 +662,7 @@ ValidatedPipelinePlan ValidateAndPlanInternal( std::unordered_map>> producers; - // Resolve ownership before ordering. Array order never selects a producer. + // 先确定归属再排序。数组顺序从不决定生产者。 for (const auto& node : nodes) { auto def_it = def_by_id.find(node.id); if (def_it == def_by_id.end()) continue; @@ -715,9 +713,8 @@ ValidatedPipelinePlan ValidateAndPlanInternal( } } - // Missing-producer errors cascade only when an unregistered node's - // explicit mapping is the sole possible source. Known producers or ingress - // still permit independent type/uniqueness/conflict checks. + // 仅当未注册 Node 的显式映射是唯一可能来源时,缺失生产者错误才会级联。 + // 已知生产者或入口仍允许独立进行类型/唯一性/冲突检查。 auto has_only_unresolved_source = [&](const std::string& key) { auto it = producers.find(key); return unresolved_output_keys.count(key) && @@ -747,8 +744,7 @@ ValidatedPipelinePlan ValidateAndPlanInternal( const auto& definition = *def_it->second; const auto& node = *node_by_id[id]; const nlohmann::json& normalized_config = normalized_config_by_node.at(id); - // Shape shared by the bound item-wise inputs; a node without one emits a - // single item per request. + // 已绑定逐条输入共享的形状;没有逐条输入的 Node 每个请求只产出一个条目。 KeyShape item_shape = KeyShape::PerRequest(); std::string item_shape_port; std::string item_shape_key; @@ -1085,8 +1081,8 @@ ValidatedPipelinePlan ValidateAndPlanInternal( } if (parsed.max_parallel_workers > 1) { - // Split unsafe nodes and users of shared serialized models into their own - // layers. Write conflicts are still checked against each original layer. + // 将非线程安全 Node 和共享串行模型的使用者拆分到各自的层。 + // 写冲突仍按原始层检查。 auto serialized_models = [&](const std::string& id) { std::unordered_set models; for (const auto& binding : plan.node_plans[id].model_bindings) { diff --git a/src/custom_nodes/CMakeLists.txt b/src/custom_nodes/CMakeLists.txt index f75cc300..dd0cbbfc 100644 --- a/src/custom_nodes/CMakeLists.txt +++ b/src/custom_nodes/CMakeLists.txt @@ -1,5 +1,5 @@ -# Every .cpp under this directory (including subdirectories) is compiled into -# the Capability Nodes layer. CONFIGURE_DEPENDS picks up changes on the next build. +# 本目录 (含子目录) 下的每个 .cpp 都编译进能力节点层。 +# CONFIGURE_DEPENDS 会在下次构建时识别变更。 file(GLOB_RECURSE EDGEFLOW_CUSTOM_NODE_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") target_sources(edgeflow_capability_nodes_objects PRIVATE diff --git a/src/custom_nodes/prompt_guided_llm_node.cpp b/src/custom_nodes/prompt_guided_llm_node.cpp index 2497ce56..40771110 100644 --- a/src/custom_nodes/prompt_guided_llm_node.cpp +++ b/src/custom_nodes/prompt_guided_llm_node.cpp @@ -14,7 +14,7 @@ namespace custom_nodes { namespace { -// Ordinary, owned configuration used by processing after initialization. +// 初始化后供处理阶段使用的普通自有配置。 struct PromptConfig { std::vector prompt_parts; bool uses_context = false; @@ -23,8 +23,8 @@ struct PromptConfig { GenerateOptions generation; }; -// Fields have already been validated and defaulted. Keep only this Node's -// semantic conversion here; request values never enter configuration parsing. +// 字段已校验并填充默认值。此处只保留本 Node 的语义转换; +// 请求值从不进入配置解析。 bool ParsePromptConfig(const nlohmann::json& config, PromptConfig* parameters, std::string* error) { auto reject = [&](const std::string& message) { @@ -73,7 +73,7 @@ std::vector PromptConfigFields() { {}, "在渲染模板前追加的普通文本及换行;模型的 system " "角色请使用 model_config.system_prompt。"}}); - // Preserve the Catalog presentation order without assuming a field index. + // 保持 Catalog 展示顺序,不假设字段索引。 const auto stop_words = std::find_if( fields.begin(), fields.end(), [](const auto& field) { return field.name == "stop_words"; }); @@ -96,7 +96,7 @@ const NodeConfigParser& PromptConfiguration() { return parser; } -// Pure algorithm: renders prompt template with optional prefix and variables. +// 纯算法:使用可选前缀和变量渲染 prompt 模板。 std::string RenderPromptFromParts(const std::string& prefix, const std::vector& parts, const std::string& input, @@ -115,7 +115,7 @@ std::string RenderPromptFromParts(const std::string& prefix, return result; } -// Pure algorithm: removes markdown code fences from LLM responses. +// 纯算法:移除 LLM 响应中的 Markdown 代码围栏。 std::string StripMarkdownCodeFence(std::string_view text) { size_t start = text.find_first_not_of(" \t\r\n"); if (start == std::string_view::npos) return ""; diff --git a/src/engine/backends/kite_llm/kite_llm_backend.cpp b/src/engine/backends/kite_llm/kite_llm_backend.cpp index 27131f49..2a6d7173 100644 --- a/src/engine/backends/kite_llm/kite_llm_backend.cpp +++ b/src/engine/backends/kite_llm/kite_llm_backend.cpp @@ -73,7 +73,7 @@ bool ValidateCpuRunConfig(const std::string& platform, std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](unsigned char c) { return std::toupper(c); }); if (!IsCpuPlatform(normalized)) return true; - // Only enforce the explicit CPU constraint; the SDK owns its full schema. + // 只强制显式的 CPU 约束;完整 schema 归 SDK 所有。 if (config.is_object() && config.contains("model") && config["model"].is_object()) { const auto& model = config["model"]; @@ -133,8 +133,7 @@ bool ResolveRunConfig(const std::string& model_path, return true; } -// Runtime must outlive every model handle, including failed session -// construction. +// Runtime 的生命周期必须长于所有模型句柄,包括会话构造失败的情况。 class KiteRuntime final { public: KiteRuntime() { kiteLLM_Init(); } @@ -190,8 +189,8 @@ class KiteSession : public Interface { prompt.size() > static_cast(std::numeric_limits::max())) { throw std::runtime_error("Invalid kiteLLM prompt length"); } - // The Model owns formatting. Preserve special tokens and the BOS policy - // using token input, avoiding a second vendor chat template. + // 格式化归 Model 负责。使用 token 输入以保留特殊 token 和 BOS 策略, + // 避免再套一层厂商 chat 模板。 int count = 0; const int probe = kiteLLM_Tokenizer_Encode( handle_.get(), prompt.data(), static_cast(prompt.size()), nullptr, diff --git a/src/engine/backends/llama_cpp/llama_cpp_backend.h b/src/engine/backends/llama_cpp/llama_cpp_backend.h index 226d5da8..224d027c 100644 --- a/src/engine/backends/llama_cpp/llama_cpp_backend.h +++ b/src/engine/backends/llama_cpp/llama_cpp_backend.h @@ -9,11 +9,10 @@ namespace llm_edgeflow { /** - * @brief llama.cpp GGUF provider for the neutral text-generation protocol. + * @brief 面向中性文本生成协议的 llama.cpp GGUF 提供者。 * - * Vendor declarations are intentionally hidden in the implementation file. - * This class owns no chat template, sampling, stop-word, or generation-loop - * semantics. + * 厂商声明有意隐藏在实现文件中。本类不承担 chat 模板、采样、停止词或 + * 生成循环语义。 */ class LlamaCppBackend final : public BackendIdentity { public: diff --git a/src/engine/backends/onnxruntime/onnxruntime_backend.h b/src/engine/backends/onnxruntime/onnxruntime_backend.h index 2934e88c..b2de1c82 100644 --- a/src/engine/backends/onnxruntime/onnxruntime_backend.h +++ b/src/engine/backends/onnxruntime/onnxruntime_backend.h @@ -12,8 +12,8 @@ namespace llm_edgeflow { namespace onnxruntime_detail { -// Neutral Tensor contract helpers are intentionally vendor-type free so the -// strict boundary can be unit-tested even when ONNX Runtime is disabled. +// 中性 Tensor 契约辅助函数有意不含厂商类型, +// 以便在禁用 ONNX Runtime 时也能单测严格边界。 bool ValidateInputTensor(const Tensor& tensor, const TensorSpec& spec, const BatchPolicy& policy, std::string* diagnostic = nullptr) noexcept; diff --git a/src/engine/backends/whisper_cpp/whisper_cpp_backend.cpp b/src/engine/backends/whisper_cpp/whisper_cpp_backend.cpp index 0befb8f6..f4310117 100644 --- a/src/engine/backends/whisper_cpp/whisper_cpp_backend.cpp +++ b/src/engine/backends/whisper_cpp/whisper_cpp_backend.cpp @@ -97,7 +97,7 @@ class WhisperCppSession final : public IAudioTranscriptionSession { diagnostic, "Audio duration too short (< 100ms / 1600 samples)"); return -1; } - if (n_samples > 960000) { // 60 seconds + if (n_samples > 960000) { // 60 秒 SetDiagnosticNoexcept(diagnostic, "Audio duration exceeds 60s limit"); return -1; } @@ -325,7 +325,7 @@ std::shared_ptr WhisperCppBackend::Load( } context_guard{raw_ctx}; auto session = std::make_shared(raw_ctx, n_threads); - context_guard.ctx = nullptr; // ownership transferred successfully + context_guard.ctx = nullptr; // 所有权已成功转移 return session; #endif } catch (const std::exception& e) { diff --git a/src/engine/models/bge_embedding/bge_embedding_model.cpp b/src/engine/models/bge_embedding/bge_embedding_model.cpp index b6dbed82..f1a396db 100644 --- a/src/engine/models/bge_embedding/bge_embedding_model.cpp +++ b/src/engine/models/bge_embedding/bge_embedding_model.cpp @@ -280,7 +280,7 @@ int BgeEmbeddingModel::RawEmbedSlice( return -1; } } else { - // Dummy padding item + // 填充用的空条目 tokenizer_.Encode("", max_length_, &sample_ids, &sample_mask, nullptr); } diff --git a/src/engine/models/bge_reranker/bge_reranker_model.cpp b/src/engine/models/bge_reranker/bge_reranker_model.cpp index 80e0290c..7696e06d 100644 --- a/src/engine/models/bge_reranker/bge_reranker_model.cpp +++ b/src/engine/models/bge_reranker/bge_reranker_model.cpp @@ -223,7 +223,7 @@ int BgeRerankerModel::RawScoreSlice(const QueryCandidatesBatch& all_inputs, return -1; } } else { - // Dummy padding item + // 填充用的空条目 if (!tokenizer_.EncodePair("", "", max_length_, &sample_ids, &sample_mask, &sample_type, &diag)) { ALG_LOG_ERROR("[BgeRerankerModel] Dummy EncodePair error: %s\n", diff --git a/src/engine/models/common/embedding_numeric_support.h b/src/engine/models/common/embedding_numeric_support.h index 3c5b6187..373d7f18 100644 --- a/src/engine/models/common/embedding_numeric_support.h +++ b/src/engine/models/common/embedding_numeric_support.h @@ -9,16 +9,14 @@ namespace llm_edgeflow { namespace embedding_support { /** - * @brief Finalize and optionally L2-normalize an embedding vector in double - * precision. + * @brief 以双精度完成 embedding 向量计算,并可选做 L2 归一化。 * - * Enforces: - * - Non-empty dimensions. - * - All inputs and outputs must be finite. - * - When normalize == true, zero-norm vectors fail (cannot normalize to unit - * direction). - * - When normalize == false, finite zero vectors are allowed. - * - Double accumulation avoids float overflow for bounded float model rows. + * 保证: + * - 维度非空。 + * - 所有输入和输出必须是有限值。 + * - normalize == true 时,零范数向量失败 (无法归一化为单位方向)。 + * - normalize == false 时,允许有限的零向量。 + * - 双精度累加避免有界 float 模型行溢出。 */ template inline bool FinalizeEmbeddingVector(const T* values, size_t dim, bool normalize, diff --git a/src/engine/models/generated_text_embedding/generated_text_embedding_model.h b/src/engine/models/generated_text_embedding/generated_text_embedding_model.h index e847a03d..202a47ea 100644 --- a/src/engine/models/generated_text_embedding/generated_text_embedding_model.h +++ b/src/engine/models/generated_text_embedding/generated_text_embedding_model.h @@ -7,9 +7,8 @@ namespace llm_edgeflow { -// Pools generated-token hidden states. This is a distinct vector space from -// encoder embeddings: callers must evaluate retrieval quality and rebuild -// indexes when changing the model, prompt, generation limit or pooling. +// 对生成 token 的隐藏状态做池化。其向量空间不同于编码器 embedding: +// 更换模型、prompt、生成上限或池化方式时,调用方须评估检索质量并重建索引。 class GeneratedTextEmbeddingModel final : public ModelIdentity { public: diff --git a/src/engine/models/qwen_causal_lm/qwen_causal_lm_model.h b/src/engine/models/qwen_causal_lm/qwen_causal_lm_model.h index 79395abb..ed48f0d8 100644 --- a/src/engine/models/qwen_causal_lm/qwen_causal_lm_model.h +++ b/src/engine/models/qwen_causal_lm/qwen_causal_lm_model.h @@ -12,11 +12,10 @@ namespace llm_edgeflow { /** - * @brief Qwen ChatML semantics over a neutral text-generation session. + * @brief 基于中性文本生成会话的 Qwen ChatML 语义。 * - * This Model owns prompt formatting and provenance only. Tokenization, - * sampling, generation loops and vendor resources belong below the unified - * ITextGenerationSession boundary. + * 本 Model 只负责 prompt 格式化和来源追踪。分词、采样、生成循环和厂商 + * 资源都位于统一的 ITextGenerationSession 边界之下。 */ class QwenCausalLmModel final : public ModelIdentity { diff --git a/src/engine/models/vision_document/vision_document_model.cpp b/src/engine/models/vision_document/vision_document_model.cpp index 35fac0e5..23879939 100644 --- a/src/engine/models/vision_document/vision_document_model.cpp +++ b/src/engine/models/vision_document/vision_document_model.cpp @@ -108,7 +108,7 @@ int VisionDocumentModel::Recognize(const ImageRefBatch& images, reason.empty() ? "Image generation returned empty text" : reason); return -1; } - // Generative recognition has no measured boxes or confidence scores. + // 生成式识别没有实测的框或置信度。 *output = std::move(document); return 0; }, diff --git a/src/engine/models/whisper_asr/whisper_asr_model.cpp b/src/engine/models/whisper_asr/whisper_asr_model.cpp index 21ec1e57..bda32369 100644 --- a/src/engine/models/whisper_asr/whisper_asr_model.cpp +++ b/src/engine/models/whisper_asr/whisper_asr_model.cpp @@ -104,7 +104,7 @@ int WhisperAsrModel::Transcribe(const AudioPcmBatch& audio, TextBatch* outputs, if (n_samples == 0) { continue; } - if (n_samples < 1600) { // 100 ms + if (n_samples < 1600) { // 100 毫秒 ALG_LOG_ERROR( "[WhisperAsrModel] Audio sample count %zu < 1600 (100 ms)\n", n_samples); diff --git a/src/engine/text/utf8.h b/src/engine/text/utf8.h index 0b42cae1..f16c04e5 100644 --- a/src/engine/text/utf8.h +++ b/src/engine/text/utf8.h @@ -9,18 +9,18 @@ namespace llm_edgeflow { namespace utf8 { -// Returns the consumed byte count, or zero for an invalid UTF-8 sequence. +// 返回消耗的字节数;UTF-8 序列非法时返回 0。 size_t DecodeCodePoint(const char* data, size_t length, uint32_t* code_point) noexcept; -// Produces byte offsets for every Unicode code-point boundary, including 0 -// and text.size(). Returns false and the first invalid byte offset on failure. +// 生成每个 Unicode 码点边界的字节偏移,包含 0 和 text.size()。 +// 失败时返回 false 并给出首个非法字节的偏移。 bool BuildCodePointBoundaries(std::string_view text, std::vector* boundaries, size_t* invalid_offset = nullptr); -// Removes only an incomplete or invalid trailing code unit sequence. Complete -// UTF-8 content and any earlier bytes are left unchanged. +// 只移除末尾不完整或非法的码元序列。完整的 UTF-8 内容及之前的字节 +// 保持不变。 void StripIncompleteSuffix(std::string* text) noexcept; } // namespace utf8 diff --git a/src/engine/text_generation/common_autoregressive_generator.h b/src/engine/text_generation/common_autoregressive_generator.h index 423431e9..4880f55a 100644 --- a/src/engine/text_generation/common_autoregressive_generator.h +++ b/src/engine/text_generation/common_autoregressive_generator.h @@ -11,8 +11,8 @@ namespace llm_edgeflow { namespace text_generation { -// Backend-private, vendor-neutral state for one autoregressive request. It is -// intentionally not a Pipeline/Catalog execution protocol. +// Backend 私有、与厂商无关的单次自回归请求状态。 +// 有意不作为 Pipeline/Catalog 执行协议。 class IAutoregressiveDecoder { public: virtual ~IAutoregressiveDecoder() = default; @@ -25,8 +25,8 @@ class IAutoregressiveDecoder { virtual bool IsEndToken(int32_t token) const noexcept = 0; virtual size_t MaxContextTokens() const noexcept = 0; - // The first call receives the complete prompt. Later calls receive only the - // newly sampled token; the concrete decoder owns its incremental state. + // 首次调用接收完整 prompt,后续调用只接收新采样的 token; + // 增量状态由具体解码器维护。 virtual int Evaluate(const std::vector& incremental_tokens, std::vector* logits, std::string* diagnostic = nullptr) noexcept = 0; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5c917bc1..40630ad7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,12 +1,12 @@ link_libraries(edgeflow_internal_headers) if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - # Match runtime type visibility for std::any Blackboard payloads. With default - # visibility, TextTemplateNodeTest/CommonNodesTest fail to read attributes. + # 与 std::any 类型的 Blackboard payload 保持一致的运行时类型可见性。使用默认 + # 可见性时,TextTemplateNodeTest/CommonNodesTest 无法读取属性。 set(CMAKE_CXX_VISIBILITY_PRESET hidden) endif() -# Allocation replacement is confined to the focused test executables. It is -# never linked into runtime objects, the SDK, demos or repository tools. +# 分配替换仅限于相关的测试可执行文件, +# 绝不链接进运行时对象、SDK、Demo 或仓库工具。 add_library(edgeflow_test_allocation_failure OBJECT support/scoped_allocation_failure.cpp) target_include_directories(edgeflow_test_allocation_failure PUBLIC @@ -22,8 +22,7 @@ if(UNIX AND NOT APPLE) LABELS "tier2;static-gate;dev-fast;sanitizer-compatible") endif() -# Validation parity uses deterministic registrations without contaminating the -# production CLI or shared SDK. +# 校验一致性测试使用确定性注册,不污染生产 CLI 或共享 SDK。 add_executable(alg_pipeline_tool_test ${PROJECT_SOURCE_DIR}/src/cli/alg_pipeline_tool.cpp ${PROJECT_SOURCE_DIR}/src/cli/pipeline_authoring.cpp @@ -38,7 +37,7 @@ target_compile_definitions(alg_pipeline_tool_test PRIVATE target_include_directories(alg_pipeline_tool_test PRIVATE "${PROJECT_BINARY_DIR}/generated/internal") -# GNU-compatible link wrapping is confined to the existing model test runner. +# GNU 兼容的链接包装仅限于现有的模型测试 runner。 function(edgeflow_enable_aligned_allocation_failure target_name) if(UNIX AND NOT APPLE) target_compile_definitions(${target_name} PRIVATE diff --git a/tests/RuntimeTests.cmake b/tests/RuntimeTests.cmake index b2eae89a..d87e4325 100644 --- a/tests/RuntimeTests.cmake +++ b/tests/RuntimeTests.cmake @@ -1,4 +1,4 @@ -# Sharded Google Test runners and label-driven development test matrix. +# 分片的 Google Test runner 与按标签驱动的开发测试矩阵。 include(${CMAKE_CURRENT_LIST_DIR}/ScaffoldFixtures.cmake) @@ -35,14 +35,14 @@ function(edgeflow_add_runner_test test_name runner_name gtest_filter labels) LABELS "${labels}") endfunction() -# Discover only each runner's owned test directories. Keep generated sources, -# process-isolated contracts and opt-in E2E targets explicit. CONFIGURE_DEPENDS -# makes additions/removals trigger regeneration on the next build (CMake 3.19). +# 只扫描每个 runner 自有的测试目录。生成的源码、进程隔离的契约测试和 +# 需显式开启的 E2E 目标保持显式列出。CONFIGURE_DEPENDS 使增删文件在下次 +# 构建时触发重新生成 (CMake 3.19)。 file(GLOB EDGEFLOW_TEST_CORE_SRCS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/unit/core/test_*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/unit/engine/test_*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/unit/logging/test_*.cpp") -# Pipeline integration tests have different runner dependencies. +# Pipeline 集成测试的 runner 依赖不同。 list(APPEND EDGEFLOW_TEST_CORE_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/integration/pipeline/test_model_backend_pipeline.cpp") add_executable(edgeflow_test_core_runner @@ -170,8 +170,7 @@ if(LLM_EDGEFLOW_HAS_ONNXRUNTIME) add_dependencies(alg_demo edgeflow_generated_model_fixtures) endif() -# Process-isolated targets. Registry conflict intentionally runs each dirty -# singleton scenario in its own process. +# 进程隔离的目标。注册表冲突测试有意让每个污染单例的场景运行在独立进程中。 add_executable(test_cpp_operator_sdk "${PROJECT_SOURCE_DIR}/tests/contract/abi/test_cpp_operator_sdk.cpp") set_target_properties(test_cpp_operator_sdk PROPERTIES LINK_LIBRARIES "llm_edgeflow::sdk") @@ -297,8 +296,8 @@ edgeflow_add_runner_test(ComplexConvertersTest edgeflow_test_adapter_runner edgeflow_add_runner_test(DocQaRerankTest edgeflow_test_tooling_runner "DocQaRerankPipelineTest.*" "${_edgeflow_tier1}") -# This suite exercises Validator, typed Blackboard and Pipeline::Execute. -# Its runner grouping does not make it tooling-only coverage. +# 该测试套件覆盖 Validator、类型化 Blackboard 和 Pipeline::Execute。 +# 放在工具 runner 中并不意味着它只是工具测试。 edgeflow_add_runner_test(PipelineStudioTest edgeflow_test_tooling_runner "PipelineCatalogTest.*:PipelineValidatorTest.*" "${_edgeflow_tier3}") @@ -335,7 +334,7 @@ edgeflow_add_runner_test(LlamaCppBackendTest edgeflow_test_core_runner edgeflow_add_runner_test(WhisperCppBackendTest edgeflow_test_core_runner "WhisperCppBackendTest.*" "${_edgeflow_tier1}") -# Architecture and source-governance gates. +# 架构与源码治理门禁。 add_test(NAME LayerGuardTest COMMAND ${CMAKE_COMMAND} -E env "LLM_EDGEFLOW_LAYER_COMPILE_MANIFEST=${PROJECT_BINARY_DIR}/layer_includes/compile_checks_$.cmake" @@ -492,12 +491,11 @@ set_tests_properties(RegistryConflictNodeTest RegistryConflictModelTest RegistryAuthoringStartup_duplicate_member RegistryAuthoringStartup_factory_exception PROPERTIES TIMEOUT 5) -# The opt-in real Kite deployment suite loads text, ONNX and vision models. +# 需显式开启的真实 Kite 部署测试会加载文本、ONNX 和视觉模型。 set_tests_properties(DemoRunnerTest PROPERTIES TIMEOUT 300) -# Required runtime contract suites for the canonical runner assembly. -# Smoke and tooling tests may extend this set; the assembly must not silently -# omit any of these core contracts. +# 规范 runner 组装必须包含的运行时契约测试。 +# 冒烟测试和工具测试可以扩展此集合,但组装时不得静默遗漏其中任何一项。 set(EDGEFLOW_REQUIRED_CONTRACT_TESTS QualityGateScriptsContractTest BatchExecutorTest diff --git a/tests/ScaffoldFixtures.cmake b/tests/ScaffoldFixtures.cmake index 6cd66b06..97247392 100644 --- a/tests/ScaffoldFixtures.cmake +++ b/tests/ScaffoldFixtures.cmake @@ -1,4 +1,4 @@ -# Generated extensions belong only to tests, never the SDK or production Catalog. +# 生成的扩展只属于测试,绝不进入 SDK 或生产 Catalog。 set(EDGEFLOW_CONTROL_FIXTURE_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/test-fixtures/control/test_control_node.cpp") get_filename_component(_control_fixture_dir "${EDGEFLOW_CONTROL_FIXTURE_SOURCE}" DIRECTORY) @@ -32,7 +32,7 @@ add_custom_command( COMMENT "Generating custom Node snippets and standalone behavioral test fixtures" VERBATIM) -# Compile the exact function examples in test runners, never the SDK Catalog. +# 在测试 runner 中编译文档里的原样函数示例,绝不进入 SDK Catalog。 list(APPEND EDGEFLOW_SCAFFOLD_FIXTURE_SOURCE "${PROJECT_SOURCE_DIR}/dev_support/node_authoring/starter_batch_node.cpp" "${PROJECT_SOURCE_DIR}/dev_support/node_authoring/starter_multi_model_node.cpp" diff --git a/tests/contract/abi/test_adapter_contract_security.cpp b/tests/contract/abi/test_adapter_contract_security.cpp index 64164b6b..b17c7c0c 100644 --- a/tests/contract/abi/test_adapter_contract_security.cpp +++ b/tests/contract/abi/test_adapter_contract_security.cpp @@ -50,8 +50,8 @@ class AdapterContractSecurityTest : public ::testing::Test { namespace { -// This fixture records actual generation calls while the test executes the -// shipped translation Pipeline through Operator API. It does no JSON handling. +// 测试通过 Operator API 执行随附的翻译 Pipeline,该夹具记录实际的生成调用, +// 不做任何 JSON 处理。 class TranslationProbeModel final : public ILlmModel { public: inline static constexpr char kModelType[] = "test_translation_probe"; @@ -99,7 +99,7 @@ class TranslationProbeModel final : public ILlmModel { int failure = 0; }; -// Fails during model creation so Operator Create reaches the loading stage. +// 在模型创建时失败,使 Operator Create 进入加载阶段。 class FailingCreateModel { public: inline static constexpr char kModelType[] = "test_failing_create_model"; @@ -152,8 +152,8 @@ TEST_F(AdapterContractSecurityTest, ASSERT_TRUE(source.is_open()); nlohmann::json pipeline; source >> pipeline; - // Keep the production graph and port bindings. Substitute only model - // execution so these assertions require neither model assets nor a Demo. + // 保留生产计算图和端口绑定,只替换模型执行, + // 使这些断言既不需要模型资源也不需要 Demo。 ASSERT_EQ(pipeline.at("pipeline").size(), 1U); EXPECT_EQ(pipeline["pipeline"][0]["node_type"], "LlmGenerateNode"); ASSERT_EQ(pipeline.at("models").size(), 1U); @@ -254,8 +254,8 @@ TEST_F(AdapterContractSecurityTest, nlohmann::json({{"translated", translations[i]}})); } } - // Text that happens to resemble JSON or Markdown is still the original - // model result: no JSON parsing, field extraction, stripping, or retries. + // 看起来像 JSON 或 Markdown 的文本仍是原始模型结果: + // 不做 JSON 解析、字段抽取、裁剪或重试。 for (const std::string text : {R"({"translated":"literal","extra":42})", "```json\n{\"translated\":\"literal\"}\n```"}) { @@ -279,7 +279,7 @@ TEST_F(AdapterContractSecurityTest, model->response.assign(2200, 'x'); EXPECT_EQ(process("{\"query\":\"hello\"}"), COMPANY_ALG_ERR_BUFFER_TOO_SMALL); EXPECT_EQ(model->calls.size(), before_invalid + 1); - // Model codes stay in diagnostics; the host sees the execution category. + // Model 错误码保留在诊断中;宿主看到的是执行阶段类别。 model->failure = -731; EXPECT_EQ(process("{\"query\":\"hello\"}"), COMPANY_ALG_ERR_UNKNOWN); const std::string model_error = operator_api::GetOperatorLastError(); @@ -295,8 +295,8 @@ TEST_F(AdapterContractSecurityTest, EXPECT_EQ(nlohmann::json::parse(out_entities_json), nlohmann::json({{"translated", "你好"}})); - // Invalid UTF-8 in model response causes JSON dump to throw, mapping to - // COMPANY_ALG_ERR_EXCEPTION (-99) through the public Operator Process barrier + // 模型响应中的非法 UTF-8 会使 JSON dump 抛异常,经公开的 Operator Process + // 屏障映射为 COMPANY_ALG_ERR_EXCEPTION (-99) model->response = "prefix\xFF\xFFsuffix"; EXPECT_EQ(process("{\"query\":\"hello\"}"), COMPANY_ALG_ERR_EXCEPTION); model->response = "你好"; @@ -334,7 +334,7 @@ TEST_F(AdapterContractSecurityTest, &written, &status), COMPANY_ALG_ERR_BUFFER_TOO_SMALL); - // Reordered internal results must map back to external request IDs. + // 重排后的内部结果必须映射回外部请求 ID。 AlgContext reordered; request_ids = {999, 123}; reordered.Publish(kLlmAnswers, TextBatch{{1, 0, "第二句"}, {0, 0, "第一句"}}); @@ -876,14 +876,14 @@ TEST_F(AdapterContractSecurityTest, ConcurrentStatelessAdapterExecution) { } } -// Cross-sample carrier error vs biz decode error priority +// 跨样本的载体错误与 biz 解码错误的优先级 TEST_F(AdapterContractSecurityTest, TranslationCrossSampleCarrierVsBizErrorPriority) { const auto* converter = IoConverterRegistry::Instance().FindInputConverter( "translate.json.operator.v1"); ASSERT_NE(converter, nullptr); - // Sample 0 has carrier error (oversized string) + // 样本 0 有载体错误 (字符串超长) std::string oversized(64 * 1024 + 1, 'z'); CompanyString cs_oversized{static_cast(oversized.size()), const_cast(oversized.data())}; @@ -907,7 +907,7 @@ TEST_F(AdapterContractSecurityTest, EXPECT_EQ(carrier_status.SampleIndex(), 0); EXPECT_EQ(carrier_status.FieldPath(), "sentence_text"); - // Pure biz decode error retains translate.json.operator.v1 converter name + // 纯 biz 解码错误保留 translate.json.operator.v1 Converter 名称 std::string bad_json = "{\"wrong_field\":123}"; CompanyString cs_biz{static_cast(bad_json.size()), const_cast(bad_json.data())}; @@ -926,14 +926,14 @@ TEST_F(AdapterContractSecurityTest, EXPECT_EQ(biz_status.FieldPath(), "json"); } -// Return code and AdapterStatus independence +// 返回码与 AdapterStatus 相互独立 TEST_F(AdapterContractSecurityTest, TranslationReturnCodeAndAdapterStatusIndependence) { const auto* converter = IoConverterRegistry::Instance().FindOutputConverter( "translate.json.operator.v1"); ASSERT_NE(converter, nullptr); - // A request ID table is present, but answers are missing + // 存在请求 ID 表,但缺少答案 AlgContext ctx; const std::vector request_ids{1001}; @@ -953,21 +953,19 @@ TEST_F(AdapterContractSecurityTest, int ret = converter->encode_fn(&ctx, bindings, options, &view, &written, &status); - // Missing internal data is invalid input, independently of output capacity. + // 缺少内部数据属于非法输入,与输出容量无关。 EXPECT_EQ(ret, COMPANY_ALG_ERR_INVALID_INPUT); EXPECT_EQ(status.Code(), COMPANY_ALG_ERR_INVALID_INPUT); } -// Translate serialization failure (invalid UTF-8) priority -// over capacity check +// 翻译序列化失败 (非法 UTF-8) 优先于容量检查 TEST_F(AdapterContractSecurityTest, TranslationSerializationFailurePriorityOverCapacity) { const auto* converter = IoConverterRegistry::Instance().FindOutputConverter( "translate.json.operator.v1"); ASSERT_NE(converter, nullptr); - // A request ID table is present, but the answer has invalid UTF-8 byte - // sequence + // 存在请求 ID 表,但答案含非法 UTF-8 字节序列 AlgContext ctx; const std::vector request_ids{1001}; std::string invalid_utf8 = "prefix\xFF\xFFsuffix"; @@ -987,14 +985,14 @@ TEST_F(AdapterContractSecurityTest, size_t written = 0; AdapterStatus status; - // Serialization in Encode precedes capacity validation; - // unhandled dump exception propagates out of encode_fn + // Encode 中先序列化再校验容量; + // 未处理的 dump 异常会从 encode_fn 抛出 EXPECT_THROW( converter->encode_fn(&ctx, bindings, options, &view, &written, &status), std::exception); } -// Translate null AlgContext diagnostics +// 翻译在 AlgContext 为空时的诊断 TEST_F(AdapterContractSecurityTest, TranslateNullContextDiagnostics) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "translate.json.operator.v1"); @@ -1003,8 +1001,7 @@ TEST_F(AdapterContractSecurityTest, TranslateNullContextDiagnostics) { "translate.json.operator.v1"); ASSERT_NE(out_conv, nullptr); - // 1. Decode with null context: must return INVALID_INPUT (-3) with field - // "context" + // 1. context 为空时 Decode 必须返回 INVALID_INPUT (-3),字段为 "context" std::string query_json = "{\"query\":\"test\"}"; CompanyString cs{static_cast(query_json.size()), const_cast(query_json.data())}; @@ -1030,8 +1027,7 @@ TEST_F(AdapterContractSecurityTest, TranslateNullContextDiagnostics) { in_conv->decode_fn(in_view, in_options, in_bindings, nullptr, nullptr), COMPANY_ALG_ERR_INVALID_INPUT); - // 2. Encode with null context: must return INVALID_INPUT (-3) with field - // "context" + // 2. context 为空时 Encode 必须返回 INVALID_INPUT (-3),字段为 "context" CompanyOperatorEntityOutput output{}; ExternalOutputBatchView out_view; out_view.count = 1; @@ -1114,7 +1110,7 @@ TEST_F(AdapterContractSecurityTest, InputLengthLimitsStayUnchanged) { size_t limit; const char* message; }; - // Keep numeric expectations independent of the implementation constants. + // 数值期望与实现中的常量保持独立。 const Boundary boundaries[] = { {"keyword.plain.operator.v1", "sentence_text", 65536, "sentence_text length exceeds 64 KiB limit"}, @@ -1248,7 +1244,7 @@ TEST_F(AdapterContractSecurityTest, OperatorInputLimitsStayUnchanged) { ASSERT_EQ(ops.Create(&raw_handle, ¶m), 0) << GetOperatorLastError(); const auto destroy = [ops](void* handle) { ops.Destroy(handle); }; std::unique_ptr handle(raw_handle, destroy); - // Successful calls preserve the existing thread-local last error. + // 成功的调用保留现有的线程局部 last error。 const std::string previous_error = GetOperatorLastError(); EXPECT_EQ(ops.Process(handle.get(), inputs, outputs), expected) << GetOperatorLastError(); @@ -1278,8 +1274,8 @@ TEST_F(AdapterContractSecurityTest, OperatorInputLimitsStayUnchanged) { "sentence_text length 65537 exceeds max limit 65536"); } - // Use the production graph with test model execution; neither rejection - // path should reach model inference, and no external model assets are needed. + // 使用生产计算图和测试模型执行;两条拒绝路径都不应到达模型推理, + // 也不需要外部模型资源。 std::ifstream source(GetConfigPath("configs/pipeline_cross_rerank_cpu.json")); ASSERT_TRUE(source.is_open()); nlohmann::json pipeline; @@ -1352,8 +1348,8 @@ TEST_F(AdapterContractSecurityTest, std::filesystem::remove_all(directory, error); } } cleanup{directory}; - // Keyword and rule hits, typed rule constants, regex captures and the - // default hit all reach the external JSON response. + // 关键词和规则命中、类型化规则常量、正则捕获以及默认命中 + // 都会进入外部 JSON 响应。 const nlohmann::json pipeline = nlohmann::json::parse(R"j({ "deployment": {"io": {"io_binding": "keyword_match.operator.v1"}}, "models": [], @@ -1459,7 +1455,7 @@ TEST_F(AdapterContractSecurityTest, CreateAndExecutionFailuresUseStageCodes) { return op.Create(handle, ¶m); }; - // Validation and model loading failures are creation parameter errors. + // 校验失败和模型加载失败都属于创建参数错误。 void* handle = nullptr; auto invalid = pipeline; invalid["pipeline"][1]["node_type"] = "MissingNodeType"; @@ -1483,7 +1479,7 @@ TEST_F(AdapterContractSecurityTest, CreateAndExecutionFailuresUseStageCodes) { << operator_api::GetOperatorLastError(); std::unique_ptr owner(handle, op.Destroy); - // The test backend returns plain text, so the fail policy rejects it. + // 测试 Backend 返回纯文本,因此 fail 策略会拒绝它。 std::string text = "张三在北京"; CompanyString sentence{static_cast(text.size()), text.data()}; CompanyOperatorEntityInput input{11, &sentence}; diff --git a/tests/contract/abi/test_cpp_operator_sdk.cpp b/tests/contract/abi/test_cpp_operator_sdk.cpp index 0602d475..fbe4622e 100644 --- a/tests/contract/abi/test_cpp_operator_sdk.cpp +++ b/tests/contract/abi/test_cpp_operator_sdk.cpp @@ -1,10 +1,9 @@ /** * @file test_cpp_operator_sdk.cpp - * @brief Public C++ Operator SDK consumer test. + * @brief 公开 C++ Operator SDK 的消费方测试。 * - * This file verifies that an external C++ consumer can link against - * llm_edgeflow::sdk using solely the public SDK headers, without any - * internal headers, runtime objects, or test mocks. + * 验证外部 C++ 消费方只使用公开 SDK 头文件即可链接 llm_edgeflow::sdk, + * 不依赖任何内部头文件、运行时对象或测试 mock。 */ #include @@ -23,7 +22,7 @@ #include "platform_mock/operator_data_types.h" #include "platform_mock/operator_types.h" -// Public layout and contract assertions +// 公开布局与契约断言 static_assert(sizeof(CompanyString) == sizeof(int32_t) + sizeof(char*) + (sizeof(char*) == 8 ? 4 : 0), "CompanyString memory layout check"); @@ -36,7 +35,7 @@ static_assert(E_ALG_BASE_LOG_LEVEL_VERBOSE == 5, "Verbose log level must remain 5"); int main() { - // 1. Version contract check + // 1. 版本契约检查 if (std::strcmp(COMPANY_ALG_PRODUCT_VERSION, "11.0.0") != 0 || std::strcmp(COMPANY_ALG_ABI_VERSION, "9.0.0") != 0 || COMPANY_ALG_ABI_VERSION_MAJOR != 9) { @@ -45,7 +44,7 @@ int main() { return 1; } - // 2. Logging API check + // 2. 日志 API 检查 if (AlgBase_getLogLevelByName("LLM_EDGEFLOW") != E_ALG_BASE_LOG_LEVEL_WARNING) { std::fprintf(stderr, @@ -63,7 +62,7 @@ int main() { ALG_LOG_DEBUG("This debug record is filtered\n"); ALG_LOG_WARNING("[SDK Consumer Test] Public log macro is operational\n"); - // 3. Operator table inspection + // 3. 检查 Operator 函数表 auto op = llm_edgeflow::operator_api::Get_LLM_EDGEFLOW_OperatorTable(); if (!op.Init || !op.Create || !op.Process || !op.Control || !op.Destroy || !op.DeInit) { @@ -79,7 +78,7 @@ int main() { return 5; } - // Locate configs directory + // 定位 configs 目录 const char* root_candidates[] = {".", "..", "../.."}; const char* config_rel = "configs/pipeline_keyword_match_rules.conf"; std::string root_dir; @@ -162,7 +161,7 @@ int main() { return 11; } - // 8. Control: update rule categories + // 8. Control:更新规则类别 llm_edgeflow::operator_api::ControlUpdateRulesParam rules_param{ "{\"categories\":{\"URGENT\":[\"加急\"]}}"}; if (op.Control(handle, @@ -173,7 +172,7 @@ int main() { return 12; } - // 9. Process again and verify rule change took effect + // 9. 再次 Process,验证规则变更已生效 outputs[0]["client_channel.keyword_out"] = nullptr; if (op.Process(handle, inputs, outputs) != 0) { std::fprintf(stderr, "[SDK Consumer Test] Second op.Process failed: %s\n", @@ -188,7 +187,7 @@ int main() { return 14; } - // 10. Copy fields and release all leases before destroy + // 10. 复制字段,并在 Destroy 前释放所有租约 std::string match_copy; if (out_dto->match_result_json && out_dto->match_result_json->data) { match_copy.assign(out_dto->match_result_json->data, @@ -198,7 +197,7 @@ int main() { outputs.clear(); inputs.clear(); - // 11. Destroy handle + // 11. Destroy 句柄 if (op.Destroy(handle) != 0) { std::fprintf(stderr, "[SDK Consumer Test] op.Destroy failed: %s\n", llm_edgeflow::operator_api::GetOperatorLastError()); diff --git a/tests/contract/abi/test_operator_safety.cpp b/tests/contract/abi/test_operator_safety.cpp index d1615bd4..93e270f2 100644 --- a/tests/contract/abi/test_operator_safety.cpp +++ b/tests/contract/abi/test_operator_safety.cpp @@ -230,8 +230,7 @@ TEST_F(OperatorSafetyTest, InputErrorsPrecedeOutputErrorsAndDoNotPublish) { NamedIoBatch outputs(1); outputs[0]["invalid_output_key"] = nullptr; - // Both sides are invalid: carrier validation must win over output key - // parsing. + // 两侧均非法:载体校验必须优先于输出键解析。 EXPECT_EQ(op.Process(handle.get(), inputs, outputs), COMPANY_ALG_ERR_INVALID_INPUT); EXPECT_NE(std::string(GetOperatorLastError()) @@ -269,8 +268,8 @@ TEST_F(OperatorSafetyTest, ASSERT_EQ(op.Create(&raw_handle, ¶m), 0); std::unique_ptr handle(raw_handle, op.Destroy); - // Only the second sample matches a category whose serialized result exceeds - // the configured 2047-byte capacity. The first sample can encode normally. + // 只有第二个样本命中的类别序列化后超过配置的 2047 字节容量, + // 第一个样本可以正常编码。 const std::string rules = "{\"categories\":{\"" + std::string(2100, 'x') + "\":[\"overflow\"]}}"; ControlUpdateRulesParam control{rules.c_str()}; @@ -297,8 +296,8 @@ TEST_F(OperatorSafetyTest, EXPECT_EQ(output.at("client.keyword_out"), nullptr); } - // Reusing a depth-two pool for another two-item batch proves all failed - // leases were returned, including the already encoded first sample. + // 用深度为 2 的池再处理一个两条目批次,证明所有失败的租约都已归还, + // 包括已编码的第一个样本。 second.sentence_text = &first_text; ASSERT_EQ(op.Process(handle.get(), inputs, outputs), COMPANY_ALG_SUCCESS); for (size_t i = 0; i < outputs.size(); ++i) { @@ -654,8 +653,7 @@ TEST_F(OperatorSafetyTest, EntityFailureSampleSentinelValues) { EXPECT_EQ(out0.status_code, 0); EXPECT_STREQ(out0.entities_json->data, "[\"valid_entity\"]"); - // Sample 1: request_id was written, but status_code and entities_json - // retained sentinels + // 样本 1:request_id 已写入,但 status_code 和 entities_json 仍保留哨兵值 EXPECT_EQ(out1.request_id, 2002u); EXPECT_EQ(out1.status_code, -777); EXPECT_STREQ(out1.entities_json->data, "SENTINEL_PAYLOAD"); diff --git a/tests/contract/architecture/test_diagram_render_gate.sh b/tests/contract/architecture/test_diagram_render_gate.sh index 7b21cf79..883733c0 100755 --- a/tests/contract/architecture/test_diagram_render_gate.sh +++ b/tests/contract/architecture/test_diagram_render_gate.sh @@ -23,7 +23,7 @@ run_fixture_renderer() { ./scripts/render_architecture_diagrams.sh --check >/dev/null 2>&1 -# A syntactically valid source change must make the committed asset stale. +# 语法合法的源文件改动必须使已提交的资源过期。 if sed --version >/dev/null 2>&1; then sed -i '/@enduml/i class SourceDriftProbe' \ "${FIXTURE_DOC_ROOT}/architecture_classes.puml" @@ -38,7 +38,7 @@ if run_fixture_renderer --check; then fi cp doc/architecture_classes.puml "${FIXTURE_DOC_ROOT}/architecture_classes.puml" -# A committed asset that does not match the generated result must fail. +# 已提交的资源与生成结果不一致时必须失败。 echo "corrupted svg" > \ "${FIXTURE_DOC_ROOT}/assets/architecture_class_diagram.svg" if run_fixture_renderer --check; then @@ -46,7 +46,7 @@ if run_fixture_renderer --check; then exit 1 fi -# Generate must repair only the temporary fixture, then check must pass. +# generate 只能修复临时夹具,之后 check 必须通过。 run_fixture_renderer --generate run_fixture_renderer --check diff --git a/tests/contract/architecture/test_layer_header_views.cmake b/tests/contract/architecture/test_layer_header_views.cmake index 4250dcd6..366dcf29 100644 --- a/tests/contract/architecture/test_layer_header_views.cmake +++ b/tests/contract/architecture/test_layer_header_views.cmake @@ -1,4 +1,4 @@ -# Invoked by the existing LayerGuardTest with generated, evaluated target paths. +# 由现有的 LayerGuardTest 调用,传入生成并求值后的目标路径。 include("${LAYER_COMPILE_MANIFEST}") file(MAKE_DIRECTORY "${layer_test_root}") @@ -24,7 +24,7 @@ function(check_header layer header allowed) if(status EQUAL 0) message(FATAL_ERROR "${layer} can compile forbidden header ${header}") endif() - # A failing transitive include is not proof that the named boundary held. + # 传递 include 失败并不能证明目标边界成立。 string(FIND "${error}" "${header}: No such file" gcc_missing) string(FIND "${error}" "'${header}' file not found" clang_missing) if(gcc_missing EQUAL -1 AND clang_missing EQUAL -1) @@ -33,8 +33,8 @@ function(check_header layer header allowed) endif() endfunction() -# Imported runtime targets must not bring their compile usage requirements -# back through final linking, including into Models and the composition root. +# 导入的运行时目标不得在最终链接时把编译使用要求带回来, +# 包括带入 Model 和组合根。 foreach(layer model_execution capability_nodes orchestration integration composition alg_sdk alg_pipeline_tool) foreach(header onnxruntime_cxx_api.h llama.h whisper.h kiteLLM.h) check_header(${layer} ${header} FALSE) @@ -128,8 +128,8 @@ foreach(layer capability_nodes orchestration integration) check_header(${layer} engine/backends/onnxruntime/onnxruntime_backend.h FALSE) endforeach() -# Compile author-facing contracts using only the capability layer's include view. -# A failed negative example must name the intended contract, not a missing header. +# 只用能力节点层的 include 视图编译面向作者的契约。 +# 反例失败时必须指明目标契约,而不是缺少头文件。 function(check_authoring_snippet case_name body expected_diagnostic) set(source "${layer_test_root}/authoring_${case_name}.cpp") file(WRITE "${source}" "#include \nusing namespace llm_edgeflow;\n${body}\n") @@ -181,8 +181,8 @@ struct Models { EmbeddingCall generator; }; auto slot = Llm("generator", "bind_model", &Models::generator); ]=] "no matching function" "LlmCall") -# Verify the actual CMake module through an incremental, dependency-free fixture. -# The normal generator is reused, including when the parent uses multi-config. +# 通过增量、无依赖的夹具验证实际的 CMake 模块。 +# 复用常规生成器,包括父项目使用 multi-config 的情况。 set(fixture "${layer_test_root}/lifecycle") if(DEFINED LAYER_LIFECYCLE_GENERATOR) set(layer_generator "${LAYER_LIFECYCLE_GENERATOR}") diff --git a/tests/contract/architecture/test_llama_cache.cmake b/tests/contract/architecture/test_llama_cache.cmake index 6c111848..bd094243 100644 --- a/tests/contract/architecture/test_llama_cache.cmake +++ b/tests/contract/architecture/test_llama_cache.cmake @@ -1,5 +1,5 @@ -# Exercise the production configure/import/archive paths with tiny local targets. -# No llama.cpp download or target-platform compiler is needed for these checks. +# 用很小的本地目标覆盖生产环境的 configure/import/archive 路径。 +# 这些检查无需下载 llama.cpp,也无需目标平台编译器。 set(_llama_fixture "${TEST_ROOT}/llama_fixture") file(MAKE_DIRECTORY "${_llama_fixture}/upstream/include" "${_llama_fixture}/upstream/ggml/include") @@ -20,7 +20,7 @@ endif() file(WRITE "${_llama_fixture}/CMakeLists.txt" [=[ cmake_minimum_required(VERSION 3.16) project(llama_cache_fixture C CXX) -# Simulate platform defaults only; compilation still uses the host toolchain. +# 只模拟平台默认值;编译仍使用宿主工具链。 set(APPLE ${TEST_APPLE}) include("${EDGEFLOW_SOURCE_DIR}/cmake_ext/ThirdPartyEngines.cmake") if(NOT "${_LLAMA_FOUND}" STREQUAL "${EXPECT_FOUND}") @@ -98,8 +98,8 @@ function(check_llama_cache _case_name _apple _blas) configure_llama_case(changed_vendor OFF ${_blas} ${ARGN} -DGGML_BLAS_VENDOR=FixtureVendor) - # Recreate a parent-revision marker: identical source/toolchain, no BLAS fields. - # Keep every archive present so rejection depends on the metadata alone. + # 重建父 revision 的标记:源码/工具链相同,但不含 BLAS 字段。 + # 保留所有归档,使拒绝只取决于元数据。 set(_marker "${_cache}/.edgeflow-cache-fingerprint") file(READ "${_marker}" _legacy) string(REGEX REPLACE "GGML_BLAS=[^,\n]*," "" _legacy "${_legacy}") diff --git a/tests/contract/architecture/test_quality_gate_contract.py b/tests/contract/architecture/test_quality_gate_contract.py index f2c57363..60d1a712 100644 --- a/tests/contract/architecture/test_quality_gate_contract.py +++ b/tests/contract/architecture/test_quality_gate_contract.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Exercise canonical gate arguments, failure propagation and CI evidence.""" +"""覆盖规范门禁的参数、失败传播和 CI 证据。""" import json import os @@ -123,7 +123,7 @@ def check_delivery_contract(root): def check_delivery_history_contract(root): - """Use real Git graphs and a local remote to prove the PR history policy.""" + """使用真实的 Git 图和本地远端证明 PR 历史策略。""" fixture = root / "delivery history" project = fixture / "project" project.mkdir(parents=True) @@ -473,7 +473,7 @@ def cache_values(build): def check_cache_reset(root, configurations): - """Apply the actual script commands and shared profiles to stale CMake caches.""" + """对过期的 CMake 缓存应用实际的脚本命令和共享 Profile。""" fixture = root / "fixture" fixture.mkdir() shutil.copy2(ROOT / "CMakePresets.json", fixture / "CMakePresets.json") @@ -488,7 +488,7 @@ def check_cache_reset(root, configurations): backend_flags = {"minimal": (), "dev-gate": ("ONNXRUNTIME", "LLAMACPP"), "default-cpu": ("ONNXRUNTIME", "LLAMACPP", "WHISPERCPP"), "kite-cpu": ("ONNXRUNTIME", "KITELLM")} - # Also check every supported direct profile, including the Kite build. + # 同时检查所有支持的直接 Profile,包括 Kite 构建。 configurations += [(None, preset, False, False) for preset in backend_flags] for command, preset, sanitizer, real_models in configurations: expected = {"BUILD_TESTING": "ON", "ENABLE_SANITIZERS": "ON" if sanitizer else "OFF", @@ -502,7 +502,7 @@ def check_cache_reset(root, configurations): result = run([cmake, "-S", str(fixture), "-B", str(build), "-G", generator, *stale, "-DCMAKE_BUILD_TYPE=RelWithDebInfo"]) assert result.returncode == 0, result.stdout + result.stderr if command is None: - # Exercise profile resolution from outside the fixture's source cwd. + # 从夹具源码目录之外解析 Profile。 actual = [str(ROOT / "scripts/configure_build.sh"), str(fixture), str(build), preset] else: actual = [cmake, *command[1:]] @@ -541,8 +541,8 @@ def main(): assert "KITELLM_GATE_RESULT: ${{ needs.kite-llm.result }}" in workflow assert "run: ./scripts/fetch_real_test_models.sh --gguf-only" not in workflow assert "run: ./scripts/fetch_real_test_models.sh --whisper" not in workflow - # Each ccache job falls back to its own main snapshot before any shared prefix, and - # every restored ccache is saved through the pruning action. + # 每个 ccache 作业先回退到自身的 main 快照,再回退到共享前缀, + # 且每个恢复的 ccache 都会通过清理 action 保存。 for prefix in ("ccache-real-", "ccache-whisper-"): assert f" {prefix}${{{{ runner.os }}}}-\n" in workflow, prefix assert "uses: actions/cache@v4\n with:\n path: ${{ env.CCACHE_DIR }}" \ diff --git a/tests/contract/architecture/test_test_labels_contract.py b/tests/contract/architecture/test_test_labels_contract.py index be538df2..7287e2e5 100644 --- a/tests/contract/architecture/test_test_labels_contract.py +++ b/tests/contract/architecture/test_test_labels_contract.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -"""Enforce CTest label contracts to prevent CI scope drift. +"""强制 CTest 标签契约,防止 CI 覆盖范围漂移。 -Validates that: -- Static gates and tooling tests do not run under sanitizer-runtime. -- Core C/C++ runtime suites carry sanitizer-runtime. -- Kite-specific tests carry kite and kite-real labels as appropriate. -- Tests with kite-real also carry kite. +校验: +- 静态门禁和工具测试不在 sanitizer-runtime 下运行。 +- 核心 C/C++ 运行时测试套件带有 sanitizer-runtime 标签。 +- Kite 专属测试按需带有 kite 和 kite-real 标签。 +- 带 kite-real 的测试也带有 kite。 """ import argparse @@ -74,9 +74,9 @@ def parse_gtest_list_tests_output(output: str) -> list: if not line or line.isspace(): continue if not line[0].isspace(): - # Strip trailing comment, e.g. "TypedSuite/0. # TypeParam = int" + # 去掉行尾注释,如 "TypedSuite/0. # TypeParam = int" suite_part = line.split("#", 1)[0].strip() - # Valid suite names must end with "." and contain no spaces, and not just "." + # 合法的套件名必须以 "." 结尾、不含空格,且不能只是 "." if ( len(suite_part) <= 1 or not suite_part.endswith(".") @@ -87,10 +87,10 @@ def parse_gtest_list_tests_output(output: str) -> list: else: if not current_suite: continue - # Strip trailing comment, e.g. "TestCase # GetParam() = 1" + # 去掉行尾注释,如 "TestCase # GetParam() = 1" test_part = line.split("#", 1)[0].strip() test_words = test_part.split() - # Valid test cases are single identifiers without whitespace + # 合法的测试用例是不含空白的单个标识符 if len(test_words) != 1: continue test_case = test_words[0] @@ -100,7 +100,7 @@ def parse_gtest_list_tests_output(output: str) -> list: def match_gtest_filter(test_name: str, gtest_filter: str) -> bool: if not gtest_filter or not gtest_filter.strip(): - # An empty filter (e.g. --gtest_filter=) matches nothing in GoogleTest. + # 空过滤器 (如 --gtest_filter=) 在 GoogleTest 中不匹配任何测试。 return False clean_filter = gtest_filter.strip() if (clean_filter.startswith('"') and clean_filter.endswith('"')) or ( @@ -115,8 +115,8 @@ def match_gtest_filter(test_name: str, gtest_filter: str) -> bool: pos_pats = [p for p in pos_part.split(":") if p] neg_pats = [p for p in neg_part.split(":") if p] - # If positive patterns are omitted but negative patterns exist (e.g. "-Excluded.*"), - # GoogleTest defaults positive matching to "*". + # 省略正向模式但存在反向模式 (如 "-Excluded.*") 时, + # GoogleTest 默认正向匹配 "*"。 if pos_pats: pos_match = any(fnmatch.fnmatchcase(test_name, pat) for pat in pos_pats) else: @@ -193,7 +193,7 @@ def verify_compiled_gtest_coverage( exe = resolve_command_executable(cmd, resolved_build_dir) if filt is not None: - # Explicit GoogleTest target + # 显式的 GoogleTest 目标 if not exe: errors.append( f"Failed to enumerate GoogleTest cases from '{test_name}': " @@ -208,7 +208,7 @@ def verify_compiled_gtest_coverage( continue exec_filters.setdefault(exe, []).append(filt) elif exe: - # Check if this executable is a GoogleTest binary + # 检查该可执行文件是否为 GoogleTest 二进制 is_gtest = ( is_gtest_fn(exe) if is_gtest_fn is not None @@ -272,7 +272,7 @@ def verify_compiled_gtest_coverage( def run_self_tests(): - # 1. Issue 1: Typed test parsing with "# TypeParam = ..." and comment stripping + # 1. 问题 1:解析带 "# TypeParam = ..." 的类型化测试并去除注释 sample_typed = """ Running main() from /path/to/gtest_main.cc Note: Random seed = 12345. @@ -300,13 +300,13 @@ def run_self_tests(): ] assert parsed == expected, f"Parsed tests mismatch: {parsed} != {expected}" - # Typed tests must not be falsely covered by a predecessor suite filter + # 类型化测试不能被前一个套件的过滤器误判为已覆盖 assert not match_gtest_filter("TypedSuite/0.TypedTestA", "NormalSuite.*") assert match_gtest_filter("TypedSuite/0.TypedTestA", "TypedSuite/*") assert match_gtest_filter("TypedSuite/0.TypedTestA", "TypedSuite/0.*") assert not match_gtest_filter("TypedSuite/0.TypedTestA", "TypedSuite/1.*") - # 2. Issue 2: Empty filter "--gtest_filter=" does not select any test + # 2. 问题 2:空过滤器 "--gtest_filter=" 不选中任何测试 assert not match_gtest_filter("AnySuite.AnyTest", "") assert not match_gtest_filter("AnySuite.AnyTest", " ") assert not match_gtest_filter("AnySuite.AnyTest", '""') @@ -317,18 +317,18 @@ def run_self_tests(): assert is_test_covered("AnySuite.AnyTest", ["", "AnySuite.*"]) assert not is_test_covered("OtherSuite.AnyTest", ["", "AnySuite.*"]) - # Filter case sensitivity + # 过滤器区分大小写 assert not match_gtest_filter("AnySuite.AnyTest", "anysuite.*") assert match_gtest_filter("AnySuite.AnyTest", "AnySuite.*") - # Negative pattern handling + # 反向模式处理 assert match_gtest_filter("Suite.Good", "-Suite.Bad") assert not match_gtest_filter("Suite.Bad", "-Suite.Bad") assert match_gtest_filter("Suite.Good", "Suite.*:-Suite.Bad") assert not match_gtest_filter("Suite.Bad", "Suite.*:-Suite.Bad") assert not match_gtest_filter("Suite.Good", "-*") - # 3. Issue 3: Enumeration failure on explicit GoogleTest target must report errors + # 3. 问题 3:显式 GoogleTest 目标枚举失败时必须报告错误 class FakeProc: def __init__(self, returncode, stdout="", stderr=""): self.returncode = returncode @@ -338,7 +338,7 @@ def __init__(self, returncode, stdout="", stderr=""): fake_build = Path("/fake/build") fake_exe = fake_build / "fake_test_runner" - # 3a. Target with explicit --gtest_filter fails enumeration (exit code) + # 3a. 带显式 --gtest_filter 的目标枚举失败 (退出码) mock_ctest_explicit = { "tests": [ { @@ -360,7 +360,7 @@ def __init__(self, returncode, stdout="", stderr=""): ), errs assert any("exit code 2" in e for e in errs), errs - # 3b. Target returns 0 but produces no test cases + # 3b. 目标返回 0 但没有产出测试用例 errs = [] verify_compiled_gtest_coverage( fake_build, @@ -370,7 +370,7 @@ def __init__(self, returncode, stdout="", stderr=""): ) assert any("no test cases discovered" in e for e in errs), errs - # 3c. Target execution raises an exception + # 3c. 执行目标时抛异常 errs = [] def crashing_runner(cmd): raise RuntimeError("Subprocess timeout or spawn failure") @@ -382,7 +382,7 @@ def crashing_runner(cmd): ) assert any("Failed to execute 'fake_test_runner --gtest_list_tests'" in e for e in errs), errs - # 3d. Wrapped command resolution with runner_fn (e.g. cmake -E env) + # 3d. 通过 runner_fn 解析被包装的命令 (如 cmake -E env) called_cmds = [] def recording_runner(cmd): called_cmds.append(cmd) @@ -413,7 +413,7 @@ def recording_runner(cmd): assert not errs, errs assert called_cmds == [[str(fake_exe), "--gtest_list_tests"]], called_cmds - # 3e. Explicit GoogleTest target with missing or unresolvable executable reports error + # 3e. 显式 GoogleTest 目标的可执行文件缺失或无法解析时报告错误 errs = [] mock_ctest_missing_exe = { "tests": [ @@ -446,7 +446,7 @@ def recording_runner(cmd): ) assert any("could not resolve executable" in e for e in errs), errs - # 3f. Explicit --gtest_filter= (empty) must fail coverage check if tests exist + # 3f. 存在测试时,显式的 --gtest_filter= (空) 必须使覆盖检查失败 errs = [] mock_ctest_empty_filter = { "tests": [ @@ -466,7 +466,7 @@ def recording_runner(cmd): assert any("tests/RuntimeTests.cmake" in e and "edgeflow_add_runner_test" in e and "CustomNodeCatalogTest" in e for e in errs), errs - # 3g. Typed test omission reproduction (reproducing missed typed suite) + # 3g. 复现类型化测试被遗漏的问题 mock_ctest_typed = { "tests": [ { @@ -496,7 +496,7 @@ def main(): build_dir = args.build_dir if not build_dir.is_dir(): - # Fallback to looking relative to script root if build dir is relative + # 构建目录为相对路径时,回退为相对脚本根目录查找 candidate = Path(__file__).resolve().parents[3] / build_dir if candidate.is_dir(): build_dir = candidate @@ -536,7 +536,7 @@ def main(): if not tooling_tests: errors.append("Expected non-empty tooling test set.") - # Rule 1: static-gate and tooling tests must NOT be in sanitizer-runtime + # 规则 1:静态门禁和工具测试不得属于 sanitizer-runtime for name in static_tests: if name in sanitizer_runtime_tests: errors.append( @@ -548,12 +548,12 @@ def main(): f"Tooling test '{name}' must not have 'sanitizer-runtime' label." ) - # Rule 2: kite-real tests must also carry kite label + # 规则 2:kite-real 测试必须同时带有 kite 标签 for name in kite_real_tests: if name not in kite_tests: errors.append(f"Kite real test '{name}' must also have 'kite' label.") - # Rule 3: Key kite targets must be labeled + # 规则 3:关键的 kite 目标必须带标签 required_kite = { "CatalogContractSsotTest", "ModelBackendDecouplingTest", @@ -573,8 +573,8 @@ def main(): if target in inventory and target not in kite_real_tests: errors.append(f"Expected test '{target}' to be labeled with 'kite-real'.") - # Rule 4: Orchestration runtime coverage must survive CI label filtering. - # Check names explicitly: a non-empty runtime set cannot detect one omission. + # 规则 4:编排层运行时覆盖必须经得起 CI 标签过滤。 + # 显式检查名称:仅检查运行时集合非空无法发现单个遗漏。 required_sanitizer_runtime = {"PipelineStudioTest"} for target in sorted(required_sanitizer_runtime): if target not in inventory: @@ -585,8 +585,8 @@ def main(): "with 'sanitizer-runtime'." ) - # Rule 5: All compiled GoogleTest test cases in binaries executed by CTest - # must be covered by at least one CTest filter. + # 规则 5:CTest 执行的二进制中所有已编译的 GoogleTest 用例 + # 都必须至少被一个 CTest 过滤器覆盖。 covered_gtest_count = verify_compiled_gtest_coverage( build_dir, ctest_data, errors ) diff --git a/tests/contract/architecture/test_third_party_cache_metadata.cmake b/tests/contract/architecture/test_third_party_cache_metadata.cmake index c70be8fd..ebd743b4 100644 --- a/tests/contract/architecture/test_third_party_cache_metadata.cmake +++ b/tests/contract/architecture/test_third_party_cache_metadata.cmake @@ -53,7 +53,7 @@ if(_valid) message(FATAL_ERROR "A modified cache marker must not be accepted") endif() -# Dependency failures must be diagnosed before package loading or network I/O. +# 依赖失败必须在加载包或访问网络之前诊断出来。 function(expect_kite_failure expected) execute_process( COMMAND "${CMAKE_COMMAND}" -DENABLE_KITELLM=ON @@ -87,8 +87,8 @@ expect_whisper_failure("cannot coexist with ENABLE_KITELLM=ON" -DENABLE_LLAMACPP include("${PROJECT_SOURCE_DIR}/tests/contract/architecture/test_llama_cache.cmake") -# Exercise the uncached archive path on the running CMake version. Reusing -# prebuilt libraries alone would not catch unsupported FetchContent options. +# 在当前运行的 CMake 版本上覆盖无缓存的归档路径。 +# 仅复用预构建库无法发现不受支持的 FetchContent 选项。 set(_archive_source "${TEST_ROOT}/archive-source") set(_archive_project "${TEST_ROOT}/archive-project") file(MAKE_DIRECTORY "${_archive_source}" "${_archive_project}") diff --git a/tests/contract/authoring/test_spec_signature_diagnostics.cmake b/tests/contract/authoring/test_spec_signature_diagnostics.cmake index 14302411..d8bcfd9d 100644 --- a/tests/contract/authoring/test_spec_signature_diagnostics.cmake +++ b/tests/contract/authoring/test_spec_signature_diagnostics.cmake @@ -1,4 +1,4 @@ -# Compile author-facing contracts using the target's evaluated include view. +# 使用目标求值后的 include 视图编译面向作者的契约。 include("${LAYER_COMPILE_MANIFEST}") set(compile_flags ${layer_cxx_flags}) foreach(directory IN LISTS capability_nodes_includes) @@ -48,8 +48,8 @@ foreach(fixture IN LISTS valid_fixtures invalid_fixtures) endif() endforeach() -# Join adjacent C++ string literals before extracting the canonical signatures. -# The script carries no duplicate signature list of its own. +# 提取规范签名前先拼接相邻的 C++ 字符串字面量。 +# 本脚本自身不维护重复的签名列表。 file(READ "${FUNCTION_NODE_HEADER}" contract_text) string(REGEX REPLACE "\"[ \t\r\n]+\"" "" contract_text "${contract_text}") string(REGEX MATCHALL diff --git a/tests/contract/catalog/test_catalog_contract_ssot.cpp b/tests/contract/catalog/test_catalog_contract_ssot.cpp index e5c997de..a7774f1b 100644 --- a/tests/contract/catalog/test_catalog_contract_ssot.cpp +++ b/tests/contract/catalog/test_catalog_contract_ssot.cpp @@ -26,7 +26,7 @@ TEST_F(CatalogContractSsotTest, AllProductionNodesHaveValidDefinitions) { const auto nodes = PipelineCatalog::Nodes(); EXPECT_GE(nodes.size(), 11U); - // R1: NodeRegistry::ListDefinitions() equals PipelineCatalog::Nodes() + // R1:NodeRegistry::ListDefinitions() 等于 PipelineCatalog::Nodes() const auto reg_defs = NodeRegistry::Instance().ListDefinitions(); EXPECT_EQ(reg_defs.size(), nodes.size()); EXPECT_TRUE( diff --git a/tests/contract/catalog/test_registry_conflict.cpp b/tests/contract/catalog/test_registry_conflict.cpp index 4743525d..31abb8d8 100644 --- a/tests/contract/catalog/test_registry_conflict.cpp +++ b/tests/contract/catalog/test_registry_conflict.cpp @@ -57,8 +57,8 @@ struct BadAuthoringParameters { int count = 0; }; -// This declaration executes before main. Each CTest selects a separate process -// so an authoring failure cannot contaminate the existing registry scenarios. +// 该声明在 main 之前执行。每个 CTest 选择独立进程, +// 因此编写期失败不会污染现有的注册表场景。 const bool kAuthoringStartupAttempted = [] { const char* selected = std::getenv("EDGEFLOW_BAD_AUTHORING_CASE"); if (!selected) return false; diff --git a/tests/integration/demo/test_demo_runner.cpp b/tests/integration/demo/test_demo_runner.cpp index d36c3732..87b8081b 100644 --- a/tests/integration/demo/test_demo_runner.cpp +++ b/tests/integration/demo/test_demo_runner.cpp @@ -147,7 +147,7 @@ TEST(DemoRunnerTest, RealKiteEntityExtractionThroughOperator) { } ASSERT_TRUE(std::filesystem::is_regular_file(model_path)); KiteDemoDirectory temporary; - // Keep the artifact inside the deployment root without a symlink escape. + // 产物保留在部署根目录内,不能通过符号链接逃逸。 std::error_code ec; std::filesystem::create_hard_link(std::filesystem::absolute(model_path), temporary.path / "model.gguf", ec); @@ -297,7 +297,7 @@ TEST(DemoRunnerTest, ConfigAloneResolvesRegisteredRunner) { ASSERT_NE(DemoRegistry::Instance().Find(options.biz), nullptr); } -// CLI errors retain exit code 2. +// CLI 错误保持退出码 2。 TEST(DemoRunnerTest, CommandLineParsingErrors) { DemoOptions opts; std::string err; @@ -360,7 +360,7 @@ TEST(DemoRunnerTest, ComputePlatformWhitelistValidation) { EXPECT_EQ(type, ComputePlatform::kUnknown); } -// 3. Profile loading and remaining CLI overrides. +// 3. Profile 加载及其余 CLI 覆盖。 TEST(DemoRunnerTest, ProfileLoadAndMerge) { DemoOptions cli_opts; cli_opts.profile = "entity_extract_mock"; @@ -572,7 +572,7 @@ TEST(DemoRunnerTest, RegistryLookupAndConflictDetection) { } } restore{reg.ListDescriptors()}; - // Demo dispatch uses the Pipeline business identity directly. + // Demo 分派直接使用 Pipeline 的业务标识。 for (const char* biz : {"entity_extract", "keyword_match", "doc_qa", "dialogue_audit", "ocr_invoice_qa", "audio_asr_intent", "cross_rerank", "translate"}) { @@ -582,7 +582,7 @@ TEST(DemoRunnerTest, RegistryLookupAndConflictDetection) { EXPECT_EQ(descriptor->biz_name, biz); EXPECT_NE(descriptor->run, nullptr); } - // Retired identifiers are not kept as aliases. + // 已退役的标识符不保留为别名。 for (const char* retired : {"entity_extract_v1", "keyword_match_v1", "smart_doc_qa_v1", "dialogue_compliance_audit_v1", "multimodal_ocr_invoice_qa", @@ -625,7 +625,7 @@ TEST(DemoRunnerTest, ASSERT_EQ(LoadAndMergeProfiles("demo/profiles.json", cli, &options, &error), 0) << error; - options.batch_size = 2; // Exercise multi-request custom-node execution. + options.batch_size = 2; // 覆盖多请求的自定义 Node 执行。 std::string resolution_error; ASSERT_TRUE(ResolveConfigBiz(&options, &resolution_error)) << resolution_error; @@ -789,7 +789,7 @@ TEST(DemoRunnerTest, ResultWriterAtomicOutputAndCumulativeAppend) { } } -// Native resolution supplies the business identity and clears stale results. +// 原生解析提供业务标识,并清除过期结果。 TEST(DemoRunnerTest, ConfigBizResolution) { std::string error; for (const auto& entry : std::vector>{ @@ -892,7 +892,7 @@ TEST(DemoRunnerTest, GenericControlCommandChangesCustomNodeOutput) { EXPECT_NE(record.dump().find("PREFIX_APPLIED"), std::string::npos); options.control_file.reset(); EXPECT_EQ(demo->run(options), - 3); // Never substitute a default rules payload. + 3); // 绝不替换为默认规则 payload。 options.control_file = (temporary.path / "control.json").string(); options.control_cmd = 19999; EXPECT_EQ(demo->run(options), 5); @@ -1057,7 +1057,7 @@ TEST(DemoRunnerTest, OcrDemoAppliesExplicitControlBeforeProcessing) { const auto control_path = temporary.path / "control.json"; std::ofstream(control_path) << R"({"template":"提取实体:{{primary}}"})"; options.control_file = control_path.string(); - // OCR defaults to its registered prompt update command when no ID is given. + // 未指定 ID 时,OCR 默认使用其注册的 prompt 更新命令。 ASSERT_EQ(demo->run(options), 0); const auto updated = read_sample(); EXPECT_EQ(updated["status"], 0); diff --git a/tests/integration/operator/test_operator_api.cpp b/tests/integration/operator/test_operator_api.cpp index 50d2e322..41ae3123 100644 --- a/tests/integration/operator/test_operator_api.cpp +++ b/tests/integration/operator/test_operator_api.cpp @@ -73,7 +73,7 @@ class OperatorApiTest : public ::testing::Test { CreateParam DefaultCreateParam(const char* config_file) const { CreateParam param{}; param.model_path = config_root_.c_str(); - // Callers below use string literals; the root is owned by this fixture. + // 下方调用方使用字符串字面量;根目录归本夹具所有。 param.cfg_file_name = config_file; param.device_id = 0; param.compute_platform = ComputePlatform::kAx650; @@ -321,7 +321,7 @@ TEST_F(OperatorApiTest, GenericJsonControlReachesCustomNodeAndReportsFailures) { param.compute_platform = ComputePlatform::kCpu; void* handle = nullptr; ASSERT_EQ(ops_.Create(&handle, ¶m), 0) << GetOperatorLastError(); - // Ensure Destroy also runs if a fatal assertion exits this test early. + // 确保即使致命断言提前退出本测试,也会执行 Destroy。 const auto owner = std::shared_ptr(handle, [this](void* h) { ops_.Destroy(h); }); const auto check = [&](int expected_hit) { @@ -343,10 +343,10 @@ TEST_F(OperatorApiTest, GenericJsonControlReachesCustomNodeAndReportsFailures) { std::string payload = R"({"prefix":"VIP:"})"; ControlJsonParam command{2000000041, payload.c_str()}; ASSERT_EQ(ops_.Control(handle, ControlCommand::kJson, &command), 0); - payload.assign(payload.size(), 'x'); // Caller memory is no longer needed. + payload.assign(payload.size(), 'x'); // 调用方内存已不再需要。 check(1); - // Different command IDs update their own Nodes in the same Pipeline. + // 不同的命令 ID 更新同一 Pipeline 中各自的 Node。 ControlJsonParam rules{ llm_edgeflow::kControlCmdUpdateRules, R"({"categories":{"AFTER_RULE_UPDATE":["NEW:sample"]}})"}; @@ -355,10 +355,10 @@ TEST_F(OperatorApiTest, GenericJsonControlReachesCustomNodeAndReportsFailures) { command.json_param_str = R"({"$edgeflow_control":1,"node_id":"prefix","payload":{"prefix":"NEW:"}})"; ASSERT_EQ(ops_.Control(handle, ControlCommand::kJson, &command), 0); - check(1); // Updating the prefix preserves the matcher's new rules. + check(1); // 更新 prefix 会保留匹配器的新规则。 - // Rejected requests are invalid parameters; the internal Core code stays in - // the diagnostic instead of colliding with the invalid-handle code. + // 被拒绝的请求属于非法参数;内部 Core 错误码保留在诊断信息中, + // 不会与无效句柄错误码冲突。 command.json_param_str = R"({"$edgeflow_control":1,"node_id":"missing","payload":{"prefix":"BAD:"}})"; EXPECT_EQ(ops_.Control(handle, ControlCommand::kJson, &command), @@ -388,7 +388,7 @@ TEST_F(OperatorApiTest, GenericJsonControlReachesCustomNodeAndReportsFailures) { std::string::npos); check(1); - // A Node that rejects a well-formed payload reports an execution failure. + // Node 拒绝格式正确的 payload 时报告执行失败。 ControlJsonParam bad_regex{ llm_edgeflow::kControlCmdUpdateRules, R"({"rules":[{"pattern":"(","strategy":"regex"}]})"}; @@ -1042,24 +1042,24 @@ TEST_F(OperatorApiTest, CompanyBufferAndAnyValidation) { InputLimits limits; std::string err; - // CompanyBuffer: null pointer + // CompanyBuffer:空指针 EXPECT_EQ(buf_binding->validate_external(nullptr, limits, &err), -3); - // CompanyBuffer: negative length + // CompanyBuffer:负长度 uint8_t dummy_data[] = {0x01, 0x02, 0x03}; CompanyBuffer buf_neg{-1, dummy_data}; EXPECT_EQ(buf_binding->validate_external(&buf_neg, limits, &err), -3); - // CompanyBuffer: length > max + // CompanyBuffer:length > max CompanyBuffer buf_toolarge{static_cast(limits.max_buffer_bytes + 1), dummy_data}; EXPECT_EQ(buf_binding->validate_external(&buf_toolarge, limits, &err), -3); - // CompanyBuffer: length > 0 with null data + // CompanyBuffer:length > 0 但 data 为空 CompanyBuffer buf_nulldata{10, nullptr}; EXPECT_EQ(buf_binding->validate_external(&buf_nulldata, limits, &err), -3); - // CompanyBuffer: valid binary + // CompanyBuffer:合法二进制数据 CompanyBuffer buf_valid{3, dummy_data}; EXPECT_EQ(buf_binding->validate_external(&buf_valid, limits, &err), 0); @@ -1069,14 +1069,14 @@ TEST_F(OperatorApiTest, CompanyBufferAndAnyValidation) { ASSERT_NE(any_binding, nullptr); ASSERT_TRUE(any_binding->validate_external); - // CompanyAny: null pointer + // CompanyAny:空指针 EXPECT_EQ(any_binding->validate_external(nullptr, limits, &err), -3); - // CompanyAny: negative count / length + // CompanyAny:负的 count / length CompanyAny any_neg{1, -1, 10, dummy_data}; EXPECT_EQ(any_binding->validate_external(&any_neg, limits, &err), -3); - // CompanyAny: byte_length > max + // CompanyAny:byte_length > max CompanyAny any_toolarge{1, 10, static_cast(limits.max_any_bytes + 1), dummy_data}; EXPECT_EQ(any_binding->validate_external(&any_toolarge, limits, &err), -3); @@ -1110,17 +1110,16 @@ TEST_F(OperatorApiTest, InputSharedPtrUseCountNotRetained) { in_b[0]["chan.keyword_in"] = in_ptr; out_b[0]["chan.keyword_out"] = std::shared_ptr(); - // Before process: in_ptr is held by in_ptr and in_b[0] (use_count == 2) + // process 前:in_ptr 由 in_ptr 和 in_b[0] 持有 (use_count == 2) EXPECT_EQ(in_ptr.use_count(), 2); ASSERT_EQ(ops_.Process(handle, in_b, out_b), 0); - // After process: in_ptr is still held only by in_ptr and in_b[0] (use_count - // == 2) + // process 后:in_ptr 仍只由 in_ptr 和 in_b[0] 持有 (use_count == 2) EXPECT_EQ(in_ptr.use_count(), 2); in_b.clear(); - // Now only in_ptr holds it (use_count == 1) + // 现在只有 in_ptr 持有 (use_count == 1) EXPECT_EQ(in_ptr.use_count(), 1); out_b.clear(); @@ -1318,7 +1317,7 @@ TEST_F(OperatorApiTest, OutputsConfigValidationFailClosed) { .find("Unknown field at /deployment/io/mem_que"), std::string::npos); - // 1. Missing out_mem uses the registered required output defaults. + // 1. 缺少 out_mem 时使用注册的必需输出默认值。 { std::ifstream json_in(std::filesystem::path(GetConfDir()) / "configs/pipeline_keyword_match_rules.json"); @@ -1332,7 +1331,7 @@ TEST_F(OperatorApiTest, OutputsConfigValidationFailClosed) { EXPECT_EQ(ops_.Destroy(handle), 0); handle = nullptr; - // 2. Removed output allocation type is rejected even when it matches. + // 2. 已移除的输出分配 type 字段即使匹配也会被拒绝。 { std::ifstream json_in(std::filesystem::path(GetConfDir()) / "configs/pipeline_keyword_match_rules.json"); @@ -1412,7 +1411,7 @@ TEST_F(OperatorApiTest, OutputsConfigValidationFailClosed) { .find("Unknown field at /deployment/model_path"), std::string::npos); - // 6b. Removed model_paths is rejected even when empty or redundant. + // 6b. 已移除的 model_paths 即使为空或冗余也会被拒绝。 for (const auto& legacy_value : nlohmann::json::array({nlohmann::json::object(), {{"unused_model", "models/unused.bin"}}})) { @@ -1543,7 +1542,7 @@ TEST_F(OperatorApiTest, PathSandboxStrictBoundaries) { // 8. 对 Create 接口同样严格拦截非普通文件与不存在文件 CreateParam bad_param{}; bad_param.model_path = root_dir.c_str(); - bad_param.cfg_file_name = "configs"; // Directory + bad_param.cfg_file_name = "configs"; // 目录 void* handle = nullptr; EXPECT_EQ(ops_.Create(&handle, &bad_param), -2); EXPECT_EQ(handle, nullptr); @@ -2091,9 +2090,8 @@ TEST_F(OperatorApiTest, ModelPathNonExistentFileAllowedWhileEscapeRejected) { EXPECT_FALSE(std::filesystem::exists(resolved_model)); } - // Current model resolution accepts canonical absolute paths within the root, - // and rejects relative traversal or symlink escape even for missing - // artifacts. + // 当前的模型解析接受根目录内的规范绝对路径;即使文件不存在, + // 也拒绝相对路径遍历和符号链接逃逸。 { auto resolve = [&](const std::string& reference, nlohmann::json* resolved) { return llm_edgeflow::ResolveDeploymentModelPaths( @@ -2256,8 +2254,7 @@ TEST_F(OperatorApiTest, VariableResultsUsePoolCapacityAndRollbackOnFailure) { EXPECT_EQ(outputs[0]["chan.keyword_out"], nullptr); } } - // Failed conversion must return the sole lease so a later small result - // works. + // 转换失败时必须归还唯一的租约,以便之后的小结果能正常写入。 std::string short_word = "no match"; text = {static_cast(short_word.size()), short_word.data()}; EXPECT_EQ(ops_.Process(handle, inputs, outputs), 0) @@ -2276,7 +2273,7 @@ TEST_F(OperatorApiTest, MetadataTypeIdOutOfInt32RangeIsRejected) { "configs/pipeline_keyword_match_rules.json", root / "configs/pipeline_keyword_match_rules.json"); - // 1. Unsigned integer > INT32_MAX + // 1. 无符号整数 > INT32_MAX { std::ifstream json_in(root / "configs/pipeline_keyword_match_rules.json"); nlohmann::json pipe_json; @@ -2299,7 +2296,7 @@ TEST_F(OperatorApiTest, MetadataTypeIdOutOfInt32RangeIsRejected) { EXPECT_NE(err.find("exceeds int32 range"), std::string::npos); } - // 2. Negative integer < INT32_MIN + // 2. 负整数 < INT32_MIN { std::ifstream json_in(root / "configs/pipeline_keyword_match_rules.json"); nlohmann::json pipe_json; @@ -2322,7 +2319,7 @@ TEST_F(OperatorApiTest, MetadataTypeIdOutOfInt32RangeIsRejected) { EXPECT_NE(err.find("exceeds int32 range"), std::string::npos); } - // 3. Non-integer (floating point or string) + // 3. 非整数 (浮点数或字符串) { std::ifstream json_in(root / "configs/pipeline_keyword_match_rules.json"); nlohmann::json pipe_json; @@ -2553,7 +2550,7 @@ TEST_F(OperatorApiTest, ProcessRejectsConverterRecordingWrongRequestIdCount) { if (ret == 0) options.request_ids->resize(1); return ret; }; - // The registries are process-global; register once so the test can repeat. + // 注册表是进程级全局的;只注册一次,以便测试可重复运行。 if (!IoConverterRegistry::Instance().FindInputConverter(input.converter_id)) ASSERT_TRUE(IoConverterRegistry::Instance().RegisterInputConverter(input)); const auto* nested = IoBindingRegistry::Instance().FindBinding( @@ -2597,7 +2594,7 @@ TEST_F(OperatorApiTest, ProcessRejectsConverterRecordingWrongRequestIdCount) { EXPECT_EQ(frame.at("test.result"), nullptr); EXPECT_EQ(frame.at("test.audit"), nullptr); } - // Returning an acquired block resets it, even when no output was published. + // 归还已取出的块会将其重置,即使没有发布任何输出。 EXPECT_EQ(nested_resets, resets_before); } @@ -2650,8 +2647,7 @@ TEST_F(OperatorApiTest, EXPECT_EQ(ops_.Destroy(ptr), 0) << GetOperatorLastError(); }); } - // Two handles, two output slots each, and two single-object allocations per - // pool. + // 两个句柄,每个两个输出槽位,每个池两次单对象分配。 ASSERT_EQ(nested_allocations - allocations_before, 8); std::string text = "初始化"; CompanyString sentence{static_cast(text.size()), text.data()}; @@ -2688,7 +2684,7 @@ TEST_F(OperatorApiTest, previous[i][0] = outputs[i].at("chan.result").get(); previous[i][1] = outputs[i].at("chan.audit").get(); } - // Return in frame order so each independent pool reuses its FIFO order. + // 按帧顺序归还,使每个独立的池复用其 FIFO 顺序。 for (auto& output : outputs) output.clear(); } } @@ -2936,7 +2932,7 @@ TEST_F(OperatorApiTest, SharedCarrierDoesNotMergePayloadSchema) { ASSERT_EQ(ops_.Create(&entity_handle, ¶m), 0); ASSERT_NE(entity_handle, nullptr); - // 1. Plain text: valid for Entity Extract, but invalid for Translate schema + // 1. 纯文本:对实体抽取合法,但不符合翻译的 schema std::string plain_text = "普通中文句子非JSON格式"; CompanyString cs_plain{static_cast(plain_text.size()), const_cast(plain_text.data())}; @@ -2946,12 +2942,11 @@ TEST_F(OperatorApiTest, SharedCarrierDoesNotMergePayloadSchema) { in_b[0]["nlp.entity_in"] = MakeBorrowedOperatorInput(&in_plain); out_b[0]["nlp.entity_out"] = std::shared_ptr(); - // Entity Extract accepts plain text + // 实体抽取接受纯文本 EXPECT_EQ(ops_.Process(entity_handle, in_b, out_b), 0); out_b.clear(); - // Translate adapter rejects plain text because it requires JSON object with - // "query" + // 翻译 Adapter 要求带 "query" 的 JSON 对象,因此拒绝纯文本 const auto* translate_in_conv = llm_edgeflow::IoConverterRegistry::Instance().FindInputConverter( "translate.json.operator.v1"); @@ -2975,7 +2970,7 @@ TEST_F(OperatorApiTest, SharedCarrierDoesNotMergePayloadSchema) { &ctx, &status), COMPANY_ALG_ERR_INVALID_INPUT); - // 2. JSON text: Translate accepts and extracts "query" + // 2. JSON 文本:翻译接受并提取 "query" std::string json_text = "{\"query\":\"有效翻译查询\"}"; CompanyString cs_json{static_cast(json_text.size()), const_cast(json_text.data())}; diff --git a/tests/integration/operator/test_operator_golden.cpp b/tests/integration/operator/test_operator_golden.cpp index 0e14abe7..89330695 100644 --- a/tests/integration/operator/test_operator_golden.cpp +++ b/tests/integration/operator/test_operator_golden.cpp @@ -117,7 +117,6 @@ TEST_F(OperatorGoldenTest, HandlesCloseIndependently) { EXPECT_EQ(second.Close(), 0) << second.close_diagnostic(); } -// Golden Test 1: DocQA (Biz 1) TEST_F(OperatorGoldenTest, DocQaGolden) { using namespace llm_edgeflow::operator_api; ScopedTestOperator instance(ops_); @@ -164,7 +163,6 @@ TEST_F(OperatorGoldenTest, DocQaGolden) { EXPECT_EQ(instance.Close(), 0) << instance.close_diagnostic(); } -// Golden Test 2: KeywordMatch (Biz 2) TEST_F(OperatorGoldenTest, KeywordMatchGolden) { using namespace llm_edgeflow::operator_api; ScopedTestOperator instance(ops_); @@ -210,7 +208,6 @@ TEST_F(OperatorGoldenTest, KeywordMatchGolden) { EXPECT_EQ(instance.Close(), 0) << instance.close_diagnostic(); } -// Golden Test 3: EntityExtract (Biz 3) TEST_F(OperatorGoldenTest, EntityExtractGolden) { using namespace llm_edgeflow::operator_api; ScopedTestOperator instance(ops_); @@ -248,7 +245,6 @@ TEST_F(OperatorGoldenTest, EntityExtractGolden) { EXPECT_EQ(instance.Close(), 0) << instance.close_diagnostic(); } -// Golden Test 4: DialogueAudit (Biz 4) TEST_F(OperatorGoldenTest, DialogueAuditGolden) { using namespace llm_edgeflow::operator_api; ScopedTestOperator instance(ops_); @@ -293,7 +289,6 @@ TEST_F(OperatorGoldenTest, DialogueAuditGolden) { EXPECT_EQ(instance.Close(), 0) << instance.close_diagnostic(); } -// Golden Test 5: OcrInvoiceQa (Biz 5) TEST_F(OperatorGoldenTest, OcrInvoiceQaGolden) { using namespace llm_edgeflow::operator_api; ScopedTestOperator instance(ops_); @@ -334,8 +329,6 @@ TEST_F(OperatorGoldenTest, OcrInvoiceQaGolden) { EXPECT_EQ(instance.Close(), 0) << instance.close_diagnostic(); } -// Golden Test 6: AudioAsrIntent (Biz 6) with Slot Extraction Exact Golden -// Verification TEST_F(OperatorGoldenTest, AudioAsrIntentSlotExtractionGolden) { using namespace llm_edgeflow::operator_api; ScopedTestOperator instance(ops_); @@ -344,11 +337,11 @@ TEST_F(OperatorGoldenTest, AudioAsrIntentSlotExtractionGolden) { << instance.create_diagnostic(); ASSERT_NE(instance.get(), nullptr); - // Sample 1: Navigation with avoid traffic (sum > 120 in mock ASR) + // 样本 1:导航并避开拥堵 (mock ASR 中 sum > 120) std::vector pcm_nav(16000, 0.05f); // sum = 800 > 120 - // Sample 2: HVAC temp and fan speed (sum <= 40 in mock ASR) + // 样本 2:空调温度和风速 (mock ASR 中 sum <= 40) std::vector pcm_hvac(16000, 0.001f); // sum = 16 <= 40 - // Sample 3: Unmatched general voice command (sum = 80, > 40 and <= 120) + // 样本 3:未匹配的通用语音指令 (sum = 80,> 40 且 <= 120) std::vector pcm_gen(16000, 0.005f); // sum = 80 CompanyOperatorAudioInput in1{6001, pcm_nav.data(), @@ -372,7 +365,7 @@ TEST_F(OperatorGoldenTest, AudioAsrIntentSlotExtractionGolden) { ASSERT_EQ(p_ret, 0) << "Process error: " << llm_edgeflow::operator_api::GetOperatorLastError(); - // Verify Sample 1: Navigation + // 校验样本 1:导航 auto out_sp1 = outputs[0]["mic_0.audio_out"]; ASSERT_NE(out_sp1, nullptr); auto* out_dto1 = static_cast(out_sp1.get()); @@ -393,7 +386,7 @@ TEST_F(OperatorGoldenTest, AudioAsrIntentSlotExtractionGolden) { EXPECT_EQ(j1["slots"]["avoid_traffic"], true); EXPECT_EQ(j1["slots"]["avoid_toll"], false); - // Verify Sample 2: HVAC Control + // 校验样本 2:空调控制 auto out_sp2 = outputs[1]["mic_0.audio_out"]; ASSERT_NE(out_sp2, nullptr); auto* out_dto2 = static_cast(out_sp2.get()); @@ -413,7 +406,7 @@ TEST_F(OperatorGoldenTest, AudioAsrIntentSlotExtractionGolden) { EXPECT_EQ(j2["slots"]["temperature"], 24); EXPECT_EQ(j2["slots"]["fan_speed"], 2); - // Verify Sample 3: General Voice Command Fallback + // 校验样本 3:通用语音指令回退 auto out_sp3 = outputs[2]["mic_0.audio_out"]; ASSERT_NE(out_sp3, nullptr); auto* out_dto3 = static_cast(out_sp3.get()); @@ -439,7 +432,6 @@ TEST_F(OperatorGoldenTest, AudioAsrIntentSlotExtractionGolden) { EXPECT_EQ(instance.Close(), 0) << instance.close_diagnostic(); } -// Golden Test 7: CrossRerank (Biz 7) TEST_F(OperatorGoldenTest, CrossRerankGolden) { if (!BackendRegistry::Instance().Find("onnxruntime").has_value()) { GTEST_SKIP() << "ONNX Runtime backend disabled in this build"; @@ -520,7 +512,6 @@ TEST_F(OperatorGoldenTest, CrossRerankGolden) { std::filesystem::remove_all(temp_dir, ec); } -// Golden Test 8: Translate (Biz 8) TEST_F(OperatorGoldenTest, TranslateGolden) { using namespace llm_edgeflow::operator_api; ScopedTestOperator instance(ops_); diff --git a/tests/integration/pipeline/test_model_backend_pipeline.cpp b/tests/integration/pipeline/test_model_backend_pipeline.cpp index 906692fd..6c21e034 100644 --- a/tests/integration/pipeline/test_model_backend_pipeline.cpp +++ b/tests/integration/pipeline/test_model_backend_pipeline.cpp @@ -21,14 +21,14 @@ namespace llm_edgeflow { namespace test_mb { -// Counters to verify zero side-effects in validator +// 用于验证 Validator 无副作用的计数器 static std::atomic g_backend_create_count{0}; static std::atomic g_backend_load_count{0}; static std::atomic g_model_create_count{0}; static BackendLoadSpec g_last_backend_load_spec{ ExecutionProtocol::kTensorGraph}; -// Mock Backend Session +// Mock Backend 会话 class MockBackendSession : public IBackendSession { public: MockBackendSession(std::string backend_type, ExecutionProtocol protocol, @@ -126,7 +126,7 @@ class MockEmbeddingModel : public IModel { nlohmann::json model_config_; }; -// Mock Model Bound Node +// 绑定 Mock Model 的 Node class MockEmbeddingConsumerNode : public INode { public: inline static constexpr char kNodeType[] = "MockEmbeddingConsumerNode"; @@ -225,7 +225,7 @@ class ModelBackendPipelineTest : public ::testing::Test { void TearDown() override {} }; -// 1. ValidateAndNormalizeConfig Unit Tests +// 1. ValidateAndNormalizeConfig 单元测试 TEST_F(ModelBackendPipelineTest, ValidateAndNormalizeConfigSuccessAndDefaults) { std::vector schema = { {"str_field", ConfigValueKind::kString, false, "default_str"}, @@ -258,7 +258,7 @@ TEST_F(ModelBackendPipelineTest, ValidateAndNormalizeConfigSuccessAndDefaults) { EXPECT_EQ(normalized["bool_field"], true); EXPECT_EQ(normalized["enum_field"], "opt_a"); - // Verify input was not modified + // 验证输入未被修改 EXPECT_FALSE(input.contains("str_field")); } @@ -292,7 +292,7 @@ TEST_F(ModelBackendPipelineTest, ValidateAndNormalizeConfigBoundsAndEnum) { {"opt_a", "opt_b"}}, }; - // Below min + // 低于最小值 { nlohmann::json input = {{"int_field", 2}}; std::vector diags; @@ -302,7 +302,7 @@ TEST_F(ModelBackendPipelineTest, ValidateAndNormalizeConfigBoundsAndEnum) { EXPECT_EQ(diags[0].code, DiagnosticCode::kConfigFieldRange); } - // Above max + // 高于最大值 { nlohmann::json input = {{"int_field", 50}}; std::vector diags; @@ -312,7 +312,7 @@ TEST_F(ModelBackendPipelineTest, ValidateAndNormalizeConfigBoundsAndEnum) { EXPECT_EQ(diags[0].code, DiagnosticCode::kConfigFieldRange); } - // Invalid enum + // 非法 enum { nlohmann::json input = {{"enum_field", "opt_c"}}; std::vector diags; @@ -323,7 +323,7 @@ TEST_F(ModelBackendPipelineTest, ValidateAndNormalizeConfigBoundsAndEnum) { } } -// 2. PipelineValidator and ValidatedModelPlan Tests +// 2. PipelineValidator 与 ValidatedModelPlan 测试 TEST_F(ModelBackendPipelineTest, ValidatorInfersModelCapabilityWithZeroSideEffects) { nlohmann::json cfg = { @@ -360,14 +360,13 @@ TEST_F(ModelBackendPipelineTest, EXPECT_EQ(mp.protocol, ExecutionProtocol::kTensorGraph); EXPECT_EQ(mp.effective_concurrency, InferenceConcurrency::kConcurrent); - // Check normalized configs with injected defaults + // 检查注入默认值后的归一化配置 EXPECT_EQ(mp.normalized_model_config["max_length"], 256); - EXPECT_EQ(mp.normalized_model_config["normalize"], true); // injected default + EXPECT_EQ(mp.normalized_model_config["normalize"], true); // 注入的默认值 EXPECT_EQ(mp.normalized_backend_config["device"], "cpu"); - EXPECT_EQ(mp.normalized_backend_config["threads"], 4); // injected default + EXPECT_EQ(mp.normalized_backend_config["threads"], 4); // 注入的默认值 - // CRITICAL INVARIANT: Validator must NOT call backend create, load, or model - // create! + // 关键不变量:Validator 不得创建或加载 Backend,也不得创建 Model! EXPECT_EQ(g_backend_create_count.load(), 0); EXPECT_EQ(g_backend_load_count.load(), 0); EXPECT_EQ(g_model_create_count.load(), 0); @@ -441,7 +440,7 @@ TEST_F(ModelBackendPipelineTest, } TEST_F(ModelBackendPipelineTest, ValidatorRejectsProtocolMismatch) { - // Register a backend that only supports text generation. + // 注册一个只支持文本生成的 Backend。 BackendDefinition bdef; bdef.backend_type = "text_generation_only_backend"; bdef.supported_protocols = {ExecutionProtocol::kTextGeneration}; @@ -454,13 +453,12 @@ TEST_F(ModelBackendPipelineTest, ValidatorRejectsProtocolMismatch) { nlohmann::json cfg = { {"biz_name", "proto_mismatch_test"}, - {"models", - nlohmann::json::array({{ - {"model_id", "emb_model"}, - {"model_type", "mock_bge_embedding"}, // requires kTensorGraph - {"backend", "text_generation_only_backend"}, - {"model_path", "./model.bin"}, - }})}, + {"models", nlohmann::json::array({{ + {"model_id", "emb_model"}, + {"model_type", "mock_bge_embedding"}, // 需要 kTensorGraph + {"backend", "text_generation_only_backend"}, + {"model_path", "./model.bin"}, + }})}, {"pipeline", nlohmann::json::array({{ {"id", "node1"}, {"node_type", "MockEmbeddingConsumerNode"}, @@ -489,9 +487,8 @@ TEST_F(ModelBackendPipelineTest, ValidatorRejectsRemovedCapabilityField) { {"models", nlohmann::json::array({{ {"model_id", "emb_model"}, - {"capability", - "rerank"}, // Removed field is rejected regardless of value. - {"model_type", "mock_bge_embedding"}, // definition is embedding + {"capability", "rerank"}, // 已移除的字段无论取值都会被拒绝。 + {"model_type", "mock_bge_embedding"}, // Definition 为 embedding {"backend", "mock_test_backend"}, {"model_path", "./model.onnx"}, }})}, @@ -515,7 +512,7 @@ TEST_F(ModelBackendPipelineTest, ValidatorRejectsRemovedCapabilityField) { EXPECT_TRUE(found_removed_field); } -// 3. Pipeline Build and Atomic Materialization Tests +// 3. Pipeline 构建与原子化实例化测试 TEST_F(ModelBackendPipelineTest, PipelineBuildMaterializesAndRegistersModel) { nlohmann::json cfg = { {"biz_name", "pipeline_build_success"}, @@ -541,12 +538,12 @@ TEST_F(ModelBackendPipelineTest, PipelineBuildMaterializesAndRegistersModel) { EXPECT_TRUE(ok) << diag.message; EXPECT_EQ(diag.code, DiagnosticCode::kOk); - // Verify backend and model creation + // 验证 Backend 和 Model 的创建 EXPECT_EQ(g_backend_create_count.load(), 1); EXPECT_EQ(g_backend_load_count.load(), 1); EXPECT_EQ(g_model_create_count.load(), 1); - // Verify model is accessible in session ModelManager + // 验证可通过会话 ModelManager 访问 Model auto model = pipeline.GetSessionContext().GetModelManager().GetModel( "emb_model"); ASSERT_NE(model, nullptr); @@ -556,12 +553,12 @@ TEST_F(ModelBackendPipelineTest, PipelineBuildMaterializesAndRegistersModel) { auto mock_model = std::dynamic_pointer_cast(model); ASSERT_NE(mock_model, nullptr); EXPECT_EQ(mock_model->ModelConfig()["max_length"], 128); - EXPECT_EQ(mock_model->ModelConfig()["normalize"], true); // default injected + EXPECT_EQ(mock_model->ModelConfig()["normalize"], true); // 注入的默认值 } TEST_F(ModelBackendPipelineTest, PipelineBuildAtomicRollbackOnSecondModelFailure) { - // Register second backend that fails to load + // 注册第二个加载会失败的 Backend BackendDefinition bdef_fail; bdef_fail.backend_type = "failing_backend"; bdef_fail.supported_protocols = {ExecutionProtocol::kTensorGraph}; @@ -606,14 +603,12 @@ TEST_F(ModelBackendPipelineTest, EXPECT_FALSE(ok); EXPECT_EQ(diag.code, DiagnosticCode::kModelMaterializationFailed); - // Verify that failing_backend truly attempted to load (preventing false - // positives) + // 验证 failing_backend 确实尝试了加载 (避免误判) EXPECT_EQ(g_backend_create_count.load(), 2); EXPECT_EQ(g_backend_load_count.load(), 2); EXPECT_EQ(g_model_create_count.load(), 1); - // Verify that good_model is NOT registered in ModelManager due to atomic - // staging! + // 验证由于原子暂存,good_model 未注册到 ModelManager! EXPECT_EQ(pipeline.GetSessionContext().GetModelManager().GetModel( "good_model"), nullptr); @@ -747,9 +742,8 @@ TEST_F(ModelBackendPipelineTest, PipelinePassesResolvedPathAndTargetToBackend) { EXPECT_TRUE(ok); EXPECT_EQ(diag.code, DiagnosticCode::kOk); - // Session registration retains model identity metadata, while the - // execution target remains transient and is captured at the Backend load - // boundary below. + // 会话注册保留模型标识元数据,而执行目标是临时的, + // 在下方 Backend 加载边界处捕获。 auto reg = pipeline.GetSessionContext().GetModelManager().GetModelRegistration( "emb_model"); diff --git a/tests/integration/pipeline/test_pipeline_catalog_validator.cpp b/tests/integration/pipeline/test_pipeline_catalog_validator.cpp index 706c00cf..033786aa 100644 --- a/tests/integration/pipeline/test_pipeline_catalog_validator.cpp +++ b/tests/integration/pipeline/test_pipeline_catalog_validator.cpp @@ -531,7 +531,7 @@ TEST(PipelineValidatorTest, ReportsConfigAndCapabilityErrors) { EXPECT_TRUE(codes.count(DiagnosticCode::kConfigFieldRange)); EXPECT_TRUE(codes.count(DiagnosticCode::kModelCapabilityMismatch)); - // Verify external JSON serialization parity + // 验证外部 JSON 序列化一致性 auto json_rep = report.ToJson(); std::set json_codes; for (const auto& item : json_rep["diagnostics"]) { @@ -638,7 +638,7 @@ TEST(PipelineValidatorTest, TableDrivenParityMatrix) { SCOPED_TRACE(test["name"].get()); const auto& config = test["pipeline"]; - // 1. Validator is the complete structured-report baseline. + // 1. Validator 是完整结构化报告的基准。 auto plan = PipelineValidator::ValidateAndPlan(config); EXPECT_FALSE(plan.report.ok); ASSERT_FALSE(plan.report.diagnostics.empty()); @@ -653,7 +653,7 @@ TEST(PipelineValidatorTest, TableDrivenParityMatrix) { << "Missing required diagnostic " << required_code; } - // 2. Pipeline maps the first Validator diagnostic without recomputing it. + // 2. Pipeline 直接映射 Validator 的第一条诊断,不重新计算。 Pipeline pipeline; PipelineDiagnostic pipe_diag; bool built = BuildTestPipeline(pipeline, config, &pipe_diag); @@ -664,8 +664,8 @@ TEST(PipelineValidatorTest, TableDrivenParityMatrix) { EXPECT_EQ(pipe_diag.path, test["primary_path"].get()); EXPECT_EQ(pipe_diag.message, primary["message"].get()); - // 3. The shared runtime must fail before materialization and preserve the - // primary structured diagnostic in its internal C++ error boundary. + // 3. 共享运行时必须在实例化前失败,并在其内部 C++ 错误边界中 + // 保留主结构化诊断。 std::string biz = config.value("biz_name", ""); std::string binding_id; for (const auto& b : IoBindingRegistry::Instance().AllBindings()) { @@ -838,7 +838,7 @@ TEST(PipelineValidatorTest, ExplainReturnsCandidateFixForUnknownConfigField) { stream >> root; root = PrepareExternalFixtureForCore(root); - // Misspell "temperature" as "temprature" + // 把 "temperature" 误拼为 "temprature" root["pipeline"][0]["config"]["temprature"] = 0.1; root["pipeline"][0]["config"].erase("temperature"); @@ -864,15 +864,14 @@ TEST(PipelineValidatorTest, ExplainReturnsCandidateFixForUnknownConfigField) { ASSERT_TRUE(rem.facts.contains("candidate_fields")); const auto& candidate_fields = rem.facts["candidate_fields"]; ASSERT_FALSE(candidate_fields.empty()); - // The closest candidate "temperature" (Levenshtein distance 1) should be - // first + // 最接近的候选 "temperature" (Levenshtein 距离为 1) 应排在首位 EXPECT_EQ(candidate_fields[0], "temperature"); ASSERT_FALSE(rem.fixes.empty()); const auto& fix = rem.fixes.front(); EXPECT_EQ(fix.verification, "pipeline_valid"); - // Verify the patch moves the field from "temprature" to "temperature" + // 验证补丁把字段从 "temprature" 移到 "temperature" bool found_move = false; for (const auto& op : fix.patch) { if (op.value("op", "") == "move" && @@ -884,17 +883,16 @@ TEST(PipelineValidatorTest, ExplainReturnsCandidateFixForUnknownConfigField) { } EXPECT_TRUE(found_move); - // Applying patch recovers a fully valid pipeline + // 应用补丁后恢复为完全合法的 Pipeline const auto patched = root.patch(fix.patch); const auto verified_report = ValidateWithRemediation(patched); EXPECT_TRUE(verified_report.ok) << verified_report.ToJson().dump(2); } TEST(PipelineValidatorTest, ExplainRespectsFixBounds) { - // Construct a pipeline with 5 independent upstream nodes and 4 consumer nodes - // having invalid dependencies. Without bounds, each consumer would produce 8 - // candidate fixes, yielding 32 candidates total. - // Explain must cap at max 3 fixes per diagnostic and max 8 fixes per report. + // 构造含 5 个独立上游 Node 和 4 个依赖非法的消费 Node 的 Pipeline。 + // 若不设上限,每个消费者会产生 8 个候选修复,共 32 个。 + // Explain 必须限制每条诊断最多 3 个修复、每份报告最多 8 个修复。 nlohmann::json root = {{"biz_name", "keyword_match"}, {"models", nlohmann::json::array()}, {"pipeline", nlohmann::json::array()}}; @@ -953,14 +951,12 @@ TEST(PipelineValidatorTest, ExplainTargetResolved) { stream >> root; root = PrepareExternalFixtureForCore(root); - // Introduce two independent errors: - // 1. Misspelled config field in custom_prompt ("temprature" instead of - // "temperature") + // 引入两个相互独立的错误: + // 1. custom_prompt 中的配置字段拼写错误 ("temprature" 而非 "temperature") root["pipeline"][0]["config"]["temprature"] = 0.1; root["pipeline"][0]["config"].erase("temperature"); - // 2. Invalid configuration in node_2_StructuredJsonParseNode that will remain - // unresolved + // 2. node_2_StructuredJsonParseNode 中仍未解决的非法配置 root["pipeline"][1]["config"]["field_types"] = { {"field_x", "unsupported_type"}}; @@ -981,9 +977,8 @@ TEST(PipelineValidatorTest, ExplainTargetResolved) { ASSERT_TRUE(typo_diag->remediation.has_value()); ASSERT_FALSE(typo_diag->remediation->fixes.empty()); - // Applying the fix only resolves the unknown config field; the invalid - // field_types error in node 1 persists. Therefore, verification must be - // "target_resolved". + // 应用该修复只解决了未知配置字段,node 1 中非法的 field_types + // 错误仍然存在,因此 verification 必须为 "target_resolved"。 const auto& fix = typo_diag->remediation->fixes.front(); EXPECT_EQ(fix.verification, "target_resolved"); } @@ -996,7 +991,7 @@ TEST(PipelineValidatorTest, ValidateProducesBasicRemediation) { stream >> root; root = PrepareExternalFixtureForCore(root); - // Explicitly connect an input to a key with no producer. + // 显式把输入连接到没有生产者的键。 root["pipeline"][1]["inputs"]["text"] = "missing_result"; const auto report = ValidateWithRemediation(root); @@ -1155,7 +1150,7 @@ TEST(PipelineValidatorTest, SplitItemsFeedItemWiseNodeButNotPerRequestEgress) { const auto accepted = ValidateWithRemediation(root); EXPECT_TRUE(accepted.ok) << accepted.ToJson().dump(); - // Per-chunk matches cannot become the one-row-per-request biz output. + // 逐分块的匹配结果不能作为每请求一行的 biz 输出。 root["pipeline"][1]["outputs"]["matches"] = "rule_matches"; root["pipeline"][2]["outputs"]["matches"] = "request_matches"; const auto rejected = ValidateWithRemediation(root); @@ -1240,7 +1235,7 @@ TEST(PipelineValidatorTest, ExplainReturnsPortFlowMismatchRemediation) { EXPECT_FALSE(report.ok); ASSERT_FALSE(report.diagnostics.empty()); - // Request-scoped chunks cannot back a session-scoped embedding cache. + // 请求作用域的分块不能支撑会话作用域的 embedding 缓存。 const ValidationDiagnostic* target_diag = nullptr; for (const auto& diag : report.diagnostics) { if (diag.code == DiagnosticCode::kPortLifetimeMismatch && diff --git a/tests/integration/runtime/test_runtime_control_and_hot_swap.cpp b/tests/integration/runtime/test_runtime_control_and_hot_swap.cpp index 5727fd4c..5a209f71 100644 --- a/tests/integration/runtime/test_runtime_control_and_hot_swap.cpp +++ b/tests/integration/runtime/test_runtime_control_and_hot_swap.cpp @@ -98,7 +98,7 @@ TEST_F(RuntimeControlAndHotSwapTest, << error; ExpectRuleCategories(&pipeline, "UPDATED_A", "UPDATED_B"); - // Unwrapped payloads retain their existing broadcast behavior. + // 未包装的 payload 保持原有的广播行为。 ASSERT_EQ( pipeline.Control(kControlCmdUpdateRules, R"({"categories":{"BROADCAST":["sample"]}})", &error), @@ -161,7 +161,7 @@ TEST_F(RuntimeControlAndHotSwapTest, &stage), 0); EXPECT_NE(error.find(field), std::string::npos) << error; - // Only the regex compile happens inside the Node; schema checks run first. + // 只有正则编译在 Node 内部进行;schema 检查先执行。 EXPECT_EQ(stage, field == "rules_a" ? ControlFailureStage::kNode : ControlFailureStage::kRequest); ExpectRuleCategories(&pipeline, "INITIAL_A", "INITIAL_B"); diff --git a/tests/support/adapter_examples/tagged_union_adapter.h b/tests/support/adapter_examples/tagged_union_adapter.h index bdb7b504..eac817c0 100644 --- a/tests/support/adapter_examples/tagged_union_adapter.h +++ b/tests/support/adapter_examples/tagged_union_adapter.h @@ -28,7 +28,7 @@ typedef struct { typedef struct { uint64_t request_id; - int payload_type; // 1: Text, 2: Image + int payload_type; // 1:文本,2:图像 union { TemplateTextPayload text; TemplateImagePayload image; diff --git a/tests/support/control_test_utils.h b/tests/support/control_test_utils.h index 8ae6db84..e5e6ec7a 100644 --- a/tests/support/control_test_utils.h +++ b/tests/support/control_test_utils.h @@ -7,7 +7,7 @@ namespace llm_edgeflow::test { -// Uses the compiled authoring starter and the existing keyword I/O contract. +// 使用已编译的编写入门模板和现有的 keyword I/O 契约。 inline void WriteControlTestPipeline(const std::filesystem::path& directory) { const nlohmann::json pipeline = { {"deployment", diff --git a/tests/support/model_registration.h b/tests/support/model_registration.h index babc5887..1c6b18f7 100644 --- a/tests/support/model_registration.h +++ b/tests/support/model_registration.h @@ -8,8 +8,7 @@ namespace llm_edgeflow { -// Registers one test model through the same atomic batch path that Pipeline -// uses for a deployment's models. +// 通过与 Pipeline 注册部署模型相同的原子批处理路径注册一个测试模型。 inline bool RegisterTestModel(ModelManager& manager, std::string model_id, std::shared_ptr model, std::string revision = {}, diff --git a/tests/support/node_process_pause.h b/tests/support/node_process_pause.h index bbee7962..c1d6db0a 100644 --- a/tests/support/node_process_pause.h +++ b/tests/support/node_process_pause.h @@ -7,9 +7,8 @@ namespace llm_edgeflow::test_support { -// A bounded handshake: timeout must fail the caller, never silently allow a -// Process to finish before the publication under test. Always resume and join -// the reader before fatal GoogleTest assertions. +// 有界握手:超时必须使调用方失败,绝不能静默放任 Process 在被测发布之前 +// 完成。在 GoogleTest 致命断言前务必恢复并 join 读线程。 class NodeProcessPause { public: explicit NodeProcessPause( diff --git a/tests/support/node_test_utils.h b/tests/support/node_test_utils.h index 7661f65b..998ddf09 100644 --- a/tests/support/node_test_utils.h +++ b/tests/support/node_test_utils.h @@ -16,8 +16,7 @@ namespace llm_edgeflow { -// The caller keeps the session alive through Node destruction, as in -// production. +// 与生产环境一样,调用方在 Node 析构期间保持会话存活。 struct NodeFixturePlans { std::mutex mutex; std::vector> plans; diff --git a/tests/support/operator_nested_output_fixture.h b/tests/support/operator_nested_output_fixture.h index 190375c9..3e5bb896 100644 --- a/tests/support/operator_nested_output_fixture.h +++ b/tests/support/operator_nested_output_fixture.h @@ -11,7 +11,7 @@ namespace llm_edgeflow::test_support { -// Internal result that the nested-output fixture converts. +// 嵌套输出夹具要转换的内部结果。 struct NestedOutputSource { uint64_t request_id = 0; int is_hit = 0; @@ -23,7 +23,7 @@ struct NestedOutputParameters { bool reject_hit = false; }; -// Neutral test carrier: the same outer type can contain either nested layout. +// 中性的测试载体:同一外层类型可以容纳任一种嵌套布局。 struct NestedOutputPayload { uint32_t capacity = 0; uint32_t count = 0; @@ -137,7 +137,7 @@ inline int ConvertNestedOutput(const void* internal, void* external, if (error) *error = "Allocation, conversion and reset contracts disagree"; return -4; } - // Fail after modifying this slot, so rollback must reset every acquired slot. + // 修改该槽位后才失败,因此回滚必须重置每个已取出的槽位。 root.request_id = result.request_id; payload.count = payload.capacity; if (parameters.reject_hit && result.is_hit) { diff --git a/tests/support/operator_test_fixture.h b/tests/support/operator_test_fixture.h index 2f5596e6..407f7cc8 100644 --- a/tests/support/operator_test_fixture.h +++ b/tests/support/operator_test_fixture.h @@ -24,8 +24,8 @@ class OperatorTestFixture : public ::testing::Test { operator_api::OperatorFunc ops_{}; }; -// Declare before output containers so their leases are returned before cleanup, -// including when a fatal assertion returns early from a test. +// 在输出容器之前声明,使其租约先于清理归还, +// 包括致命断言提前从测试返回的情况。 class ScopedTestOperator { public: explicit ScopedTestOperator(operator_api::OperatorFunc ops) : ops_(ops) {} @@ -60,7 +60,7 @@ class ScopedTestOperator { int Close() { if (!handle_) return 0; - // Destroy consumes a handle even when it reports an error. + // 即使报告错误,Destroy 也会消耗句柄。 void* handle = std::exchange(handle_, nullptr); const int code = ops_.Destroy(handle); close_diagnostic_ = operator_api::GetOperatorLastError(); diff --git a/tests/support/registry_test_access.h b/tests/support/registry_test_access.h index 6797cdc1..d8710632 100644 --- a/tests/support/registry_test_access.h +++ b/tests/support/registry_test_access.h @@ -22,7 +22,7 @@ class RegistryTestAccess { std::memory_order_release); NodeRegistry::Instance().conflict_errors_.clear(); } - // old_entries are destructed outside the lock + // old_entries 在锁外析构 } static void ResetBizs() { PipelineCatalog::ResetBizsForTesting(); } @@ -60,7 +60,7 @@ class RegistryTestAccess { NodeRegistry::Instance().conflict_errors_.swap(saved_conflict_errors_); } catch (...) { } - // old_entries, old_errors destructed outside lock without allocating + // old_entries、old_errors 在锁外析构,且不分配内存 } ScopedNodeState(const ScopedNodeState&) = delete; diff --git a/tests/support/scoped_allocation_failure.cpp b/tests/support/scoped_allocation_failure.cpp index 77c42add..97667aee 100644 --- a/tests/support/scoped_allocation_failure.cpp +++ b/tests/support/scoped_allocation_failure.cpp @@ -40,7 +40,7 @@ ScopedAllocationFailure::~ScopedAllocationFailure() { current_ = previous_; } void ScopedAllocationFailure::BeforeAllocation() { if (!current_ || current_->remaining_ < 0) return; if (current_->remaining_-- == 0) { - // One-shot failure allows diagnostics and exception cleanup to allocate. + // 单次失败允许诊断信息和异常清理继续分配内存。 current_->triggered_ = true; throw std::bad_alloc(); } diff --git a/tests/support/scoped_allocation_failure.h b/tests/support/scoped_allocation_failure.h index 7d20c4c9..ec704bfc 100644 --- a/tests/support/scoped_allocation_failure.h +++ b/tests/support/scoped_allocation_failure.h @@ -5,8 +5,8 @@ namespace llm_edgeflow::test_support { -// One-shot callback on this thread's next replacement-new allocation. Clear -// before invoking so the callback may itself allocate without recursion. +// 本线程下一次替换 new 分配时触发的一次性回调。调用前先清除, +// 使回调自身可以分配内存而不会递归。 class ScopedNextAllocationCallback { public: using Callback = void (*)(void*); @@ -24,12 +24,10 @@ class ScopedNextAllocationCallback { void* user_data_; }; -// Test-executable-only replacement new/delete support. Arm only around a -// synchronous operation, outside GoogleTest assertions. For leak assertions, -// destroy tracked allocations on this thread before checking Outstanding(). -// Successful operations may transfer ownership beyond the scope; those -// surviving allocations are no longer tracked. Nested scopes restore the outer -// injection state; other threads are unaffected. +// 仅供测试可执行文件使用的替换 new/delete 支持。只在同步操作期间启用, +// 且不要包住 GoogleTest 断言。做泄漏断言时,须先在本线程销毁被跟踪的分配, +// 再检查 Outstanding()。成功的操作可能把所有权转移到作用域之外,这些存活的 +// 分配不再被跟踪。嵌套作用域会恢复外层的注入状态;其他线程不受影响。 class ScopedAllocationFailure { public: explicit ScopedAllocationFailure(std::ptrdiff_t fail_after = -1) noexcept; @@ -42,7 +40,7 @@ class ScopedAllocationFailure { size_t Outstanding() const noexcept { return count_; } bool Overflowed() const noexcept { return overflowed_; } - // Used exclusively by the replacement allocation functions in the .cpp. + // 仅供 .cpp 中的替换分配函数使用。 static void BeforeAllocation(); static void RecordAllocation(void* ptr) noexcept; static void RecordDeallocation(void* ptr) noexcept; @@ -54,8 +52,8 @@ class ScopedAllocationFailure { bool triggered_ = false; bool overflowed_ = false; size_t count_ = 0; - // A bounded ledger avoids allocating while observing allocation. Tests must - // assert !Overflowed() before using Outstanding() as leak evidence. + // 使用有界账本,避免在观察分配时自身分配内存。 + // 测试在把 Outstanding() 作为泄漏证据前,必须先断言 !Overflowed()。 std::array allocations_{}; }; diff --git a/tests/tooling/generate_scaffold_fixtures.py b/tests/tooling/generate_scaffold_fixtures.py index 3a573dd2..d459fe7b 100644 --- a/tests/tooling/generate_scaffold_fixtures.py +++ b/tests/tooling/generate_scaffold_fixtures.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Build-only test fixtures: exercise the public CLI and compile its exact output.""" +"""仅用于构建的测试夹具:调用公开 CLI,并编译其原样输出。""" from pathlib import Path import os import json @@ -21,7 +21,7 @@ def apply_documented_text_functions(code): - """Compile the exact two business function bodies taught in the walkthrough.""" + """编译教程中讲解的两个业务函数体原文。""" guide = (root / "doc/dev_guide/first_custom_node.md").read_text(encoding="utf-8") for function in ("BuildPrompt", "FormatAnswer"): snippet = re.search(r"\s*```cpp\n(.*?)\n```", @@ -59,7 +59,7 @@ def apply_documented_text_functions(code): code = source.read_text(encoding="utf-8") if source.name == "scaffold_tutorial_llm_node.cpp": code = apply_documented_text_functions(code) - # Preserve each source file's anonymous namespace isolation in this amalgamation. + # 在合并后的源码中保留各源文件匿名命名空间的隔离性。 code = code.replace("namespace {", f"namespace {source.stem} {{", 1) stream.write(code) for test in sorted((fixture_root / "tests/unit/nodes").glob("test_*.cpp")): @@ -68,8 +68,8 @@ def apply_documented_text_functions(code): code = code.replace('mock_answer:', 'mock_answer:实体抽取:\\n') stream.write(code) -# Keep the runnable Control deployment in the walkthrough under native validation. -# Only the tutorial node type is replaced with its isolated generated equivalent. +# 让教程中可运行的 Control 部署继续接受原生校验。 +# 只把教程 Node 类型替换为隔离生成的等价类型。 guide = (root / "doc/dev_guide/first_control.md").read_text(encoding="utf-8") json_blocks = re.findall(r"```json\n(.*?)\n```", guide, re.DOTALL) if len(json_blocks) < 2: diff --git a/tests/tooling/studio_browser_test.mjs b/tests/tooling/studio_browser_test.mjs index 3aa0debc..6bb0173a 100644 --- a/tests/tooling/studio_browser_test.mjs +++ b/tests/tooling/studio_browser_test.mjs @@ -1,5 +1,5 @@ -// Optional browser coverage, invoked by the existing Python Studio suite. -// Supply an installed Playwright module and, optionally, a Chromium executable. +// 可选的浏览器覆盖测试,由现有的 Python Studio 测试套件调用。 +// 需提供已安装的 Playwright 模块,Chromium 可执行文件可选。 import assert from "node:assert/strict"; import { createRequire } from "node:module"; import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; @@ -52,7 +52,7 @@ try { }); page.once("dialog", dialog => dialog.accept()); await open("pipeline_browser.json"); - await page.click("#editModeButton"); // browse + await page.click("#editModeButton"); // 浏览模式 for (const width of [1280, 1366, 1920]) { await page.setViewportSize({ width, height: width === 1920 ? 1080 : 768 }); for (const filename of ["pipeline_browser.json", "pipeline_browser_multi.json"]) { @@ -230,7 +230,7 @@ try { assert.equal(await page.locator('#nodeId').inputValue(), 'browser_renamed'); assert.ok(!(await json()).pipeline.some(n => n.id === 'discarded_name')); await page.click('#undoButton'); assert.ok(!(await json()).pipeline.some(n => n.id === 'browser_renamed')); - // Validation applies model and raw JSON buffers in the same action. + // 校验在同一个动作中应用模型缓冲和原始 JSON 缓冲。 const originalMulti = await json(); await page.click('[data-tab="models"]'); await page.selectOption('#modelSelect', originalMulti.models[0].model_id); @@ -252,8 +252,8 @@ try { assert.equal((await json()).comment, 'browser JSON'); await page.click('#undoButton'); assert.deepEqual(await json(), originalMulti); await open('pipeline_browser.json'); - // Hold the POST response until another document is open. Even this earliest - // race must retain the originating document identity, not the active editor. + // 在打开另一个文档前挂起 POST 响应。即使是最早的这种竞争,也必须保留 + // 发起请求的文档标识,而不是当前活动的编辑器。 let releaseStart, started; const heldStart = new Promise(resolve => { releaseStart = resolve; }); const sawStart = new Promise(resolve => { started = resolve; }); @@ -279,8 +279,8 @@ try { await page.locator("#nodeForm details > summary").click(); assert.equal(await page.locator("#nodeBindings button").filter({ hasText: "恢复默认绑定" }).count(), 0); - // Dependency controls are graph actions, not node-property draft buffers. - // Add an independent node so the new ordering is observable and acyclic. + // 依赖控件属于图操作,而非 Node 属性草稿缓冲。 + // 添加一个独立 Node,使新的顺序可观察且无环。 const beforeAdd = await json(); if (!(await page.locator("#operatorSearch").isVisible())) await page.click("#operatorsToggle"); await page.locator("#operatorSearch").fill("TextTemplateNode"); @@ -307,7 +307,7 @@ try { assert.deepEqual(await json(), beforeAdd, "The preceding undo removes the added node"); await page.locator("#operatorSearch").fill(""); - // Data connections need no explicit ordering or dependency repair. + // 数据连接无需显式排序或依赖修复。 await open("pipeline_browser_multi.json"); const inferredDependencies = await json(); for (const node of inferredDependencies.pipeline) delete node.depends_on; @@ -317,7 +317,7 @@ try { await page.waitForFunction(() => document.querySelector("#validationOutput").textContent.includes("校验通过")); assert.equal(await page.locator("#validationOutput .fix-apply-btn").count(), 0); await open("pipeline_browser_other.json"); - // Native Validator reports use an object-valued error.message. + // 原生 Validator 报告中的 error.message 是对象值。 await page.click('[data-tab="json"]'); const invalidPipeline = await json(); invalidPipeline.pipeline[0].node_type = 'MissingNode'; await page.locator('#rawJson').fill(JSON.stringify(invalidPipeline, null, 2)); @@ -326,7 +326,7 @@ try { assert.equal(await page.locator('#validationTab').isVisible(), true); assert.match(await page.locator('#operationFeedback').textContent(), /方案校验未通过/); await page.click('#undoButton'); - // Startup failures must remain readable after the old 2.6-second timeout. + // 启动失败信息在超过旧的 2.6 秒超时后仍必须可读。 const startupPage = await browser.newPage(); try { await startupPage.route('**/api/v1/assets', route => route.fulfill({ status: 503, diff --git a/tests/tooling/studio_editor_test.mjs b/tests/tooling/studio_editor_test.mjs index f0f61c84..7deb590f 100644 --- a/tests/tooling/studio_editor_test.mjs +++ b/tests/tooling/studio_editor_test.mjs @@ -44,8 +44,8 @@ drafts.clear(); assert.equal(drafts.pending, false); console.log("Studio document history and pending editor buffer checks passed"); -// Keep the browser value-sanitization behavior that caused the original bug: -// text inputs remove newlines; textarea values normalize CR/CRLF to LF. +// 保留导致原始缺陷的浏览器值清洗行为: +// 文本输入框会去掉换行;textarea 的值会把 CR/CRLF 规范化为 LF。 class FormElement { constructor(tag) { this.tagName = tag.toUpperCase(); this.type = "text"; this.dataset = {}; diff --git a/tests/tooling/studio_fix_workflow_test.mjs b/tests/tooling/studio_fix_workflow_test.mjs index d1b3c538..5244321c 100644 --- a/tests/tooling/studio_fix_workflow_test.mjs +++ b/tests/tooling/studio_fix_workflow_test.mjs @@ -1,5 +1,5 @@ -// Execute the application's real repair handler and history. Rendering/network -// boundaries are replaced; application state transitions remain production code. +// 执行应用真实的修复处理函数和历史记录。只替换渲染/网络边界, +// 应用状态转换仍是生产代码。 import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import vm from "node:vm"; @@ -133,8 +133,8 @@ await handleApplyFix(fix); assert.equal(previewCalls, 0, "Do not preview a repair against unapplied form buffers"); -// The application must route graph changes through the native endpoint and treat -// its complete candidate as one undoable operation, including retained ordering. +// 应用必须通过原生接口提交图修改,并把完整候选 (含保留的顺序) +// 视为一次可撤销的操作。 reset(); const graphInitial = { ...initial, pipeline: [{ id: "a" }, { id: "b", depends_on: ["a"], inputs: { text: "shared" } }] }; state.pipeline = structuredClone(graphInitial); @@ -173,8 +173,8 @@ for (const change of [ assert.equal(history.canUndo, false); } -// Page review can last arbitrarily long: reject draft edits and changed tools -// before committing the reviewed candidate. +// 页面审阅可能持续任意时长:提交已审阅的候选前, +// 拒绝草稿编辑和已变化的工具。 reset(); await handleApplyFix(fix); state.pipeline.comment = "edited while reviewing"; @@ -217,7 +217,7 @@ for (const change of [ } console.log("Studio native authoring, page approval and preflight snapshot regressions passed"); -// Render the actual diagnostic card for unsupported wire-format versions. +// 为不支持的线格式版本渲染实际的诊断卡片。 const { appendDiagnostic } = modules.get("editor.js").namespace; for (const schema_version of [0, 2, 999]) { const container = new Element(); @@ -230,7 +230,7 @@ for (const schema_version of [0, 2, 999]) { } console.log("Studio real repair handler, history, late response and schema-version checks passed"); -// Exercise both Catalog entry paths and the real validation guard. +// 覆盖两条 Catalog 入口路径和真实的校验守卫。 for (const opened of [false, true]) { reset(); if (!opened) state.pipeline = null; diff --git a/tests/tooling/studio_graph_test.mjs b/tests/tooling/studio_graph_test.mjs index 503a1e54..e4494cc8 100644 --- a/tests/tooling/studio_graph_test.mjs +++ b/tests/tooling/studio_graph_test.mjs @@ -47,7 +47,7 @@ for (const [from, to, rectangles] of [ } } -// Small DOM harness exercises event safety and camera lifecycle without a browser dependency. +// 小型 DOM 测试桩,无需浏览器依赖即可覆盖事件安全和相机生命周期。 class Element { constructor(tag = "g") { this.tag = tag; this.attrs = {}; this.dataset = {}; this.children = []; this.listeners = {}; @@ -129,8 +129,8 @@ layers["#nodes"].children[1].fire("dblclick", { stopPropagation() {}, target: { near(graph.positions.b.x + graph.sizes.b.width / 2, (root.clientWidth / 2 - graph.offset.x) / graph.scale); console.log("Studio graph geometry, routing and interaction checks passed"); -// Raw data mappings alone drive visible data edges. No missing required or -// optional input may silently bind to a same-name key. +// 可见的数据边只由原始数据映射决定。缺失的必需或可选输入 +// 都不得静默绑定到同名键。 const workbenchSource = readFileSync(new URL("../../tools/pipeline_studio/web/workbench.js", import.meta.url), "utf8"); const { graphDocument } = await import(`data:text/javascript;base64,${Buffer.from(workbenchSource).toString("base64")}`); const catalog = { io_bindings: [{ binding_id: "example.binding", biz_name: "example" }], bizs: [{ biz_name: "example", ingress: [{ key: "request" }], egress: [] }], nodes: [ diff --git a/tests/tooling/test_dev_recipe.py b/tests/tooling/test_dev_recipe.py index 0c5b4a9f..2b5e6a9b 100644 --- a/tests/tooling/test_dev_recipe.py +++ b/tests/tooling/test_dev_recipe.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -"""Recipe contracts with the real native Catalog, Validator and Demo. +"""使用真实原生 Catalog、Validator 和 Demo 的 Recipe 契约测试。 -Only incremental-build and focused-test boundaries are stubbed when exercising -their failure decisions. This suite never starts a competing CMake build. +只在测试增量构建和定向测试的失败决策时对这两个边界打桩。 +本测试套件从不启动与之竞争的 CMake 构建。 """ import contextlib import importlib.util diff --git a/tests/tooling/test_json_prompt_demo.py b/tests/tooling/test_json_prompt_demo.py index 74b01928..eca0f2b5 100644 --- a/tests/tooling/test_json_prompt_demo.py +++ b/tests/tooling/test_json_prompt_demo.py @@ -1,4 +1,4 @@ -"""String boundary and native Demo transport tests; no simulated quality claims.""" +"""字符串边界与原生 Demo 传输测试;不做模拟的质量结论。""" import contextlib import importlib.util @@ -28,7 +28,7 @@ def test_forwards_complete_object_and_preserves_content(self): encoded = demo.encode_request(json.dumps(request, indent=2)) self.assertEqual(json.loads(encoded), request) self.assertNotIn("\n", encoded) - # Business validation must occur inside the SDK. + # 业务校验必须在 SDK 内部进行。 self.assertEqual(json.loads(demo.encode_request('{"query":123}')), {"query": 123}) self.assertEqual(demo.encode_request('{}'), '{}') @@ -99,15 +99,15 @@ def test_invalid_request_or_native_failure_does_not_publish_response(self): self.assertEqual(native.call_count, int(native_exit != 0)) def test_direct_run_demo_validates_and_normalizes_requests(self): - # Empty input rejects with ValueError + # 空输入以 ValueError 拒绝 with tempfile.TemporaryDirectory() as tmp, self.assertRaises(ValueError): demo.run_demo([], "config.conf", Path(tmp), "bin") - # Invalid input rejects with ValueError before invoking any process + # 非法输入在启动任何进程前以 ValueError 拒绝 with tempfile.TemporaryDirectory() as tmp, self.assertRaises(ValueError): demo.run_demo(["invalid json"], "config.conf", Path(tmp), "bin") - # Valid input is prepared and formatted + # 合法输入被准备并格式化 prepared = demo.prepare_requests(['{"b": 2, "a": 1}']) self.assertEqual(prepared, ['{"b":2,"a":1}']) diff --git a/tests/tooling/test_pipeline_studio.py b/tests/tooling/test_pipeline_studio.py index 0e8de463..fc19de0a 100644 --- a/tests/tooling/test_pipeline_studio.py +++ b/tests/tooling/test_pipeline_studio.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""API and filesystem boundary tests for the local Pipeline Studio.""" +"""本地 Pipeline Studio 的 API 与文件系统边界测试。""" import copy import importlib.util @@ -232,21 +232,21 @@ def _valid_fingerprint(self): def test_preview_fix_requires_expected_revision(self): valid_fp = self._valid_fingerprint() - # Omitted expected_revision (None) raises REVISION_CONFLICT + # 省略 expected_revision (None) 时抛出 REVISION_CONFLICT with self.assertRaises(SHOW.StudioError) as err_none: self.service.preview_fix( self.keyword, self.patch, expected_revision=None, tool_fingerprint=valid_fp ) self.assertEqual(err_none.exception.code, "REVISION_CONFLICT") - # Omitted expected_revision (empty string) raises REVISION_CONFLICT + # 省略 expected_revision (空字符串) 时抛出 REVISION_CONFLICT with self.assertRaises(SHOW.StudioError) as err_empty: self.service.preview_fix( self.keyword, self.patch, expected_revision="", tool_fingerprint=valid_fp ) self.assertEqual(err_empty.exception.code, "REVISION_CONFLICT") - # Mismatched expected_revision raises REVISION_CONFLICT + # expected_revision 不匹配时抛出 REVISION_CONFLICT with self.assertRaises(SHOW.StudioError) as err_mismatch: self.service.preview_fix( self.keyword, self.patch, expected_revision="mismatched_revision", tool_fingerprint=valid_fp @@ -257,21 +257,21 @@ def test_preview_fix_requires_tool_fingerprint(self): raw = json.dumps(self.keyword, sort_keys=True).encode("utf-8") valid_rev = SHOW.revision_for(raw) - # Omitted tool_fingerprint (None) raises TOOL_OUTDATED + # 省略 tool_fingerprint (None) 时抛出 TOOL_OUTDATED with self.assertRaises(SHOW.StudioError) as err_none: self.service.preview_fix( self.keyword, self.patch, expected_revision=valid_rev, tool_fingerprint=None ) self.assertEqual(err_none.exception.code, "TOOL_OUTDATED") - # Omitted tool_fingerprint (empty string) raises TOOL_OUTDATED + # 省略 tool_fingerprint (空字符串) 时抛出 TOOL_OUTDATED with self.assertRaises(SHOW.StudioError) as err_empty: self.service.preview_fix( self.keyword, self.patch, expected_revision=valid_rev, tool_fingerprint="" ) self.assertEqual(err_empty.exception.code, "TOOL_OUTDATED") - # Mismatched tool_fingerprint raises TOOL_OUTDATED + # tool_fingerprint 不匹配时抛出 TOOL_OUTDATED with self.assertRaises(SHOW.StudioError) as err_mismatch: self.service.preview_fix( self.keyword, self.patch, expected_revision=valid_rev, tool_fingerprint="mismatched_tool_fingerprint" @@ -440,7 +440,7 @@ def reject_conf(source, target): self.assertEqual(error.exception.code, "SAVE_FAILED") self.assertEqual({path: path.read_bytes() for path in paths}, originals) self.assertEqual(set(self.configs.iterdir()), set(paths)) - # A recovered failure must not advance either revision. + # 已恢复的失败不得推进任何一个 revision。 self.assertTrue(self.service.save_pipeline(saved["filename"], self.keyword, saved["revision"])["ok"]) def test_restarted_service_saves_model_path_without_shadowed_values(self): @@ -898,8 +898,8 @@ def test_init_rejects_invalid_options(self): self.assertEqual(process.stdout, "") def test_resolve_conf_exposes_model_sources_defaults_and_native_pool_errors(self): - # Resolver semantics must run in every backend variant, including Kite - # and minimal builds where llama.cpp is deliberately unavailable. + # 解析器语义必须在所有 Backend 变体中运行, + # 包括 Kite 构建和有意不含 llama.cpp 的最小构建。 conf_path = ROOT / "demo/fixtures/mock/pipeline_entity_extract.conf" code, report = self.command("resolve-conf", str(conf_path.relative_to(ROOT)), "--root", str(ROOT), "--depth", "1") self.assertEqual(code, 0, report) @@ -989,12 +989,12 @@ def test_cli_model_path_is_required_and_typed(self): self.assertEqual(report["plan"], {"layers": [], "topological_order": []}) def test_cli_plan_envelopes_across_entrypoints(self): - # Plan returns envelope with diagnostics on deployment preparation failure, - # partial plan on Core failure, and full topological order on success, with CLI parity. + # 部署准备失败时 plan 返回带诊断的信封,Core 失败时返回部分计划, + # 成功时返回完整拓扑顺序,且与 CLI 一致。 pipeline = json.loads( (ROOT / "demo/fixtures/mock/pipeline_entity_extract.json").read_text() ) - # 1. Removed deployment field is rejected + # 1. 已移除的部署字段被拒绝 invalid_doc = copy.deepcopy(pipeline) invalid_doc["deployment"]["model_paths"] = {"unknown_model_id": "models/foo.bin"} for ep in self.CLI_PARITY_ENTRYPOINTS: @@ -1008,7 +1008,7 @@ def test_cli_plan_envelopes_across_entrypoints(self): if ep[0] == "plan": self.assertEqual(res["plan"], {"layers": [], "topological_order": []}) - # 2. Core failure with valid deployment + # 2. 部署合法但 Core 失败 invalid_core = copy.deepcopy(pipeline) invalid_core["pipeline"].append({ "id": "bad_node", @@ -1025,7 +1025,7 @@ def test_cli_plan_envelopes_across_entrypoints(self): if ep[0] == "plan": self.assertIn("plan", res) - # 3. Valid pipeline plan + # 3. 合法的 Pipeline 计划 for ep in self.CLI_PARITY_ENTRYPOINTS: with self.subTest(case="valid_plan", entrypoint=ep): code, res = self.command(*ep, "--stdin", input_pipeline=pipeline) @@ -1037,11 +1037,11 @@ def test_cli_plan_envelopes_across_entrypoints(self): self.assertTrue(res["plan"]["topological_order"]) def test_cli_plan_envelopes_repeat(self): - # Repeat the full CLI parity matrix. + # 重复执行完整的 CLI 一致性矩阵。 self.test_cli_plan_envelopes_across_entrypoints() def test_cli_validate_io_exact_diagnostic_pointer(self): - # validate-io returns structured diagnostics with the exact JSON pointer. + # validate-io 返回带精确 JSON 指针的结构化诊断。 conf_path = ROOT / "demo/fixtures/mock/pipeline_entity_extract.conf" conf = json.loads(conf_path.read_text()) pipe_file = conf_path.with_name(conf["pipe_path"]) @@ -1050,7 +1050,7 @@ def test_cli_validate_io_exact_diagnostic_pointer(self): with tempfile.TemporaryDirectory(prefix="validate-io-", dir=ROOT / "build") as directory: changed_conf = Path(directory) / "pipeline.conf" changed_pipe = Path(directory) / conf["pipe_path"] - # Explicit malformed allocation retains its exact native pointer. + # 显式的错误分配保留其精确的原生指针。 output_override(pipe_doc, "entity_out")["capacities"] = {"entities_json": 0} changed_pipe.write_text(json.dumps(pipe_doc)) changed_conf.write_text(json.dumps(conf)) @@ -1079,20 +1079,20 @@ def test_cli_rejects_removed_model_paths_field_for_every_value(self): self.assertEqual(report["plan"], {"layers": [], "topological_order": []}) def test_cli_unknown_binding_unknown_root_and_malformed_binding(self): - # External selector diagnostics are identical across native entrypoints. + # 外部选择器的诊断在各原生入口之间完全一致。 pipeline = json.loads( (ROOT / "demo/fixtures/mock/pipeline_entity_extract.json").read_text() ) - # 1. Unknown io_binding + # 1. 未知 io_binding doc1 = copy.deepcopy(pipeline) io_overrides(doc1)["io_binding"] = "nonexistent.binding.v99" - # 2. Removed root selector is rejected even when a binding is present. + # 2. 即使存在 binding,已移除的根选择器也会被拒绝。 doc2 = copy.deepcopy(pipeline) doc2["biz_name"] = "unmatched_biz_name" io_overrides(doc2)["io_binding"] = "entity_extract.operator.v1" - # 3. Binding must be a nonempty string. + # 3. binding 必须是非空字符串。 doc3 = copy.deepcopy(pipeline) io_overrides(doc3)["io_binding"] = 12345 @@ -1114,23 +1114,23 @@ def test_cli_unknown_binding_unknown_root_and_malformed_binding(self): self.assertEqual(res["plan"], {"layers": [], "topological_order": []}) def test_cli_output_slot_allocation_errors(self): - # Reject removed type fields, unknown slots, invalid allocation shapes and capacities. + # 拒绝已移除的 type 字段、未知槽位、非法的分配结构和容量。 pipeline = json.loads( (ROOT / "demo/fixtures/mock/pipeline_entity_extract.json").read_text() ) - # 1. A removed type declaration is an unknown field. + # 1. 已移除的 type 声明属于未知字段。 doc1 = copy.deepcopy(pipeline) output_override(doc1, "entity_out")["type"] = "entity_out" - # 2. Unknown output slot + # 2. 未知输出槽位 doc2 = copy.deepcopy(pipeline) io_overrides(doc2)["out_mem"] = {"bogus_slot": {}} - # 3. Invalid output allocation shape + # 3. 非法的输出分配结构 doc3 = copy.deepcopy(pipeline) io_overrides(doc3)["out_mem"] = {"entity_out": []} - # 4. Invalid output allocation capacity (non-positive capacity value 0) + # 4. 非法的输出分配容量 (非正数容量 0) doc4 = copy.deepcopy(pipeline) output_override(doc4, "entity_out")["capacities"] = {"entities_json": 0} @@ -1155,12 +1155,12 @@ def test_cli_output_slot_allocation_errors(self): self.assertEqual(res["plan"], {"layers": [], "topological_order": []}) def test_cli_multiple_core_errors_with_deployment(self): - # Valid deployment, but Core has multiple Node errors. - # All diagnostics must be preserved in the response array across all CLI entrypoints. + # 部署合法,但 Core 有多个 Node 错误。 + # 所有 CLI 入口的响应数组都必须保留全部诊断。 pipeline = json.loads( (ROOT / "demo/fixtures/mock/pipeline_entity_extract.json").read_text() ) - # Add two invalid nodes to pipeline + # 向 Pipeline 添加两个非法 Node doc = copy.deepcopy(pipeline) doc["pipeline"].append({ "id": "bad_node_1", @@ -1179,7 +1179,7 @@ def test_cli_multiple_core_errors_with_deployment(self): self.assertEqual(code, 1) self.assertFalse(res["ok"]) self.assertIn("diagnostics", res) - # Must retain multiple diagnostics, not compressed + # 必须保留多条诊断,不能合并 self.assertGreaterEqual(len(res["diagnostics"]), 2) diag_codes = [d["code"] for d in res["diagnostics"]] self.assertIn("UNKNOWN_NODE_TYPE", diag_codes) @@ -1187,7 +1187,7 @@ def test_cli_multiple_core_errors_with_deployment(self): self.assertIn("plan", res) def test_cli_edit_invalid_deployment_validation_policy(self): - # Edit invalid deployment with require_valid=false vs require_valid=true. + # 分别以 require_valid=false 和 require_valid=true 编辑非法部署。 pipeline = json.loads( (ROOT / "demo/fixtures/mock/pipeline_entity_extract.json").read_text() ) @@ -1201,7 +1201,7 @@ def test_cli_edit_invalid_deployment_validation_policy(self): "config": {"template": "{{input}}"}, } - # Case 1: require_valid=false -> returns modified draft with validation.ok=false + # 情形 1:require_valid=false -> 返回修改后的草稿,validation.ok=false req_false = { "schema_version": 1, "pipeline": invalid_doc, @@ -1218,7 +1218,7 @@ def test_cli_edit_invalid_deployment_validation_policy(self): self.assertFalse(res_false["validation"]["ok"]) self.assertEqual(res_false["validation"]["diagnostics"][0]["code"], "UNKNOWN_IO_BINDING") - # Case 2: require_valid=true -> rejects without returning modified pipeline + # 情形 2:require_valid=true -> 拒绝,且不返回修改后的 Pipeline req_true = { "schema_version": 1, "pipeline": invalid_doc, @@ -1235,8 +1235,8 @@ def test_cli_edit_invalid_deployment_validation_policy(self): def test_cli_file_failures_return_json_diagnostics(self): - # Missing files produce schema-compliant JSON diagnostics through CLI entrypoints. - # 1. validate-io on non-existent config file + # 文件缺失时,各 CLI 入口产出符合 schema 的 JSON 诊断。 + # 1. 对不存在的配置文件执行 validate-io code, res = self.command("validate-io", "nonexistent_config_file.conf") self.assertEqual(code, 1) self.assertFalse(res["ok"]) @@ -1244,7 +1244,7 @@ def test_cli_file_failures_return_json_diagnostics(self): self.assertEqual(res["diagnostics"][0]["code"], "IO_VALIDATION_ERROR") self.assertIn("nonexistent_config_file.conf", res["diagnostics"][0]["message"]) - # 2. resolve-conf on non-existent file + # 2. 对不存在的文件执行 resolve-conf code, res = self.command("resolve-conf", "nonexistent_config_file.conf", "--root", str(ROOT)) self.assertEqual(code, 1) self.assertFalse(res["ok"]) @@ -1252,7 +1252,7 @@ def test_cli_file_failures_return_json_diagnostics(self): self.assertEqual(res["diagnostics"][0]["code"], "DEPLOYMENT_CONFIG") self.assertEqual(res["diagnostics"][0]["path"], "/") - # 3. validate on non-existent file + # 3. 对不存在的文件执行 validate code, res = self.command("validate", "nonexistent_pipeline.json") self.assertEqual(code, 1) self.assertFalse(res["ok"]) @@ -1702,7 +1702,7 @@ def test_invalid_fixtures_table_driven_parity_matrix(self): case = {**case, "primary_code": "UNKNOWN_IO_BINDING", "primary_path": "/deployment/io/io_binding", "required_codes": ["UNKNOWN_IO_BINDING"]} elif case["primary_code"] == "UNKNOWN_FIELD" and case["primary_path"].count("/") == 1: - # The external envelope rejects unknown root fields before Core. + # 外部信封在进入 Core 前拒绝未知的根字段。 case = {**case, "primary_code": "DEPLOYMENT_ERROR", "required_codes": ["DEPLOYMENT_ERROR"]} proc_val = subprocess.run( @@ -1734,8 +1734,7 @@ def test_invalid_fixtures_table_driven_parity_matrix(self): with urllib.request.urlopen(req, timeout=5) as resp: api_val = json.load(resp) - # Web is a byte-semantic pass-through of the CLI/Validator - # report; all optional diagnostic fields are compared. + # Web 按字节语义透传 CLI/Validator 报告;所有可选诊断字段都会比较。 self.assertEqual(api_val, cli_val) proc_plan = subprocess.run( @@ -1748,8 +1747,8 @@ def test_invalid_fixtures_table_driven_parity_matrix(self): ) self.assertEqual(proc_plan.returncode, 1) cli_plan = json.loads(proc_plan.stdout) - # Preparation errors have an explicit empty plan envelope; - # diagnostics remain identical to validate and the HTTP API. + # 准备阶段错误带显式的空 plan 信封; + # 诊断与 validate 和 HTTP API 保持一致。 if "plan" not in cli_val: self.assertEqual(cli_plan.pop("plan"), {"layers": [], "topological_order": []}) self.assertEqual(cli_plan, cli_val) @@ -1903,8 +1902,8 @@ def test_native_build_variant_and_real_effects_are_bound_to_selection(self): self.assertFalse(report["ready_for_biz"]) self.assertEqual(report["schema_version"], 2) self.assertNotIn("ready_for_business", report) - # Current canonical build has enabled backends; claiming the empty - # minimal variant must fail independently of this model-free Pipeline. + # 当前规范构建启用了若干 Backend;声明为空的 minimal 变体必须失败, + # 与这个无模型的 Pipeline 无关。 mismatched = selection.inspect_selection(pipeline, tool, ROOT / "models", variant="minimal" if report["build"]["enabled_backends"] else "default-cpu") self.assertFalse(mismatched["ok"]) receipt = selection.evaluate(pipeline, report, tool, ROOT / "models", spec, conf, demo) @@ -2039,7 +2038,7 @@ def test_authoring_connect_preserves_valid_default_output_fanout(self): existing = pipe["pipeline"][0] existing["id"] = "b" existing.pop("depends_on", None) - existing["inputs"] = {"text": "text"} # Explicit input uses the default output key. + existing["inputs"] = {"text": "text"} # 显式输入使用默认输出键。 source = { "id": "a", "node_type": "TextTemplateNode", "depends_on": [], "inputs": {"primary": "input_sentences"}, "outputs": {}, @@ -2191,7 +2190,7 @@ def test_authoring_add_node_explicit_auto_id_and_collision(self): self.assertNotIn("depends_on", added) self.assertEqual(added["inputs"], {}) - # Duplicate ID rejection + # 拒绝重复 ID req2 = { "schema_version": 1, "pipeline": res["pipeline"], @@ -2206,7 +2205,7 @@ def test_authoring_add_node_explicit_auto_id_and_collision(self): self.assertFalse(res2["ok"]) self.assertIn("DUPLICATE_NODE_ID", res2["diagnostics"][0]["message"]) - # Auto ID allocation + # 自动分配 ID req3 = { "schema_version": 1, "pipeline": res["pipeline"], @@ -2220,7 +2219,7 @@ def test_authoring_add_node_explicit_auto_id_and_collision(self): self.assertTrue(res3["ok"]) self.assertNotEqual(res3["pipeline"]["pipeline"][1]["id"], "custom_rule") - # Unknown node type + # 未知 Node 类型 req4 = { "schema_version": 1, "pipeline": res["pipeline"], @@ -2263,9 +2262,9 @@ def test_authoring_remove_node_detaches_inputs_and_dependencies(self): self.assertEqual(len(res["pipeline"]["pipeline"]), 1) rule_node = res["pipeline"]["pipeline"][0] self.assertEqual(rule_node["id"], "rule") - # depends_on detached + # depends_on 已解除 self.assertEqual(rule_node["depends_on"], []) - # Required input is absent after its producer is removed. + # 生产者被移除后,必需输入随之缺失。 self.assertNotIn("text", rule_node["inputs"]) def test_authoring_rename_node_syncs_dependencies_and_preserves_keys(self): @@ -2304,12 +2303,12 @@ def test_authoring_rename_node_syncs_dependencies_and_preserves_keys(self): tpl = res["pipeline"]["pipeline"][0] rule = res["pipeline"]["pipeline"][1] self.assertEqual(tpl["id"], "new_template") - # Data keys are preserved + # 数据键保持不变 self.assertEqual(tpl["outputs"]["text"], "tpl_text") - # depends_on updated + # depends_on 已更新 self.assertEqual(rule["depends_on"], ["new_template"]) - # Duplicate ID rejection + # 拒绝重复 ID req_dup = { "schema_version": 1, "pipeline": pipe, @@ -2340,7 +2339,7 @@ def test_authoring_connect_and_cycle_and_redundant_dependencies(self): }, ], } - # Ingress to node + # 入口到 Node req1 = { "schema_version": 1, "pipeline": pipe, @@ -2358,7 +2357,7 @@ def test_authoring_connect_and_cycle_and_redundant_dependencies(self): ) self.assertEqual(res1["pipeline"]["pipeline"][0]["depends_on"], []) - # Node to node + # Node 到 Node req2 = { "schema_version": 1, "pipeline": res1["pipeline"], @@ -2376,7 +2375,7 @@ def test_authoring_connect_and_cycle_and_redundant_dependencies(self): self.assertEqual(rule["inputs"]["text"], tpl_key) self.assertEqual(rule["depends_on"], []) - # Cycle detection: connecting rule to template creates cycle + # 环检测:把 rule 连到 template 会形成环 req_cycle = { "schema_version": 1, "pipeline": res2["pipeline"], @@ -2412,7 +2411,7 @@ def test_authoring_connect_and_disconnect_biz_egress(self): }, ], } - # Connect rule.matches to $egress rule_matches + # 把 rule.matches 连接到 $egress rule_matches req_egress = { "schema_version": 1, "pipeline": pipe, @@ -2429,7 +2428,7 @@ def test_authoring_connect_and_disconnect_biz_egress(self): self.assertEqual(rule_out, "rule_matches") self.assertEqual(audit_in, "rule_matches") - # Disconnect rule.matches from $egress rule_matches + # 断开 rule.matches 与 $egress rule_matches 的连接 req_disc = { "schema_version": 1, "pipeline": res_egress["pipeline"], @@ -2467,7 +2466,7 @@ def test_authoring_disconnect_preserves_extra_order_and_removes_input(self): }, ], } - # Disconnecting data port preserves execution dependency (Regression Case 1) + # 断开数据端口时保留执行依赖 (回归用例 1) req = { "schema_version": 1, "pipeline": pipe, @@ -2483,7 +2482,7 @@ def test_authoring_disconnect_preserves_extra_order_and_removes_input(self): self.assertEqual(rule["depends_on"], ["tpl"]) self.assertNotIn("text", rule["inputs"]) - # remove_dependency removes execution dependency + # remove_dependency 移除执行依赖 req_rm_dep = { "schema_version": 1, "pipeline": res["pipeline"], @@ -2517,7 +2516,7 @@ def test_authoring_batch_operations_and_atomic_rollback(self): self.assertTrue(res_valid["validation"]["ok"]) self.assertEqual(len(res_valid["pipeline"]["pipeline"]), 2) - # Failure mid-batch discards changes + # 批处理中途失败时丢弃所有修改 req_fail = { "schema_version": 1, "pipeline": pipe, @@ -2533,7 +2532,7 @@ def test_authoring_batch_operations_and_atomic_rollback(self): self.assertEqual(res_fail["failed_operation_index"], 1) self.assertNotIn("pipeline", res_fail) - # Batch limit > 128 + # 批大小超过 128 req_oversize = { "schema_version": 1, "pipeline": pipe, @@ -2579,7 +2578,7 @@ def test_studio_authoring_preview_endpoint(self): self.assertIn("tool_fingerprint", res) self.assertEqual(res["pipeline"]["pipeline"][0]["id"], "n1") - # Stale revision conflict + # 过期 revision 冲突 with self.assertRaises(SHOW.StudioError) as ctx: self.service.preview_authoring( pipe, @@ -2618,7 +2617,7 @@ def test_studio_preflight_independent_summary(self): self.assertIn("tools", summary) self.assertIn("pipeline_snapshot", summary) self.assertIn("status", summary) - # Verify no user file was written + # 验证没有写入任何用户文件 self.assertFalse((self.configs / "pipeline_preflight_leak.json").exists()) def associated_doc_qa(self): @@ -2640,7 +2639,7 @@ def test_associated_preflight_run_and_save_share_candidate(self): pipeline["models"][0]["model_path"] = "models/selected_A.onnx" model_id = pipeline["models"][0]["model_id"] expected_conf = {"pipe_path": path.name} - # Spy on real native resolution so this checks exactly what preflight resolves. + # 监视真实的原生解析,确保检查的正是预检所解析的内容。 resolved_candidates = [] resolve = self.service.resolve_run_conf @@ -2709,18 +2708,17 @@ def test_associated_external_file_changes_block_candidate_and_save(self): self.assertEqual((path.read_bytes(), conf_path.read_bytes()), originals) def test_studio_deployment_associate_and_model_path_update(self): - # Create pipeline in configs + # 在 configs 中创建 Pipeline pipe_path = self.configs / "pipeline_doc_qa_assoc.json" doc_qa_pipe = json.loads((ROOT / "configs" / "pipeline_doc_qa_cpu.json").read_text()) output_override(doc_qa_pipe, "doc_out")["capacities"] = {"answer_text": 2047} pipe_path.write_text(json.dumps(doc_qa_pipe, indent=2)) - # Create conf in configs pointing to this pipeline + # 在 configs 中创建指向该 Pipeline 的 conf conf_path = self.configs / "pipeline_doc_qa_assoc.conf" doc_qa_conf = {"pipe_path": pipe_path.name} conf_path.write_text(json.dumps(doc_qa_conf, indent=2)) - # Associate assoc_res = self.service.associate_deployment( "pipeline_doc_qa_assoc.json", "pipeline_doc_qa_assoc.conf" ) @@ -2728,9 +2726,9 @@ def test_studio_deployment_associate_and_model_path_update(self): self.assertEqual(assoc_res["conf_name"], "pipeline_doc_qa_assoc.conf") self.assertIn("pipeline_doc_qa_assoc.conf", self.service.save_targets(pipe_path)) - # Now update pipeline models and save + # 更新 Pipeline 模型并保存 modified_pipe = copy.deepcopy(doc_qa_pipe) - # Update model_path of first model + # 更新第一个模型的 model_path modified_pipe["models"][0]["model_path"] = "models/new_embed_model.onnx" pipe_raw = pipe_path.read_bytes() pipe_rev = SHOW.revision_for(pipe_raw) @@ -2740,13 +2738,13 @@ def test_studio_deployment_associate_and_model_path_update(self): ) self.assertTrue(save_res["ok"]) - # Verify the model entry owns the effective path + # 验证模型条目持有有效路径 updated_pipe = json.loads(pipe_path.read_text()) self.assertEqual( updated_pipe["models"][0]["model_path"], "models/new_embed_model.onnx", ) - # Verify non-model conf settings preserved + # 验证 conf 中与模型无关的设置保持不变 self.assertEqual(updated_pipe["deployment"]["io"]["out_mem"], {"doc_out": {"capacities": {"answer_text": 2047}}}) updated_conf = json.loads(conf_path.read_text()) @@ -2796,7 +2794,7 @@ def test_authoring_reserved_node_ids_rejected(self): self.assertIn("RESERVED_NODE_ID", res2["diagnostics"][0]["message"]) def test_authoring_disconnect_requires_explicit_binding(self): - # A same-name output plus explicit order must not create an input binding. + # 同名输出加显式顺序不得产生输入绑定。 pipe = { "deployment": {"io": {"io_binding": "keyword_match.operator.v1"}}, "models": [], @@ -2831,7 +2829,7 @@ def test_authoring_disconnect_requires_explicit_binding(self): self.assertFalse(res["ok"]) self.assertIn("连线不存在", res["diagnostics"][0]["message"]) - # Ingress disconnect + # 断开入口连接 pipe_ing = { "deployment": {"io": {"io_binding": "keyword_match.operator.v1"}}, "models": [], @@ -2942,7 +2940,7 @@ def test_authoring_add_dependency_avoids_redundant_indirect_ancestor(self): {"id": "c", "node_type": "TextRuleMatchNode", "depends_on": ["b"], "inputs": {}, "outputs": {}, "config": {}}, ], } - # Adding c depends on a: a is already an indirect ancestor of c via b + # 添加 c 依赖 a:a 已经通过 b 成为 c 的间接祖先 req = { "schema_version": 1, "pipeline": pipe, @@ -3098,7 +3096,7 @@ def test_complete_deployment_envelope_is_available(self): "metadata_type_id", "capacities"}) self.assertEqual(allocation["required"], []) self.assertFalse(allocation["additionalProperties"]) - # Array and scalar params are interpreted by the allocator. + # 数组和标量参数由分配器解释。 params = allocation["properties"]["params"] self.assertNotIn("type", params) self.assertNotIn("enum", params) diff --git a/tests/tooling/test_scaffold_custom_node.py b/tests/tooling/test_scaffold_custom_node.py index 0c9a5980..f86bb9c5 100755 --- a/tests/tooling/test_scaffold_custom_node.py +++ b/tests/tooling/test_scaffold_custom_node.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Generator CLI/file contracts. Generated C++ also compiles in the Node runner.""" +"""生成器 CLI/文件契约测试。生成的 C++ 也会在 Node runner 中编译。""" import importlib.util import contextlib import io @@ -63,7 +63,7 @@ def test_overwrite_and_dry_run(self): self.assertEqual(list(Path(temp).iterdir()), []) result = self.run_cli(*args) self.assertEqual(result.returncode, 0, result.stderr) - # Only collected source directories may claim automatic compilation. + # 只有被收集的源码目录才能声明自动编译。 self.assertIn("the build does not collect it", result.stdout) self.assertNotIn("compiled automatically", result.stdout) node = Path(temp) / "example_node.cpp" @@ -141,21 +141,21 @@ def test_write_test_creates_source_and_test_file(self): result = self.run_cli("AwesomeFeatureNode", "--write-test", env=env) self.assertEqual(result.returncode, 0, result.stderr) - # Check custom node source generated under src/custom_nodes/awesome_feature_node.cpp + # 检查在 src/custom_nodes/awesome_feature_node.cpp 生成的自定义 Node 源文件 source_file = Path(temp) / "src" / "custom_nodes" / "awesome_feature_node.cpp" self.assertTrue(source_file.exists()) source_content = source_file.read_text(encoding="utf-8") self.assertIn("REGISTER_FUNCTION_NODE(AwesomeFeatureNode, AwesomeFeatureNodeSpec());", source_content) self.assertIn("MakeMapSpec", source_content) - # Check test file generated under tests/unit/nodes/test_awesome_feature_node.cpp + # 检查在 tests/unit/nodes/test_awesome_feature_node.cpp 生成的测试文件 test_file = Path(temp) / "tests" / "unit" / "nodes" / "test_awesome_feature_node.cpp" self.assertTrue(test_file.exists()) test_content = test_file.read_text(encoding="utf-8") self.assertIn("TEST(CustomNodeCatalogTest, AwesomeFeatureNode_RegistrationAndInstantiation)", test_content) self.assertIn("TEST(CustomNodeCatalogTest, AwesomeFeatureNode_MapPreservesInputData)", test_content) - # Output reporting + # 检查输出报告 self.assertIn(f"Created {source_file}", result.stdout) self.assertIn(f"Created {test_file}", result.stdout) self.assertIn("compiled automatically", result.stdout) @@ -176,7 +176,7 @@ def test_write_test_dry_run_does_not_create_files(self): ) self.assertEqual(result.returncode, 0, result.stderr) - # Check preview output + # 检查预览输出 source_file = Path(temp) / "src" / "custom_nodes" / "dry_run_node.cpp" test_file = Path(temp) / "tests" / "unit" / "nodes" / "test_dry_run_node.cpp" self.assertIn(f"--- {source_file} (new file) ---", result.stdout) @@ -186,7 +186,7 @@ def test_write_test_dry_run_does_not_create_files(self): self.assertIn("Tests in tests/unit/nodes/test_*.cpp are discovered automatically.", result.stdout) self.assertNotIn("+ test_dry_run_node.cpp", result.stdout) - # Verify no files were created or modified + # 验证没有创建或修改任何文件 self.assertFalse(source_file.exists()) self.assertFalse(test_file.exists()) self.assertEqual(list(Path(temp).rglob("CMakeLists.txt")), []) @@ -210,7 +210,7 @@ def test_write_test_rejects_incompatible_flags_and_custom_output_dir(self): self.assertNotEqual(result.returncode, 0) self.assertIn(expected_error, result.stderr) - # Ensure no files were touched in any directory + # 确保所有目录中的文件都未被改动 source_file = Path(temp) / "src" / "custom_nodes" / "rejected_node.cpp" test_file = Path(temp) / "tests" / "unit" / "nodes" / "test_rejected_node.cpp" self.assertFalse(source_file.exists()) @@ -221,7 +221,7 @@ def test_write_test_atomic_rollback_on_conflict(self): with tempfile.TemporaryDirectory() as temp: env = self._setup_mock_repo(temp) - # Case 1: Target source file already exists + # 情形 1:目标源文件已存在 conflict_source = Path(temp) / "src" / "custom_nodes" / "conflict_source_node.cpp" conflict_source.write_text("existing custom node source", encoding="utf-8") res_source_conflict = self.run_cli( @@ -234,7 +234,7 @@ def test_write_test_atomic_rollback_on_conflict(self): self.assertEqual(list(Path(temp).rglob("CMakeLists.txt")), []) self.assertEqual(list((Path(temp) / "tests").glob("*.cmake")), []) - # Case 2: Target test file already exists + # 情形 2:目标测试文件已存在 conflict_test = Path(temp) / "tests" / "unit" / "nodes" / "test_conflict_test_node.cpp" conflict_test.write_text("existing custom node test", encoding="utf-8") res_test_conflict = self.run_cli( @@ -250,7 +250,7 @@ def test_write_test_atomic_rollback_on_conflict(self): def test_change_plan_rollback_preserves_concurrent_edit(self): with tempfile.TemporaryDirectory() as temp: env = self._setup_mock_repo(temp) - # Generic modifications remain transactional even without source manifests. + # 即使没有源码清单,通用修改仍保持事务性。 plan = SCAFFOLD.ChangePlan() new_file = Path(temp) / "src" / "custom_nodes" / "rollback_probe.cpp" new_test = Path(temp) / "tests" / "unit" / "nodes" / "test_rollback_probe.cpp" @@ -260,11 +260,11 @@ def test_change_plan_rollback_preserves_concurrent_edit(self): plan.add_new_file(new_file, "// rollback probe content") plan.add_new_file(new_test, "// rollback test probe content") plan.add_modification(mod_target, mod_orig, mod_orig + "\n# Modified\n") - # Simulate concurrent modification to mod_target before commit + # 在提交前模拟对 mod_target 的并发修改 mod_target.write_text(mod_orig + "\n# Concurrent user edit\n", encoding="utf-8") with self.assertRaises(RuntimeError): plan.commit() - # Verify new_file was unlinked and mod_target was not clobbered + # 验证 new_file 已被删除,且 mod_target 未被覆盖 self.assertFalse(new_file.exists()) self.assertFalse(new_test.exists()) self.assertEqual(mod_target.read_text(encoding="utf-8"), mod_orig + "\n# Concurrent user edit\n") diff --git a/tests/unit/adapter/test_adapter_purity.cpp b/tests/unit/adapter/test_adapter_purity.cpp index de9f3998..7cfc23af 100644 --- a/tests/unit/adapter/test_adapter_purity.cpp +++ b/tests/unit/adapter/test_adapter_purity.cpp @@ -133,10 +133,9 @@ struct AudioOutputFixture { }; // ========================================================================= -// 1. All 8 Businesses Converter Purity +// 1. 全部 8 个业务的 Converter 纯净性 // ========================================================================= -// 1.1 DocQaConverter Purity (Biz 1) TEST_F(AdapterPurityTest, DocQaAdapterPurity) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "doc_query.plain.operator.v1"); @@ -172,7 +171,7 @@ TEST_F(AdapterPurityTest, DocQaAdapterPurity) { EXPECT_EQ((*docs)[0].data, "Doc Content"); EXPECT_EQ((*queries)[0].data, "Query Question"); - // Output encoding + // 输出编码 TextBatch answers; answers.emplace_back(0, 0, "Model Generated Answer"); harness.Publish("llm_answers", std::move(answers)); @@ -198,7 +197,6 @@ TEST_F(AdapterPurityTest, DocQaAdapterPurity) { EXPECT_STREQ(outputs[0].answer_text->data, "Model Generated Answer"); } -// 1.2 KeywordMatchConverter Purity (Biz 2) TEST_F(AdapterPurityTest, KeywordMatchAdapterPurity) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "text.plain.operator.v1"); @@ -233,7 +231,6 @@ TEST_F(AdapterPurityTest, KeywordMatchAdapterPurity) { "\"测试\",\"matches\":[],\"slots\":{}}"); } -// 1.3 EntityExtractConverter Purity (Biz 3) TEST_F(AdapterPurityTest, EntityExtractAdapterPurity) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "text.plain.operator.v1"); @@ -267,7 +264,6 @@ TEST_F(AdapterPurityTest, EntityExtractAdapterPurity) { EXPECT_STREQ(outputs[0].entities_json->data, "[\"E1\"]"); } -// 1.4 DialogueAuditConverter Purity (Biz 4) TEST_F(AdapterPurityTest, DialogueAuditAdapterPurity) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "audit.plain.operator.v1"); @@ -314,7 +310,6 @@ TEST_F(AdapterPurityTest, DialogueAuditAdapterPurity) { EXPECT_STREQ(outputs[0].matched_policy_clause->data, "Clause 1"); } -// 1.5 OcrInvoiceQaConverter Purity (Biz 5) TEST_F(AdapterPurityTest, OcrInvoiceQaAdapterPurity) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "image_query.plain.operator.v1"); @@ -385,7 +380,6 @@ TEST_F(AdapterPurityTest, OcrInvoiceQaAdapterPurity) { EXPECT_STREQ(od_fix.out.result_json->data, "{\"total\":99.9}"); } -// 1.6 AudioAsrIntentConverter Purity (Biz 6) TEST_F(AdapterPurityTest, AudioAsrIntentAdapterPurity) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "audio.pcm.operator.v1"); @@ -425,7 +419,6 @@ TEST_F(AdapterPurityTest, AudioAsrIntentAdapterPurity) { "\"matches\":[],\"slots\":{}}"); } -// 1.7 CrossRerankConverter Purity (Biz 7) TEST_F(AdapterPurityTest, CrossRerankAdapterPurity) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "rerank.plain.operator.v1"); @@ -475,7 +468,6 @@ TEST_F(AdapterPurityTest, CrossRerankAdapterPurity) { EXPECT_EQ(outputs[0].sorted_indices[1], 0); } -// 1.8 TranslateConverter Purity (Biz 8) TEST_F(AdapterPurityTest, TranslateAdapterPurity) { const auto* in_conv = IoConverterRegistry::Instance().FindInputConverter( "translate.json.operator.v1"); @@ -511,7 +503,7 @@ TEST_F(AdapterPurityTest, TranslateAdapterPurity) { } // ========================================================================= -// 2. Contract Invariants and Security Edge Cases +// 2. 契约不变量与安全边界情形 // ========================================================================= TEST_F(AdapterPurityTest, DocQaAdapter_FailClosedWhenMissingOutputs) { @@ -523,7 +515,7 @@ TEST_F(AdapterPurityTest, DocQaAdapter_FailClosedWhenMissingOutputs) { {"intent_matches", "intent_matches"}, {"doc_chunk_counts", "doc_chunk_counts"}}); - // Case 1: missing llm_answers + // 情形 1:缺少 llm_answers { test::AdapterHarness harness(out_conv, out_bindings); harness.SetRequestIds(std::vector{1001}); @@ -532,7 +524,7 @@ TEST_F(AdapterPurityTest, DocQaAdapter_FailClosedWhenMissingOutputs) { EXPECT_NE(harness.EncodeOperator(&outputs, fix.Capacities()), 0); } - // Case 2: has llm_answers but missing intent_matches -> MUST fail-closed + // 情形 2:有 llm_answers 但缺少 intent_matches -> 必须 fail-closed { test::AdapterHarness harness(out_conv, out_bindings); harness.SetRequestIds(std::vector{1001}); @@ -545,8 +537,7 @@ TEST_F(AdapterPurityTest, DocQaAdapter_FailClosedWhenMissingOutputs) { COMPANY_ALG_ERR_INVALID_INPUT); } - // Case 3: has intent_matches but missing explicit chunk counts -> MUST - // fail-closed + // 情形 3:有 intent_matches 但缺少显式分块计数 -> 必须 fail-closed { test::AdapterHarness harness(out_conv, out_bindings); harness.SetRequestIds(std::vector{1001}); @@ -562,7 +553,7 @@ TEST_F(AdapterPurityTest, DocQaAdapter_FailClosedWhenMissingOutputs) { COMPANY_ALG_ERR_INVALID_INPUT); } - // Case 4: all outputs exist but the request id table is absent + // 情形 4:所有输出都存在,但缺少请求 ID 表 { test::AdapterHarness harness(out_conv, out_bindings); TextBatch answers; @@ -607,7 +598,7 @@ TEST_F(AdapterPurityTest, test::AdapterHarness harness(out_conv, out_bindings); harness.SetRequestIds(std::vector{1001}); - // structured_verdicts missing required field 'risk_level' -> MUST fail-closed + // structured_verdicts 缺少必填字段 'risk_level' -> 必须 fail-closed StructuredDocumentBatch verdicts; nlohmann::json incomplete_obj = {{"only_verdict", "合规"}}; verdicts.emplace_back( @@ -703,7 +694,7 @@ TEST_F(AdapterPurityTest, DialogueAuditAdapter_RejectsOversizedChannelName) { CompanyString cs_query{static_cast(query_str.size()), query_str.data()}; - // Valid length <= 256 + // 合法长度 <= 256 { test::AdapterHarness harness(in_conv, in_bindings); CompanyString cs_chan{static_cast(valid_channel.size()), @@ -712,7 +703,7 @@ TEST_F(AdapterPurityTest, DialogueAuditAdapter_RejectsOversizedChannelName) { EXPECT_EQ(harness.DecodeOperator({&in}), COMPANY_ALG_SUCCESS); } - // Oversized length > 256 + // 超长 > 256 { test::AdapterHarness harness(in_conv, in_bindings); CompanyString cs_chan{static_cast(oversized_channel.size()), @@ -744,7 +735,7 @@ TEST_F(AdapterPurityTest, options.request_ids = &request_ids; options.converter_id = op_conv->converter_id; - // 1. Operator buffer with small capacity (500) -> BUFFER_TOO_SMALL + // 1. Operator 缓冲区容量较小 (500) -> BUFFER_TOO_SMALL { CompanyOperatorDocOutput small_out{}; std::vector ans_buf(500); @@ -768,8 +759,7 @@ TEST_F(AdapterPurityTest, EXPECT_EQ(ret, COMPANY_ALG_ERR_BUFFER_TOO_SMALL); } - // 2. Operator variable buffer: 6000 bytes of storage, preserve - // full answer + // 2. Operator 可变缓冲区:6000 字节存储,保留完整答案 { CompanyOperatorDocOutput op_out{}; std::vector ans_buf(6000); @@ -818,7 +808,7 @@ TEST_F(AdapterPurityTest, DocAnswerExactCapacityAndOneByteOverflow) { RuleMatchBatch{{0, 0, RuleMatchItem(1, "QA", "", 0.9f)}}); context.Publish(kDocChunkCounts, Int32Batch{{0, 0, 3}}); - // Capacities count payload bytes; storage also reserves the terminator. + // 容量按 payload 字节计;存储还需为结束符预留空间。 char answer[6] = {'o', 'l', 'd', '\0', '#', '!'}; char intent[4] = {'?', '?', '?', '!'}; CompanyString answer_string{3, answer}; @@ -871,7 +861,7 @@ TEST_F(AdapterPurityTest, InputBatchSkeleton_CopyInPurity) { ASSERT_EQ(harness.DecodeOperator({&in}), 0); - // Overwrite external buffer + // 覆写外部缓冲区 buffer[11] = 'X'; buffer[12] = 'X'; @@ -940,7 +930,7 @@ TEST_F(AdapterPurityTest, InputBatchSkeleton_AllSamplesValidatedBeforePublish) { EXPECT_EQ(harness.DecodeOperator({&in0, &in1}), COMPANY_ALG_ERR_INVALID_INPUT); - // Neither the request id table nor business values may be published. + // 请求 ID 表和业务值都不得发布。 EXPECT_TRUE(harness.RequestIds().empty()); EXPECT_EQ(harness.Context().Read("input_sentences"), nullptr); } @@ -957,7 +947,7 @@ TEST_F(AdapterPurityTest, DocQaAdapter_MultiWayResultsReorderedAndPerturbed) { harness.SetRequestIds(std::vector{1001, 2002}); - // Perturbed order: index 1 published before index 0 + // 打乱顺序:索引 1 先于索引 0 发布 TextBatch answers{{1, 0, "Answer 1"}, {0, 0, "Answer 0"}}; RuleMatchBatch intents{{0, 0, RuleMatchItem(1, "INTENT_0", "", 0.9f)}, {1, 0, RuleMatchItem(2, "INTENT_1", "", 0.8f)}}; @@ -986,11 +976,11 @@ TEST_F(AdapterPurityTest, DocQaAdapter_MultiWayResultsReorderedAndPerturbed) { } // ========================================================================= -// 3. Section 13.1 Independent Reuse Proofs +// 3. 独立复用证明 (第 13.1 节) // ========================================================================= -// Proof 1: Input Converters Match Biz Declared Host Types & Prove Reuse via -// Test Binding +// 证明 1:输入 Converter 与业务声明的宿主类型匹配, +// 并通过测试 binding 证明可复用 TEST_F(AdapterPurityTest, ReuseProof_1_InputConverterReusedAcrossBindings) { const auto* entity_binding = IoBindingRegistry::Instance().FindBinding("entity_extract.operator.v1"); @@ -1012,7 +1002,7 @@ TEST_F(AdapterPurityTest, ReuseProof_1_InputConverterReusedAcrossBindings) { ASSERT_NE(keyword_conv, nullptr); EXPECT_EQ(keyword_conv->external_type, "CompanyOperatorKeywordInput"); - // Decode input with entity binding + // 用 entity binding 解码输入 { test::AdapterHarness harness( entity_conv, @@ -1029,7 +1019,7 @@ TEST_F(AdapterPurityTest, ReuseProof_1_InputConverterReusedAcrossBindings) { EXPECT_EQ((*sentences)[0].data, "entity sentence"); } - // Decode input with keyword binding + // 用 keyword binding 解码输入 { test::AdapterHarness harness( keyword_conv, @@ -1066,7 +1056,7 @@ TEST_F(AdapterPurityTest, ReuseProof_1_InputConverterReusedAcrossBindings) { } } -// Proof 2: Output Converter Reused Across Pipelines +// 证明 2:输出 Converter 可跨 Pipeline 复用 TEST_F(AdapterPurityTest, ReuseProof_2_OutputConverterReusedAcrossPipelines) { const auto* out_conv = IoConverterRegistry::Instance().FindOutputConverter( "document.structured.operator.v1"); @@ -1074,7 +1064,7 @@ TEST_F(AdapterPurityTest, ReuseProof_2_OutputConverterReusedAcrossPipelines) { OutputPortBindings bindings({{"extracted_entities", "extracted_entities"}}); - // Context A: Entity Extraction pipeline output + // 上下文 A:实体抽取 Pipeline 的输出 { test::AdapterHarness harness(out_conv, bindings); harness.SetRequestIds(std::vector{9001}); @@ -1092,7 +1082,7 @@ TEST_F(AdapterPurityTest, ReuseProof_2_OutputConverterReusedAcrossPipelines) { EXPECT_STREQ(outputs[0].entities_json->data, "[\"PERSON: Alice\"]"); } - // Context B: Generic structured JSON pipeline output producing same schema + // 上下文 B:产出相同 schema 的通用结构化 JSON Pipeline 输出 { test::AdapterHarness harness(out_conv, bindings); harness.SetRequestIds(std::vector{9002}); @@ -1111,7 +1101,7 @@ TEST_F(AdapterPurityTest, ReuseProof_2_OutputConverterReusedAcrossPipelines) { } } -// Proof 3: Multiple External Input Formats Driving Same Pipeline +// 证明 3:多种外部输入格式驱动同一 Pipeline TEST_F(AdapterPurityTest, ReuseProof_3_MultipleExternalInputFormatsForSamePipeline) { InputConverterDefinition custom_in_def; @@ -1150,7 +1140,7 @@ TEST_F(AdapterPurityTest, EXPECT_TRUE( IoConverterRegistry::Instance().RegisterInputConverter(custom_in_def)); - // Format A: CompanyOperatorEntityInput via text.plain.operator.v1 + // 格式 A:经 text.plain.operator.v1 的 CompanyOperatorEntityInput AlgContext ctx_a; { const auto* in_a = IoConverterRegistry::Instance().FindInputConverter( @@ -1175,7 +1165,7 @@ TEST_F(AdapterPurityTest, ASSERT_EQ(in_a->decode_fn(view, opts, bindings, &ctx_a, &st), 0); } - // Format B: CustomMultiFieldInput via test.multi_field.operator.v1 + // 格式 B:经 test.multi_field.operator.v1 的 CustomMultiFieldInput AlgContext ctx_b; { const auto* in_b = IoConverterRegistry::Instance().FindInputConverter( @@ -1205,9 +1195,9 @@ TEST_F(AdapterPurityTest, EXPECT_EQ((*texts_a)[0].data, "AI: Revolution in robotics"); } -// Proof 4: Independently Switch Output Formats for Same Pipeline +// 证明 4:同一 Pipeline 可独立切换输出格式 TEST_F(AdapterPurityTest, ReuseProof_4_IndependentlySwitchOutputFormat) { - // Binding Output A: document.structured.operator.v1 -> + // 输出 binding A:document.structured.operator.v1 -> // CompanyOperatorEntityOutput { const auto* out_a = IoConverterRegistry::Instance().FindOutputConverter( @@ -1230,7 +1220,7 @@ TEST_F(AdapterPurityTest, ReuseProof_4_IndependentlySwitchOutputFormat) { EXPECT_STREQ(outputs[0].entities_json->data, "[\"item_1\"]"); } - // Binding Output B: keyword.result.operator.v1 -> + // 输出 binding B:keyword.result.operator.v1 -> // CompanyOperatorKeywordOutput { const auto* out_b = IoConverterRegistry::Instance().FindOutputConverter( @@ -1254,7 +1244,7 @@ TEST_F(AdapterPurityTest, ReuseProof_4_IndependentlySwitchOutputFormat) { } } -// Proof 5: Same Carrier with Different Schemas +// 证明 5:同一载体承载不同 schema TEST_F(AdapterPurityTest, ReuseProof_5_SameCarrierDifferentSchema) { const auto* plain_conv = IoConverterRegistry::Instance().FindInputConverter( "text.plain.operator.v1"); @@ -1263,7 +1253,7 @@ TEST_F(AdapterPurityTest, ReuseProof_5_SameCarrierDifferentSchema) { "translate.json.operator.v1"); ASSERT_NE(json_conv, nullptr); - // Payload 1: Pure plain text "Hello plain text" + // Payload 1:纯文本 "Hello plain text" std::string plain_str = "Hello plain text"; CompanyString cs_plain{static_cast(plain_str.size()), plain_str.data()}; @@ -1279,7 +1269,7 @@ TEST_F(AdapterPurityTest, ReuseProof_5_SameCarrierDifferentSchema) { InputDecodeOptions opts; opts.request_ids = &request_ids; - // text.plain.operator.v1 accepts it as plain text + // text.plain.operator.v1 将其作为纯文本接受 { AlgContext ctx; AdapterStatus st; @@ -1290,7 +1280,7 @@ TEST_F(AdapterPurityTest, ReuseProof_5_SameCarrierDifferentSchema) { EXPECT_EQ((*s)[0].data, "Hello plain text"); } - // translate.json.operator.v1 rejects it because it is not JSON + // translate.json.operator.v1 因其不是 JSON 而拒绝 { AlgContext ctx; AdapterStatus st; @@ -1300,7 +1290,7 @@ TEST_F(AdapterPurityTest, ReuseProof_5_SameCarrierDifferentSchema) { EXPECT_EQ(st.FieldPath(), "json"); } - // Payload 2: JSON formatted string "{\"query\": \"Hello JSON\"}" + // Payload 2:JSON 格式字符串 "{\"query\": \"Hello JSON\"}" std::string json_str = "{\"query\": \"Hello JSON\"}"; CompanyString cs_json{static_cast(json_str.size()), json_str.data()}; CompanyOperatorEntityInput json_req{102, &cs_json}; @@ -1309,7 +1299,7 @@ TEST_F(AdapterPurityTest, ReuseProof_5_SameCarrierDifferentSchema) { json_view.slot_types["entity_in"] = "CompanyOperatorEntityInput"; json_view.count = 1; - // translate.json.operator.v1 succeeds and extracts "query" + // translate.json.operator.v1 解析成功并提取 "query" { AlgContext ctx; AdapterStatus st; @@ -1321,7 +1311,7 @@ TEST_F(AdapterPurityTest, ReuseProof_5_SameCarrierDifferentSchema) { } } -// Proof 6: Negative Combinations Rejected +// 证明 6:拒绝非法组合 TEST_F(AdapterPurityTest, ReuseProof_6_NegativeCombinations) { nlohmann::json valid_pipeline = nlohmann::json::array( {{{"id", "node_0_TextRuleMatchNode"}, @@ -1333,7 +1323,7 @@ TEST_F(AdapterPurityTest, ReuseProof_6_NegativeCombinations) { {{"categories", {{"SYSTEM_INIT", nlohmann::json::array({"init"})}}}}}}}); - // 1. Unknown or unregistered io_binding + // 1. 未知或未注册的 io_binding nlohmann::json bad_binding_json = { {"deployment", {{"io", @@ -1353,7 +1343,7 @@ TEST_F(AdapterPurityTest, ReuseProof_6_NegativeCombinations) { EXPECT_NE(error.find("Unknown or unregistered io_binding"), std::string::npos); - // 3. DeploymentIoConfig schema validation rejects invalid / old format + // 3. DeploymentIoConfig schema 校验拒绝非法/旧格式 nlohmann::json invalid_version_json = { {"schema_version", 999}, {"data", @@ -1364,7 +1354,7 @@ TEST_F(AdapterPurityTest, ReuseProof_6_NegativeCombinations) { &error)); EXPECT_NE(error.find("Unknown field"), std::string::npos); - // 4. Operator config with unknown output slot rejected by parity check + // 4. 一致性检查拒绝含未知输出槽位的 Operator 配置 nlohmann::json unknown_out_json = { {"deployment", {{"io", @@ -1378,7 +1368,7 @@ TEST_F(AdapterPurityTest, ReuseProof_6_NegativeCombinations) { EXPECT_NE(error.find("Unknown configured output slot: unknown_slot"), std::string::npos); - // 5. Removed deployment model_paths is rejected as an unknown field + // 5. 已移除的部署字段 model_paths 作为未知字段被拒绝 nlohmann::json unknown_mid_json = { {"deployment", {{"model_paths", {{"non_existent_model", "dummy_path"}}}, @@ -1399,8 +1389,7 @@ TEST_F(AdapterPurityTest, ReuseProof_6_NegativeCombinations) { << "actual error was: " << error; } -// Proof 7: Validation Before Initialization (probe model not loaded on invalid -// binding) +// 证明 7:初始化前校验 (binding 非法时不加载探测模型) TEST_F(AdapterPurityTest, ReuseProof_7_ValidationBeforeInitialization) { operator_api::CreateParam param{}; param.cfg_file_name = "non_existent_path.conf"; diff --git a/tests/unit/adapter/test_complex_converters.cpp b/tests/unit/adapter/test_complex_converters.cpp index a469b10a..07da7042 100644 --- a/tests/unit/adapter/test_complex_converters.cpp +++ b/tests/unit/adapter/test_complex_converters.cpp @@ -56,7 +56,7 @@ TEST_F(ComplexConvertersTest, DocQaOperatorInputAndOutput) { int ret = in_conv->decode_fn(in_view, in_options, in_bindings, &ctx, &status); EXPECT_EQ(ret, COMPANY_ALG_SUCCESS); - // Populate answer context + // 填充答案上下文 TextBatch answers; answers.emplace_back(0, 0, "This is the answer."); ctx.Publish("llm_answers", std::move(answers)); @@ -197,7 +197,7 @@ TEST_F(ComplexConvertersTest, CrossRerankOperatorInputAndOutput) { int ret = in_conv->decode_fn(in_view, in_options, in_bindings, &ctx, &status); EXPECT_EQ(ret, COMPANY_ALG_SUCCESS); - // Setup ranked_results in context + // 在上下文中准备 ranked_results RankedTextBatch ranked; RankedCandidate cand; cand.rank = 1; @@ -263,7 +263,7 @@ TEST_F(ComplexConvertersTest, DialogueAuditOperatorInputAndOutput) { int ret = in_conv->decode_fn(in_view, in_options, in_bindings, &ctx, &status); EXPECT_EQ(ret, COMPANY_ALG_SUCCESS); - // Setup audit results in context + // 在上下文中准备审核结果 StructuredDocumentBatch verdicts; verdicts.emplace_back( 0, 0, @@ -404,7 +404,7 @@ TEST_F(ComplexConvertersTest, OcrInvoiceQaOperatorInputAndOutput) { "invoice_result.plain.operator.v1"); ASSERT_NE(out_conv, nullptr); - // Setup operator input: frame and string + // 准备 Operator 输入:frame 和 string std::string uri_str = "/path/to/invoice.jpg"; CompanyString uri{static_cast(uri_str.size()), const_cast(uri_str.data())}; @@ -435,7 +435,7 @@ TEST_F(ComplexConvertersTest, OcrInvoiceQaOperatorInputAndOutput) { int ret = in_conv->decode_fn(in_view, in_options, in_bindings, &ctx, &status); EXPECT_EQ(ret, COMPANY_ALG_SUCCESS); - // Setup invoice output in AlgContext + // 在 AlgContext 中准备发票输出 StructuredDocumentBatch invoices; JsonDocumentItem doc_item; doc_item.is_valid = true; @@ -450,7 +450,7 @@ TEST_F(ComplexConvertersTest, OcrInvoiceQaOperatorInputAndOutput) { ocr_docs.emplace_back(0, 0, ocr_doc); ctx.Publish("ocr_docs", std::move(ocr_docs)); - // Destination operator od_out + // 目标 Operator od_out CompanyOdOutput od_out{}; std::vector buf(256); CompanyString res_str{255, buf.data()}; @@ -480,7 +480,7 @@ TEST_F(ComplexConvertersTest, OcrInvoiceQaOperatorInputAndOutput) { EXPECT_STREQ(od_out.result_json->data, "{\"total\":123.45}"); } -// ==================== All 8 Businesses Bound ==================== +// ==================== 8 个业务全部绑定 ==================== TEST_F(ComplexConvertersTest, AllEightBusinessesRegistered) { const std::vector expected_biz = { "translate", "entity_extract", "keyword_match", "doc_qa", @@ -502,7 +502,7 @@ TEST_F(ComplexConvertersTest, AllEightBusinessesRegistered) { converters.FindOutputConverter(binding->output_converter_id); ASSERT_NE(input, nullptr) << biz; ASSERT_NE(output, nullptr) << biz; - // Production declares the limit once, on the binding. + // 生产代码只在 binding 上声明一次上限。 EXPECT_EQ(binding->max_batch_size, 64U) << biz; EXPECT_EQ(input->max_batch_size, 0U) << biz; EXPECT_EQ(output->max_batch_size, 0U) << biz; @@ -510,7 +510,7 @@ TEST_F(ComplexConvertersTest, AllEightBusinessesRegistered) { } } -// Only the renamed port is declared; the others map to their own names. +// 只声明改名的端口,其余端口映射到同名键。 TEST_F(ComplexConvertersTest, ComplianceBindingDeclaresOnlyRenamedPort) { const auto* binding = IoBindingRegistry::Instance().FindBinding("dialogue_audit.operator.v1"); diff --git a/tests/unit/adapter/test_io_binding_registry.cpp b/tests/unit/adapter/test_io_binding_registry.cpp index 507a24de..020efd71 100644 --- a/tests/unit/adapter/test_io_binding_registry.cpp +++ b/tests/unit/adapter/test_io_binding_registry.cpp @@ -154,7 +154,7 @@ TEST_F(IoBindingRegistryTest, RegistryConflictsStayRecordedWithoutMessage) { conflicts.Clear(); EXPECT_FALSE(conflicts.HasConflict()); - // Losing the message to an allocation failure must not reopen the registry. + // 因分配失败丢失消息时,注册表不得重新开放。 RegistryConflicts lossy; std::string message = "lost"; bool allocation_failed = false; @@ -300,7 +300,7 @@ TEST_F(IoBindingRegistryTest, CapacityFieldsDeriveFromValueType) { *IoConverterRegistry::Instance().FindOutputConverter("test.out.operator"); IoConverterRegistry::Instance().ClearForTesting(); ASSERT_TRUE(IoConverterRegistry::Instance().RegisterInputConverter(input)); - // Keep the fixture's logical contract and use the registered keyword layout. + // 保留夹具的逻辑契约,并使用已注册的 keyword 布局。 output.external_slots = { ExternalOutputSlot("keyword_out")}; output.external_type = "CompanyOperatorKeywordOutput"; @@ -320,7 +320,7 @@ TEST_F(IoBindingRegistryTest, CapacityFieldsDeriveFromValueType) { unknown.type_suffix = "unregistered"; EXPECT_TRUE(EffectiveCapacityFields(unknown).empty()); - // Explicit and inferred declarations of the same layout are compatible. + // 同一布局的显式声明与推导声明相互兼容。 RegisterTestBizBinding(); auto binding = *IoBindingRegistry::Instance().FindBinding("test_biz.operator.v1"); @@ -969,7 +969,7 @@ TEST_F(IoBindingRegistryTest, ModelStructureInvalidRejected_T04) { PreparedDeployment prepared; DeploymentDiagnostic diag; - // Case 1: models is not an array (e.g. object) + // 情形 1:models 不是数组 (如对象) { nlohmann::json doc = { {"deployment", @@ -983,7 +983,7 @@ TEST_F(IoBindingRegistryTest, ModelStructureInvalidRejected_T04) { EXPECT_EQ(diag.path, "/models"); } - // Case 2: model_path is required in every model entry. + // 情形 2:每个模型条目都必须有 model_path。 { nlohmann::json doc = { {"deployment", @@ -1000,7 +1000,7 @@ TEST_F(IoBindingRegistryTest, ModelStructureInvalidRejected_T04) { EXPECT_EQ(diag.path, "/models/0/model_path"); } - // Case 3: Duplicate model_id + // 情形 3:model_id 重复 { nlohmann::json doc = { {"deployment", @@ -1273,7 +1273,7 @@ TEST_F(IoBindingRegistryTest, PrepareDeploymentDocument(document, {}, &prepared, &diagnostic)); EXPECT_EQ(diagnostic.code, "BIZ_IO_CONTRACT_MISMATCH"); - // The public preflight must enforce the carrier contract without Init. + // 公开预检必须在不执行 Init 的情况下强制载体契约。 std::ofstream(root / "pipeline.json") << document; std::ofstream(root / "pipeline.conf") << nlohmann::json{{"pipe_path", "pipeline.json"}}; @@ -1289,7 +1289,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(dummy_decode_calls, decoded_before); EXPECT_EQ(dummy_encode_calls, encoded_before); - // A different business has its own carrier contract and is not poisoned. + // 其他业务有自己的载体契约,不受影响。 auto independent = *PipelineCatalog::FindBiz("test_biz_v1"); independent.biz_name = incompatible_input ? "separate_input_biz" : "separate_output_biz"; @@ -1375,7 +1375,7 @@ TEST_F(IoBindingRegistryTest, } TEST_F(IoBindingRegistryTest, EffectiveBatchLimitIncludesBindingBound) { - // Zero means that source adds no bound; the smallest positive value wins. + // 0 表示该来源不设上限;取最小的正值。 struct Limits { size_t binding; size_t input; @@ -1492,7 +1492,7 @@ TEST_F(IoBindingRegistryTest, DeploymentIoUnknownBindingOrMismatch_T07) { PreparedDeployment prepared; DeploymentDiagnostic diag; - // Case 1: Unknown binding + // 情形 1:未知 binding { nlohmann::json doc = base_doc; doc["deployment"]["io"]["io_binding"] = "completely_unknown_binding"; @@ -1501,8 +1501,7 @@ TEST_F(IoBindingRegistryTest, DeploymentIoUnknownBindingOrMismatch_T07) { EXPECT_EQ(diag.path, "/deployment/io/io_binding"); } - // Case 2: Removed root business identity is rejected even when the binding - // exists. + // 情形 2:即使 binding 存在,已移除的根业务标识也会被拒绝。 { nlohmann::json doc = base_doc; doc["biz_name"] = "other_biz_v1"; @@ -1533,7 +1532,7 @@ TEST_F(IoBindingRegistryTest, DeploymentIoSlotValidation_T08) { PreparedDeployment prepared; DeploymentDiagnostic diag; - // Case 1: Missing required slot receives its registered defaults. + // 情形 1:缺少的必填槽位取其注册的默认值。 { nlohmann::json doc = base_doc; doc["deployment"]["io"]["out_mem"].erase("entity_out"); @@ -1542,7 +1541,7 @@ TEST_F(IoBindingRegistryTest, DeploymentIoSlotValidation_T08) { EXPECT_EQ(prepared.output_specs.count("entity_out"), 1U); } - // Case 2: Unknown slot in out_mem + // 情形 2:out_mem 中有未知槽位 { nlohmann::json doc = base_doc; doc["deployment"]["io"]["out_mem"]["unexpected_extra_slot"] = @@ -1552,7 +1551,7 @@ TEST_F(IoBindingRegistryTest, DeploymentIoSlotValidation_T08) { EXPECT_EQ(diag.path, "/deployment/io/out_mem/unexpected_extra_slot"); } - // Case 3: The removed type field is rejected even if it matches the slot. + // 情形 3:已移除的 type 字段即使与槽位一致也会被拒绝。 { nlohmann::json doc = base_doc; doc["deployment"]["io"]["out_mem"]["entity_out"]["type"] = "entity_out"; @@ -1590,7 +1589,7 @@ TEST_F(IoBindingRegistryTest, PrepareFailureResetsPreparedStateAtomically_T18) { PreparedDeployment prepared; DeploymentDiagnostic diag; - // 1. Initial successful preparation + // 1. 首次准备成功 ASSERT_TRUE(PrepareDeploymentDocument(valid_doc, options, &prepared, &diag)); EXPECT_FALSE(prepared.binding.binding_id.empty()); EXPECT_NE(prepared.input_converter, nullptr); @@ -1598,7 +1597,7 @@ TEST_F(IoBindingRegistryTest, PrepareFailureResetsPreparedStateAtomically_T18) { EXPECT_FALSE(prepared.neutral_pipeline_json.is_null()); EXPECT_FALSE(prepared.output_specs.empty()); - // 2. Reusing the SAME prepared instance on a failing document + // 2. 在失败的文档上复用同一个 prepared 实例 nlohmann::json invalid_doc = valid_doc; invalid_doc["deployment"]["io"]["io_binding"] = "unknown.binding"; const nlohmann::json invalid_doc_copy = invalid_doc; @@ -1606,7 +1605,7 @@ TEST_F(IoBindingRegistryTest, PrepareFailureResetsPreparedStateAtomically_T18) { EXPECT_FALSE( PrepareDeploymentDocument(invalid_doc, options, &prepared, &diag)); - // Verify all fields of prepared are completely reset + // 验证 prepared 的所有字段都已完全重置 EXPECT_TRUE(prepared.binding.binding_id.empty()); EXPECT_EQ(prepared.input_converter, nullptr); EXPECT_EQ(prepared.output_converter, nullptr); @@ -1619,10 +1618,10 @@ TEST_F(IoBindingRegistryTest, PrepareFailureResetsPreparedStateAtomically_T18) { EXPECT_TRUE(prepared.io_boundary.input_published_ports.empty()); EXPECT_TRUE(prepared.io_boundary.output_consumed_ports.empty()); - // Verify the input document was NOT mutated + // 验证输入文档未被修改 EXPECT_EQ(invalid_doc, invalid_doc_copy); - // Failure after resolving the first model must not publish partial state. + // 解析完第一个模型后失败时,不得发布部分状态。 valid_doc["models"].push_back(valid_doc["models"][0]); valid_doc["models"][1]["model_id"] = "mid_2"; valid_doc["models"][1]["model_path"] = "models/second.bin"; @@ -1772,7 +1771,7 @@ TEST_F(IoBindingRegistryTest, ResolveFromPipelineJsonDiagnosticCarrier_T03_T06_T07_T08) { RegisterTestBizBinding(); - // T03: Missing direct model path through IoBindingResolver + // T03:经 IoBindingResolver 缺少直接模型路径 nlohmann::json t03_doc = { {"deployment", {{"io", @@ -1800,7 +1799,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.code, "MISSING_FIELD"); EXPECT_EQ(diag.path, "/models/0/model_path"); - // T06: Removed deployment model_paths field + // T06:已移除的部署字段 model_paths nlohmann::json t06_doc = t03_doc; t06_doc["models"][0]["model_path"] = "models/original.bin"; t06_doc["deployment"]["model_paths"] = {{"unknown_mid", "models/foo.bin"}}; @@ -1811,7 +1810,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.code, "DEPLOYMENT_ERROR"); EXPECT_EQ(diag.path, "/deployment/model_paths"); - // T07: Unknown binding through IoBindingResolver + // T07:经 IoBindingResolver 的未知 binding nlohmann::json t07_doc = t03_doc; t07_doc["models"][0]["model_path"] = "models/original.bin"; t07_doc["deployment"]["io"]["io_binding"] = "nonexistent.binding"; @@ -1822,7 +1821,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.code, "UNKNOWN_IO_BINDING"); EXPECT_EQ(diag.path, "/deployment/io/io_binding"); - // T08: An invalid output allocation retains its precise diagnostic + // T08:非法的输出分配保留其精确诊断 nlohmann::json t08_doc = t03_doc; t08_doc["models"][0]["model_path"] = "models/original.bin"; t08_doc["deployment"]["io"]["out_mem"]["entity_out"] = 42; @@ -1887,14 +1886,14 @@ TEST_F(IoBindingRegistryTest, std::string err; DeploymentDiagnostic diag; - // 1. Success case + // 1. 成功情形 int rc = IoBindingResolver::ResolveFromFile(conf_path.string(), "", &plan, &err, &diag); EXPECT_EQ(rc, 0); EXPECT_NE(plan, nullptr); EXPECT_TRUE(diag.code.empty()); - // 2. T03 via file: missing model_path + // 2. 经文件的 T03:缺少 model_path { nlohmann::json t03_pipe = base_pipeline; t03_pipe["models"] = { @@ -1913,7 +1912,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.path, "/models/0/model_path"); } - // 3. T06 via file: removed deployment model_paths field + // 3. 经文件的 T06:已移除的部署字段 model_paths { nlohmann::json t06_pipe = base_pipeline; t06_pipe["deployment"]["model_paths"] = {{"unknown_mid", "models/foo.bin"}}; @@ -1927,7 +1926,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.path, "/deployment/model_paths"); } - // 4. T07 via file: unknown io_binding + // 4. 经文件的 T07:未知 io_binding { nlohmann::json t07_pipe = base_pipeline; t07_pipe["deployment"]["io"]["io_binding"] = "unregistered.binding"; @@ -1941,7 +1940,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.path, "/deployment/io/io_binding"); } - // 5. T08 via file: invalid output allocation + // 5. 经文件的 T08:非法的输出分配 { nlohmann::json t08_pipe = base_pipeline; t08_pipe["deployment"]["io"]["out_mem"]["entity_out"] = 42; @@ -1955,10 +1954,10 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.path, "/deployment/io/out_mem/entity_out"); } - // Restore valid pipeline file + // 恢复合法的 Pipeline 文件 write_file(pipe_path, base_pipeline); - // 6. Non-existent conf file -> CONFIG_FILE_OPEN + // 6. conf 文件不存在 -> CONFIG_FILE_OPEN rc = IoBindingResolver::ResolveFromFile( (temp_dir / "nonexistent.conf").string(), "", &plan, &err, &diag); EXPECT_EQ(rc, -2); @@ -1966,7 +1965,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.code, "CONFIG_FILE_OPEN"); EXPECT_EQ(diag.path, "/"); - // 7. Malformed JSON in conf file -> JSON_PARSE + // 7. conf 文件 JSON 格式错误 -> JSON_PARSE { fs::path bad_conf = temp_dir / "bad_syntax.conf"; write_raw(bad_conf, "{ unquoted: invalid JSON ..."); @@ -1978,8 +1977,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.path, "/"); } - // 8. Missing pipeline file referenced by conf -> DEPLOYMENT_ERROR at - // /pipe_path + // 8. conf 引用的 Pipeline 文件缺失 -> /pipe_path 处 DEPLOYMENT_ERROR { fs::path missing_pipe_conf = temp_dir / "missing_pipe.conf"; write_file(missing_pipe_conf, {{"pipe_path", "missing_pipeline.json"}}); @@ -1991,7 +1989,7 @@ TEST_F(IoBindingRegistryTest, EXPECT_EQ(diag.path, "/pipe_path"); } - // 9. Malformed JSON in pipeline file -> JSON_PARSE + // 9. Pipeline 文件 JSON 格式错误 -> JSON_PARSE { fs::path bad_pipe = temp_dir / "bad_pipe.json"; write_raw(bad_pipe, "{ bad_pipe_json: invalid"); diff --git a/tests/unit/adapter/test_io_converters.cpp b/tests/unit/adapter/test_io_converters.cpp index 0ff692c0..af71b0bc 100644 --- a/tests/unit/adapter/test_io_converters.cpp +++ b/tests/unit/adapter/test_io_converters.cpp @@ -87,8 +87,8 @@ TEST(IoConverterTest, ViewAccessorsAndPortBindings) { EXPECT_FALSE(out_bindings.HasKey("unknown")); EXPECT_STREQ(out_bindings.Key("unknown").name, ""); - // Shared lookup keeps the direction in the type: decoders cannot receive - // output bindings and encoders cannot receive input bindings. + // 共享查找把方向编码进类型:解码器不能接收输出 binding, + // 编码器不能接收输入 binding。 static_assert(!std::is_same_v); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); @@ -161,7 +161,7 @@ TEST(IoConverterTest, ExternalTypeDefaultsToSlotTypesInOrder) { ->external_type, "CompanyFrame,CompanyString"); - // An explicit protocol label is kept as declared. + // 显式的协议标签按声明保留。 input.converter_id = "labeled.external_type.in"; input.external_type = "custom.carrier"; ASSERT_TRUE(IoConverterRegistry::Instance().RegisterInputConverter(input)); @@ -234,7 +234,7 @@ TEST(IoConverterTest, RejectsInvalidDefinitions) { } TEST(IoConverterTest, ExactSlotLookupAndTypeSafety) { - // 1. shared_ptr (slots) path + // 1. shared_ptr (slots) 路径 ExternalInputBatchView v; v.count = 1; @@ -242,11 +242,11 @@ TEST(IoConverterTest, ExactSlotLookupAndTypeSafety) { v.slots["channel.slot"] = {b_ptr}; v.slot_types["channel.slot"] = "ReproB"; - // Exact lookup with correct type succeeds + // 类型正确的精确查找成功 EXPECT_EQ(v.GetSlot("channel.slot", 0), b_ptr.get()); - // Exact lookup with wrong type returns nullptr + // 类型错误的精确查找返回 nullptr EXPECT_EQ(v.GetSlot("channel.slot", 0), nullptr); - // Short suffix lookup must NOT fallback: returns nullptr + // 短后缀查找不得回退:返回 nullptr EXPECT_EQ(v.GetSlot("slot", 0), nullptr); EXPECT_EQ(v.GetSlot("slot", 0), nullptr); @@ -266,7 +266,7 @@ TEST(IoConverterTest, ExactSlotLookupAndTypeSafety) { EXPECT_EQ(output.GetSlotCapacity("channel.slot", "bytes"), 512U); EXPECT_EQ(output.GetSlotCapacity("slot", "bytes", 7), 7U); - // 3. Multi-slot ambiguity resolution: distinct logical slots of same type + // 3. 多槽位歧义消解:同一类型的不同逻辑槽位 ExternalInputBatchView multi; multi.count = 1; auto left = std::make_shared(); @@ -289,7 +289,7 @@ TEST(IoConverterTest, RejectsEmptyTypeSuffixAndSupportsMultipleSlotsOfSameType) { auto& reg = IoConverterRegistry::Instance(); - // 1. Input converter with empty type_suffix must be rejected + // 1. type_suffix 为空的输入 Converter 必须被拒绝 InputConverterDefinition bad_in; bad_in.converter_id = "test.empty_suffix.in"; @@ -305,7 +305,7 @@ TEST(IoConverterTest, EXPECT_FALSE(reg.RegisterInputConverter(bad_in)); - // 2. Output converter with empty type_suffix must be rejected + // 2. type_suffix 为空的输出 Converter 必须被拒绝 OutputConverterDefinition bad_out; bad_out.converter_id = "test.empty_suffix.out"; @@ -321,8 +321,7 @@ TEST(IoConverterTest, EXPECT_FALSE(reg.RegisterOutputConverter(bad_out)); - // 3. Multiple slots of same ValueType with distinct slot names can register - // successfully + // 3. 同一 ValueType 的多个槽位使用不同槽位名时可以注册成功 InputConverterDefinition multi_in; multi_in.converter_id = "test.multi_slot.in"; @@ -698,7 +697,7 @@ TEST(IoConverterTest, DecodeRowsUsesEffectiveBatchLimitFromOptions) { std::string::npos); EXPECT_FALSE(limited.Has("actual_texts")); - // Zero means the caller did not pass a limit; only emptiness is checked. + // 0 表示调用方未传上限;只检查是否为空。 AlgContext unlimited; AdapterStatus unlimited_status; EXPECT_EQ(decode(0, &unlimited, &unlimited_status), COMPANY_ALG_SUCCESS); diff --git a/tests/unit/adapter/test_text_converters.cpp b/tests/unit/adapter/test_text_converters.cpp index 9f1a1bb9..c8373698 100644 --- a/tests/unit/adapter/test_text_converters.cpp +++ b/tests/unit/adapter/test_text_converters.cpp @@ -66,7 +66,7 @@ TEST_F(TextConvertersTest, TranslateJsonInputDecodeValidAndInvalid) { ASSERT_NE(conv, nullptr); ASSERT_NE(conv->decode_fn, nullptr); - // 1. Valid JSON with query field + // 1. 含 query 字段的合法 JSON std::string valid_json = "{\"query\": \"Translate me!\", \"lang\": \"en\"}"; CompanyString cs_valid{static_cast(valid_json.size()), const_cast(valid_json.data())}; @@ -93,7 +93,7 @@ TEST_F(TextConvertersTest, TranslateJsonInputDecodeValidAndInvalid) { ASSERT_EQ(sentences->size(), 1U); EXPECT_EQ((*sentences)[0].data, "Translate me!"); - // 2. Invalid JSON without query + // 2. 不含 query 的非法 JSON std::string invalid_json = "{\"text\": \"No query field\"}"; CompanyString cs_invalid{static_cast(invalid_json.size()), const_cast(invalid_json.data())}; @@ -163,22 +163,22 @@ TEST_F(TextConvertersTest, ProductionBindingsUseDeclaredHostTypes) { IoBindingRegistry::Instance().FindBinding("keyword_match.operator.v1"); ASSERT_NE(keyword_binding, nullptr); - // Entity extract uses CompanyOperatorEntityInput via text.plain.operator.v1 + // 实体抽取经 text.plain.operator.v1 使用 CompanyOperatorEntityInput EXPECT_EQ(entity_binding->input_converter_id, "text.plain.operator.v1"); const auto* entity_conv = IoConverterRegistry::Instance().FindInputConverter( entity_binding->input_converter_id); ASSERT_NE(entity_conv, nullptr); EXPECT_EQ(entity_conv->external_type, "CompanyOperatorEntityInput"); - // Keyword match uses CompanyOperatorKeywordInput via - // keyword.plain.operator.v1 + // 关键词匹配经 keyword.plain.operator.v1 使用 + // CompanyOperatorKeywordInput EXPECT_EQ(keyword_binding->input_converter_id, "keyword.plain.operator.v1"); const auto* keyword_conv = IoConverterRegistry::Instance().FindInputConverter( keyword_binding->input_converter_id); ASSERT_NE(keyword_conv, nullptr); EXPECT_EQ(keyword_conv->external_type, "CompanyOperatorKeywordInput"); - // Output converters are distinct + // 输出 Converter 互不相同 EXPECT_EQ(entity_binding->output_converter_id, "document.structured.operator.v1"); EXPECT_EQ(keyword_binding->output_converter_id, "keyword.result.operator.v1"); diff --git a/tests/unit/core/test_dag_pipeline.cpp b/tests/unit/core/test_dag_pipeline.cpp index b4c57354..1babcf22 100644 --- a/tests/unit/core/test_dag_pipeline.cpp +++ b/tests/unit/core/test_dag_pipeline.cpp @@ -522,10 +522,10 @@ TEST_F(DagPipelineTest, ParallelWavefrontExecution) { const auto& layers = pipeline.GetTopologicalLayers(); ASSERT_EQ(layers.size(), 3); - EXPECT_EQ(layers[0].size(), 1); // Layer 0: node_a + EXPECT_EQ(layers[0].size(), 1); // 第 0 层:node_a EXPECT_EQ(layers[1].size(), - 2); // Layer 1: node_b, node_c (Parallel Wavefront) - EXPECT_EQ(layers[2].size(), 1); // Layer 2: node_d + 2); // 第 1 层:node_b、node_c (并行波前) + EXPECT_EQ(layers[2].size(), 1); // 第 2 层:node_d AlgContext req_ctx; ResetExecutionTrace(); diff --git a/tests/unit/core/test_definition_schema_validation.cpp b/tests/unit/core/test_definition_schema_validation.cpp index cca0dccf..ec690261 100644 --- a/tests/unit/core/test_definition_schema_validation.cpp +++ b/tests/unit/core/test_definition_schema_validation.cpp @@ -193,7 +193,7 @@ BackendDefinition MakeSchemaProbeBackendDefinition() { ++SchemaProbeBackend::s_validate_count; SchemaProbeBackend::s_validated_config = config; const int device_id = config.at("device_id").get(); - // Exercise both exception barriers using otherwise valid field values. + // 使用其余均合法的字段值覆盖两种异常屏障。 if (device_id == 15) throw std::runtime_error("probe validation exception"); if (device_id == 16) throw 16; if (device_id == 0 && config.at("precision") == "int8") { @@ -463,7 +463,7 @@ TEST_F(DefinitionSchemaValidationTest, ValidationFailureHasZeroSideEffects) { } TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { - // 1. Duplicate field names + // 1. 字段名重复 NodeDefinition dup_field_def; dup_field_def.node_type = "InvalidDupFieldNode"; dup_field_def.config_fields = { @@ -481,7 +481,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(invalid_range_def)); - // 3. Default value kind mismatch + // 3. 默认值类型不匹配 NodeDefinition default_mismatch_def; default_mismatch_def.node_type = "DefaultMismatchNode"; default_mismatch_def.config_fields = { @@ -490,7 +490,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(default_mismatch_def)); - // 4. Default value not in enum + // 4. 默认值不在 enum 中 NodeDefinition enum_mismatch_def; enum_mismatch_def.node_type = "EnumMismatchNode"; enum_mismatch_def.config_fields = { @@ -504,7 +504,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(enum_mismatch_def)); - // 5. Duplicate enum values + // 5. enum 值重复 NodeDefinition dup_enum_def; dup_enum_def.node_type = "DupEnumNode"; dup_enum_def.config_fields = { @@ -518,7 +518,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(dup_enum_def)); - // 6. Non-numeric field carrying minimum/maximum (CR-005) + // 6. 非数值字段带有 minimum/maximum NodeDefinition string_range_def; string_range_def.node_type = "StringRangeNode"; string_range_def.config_fields = { @@ -535,7 +535,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(bool_range_def)); - // 7. Node declares model_dependencies with empty config_field + // 7. Node 声明了 model_dependencies,但 config_field 为空 NodeDefinition missing_model_field_def; missing_model_field_def.node_type = "MissingModelFieldNode"; missing_model_field_def.model_dependencies = {{"generator", "llm", ""}}; @@ -544,7 +544,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(missing_model_field_def)); - // 8. Node declares model_dependencies but field not in config_fields + // 8. Node 声明了 model_dependencies,但字段不在 config_fields 中 NodeDefinition unlisted_model_field_def; unlisted_model_field_def.node_type = "UnlistedModelFieldNode"; unlisted_model_field_def.model_dependencies = { @@ -554,7 +554,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(unlisted_model_field_def)); - // 9. Node declares model_dependencies but config_field is not string + // 9. Node 声明了 model_dependencies,但 config_field 不是 string NodeDefinition nonstring_model_field_def; nonstring_model_field_def.node_type = "NonStringModelFieldNode"; nonstring_model_field_def.model_dependencies = { @@ -564,7 +564,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(nonstring_model_field_def)); - // 9b. Node declares duplicate slot name or duplicate config field + // 9b. Node 声明了重复的槽位名或重复的配置字段 NodeDefinition dup_slot_def; dup_slot_def.node_type = "DupSlotNode"; dup_slot_def.model_dependencies = { @@ -588,7 +588,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { }; EXPECT_FALSE(ValidateNodeDefinitionStructure(dup_dep_field_def)); - // 10. Port constraints referencing undeclared ports + // 10. 端口约束引用了未声明的端口 NodeDefinition invalid_constraint_def; invalid_constraint_def.node_type = "InvalidConstraintNode"; invalid_constraint_def.inputs = { @@ -598,15 +598,14 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidDefinitionAtRegistration) { std::vector{"text", "unknown_port"})}; EXPECT_FALSE(ValidateNodeDefinitionStructure(invalid_constraint_def)); - // 11. Invalid control command definition + // 11. 非法的 Control 命令定义 NodeDefinition invalid_cmd_def; invalid_cmd_def.node_type = "InvalidCmdNode"; invalid_cmd_def.control_commands = { ControlCommandDefinition(0, "invalid_cmd")}; // id <= 0 EXPECT_FALSE(ValidateNodeDefinitionStructure(invalid_cmd_def)); - // 12. A dynamic lifetime must reference a declared string enum containing - // only framework lifetimes. + // 12. 动态 lifetime 必须引用已声明的 string enum,且其中只含框架 lifetime。 NodeDefinition invalid_lifetime_override; invalid_lifetime_override.node_type = "InvalidLifetimeOverrideNode"; invalid_lifetime_override.inputs = {NodePortDefinition{ @@ -898,42 +897,42 @@ TEST_F(DefinitionSchemaValidationTest, ProductionCatalogSelfCheck) { } TEST_F(DefinitionSchemaValidationTest, RejectsInvalidNodePortDefinitions) { - // Empty key + // 空键 NodeDefinition empty_key_node; empty_key_node.node_type = "EmptyKeyPortNode"; empty_key_node.inputs = { NodePortDefinition{"", "TextBatch", true, "1:1", "preserve", "request"}}; EXPECT_FALSE(ValidateNodeDefinitionStructure(empty_key_node)); - // Empty type_id + // 空 type_id NodeDefinition empty_type_node; empty_type_node.node_type = "EmptyTypePortNode"; empty_type_node.inputs = { NodePortDefinition{"text", "", true, "1:1", "preserve", "request"}}; EXPECT_FALSE(ValidateNodeDefinitionStructure(empty_type_node)); - // Invalid cardinality + // 非法 cardinality NodeDefinition invalid_card_node; invalid_card_node.node_type = "InvalidCardPortNode"; invalid_card_node.inputs = {NodePortDefinition{"text", "TextBatch", true, "3:3", "preserve", "request"}}; EXPECT_FALSE(ValidateNodeDefinitionStructure(invalid_card_node)); - // Invalid provenance + // 非法 provenance NodeDefinition invalid_prov_node; invalid_prov_node.node_type = "InvalidProvPortNode"; invalid_prov_node.inputs = { NodePortDefinition{"text", "TextBatch", true, "1:1", "magic", "request"}}; EXPECT_FALSE(ValidateNodeDefinitionStructure(invalid_prov_node)); - // Invalid lifetime + // 非法 lifetime NodeDefinition invalid_life_node; invalid_life_node.node_type = "InvalidLifePortNode"; invalid_life_node.inputs = {NodePortDefinition{"text", "TextBatch", true, "1:1", "preserve", "eternal"}}; EXPECT_FALSE(ValidateNodeDefinitionStructure(invalid_life_node)); - // Duplicate input port key + // 输入端口键重复 NodeDefinition dup_key_node; dup_key_node.node_type = "DupKeyPortNode"; dup_key_node.inputs = {NodePortDefinition{"text", "TextBatch", true, "1:1", @@ -942,7 +941,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsInvalidNodePortDefinitions) { "preserve", "request"}}; EXPECT_FALSE(ValidateNodeDefinitionStructure(dup_key_node)); - // Biz definition with invalid port + // 含非法端口的 Biz 定义 BizDefinition invalid_biz; invalid_biz.biz_name = "invalid_port_biz"; invalid_biz.ingress = { @@ -973,7 +972,7 @@ TEST_F(DefinitionSchemaValidationTest, RejectsNonIntegerFloatsForIntegerField) { TEST_F(DefinitionSchemaValidationTest, ValidateConfigExceptionMappingAndShortCircuit) { - // Case 1: Field validation fails -> validate_config must NOT be called + // 情形 1:字段校验失败 -> 不得调用 validate_config ThrowingValidateConfigNode::s_called = false; ThrowingValidateConfigNode::s_throw_mode = 1; nlohmann::json pipeline_field_fail = { @@ -988,8 +987,8 @@ TEST_F(DefinitionSchemaValidationTest, EXPECT_FALSE(plan1.report.ok); EXPECT_FALSE(ThrowingValidateConfigNode::s_called); - // Case 2: Field validation passes, validate_config throws std::runtime_error - // -> mapped to kInvalidCombination + // 情形 2:字段校验通过,validate_config 抛出 std::runtime_error + // -> 映射为 kInvalidCombination ThrowingValidateConfigNode::s_called = false; ThrowingValidateConfigNode::s_throw_mode = 1; nlohmann::json pipeline_std_throw = { @@ -1012,8 +1011,8 @@ TEST_F(DefinitionSchemaValidationTest, EXPECT_NE(it2->message.find("simulated config validation crash"), std::string::npos); - // Case 3: Field validation passes, validate_config throws non-std exception - // -> mapped to kInvalidCombination + // 情形 3:字段校验通过,validate_config 抛出非 std 异常 + // -> 映射为 kInvalidCombination ThrowingValidateConfigNode::s_called = false; ThrowingValidateConfigNode::s_throw_mode = 2; auto plan3 = PipelineValidator::ValidateAndPlan(pipeline_std_throw); diff --git a/tests/unit/core/test_framework_core.cpp b/tests/unit/core/test_framework_core.cpp index ae18c2de..c897ba27 100644 --- a/tests/unit/core/test_framework_core.cpp +++ b/tests/unit/core/test_framework_core.cpp @@ -40,7 +40,7 @@ TEST(PipelineTest, ThreadPoolConstructionFailureCleansUpAndAllowsRetry) { failure.DisableFailure(); } EXPECT_EQ(threw, triggered) << "allocation position " << fail_after; - // A new pool must still execute work after every failed construction. + // 每次构造失败后,新建的线程池仍必须能执行任务。 ThreadPool recovered(2); EXPECT_EQ(recovered.Submit([] { return 42; }).get(), 42); if (!triggered) { @@ -175,8 +175,7 @@ TEST(SessionContextTest, SingleFlightSharesFactoryFailureAndAllowsRetry) { context.GetOrCreateResource( key, [&]() -> std::shared_ptr { ++factory_calls; - // Keep the flight open long enough for concurrent callers to - // join. + // 让 flight 保持足够长的时间,以便并发调用方加入。 std::this_thread::sleep_for(std::chrono::milliseconds(50)); throw std::runtime_error("resource factory unavailable"); }); @@ -192,8 +191,7 @@ TEST(SessionContextTest, SingleFlightSharesFactoryFailureAndAllowsRetry) { std::future_status::ready); EXPECT_EQ(worker.get(), "resource factory unavailable"); } - // At least one caller received the exception as a waiter, not a factory - // owner. + // 至少有一个调用方是作为等待者 (而非工厂所有者) 收到异常的。 EXPECT_LT(factory_calls.load(), 8); EXPECT_EQ(context.GetResource(key), nullptr); auto recovered = context.GetOrCreateResource(key, [&] { diff --git a/tests/unit/core/test_node_base_contracts.cpp b/tests/unit/core/test_node_base_contracts.cpp index 4ca08484..ce7e991a 100644 --- a/tests/unit/core/test_node_base_contracts.cpp +++ b/tests/unit/core/test_node_base_contracts.cpp @@ -177,7 +177,7 @@ TEST(NodeErrorCodesTest, ActiveBusinessAndAuthoringErrorsAreDistinct) { codes.size()); } -// 1. Exception Test Node +// 1. 抛异常的测试 Node class ExceptionThrowingNode : public NodeBase { public: inline static constexpr char kNodeType[] = "ExceptionThrowingNode"; @@ -238,7 +238,7 @@ TEST(NodeBaseContractsTest, InitAndProcessExceptionSafety) { std::string::npos); } -// 2. Require / Publish Helper Test +// 2. Require / Publish 辅助函数测试 inline constexpr BlackboardKey kTestInputKey{"test_input_key", "string"}; inline constexpr BlackboardKey kTestOutputKey{"test_output_key", @@ -265,7 +265,7 @@ TEST(NodeBaseContractsTest, RequireAndPublishHelpers) { plan.normalized_config = nlohmann::json::object(); ASSERT_TRUE(node.Init({&plan, &session_ctx})); - // Missing input key + // 缺少输入键 { AlgContext ctx; int ret = node.Process(&ctx); @@ -275,7 +275,7 @@ TEST(NodeBaseContractsTest, RequireAndPublishHelpers) { std::string::npos); } - // Success path + // 成功路径 { AlgContext ctx; ctx.Publish(kTestInputKey, std::string("hello")); @@ -285,8 +285,7 @@ TEST(NodeBaseContractsTest, RequireAndPublishHelpers) { ASSERT_NE(out_val, nullptr); EXPECT_EQ(*out_val, "hello_processed"); - // A second producer invocation in the same request cannot silently - // overwrite the already published output. + // 同一请求内第二次调用生产者,不能静默覆盖已发布的输出。 EXPECT_EQ(node.Process(&ctx), static_cast(NodeRuntimeCode::kUnhandledException)); EXPECT_EQ(*out_val, "hello_processed"); @@ -294,7 +293,7 @@ TEST(NodeBaseContractsTest, RequireAndPublishHelpers) { std::string::npos); } - // Existing key with an incompatible runtime type. + // 键已存在但运行时类型不兼容。 { AlgContext ctx; ctx.Publish(std::string(kTestInputKey.name), 42); @@ -459,8 +458,7 @@ TEST(NodeBaseContractsTest, OutputBindingRejectsMissingKeysBeforeTypeMismatch) { SCOPED_TRACE(fault); ValidatedNodePlan plan; plan.normalized_config = nlohmann::json::object(); - // The runtime base permits unbound inputs; the author contract validates - // required inputs. + // 运行时基类允许输入不绑定;必需输入由编写契约校验。 plan.ports.push_back({"optional", "", "integer", "1:1", "preserve", "request", PortDirection::kInput}); if (fault != 0) { @@ -484,7 +482,7 @@ TEST(NodeBaseContractsTest, OutputBindingRejectsMissingKeysBeforeTypeMismatch) { } } -// 3. Unified function authoring with a model capability +// 3. 使用模型能力的统一函数式编写 class MockAsrModel : public IAsrModel { public: const std::string& ModelType() const noexcept override { diff --git a/tests/unit/core/test_node_ownership_and_reuse.cpp b/tests/unit/core/test_node_ownership_and_reuse.cpp index 3c38c40a..500e57e8 100644 --- a/tests/unit/core/test_node_ownership_and_reuse.cpp +++ b/tests/unit/core/test_node_ownership_and_reuse.cpp @@ -47,7 +47,7 @@ TEST(NodeOwnershipAndReuseTest, TestFixtureRetainsPlanForNodeLifetime) { RetainedPlanProbeNode node; ASSERT_TRUE(InitNodeForTest(node, nlohmann::json::object(), &session)); EXPECT_EQ(node.Process(nullptr), 17); - // A second initialization cannot invalidate the first node's stored pointer. + // 第二次初始化不能使第一个 Node 保存的指针失效。 RetainedPlanProbeNode second; ASSERT_TRUE(InitNodeForTest(second, {{"value", 29}}, &session)); EXPECT_EQ(second.Process(nullptr), 29); @@ -55,7 +55,7 @@ TEST(NodeOwnershipAndReuseTest, TestFixtureRetainsPlanForNodeLifetime) { } TEST(NodeOwnershipAndReuseTest, CatalogCategoriesAndOwnership) { - // Common nodes in Phase 1 + // 通用 Node const auto llm_gen = PipelineCatalog::FindNode("LlmGenerateNode"); ASSERT_TRUE(llm_gen.has_value()); EXPECT_EQ(llm_gen->category, "common"); @@ -77,8 +77,7 @@ TEST(NodeOwnershipAndReuseTest, CatalogCategoriesAndOwnership) { EXPECT_EQ(text_rerank->category, "common"); } -// Mock Embedding Engine that computes distinct vector based on string hash / -// features +// 根据字符串哈希特征计算不同向量的 Mock Embedding 引擎 class DistinctMockEmbeddingModel : public IEmbeddingModel { public: const std::string& ModelType() const noexcept override { diff --git a/tests/unit/core/test_pipeline_config.cpp b/tests/unit/core/test_pipeline_config.cpp index cc5110b0..68723578 100644 --- a/tests/unit/core/test_pipeline_config.cpp +++ b/tests/unit/core/test_pipeline_config.cpp @@ -401,13 +401,13 @@ TEST_F(PipelineConfigTest, PositiveProductionAndStage7FixtureConfigs) { if ((cfg_file == "configs/pipeline_doc_qa_cpu.json" || cfg_file == "configs/pipeline_cross_rerank_cpu.json") && !BackendRegistry::Instance().Find("onnxruntime").has_value()) { - // Optional backend is deliberately absent in ONNX-disabled builds. + // 禁用 ONNX 的构建中有意不包含该可选 Backend。 continue; } if ((cfg_file == "configs/pipeline_entity_extract_cpu.json" || cfg_file == "configs/pipeline_doc_qa_cpu.json") && !BackendRegistry::Instance().Find("llama_cpp").has_value()) { - // Optional backend is absent in llama.cpp-disabled builds. + // 禁用 llama.cpp 的构建中不包含该可选 Backend。 continue; } @@ -1360,7 +1360,7 @@ TEST_F(PipelineConfigTest, ModelBackendDialectPositiveParsing) { EXPECT_EQ(diag.code, DiagnosticCode::kOk); ASSERT_EQ(parsed_cfg.models.size(), 2u); - // Model 0: Model/Backend Dialect (Full) + // Model 0:Model/Backend 完整写法 const auto& m1 = parsed_cfg.models[0]; EXPECT_EQ(m1.model_id, "m_mb_full"); EXPECT_EQ(m1.model_type, "bge_embedding"); @@ -1372,7 +1372,7 @@ TEST_F(PipelineConfigTest, ModelBackendDialectPositiveParsing) { EXPECT_EQ(m1.backend_config.value("device", ""), "cpu"); EXPECT_EQ(m1.source_index, 0u); - // Model 1: Minimal with default empty configs + // Model 1:最简写法,配置默认为空 const auto& m2 = parsed_cfg.models[1]; EXPECT_EQ(m2.model_id, "m_mb_minimal"); EXPECT_EQ(m2.model_type, "bge_reranker"); diff --git a/tests/unit/core/test_registry_reentrant.cpp b/tests/unit/core/test_registry_reentrant.cpp index b320042f..d836a5e8 100644 --- a/tests/unit/core/test_registry_reentrant.cpp +++ b/tests/unit/core/test_registry_reentrant.cpp @@ -141,8 +141,7 @@ class SimpleTestNode : public INode { } }; -// R5: creator execution, creator copy, Definition callback copy, reentrant -// queries +// R5:creator 执行、creator 拷贝、Definition 回调拷贝及重入查询 TEST(RegistryReentrantTest, ReentrantCreatorAndFactoryZeroDeadlock) { test_support::RegistryTestAccess::ScopedNodeState scoped; bool factory_invoked = false; @@ -172,7 +171,7 @@ TEST(RegistryReentrantTest, ReentrantCreatorAndFactoryZeroDeadlock) { EXPECT_NE(instance, nullptr); } -// R5: creator copy and Definition callback copy reentrancy without deadlock +// R5:creator 拷贝和 Definition 回调拷贝的重入,且不死锁 struct ReentrantCopyCreator { static inline std::atomic copy_count{0}; @@ -245,8 +244,7 @@ TEST(RegistryReentrantTest, ReentrantCallableCopyZeroDeadlock) { ReentrantCopyCreator creator; ReentrantCopyFactory factory; - // Pass factory wrapped in std::function to explicitly exercise factory - // callable copy reentrancy + // 用 std::function 包装工厂,显式覆盖工厂可调用对象拷贝时的重入 std::function factory_wrapper = factory; bool registered = NodeRegistry::Instance().RegisterWithDefinitionFactory( @@ -254,26 +252,26 @@ TEST(RegistryReentrantTest, ReentrantCallableCopyZeroDeadlock) { EXPECT_TRUE(registered); EXPECT_TRUE(NodeRegistry::Instance().Has("ReentrantCallableCopyNode")); - // Create node: copies handle->creator outside registry lock, triggers - // ReentrantCopyCreator copy + // 创建 Node:在注册表锁外拷贝 handle->creator,触发 + // ReentrantCopyCreator 拷贝 auto instance = NodeRegistry::Instance().Create("ReentrantCallableCopyNode"); EXPECT_NE(instance, nullptr); EXPECT_GT(ReentrantCopyCreator::copy_count.load(), 0); - // Snapshot: copies handle->definition outside registry lock, triggers - // ReentrantConfigValidator copy + // Snapshot:在注册表锁外拷贝 handle->definition,触发 + // ReentrantConfigValidator 拷贝 auto snap = NodeRegistry::Instance().Snapshot(); EXPECT_FALSE(snap.definitions.empty()); EXPECT_GT(ReentrantConfigValidator::copy_count.load(), 0); - // ListDefinitions: also copies definitions outside lock + // ListDefinitions:同样在锁外拷贝 Definition auto list = NodeRegistry::Instance().ListDefinitions(); EXPECT_FALSE(list.empty()); EXPECT_GT(ReentrantCopyFactory::copy_count.load(), 0); } -// R4: Register fail-after-N loop with ScopedAllocationFailure +// R4:配合 ScopedAllocationFailure 循环测试 Register 第 N 次分配后失败 TEST(RegistryReentrantTest, RegisterFailAfterNIntegrity) { test_support::RegistryTestAccess::ScopedNodeState scoped; const std::string sentinel = "AllocFailSentinelNode"; @@ -322,8 +320,8 @@ TEST(RegistryReentrantTest, RegisterFailAfterNIntegrity) { EXPECT_TRUE(completed); } -// R4/R5: Insertion allocation failure with user resource destructor calling -// NodeRegistry::Has() must not deadlock on mutex_. +// R4/R5:插入时分配失败,且用户资源的析构函数调用 NodeRegistry::Has(), +// 不得在 mutex_ 上死锁。 struct ReentrantDestructorResource { static inline std::atomic destruct_count{0}; static inline std::atomic reentrant_has_count{0}; @@ -406,7 +404,7 @@ TEST(RegistryReentrantTest, ReentrantDestructorResource::reentrant_has_count.load()); } -// R4: Register duplicate fail-after-N loop with ScopedAllocationFailure +// R4:配合 ScopedAllocationFailure 循环测试重复 Register 第 N 次分配后失败 TEST(RegistryReentrantTest, RegisterDuplicateFailAfterNIntegrity) { test_support::RegistryTestAccess::ScopedNodeState scoped; const std::string existing = "AllocFailDuplicateNode"; @@ -449,8 +447,8 @@ TEST(RegistryReentrantTest, RegisterDuplicateFailAfterNIntegrity) { EXPECT_TRUE(completed); } -// R4: Register cross-node control conflict fail-after-N loop with -// ScopedAllocationFailure +// R4:配合 ScopedAllocationFailure 循环测试跨 Node Control 冲突的 Register +// 第 N 次分配后失败 TEST(RegistryReentrantTest, RegisterControlConflictFailAfterNIntegrity) { test_support::RegistryTestAccess::ScopedNodeState scoped; const std::string node_a = "AllocFailControlNodeA"; @@ -470,7 +468,7 @@ TEST(RegistryReentrantTest, RegisterControlConflictFailAfterNIntegrity) { const std::string node_b = "AllocFailControlNodeB"; NodeDefinition def_b = MakeTestNodeDef(node_b); ControlCommandDefinition cmd_b; - cmd_b.cmd_id = 999; // Conflict with node_a command + cmd_b.cmd_id = 999; // 与 node_a 的命令冲突 cmd_b.name = "different_name"; cmd_b.shared_id = false; cmd_b.payload_schema = nlohmann::json::object(); @@ -513,8 +511,8 @@ TEST(RegistryReentrantTest, RegisterControlConflictFailAfterNIntegrity) { EXPECT_TRUE(completed); } -// R4: RegisterWithDefinitionFactory fail-after-N loop with -// ScopedAllocationFailure +// R4:配合 ScopedAllocationFailure 循环测试 RegisterWithDefinitionFactory +// 第 N 次分配后失败 TEST(RegistryReentrantTest, RegisterWithDefinitionFactoryFailAfterNIntegrity) { test_support::RegistryTestAccess::ScopedNodeState scoped; const std::string sentinel = "FactoryAllocFailSentinelNode"; @@ -561,8 +559,7 @@ TEST(RegistryReentrantTest, RegisterWithDefinitionFactoryFailAfterNIntegrity) { EXPECT_TRUE(completed); } -// R8: ScopedNodeState restoration under allocation failure is non-allocating -// and robust +// R8:分配失败时 ScopedNodeState 的恢复不分配内存且稳健 TEST(RegistryReentrantTest, ScopedNodeStateRestorationUnderAllocationFailure) { const std::string sentinel = "RestorationSentinelNode"; { @@ -582,22 +579,21 @@ TEST(RegistryReentrantTest, ScopedNodeStateRestorationUnderAllocationFailure) { MakeTestNodeDef(temp_node))); EXPECT_TRUE(NodeRegistry::Instance().Has(temp_node)); - // Trigger conflict so has_conflict_ is true + // 触发冲突,使 has_conflict_ 为 true EXPECT_FALSE(NodeRegistry::Instance().Register( temp_node, []() { return std::make_unique(); }, MakeTestNodeDef(temp_node))); EXPECT_TRUE(NodeRegistry::Instance().HasConflict()); - // Arm allocation failure so that ~ScopedNodeState runs with failure(0) + // 启用分配失败,使 ~ScopedNodeState 在 failure(0) 下运行 failure.emplace(0); } - // inner is destructed while failure is active, then failure is destructed + // inner 在 failure 生效期间析构,随后 failure 析构 ASSERT_TRUE(failure.has_value()); EXPECT_FALSE(failure->Triggered()); } - // After inner scope destroyed under allocation failure, registry must be - // cleanly restored + // inner 作用域在分配失败下销毁后,注册表必须完整恢复 EXPECT_FALSE(NodeRegistry::Instance().HasConflict()); EXPECT_TRUE(NodeRegistry::Instance().Has(sentinel)); EXPECT_NE(NodeRegistry::Instance().Create(sentinel), nullptr); diff --git a/tests/unit/core/test_validated_pipeline_plan.cpp b/tests/unit/core/test_validated_pipeline_plan.cpp index 1ef9d214..0933239b 100644 --- a/tests/unit/core/test_validated_pipeline_plan.cpp +++ b/tests/unit/core/test_validated_pipeline_plan.cpp @@ -214,8 +214,8 @@ REGISTER_NODE_WITH_DEFINITION(FlowContractProducerNode, REGISTER_NODE_WITH_DEFINITION(FlowContractConsumerNode, MakeFlowContractConsumerDefinition()); -// Shape fixtures: one item-wise pair, one per-request group and one opaque -// relation. Only their Definitions matter to the Validator. +// 形状夹具:一个逐条配对、一个按请求分组、一个不透明关系。 +// Validator 只关心它们的 Definition。 class ShapeFixtureNode : public INode { public: bool Init(const NodeInitContext&) override { return true; } @@ -371,7 +371,7 @@ TEST_F(ValidatedPipelinePlanTest, RejectsIncompatiblePortExecutionContracts) { } } for (const auto code : expected) EXPECT_TRUE(actual.count(code)); - // An item-wise input accepts split items; only provenance and lifetime fail. + // 逐条输入接受拆分后的条目;只有 provenance 和 lifetime 校验失败。 EXPECT_FALSE(actual.count(DiagnosticCode::kPortCardinalityMismatch)); } @@ -800,7 +800,7 @@ TEST_F(ValidatedPipelinePlanTest, NormalizedNodeConfigIsRuntimeSingleSource) { } TEST_F(ValidatedPipelinePlanTest, MultiLayerWavefrontTopology) { - // Test DAG Wavefront layers calculation + // 测试 DAG 波前分层计算 nlohmann::json dag_json = { {"biz_name", "plan_fixture_biz"}, {"models", nlohmann::json::array()}, @@ -820,9 +820,9 @@ TEST_F(ValidatedPipelinePlanTest, MultiLayerWavefrontTopology) { auto plan = PipelineValidator::ValidateAndPlan(dag_json); EXPECT_TRUE(plan.report.ok); ASSERT_EQ(plan.report.topological_layers.size(), 2u); - // Layer 0 has node_a and node_b + // 第 0 层为 node_a 和 node_b EXPECT_EQ(plan.report.topological_layers[0].size(), 2u); - // Layer 1 has node_c + // 第 1 层为 node_c EXPECT_EQ(plan.report.topological_layers[1].size(), 1u); EXPECT_EQ(plan.report.topological_layers[1][0], "node_c"); } @@ -957,8 +957,7 @@ TEST_F(ValidatedPipelinePlanTest, {"outputs", {{"ranked", "ranked_results"}}}, {"config", {{"bind_model", "m_rel"}}}}})}}; - // Orchestration only performs deterministic lexical normalization. Deployment - // roots are an Integration concern. + // 流程编排层只做确定性的词法归一化。部署根目录由接入适配层负责。 auto plan = PipelineValidator::ValidateAndPlan(pipeline_json); ASSERT_TRUE(plan.report.ok) << plan.report.ToJson().dump(); EXPECT_EQ(plan.models[0].resolved_model_path, "models/sub/model.onnx"); @@ -974,7 +973,7 @@ TEST_F(ValidatedPipelinePlanTest, EXPECT_EQ(plan_repeat.models[2].resolved_model_path, plan.models[2].resolved_model_path); - // Parent traversal remains invalid even before deployment resolution. + // 即使尚未解析部署,父目录遍历仍然非法。 nlohmann::json escape_json = pipeline_json; escape_json["models"][0]["model_path"] = "../escape.onnx"; auto plan_escape = PipelineValidator::ValidateAndPlan(escape_json); diff --git a/tests/unit/engine/test_batch_executor.cpp b/tests/unit/engine/test_batch_executor.cpp index 75d3bc2f..08d35e07 100644 --- a/tests/unit/engine/test_batch_executor.cpp +++ b/tests/unit/engine/test_batch_executor.cpp @@ -132,7 +132,7 @@ TEST(FixedBatchExecutorTest, ErrorPropagationFromKernel) { TEST(FixedBatchExecutorTest, StrictOutputsAndRollback) { std::vector> inputs = { {1, 0, 10}, {1, 1, 20}, {2, 0, 30}, {2, 1, 40}, {3, 0, 50}}; - BatchPolicy policy{2, 0}; // dynamic max batch size = 2 + BatchPolicy policy{2, 0}; // 动态批大小上限 = 2 std::vector> outputs; // 1. 正常执行 diff --git a/tests/unit/engine/test_engine_fault_tolerance_and_lifecycle.cpp b/tests/unit/engine/test_engine_fault_tolerance_and_lifecycle.cpp index a3706b0c..cc70bcae 100644 --- a/tests/unit/engine/test_engine_fault_tolerance_and_lifecycle.cpp +++ b/tests/unit/engine/test_engine_fault_tolerance_and_lifecycle.cpp @@ -254,11 +254,11 @@ TEST_F(EngineFaultToleranceAndLifecycleTest, Deep5LayerWavefrontDagExecution) { const auto& layers = pipeline.GetTopologicalLayers(); ASSERT_EQ(layers.size(), 5); - EXPECT_EQ(layers[0].size(), 2); // Layer 0: R1, R2 - EXPECT_EQ(layers[1].size(), 3); // Layer 1: A1, A2, A3 - EXPECT_EQ(layers[2].size(), 2); // Layer 2: M1, M2 - EXPECT_EQ(layers[3].size(), 3); // Layer 3: B1, B2, B3 - EXPECT_EQ(layers[4].size(), 1); // Layer 4: Final + EXPECT_EQ(layers[0].size(), 2); // 第 0 层:R1、R2 + EXPECT_EQ(layers[1].size(), 3); // 第 1 层:A1、A2、A3 + EXPECT_EQ(layers[2].size(), 2); // 第 2 层:M1、M2 + EXPECT_EQ(layers[3].size(), 3); // 第 3 层:B1、B2、B3 + EXPECT_EQ(layers[4].size(), 1); // 第 4 层:Final AlgContext req_ctx; ResetDeepDagTrace(); diff --git a/tests/unit/engine/test_llama_cpp_backend.cpp b/tests/unit/engine/test_llama_cpp_backend.cpp index 5404f36a..407545df 100644 --- a/tests/unit/engine/test_llama_cpp_backend.cpp +++ b/tests/unit/engine/test_llama_cpp_backend.cpp @@ -75,7 +75,7 @@ TEST(LlamaCppBackendTest, ConfigPreflightAndLoadShareCombinationValidation) { "decode_batch_size must not exceed context_size"); BackendLoadSpec spec{ExecutionProtocol::kTextGeneration}; spec.backend_config = config; - // The config error is returned even without a path, before model access. + // 即使没有路径,也会在访问模型前返回配置错误。 std::string load_diagnostic; EXPECT_EQ(backend->Load(spec, &load_diagnostic), nullptr); EXPECT_EQ(load_diagnostic, preflight_diagnostic); @@ -212,7 +212,7 @@ TEST(LlamaCppBackendTest, KiteAcceptsCpuAndUnspecifiedExecutionTargets) { std::optional{0}}) { spec.execution_target.device_id = device; EXPECT_EQ(backend.Load(spec, &diagnostic), nullptr); - // A valid target reaches model validation (or the unavailable SDK). + // 合法的目标会进入模型校验 (或 SDK 不可用)。 EXPECT_TRUE(diagnostic.find("regular file") != std::string::npos || diagnostic.find("not compiled") != std::string::npos) << diagnostic; @@ -238,7 +238,7 @@ TEST(LlamaCppBackendTest, KiteRejectsInvalidModelsAndRunConfigs) { spec.backend_config = {{"run_config_file", "gpu.json"}}; EXPECT_EQ(backend->Load(spec, &diagnostic), nullptr); EXPECT_NE(diagnostic.find("conflicts"), std::string::npos); - // A CPU-compatible file reaches the native model loader. + // CPU 兼容的文件会进入原生模型加载器。 std::ofstream(temporary.path / "cpu.json") << R"({"schema_version":1,"model":{"gpu_layers":0}})"; spec.backend_config = {{"run_config_file", "cpu.json"}}; @@ -338,7 +338,7 @@ TEST(LlamaCppBackendTest, RealKiteSdkGenerationAndFixedSeedPolicy) { EXPECT_EQ(first.get(), baseline); EXPECT_EQ(second.get(), baseline); - // Releasing one session must not deinitialize a second live model handle. + // 释放一个会话不得反初始化另一个仍存活的模型句柄。 auto another = std::dynamic_pointer_cast( backend->Load(spec, &diagnostic)); ASSERT_NE(another, nullptr) << diagnostic; @@ -352,8 +352,8 @@ TEST(LlamaCppBackendTest, RealKiteSdkGenerationAndFixedSeedPolicy) { another.reset(); EXPECT_NE(backend->Load(spec, &diagnostic), nullptr) << diagnostic; - // Exercise the native setter, both with and without an optional run-config. - // Release each session before changing native load parameters for this model. + // 分别在带和不带可选 run-config 的情况下调用原生 setter。 + // 修改该模型的原生加载参数前,先释放每个会话。 for (const bool with_config : {true, false}) { if (!with_config) spec.backend_config = nlohmann::json::object(); spec.execution_target.platform = "CPU"; @@ -370,7 +370,7 @@ TEST(LlamaCppBackendTest, RealKiteSdkGenerationAndFixedSeedPolicy) { spec.execution_target.platform.clear(); spec.execution_target.device_id = -1; EXPECT_NE(backend->Load(spec, &diagnostic), nullptr) << diagnostic; - // A nonexistent native index must fail instead of silently using the CPU. + // 不存在的原生设备索引必须失败,而不是静默回退到 CPU。 spec.execution_target.device_id = std::numeric_limits::max(); EXPECT_EQ(backend->Load(spec, &diagnostic), nullptr); EXPECT_NE(diagnostic.find("model load failed"), std::string::npos); diff --git a/tests/unit/engine/test_model_backend_decoupling.cpp b/tests/unit/engine/test_model_backend_decoupling.cpp index 63792d6c..77cc22c6 100644 --- a/tests/unit/engine/test_model_backend_decoupling.cpp +++ b/tests/unit/engine/test_model_backend_decoupling.cpp @@ -291,7 +291,7 @@ class DiagnosticAllocationException final : public std::exception { : failure_(failure) {} const char* what() const noexcept override { - // The test owns the scope across both the first append and its fallback. + // 测试在首次 append 及其回退期间都持有该作用域。 if (failure_ && !failure_->has_value()) failure_->emplace(0); return kReason; } @@ -696,7 +696,7 @@ TEST_F(ModelConfigValidationTest, EXPECT_EQ(ModelValidationBackend::provider_calls, 0); EXPECT_EQ(ModelValidationBackend::load_calls, 0); - // The probe intentionally stops at Load, without weights or resource I/O. + // 探针有意止步于 Load,不涉及权重或资源 I/O。 std::string diagnostic; EXPECT_EQ(ModelRuntimeFactory::Create(spec, &diagnostic), nullptr); EXPECT_EQ(ModelValidationBackend::provider_calls, 1); @@ -837,9 +837,8 @@ TEST_F(ModelConfigValidationTest, ConfigValidatedEmbeddingModel::diagnostic_failure = &failure; std::string diagnostic; - // what() arms the next allocation only after Factory enters its catch block; - // the long reason forces append to allocate. No allocation failure is active - // during definition lookup, normalization or validation. + // what() 在 Factory 进入 catch 块后才启用下一次分配失败;较长的原因使 append + // 必须分配内存。查找 Definition、归一化和校验期间都未启用分配失败。 auto model = ModelRuntimeFactory::Create(spec, &diagnostic); const bool injected = failure.has_value() && failure->Triggered(); failure.reset(); @@ -869,7 +868,7 @@ TEST_F(ModelConfigValidationTest, DirectBgeCreationRejectsInvalidStringFields) { SCOPED_TRACE(field); for (const auto& value : invalid_values) { SCOPED_TRACE(value.dump()); - // No Backend session or model resource exists; type validation wins. + // 不存在 Backend 会话或模型资源;类型校验优先。 ModelCreateContext context; context.model_config[field] = value; std::string diagnostic; @@ -1372,48 +1371,48 @@ TEST(ModelBackendDecouplingTest, std::string error; - // 1. Successful creation with defaults + // 1. 使用默认值成功创建 auto model = WhisperAsrModel::Create(context, &error); ASSERT_NE(model, nullptr) << error; EXPECT_EQ(model->ModelType(), "whisper_asr"); EXPECT_EQ(model->Capability(), "asr"); EXPECT_EQ(model->Concurrency(), InferenceConcurrency::kConcurrent); - // 2. Null session + // 2. 会话为空 ModelCreateContext null_ctx; EXPECT_EQ(WhisperAsrModel::Create(null_ctx, &error), nullptr); - // 3. Wrong protocol + // 3. 协议错误 session->protocol = ExecutionProtocol::kTextGeneration; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); session->protocol = ExecutionProtocol::kAudioTranscription; - // 4. Incompatible batch policy + // 4. 批策略不兼容 session->policy = {2, 0}; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); session->policy = {1, 1}; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); session->policy = {1, 0}; - // 5. Config validation: invalid language + // 5. 配置校验:非法 language context.model_config = {{"language", "fr"}}; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); context.model_config = {{"language", ""}}; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); - // 6. Unsupported language by session + // 6. 会话不支持该语言 session->supported_languages = {"en"}; context.model_config = {{"language", "zh"}}; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); session->supported_languages = {"zh", "en", "auto"}; - // 7. Config validation: max_audio_seconds bounds [1, 60] + // 7. 配置校验:max_audio_seconds 范围 [1, 60] context.model_config = {{"max_audio_seconds", 0}}; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); context.model_config = {{"max_audio_seconds", 61}}; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); - // 8. Config validation: max_output_bytes bounds [1, 65536] + // 8. 配置校验:max_output_bytes 范围 [1, 65536] context.model_config = {{"max_output_bytes", 0}}; EXPECT_EQ(WhisperAsrModel::Create(context, &error), nullptr); context.model_config = {{"max_output_bytes", 65537}}; @@ -1434,17 +1433,17 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { WhisperAsrModel::Create(context, &error)); ASSERT_NE(model, nullptr) << error; - // 1. Null outputs pointer returns -1 + // 1. outputs 指针为空时返回 -1 AudioPcmBatch audio; EXPECT_EQ(model->Transcribe(audio, nullptr), -1); - // 2. Empty batch returns 0, no session calls + // 2. 空批次返回 0,且不调用会话 TextBatch outputs; EXPECT_EQ(model->Transcribe(audio, &outputs), 0); EXPECT_TRUE(outputs.empty()); EXPECT_EQ(session->transcribe_call_count, 0U); - // 3. Item with empty PCM returns empty string, preserving req_id and sub_id + // 3. PCM 为空的条目返回空字符串,并保留 req_id 和 sub_id audio.emplace_back(10, 1, AudioPcmPayload({}, 16000)); EXPECT_EQ(model->Transcribe(audio, &outputs), 0); ASSERT_EQ(outputs.size(), 1U); @@ -1452,9 +1451,9 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_EQ(outputs[0].sub_id, 1U); EXPECT_EQ(outputs[0].data, ""); EXPECT_EQ(session->transcribe_call_count, - 0U); // empty pcm skips backend call + 0U); // 空 PCM 跳过 Backend 调用 - // 4. Sample rate != 16000 fails closed before session call + // 4. 采样率 != 16000 时,在调用会话前 fail-closed audio.clear(); outputs.clear(); audio.emplace_back(11, 0, @@ -1463,7 +1462,7 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_TRUE(outputs.empty()); EXPECT_EQ(session->transcribe_call_count, 0U); - // 5. Audio < 1600 samples (100ms) fails closed before session call + // 5. 音频少于 1600 个采样点 (100 毫秒) 时,在调用会话前 fail-closed audio.clear(); audio.emplace_back(12, 0, AudioPcmPayload(std::vector(1599, 0.0f), 16000)); @@ -1471,7 +1470,7 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_TRUE(outputs.empty()); EXPECT_EQ(session->transcribe_call_count, 0U); - // 6. Audio > max_audio_seconds fails closed before session call + // 6. 音频超过 max_audio_seconds 时,在调用会话前 fail-closed audio.clear(); audio.emplace_back( 13, 0, AudioPcmPayload(std::vector(30 * 16000 + 1, 0.0f), 16000)); @@ -1479,7 +1478,7 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_TRUE(outputs.empty()); EXPECT_EQ(session->transcribe_call_count, 0U); - // 7. Non-finite sample fails closed + // 7. 含非有限采样值时 fail-closed audio.clear(); std::vector nan_pcm(1600, 0.0f); nan_pcm[10] = std::numeric_limits::quiet_NaN(); @@ -1488,7 +1487,7 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_TRUE(outputs.empty()); EXPECT_EQ(session->transcribe_call_count, 0U); - // 8. Sample outside [-1, 1] fails closed + // 8. 采样值超出 [-1, 1] 时 fail-closed audio.clear(); std::vector overflow_pcm(1600, 0.0f); overflow_pcm[5] = 1.05f; @@ -1497,7 +1496,7 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_TRUE(outputs.empty()); EXPECT_EQ(session->transcribe_call_count, 0U); - // 9. Valid audio transcribes and trims whitespace + // 9. 合法音频完成转写并去除首尾空白 audio.clear(); audio.emplace_back(20, 0, AudioPcmPayload(std::vector(16000, 0.1f), 16000)); @@ -1508,28 +1507,28 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_EQ(outputs[0].data, "你好世界"); EXPECT_EQ(session->transcribe_call_count, 1U); - // 10. Embedded NUL byte in output rejected and cleared + // 10. 输出中嵌入 NUL 字节时拒绝并清空 session->return_embedded_nul = true; outputs.clear(); EXPECT_EQ(model->Transcribe(audio, &outputs), -1); EXPECT_TRUE(outputs.empty()); session->return_embedded_nul = false; - // 11. Invalid UTF-8 in output rejected and cleared + // 11. 输出含非法 UTF-8 时拒绝并清空 session->return_invalid_utf8 = true; outputs.clear(); EXPECT_EQ(model->Transcribe(audio, &outputs), -1); EXPECT_TRUE(outputs.empty()); session->return_invalid_utf8 = false; - // 12. Output exceeds max_output_bytes rejected and cleared + // 12. 输出超过 max_output_bytes 时拒绝并清空 session->transcript_to_return = std::string(2000, 'A'); outputs.clear(); EXPECT_EQ(model->Transcribe(audio, &outputs), -1); EXPECT_TRUE(outputs.empty()); session->transcript_to_return = "你好世界"; - // 13. Multi-item batch preserves ordering and provenance + // 13. 多条目批次保持顺序和来源 audio.clear(); audio.emplace_back(100, 0, AudioPcmPayload(std::vector(16000, 0.1f), 16000)); @@ -1549,7 +1548,7 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_EQ(outputs[2].sub_id, 0U); EXPECT_EQ(session->transcribe_call_count, 3U); - // 14. Second item fails during inference -> all outputs cleared (rollback) + // 14. 第二个条目推理失败 -> 清空所有输出 (回滚) session->fail_on_call_index = 2; session->transcribe_call_count = 0; outputs.clear(); @@ -1563,7 +1562,7 @@ TEST(ModelBackendDecouplingTest, WhisperAsrModelTranscribeInputsAndBatching) { EXPECT_TRUE(diagnostic.empty()); ASSERT_EQ(outputs.size(), audio.size()); - // 15. Pre-validation on 3rd item failure -> session called 0 times + // 15. 第 3 个条目预校验失败 -> 会话调用 0 次 audio[2].data.sample_rate = 8000; session->transcribe_call_count = 0; outputs.clear(); diff --git a/tests/unit/engine/test_onnx_and_embedding_model.cpp b/tests/unit/engine/test_onnx_and_embedding_model.cpp index b0099739..8160eedc 100644 --- a/tests/unit/engine/test_onnx_and_embedding_model.cpp +++ b/tests/unit/engine/test_onnx_and_embedding_model.cpp @@ -146,7 +146,7 @@ TEST_F(OnnxAndEmbeddingModelTest, TokenizerCaseAndSidecarSecurity) { ASSERT_TRUE(tokenizer.LoadFromTokens(tokens, /*do_lower_case=*/false, &diag)); std::vector ids, mask; EXPECT_TRUE(tokenizer.Encode("Hello", 4, &ids, &mask)); - EXPECT_EQ(ids[1], 5); // Matches "Hello" + EXPECT_EQ(ids[1], 5); // 匹配 "Hello" // 词表文件存在性与路径安全 auto vocab_file = temp_dir_ / "vocab.txt"; @@ -560,7 +560,7 @@ TEST_F(OnnxAndEmbeddingModelTest, BgeEmbeddingModelCLSAndMeanPooling) { "hello", "world", "bge", "model"}; ASSERT_TRUE(tokenizer.LoadFromTokens(tokens, true)); - // 1. CLS Pooling + // 1. CLS 池化 BgeEmbeddingModel model_cls(fake_session_3d, tokenizer, /*max_length=*/16, /*pooling_strategy=*/"cls", /*output_name=*/"last_hidden_state", @@ -586,7 +586,7 @@ TEST_F(OnnxAndEmbeddingModelTest, BgeEmbeddingModelCLSAndMeanPooling) { for (float v : outputs[0].data) sum_sq += v * v; EXPECT_NEAR(std::sqrt(sum_sq), 1.0f, 1e-4f); - // 2. Mean Pooling + // 2. Mean 池化 BgeEmbeddingModel model_mean(fake_session_3d, tokenizer, 16, "mean", "last_hidden_state", 4, 2); EXPECT_EQ(model_mean.Embed(inputs, opts, &outputs), 0); @@ -1120,9 +1120,8 @@ TEST_F(OnnxAndEmbeddingModelTest, OnnxRuntimeFixturePassEvidence) { vocab_path.string(); pipeline_config["models"][0]["model_config"]["max_length"] = 32; pipeline_config["models"][0]["model_config"]["embedding_dim"] = 128; - // This test proves the ONNX embedding path and must not depend on an - // external GGUF asset. Keep the same LLM node, but replace only its test - // model registration with an explicit typed Model/Backend fixture. + // 本测试验证 ONNX embedding 路径,不得依赖外部 GGUF 资源。保留同一个 + // LLM Node,只把它的测试模型注册替换为显式类型化的 Model/Backend 夹具。 pipeline_config["models"][1] = { {"model_id", "llm_model_llamacpp"}, {"model_type", "test_biz_llm"}, @@ -1252,7 +1251,7 @@ TEST_F(OnnxAndEmbeddingModelTest, OnnxRuntimeBackendNegativeValidation) { TEST_F(OnnxAndEmbeddingModelTest, FinalizeEmbeddingVectorPrecisionAndBoundary) { std::vector output; - // 1. Valid normalized vector: unit norm + // 1. 合法的归一化向量:单位范数 std::vector input = {3.0f, 4.0f}; EXPECT_TRUE(embedding_support::FinalizeEmbeddingVector(input, true, &output)); ASSERT_EQ(output.size(), 2u); @@ -1261,25 +1260,25 @@ TEST_F(OnnxAndEmbeddingModelTest, FinalizeEmbeddingVectorPrecisionAndBoundary) { float norm = std::sqrt(output[0] * output[0] + output[1] * output[1]); EXPECT_NEAR(norm, 1.0f, 1e-6); - // 2. Unnormalized vector: preserves values + // 2. 未归一化向量:保留原值 EXPECT_TRUE( embedding_support::FinalizeEmbeddingVector(input, false, &output)); ASSERT_EQ(output.size(), 2u); EXPECT_FLOAT_EQ(output[0], 3.0f); EXPECT_FLOAT_EQ(output[1], 4.0f); - // 3. Zero vector with normalize=true fails + // 3. normalize=true 时零向量失败 std::vector zero_vec = {0.0f, 0.0f, 0.0f}; EXPECT_FALSE( embedding_support::FinalizeEmbeddingVector(zero_vec, true, &output)); - // 4. Zero vector with normalize=false succeeds + // 4. normalize=false 时零向量成功 EXPECT_TRUE( embedding_support::FinalizeEmbeddingVector(zero_vec, false, &output)); ASSERT_EQ(output.size(), 3u); EXPECT_FLOAT_EQ(output[0], 0.0f); - // 5. Non-finite values fail + // 5. 非有限值失败 std::vector nan_vec = {1.0f, std::numeric_limits::quiet_NaN()}; EXPECT_FALSE( embedding_support::FinalizeEmbeddingVector(nan_vec, true, &output)); @@ -1292,7 +1291,7 @@ TEST_F(OnnxAndEmbeddingModelTest, FinalizeEmbeddingVectorPrecisionAndBoundary) { EXPECT_FALSE( embedding_support::FinalizeEmbeddingVector(inf_vec, false, &output)); - // 6. Null output or empty input fails + // 6. 输出为空或输入为空时失败 EXPECT_FALSE( embedding_support::FinalizeEmbeddingVector(input, true, nullptr)); std::vector empty_vec; @@ -1301,7 +1300,7 @@ TEST_F(OnnxAndEmbeddingModelTest, FinalizeEmbeddingVectorPrecisionAndBoundary) { EXPECT_FALSE(embedding_support::FinalizeEmbeddingVector( static_cast(nullptr), 2, true, &output)); - // 7. Double precision accumulation handles large elements + // 7. 双精度累加可处理很大的元素 std::vector large_vec = {1e20, 1e20}; EXPECT_TRUE( embedding_support::FinalizeEmbeddingVector(large_vec, true, &output)); diff --git a/tests/unit/engine/test_onnx_and_reranker_model.cpp b/tests/unit/engine/test_onnx_and_reranker_model.cpp index 4a73d4a2..249ee532 100644 --- a/tests/unit/engine/test_onnx_and_reranker_model.cpp +++ b/tests/unit/engine/test_onnx_and_reranker_model.cpp @@ -78,13 +78,13 @@ TEST_F(OnnxAndRerankerModelTest, TokenizerPairEncodingBasicAndWordPiece) { EXPECT_EQ(ids[8], 0); EXPECT_EQ(ids[9], 0); - // mask: 1 for first 8, 0 for last 2 + // mask:前 8 个为 1,后 2 个为 0 for (size_t i = 0; i < 8; ++i) EXPECT_EQ(mask[i], 1); EXPECT_EQ(mask[8], 0); EXPECT_EQ(mask[9], 0); - // token_type_ids: 0 for query side & first [SEP] (indices 0..5), 1 for cand & - // second [SEP] (indices 6..7), 0 for padding + // token_type_ids:query 侧及第一个 [SEP] (索引 0..5) 为 0,cand 及第二个 + // [SEP] (索引 6..7) 为 1,填充为 0 for (size_t i = 0; i <= 5; ++i) EXPECT_EQ(types[i], 0); EXPECT_EQ(types[6], 1); EXPECT_EQ(types[7], 1); @@ -154,7 +154,7 @@ TEST_F(OnnxAndRerankerModelTest, TokenizerPairEncodingBasicAndWordPiece) { tokenizer.EncodePair(bad_utf8, "cand", 8, &ids, &mask, &types, &diag)); EXPECT_TRUE(ids.empty()); - // 8. Null output pointers + // 8. 输出指针为空 EXPECT_FALSE( tokenizer.EncodePair("query", "cand", 8, nullptr, &mask, &types, &diag)); } @@ -691,7 +691,7 @@ TEST_F(OnnxAndRerankerModelTest, ModelStrictTensorBoundaryFailures) { EXPECT_NE(model.Score(inputs, &outputs), 0); EXPECT_TRUE(outputs.empty()); - // 6. Zero Dim + // 6. 零维度 fake_session->corrupt_rank_ = false; fake_session->corrupt_zero_dim_ = true; EXPECT_NE(model.Score(inputs, &outputs), 0); @@ -961,13 +961,13 @@ TEST_F(OnnxAndRerankerModelTest, RealPipelineBuildAndExecuteSmoke) { pipe_json["models"][0]["model_config"]["max_length"] = 32; pipe_json["pipeline"][0]["config"]["top_k"] = 2; - // 3. PipelineValidator Validate/Plan + // 3. PipelineValidator 校验并规划 auto planned_plan = PipelineValidator::ValidateAndPlan(pipe_json); ASSERT_TRUE(planned_plan.report.ok) << planned_plan.report.ToJson().dump(); ASSERT_EQ(planned_plan.report.topological_order.size(), 1u); EXPECT_EQ(planned_plan.report.topological_order[0], "node_0_TextRerankNode"); - // 4. Pipeline Build + // 4. 构建 Pipeline Pipeline pipeline; PipelineDiagnostic build_diag; bool build_ok = BuildTestPipeline(pipeline, pipe_json, &build_diag); @@ -990,7 +990,7 @@ TEST_F(OnnxAndRerankerModelTest, RealPipelineBuildAndExecuteSmoke) { ctx.Publish("rerank_queries", queries); ctx.Publish("rerank_candidates", candidates); - // 6. Pipeline Execute + // 6. 执行 Pipeline int exec_ret = pipeline.Execute(&ctx); EXPECT_EQ(exec_ret, 0); diff --git a/tests/unit/engine/test_whisper_cpp_backend.cpp b/tests/unit/engine/test_whisper_cpp_backend.cpp index 24227227..7201ac53 100644 --- a/tests/unit/engine/test_whisper_cpp_backend.cpp +++ b/tests/unit/engine/test_whisper_cpp_backend.cpp @@ -91,8 +91,8 @@ TEST(WhisperCppBackendTest, UnsupportedExecutionTargetFailsBeforeFilesystem) { TEST(WhisperCppBackendTest, LoadExceptionBarrierProtectsEntireEntrypoint) { WhisperCppBackend backend; - // Set a terminate handler to verify std::terminate is never called - // (reproducing the exit code 86 scenario) + // 设置 terminate handler,验证 std::terminate 从未被调用 + // (复现退出码 86 的场景) static bool terminate_invoked = false; terminate_invoked = false; auto old_terminate = std::set_terminate([] { @@ -108,8 +108,7 @@ TEST(WhisperCppBackendTest, LoadExceptionBarrierProtectsEntireEntrypoint) { BackendLoadSpec spec{ExecutionProtocol::kAudioTranscription}; spec.model_path = "./models/does-not-exist.bin"; - // 1. bad_alloc exception at entrypoint (reproduces allocation failure during - // NormalizePlatform / string copies) + // 1. 入口处抛 bad_alloc (复现 NormalizePlatform / 字符串拷贝期间的分配失败) { backend.SetLoadHook([] { throw std::bad_alloc(); }); std::string diagnostic; @@ -120,14 +119,14 @@ TEST(WhisperCppBackendTest, LoadExceptionBarrierProtectsEntireEntrypoint) { EXPECT_FALSE(terminate_invoked); } - // 2. bad_alloc with nullptr diagnostic + // 2. 诊断指针为空时抛 bad_alloc { backend.SetLoadHook([] { throw std::bad_alloc(); }); EXPECT_EQ(backend.Load(spec, nullptr), nullptr); EXPECT_FALSE(terminate_invoked); } - // 3. standard runtime_error at entrypoint + // 3. 入口处抛标准 runtime_error { backend.SetLoadHook( [] { throw std::runtime_error("simulated entrypoint failure"); }); @@ -138,7 +137,7 @@ TEST(WhisperCppBackendTest, LoadExceptionBarrierProtectsEntireEntrypoint) { EXPECT_FALSE(terminate_invoked); } - // 4. non-std exception at entrypoint + // 4. 入口处抛非 std 异常 { backend.SetLoadHook([] { throw 42; }); std::string diagnostic; @@ -148,7 +147,7 @@ TEST(WhisperCppBackendTest, LoadExceptionBarrierProtectsEntireEntrypoint) { EXPECT_FALSE(terminate_invoked); } - // 5. Clean hook reset allows normal failure handling + // 5. 清除钩子后恢复正常的失败处理 backend.SetLoadHook(nullptr); std::string diagnostic; EXPECT_EQ(backend.Load(spec, &diagnostic), nullptr); @@ -201,7 +200,7 @@ TEST(WhisperCppBackendTest, SessionLifecycleAndInference) { EXPECT_TRUE(asr_session->SupportsLanguage("auto")); EXPECT_FALSE(asr_session->SupportsLanguage("unknown_lang")); - // 1. Empty audio returns empty string + // 1. 空音频返回空字符串 AudioPcmPayload empty_audio; empty_audio.sample_rate = 16000; std::string output; @@ -212,27 +211,27 @@ TEST(WhisperCppBackendTest, SessionLifecycleAndInference) { 0); EXPECT_TRUE(output.empty()); - // 2. Audio with wrong sample rate fails + // 2. 采样率错误的音频失败 AudioPcmPayload wrong_rate; wrong_rate.sample_rate = 8000; wrong_rate.pcm_data = std::vector(16000, 0.01f); EXPECT_NE(asr_session->Transcribe(wrong_rate, opts, &output, &diagnostic), 0); - // 3. Audio too short (< 1600 samples) fails + // 3. 音频过短 (< 1600 个采样点) 时失败 AudioPcmPayload short_audio; short_audio.sample_rate = 16000; short_audio.pcm_data = std::vector(1500, 0.01f); EXPECT_NE(asr_session->Transcribe(short_audio, opts, &output, &diagnostic), 0); - // 4. Null output pointer fails + // 4. 输出指针为空时失败 AudioPcmPayload valid_audio; valid_audio.sample_rate = 16000; valid_audio.pcm_data = std::vector(16000, 0.01f); EXPECT_NE(asr_session->Transcribe(valid_audio, opts, nullptr, &diagnostic), 0); - // 5. Valid audio transcription + // 5. 合法音频转写 const std::string audio_path = "data/audio/nav_001.f32"; if (std::filesystem::is_regular_file(audio_path)) { std::ifstream ifs(audio_path, std::ios::binary); @@ -250,14 +249,14 @@ TEST(WhisperCppBackendTest, SessionLifecycleAndInference) { 0); EXPECT_FALSE(output.empty()); - // 6. Max output bytes boundary test + // 6. max_output_bytes 边界测试 opts.max_output_bytes = 2; output.clear(); EXPECT_NE(asr_session->Transcribe(jfk_audio, opts, &output, &diagnostic), 0); EXPECT_TRUE(output.empty()); - // 7. Concurrent calls on same session (serialized by session mutex) + // 7. 同一会话上的并发调用 (由会话互斥锁串行化) opts.max_output_bytes = 65536; auto f1 = std::async(std::launch::async, [&]() { std::string out1, diag1; diff --git a/tests/unit/nodes/test_asr_transcribe_node.cpp b/tests/unit/nodes/test_asr_transcribe_node.cpp index 56a58e14..6a512847 100644 --- a/tests/unit/nodes/test_asr_transcribe_node.cpp +++ b/tests/unit/nodes/test_asr_transcribe_node.cpp @@ -30,7 +30,6 @@ class AsrTranscribeNodeTest : public ::testing::Test { std::shared_ptr asr_model_; }; -// 1. Process Audio Transcription TEST_F(AsrTranscribeNodeTest, ProcessAudioTranscription) { auto node = NodeRegistry::Instance().Create("AsrTranscribeNode"); ASSERT_NE(node, nullptr); @@ -53,7 +52,7 @@ TEST_F(AsrTranscribeNodeTest, ProcessAudioTranscription) { EXPECT_EQ((*out)[0].data, "transcript:16000:16000"); } -// 2. Empty Audio Yields Empty Transcript +// 空音频产出空转写结果 TEST_F(AsrTranscribeNodeTest, EmptyAudioInput) { auto node = NodeRegistry::Instance().Create("AsrTranscribeNode"); ASSERT_NE(node, nullptr); @@ -69,7 +68,6 @@ TEST_F(AsrTranscribeNodeTest, EmptyAudioInput) { EXPECT_TRUE(out->empty()); } -// 3. Missing Audio Fails Closed TEST_F(AsrTranscribeNodeTest, MissingInputFailsClosed) { auto node = NodeRegistry::Instance().Create("AsrTranscribeNode"); ASSERT_NE(node, nullptr); diff --git a/tests/unit/nodes/test_common_nodes.cpp b/tests/unit/nodes/test_common_nodes.cpp index a97b76b1..daf89bf1 100644 --- a/tests/unit/nodes/test_common_nodes.cpp +++ b/tests/unit/nodes/test_common_nodes.cpp @@ -88,17 +88,17 @@ TEST_F(CommonNodesTest, ModelBindingsAreExplicitWithoutInstanceDefaults) { } } -// 1. TextTemplateNode: placeholder validation, join, overflow policy, control +// 1. TextTemplateNode:占位符校验、拼接、溢出策略、Control TEST_F(CommonNodesTest, TextTemplateNodeComprehensive) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); - // 1.1 Invalid placeholder should fail init + // 1.1 非法占位符应导致 Init 失败 nlohmann::json invalid_cfg = {{"template", "Hello {{unknown_variable}}!"}}; EXPECT_FALSE(InitNodeForTest(*node, invalid_cfg, session_ctx_.get(), nullptr, {"attributes"})); - // 1.2 Valid placeholder and static values + // 1.2 合法占位符和静态值 nlohmann::json valid_cfg = { {"template", "Prefix: {{tag}} | Query: {{primary}} | Context: {{context}}"}, @@ -128,15 +128,14 @@ TEST_F(CommonNodesTest, TextTemplateNodeComprehensive) { EXPECT_NE((*out)[0].data.find("What is LLM?"), std::string::npos); EXPECT_NE((*out)[0].data.find("Large Language Model"), std::string::npos); - // 1.3 Control update prompt + // 1.3 通过 Control 更新 prompt nlohmann::json update_json = {{"template", "NewTemplate: {{primary}}"}, {"values", nlohmann::json::object()}}; NodeControlResult c_res = node->Control(kControlCmdUpdatePrompt, update_json.dump()); EXPECT_EQ(c_res.status, NodeControlStatus::kHandled); - // Control affects the next request; each request owns a fresh write-once - // output namespace. + // Control 作用于下一个请求;每个请求拥有全新的、只写一次的输出命名空间。 AlgContext updated_ctx; updated_ctx.Publish("primary", primary); updated_ctx.Publish("context", context); @@ -146,7 +145,7 @@ TEST_F(CommonNodesTest, TextTemplateNodeComprehensive) { EXPECT_EQ((*out2)[0].data, "NewTemplate: What is LLM?"); } -// 1.4 TextTemplateNode attributes and sub_id preservation +// 1.4 TextTemplateNode 属性与 sub_id 保持 TEST_F(CommonNodesTest, TextTemplateNodeAttributesAndSubIdPreservation) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); @@ -184,7 +183,7 @@ TEST_F(CommonNodesTest, TextTemplateNodeAttributesAndSubIdPreservation) { EXPECT_EQ((*out)[1].data, "User: Bob | Role: User | Loc: Shanghai"); } -// 2. TextChunkNode: chunking, overlap, provenance +// 2. TextChunkNode:分块、重叠、来源追踪 TEST_F(CommonNodesTest, TextChunkNodeComprehensive) { auto node = NodeRegistry::Instance().Create("TextChunkNode"); ASSERT_NE(node, nullptr); @@ -194,7 +193,7 @@ TEST_F(CommonNodesTest, TextChunkNodeComprehensive) { AlgContext ctx; TextBatch input; - input.emplace_back(101, 0, "0123456789abcdefghij"); // 20 chars + input.emplace_back(101, 0, "0123456789abcdefghij"); // 20 个字符 ctx.Publish("text", input); EXPECT_EQ(node->Process(&ctx), 0); @@ -207,7 +206,7 @@ TEST_F(CommonNodesTest, TextChunkNodeComprehensive) { EXPECT_EQ((*out)[1].sub_id, 1u); } -// 3. TextRuleMatchNode: categories, regex named captures, constants, control +// 3. TextRuleMatchNode:类别、正则命名捕获、常量、Control TEST_F(CommonNodesTest, TextRuleMatchNodeComprehensive) { auto node = NodeRegistry::Instance().Create("TextRuleMatchNode"); ASSERT_NE(node, nullptr); @@ -233,7 +232,7 @@ TEST_F(CommonNodesTest, TextRuleMatchNodeComprehensive) { ASSERT_NE(out, nullptr); ASSERT_EQ(out->size(), 2u); - // Sample 1 hits both GREETING and NAVIGATION + // 样本 1 同时命中 GREETING 和 NAVIGATION const auto& first = (*out)[0].data; EXPECT_EQ(first.is_hit, 1); EXPECT_EQ(first.category, "GREETING"); @@ -247,10 +246,10 @@ TEST_F(CommonNodesTest, TextRuleMatchNodeComprehensive) { EXPECT_EQ(first.matches[1].rule_id, "nav_dest"); EXPECT_EQ(first.matches[1].category, "NAVIGATION"); - // Sample 2 no hit + // 样本 2 未命中 EXPECT_EQ((*out)[1].data.is_hit, 0); - // Dynamic rule update via Control + // 通过 Control 动态更新规则 nlohmann::json update_rules = {{"rules", {{{"id", "weather"}, {"strategy", "regex"}, @@ -272,8 +271,7 @@ TEST_F(CommonNodesTest, TextRuleMatchNodeComprehensive) { EXPECT_EQ((*out2)[0].data.slots["city"], "北京"); } -// 4. StructuredJsonParseNode: direct, markdown block, truncated input, failure -// policies +// 4. StructuredJsonParseNode:直接解析、Markdown 代码块、截断输入、失败策略 TEST_F(CommonNodesTest, StructuredJsonParseNodeComprehensive) { auto node = NodeRegistry::Instance().Create("StructuredJsonParseNode"); ASSERT_NE(node, nullptr); @@ -285,15 +283,15 @@ TEST_F(CommonNodesTest, StructuredJsonParseNodeComprehensive) { AlgContext ctx; TextBatch input; - // 1. Direct JSON + // 1. 直接 JSON input.emplace_back(1, 0, "{\"entities\": [\"Apple\", \"Google\"]}"); - // 2. Markdown block + // 2. Markdown 代码块 input.emplace_back( 2, 0, "Here is the result:\n```json\n{\"entities\": [\"DeepMind\"]}\n```"); - // 3. Unclosed array follows the configured failure policy + // 3. 未闭合的数组按配置的失败策略处理 input.emplace_back(3, 0, "Found entities: [\"TensorFlow\", \"PyTorch\""); - // 4. Broken text + // 4. 无法解析的文本 input.emplace_back(4, 0, "No valid json here at all"); ctx.Publish("text", input); @@ -311,7 +309,7 @@ TEST_F(CommonNodesTest, StructuredJsonParseNodeComprehensive) { EXPECT_EQ((*out)[3].data.json_payload, "{\"entities\":[]}"); } -// 5. TextEmbeddingNode: L2 normalization & session-level cache +// 5. TextEmbeddingNode:L2 归一化与会话级缓存 TEST_F(CommonNodesTest, TextEmbeddingNodeComprehensive) { auto node = NodeRegistry::Instance().Create("TextEmbeddingNode"); ASSERT_NE(node, nullptr); @@ -332,7 +330,7 @@ TEST_F(CommonNodesTest, TextEmbeddingNodeComprehensive) { ASSERT_NE(out1, nullptr); ASSERT_EQ(out1->size(), 2u); - // Subsequent call should reuse session cache seamlessly + // 后续调用应直接复用会话缓存 AlgContext ctx2; ctx2.Publish("text", input); EXPECT_EQ(node->Process(&ctx2), 0); @@ -342,7 +340,7 @@ TEST_F(CommonNodesTest, TextEmbeddingNodeComprehensive) { EXPECT_EQ((*out1)[0].data, (*out2)[0].data); } -// 6. VectorTopKNode: cosine similarity & shared candidate pool +// 6. VectorTopKNode:余弦相似度与共享候选池 TEST_F(CommonNodesTest, VectorTopKNodeComprehensive) { auto node = NodeRegistry::Instance().Create("VectorTopKNode"); ASSERT_NE(node, nullptr); @@ -358,11 +356,11 @@ TEST_F(CommonNodesTest, VectorTopKNodeComprehensive) { EmbeddingBatch candidates; candidates.emplace_back(0, 0, - std::vector{1.0f, 0.0f, 0.0f}); // sim 1.0 + std::vector{1.0f, 0.0f, 0.0f}); // 相似度 1.0 candidates.emplace_back( - 0, 1, std::vector{0.707f, 0.707f, 0.0f}); // sim 0.707 + 0, 1, std::vector{0.707f, 0.707f, 0.0f}); // 相似度 0.707 candidates.emplace_back(0, 2, - std::vector{0.0f, 1.0f, 0.0f}); // sim 0.0 + std::vector{0.0f, 1.0f, 0.0f}); // 相似度 0.0 ctx.Publish("candidates", candidates); TextBatch cand_texts; @@ -380,7 +378,7 @@ TEST_F(CommonNodesTest, VectorTopKNodeComprehensive) { EXPECT_EQ((*out)[1].data.text, "Partial match passage"); } -// 7. TextRerankNode: cross-encoder reranking +// 7. TextRerankNode:交叉编码器精排 TEST_F(CommonNodesTest, TextRerankNodeComprehensive) { auto node = NodeRegistry::Instance().Create("TextRerankNode"); ASSERT_NE(node, nullptr); @@ -405,7 +403,7 @@ TEST_F(CommonNodesTest, TextRerankNodeComprehensive) { ASSERT_EQ(out->size(), 1u); } -// 7.1 TextRerankNode combination constraints validation test +// 7.1 TextRerankNode 端口组合约束校验 TEST_F(CommonNodesTest, TextRerankCombinationConstraintsValidation) { RegisterTestBizs( {"custom_rerank_test"}, @@ -424,7 +422,7 @@ TEST_F(CommonNodesTest, TextRerankCombinationConstraintsValidation) { }); }; - // Test valid scheme 1: 'pairs' input only + // 合法组合 1:仅 'pairs' 输入 nlohmann::json valid_pipeline_pairs = { {"biz_name", "custom_rerank_test"}, {"models", @@ -441,7 +439,7 @@ TEST_F(CommonNodesTest, TextRerankCombinationConstraintsValidation) { auto plan_pairs = PipelineValidator::ValidateAndPlan(valid_pipeline_pairs); EXPECT_TRUE(plan_pairs.report.ok); - // Test valid scheme 2: 'queries' + 'candidates' + // 合法组合 2:'queries' + 'candidates' nlohmann::json valid_pipeline_qc = { {"biz_name", "custom_rerank_test"}, {"models", @@ -459,7 +457,7 @@ TEST_F(CommonNodesTest, TextRerankCombinationConstraintsValidation) { auto plan_qc = PipelineValidator::ValidateAndPlan(valid_pipeline_qc); EXPECT_TRUE(plan_qc.report.ok); - // Test valid scheme 3: 'queries' + 'candidate_texts' + // 合法组合 3:'queries' + 'candidate_texts' nlohmann::json valid_pipeline_qct = { {"biz_name", "custom_rerank_test"}, {"models", @@ -478,7 +476,7 @@ TEST_F(CommonNodesTest, TextRerankCombinationConstraintsValidation) { auto plan_qct = PipelineValidator::ValidateAndPlan(valid_pipeline_qct); EXPECT_TRUE(plan_qct.report.ok); - // Test invalid case 1: only candidates, missing queries + // 非法情形 1:只有 candidates,缺少 queries nlohmann::json bad_pipeline_1 = { {"biz_name", "custom_rerank_test"}, {"models", @@ -496,7 +494,7 @@ TEST_F(CommonNodesTest, TextRerankCombinationConstraintsValidation) { EXPECT_FALSE(plan1.report.ok); EXPECT_TRUE(has_constraint_err(plan1.report)); - // Test invalid case 2: only queries, missing candidates + // 非法情形 2:只有 queries,缺少 candidates nlohmann::json bad_pipeline_2 = { {"biz_name", "custom_rerank_test"}, {"models", @@ -514,7 +512,7 @@ TEST_F(CommonNodesTest, TextRerankCombinationConstraintsValidation) { EXPECT_FALSE(plan2.report.ok); EXPECT_TRUE(has_constraint_err(plan2.report)); - // Test invalid case 3: pairs + candidates (ambiguous/conflicting combination) + // 非法情形 3:pairs + candidates (组合冲突) nlohmann::json bad_pipeline_3 = { {"biz_name", "custom_rerank_test"}, {"models", @@ -532,7 +530,7 @@ TEST_F(CommonNodesTest, TextRerankCombinationConstraintsValidation) { EXPECT_FALSE(plan3.report.ok); EXPECT_TRUE(has_constraint_err(plan3.report)); - // Test invalid case 4: queries + candidates + candidate_texts (conflicting) + // 非法情形 4:queries + candidates + candidate_texts (冲突) nlohmann::json bad_pipeline_4 = { {"biz_name", "custom_rerank_test"}, {"models", @@ -587,7 +585,7 @@ class CountingEmbeddingModel final : public IEmbeddingModel { } // namespace -// 7.2 TextEmbeddingNode single-flight session caching concurrency test +// 7.2 TextEmbeddingNode 会话缓存 single-flight 并发测试 TEST_F(CommonNodesTest, TextEmbeddingNodeSingleFlightSessionCaching) { auto counting_model = std::make_shared(); ASSERT_TRUE(RegisterTestModel(session_ctx_->GetModelManager(), @@ -631,7 +629,7 @@ TEST_F(CommonNodesTest, TextEmbeddingNodeSingleFlightSessionCaching) { EXPECT_EQ(success_count.load(), kNumThreads); EXPECT_EQ(counting_model->infer_calls.load(), 1); - // Invalidation test: changing corpus triggers recomputation + // 失效测试:更换语料会触发重新计算 { AlgContext ctx; TextBatch updated_corpus; @@ -644,7 +642,7 @@ TEST_F(CommonNodesTest, TextEmbeddingNodeSingleFlightSessionCaching) { } } -// 8. LlmGenerateNode: prompt inference +// 8. LlmGenerateNode:prompt 推理 TEST_F(CommonNodesTest, LlmGenerateNodeComprehensive) { auto node = NodeRegistry::Instance().Create("LlmGenerateNode"); ASSERT_NE(node, nullptr); @@ -665,7 +663,7 @@ TEST_F(CommonNodesTest, LlmGenerateNodeComprehensive) { EXPECT_FALSE((*out)[0].data.empty()); } -// 9. AsrTranscribeNode: speech transcription +// 9. AsrTranscribeNode:语音转写 TEST_F(CommonNodesTest, AsrTranscribeNodeComprehensive) { auto node = NodeRegistry::Instance().Create("AsrTranscribeNode"); ASSERT_NE(node, nullptr); @@ -686,7 +684,7 @@ TEST_F(CommonNodesTest, AsrTranscribeNodeComprehensive) { EXPECT_FALSE((*out)[0].data.empty()); } -// 10. OcrDetectNode: OCR bounding box & text recognition +// 10. OcrDetectNode:OCR 检测框与文本识别 TEST_F(CommonNodesTest, OcrDetectNodeComprehensive) { auto node = NodeRegistry::Instance().Create("OcrDetectNode"); ASSERT_NE(node, nullptr); @@ -708,7 +706,7 @@ TEST_F(CommonNodesTest, OcrDetectNodeComprehensive) { EXPECT_FALSE((*out_text)[0].data.empty()); } -// 11. TextCorpusSourceNode: static corpus emission +// 11. TextCorpusSourceNode:静态语料输出 TEST_F(CommonNodesTest, TextCorpusSourceNodeComprehensive) { auto node = NodeRegistry::Instance().Create("TextCorpusSourceNode"); ASSERT_NE(node, nullptr); @@ -726,7 +724,7 @@ TEST_F(CommonNodesTest, TextCorpusSourceNodeComprehensive) { EXPECT_EQ((*out)[1].data, "Clause 2: Security"); } -// 12. StructuredJsonParseNode required_fields validation test +// 12. StructuredJsonParseNode required_fields 校验 TEST_F(CommonNodesTest, StructuredJsonParseNodeRequiredFields) { auto node = NodeRegistry::Instance().Create("StructuredJsonParseNode"); ASSERT_NE(node, nullptr); @@ -735,7 +733,7 @@ TEST_F(CommonNodesTest, StructuredJsonParseNodeRequiredFields) { {"failure_policy", "fail"}}; EXPECT_TRUE(InitNodeForTest(*node, cfg, session_ctx_.get())); - // Valid sample with required fields + // 包含必填字段的合法样本 { AlgContext ctx; TextBatch inputs; @@ -748,7 +746,7 @@ TEST_F(CommonNodesTest, StructuredJsonParseNodeRequiredFields) { EXPECT_TRUE((*doc)[0].data.is_valid); } - // Invalid sample missing required field 'risk_score' + // 缺少必填字段 'risk_score' 的非法样本 { AlgContext ctx; TextBatch inputs; @@ -758,17 +756,17 @@ TEST_F(CommonNodesTest, StructuredJsonParseNodeRequiredFields) { } } -// 13. TextTemplateNode missing variable failure test +// 13. TextTemplateNode 缺失变量时失败 TEST_F(CommonNodesTest, TextTemplateNodeMissingVariableFail) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); - // allow_dynamic_attributes is false by default + // allow_dynamic_attributes 默认为 false nlohmann::json cfg = {{"template", "Hello {{user_name}}, welcome!"}, {"allow_dynamic_attributes", true}}; EXPECT_TRUE(InitNodeForTest(*node, cfg, session_ctx_.get())); - // Attributes provided + // 提供属性 { AlgContext ctx; TextAttributesBatch attrs; @@ -870,7 +868,7 @@ void CheckScaffoldExecution(const std::string& name, const std::string& model, SessionContext* session) { auto node = NodeRegistry::Instance().Create(name); ASSERT_NE(node, nullptr); - // Resolved keys differ from logical names: exercise typed binding too. + // 解析后的键与逻辑名不同:同时覆盖类型化绑定。 ValidatedNodePlan plan; plan.normalized_config = {{"bind_model", model}}; plan.ports = {{"input", "source", BlackboardTypeTraits::TypeName(), @@ -929,7 +927,7 @@ TEST_F(CommonNodesTest, LlmGeneratePreservesBatchOptionsAndEmptyInputContract) { AlgContext ctx; ctx.Publish("prompt", prompts); ASSERT_EQ(node->Process(&ctx), 0) << ctx.GetErrorMessage(); - EXPECT_EQ(model->calls, before + 1); // Model owns batch scheduling. + EXPECT_EQ(model->calls, before + 1); // 批调度由 Model 负责。 ASSERT_EQ(model->prompts.size(), prompts.size()); const auto* output = ctx.Read("text"); ASSERT_NE(output, nullptr); @@ -1105,7 +1103,7 @@ TEST_F(CommonNodesTest, PromptRendersOriginalTemplateAndIsolatesRequests) { EXPECT_EQ((*output)[2].sub_id, 6U); EXPECT_EQ(model->last_options.max_tokens, 23); EXPECT_EQ(model->last_options.stop_words, std::vector{"END"}); - // Reuse the same Node with fresh request data; no prior context may survive. + // 用新的请求数据复用同一个 Node;之前的上下文不得残留。 AlgContext next; next.Publish("input", TextBatch{{29, 1, "next"}}); next.Publish("context", TextBatch{}); @@ -1195,7 +1193,7 @@ TEST_F(CommonNodesTest, auto model = std::make_shared(); ASSERT_TRUE(RegisterTestModel(session_ctx_->GetModelManager(), "prompt_contract", model, "v1")); - // 1. Malformed templates are rejected at validation and Init + // 1. 非法模板在校验和 Init 时被拒绝 for (const std::string pattern : {"{{unclosed", "{{unknown}}", "{{}}", "{{invalid name}}"}) { SCOPED_TRACE(pattern); @@ -1209,7 +1207,7 @@ TEST_F(CommonNodesTest, EXPECT_FALSE(InitNodeForTest(*node, config, session_ctx_.get())); } - // 2. Valid templates with JSON literal braces and {{input}} substitution + // 2. 含 JSON 字面花括号和 {{input}} 替换的合法模板 for (const auto& [pattern, expected] : std::vector>{ {"{\"text\": \"{{input}}\"}", "{\"text\": \"value\"}"}, @@ -1362,7 +1360,7 @@ TEST_F(CommonNodesTest, CustomAndGeneratedNodesUseStrictNativePlans) { auto plan = PipelineValidator::ValidateAndPlan(doc); ASSERT_TRUE(plan.report.ok) << plan.report.ToJson().dump(2); auto node = NodeRegistry::Instance().Create(name); - // Use the actual native plan, including normalized configuration and keys. + // 使用真实的原生计划,包括归一化后的配置和键。 const auto& node_plan = plan.node_plans.at("custom_prompt"); ASSERT_TRUE(node->Init({&node_plan, session_ctx_.get()})); AlgContext ctx; @@ -1389,8 +1387,7 @@ TEST_F(CommonNodesTest, StarterTextFunctionsFollowTheDocumentedExercise) { ASSERT_TRUE( node->Init({&plan.node_plans.at("custom_prompt"), session_ctx_.get()})); - // Out-of-order request IDs and nonzero sub-IDs must survive both text - // functions. + // 乱序的请求 ID 和非零 sub_id 经过两个文本函数后必须保持不变。 const TextBatch inputs{{51, 8, "张三"}, {19, 3, "李四"}}; AlgContext ctx; ctx.Publish("input_sentences", inputs); @@ -1405,7 +1402,7 @@ TEST_F(CommonNodesTest, StarterTextFunctionsFollowTheDocumentedExercise) { const auto expected = "实体抽取:\n" + inputs[i].data; EXPECT_EQ(model->prompts[i].data, expected); EXPECT_EQ((*output)[i].data, - expected); // Model's trailing newlines removed. + expected); // 去掉了 Model 输出末尾的换行。 EXPECT_EQ((*output)[i].req_id, inputs[i].req_id); EXPECT_EQ((*output)[i].sub_id, inputs[i].sub_id); EXPECT_EQ((*unchanged)[i].data, inputs[i].data); diff --git a/tests/unit/nodes/test_function_node.cpp b/tests/unit/nodes/test_function_node.cpp index 10213a5a..fc3ba013 100644 --- a/tests/unit/nodes/test_function_node.cpp +++ b/tests/unit/nodes/test_function_node.cpp @@ -45,7 +45,7 @@ auto ComplexConfig() { return false; } params->prefix = nested["prefix"].get(); - params->limit = -1; // The basic member binding must override this. + params->limit = -1; // 基础成员绑定必须覆盖此值。 return true; }); auto config = Parameters({ @@ -143,7 +143,7 @@ auto CleanSpec() { REGISTER_FUNCTION_NODE(CleanTextMapNode, CleanSpec()); -// Node returning NodeResult with failure on specific keyword +// 遇到特定关键字时返回失败 NodeResult 的 Node NodeResult FailableCleanFn(const std::string& in) { if (in == "FAIL") { return NodeResult::Failure( @@ -160,7 +160,7 @@ auto FailableSpec() { REGISTER_FUNCTION_NODE(FailableMapNode, FailableSpec()); -// Fails without its own message, so the framework names the Map function. +// 失败时不带自身消息,由框架给出 Map 函数名。 auto SilentFailureSpec() { return MakeMapSpec(Input("input"), Output("output"), [](const std::string& in) { @@ -174,7 +174,7 @@ auto SilentFailureSpec() { REGISTER_FUNCTION_NODE(SilentFailureMapNode, SilentFailureSpec()); -// Node without parameters returning plain string +// 无参数、返回普通 string 的 Node std::string UpperFn(const std::string& in) { std::string out = in; for (auto& c : out) c = static_cast(toupper(c)); @@ -192,7 +192,7 @@ REGISTER_FUNCTION_NODE(UpperMapNode, UpperSpec()); int move_only_map_calls = 0; int move_only_map_instances = 0; -// The callable owns its immutable resource; parameters remain copyable. +// 可调用对象持有其不可变资源;参数仍可拷贝。 auto MoveOnlyMapSpec() { auto transform = [owned = std::make_unique( @@ -224,7 +224,7 @@ auto MoveOnlyResultMapSpec() { REGISTER_FUNCTION_NODE(MoveOnlyResultMapNode, MoveOnlyResultMapSpec()); // --------------------------------------------------------------------------- -// Batch Fixtures +// Batch 夹具 // --------------------------------------------------------------------------- class CountingMockLlmModel final : public ILlmModel { @@ -404,7 +404,7 @@ NodeDefinition TwoStageDefinition() { REGISTER_NODE_WITH_DEFINITION(TwoStageHarnessNode, TwoStageDefinition()); -// Object logic batch node +// 对象逻辑 Batch Node struct LogicAnswerParams { std::string tag = "logic"; }; @@ -445,7 +445,7 @@ auto LogicBatchSpec() { REGISTER_FUNCTION_NODE(LogicBatchNode, LogicBatchSpec()); -// LLM Text shortcut node +// LLM 文本快捷 Node struct ShortcutParams { std::string suffix = "!"; }; @@ -705,7 +705,7 @@ inline auto ControlledMapSpec() { REGISTER_FUNCTION_NODE(ControlledMapNode, ControlledMapSpec()); // --------------------------------------------------------------------------- -// Non-Copyable Params (holding unique_ptr) without Controls (Problem 1) +// 不可拷贝的参数 (持有 unique_ptr),不带 Control // --------------------------------------------------------------------------- struct NonCopyableMapParams { std::string prefix; @@ -771,7 +771,7 @@ inline auto NonCopyableBatchSpec() { REGISTER_FUNCTION_NODE(NonCopyableBatchNode, NonCopyableBatchSpec()); // --------------------------------------------------------------------------- -// Strict Unplanned Fact Checking Node (Problem 2) +// 严格的无计划事实检查 Node // --------------------------------------------------------------------------- struct ControlledBatchInputs { @@ -924,7 +924,7 @@ REGISTER_FUNCTION_NODE(ComplexStateControlAuthorNode, } // namespace // =========================================================================== -// Tests +// 测试 // =========================================================================== // A1: Map 多请求/非零 sub_id 保序、输入未修改、空批次不调用;中间项失败无输出 @@ -962,7 +962,7 @@ TEST(FunctionNodeTest, MapPreservesOrderingAndProvenanceAcrossRequests) { (std::vector{"PRE:first", "PRE:second", "PRE:third"})); } -// Every capability call shares one move-only ownership contract. +// 所有能力调用共享同一份仅可移动的所有权契约。 template void ExpectModelCallContract(const char* default_slot) { static_assert(std::is_same_v); @@ -1249,7 +1249,7 @@ TEST(FunctionNodeTest, BatchOptionalPortUnconnectedGivesNullptr) { auto result = harness.Run(); ASSERT_TRUE(result.ok()) << result.diagnostic(); - // Without context, output won't have [CTX:...] + // 无上下文时,输出不含 [CTX:...] EXPECT_EQ(result.TextValues("output"), (std::vector{"final:ans:What is AI?"})); EXPECT_EQ(mock_model->call_count, 1); @@ -1277,7 +1277,7 @@ TEST(FunctionNodeTest, BatchOptionalPortConnectedButMissingFailsClosed) { harness.Config({{"bind_model", "test_llm"}}); harness.BindModel("test_llm", mock_model); harness.TextInput("questions", {"What is AI?"}); - // context is in plan, but NOT supplied to AlgContext + // 计划中有 context,但未提供给 AlgContext auto result = harness.Run(); EXPECT_FALSE(result.ok()); @@ -1408,7 +1408,7 @@ TEST(FunctionNodeTest, BatchSingleModelCallAndOptionsPassing) { // M2: Model 返回失败、少项、乱序、错 req/sub 全部失败无输出 TEST(FunctionNodeTest, BatchModelFailuresFailClosedWithoutOutput) { - // Case 1: Model returns inference failure + // 情形 1:模型返回推理失败 { auto mock_model = std::make_shared(); mock_model->always_fail = true; @@ -1423,7 +1423,7 @@ TEST(FunctionNodeTest, BatchModelFailuresFailClosedWithoutOutput) { EXPECT_EQ(result.Output("output"), nullptr); } - // Case 2: Model returns wrong count (fewer items) + // 情形 2:模型返回的条目数不对 (偏少) { auto mock_model = std::make_shared(); mock_model->return_wrong_count = true; @@ -1438,7 +1438,7 @@ TEST(FunctionNodeTest, BatchModelFailuresFailClosedWithoutOutput) { EXPECT_EQ(result.Output("output"), nullptr); } - // Case 3: Model returns corrupt provenance + // 情形 3:模型返回损坏的来源信息 { auto mock_model = std::make_shared(); mock_model->corrupt_provenance = true; @@ -1457,7 +1457,7 @@ TEST(FunctionNodeTest, BatchModelFailuresFailClosedWithoutOutput) { // M3: 条件二次调用、失败后显式重试成功、耗尽后失败;Context 最终错误一致 TEST(FunctionNodeTest, BatchModelCallRetrySucceedsWithoutPollutingContext) { auto mock_model = std::make_shared(); - mock_model->fail_first_n = 1; // Fails on attempt 1, succeeds on attempt 2 + mock_model->fail_first_n = 1; // 第 1 次失败,第 2 次成功 NodeHarness harness("AnswerBatchNode"); harness.Config({{"bind_model", "test_llm"}, {"max_revisions", 2}}); @@ -1471,11 +1471,11 @@ TEST(FunctionNodeTest, BatchModelCallRetrySucceedsWithoutPollutingContext) { EXPECT_EQ(result.TextValues("output"), (std::vector{"final:ans:Q1"})); - // Ensure AlgContext is completely clean! + // AlgContext 必须完全干净 EXPECT_TRUE(result.Context()->IsOk()); } -// Ordinary logic object Run +// 普通逻辑对象的 Run TEST(FunctionNodeTest, BatchLogicClassExecutesPerRequest) { auto mock_model = std::make_shared(); NodeHarness harness("LogicBatchNode"); @@ -1489,7 +1489,7 @@ TEST(FunctionNodeTest, BatchLogicClassExecutesPerRequest) { (std::vector{"ans:my_tag:hello"})); } -// MakeLlmTextSpec shortcut +// MakeLlmTextSpec 快捷方式 TEST(FunctionNodeTest, LlmFormattingFailureDoesNotPublishPartiallyFormattedBatch) { for (const char* name : {"FailingFormatNode", "FailingParameterFormatNode"}) { @@ -1506,7 +1506,7 @@ TEST(FunctionNodeTest, EXPECT_EQ(result.process_code(), -9876); EXPECT_EQ(result.Output("output"), nullptr); EXPECT_EQ(formatting_calls, - 2); // First succeeded; second stopped the batch. + 2); // 第一个成功;第二个中止了批次。 EXPECT_EQ(model->call_count, 1); } } @@ -1618,19 +1618,19 @@ TEST(FunctionNodeTest, ProcessFailedPreservesAlgContextForOutputInspection) { } TEST(FunctionNodeTest, BindingValidationEnforcedInInitAndHarness) { - // Map node test + // Map Node 测试 const auto map_def = PipelineCatalog::FindNode("BindingMapNode"); ASSERT_TRUE(map_def.has_value()); ASSERT_TRUE(map_def->validate_config); - // 1. Map Definition preflight rejects configuration when extra port missing + // 1. 缺少额外端口时,Map Definition 预检拒绝配置 std::string map_diag; EXPECT_FALSE(map_def->validate_config({{"require_extra", true}}, {"input"}, &map_diag)); EXPECT_NE(map_diag.find("require_extra requires extra port"), std::string::npos); - // 2. Map Unplanned Init fails when extra port missing + // 2. 缺少额外端口时,无计划的 Map Init 失败 auto map_unplanned = NodeRegistry::Instance().Create("BindingMapNode"); ASSERT_NE(map_unplanned, nullptr); nlohmann::json invalid_map_cfg = {{"require_extra", true}}; @@ -1644,7 +1644,7 @@ TEST(FunctionNodeTest, BindingValidationEnforcedInInitAndHarness) { EXPECT_FALSE(map_unplanned->Init(init_ctx_map_unplanned)); EXPECT_NE(map_init_diag.find("ValidatedNodePlan"), std::string::npos); - // 3. Map Manual Plan Init fails when extra port missing + // 3. 缺少额外端口时,手动计划的 Map Init 失败 auto map_manual = NodeRegistry::Instance().Create("BindingMapNode"); ASSERT_NE(map_manual, nullptr); ValidatedNodePlan manual_map_plan; @@ -1665,17 +1665,17 @@ TEST(FunctionNodeTest, BindingValidationEnforcedInInitAndHarness) { EXPECT_NE(manual_map_diag.find("require_extra requires extra port"), std::string::npos); - // 4. Batch node tests + // 4. Batch Node 测试 const auto def = PipelineCatalog::FindNode("BindingTestNode"); ASSERT_TRUE(def.has_value()); ASSERT_TRUE(def->validate_config); - // Batch Definition preflight rejects configuration when mask is missing + // 缺少 mask 时,Batch Definition 预检拒绝配置 std::string diag; EXPECT_FALSE(def->validate_config({{"check_mask", true}}, {"texts"}, &diag)); EXPECT_NE(diag.find("check_mask requires mask port"), std::string::npos); - // Batch Manual Plan Init fails when mask is not connected + // mask 未连接时,手动计划的 Batch Init 失败 auto node_manual = NodeRegistry::Instance().Create("BindingTestNode"); ASSERT_NE(node_manual, nullptr); ValidatedNodePlan manual_plan; @@ -1697,7 +1697,7 @@ TEST(FunctionNodeTest, BindingValidationEnforcedInInitAndHarness) { EXPECT_NE(manual_diag.find("check_mask requires mask port"), std::string::npos); - // 5. NodeHarness fails Init when optional port is omitted + // 5. 省略可选端口时 NodeHarness 的 Init 失败 NodeHarness harness_fail("BindingTestNode"); harness_fail.Config({{"check_mask", true}}); harness_fail.OmitPortFromPlan("mask"); @@ -1708,7 +1708,7 @@ TEST(FunctionNodeTest, BindingValidationEnforcedInInitAndHarness) { EXPECT_NE(result_fail.diagnostic().find("check_mask requires mask port"), std::string::npos); - // 6. NodeHarness succeeds when mask is connected + // 6. 连接 mask 后 NodeHarness 成功 NodeHarness harness_ok("BindingTestNode"); harness_ok.Config({{"check_mask", true}}); harness_ok.TextInput("texts", {"hello"}); @@ -1723,8 +1723,7 @@ TEST(FunctionNodeTest, PlannedPortBindingsPreserveAuthorDiagnosticsAndOrder) { struct Case { const char* node; const char* port; - int fault; // 0: absent, 1: empty key and wrong type, 2: wrong type, 3: - // valid. + int fault; // 0: 缺失,1: 空键且类型错误,2: 类型错误,3: 合法 const char* expected; }; const Case cases[] = { @@ -1782,7 +1781,7 @@ TEST(FunctionNodeTest, PlannedPortBindingsPreserveAuthorDiagnosticsAndOrder) { broken ? "integer" : "TextBatch", "1:1", "preserve", "request", name == "output" ? PortDirection::kOutput : PortDirection::kInput}); } - // An input error must win even when the later output is invalid too. + // 即使后面的输出也非法,也必须优先报告输入错误。 if (std::string(test.port) != "output" && *test.expected != '\0') { plan.ports.back().type_id = "integer"; } @@ -1846,7 +1845,7 @@ TEST(FunctionNodeTest, ComplexParserMatchesPreflightInitAndOwnsConfiguration) { EXPECT_EQ(init_error, preflight_error); continue; } - // Neither the caller's document nor the plan may remain the Params owner. + // 调用方的文档和计划都不能继续持有 Params。 config["nested"]["prefix"] = "changed:"; plan.normalized_config["nested"]["prefix"] = "changed:"; AlgContext context; @@ -1879,7 +1878,7 @@ TEST(FunctionNodeTest, LogicObjectIsRecreatedForEachProcessOnSameNode) { } // --------------------------------------------------------------------------- -// ConfigurationSnapshot & Direct Concurrency +// ConfigurationSnapshot 与直接并发 // --------------------------------------------------------------------------- TEST(ConfigurationSnapshotTest, UninitializedAndNullStateHandled) { @@ -1908,9 +1907,8 @@ TEST(ConfigurationSnapshotTest, InitializeAndRead) { } TEST(ConfigurationSnapshotTest, WriterSerializationAndIndependentPatchMerging) { - // writer A updates prefix, writer B updates suffix. - // Both successful updates are preserved; B cannot submit based on stale pre-A - // state. + // 写者 A 更新 prefix,写者 B 更新 suffix。 + // 两次成功更新都会保留;B 不能基于 A 之前的过期状态提交。 struct TwoFields { std::string prefix; std::string suffix; @@ -1924,7 +1922,7 @@ TEST(ConfigurationSnapshotTest, WriterSerializationAndIndependentPatchMerging) { std::atomic b_saw_a_prefix{false}; - // Writer A holds the writer lock while B attempts to run + // 写者 A 持有写锁时 B 尝试运行 std::thread thread_a([&]() { snapshot.Update([&](const TwoFields& cur) { a_entered_lock.set_value(); @@ -1937,7 +1935,7 @@ TEST(ConfigurationSnapshotTest, WriterSerializationAndIndependentPatchMerging) { a_entered_lock.get_future().wait(); - // Writer B attempts to update suffix while A is in Update callback + // A 处于 Update 回调中时,写者 B 尝试更新 suffix std::thread thread_b([&]() { b_called.set_value(); snapshot.Update([&](const TwoFields& cur) { @@ -1951,10 +1949,10 @@ TEST(ConfigurationSnapshotTest, WriterSerializationAndIndependentPatchMerging) { }); b_called.get_future().wait(); - // A owns the transaction before B is launched. No timing assumption is - // needed: B must observe A's published value whenever it acquires the lock. + // B 启动前 A 已持有事务。无需时序假设: + // B 获取锁时必然看到 A 已发布的值。 - // Release A so it publishes its update + // 放行 A,使其发布更新 release_a.set_value(); thread_a.join(); @@ -1968,8 +1966,8 @@ TEST(ConfigurationSnapshotTest, WriterSerializationAndIndependentPatchMerging) { } TEST(ConfigurationSnapshotTest, FailedWriterRollbackPreservesActiveState) { - // Writer fails during validation / candidate generation -> no new snapshot - // published; old state remains active and intact. + // 写者在校验/候选生成阶段失败 -> 不发布新快照; + // 旧状态保持生效且完好。 struct State { std::string val; int rev; @@ -1989,7 +1987,7 @@ TEST(ConfigurationSnapshotTest, FailedWriterRollbackPreservesActiveState) { EXPECT_EQ(cur->val, "original"); EXPECT_EQ(cur->rev, 1); - // Also test exception in candidate building + // 同时测试候选构建中的异常 auto res_ex = snapshot.Update([](const State&) -> NodeResult { throw std::runtime_error("candidate throw"); }); @@ -1998,9 +1996,8 @@ TEST(ConfigurationSnapshotTest, FailedWriterRollbackPreservesActiveState) { } TEST(ConfigurationSnapshotTest, ReaderHoldsOldSnapshotWhileWriterPublishes) { - // Reader holding old snapshot is isolated from concurrent writer publication; - // old reader completes safely with old version; subsequent reader sees new - // version. + // 持有旧快照的读者不受并发写者发布的影响;旧读者使用旧版本安全完成, + // 后续读者看到新版本。 struct State { std::string val; int rev; @@ -2101,7 +2098,7 @@ TEST(ConfigurationSnapshotTest, MoveOnlyStateHandled) { } // --------------------------------------------------------------------------- -// Functional Spec WithControls & NodeHarness Tests +// 函数式 Spec 的 WithControls 与 NodeHarness 测试 // --------------------------------------------------------------------------- TEST(FunctionNodeTest, FunctionalMapSpecWithControlsReplaceAndPatch) { @@ -2115,29 +2112,29 @@ TEST(FunctionNodeTest, FunctionalMapSpecWithControlsReplaceAndPatch) { EXPECT_EQ(res1.TextValues("output"), (std::vector{"init_p:payload:init_s"})); - // 1. ReplaceFields missing multiplier -> rejected + // 1. ReplaceFields 缺少 multiplier -> 拒绝 auto bad_replace = harness.Control(kCmdReplaceMap, R"({"prefix":"new_p:"})"); EXPECT_EQ(bad_replace.status, NodeControlStatus::kFailed); EXPECT_NE(bad_replace.message.find("multiplier"), std::string::npos); - // State preserved + // 状态保持不变 auto res2 = harness.Run(); ASSERT_TRUE(res2.ok()); EXPECT_EQ(res2.TextValues("output"), (std::vector{"init_p:payload:init_s"})); - // 2. ReplaceFields with all declared fields -> handled + // 2. ReplaceFields 带齐所有声明字段 -> 处理成功 auto good_replace = harness.Control(kCmdReplaceMap, R"({"prefix":"rep_p:","multiplier":2})"); EXPECT_EQ(good_replace.status, NodeControlStatus::kHandled); - // Undeclared suffix remains ":init_s", prefix and multiplier updated + // 未声明的 suffix 仍为 ":init_s",prefix 和 multiplier 已更新 auto res3 = harness.Run(); ASSERT_TRUE(res3.ok()); EXPECT_EQ(res3.TextValues("output"), (std::vector{"rep_p:payloadpayload:init_s"})); - // 3. PatchFields with subset of fields -> handled + // 3. PatchFields 只带部分字段 -> 处理成功 auto good_patch = harness.Control(kCmdPatchMap, R"({"suffix":":patch_s"})"); EXPECT_EQ(good_patch.status, NodeControlStatus::kHandled); @@ -2146,11 +2143,11 @@ TEST(FunctionNodeTest, FunctionalMapSpecWithControlsReplaceAndPatch) { EXPECT_EQ(res4.TextValues("output"), (std::vector{"rep_p:payloadpayload:patch_s"})); - // 4. PatchFields with empty object -> rejected + // 4. PatchFields 传空对象 -> 拒绝 auto empty_patch = harness.Control(kCmdPatchMap, R"({})"); EXPECT_EQ(empty_patch.status, NodeControlStatus::kFailed); - // 5. Semantic validator failure -> rejected and state rolled back + // 5. 语义校验失败 -> 拒绝并回滚状态 auto invalid_prefix = harness.Control(kCmdPatchMap, R"({"prefix":"INVALID"})"); EXPECT_EQ(invalid_prefix.status, NodeControlStatus::kFailed); @@ -2162,7 +2159,7 @@ TEST(FunctionNodeTest, FunctionalMapSpecWithControlsReplaceAndPatch) { EXPECT_EQ(res5.TextValues("output"), (std::vector{"rep_p:payloadpayload:patch_s"})); - // 6. Unknown command -> unsupported + // 6. 未知命令 -> 不支持 auto unk = harness.Control(9999, R"({})"); EXPECT_EQ(unk.status, NodeControlStatus::kUnsupported); } @@ -2177,7 +2174,7 @@ TEST(FunctionNodeTest, FunctionalBatchSpecWithControlsAndValidation) { EXPECT_EQ(res1.TextValues("output"), (std::vector{"H:abc", "H:def"})); - // Replace update with uppercase = true + // 用 uppercase = true 做 Replace 更新 auto ctrl1 = harness.Control(kCmdReplaceBatch, R"({"header":"G:","uppercase":true})"); EXPECT_EQ(ctrl1.status, NodeControlStatus::kHandled); @@ -2187,13 +2184,13 @@ TEST(FunctionNodeTest, FunctionalBatchSpecWithControlsAndValidation) { EXPECT_EQ(res2.TextValues("output"), (std::vector{"G:ABC", "G:DEF"})); - // Semantic rejection + // 语义拒绝 auto ctrl2 = harness.Control(kCmdReplaceBatch, R"({"header":"REJECT","uppercase":true})"); EXPECT_EQ(ctrl2.status, NodeControlStatus::kFailed); EXPECT_NE(ctrl2.message.find("Rejected header"), std::string::npos); - // Preserved on failure + // 失败时保持不变 auto res3 = harness.Run(); ASSERT_TRUE(res3.ok()); EXPECT_EQ(res3.TextValues("output"), @@ -2233,8 +2230,8 @@ TEST(FunctionNodeTest, WholeBatchProcessConsistencyDuringControl) { old_ctx.Publish("bk_in_input", batch); test_support::NodeProcessPause pause; auto reader = std::async(std::launch::async, [&] { - // The first allocation is outputs.reserve, after AuthorNode has acquired - // its parameter snapshot. The callback is confined to this reader thread. + // 第一次分配是 outputs.reserve,发生在 AuthorNode 获取参数快照之后。 + // 回调仅限于该读者线程。 test_support::ScopedNextAllocationCallback callback( &test_support::NodeProcessPause::OnAllocation, &pause); return node->Process(&old_ctx); @@ -2288,7 +2285,7 @@ TEST(FunctionNodeTest, WholeBatchProcessConsistencyDuringControlForBatchSpec) { old_ctx.Publish("bk_in_texts", batch); test_support::NodeProcessPause pause; auto reader = std::async(std::launch::async, [&] { - // ControlledBatchFn reserves output after AuthorNode acquires its snapshot. + // ControlledBatchFn 在 AuthorNode 获取快照后预留输出。 test_support::ScopedNextAllocationCallback callback( &test_support::NodeProcessPause::OnAllocation, &pause); return node->Process(&old_ctx); @@ -2328,7 +2325,7 @@ TEST(FunctionNodeTest, WholeBatchProcessConsistencyDuringControlForBatchSpec) { TEST(FunctionNodeTest, SpecWithNonCopyableParamsCompilesAndExecutesWithoutControls) { - // Verifies MapSpec with non-copyable ParamsT (containing unique_ptr) + // 验证 ParamsT 不可拷贝 (含 unique_ptr) 的 MapSpec NodeHarness map_harness("NonCopyableMapNode"); map_harness.Config({{"prefix", "map_nc:"}}); map_harness.TextInput("input", {"hello", "world"}); @@ -2340,7 +2337,7 @@ TEST(FunctionNodeTest, auto ctrl_map = map_harness.Control(1001, R"({})"); EXPECT_EQ(ctrl_map.status, NodeControlStatus::kUnsupported); - // Verifies BatchSpec with non-copyable ParamsT (containing unique_ptr) + // 验证 ParamsT 不可拷贝 (含 unique_ptr) 的 BatchSpec NodeHarness batch_harness("NonCopyableBatchNode"); batch_harness.Config({{"tag", "batch_nc:"}}); batch_harness.TextInput("texts", {"foo", "bar"}); @@ -2373,42 +2370,42 @@ TEST(FunctionNodeTest, DeclarationValidationRejectsInvalidControlCommands) { }); }; - // 1. Invalid command ID (<= 0) + // 1. 非法命令 ID (<= 0) EXPECT_THROW(ValidateControlCommands({ReplaceFields(0, "set_text", {"text"})}, make_params()), std::invalid_argument); - // 2. Empty command name + // 2. 命令名为空 EXPECT_THROW(ValidateControlCommands({ReplaceFields(1001, "", {"text"})}, make_params()), std::invalid_argument); - // 3. Duplicate command ID + // 3. 命令 ID 重复 EXPECT_THROW( ValidateControlCommands({ReplaceFields(1001, "cmd_a", {"text"}), ReplaceFields(1001, "cmd_b", {"count"})}, make_params()), std::invalid_argument); - // 4. Duplicate command name + // 4. 命令名重复 EXPECT_THROW( ValidateControlCommands({ReplaceFields(1001, "same_name", {"text"}), ReplaceFields(1002, "same_name", {"count"})}, make_params()), std::invalid_argument); - // 5. Empty field names + // 5. 字段名为空 EXPECT_THROW( ValidateControlCommands({ReplaceFields(1001, "cmd", {})}, make_params()), std::invalid_argument); - // 6. Duplicate field name in same command + // 6. 同一命令内字段名重复 EXPECT_THROW( ValidateControlCommands({ReplaceFields(1001, "cmd", {"text", "text"})}, make_params()), std::invalid_argument); - // 7. Unbound field name + // 7. 字段名未绑定 EXPECT_THROW( ValidateControlCommands({ReplaceFields(1001, "cmd", {"non_existent"})}, make_params()), @@ -2524,12 +2521,12 @@ TEST(FunctionNodeTest, WithParserWithControlsRequiresExplicitPrepare) { }) .WithParser(std::move(parser)); - // HasParser() == true, commands not empty, HasPrepare() == false -> throws + // HasParser() == true、commands 非空且 HasPrepare() == false -> 抛异常 EXPECT_THROW(ValidateControlCommands( {ReplaceFields(1001, "set_text", {"text"})}, params), std::invalid_argument); - // Adding Prepare allows validation to pass + // 加上 Prepare 后校验通过 params.Prepare( [](DummyParams*, const BindingFacts&, std::string*) { return true; }); EXPECT_NO_THROW(ValidateControlCommands( @@ -2595,7 +2592,7 @@ inline auto BindingFactsProbeSpec() { REGISTER_FUNCTION_NODE(BindingFactsProbeNode, BindingFactsProbeSpec()); TEST(FunctionNodeTest, AuthorNodeInitPassesRealBindingFactsToPrepare) { - // Test planned execution: plan_seen must be true, input must be connected + // 测试有计划的执行:plan_seen 必须为 true,且输入必须已连接 NodeHarness harness_planned("BindingFactsProbeNode"); harness_planned.TextInput("input", {"hello"}); auto res_planned = harness_planned.Run(); @@ -2615,7 +2612,7 @@ TEST(FunctionNodeTest, RapidInterleavedControlsAndConcurrentProcesses) { const auto start = start_signal.get_future().share(); std::atomic successful_processes{0}; - // Writer thread 1: rapid ReplaceFields + // 写线程 1:快速 ReplaceFields std::thread writer1([&]() { start.wait(); for (int i = 1; i <= 30; ++i) { @@ -2628,7 +2625,7 @@ TEST(FunctionNodeTest, RapidInterleavedControlsAndConcurrentProcesses) { } }); - // Writer thread 2: rapid PatchFields + // 写线程 2:快速 PatchFields std::thread writer2([&]() { start.wait(); for (int i = 1; i <= 30; ++i) { @@ -2639,7 +2636,7 @@ TEST(FunctionNodeTest, RapidInterleavedControlsAndConcurrentProcesses) { } }); - // Multiple reader threads: concurrent Process calls with multi-item batches + // 多个读线程:用多条目批次并发调用 Process std::vector readers; for (int r = 0; r < 4; ++r) { readers.emplace_back([&, r]() { @@ -2659,9 +2656,9 @@ TEST(FunctionNodeTest, RapidInterleavedControlsAndConcurrentProcesses) { ASSERT_NE(out, nullptr); ASSERT_EQ(out->size(), 8u); - // Verify intra-batch snapshot consistency: - // Item format: prefix + (multiplier * "payload_i") + suffix - // Extract prefix, suffix, multiplier from item 0: + // 验证批内快照一致性: + // 条目格式:prefix + (multiplier * "payload_i") + suffix + // 从条目 0 提取 prefix、suffix 和 multiplier: const std::string& item0 = (*out)[0].data; auto pos0 = item0.find("payload_0"); ASSERT_NE(pos0, std::string::npos); @@ -2675,8 +2672,7 @@ TEST(FunctionNodeTest, RapidInterleavedControlsAndConcurrentProcesses) { multiplier++; sp += std::string("payload_0").size(); } - // Every other item in this batch must match the exact same snapshot - // parameters: + // 本批中其他条目必须与同一份快照参数完全一致: for (int i = 1; i < 8; ++i) { std::string expected = prefix; for (int m = 0; m < multiplier; ++m) { @@ -2700,7 +2696,7 @@ TEST(FunctionNodeTest, RapidInterleavedControlsAndConcurrentProcesses) { EXPECT_EQ(successful_processes.load(), 4 * 30); - // Verify node remains in a coherent final state + // 验证 Node 最终仍处于一致状态 AlgContext final_ctx; final_ctx.Publish("bk_in_input", TextBatch{{999, 0, "final"}}); ASSERT_EQ(node->Process(&final_ctx), 0); diff --git a/tests/unit/nodes/test_llm_generate_node.cpp b/tests/unit/nodes/test_llm_generate_node.cpp index 4104263a..0cc22365 100644 --- a/tests/unit/nodes/test_llm_generate_node.cpp +++ b/tests/unit/nodes/test_llm_generate_node.cpp @@ -123,7 +123,6 @@ TEST_F(LlmGenerateNodeTest, ConcurrentModelCallsKeepIndependentReasons) { std::string::npos); } -// 1. Process Batch Prompt Inference TEST_F(LlmGenerateNodeTest, ProcessBatchPromptInference) { auto node = NodeRegistry::Instance().Create("LlmGenerateNode"); ASSERT_NE(node, nullptr); @@ -181,7 +180,7 @@ TEST_F(LlmGenerateNodeTest, ValidatorAndInitializationRejectInvalidOptions) { {{"top_k", -1}}, {{"top_p", 1.0e-10}}, {{"repetition_penalty", 0.0}}}; - // No Model construction is needed to check the Node's preflight diagnostics. + // 检查 Node 的预检诊断无需构造 Model。 for (auto config : invalid) { SCOPED_TRACE(config.dump()); config["bind_model"] = "llm_model_v1"; @@ -215,7 +214,6 @@ TEST_F(LlmGenerateNodeTest, ValidatorAndInitializationRejectInvalidOptions) { << diagnostic; } -// 2. Missing Prompt Fails Closed TEST_F(LlmGenerateNodeTest, MissingInputFailsClosed) { auto node = NodeRegistry::Instance().Create("LlmGenerateNode"); ASSERT_NE(node, nullptr); diff --git a/tests/unit/nodes/test_ocr_detect_node.cpp b/tests/unit/nodes/test_ocr_detect_node.cpp index 184b33da..7dcedeee 100644 --- a/tests/unit/nodes/test_ocr_detect_node.cpp +++ b/tests/unit/nodes/test_ocr_detect_node.cpp @@ -30,7 +30,6 @@ class OcrDetectNodeTest : public ::testing::Test { std::shared_ptr ocr_model_; }; -// 1. Process OCR Document Detection TEST_F(OcrDetectNodeTest, ProcessOcrDetection) { auto node = NodeRegistry::Instance().Create("OcrDetectNode"); ASSERT_NE(node, nullptr); @@ -54,7 +53,6 @@ TEST_F(OcrDetectNodeTest, ProcessOcrDetection) { EXPECT_EQ((*out_text)[0].data, "recognized:mock_invoice.jpg"); } -// 2. Missing Input Images Fails Closed TEST_F(OcrDetectNodeTest, MissingInputFailsClosed) { auto node = NodeRegistry::Instance().Create("OcrDetectNode"); ASSERT_NE(node, nullptr); diff --git a/tests/unit/nodes/test_parameter_binding.cpp b/tests/unit/nodes/test_parameter_binding.cpp index 5340d9ae..ece78a26 100644 --- a/tests/unit/nodes/test_parameter_binding.cpp +++ b/tests/unit/nodes/test_parameter_binding.cpp @@ -74,7 +74,7 @@ TEST(ParameterBindingTest, SuccessfulParseWithDefaultsAndOverrides) { .Description("List of tags"), }); - // Empty config -> defaults apply + // 空配置 -> 使用默认值 std::string err; auto default_params = schema.Parse(nlohmann::json::object(), &err); ASSERT_TRUE(default_params.has_value()) << err; @@ -86,7 +86,7 @@ TEST(ParameterBindingTest, SuccessfulParseWithDefaultsAndOverrides) { ASSERT_EQ(default_params->tags.size(), 1u); EXPECT_EQ(default_params->tags[0], "default_tag"); - // Custom values + // 自定义值 nlohmann::json custom = { {"mode", "slow"}, {"count", 42}, {"big_id", 99999999999LL}, {"ratio", 0.75}, {"enabled", true}, {"tags", {"a", "b"}}, @@ -179,7 +179,7 @@ TEST(ParameterBindingTest, RejectsIntegerOverflowFor32Bit) { }); std::string err; - // Int64 overflow for 32-bit int + // Int64 值超出 32 位 int 范围 int64_t overflow_val = static_cast(std::numeric_limits::max()) + 100LL; auto res = schema.Parse({{"count", overflow_val}}, &err); @@ -187,7 +187,7 @@ TEST(ParameterBindingTest, RejectsIntegerOverflowFor32Bit) { } TEST(ParameterBindingTest, RejectsInvalidDefaultAtConstruction) { - // Out-of-range default throws during construction + // 超出范围的默认值在构造时抛异常 EXPECT_THROW( { auto schema = Parameters({ @@ -244,16 +244,16 @@ TEST(ParameterBindingTest, SemanticValidatorAndBindingsHook) { }); std::string err; - // Semantic validation failure + // 语义校验失败 auto res = schema.Parse({{"mode", "custom"}, {"count", 5}}, &err); EXPECT_FALSE(res.has_value()); EXPECT_EQ(err, "custom mode requires count >= 10"); - // Semantic validation pass + // 语义校验通过 res = schema.Parse({{"mode", "custom"}, {"count", 15}}, &err); EXPECT_TRUE(res.has_value()); - // Binding validation check + // 绑定校验 nlohmann::json norm = {{"mode", "custom"}, {"count", 15}}; bool bind_ok = schema.ValidateWithBindings(norm, {"other_input"}, &err); EXPECT_FALSE(bind_ok); @@ -319,7 +319,7 @@ TEST(ParameterBindingTest, std::string err; nlohmann::json norm = {{"mode", "custom"}}; - // Connected inputs is empty set {} - must still enforce binding validation! + // 已连接输入为空集 {} 时仍须执行绑定校验! bool bind_ok = schema.ValidateWithBindings(norm, {}, &err); EXPECT_FALSE(bind_ok); EXPECT_NE(err.find("custom mode requires context port"), std::string::npos); diff --git a/tests/unit/nodes/test_structured_json_parse_node.cpp b/tests/unit/nodes/test_structured_json_parse_node.cpp index 1dd02a03..1b76d128 100644 --- a/tests/unit/nodes/test_structured_json_parse_node.cpp +++ b/tests/unit/nodes/test_structured_json_parse_node.cpp @@ -20,7 +20,6 @@ class StructuredJsonParseNodeTest : public ::testing::Test { std::unique_ptr session_ctx_; }; -// 1. Process Markdown JSON Block Extraction TEST_F(StructuredJsonParseNodeTest, ProcessMarkdownJsonBlockExtraction) { auto node = NodeRegistry::Instance().Create("StructuredJsonParseNode"); ASSERT_NE(node, nullptr); @@ -152,7 +151,6 @@ TEST_F(StructuredJsonParseNodeTest, ExtractionModeControlsSurroundingText) { } } -// 2. Required Fields and Field Types Validation TEST_F(StructuredJsonParseNodeTest, RequiredFieldsAndFieldTypesValidation) { auto node = NodeRegistry::Instance().Create("StructuredJsonParseNode"); ASSERT_NE(node, nullptr); @@ -163,7 +161,7 @@ TEST_F(StructuredJsonParseNodeTest, RequiredFieldsAndFieldTypesValidation) { {"failure_policy", "fail"}}; EXPECT_TRUE(InitNodeForTest(*node, cfg, session_ctx_.get())); - // Valid sample + // 合法样本 { AlgContext ctx; TextBatch inputs; @@ -172,7 +170,7 @@ TEST_F(StructuredJsonParseNodeTest, RequiredFieldsAndFieldTypesValidation) { EXPECT_EQ(node->Process(&ctx), 0); } - // Type mismatch: risk_score is string instead of number + // 类型不匹配:risk_score 是字符串而非数字 { AlgContext ctx; TextBatch inputs; @@ -182,7 +180,7 @@ TEST_F(StructuredJsonParseNodeTest, RequiredFieldsAndFieldTypesValidation) { EXPECT_EQ(node->Process(&ctx), -6102); } - // Missing required field + // 缺少必填字段 { AlgContext ctx; TextBatch inputs; @@ -202,7 +200,6 @@ TEST_F(StructuredJsonParseNodeTest, MissingInputFailsClosed) { EXPECT_EQ(node->Process(&empty_ctx), node_error::author_node::kMissingInput); } -// 3. Fallback Policy on Malformed Input TEST_F(StructuredJsonParseNodeTest, FallbackPolicyOnMalformedInput) { auto node = NodeRegistry::Instance().Create("StructuredJsonParseNode"); ASSERT_NE(node, nullptr); diff --git a/tests/unit/nodes/test_text_chunk_node.cpp b/tests/unit/nodes/test_text_chunk_node.cpp index b0bd023c..bdc3bb3b 100644 --- a/tests/unit/nodes/test_text_chunk_node.cpp +++ b/tests/unit/nodes/test_text_chunk_node.cpp @@ -20,16 +20,15 @@ class TextChunkNodeTest : public ::testing::Test { std::unique_ptr session_ctx_; }; -// 1. Init & Config Validation TEST_F(TextChunkNodeTest, InitAndConfigValidation) { auto node = NodeRegistry::Instance().Create("TextChunkNode"); ASSERT_NE(node, nullptr); - // Default config + // 默认配置 EXPECT_TRUE( InitNodeForTest(*node, nlohmann::json::object(), session_ctx_.get())); - // Custom valid config + // 自定义合法配置 nlohmann::json cfg = {{"chunk_size", 50}, {"overlap", 10}}; EXPECT_TRUE(InitNodeForTest(*node, cfg, session_ctx_.get())); @@ -55,7 +54,6 @@ TEST_F(TextChunkNodeTest, InitAndConfigValidation) { session_ctx_.get())); } -// 2. Process Single and Batch Chunks with ChunkCounts TEST_F(TextChunkNodeTest, ProcessBatchAndChunkCounts) { auto node = NodeRegistry::Instance().Create("TextChunkNode"); ASSERT_NE(node, nullptr); @@ -65,10 +63,10 @@ TEST_F(TextChunkNodeTest, ProcessBatchAndChunkCounts) { AlgContext ctx; TextBatch input_batch; - // Item 0: 50 chars -> 3 chunks (20, 20, 10) + // 条目 0:50 个字符 -> 3 个分块 (20, 20, 10) input_batch.emplace_back( 101, 0, "12345678901234567890123456789012345678901234567890"); - // Item 1: 10 chars -> 1 chunk + // 条目 1:10 个字符 -> 1 个分块 input_batch.emplace_back(102, 0, "1234567890"); ctx.Publish("text", input_batch); @@ -141,7 +139,6 @@ TEST_F(TextChunkNodeTest, OverlappingFinalPartialChunkIsEmittedOnce) { EXPECT_EQ(counts->at(0).data, 2); } -// 3. Process Empty Input Strings TEST_F(TextChunkNodeTest, ProcessEmptyStrings) { auto node = NodeRegistry::Instance().Create("TextChunkNode"); ASSERT_NE(node, nullptr); @@ -209,7 +206,6 @@ TEST_F(TextChunkNodeTest, InvalidUtf8FailsClosed) { EXPECT_EQ(ctx.Read("chunk_counts"), nullptr); } -// 4. Missing Input Fails Closed TEST_F(TextChunkNodeTest, MissingInputFailsClosed) { auto node = NodeRegistry::Instance().Create("TextChunkNode"); ASSERT_NE(node, nullptr); @@ -248,11 +244,11 @@ TEST_F(TextChunkNodeTest, AlgContext ctx; TextBatch input_batch; - // Request 10: item 0 has sub_id 5, produces 2 chunks (20 chars) + // 请求 10:条目 0 的 sub_id 为 5,产出 2 个分块 (20 个字符) input_batch.emplace_back(10, 5, "12345678901234567890"); - // Request 10: item 1 has sub_id 9, produces 1 chunk (10 chars) + // 请求 10:条目 1 的 sub_id 为 9,产出 1 个分块 (10 个字符) input_batch.emplace_back(10, 9, "abcdefghij"); - // Request 20: item 0 has sub_id 1, produces 1 chunk + // 请求 20:条目 0 的 sub_id 为 1,产出 1 个分块 input_batch.emplace_back(20, 1, "hello"); ctx.Publish("text", input_batch); @@ -261,18 +257,18 @@ TEST_F(TextChunkNodeTest, const auto* chunks = ctx.Read("chunks"); ASSERT_NE(chunks, nullptr); ASSERT_EQ(chunks->size(), 4u); - // Request 10 chunks must have continuous sub_ids: 0, 1, 2 + // 请求 10 的分块 sub_id 必须连续:0, 1, 2 EXPECT_EQ((*chunks)[0].req_id, 10u); EXPECT_EQ((*chunks)[0].sub_id, 0u); EXPECT_EQ((*chunks)[1].req_id, 10u); EXPECT_EQ((*chunks)[1].sub_id, 1u); EXPECT_EQ((*chunks)[2].req_id, 10u); EXPECT_EQ((*chunks)[2].sub_id, 2u); - // Request 20 chunks must have sub_id: 0 + // 请求 20 的分块 sub_id 必须为 0 EXPECT_EQ((*chunks)[3].req_id, 20u); EXPECT_EQ((*chunks)[3].sub_id, 0u); - // chunk_counts must preserve parent (req_id, sub_id) + // chunk_counts 必须保留父条目的 (req_id, sub_id) const auto* counts = ctx.Read("chunk_counts"); ASSERT_NE(counts, nullptr); ASSERT_EQ(counts->size(), 3u); @@ -297,11 +293,11 @@ TEST_F(TextChunkNodeTest, InterleavedRequestsContinuousSubIdAcrossParents) { AlgContext ctx; TextBatch input_batch; - // Req 10, sub 0: 20 chars -> 2 chunks + // 请求 10,sub 0:20 个字符 -> 2 个分块 input_batch.emplace_back(10, 0, "12345678901234567890"); - // Interleaved Req 20, sub 0: 10 chars -> 1 chunk + // 交错的请求 20,sub 0:10 个字符 -> 1 个分块 input_batch.emplace_back(20, 0, "abcdefghij"); - // Resumed Req 10, sub 1: 10 chars -> 1 chunk + // 继续请求 10,sub 1:10 个字符 -> 1 个分块 input_batch.emplace_back(10, 1, "klmnopqrst"); ctx.Publish("text", input_batch); @@ -311,7 +307,7 @@ TEST_F(TextChunkNodeTest, InterleavedRequestsContinuousSubIdAcrossParents) { ASSERT_NE(chunks, nullptr); ASSERT_EQ(chunks->size(), 4u); - // Req 10 first batch + // 请求 10 的第一批 EXPECT_EQ((*chunks)[0].req_id, 10u); EXPECT_EQ((*chunks)[0].sub_id, 0u); EXPECT_EQ((*chunks)[0].data, "1234567890"); @@ -320,12 +316,12 @@ TEST_F(TextChunkNodeTest, InterleavedRequestsContinuousSubIdAcrossParents) { EXPECT_EQ((*chunks)[1].sub_id, 1u); EXPECT_EQ((*chunks)[1].data, "1234567890"); - // Interleaved Req 20 starts at 0 + // 交错的请求 20 从 0 开始 EXPECT_EQ((*chunks)[2].req_id, 20u); EXPECT_EQ((*chunks)[2].sub_id, 0u); EXPECT_EQ((*chunks)[2].data, "abcdefghij"); - // Resumed Req 10 must continue at sub_id 2 (not reset!) + // 继续的请求 10 必须从 sub_id 2 接续 (不能重置!) EXPECT_EQ((*chunks)[3].req_id, 10u); EXPECT_EQ((*chunks)[3].sub_id, 2u); EXPECT_EQ((*chunks)[3].data, "klmnopqrst"); diff --git a/tests/unit/nodes/test_text_corpus_source_node.cpp b/tests/unit/nodes/test_text_corpus_source_node.cpp index 016175e5..2d9e8637 100644 --- a/tests/unit/nodes/test_text_corpus_source_node.cpp +++ b/tests/unit/nodes/test_text_corpus_source_node.cpp @@ -19,7 +19,6 @@ class TextCorpusSourceNodeTest : public ::testing::Test { std::unique_ptr session_ctx_; }; -// 1. Process Static Corpus Emission TEST_F(TextCorpusSourceNodeTest, ProcessStaticCorpusEmission) { auto node = NodeRegistry::Instance().Create("TextCorpusSourceNode"); ASSERT_NE(node, nullptr); @@ -37,7 +36,6 @@ TEST_F(TextCorpusSourceNodeTest, ProcessStaticCorpusEmission) { EXPECT_EQ((*out)[1].data, "Clause 2: Security"); } -// 2. Empty Corpus Config TEST_F(TextCorpusSourceNodeTest, EmptyCorpusConfig) { auto node = NodeRegistry::Instance().Create("TextCorpusSourceNode"); ASSERT_NE(node, nullptr); diff --git a/tests/unit/nodes/test_text_embedding_node.cpp b/tests/unit/nodes/test_text_embedding_node.cpp index 7ec75b00..64f9e7b9 100644 --- a/tests/unit/nodes/test_text_embedding_node.cpp +++ b/tests/unit/nodes/test_text_embedding_node.cpp @@ -79,7 +79,6 @@ class TextEmbeddingNodeTest : public ::testing::Test { std::shared_ptr counting_model_; }; -// 1. Init & Process Request Lifetime TEST_F(TextEmbeddingNodeTest, ProcessRequestLifetime) { auto node = NodeRegistry::Instance().Create("TextEmbeddingNode"); ASSERT_NE(node, nullptr); @@ -100,7 +99,6 @@ TEST_F(TextEmbeddingNodeTest, ProcessRequestLifetime) { EXPECT_EQ(counting_model_->infer_calls.load(), 1); } -// 2. Session Caching Single-Flight & Invalidation TEST_F(TextEmbeddingNodeTest, SessionCachingSingleFlightAndInvalidation) { auto node = NodeRegistry::Instance().Create("TextEmbeddingNode"); ASSERT_NE(node, nullptr); @@ -139,7 +137,7 @@ TEST_F(TextEmbeddingNodeTest, SessionCachingSingleFlightAndInvalidation) { EXPECT_EQ(success_count.load(), kNumThreads); EXPECT_EQ(counting_model_->infer_calls.load(), 1); - // Invalidation test: changing corpus triggers recomputation + // 失效测试:更换语料会触发重新计算 { AlgContext ctx; TextBatch updated_corpus; @@ -151,8 +149,7 @@ TEST_F(TextEmbeddingNodeTest, SessionCachingSingleFlightAndInvalidation) { EXPECT_EQ(counting_model_->infer_calls.load(), 2); } - // A model hot update changes the revision and invalidates otherwise - // identical session cache entries. + // 模型热更新会改变 revision,使其余完全相同的会话缓存条目失效。 ASSERT_TRUE(session_ctx_->GetModelManager().UpdateModelRevision( "embed_model_v1", "revision-2")); { @@ -166,7 +163,6 @@ TEST_F(TextEmbeddingNodeTest, SessionCachingSingleFlightAndInvalidation) { } } -// 3. Missing Input Fails Closed TEST_F(TextEmbeddingNodeTest, MissingInputFailsClosed) { auto node = NodeRegistry::Instance().Create("TextEmbeddingNode"); ASSERT_NE(node, nullptr); @@ -286,7 +282,7 @@ TEST_F(TextEmbeddingNodeTest, if (std::string(lifetime) == "session") { EXPECT_LT(counting_model_->infer_calls.load() - calls_before, 8); } - // All workers have joined before changing model behavior. + // 修改模型行为前,所有 worker 都已 join。 counting_model_->fail_inference = false; const int calls_before_retry = counting_model_->infer_calls.load(); AlgContext retry; @@ -304,7 +300,6 @@ TEST_F(TextEmbeddingNodeTest, } } -// 4. Session Cache Collision Reproduction Defeated (C01) TEST_F(TextEmbeddingNodeTest, SessionCacheCollisionReproductionDefeated) { auto node_a = NodeRegistry::Instance().Create("TextEmbeddingNode"); auto node_b = NodeRegistry::Instance().Create("TextEmbeddingNode"); @@ -317,8 +312,8 @@ TEST_F(TextEmbeddingNodeTest, SessionCacheCollisionReproductionDefeated) { *node_b, {{"bind_model", "embed_model_v1"}, {"lifetime", "session"}}, session_ctx_.get())); - // Corpus A: ["a", "b\0\1c"] - // Corpus B: ["a\0\1b", "c"] + // 语料 A:["a", "b\0\1c"] + // 语料 B:["a\0\1b", "c"] std::string s_b_nul_c = std::string("b") + '\0' + '\1' + "c"; std::string s_a_nul_b = std::string("a") + '\0' + '\1' + "b"; ASSERT_EQ(s_b_nul_c.size(), 4u); @@ -345,9 +340,8 @@ TEST_F(TextEmbeddingNodeTest, SessionCacheCollisionReproductionDefeated) { AlgContext ctx_b; ctx_b.Publish("text", corpus_b); EXPECT_EQ(node_b->Process(&ctx_b), 0); - // In the old implementation, corpus_b collided with corpus_a, returning - // cached [1.0f, 4.0f] with infer_calls staying at 1. With the fix, - // infer_calls must be 2 and out_b has [4.0f, 1.0f]! + // 旧实现中 corpus_b 会与 corpus_a 冲突,返回缓存的 [1.0f, 4.0f], + // infer_calls 仍为 1。修复后 infer_calls 必须为 2,out_b 为 [4.0f, 1.0f]! EXPECT_EQ(counting_model_->infer_calls.load(), 2); const auto* out_b = ctx_b.Read("embedding"); ASSERT_NE(out_b, nullptr); @@ -355,7 +349,7 @@ TEST_F(TextEmbeddingNodeTest, SessionCacheCollisionReproductionDefeated) { EXPECT_FLOAT_EQ((*out_b)[0].data[0], 4.0f); EXPECT_FLOAT_EQ((*out_b)[1].data[0], 1.0f); - // Subsequent call with corpus_a should hit cache (infer_calls remains 2) + // 随后用 corpus_a 调用应命中缓存 (infer_calls 仍为 2) AlgContext ctx_a2; ctx_a2.Publish("text", corpus_a); EXPECT_EQ(node_a->Process(&ctx_a2), 0); @@ -365,8 +359,7 @@ TEST_F(TextEmbeddingNodeTest, SessionCacheCollisionReproductionDefeated) { EXPECT_FLOAT_EQ((*out_a2)[0].data[0], 1.0f); EXPECT_FLOAT_EQ((*out_a2)[1].data[0], 4.0f); - // Changing order, sub_id, req_id, or normalize option creates distinct - // entries + // 改变顺序、sub_id、req_id 或 normalize 选项都会产生不同的条目 TextBatch corpus_a_reordered; corpus_a_reordered.emplace_back(0, 0, s_b_nul_c); corpus_a_reordered.emplace_back(0, 1, "a"); @@ -383,7 +376,7 @@ TEST_F(TextEmbeddingNodeTest, SessionCacheCollisionReproductionDefeated) { EXPECT_EQ(node_a->Process(&ctx_sub_id), 0); EXPECT_EQ(counting_model_->infer_calls.load(), 4); - // Different normalize option creates distinct cache entry + // 不同的 normalize 选项产生不同的缓存条目 auto node_no_norm = NodeRegistry::Instance().Create("TextEmbeddingNode"); ASSERT_TRUE(InitNodeForTest(*node_no_norm, {{"bind_model", "embed_model_v1"}, diff --git a/tests/unit/nodes/test_text_rerank_node.cpp b/tests/unit/nodes/test_text_rerank_node.cpp index 51536dd6..5c25cfea 100644 --- a/tests/unit/nodes/test_text_rerank_node.cpp +++ b/tests/unit/nodes/test_text_rerank_node.cpp @@ -93,7 +93,7 @@ class TextRerankNodeTest : public ::testing::Test { std::unique_ptr session_ctx_; }; -// 1. Process Queries + Candidates (Group 2) +// 组合 2:queries + candidates TEST_F(TextRerankNodeTest, ProcessQueriesAndCandidates) { auto node = NodeRegistry::Instance().Create("TextRerankNode"); ASSERT_NE(node, nullptr); @@ -199,7 +199,7 @@ TEST_F(TextRerankNodeTest, RejectsCandidatesWithConnectedEmptyQueries) { } } -// 2. Process Pairs (Group 1) +// 组合 1:pairs TEST_F(TextRerankNodeTest, ProcessPairsInput) { auto node = NodeRegistry::Instance().Create("TextRerankNode"); ASSERT_NE(node, nullptr); @@ -222,7 +222,7 @@ TEST_F(TextRerankNodeTest, ProcessPairsInput) { EXPECT_EQ((*ranked)[0].data.original_sub_id, 20u); } -// 3. Process Queries + CandidateTexts (Group 3) +// 组合 3:queries + candidate_texts TEST_F(TextRerankNodeTest, ProcessQueriesAndCandidateTexts) { auto node = NodeRegistry::Instance().Create("TextRerankNode"); ASSERT_NE(node, nullptr); @@ -252,7 +252,6 @@ TEST_F(TextRerankNodeTest, ProcessQueriesAndCandidateTexts) { EXPECT_EQ((*ranked)[1].data.original_sub_id, 6u); } -// 4. Multi Request Grouping TEST_F(TextRerankNodeTest, MultiRequestGrouping) { auto node = NodeRegistry::Instance().Create("TextRerankNode"); ASSERT_NE(node, nullptr); @@ -282,7 +281,6 @@ TEST_F(TextRerankNodeTest, MultiRequestGrouping) { EXPECT_EQ((*ranked)[1].data.text, "Q2 HIGH"); } -// 5. Typed Model pair-input path TEST_F(TextRerankNodeTest, TypedModelPairInputPath) { auto node = NodeRegistry::Instance().Create("TextRerankNode"); ASSERT_NE(node, nullptr); @@ -302,7 +300,6 @@ TEST_F(TextRerankNodeTest, TypedModelPairInputPath) { ASSERT_EQ(ranked->size(), 1u); } -// 6. Failures and Error Handling TEST_F(TextRerankNodeTest, FailuresAndProvenanceMismatch) { auto node = NodeRegistry::Instance().Create("TextRerankNode"); ASSERT_NE(node, nullptr); @@ -317,23 +314,22 @@ TEST_F(TextRerankNodeTest, FailuresAndProvenanceMismatch) { }; ctx.Publish("pairs", pairs); - // Score error + // 打分出错 fake_model_->fail_score_ = true; EXPECT_EQ(node->Process(&ctx), -1); - // Score count mismatch + // 打分数量不一致 fake_model_->fail_score_ = false; fake_model_->return_wrong_count_ = true; EXPECT_EQ(node->Process(&ctx), node_error::author_node::kOutputCountMismatch); - // Score provenance mismatch + // 打分来源不一致 fake_model_->return_wrong_count_ = false; fake_model_->corrupt_provenance_ = true; EXPECT_EQ(node->Process(&ctx), node_error::author_node::kOutputProvenanceMismatch); } -// 7. Port Constraints Validation Check TEST_F(TextRerankNodeTest, PortConstraintsValidation) { RegisterTestBizs( {"rerank_port_constraint_fixture"}, @@ -346,7 +342,7 @@ TEST_F(TextRerankNodeTest, PortConstraintsValidation) { }); }; - // Missing query when candidates is bound -> Fail + // 绑定了 candidates 却缺少 query -> 失败 nlohmann::json bad_pipeline = { {"biz_name", "rerank_port_constraint_fixture"}, {"models", diff --git a/tests/unit/nodes/test_text_rule_match_node.cpp b/tests/unit/nodes/test_text_rule_match_node.cpp index da30fccb..3e651919 100644 --- a/tests/unit/nodes/test_text_rule_match_node.cpp +++ b/tests/unit/nodes/test_text_rule_match_node.cpp @@ -28,7 +28,6 @@ class TextRuleMatchNodeTest : public ::testing::Test { std::unique_ptr session_ctx_; }; -// 1. Process Keyword and Category Matching TEST_F(TextRuleMatchNodeTest, ProcessKeywordAndCategoryMatching) { auto node = NodeRegistry::Instance().Create("TextRuleMatchNode"); ASSERT_NE(node, nullptr); @@ -134,7 +133,7 @@ TEST_F(TextRuleMatchNodeTest, NestedDiagnosticsAgreeAcrossAuthoringAndControl) { })) << report.ToJson().dump(2); - // Direct authoring initialization reports the same offending nested value. + // 直接通过编写接口初始化时,报告同一个出错的嵌套值。 auto fresh = NodeRegistry::Instance().Create("TextRuleMatchNode"); std::string diagnostic; EXPECT_FALSE(InitNodeForTest(*fresh, invalid.config, session_ctx_.get(), @@ -230,21 +229,21 @@ TEST_F(TextRuleMatchNodeTest, InvalidScoreControlPreservesCategoriesAndRules) { EXPECT_EQ(matches->at(2).data.is_hit, 0); } -// 2. Control Command Dynamic Rule Hot-Swap & Bogus Rejection +// Control 命令动态热替换规则,并拒绝无效更新 TEST_F(TextRuleMatchNodeTest, ControlCommandDynamicRules) { auto node = NodeRegistry::Instance().Create("TextRuleMatchNode"); ASSERT_NE(node, nullptr); ASSERT_TRUE( InitNodeForTest(*node, nlohmann::json::object(), session_ctx_.get())); - // Valid update + // 合法更新 nlohmann::json update_payload = { {"categories", {{"SECURITY", {"密码", "漏洞", "盗号"}}}}}; NodeControlResult res = node->Control(kControlCmdUpdateRules, update_payload.dump()); EXPECT_EQ(res.status, NodeControlStatus::kHandled); - // Bogus update -> Rejected + // 无效更新 -> 拒绝 nlohmann::json bogus_payload = {{"bogus_field", 123}}; NodeControlResult bogus_res = node->Control(kControlCmdUpdateRules, bogus_payload.dump()); @@ -424,7 +423,6 @@ TEST_F(TextRuleMatchNodeTest, RegexErrorsFailClosed) { EXPECT_EQ(ctx.Read("matches"), nullptr); } -// 3. Missing Input Fails Closed TEST_F(TextRuleMatchNodeTest, MissingInputFailsClosed) { auto node = NodeRegistry::Instance().Create("TextRuleMatchNode"); ASSERT_NE(node, nullptr); @@ -448,7 +446,7 @@ TEST_F(TextRuleMatchNodeTest, DirectConcurrentProcessAndControl) { }; ASSERT_TRUE(InitNodeForTest(*node, configuration("OLD"), session_ctx_.get())); const auto update = configuration("NEW"); - // Both category keywords and compiled regex captures distinguish versions. + // 类别关键词和已编译的正则捕获都能区分版本。 const TextBatch inputs{{101, 2, "hello"}, {101, 7, "world"}, {202, 3, "hello"}, @@ -458,9 +456,8 @@ TEST_F(TextRuleMatchNodeTest, DirectConcurrentProcessAndControl) { test_support::NodeProcessPause pause; int process_result = -1; std::exception_ptr reader_error; - // On this valid Process path, the first heap allocation follows - // snapshot.Read: the output batch reserve. Pause with that old snapshot - // retained. + // 在这条合法的 Process 路径上,snapshot.Read 之后的第一次堆分配是 + // 输出批次的 reserve。在保留旧快照的状态下暂停。 std::thread reader([&] { try { test_support::ScopedNextAllocationCallback callback( diff --git a/tests/unit/nodes/test_text_template_node.cpp b/tests/unit/nodes/test_text_template_node.cpp index babaf56d..11ad71af 100644 --- a/tests/unit/nodes/test_text_template_node.cpp +++ b/tests/unit/nodes/test_text_template_node.cpp @@ -38,7 +38,6 @@ std::string ResolveConfigPath(const std::string& relative) { return "../" + relative; } -// 1. Process Multi-Input Aggregation TEST_F(TextTemplateNodeTest, ProcessMultiInputAggregation) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); @@ -73,7 +72,6 @@ TEST_F(TextTemplateNodeTest, ProcessMultiInputAggregation) { EXPECT_NE((*out)[0].data.find("ACCOUNT_UPGRADE"), std::string::npos); } -// 2. Missing Required Variable Fails Closed TEST_F(TextTemplateNodeTest, MissingRequiredVariableFailsClosed) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); @@ -83,7 +81,7 @@ TEST_F(TextTemplateNodeTest, MissingRequiredVariableFailsClosed) { {"missing_variable_policy", "fail"}}; EXPECT_TRUE(InitNodeForTest(*node, cfg, session_ctx_.get())); - // Attributes missing required variable 'user_name' + // 属性中缺少必填变量 'user_name' AlgContext ctx; TextAttributesBatch attrs; attrs.emplace_back( @@ -94,7 +92,6 @@ TEST_F(TextTemplateNodeTest, MissingRequiredVariableFailsClosed) { EXPECT_EQ(node->Process(&ctx), -6202); } -// 3. Dynamic Attribute Successfully Rendered TEST_F(TextTemplateNodeTest, DynamicAttributeRendered) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); @@ -116,13 +113,12 @@ TEST_F(TextTemplateNodeTest, DynamicAttributeRendered) { EXPECT_EQ((*out)[0].data, "Hello Alice, welcome!"); } -// 4. Single Braces Treated As Literal Text TEST_F(TextTemplateNodeTest, SingleBraceTreatedAsLiteral) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); - // Single braces like {user_name} and JSON objects {"k": 1} must remain - // literal text. Only double braces {{var}} are treated as template variables. + // {user_name} 这样的单花括号和 JSON 对象 {"k": 1} 必须保持为字面文本。 + // 只有双花括号 {{var}} 才是模板变量。 nlohmann::json cfg = { {"template", "Literal: {user_name}, JSON: {\"key\": 1}, Var: {{user_name}}"}, @@ -143,7 +139,6 @@ TEST_F(TextTemplateNodeTest, SingleBraceTreatedAsLiteral) { "Literal: {user_name}, JSON: {\"key\": 1}, Var: Alice"); } -// 5. Malformed Placeholders Rejected At Initialization TEST_F(TextTemplateNodeTest, MalformedPlaceholderFailsInit) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); @@ -197,21 +192,20 @@ TEST_F(TextTemplateNodeTest, TruncateRejectsInvalidUtf8) { EXPECT_EQ(ctx.Read("text"), nullptr); } -// 4. Control Command Hot-Swap & Bogus Rejection TEST_F(TextTemplateNodeTest, ControlCommandHotSwapAndBogusRejection) { auto node = NodeRegistry::Instance().Create("TextTemplateNode"); ASSERT_NE(node, nullptr); ASSERT_TRUE(InitNodeForTest(*node, {{"template", "{{primary}}"}}, session_ctx_.get())); - // Valid update + // 合法更新 nlohmann::json valid_update = { {"template", "Updated: {{primary}} [{{context}}]"}}; NodeControlResult res = node->Control(kControlCmdUpdatePrompt, valid_update.dump()); EXPECT_EQ(res.status, NodeControlStatus::kHandled); - // Operator metadata is accepted independently of a template change. + // Operator 元数据与模板变更相互独立,可单独接受。 NodeControlResult prompt_id_res = node->Control( kControlCmdUpdatePrompt, nlohmann::json{{"prompt_id", "qa-v2"}}.dump()); EXPECT_EQ(prompt_id_res.status, NodeControlStatus::kHandled); @@ -226,7 +220,7 @@ TEST_F(TextTemplateNodeTest, ControlCommandHotSwapAndBogusRejection) { nlohmann::json{{"missing_variable_policy", "invent"}}.dump()); EXPECT_EQ(invalid_policy_res.status, NodeControlStatus::kFailed); - // Bogus update with no valid fields -> Rejected + // 没有任何合法字段的无效更新 -> 拒绝 nlohmann::json bogus_update = {{"bogus_field", 123}}; NodeControlResult bogus_res = node->Control(kControlCmdUpdatePrompt, bogus_update.dump()); @@ -442,9 +436,8 @@ TEST_F(TextTemplateNodeTest, DirectConcurrentProcessAndControl) { test_support::NodeProcessPause pause; int process_result = -1; std::exception_ptr reader_error; - // On this valid Process path, the first heap allocation follows - // snapshot.Read: the template grouping container. Pause with that old - // snapshot retained. + // 在这条合法的 Process 路径上,snapshot.Read 之后的第一次堆分配是 + // 模板分组容器。在保留旧快照的状态下暂停。 std::thread reader([&] { try { test_support::ScopedNextAllocationCallback callback( diff --git a/tests/unit/nodes/test_traceable_batch_operations.cpp b/tests/unit/nodes/test_traceable_batch_operations.cpp index 2f836223..66dc3815 100644 --- a/tests/unit/nodes/test_traceable_batch_operations.cpp +++ b/tests/unit/nodes/test_traceable_batch_operations.cpp @@ -74,7 +74,7 @@ class TraceableBatchOperationsTest : public ::testing::Test { }; // ============================================================================ -// 1. JoinByItem Tests +// 1. JoinByItem 测试 // ============================================================================ TEST_F(TraceableBatchOperationsTest, JoinByItemExactMatchingOrderPreserved) { @@ -90,7 +90,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemExactMatchingOrderPreserved) { ASSERT_EQ(view.size(), 3u); EXPECT_FALSE(view.empty()); - // Strict left order + // 严格保持 left 顺序 EXPECT_EQ(view[0].req_id(), 1u); EXPECT_EQ(view[0].sub_id(), 0u); EXPECT_EQ(view[0].left_payload(), "q1"); @@ -154,7 +154,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemRightExtraKeyFailsBothModes) { std::vector> right = {{1, 0, "a1"}, {99, 0, "extra"}}; - // Exact mode fails + // Exact 模式失败 auto exact_res = JoinByItem(left, right, JoinMode::kExact); ASSERT_FALSE(exact_res.ok()); ASSERT_TRUE(exact_res.failure().batch_detail.has_value()); @@ -163,7 +163,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemRightExtraKeyFailsBothModes) { ASSERT_TRUE(exact_res.failure().batch_detail->key.has_value()); EXPECT_EQ(exact_res.failure().batch_detail->key->req_id, 99u); - // Left mode also fails on unknown right key + // Left 模式遇到 right 中的未知键同样失败 auto left_res = JoinByItem(left, right, JoinMode::kLeft); ASSERT_FALSE(left_res.ok()); ASSERT_TRUE(left_res.failure().batch_detail.has_value()); @@ -174,7 +174,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemRightExtraKeyFailsBothModes) { } TEST_F(TraceableBatchOperationsTest, JoinByItemDuplicateKeysFail) { - // Duplicate in left + // left 中有重复 std::vector> left_dup = {{1, 0, "q1"}, {1, 0, "q1_dup"}}; std::vector> right = {{1, 0, "a1"}}; @@ -186,7 +186,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemDuplicateKeysFail) { BatchFailureReason::kDuplicate); EXPECT_EQ(res1.failure().batch_detail->key->req_id, 1u); - // Duplicate in right + // right 中有重复 std::vector> left = {{1, 0, "q1"}}; std::vector> right_dup = {{1, 0, "a1"}, {1, 0, "a1_dup"}}; @@ -202,7 +202,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemEmptyCombinations) { std::vector> empty; std::vector> non_empty = {{1, 0, "q1"}}; - // Both empty: succeeds in both modes + // 两侧都为空:两种模式均成功 auto res_both_empty_exact = JoinByItem(empty, empty, JoinMode::kExact); EXPECT_TRUE(res_both_empty_exact.ok()); EXPECT_TRUE(res_both_empty_exact.value().empty()); @@ -211,7 +211,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemEmptyCombinations) { EXPECT_TRUE(res_both_empty_left.ok()); EXPECT_TRUE(res_both_empty_left.value().empty()); - // Left empty, right non-empty: fails in both modes + // left 为空、right 非空:两种模式均失败 auto res_left_empty_exact = JoinByItem(empty, non_empty, JoinMode::kExact); EXPECT_FALSE(res_left_empty_exact.ok()); EXPECT_EQ(res_left_empty_exact.failure().batch_detail->reason, @@ -222,7 +222,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemEmptyCombinations) { EXPECT_EQ(res_left_empty_left.failure().batch_detail->reason, BatchFailureReason::kUnknown); - // Left non-empty, right empty: exact fails, left succeeds + // left 非空、right 为空:exact 失败,left 成功 auto res_right_empty_exact = JoinByItem(non_empty, empty, JoinMode::kExact); EXPECT_FALSE(res_right_empty_exact.ok()); EXPECT_EQ(res_right_empty_exact.failure().batch_detail->reason, @@ -235,7 +235,7 @@ TEST_F(TraceableBatchOperationsTest, JoinByItemEmptyCombinations) { } // ============================================================================ -// 2. GroupByRequest Tests +// 2. GroupByRequest 测试 // ============================================================================ TEST_F(TraceableBatchOperationsTest, GroupByRequestAnchorFirstAppearanceOrder) { @@ -251,16 +251,16 @@ TEST_F(TraceableBatchOperationsTest, GroupByRequestAnchorFirstAppearanceOrder) { const auto& view = res.value(); ASSERT_EQ(view.size(), 2u); - // First appearance in anchor: req 10, then req 20 + // 在锚点中首次出现的顺序:先 req 10,后 req 20 EXPECT_EQ(view[0].req_id(), 10u); EXPECT_EQ(view[1].req_id(), 20u); - // Group 10 anchors + // 分组 10 的锚点 ASSERT_EQ(view[0].anchor_count(), 2u); EXPECT_EQ(view[0].anchors()[0].get().data, "A0"); EXPECT_EQ(view[0].anchors()[1].get().data, "A1"); - // Group 20 anchors + // 分组 20 的锚点 ASSERT_EQ(view[1].anchor_count(), 1u); EXPECT_EQ(view[1].anchors()[0].get().data, "B0"); } @@ -268,7 +268,7 @@ TEST_F(TraceableBatchOperationsTest, GroupByRequestAnchorFirstAppearanceOrder) { TEST_F(TraceableBatchOperationsTest, GroupByRequestMembersRelativeOrderPreserved) { std::vector> anchor = {{10, 0, "A0"}}; - // Members have sub_id 5 then 2 (unsorted!) + // 成员的 sub_id 依次为 5 和 2 (未排序!) std::vector> members = {{10, 5, "m5"}, {10, 2, "m2"}}; @@ -315,7 +315,7 @@ TEST_F(TraceableBatchOperationsTest, GroupByRequestUnknownMemberReqIdFails) { } TEST_F(TraceableBatchOperationsTest, GroupByRequestDuplicatesFail) { - // Duplicate in anchor + // 锚点中有重复 std::vector> anchor_dup = {{10, 0, "A0"}, {10, 0, "A0_dup"}}; std::vector> members = {{10, 0, "m0"}}; @@ -324,7 +324,7 @@ TEST_F(TraceableBatchOperationsTest, GroupByRequestDuplicatesFail) { EXPECT_EQ(res1.failure().batch_detail->reason, BatchFailureReason::kDuplicate); - // Duplicate in members + // 成员中有重复 std::vector> anchor = {{10, 0, "A0"}}; std::vector> members_dup = {{10, 0, "m0"}, {10, 0, "m0_dup"}}; @@ -338,12 +338,12 @@ TEST_F(TraceableBatchOperationsTest, GroupByRequestEmptyCombinations) { std::vector> empty; std::vector> members = {{10, 0, "m"}}; - // Empty anchor + empty members: success + // 锚点和成员都为空:成功 auto res_empty = GroupByRequest(empty, empty); EXPECT_TRUE(res_empty.ok()); EXPECT_TRUE(res_empty.value().empty()); - // Empty anchor + non-empty members: failure + // 锚点为空、成员非空:失败 auto res_fail = GroupByRequest(empty, members); EXPECT_FALSE(res_fail.ok()); EXPECT_EQ(res_fail.failure().batch_detail->reason, @@ -352,7 +352,7 @@ TEST_F(TraceableBatchOperationsTest, GroupByRequestEmptyCombinations) { TEST_F(TraceableBatchOperationsTest, GroupByRequestPreservesOriginalAnchorOrderFor1to1) { - // Anchor is A0, B0, A1 + // 锚点为 A0, B0, A1 std::vector> anchor = { {10, 0, "A0"}, {20, 0, "B0"}, {10, 1, "A1"}}; std::vector> members = { @@ -363,7 +363,7 @@ TEST_F(TraceableBatchOperationsTest, const auto& view = res.value(); - // Generate 1:1 output using GroupByAnchorIndex + // 用 GroupByAnchorIndex 生成 1:1 输出 std::vector> output; output.reserve(anchor.size()); for (size_t i = 0; i < anchor.size(); ++i) { @@ -377,7 +377,7 @@ TEST_F(TraceableBatchOperationsTest, output.emplace_back(item.req_id, item.sub_id, std::move(combined)); } - // Verify output strictly matches anchor order: A0, B0, A1 + // 输出必须严格按锚点顺序:A0, B0, A1 ASSERT_EQ(output.size(), 3u); EXPECT_EQ(output[0].req_id, 10u); EXPECT_EQ(output[0].sub_id, 0u); @@ -393,7 +393,7 @@ TEST_F(TraceableBatchOperationsTest, } // ============================================================================ -// 3. SelectBatch and ScatterReplace Tests +// 3. SelectBatch 与 ScatterReplace 测试 // ============================================================================ TEST_F(TraceableBatchOperationsTest, SelectAndScatterAllSelected) { @@ -447,14 +447,14 @@ TEST_F(TraceableBatchOperationsTest, std::vector> anchor = { {1, 0, "apple"}, {2, 0, "banana"}, {3, 0, "cherry"}}; - // Select items with length > 5: "banana" (index 1) and "cherry" (index 2) + // 选出长度 > 5 的条目:"banana" (索引 1) 和 "cherry" (索引 2) auto sel_res = SelectBatch( anchor, [](const std::string& text) { return text.size() > 5; }); ASSERT_TRUE(sel_res.ok()); const auto& selection = sel_res.value(); ASSERT_EQ(selection.size(), 2u); - // Provide replacements in REVERSE order + // 以逆序提供替换项 std::vector> replacements = {{3, 0, "CHERRY"}, {2, 0, "BANANA"}}; @@ -463,15 +463,15 @@ TEST_F(TraceableBatchOperationsTest, const auto& full = scatter_res.value(); ASSERT_EQ(full.size(), 3u); - // Unselected item 0 remains "apple" + // 未选中的条目 0 仍为 "apple" EXPECT_EQ(full[0].req_id, 1u); EXPECT_EQ(full[0].data, "apple"); - // Selected item 1 updated to "BANANA" + // 选中的条目 1 更新为 "BANANA" EXPECT_EQ(full[1].req_id, 2u); EXPECT_EQ(full[1].data, "BANANA"); - // Selected item 2 updated to "CHERRY" + // 选中的条目 2 更新为 "CHERRY" EXPECT_EQ(full[2].req_id, 3u); EXPECT_EQ(full[2].data, "CHERRY"); } @@ -480,7 +480,7 @@ TEST_F(TraceableBatchOperationsTest, SelectBatchPredicateFailures) { std::vector> anchor = { {1, 0, "ok"}, {2, 0, "fail"}, {3, 0, "ok"}}; - // Predicate returning NodeResult failure + // 谓词返回失败的 NodeResult auto res = SelectBatch(anchor, [](const std::string& text) -> NodeResult { if (text == "fail") { @@ -497,7 +497,7 @@ TEST_F(TraceableBatchOperationsTest, SelectBatchPredicateFailures) { ASSERT_TRUE(res.failure().batch_detail->key.has_value()); EXPECT_EQ(res.failure().batch_detail->key->req_id, 2u); - // Predicate throwing exception + // 谓词抛异常 auto throw_res = SelectBatch(anchor, [](const std::string& text) -> bool { if (text == "fail") throw std::runtime_error("Unexpected error"); return true; @@ -517,7 +517,7 @@ TEST_F(TraceableBatchOperationsTest, ScatterReplaceErrorValidations) { ASSERT_TRUE(sel_res.ok()); const auto& selection = sel_res.value(); - // 1. Duplicate in replacements + // 1. 替换项中有重复 std::vector> dup_repl = {{2, 0, "b1"}, {2, 0, "b2"}}; auto res_dup = ScatterReplace(selection, dup_repl); @@ -525,8 +525,7 @@ TEST_F(TraceableBatchOperationsTest, ScatterReplaceErrorValidations) { EXPECT_EQ(res_dup.failure().batch_detail->reason, BatchFailureReason::kDuplicate); - // 2. Replacement has unselected key (e.g. 1:0 which is in anchor but not - // selected) + // 2. 替换项含未选中的键 (如 1:0 在锚点中但未被选中) std::vector> unselected_repl = { {1, 0, "new_apple"}}; auto res_unsel = ScatterReplace(selection, unselected_repl); @@ -534,14 +533,14 @@ TEST_F(TraceableBatchOperationsTest, ScatterReplaceErrorValidations) { EXPECT_EQ(res_unsel.failure().batch_detail->reason, BatchFailureReason::kUnknown); - // 3. Replacement has unknown key (not in anchor) + // 3. 替换项含未知键 (不在锚点中) std::vector> unknown_repl = {{99, 0, "ghost"}}; auto res_unk = ScatterReplace(selection, unknown_repl); ASSERT_FALSE(res_unk.ok()); EXPECT_EQ(res_unk.failure().batch_detail->reason, BatchFailureReason::kUnknown); - // 4. Replacement missing selected key + // 4. 替换项缺少选中的键 std::vector> empty_repl; auto res_miss = ScatterReplace(selection, empty_repl); ASSERT_FALSE(res_miss.ok()); @@ -550,7 +549,7 @@ TEST_F(TraceableBatchOperationsTest, ScatterReplaceErrorValidations) { } // ============================================================================ -// 4. SplitPayloads Tests +// 4. SplitPayloads 测试 // ============================================================================ TEST_F(TraceableBatchOperationsTest, @@ -579,9 +578,9 @@ TEST_F(TraceableBatchOperationsTest, ASSERT_TRUE(res.ok()) << res.failure().message; const auto& result = res.value(); - // Children check + // 检查子条目 ASSERT_EQ(result.children.size(), 6u); - // req 1 item 0: 2 words -> sub_id 0, 1 + // req 1 条目 0:2 个词 -> sub_id 0, 1 EXPECT_EQ(result.children[0].req_id, 1u); EXPECT_EQ(result.children[0].sub_id, 0u); EXPECT_EQ(result.children[0].data, "hello"); @@ -590,12 +589,12 @@ TEST_F(TraceableBatchOperationsTest, EXPECT_EQ(result.children[1].sub_id, 1u); EXPECT_EQ(result.children[1].data, "world"); - // req 1 item 1: 1 word -> sub_id 2 (continuous for req 1!) + // req 1 条目 1:1 个词 -> sub_id 2 (req 1 内连续!) EXPECT_EQ(result.children[2].req_id, 1u); EXPECT_EQ(result.children[2].sub_id, 2u); EXPECT_EQ(result.children[2].data, "single"); - // req 2 item 0: 3 words -> sub_id starts at 0 for req 2! + // req 2 条目 0:3 个词 -> req 2 的 sub_id 从 0 开始! EXPECT_EQ(result.children[3].req_id, 2u); EXPECT_EQ(result.children[3].sub_id, 0u); EXPECT_EQ(result.children[3].data, "foo"); @@ -608,7 +607,7 @@ TEST_F(TraceableBatchOperationsTest, EXPECT_EQ(result.children[5].sub_id, 2u); EXPECT_EQ(result.children[5].data, "baz"); - // Counts check + // 检查计数 ASSERT_EQ(result.counts.size(), 3u); EXPECT_EQ(result.counts[0].req_id, 1u); EXPECT_EQ(result.counts[0].sub_id, 5u); @@ -651,7 +650,7 @@ TEST_F(TraceableBatchOperationsTest, EXPECT_EQ(children[2].req_id, 2u); EXPECT_EQ(children[2].sub_id, 0u); - // req 1 resumed: sub_id must be 2, 3! + // req 1 继续:sub_id 必须为 2, 3! EXPECT_EQ(children[3].req_id, 1u); EXPECT_EQ(children[3].sub_id, 2u); @@ -660,7 +659,7 @@ TEST_F(TraceableBatchOperationsTest, } TEST_F(TraceableBatchOperationsTest, SplitPayloadsZeroChildrenAndEmptyInput) { - // Zero children is legal + // 零个子条目是合法的 std::vector> input = {{1, 0, "empty"}}; auto res = SplitPayloads( input, [](const std::string&) { return std::vector{}; }); @@ -669,7 +668,7 @@ TEST_F(TraceableBatchOperationsTest, SplitPayloadsZeroChildrenAndEmptyInput) { ASSERT_EQ(res.value().counts.size(), 1u); EXPECT_EQ(res.value().counts[0].data, 0); - // Empty input + // 空输入 std::vector> empty_input; auto empty_res = SplitPayloads(empty_input, [](const std::string&) { return std::vector{"never"}; @@ -708,14 +707,14 @@ TEST_F(TraceableBatchOperationsTest, TEST_F(TraceableBatchOperationsTest, SplitPayloadsSubIdOverflowSeam) { std::vector> input = {{1, 0, "split"}}; - // Set near-boundary sub_id using internal test seam + // 通过内部测试接缝设置接近边界的 sub_id std::unordered_map initial = { {1, static_cast(std::numeric_limits::max())}}; auto res = detail::SplitPayloadsInternal( input, [](const std::string&) { - // Generates 2 children: first fits at UINT32_MAX, second overflows! + // 生成 2 个子条目:第一个恰好用到 UINT32_MAX,第二个溢出! return std::vector{"chunk1", "chunk2"}; }, initial); @@ -786,10 +785,10 @@ TEST_F(TraceableBatchOperationsTest, } // ============================================================================ -// 5. View and Ownership Tests +// 5. 视图与所有权测试 // ============================================================================ -// SFINAE probes to detect deleted overloads for factory functions +// 用 SFINAE 探测工厂函数被删除的重载 template struct CanJoinByItem : std::false_type {}; template @@ -834,7 +833,7 @@ TEST_F(TraceableBatchOperationsTest, CompileTimeRejectionOfRvalues) { using Item = TraceableItem; using Group = RequestGroup; - // ItemJoinView must reject non-const and const rvalues + // ItemJoinView 必须拒绝非 const 和 const 右值 static_assert( std::is_constructible_v, const Batch&, const Batch&, @@ -883,7 +882,7 @@ TEST_F(TraceableBatchOperationsTest, CompileTimeRejectionOfRvalues) { std::vector>>, "ItemJoinView must reject both const rvalues"); - // JoinByItem factory function must reject non-const and const rvalues + // JoinByItem 工厂函数必须拒绝非 const 和 const 右值 static_assert(CanJoinByItem::value, "JoinByItem must accept const lvalues"); static_assert(CanJoinByItem::value, @@ -909,7 +908,7 @@ TEST_F(TraceableBatchOperationsTest, CompileTimeRejectionOfRvalues) { static_assert(!CanJoinByItem::value, "JoinByItem must reject both const rvalues"); - // RequestGroupView must reject non-const and const rvalues + // RequestGroupView 必须拒绝非 const 和 const 右值 static_assert( std::is_constructible_v< RequestGroupView, const Batch&, @@ -970,7 +969,7 @@ TEST_F(TraceableBatchOperationsTest, CompileTimeRejectionOfRvalues) { std::vector, std::unordered_map>, "RequestGroupView must reject both const rvalues"); - // GroupByRequest factory function must reject non-const and const rvalues + // GroupByRequest 工厂函数必须拒绝非 const 和 const 右值 static_assert(CanGroupByRequest::value, "GroupByRequest must accept const lvalues"); static_assert(CanGroupByRequest::value, @@ -998,7 +997,7 @@ TEST_F(TraceableBatchOperationsTest, CompileTimeRejectionOfRvalues) { static_assert(!CanGroupByRequest::value, "GroupByRequest must reject both const rvalues"); - // Selection must reject non-const and const rvalues + // Selection 必须拒绝非 const 和 const 右值 static_assert(!std::is_constructible_v, Batch&&, std::vector>, "Selection must reject rvalue anchor"); @@ -1006,7 +1005,7 @@ TEST_F(TraceableBatchOperationsTest, CompileTimeRejectionOfRvalues) { std::vector>, "Selection must reject const rvalue anchor"); - // SelectBatch factory function must reject non-const and const rvalues + // SelectBatch 工厂函数必须拒绝非 const 和 const 右值 static_assert(CanSelectBatch::value, "SelectBatch must accept const lvalue"); static_assert(CanSelectBatch::value, @@ -1016,7 +1015,7 @@ TEST_F(TraceableBatchOperationsTest, CompileTimeRejectionOfRvalues) { static_assert(!CanSelectBatch::value, "SelectBatch must reject const rvalue anchor"); - // RequestGroup AddAnchor and AddMember must reject rvalue items + // RequestGroup 的 AddAnchor 和 AddMember 必须拒绝右值条目 static_assert(CanAddAnchor::value, "AddAnchor must accept const lvalue item"); static_assert(CanAddAnchor::value, @@ -1035,7 +1034,7 @@ TEST_F(TraceableBatchOperationsTest, CompileTimeRejectionOfRvalues) { static_assert(!CanAddMember::value, "AddMember must reject const rvalue item"); - // JoinedRow must reject rvalue left items + // JoinedRow 必须拒绝右值 left 条目 static_assert(std::is_constructible_v, const Item&, const Item*>, "JoinedRow must accept const lvalue left"); @@ -1059,14 +1058,14 @@ TEST_F(TraceableBatchOperationsTest, ASSERT_TRUE(sel_res.ok()); materialized = sel_res.value().Materialize(); } - // sel_res and Selection are now out of scope + // sel_res 和 Selection 此时已离开作用域 ASSERT_EQ(materialized.size(), 1u); EXPECT_EQ(materialized[0].req_id, 1u); EXPECT_EQ(materialized[0].data, "persist_me"); } // ============================================================================ -// 6. Starter Nodes Verification with Mock LLM +// 6. 使用 Mock LLM 验证入门 Node // ============================================================================ TEST_F(TraceableBatchOperationsTest, StarterBatchJoinNodeHarness) { @@ -1094,10 +1093,10 @@ TEST_F(TraceableBatchOperationsTest, StarterBatchGroupNodeHarness) { harness.Config({{"bind_model", "test_llm"}}); harness.BindModel("test_llm", mock_llm); - // Interleaved queries + // 交错的 query harness.CustomInput("queries", TextBatch{{10, 0, "q1"}, {20, 0, "q2"}, {10, 1, "q3"}}); - // Aggregated references + // 聚合的参考资料 harness.CustomInput( "references", TextBatch{{10, 0, "ref1"}, {10, 1, "ref2"}, {20, 0, "ref3"}}); @@ -1108,17 +1107,17 @@ TEST_F(TraceableBatchOperationsTest, StarterBatchGroupNodeHarness) { EXPECT_EQ(mock_llm->call_count, 1); const auto& prompts = mock_llm->last_prompts; ASSERT_EQ(prompts.size(), 3u); - // req 10 query 0: context ref1 + ref2 + // req 10 query 0:上下文 ref1 + ref2 EXPECT_EQ(prompts[0].req_id, 10u); EXPECT_EQ(prompts[0].sub_id, 0u); EXPECT_EQ(prompts[0].data, "ref1\nref2\nq1"); - // req 20 query 0: context ref3 + // req 20 query 0:上下文 ref3 EXPECT_EQ(prompts[1].req_id, 20u); EXPECT_EQ(prompts[1].sub_id, 0u); EXPECT_EQ(prompts[1].data, "ref3\nq2"); - // req 10 query 1: context ref1 + ref2 + // req 10 query 1:上下文 ref1 + ref2 EXPECT_EQ(prompts[2].req_id, 10u); EXPECT_EQ(prompts[2].sub_id, 1u); EXPECT_EQ(prompts[2].data, "ref1\nref2\nq3"); @@ -1135,14 +1134,14 @@ TEST_F(TraceableBatchOperationsTest, harness.BindModel("test_gen_llm", generator); harness.BindModel("test_pol_llm", polisher); - // Generator produces answers without [POLISH] + // 生成器产出的答案不含 [POLISH] harness.TextInput("input", {"hello", "world"}); auto result = harness.Run(); ASSERT_TRUE(result.ok()) << result.diagnostic(); EXPECT_EQ(generator->call_count, 1); - EXPECT_EQ(polisher->call_count, 0); // Second call skipped! + EXPECT_EQ(polisher->call_count, 0); // 跳过了第二次调用! EXPECT_EQ(result.TextValues("output"), (std::vector{"ans:hello", "ans:world"})); } @@ -1173,13 +1172,13 @@ TEST_F(TraceableBatchOperationsTest, outputs->clear(); for (const auto& item : prompts) { if (is_generator) { - // If item has "bad", output with [POLISH] + // 条目含 "bad" 时,输出带 [POLISH] std::string text = (item.data.find("bad") != std::string::npos) ? (item.data + " [POLISH]") : ("clean:" + item.data); outputs->emplace_back(item.req_id, item.sub_id, std::move(text)); } else { - // Polisher: replace [POLISH] with polished version + // 润色器:把 [POLISH] 替换为润色后的版本 std::string text = item.data; size_t tag = text.find(" [POLISH]"); if (tag != std::string::npos) text.erase(tag); @@ -1206,25 +1205,24 @@ TEST_F(TraceableBatchOperationsTest, harness.BindModel("test_gen_llm", generator); harness.BindModel("test_pol_llm", polisher); - // Input 1 is clean, input 2 is bad, input 3 is clean + // 输入 1 正常,输入 2 有问题,输入 3 正常 harness.TextInput("input", {"good1", "bad2", "good3"}); auto result = harness.Run(); ASSERT_TRUE(result.ok()) << result.diagnostic(); - // Generator called once with all 3 items + // 生成器对全部 3 个条目只调用一次 EXPECT_EQ(generator->call_count, 1); EXPECT_EQ(generator->last_prompts.size(), 3u); - // Polisher called once ONLY on the 1 selected item ("bad2 [POLISH]") + // 润色器只对选中的 1 个条目 ("bad2 [POLISH]") 调用一次 EXPECT_EQ(polisher->call_count, 1); ASSERT_EQ(polisher->last_prompts.size(), 1u); EXPECT_EQ(polisher->last_prompts[0].req_id, 102u); EXPECT_EQ(polisher->last_prompts[0].sub_id, 0u); EXPECT_EQ(polisher->last_prompts[0].data, "bad2 [POLISH]"); - // Full output has clean items unchanged, polished item replaced, in original - // order + // 完整输出按原顺序排列:正常条目不变,润色条目被替换 auto outputs = result.TextValues("output"); ASSERT_EQ(outputs.size(), 3u); EXPECT_EQ(outputs[0], "clean:good1"); @@ -1238,7 +1236,7 @@ TEST_F(TraceableBatchOperationsTest, auto polisher = std::make_shared(); polisher->always_fail = true; - // Custom node with polish_tag = "ans:" so generator outputs get selected + // 自定义 Node 设置 polish_tag = "ans:",使生成器的输出被选中 NodeHarness harness("StarterBatchSelectScatterNode"); harness.Config({{"bind_model", "test_gen_llm"}, {"polish_model", "test_pol_llm"}, @@ -1255,7 +1253,7 @@ TEST_F(TraceableBatchOperationsTest, } // ============================================================================ -// 7. Functional Integration & Provenance Assertions +// 7. 功能集成与来源断言 // ============================================================================ struct DirectSubBatchInputs { @@ -1273,8 +1271,8 @@ NodeResult RunDirectSubBatch(const DirectSubBatchInputs& in, if (!sel.ok()) { return NodeResult::Failure(std::move(sel).ExtractFailure()); } - // Intentionally return sub-batch without ScatterReplace to test fail-closed - // PreservedOutput count validation. + // 有意不经 ScatterReplace 直接返回子批次, + // 以测试 PreservedOutput 数量校验的 fail-closed 行为。 return sel.value().Materialize(); } @@ -1374,7 +1372,7 @@ TEST_F(TraceableBatchOperationsTest, std::vector> anchor = { {1, 0, "alpha"}, {2, 0, "beta"}, {3, 0, "gamma"}}; - // 1. Predicate taking const TraceableItem& returning bool + // 1. 谓词接受 const TraceableItem& 并返回 bool auto res_bool = SelectBatch( anchor, [](const TraceableItem& item) { return item.req_id == 2; }); @@ -1382,8 +1380,7 @@ TEST_F(TraceableBatchOperationsTest, EXPECT_EQ(res_bool.value().size(), 1u); EXPECT_EQ(res_bool.value()[0].data, "beta"); - // 2. Predicate taking const TraceableItem& returning - // NodeResult + // 2. 谓词接受 const TraceableItem& 并返回 NodeResult auto res_node = SelectBatch( anchor, [](const TraceableItem& item) -> NodeResult { if (item.sub_id != 0) { @@ -1399,7 +1396,7 @@ TEST_F(TraceableBatchOperationsTest, } TEST_F(TraceableBatchOperationsTest, BatchFailureDetailFormatDiagnosticDirect) { - // 1. Failure with batch_detail containing key + // 1. batch_detail 含键的失败 NodeFailure f1( NodeErrorKind::kBusinessError, "predicate failed", BatchFailureDetail{"SelectBatch", BatchFailureReason::kCallbackFailed, @@ -1409,8 +1406,7 @@ TEST_F(TraceableBatchOperationsTest, BatchFailureDetailFormatDiagnosticDirect) { f1.FormatDiagnostic("fallback"), "SelectBatch callback_failed for req_id=42, sub_id=9: predicate failed"); - // 2. Failure where message already contains req_id - avoid redundant - // formatting + // 2. 消息已含 req_id 的失败,避免重复格式化 NodeFailure f2( NodeErrorKind::kInputError, "JoinByItem right batch missing key present in left: req_id=2, sub_id=0", @@ -1420,11 +1416,11 @@ TEST_F(TraceableBatchOperationsTest, BatchFailureDetailFormatDiagnosticDirect) { f2.FormatDiagnostic("fallback"), "JoinByItem right batch missing key present in left: req_id=2, sub_id=0"); - // 3. Failure without batch_detail + // 3. 不含 batch_detail 的失败 NodeFailure f3(NodeErrorKind::kBusinessError, "plain error", -1234); EXPECT_EQ(f3.FormatDiagnostic("fallback"), "plain error"); - // 4. Failure with empty message and batch_detail with key + // 4. 消息为空、batch_detail 含键的失败 NodeFailure f4( NodeErrorKind::kBusinessError, "", BatchFailureDetail{"SplitPayloads", BatchFailureReason::kCallbackFailed, @@ -1433,8 +1429,7 @@ TEST_F(TraceableBatchOperationsTest, BatchFailureDetailFormatDiagnosticDirect) { EXPECT_EQ(f4.FormatDiagnostic("fallback"), "SplitPayloads callback_failed for req_id=10, sub_id=3: fallback"); - // 5. Message with different req_id substring collision (req_id=4 vs - // req_id=400) + // 5. 消息中 req_id 子串冲突 (req_id=4 与 req_id=400) NodeFailure f5( NodeErrorKind::kBusinessError, "failed on req_id=400", BatchFailureDetail{"SelectBatch", BatchFailureReason::kCallbackFailed, @@ -1444,7 +1439,7 @@ TEST_F(TraceableBatchOperationsTest, BatchFailureDetailFormatDiagnosticDirect) { "SelectBatch callback_failed for req_id=4, sub_id=9: failed on " "req_id=400"); - // 6. Message contains req_id but lacks sub_id + // 6. 消息含 req_id 但缺少 sub_id NodeFailure f6( NodeErrorKind::kBusinessError, "failed on req_id=42", BatchFailureDetail{"SelectBatch", BatchFailureReason::kCallbackFailed, @@ -1454,8 +1449,7 @@ TEST_F(TraceableBatchOperationsTest, BatchFailureDetailFormatDiagnosticDirect) { "SelectBatch callback_failed for req_id=42, sub_id=9: failed on " "req_id=42"); - // 7. Message already contains full structured detail - avoids redundant - // double formatting + // 7. 消息已含完整结构化细节,避免重复格式化 NodeFailure f7( NodeErrorKind::kBusinessError, "SelectBatch callback_failed for req_id=42, sub_id=9: predicate failed", @@ -1500,8 +1494,7 @@ NodeResult RunBatchSelectFail(const BatchSelectFailInputs& in, if (!in.input || in.input->empty()) { return NodeResult::Success(TextBatch{}); } - // Item (42, 9) triggers failure with cause_code = -7788 and message = - // "predicate failed" + // 条目 (42, 9) 触发失败,cause_code = -7788,message = "predicate failed" auto sel = SelectBatch(*in.input, [](const std::string& s) -> NodeResult { if (s == "trigger_partial_key_failure") { diff --git a/tests/unit/nodes/test_vector_top_k_node.cpp b/tests/unit/nodes/test_vector_top_k_node.cpp index 7aae58e8..ec17bc16 100644 --- a/tests/unit/nodes/test_vector_top_k_node.cpp +++ b/tests/unit/nodes/test_vector_top_k_node.cpp @@ -20,7 +20,6 @@ class VectorTopKNodeTest : public ::testing::Test { std::unique_ptr session_ctx_; }; -// 1. Init & Config Validation TEST_F(VectorTopKNodeTest, InitAndConfigValidation) { auto node = NodeRegistry::Instance().Create("VectorTopKNode"); ASSERT_NE(node, nullptr); @@ -44,7 +43,6 @@ TEST_F(VectorTopKNodeTest, InitAndConfigValidation) { InitNodeForTest(*invalid_node3, {{"top_k", 2.5}}, session_ctx_.get())); } -// 2. Process Top-K Ranking with Shared Candidates TEST_F(VectorTopKNodeTest, ProcessRankingSharedCandidates) { auto node = NodeRegistry::Instance().Create("VectorTopKNode"); ASSERT_NE(node, nullptr); @@ -79,7 +77,6 @@ TEST_F(VectorTopKNodeTest, ProcessRankingSharedCandidates) { EXPECT_EQ((*ranked)[1].data.text, "Doc C (Mid Sim)"); } -// 3. Missing Queries Fails Closed TEST_F(VectorTopKNodeTest, MissingInputFailsClosed) { auto node = NodeRegistry::Instance().Create("VectorTopKNode"); ASSERT_NE(node, nullptr); diff --git a/tests/unit/operator/test_operator_output_pool.cpp b/tests/unit/operator/test_operator_output_pool.cpp index b118c66d..5b11fbb1 100644 --- a/tests/unit/operator/test_operator_output_pool.cpp +++ b/tests/unit/operator/test_operator_output_pool.cpp @@ -208,7 +208,7 @@ TEST_F(OperatorOutputPoolTest, LedgerPreservesFifoAndRejectsInvalidReturns) { EXPECT_EQ(pool->FreeBlockCount(), 3u); EXPECT_EQ(pool->CheckedOutCount(), 0u); - // Leave block1 queued; return the other two out of checkout order. + // block1 留在队列中;另外两个以不同于取出的顺序归还。 void *block2 = nullptr, *block3 = nullptr; ASSERT_EQ(pool->Acquire(&block2), 0); ASSERT_EQ(pool->Acquire(&block3), 0); @@ -217,7 +217,7 @@ TEST_F(OperatorOutputPoolTest, LedgerPreservesFifoAndRejectsInvalidReturns) { EXPECT_NE(block2, block3); pool->ReturnBlock(block3); pool->ReturnBlock(block2); - // Repeated rotations preserve FIFO addresses without allocating. + // 反复轮转保持 FIFO 地址且不分配内存。 for (int round = 0; round < 3; ++round) { for (void* expected : {block1, block3, block2}) { ASSERT_GT(pool->FreeBlockCount(), 0u); @@ -398,7 +398,7 @@ void CheckMultiStringOutputReuse(const char* suffix, EXPECT_EQ(str->data, buffers[i]); EXPECT_EQ(str->length, 0); EXPECT_EQ(str->data[0], '\0'); - // The whole configured buffer remains available after reuse. + // 复用后整个配置的缓冲区仍可用。 std::memset(str->data, 'y', 17); str->data[17] = '\0'; } @@ -718,7 +718,7 @@ TEST_F(OperatorOutputPoolTest, NormalizeOutputParameters(*binding, "{}", &spec.params, &error)) << error; } - // Multiple blocks exercise rollback after earlier blocks have succeeded. + // 多个块用于验证前面的块已成功后的回滚。 for (uint32_t depth : {1u, 3u}) { bool completed = false; for (int step = 0; step < 4096; ++step) { @@ -739,8 +739,8 @@ TEST_F(OperatorOutputPoolTest, result = OutputPoolState::Create(suffix, depth, spec, binding, &pool, &error); } catch (const std::bad_alloc&) { - // Preflight allocations may propagate to the Operator exception - // barrier; they must also leave no pool or leaked allocations. + // 预检分配失败可能传播到 Operator 异常屏障; + // 此时也不得留下池或泄漏的分配。 } failure.DisableFailure(); published = pool != nullptr; @@ -951,7 +951,7 @@ TEST_F(OperatorOutputPoolTest, all_null &= !frame.begin()->second; all_present &= static_cast(frame.begin()->second); } - // The same guard used by Process rolls back any unpublished leases. + // Process 使用的同一守卫会回滚所有未发布的租约。 lease.Rollback(); outputs.clear(); injected = failure.Triggered(); @@ -1017,7 +1017,7 @@ TEST_F(OperatorOutputPoolTest, EXPECT_TRUE(worker_ok); EXPECT_TRUE(inner_threw && inner_injected); EXPECT_TRUE(outer_threw && outer_injected); - // Unwinding an armed scope restores the default allocation behavior too. + // 展开已启用的作用域也会恢复默认分配行为。 try { test_support::ScopedAllocationFailure failure(0); throw 1; @@ -1051,7 +1051,7 @@ TEST_F(OperatorOutputPoolTest, void* failed = ::operator new(17, std::nothrow); nothrow_failed = failed == nullptr && nested.Triggered(); ::operator delete(failed); - // Deallocation in a nested scope must also update its parent's ledger. + // 嵌套作用域中的释放也必须更新父作用域的账本。 ::operator delete(scalar, size_t{17}); ::operator delete[](array); ::operator delete(storage, size_t{65}, std::align_val_t{64}); diff --git a/tests/unit/operator/test_operator_value_registry.cpp b/tests/unit/operator/test_operator_value_registry.cpp index 36543e36..b36b959f 100644 --- a/tests/unit/operator/test_operator_value_registry.cpp +++ b/tests/unit/operator/test_operator_value_registry.cpp @@ -238,8 +238,7 @@ TEST(OperatorValueRegistryTest, if (mutation == 4) allocator.destroy_external = {}; if (mutation == 5) allocator.output_layout.compute_block_payload_bytes = {}; if (mutation == 6) allocator.direction = IoDirection::kInput; - // Root cross-checking may be deferred until Init for static registration - // order. + // 为适应静态注册顺序,根类型交叉检查可推迟到 Init。 registry.RegisterOutputAllocator("invalid", allocator); EXPECT_EQ(registry.GlobalInit(), -6); EXPECT_TRUE(registry.HasConflict()); @@ -275,8 +274,8 @@ TEST(OperatorValueRegistryTest, EXPECT_FALSE( NormalizeOutputParameters(binding, params, &requested.params, &error)); } - // Successful normalization must supply typed parameters; callback errors - // cannot leave partial parameters visible to pool creation. + // 归一化成功时必须提供类型化参数;回调出错时不能让部分参数 + // 对池创建可见。 binding.normalize_parameters = [](const std::string&, std::shared_ptr* result, @@ -569,13 +568,13 @@ TEST(OperatorValueRegistryTest, AllSevenOutputTypesFootprintAndBudget) { EXPECT_GT(bytes_d0, 0u); EXPECT_LT(bytes_d0, kMaxHandlePoolPayloadBytes); - // Depth 1 + // 深度 1 size_t bytes_d1 = 0; EXPECT_TRUE( ComputeOutputPoolPayloadBytes(suffix, spec, 1, &bytes_d1, &err)); EXPECT_GT(bytes_d1, 0u); - // Depth 25 + // 深度 25 size_t bytes_d25 = 0; EXPECT_TRUE( ComputeOutputPoolPayloadBytes(suffix, spec, 25, &bytes_d25, &err)); @@ -607,7 +606,7 @@ TEST(OperatorValueRegistryTest, AllSevenOutputTypesFootprintAndBudget) { ResolvedOutputPoolSpec huge_spec; huge_spec.type = "keyword_out"; huge_spec.capacities["match_result_json"] = - 100 * 1024 * 1024; // 100 MiB capacity + 100 * 1024 * 1024; // 100 MiB 容量 size_t huge_bytes = 0; std::string huge_err; EXPECT_FALSE(ComputeOutputPoolPayloadBytes("keyword_out", huge_spec, 25, @@ -734,7 +733,7 @@ TEST(OperatorValueRegistryTest, MissingValidatorOrFactoryAuditRejection) { OperatorValueTypeRegistry reg; OperatorValueTypeBinding b; b.canonical_suffix = "custom_out5"; - b.external_c_type_name = ""; // empty + b.external_c_type_name = ""; SetMinimalOutputContract(&b); b.allocate_external = [](const ResolvedOutputPoolSpec&, OwnedExternalBlock*, std::string*) { return 0; }; @@ -746,8 +745,8 @@ TEST(OperatorValueRegistryTest, MissingValidatorOrFactoryAuditRejection) { } } -// Real allocation failures must leave registration retryable and the old -// catalog unchanged, regardless of the containers used internally. +// 真实分配失败时,注册必须可重试且旧 catalog 保持不变, +// 与内部使用何种容器无关。 TEST(OperatorValueRegistryTest, AllocationFailurePreservesRegistryAndAllowsRetry) { bool completed = false; @@ -1073,8 +1072,7 @@ TEST(OperatorValueRegistryTest, NoexceptOOMFaultTolerance) { } } -// 16. audio_in zero-length accepts null buffer, rejects negative -// length +// 16. audio_in 零长度时接受空缓冲区,拒绝负长度 TEST(OperatorValueRegistryTest, AudioInZeroLengthAcceptsNullBufferAndRejectsNegative) { const auto* binding = @@ -1089,14 +1087,14 @@ TEST(OperatorValueRegistryTest, std::string err; - // Case 1: pcm_length == 0 with nullptr pcm_buffer -> success (0) + // 情形 1:pcm_length == 0 且 pcm_buffer 为 nullptr -> 成功 (0) CompanyOperatorAudioInput zero_audio{}; zero_audio.sample_rate = 16000; zero_audio.pcm_length = 0; zero_audio.pcm_buffer = nullptr; EXPECT_EQ(binding->validate_external(&zero_audio, limits, &err), 0) << err; - // Case 2: pcm_length < 0 -> rejected (-3) + // 情形 2:pcm_length < 0 -> 拒绝 (-3) CompanyOperatorAudioInput neg_audio{}; neg_audio.sample_rate = 16000; neg_audio.pcm_length = -1; @@ -1105,7 +1103,7 @@ TEST(OperatorValueRegistryTest, EXPECT_EQ(binding->validate_external(&neg_audio, limits, &err), -3); EXPECT_NE(err.find("invalid or exceeds limit"), std::string::npos); - // Case 3: pcm_length > 0 with nullptr pcm_buffer -> rejected (-3) + // 情形 3:pcm_length > 0 且 pcm_buffer 为 nullptr -> 拒绝 (-3) CompanyOperatorAudioInput null_buf_audio{}; null_buf_audio.sample_rate = 16000; null_buf_audio.pcm_length = 100; @@ -1114,7 +1112,7 @@ TEST(OperatorValueRegistryTest, EXPECT_EQ(binding->validate_external(&null_buf_audio, limits, &err), -3); EXPECT_EQ(err, "pcm_buffer pointer is null"); - // Case 4: valid pcm_length and buffer -> success (0) + // 情形 4:合法的 pcm_length 和缓冲区 -> 成功 (0) float dummy_pcm[100] = {0.0f}; CompanyOperatorAudioInput valid_audio{}; valid_audio.sample_rate = 16000; diff --git a/tools/dev_recipe.py b/tools/dev_recipe.py index 5c2708d3..7061106e 100644 --- a/tools/dev_recipe.py +++ b/tools/dev_recipe.py @@ -53,7 +53,7 @@ } UNSUPPORTED_RECIPE_DEPLOYMENT = "UNSUPPORTED_RECIPE_DEPLOYMENT" -# These are labelled task fixtures, not an alternate capability catalog. +# 这些是带标签的任务夹具,不是另一份能力 Catalog。 DEFAULT_EFFECTS = { "keyword_match_rules": "tests/fixtures/effects/keyword_exact.json", "entity_extract_mock": "tests/fixtures/effects/entity_mock_exact.json", @@ -78,7 +78,7 @@ def absolute(path, root): def require_deployment(conf_path, tool, root): - """Check the effective output slots through the native I/O resolver.""" + """通过原生 I/O 解析器检查有效的输出槽位。""" conf = read_json_file(conf_path) pipe_path = conf.get("pipe_path") if not pipe_path or not isinstance(pipe_path, str): @@ -92,7 +92,7 @@ def require_deployment(conf_path, tool, root): raise RecipeError( "This recipe supports only single-output deployment; use the native Operator workflow for other output layouts.", code=UNSUPPORTED_RECIPE_DEPLOYMENT) - # Persist only authored overrides; required slot defaults stay in native code. + # 只持久化作者显式覆盖的值;必填槽位的默认值保留在原生代码中。 doc = read_json_file(pipeline_file) return doc.get("deployment", {}).get("io", {}).get("out_mem", {}) @@ -308,8 +308,8 @@ def prepare(recipe, name, profile_name, tool_path, build_dir, pipeline_target, r spec = copy.deepcopy(spec) spec["name"] = name + "_effects" spec["dataset"] = os.path.relpath(dataset, effects_target.parent) - # Check deployment fields against the compiled source graph. A newly generated - # Node becomes visible to native validation only after verify rebuilds the tool. + # 对照已编译的源码图检查部署字段。新生成的 Node 只有在 verify + # 重新构建工具后,才对原生校验可见。 bundle = deployment_root(root, target, models) with tempfile.TemporaryDirectory(prefix=".recipe-preview-", dir=root) as temporary: temp = Path(temporary) @@ -403,7 +403,7 @@ def verify_recipe(recipe, pipeline_path, tool_path, build_dir, effects_path, mod configuration = resolved["configuration"] if Path(configuration["pipeline_path"]).resolve() != pipeline_path: raise RecipeError("Deployment points to a different Pipeline than --pipeline") - # Compare the effective model paths with the asset selection used by evaluate. + # 将有效模型路径与 evaluate 使用的资源选型进行比较。 effective = configuration["effective_pipeline"] for model in effective.get("models", []): original = next(m for m in pipeline["models"] if m["model_id"] == model["model_id"]) diff --git a/tools/pipeline_studio/server.py b/tools/pipeline_studio/server.py index bf84ac87..d62026b9 100755 --- a/tools/pipeline_studio/server.py +++ b/tools/pipeline_studio/server.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Terminal viewer and local Pipeline Studio server for LLM-EdgeFlow.""" +"""LLM-EdgeFlow 的终端查看器和本地 Pipeline Studio 服务器。""" from __future__ import annotations @@ -62,7 +62,7 @@ def __init__(self, code: str, message: str, status: int = 400): class StudioHttpServer(http.server.ThreadingHTTPServer): - """Loopback HTTP server without reverse-DNS lookup during bind.""" + """绑定时不做反向 DNS 查询的回环 HTTP 服务器。""" def server_bind(self) -> None: socketserver.TCPServer.server_bind(self) @@ -186,14 +186,14 @@ def get_tool_fingerprint() -> str: class WorkbenchService: - """State and filesystem boundary behind /api/v1.""" + """/api/v1 背后的状态与文件系统边界。""" def __init__(self, config_root: Path = CONFIG_ROOT, initial: Path | None = None): self.config_root = config_root.resolve() self.jobs: dict[str, dict[str, Any]] = {} self.job_lock = threading.Lock() self.solution_lock = threading.Lock() - # Ownership is limited to pairs created by this server session. + # 所有权仅限于本服务器会话创建的配对。 self.generated_solutions: dict[str, dict[str, Any]] = {} self.initial_document = None if initial is not None: @@ -201,7 +201,7 @@ def __init__(self, config_root: Path = CONFIG_ROOT, initial: Path | None = None) self.initial_document = json_result( True, filename=initial.name, revision="", pipeline=pipeline, imported=True ) - # Only the existing managed file contract grants overwrite access. + # 只有现有的托管文件契约才授予覆盖权限。 try: managed = self.managed_path(initial.name, must_exist=True) if initial.resolve() == managed.resolve(): @@ -521,8 +521,8 @@ def check_revisions() -> tuple[bytes, bytes]: staged_conf.write_text(json.dumps(staged_conf_data, ensure_ascii=False, indent=2)) configuration = self.resolve_run_conf(staged_conf, profile) - # Native validation used the staged JSON; installed paths have - # the same model mappings and normalized node configuration. + # 原生校验使用的是暂存 JSON;安装后的路径具有相同的模型映射 + # 和归一化后的 Node 配置。 configuration["conf_path"] = str(conf_path) configuration["pipeline_path"] = str(path) staged_conf.write_bytes(conf_encoded) @@ -580,7 +580,7 @@ def deployment_candidate( self, pipeline: Any, profile_name: str = "", model_root: str = "models", filename: str = "", conf_name: str = "", ) -> tuple[dict[str, Any], dict[str, Any]]: - """Build one deployment snapshot shared by preview, run and save.""" + """构建一份供预览、运行和保存共用的部署快照。""" if conf_name: requested_conf = self.managed_conf_path(conf_name, must_exist=True) if not filename: @@ -624,7 +624,7 @@ def resolve_run_conf(self, conf_path: Path, profile: dict[str, Any]) -> dict[str @staticmethod def demo_command(profile: dict[str, Any], conf_path: Path, output_dir: Path, configuration: dict[str, Any]) -> list[str]: - # Snapshot only settings previously passed to Demo, not runtime Control. + # 只快照之前传给 Demo 的设置,不含运行时 Control。 name = configuration["biz_name"] run_profile = { "config": str(conf_path), diff --git a/tools/pipeline_studio/web/api.js b/tools/pipeline_studio/web/api.js index fd637ded..e326e3d0 100644 --- a/tools/pipeline_studio/web/api.js +++ b/tools/pipeline_studio/web/api.js @@ -4,7 +4,7 @@ export const initialPipeline = fragment.get("pipeline") || ""; export async function api(path, options = {}) { const { allowFalse = false, ...fetchOptions } = options; const headers = { "Content-Type": "application/json", ...(options.headers || {}) }; - // Keep the page's directory prefix when Studio is opened through a port proxy. + // 通过端口代理打开 Studio 时,保留页面的目录前缀。 const url = new URL(`api/v1${path}`, location.href); let response; try { response = await fetch(url, { ...fetchOptions, headers }); } diff --git a/tools/pipeline_studio/web/app.js b/tools/pipeline_studio/web/app.js index 2171bb33..594576be 100644 --- a/tools/pipeline_studio/web/app.js +++ b/tools/pipeline_studio/web/app.js @@ -119,7 +119,7 @@ function operationFeedback(message, error = false) { function showFailure(error) { let report = error.payload; if (report?.error?.code === "VALIDATION_FAILED") { - try { report = typeof report.error.message === "string" ? JSON.parse(report.error.message) : report.error.message; } catch { /* Use original error below. */ } + try { report = typeof report.error.message === "string" ? JSON.parse(report.error.message) : report.error.message; } catch { /* 下方使用原始错误。 */ } } if (Array.isArray(report?.diagnostics) && report.diagnostics.length) { showValidation(report); switchTab("validation"); @@ -191,7 +191,7 @@ function restorePositions() { function savePositions(positions) { try { localStorage.setItem(positionsKey(), JSON.stringify(positions)); } - catch { /* A full/disabled browser store must not interrupt editing. */ } + catch { /* 浏览器存储已满或被禁用时,不得中断编辑。 */ } } function selectNode(id) { @@ -745,8 +745,8 @@ async function pollRun(run) { setTimeout(() => pollRun(run), 700); } catch (error) { if (state.run !== run) return; - // A failed status request does not mean the process stopped. Retain cancel - // and retry until the server returns its terminal status. + // 状态请求失败并不代表进程已停止。保留取消和重试, + // 直到服务器返回终态。 run.pollError = error.message; renderRun(); setTimeout(() => pollRun(run), 2000); } @@ -1084,7 +1084,7 @@ $("#rawJson").addEventListener("input", event => { }); for (const [kind, selector] of [["node", "#nodeForm"], ["model", "#modelForm"]]) { const remember = event => { - // Graph controls submit authoring operations, not node-parameter drafts. + // 图控件提交的是编辑操作,而非 Node 参数草稿。 if (event.target.closest?.("#nodeBindings")) return; event.target.setCustomValidity?.(""); if (!state.editing) return; diff --git a/tools/pipeline_studio/web/editor.js b/tools/pipeline_studio/web/editor.js index 512375dd..714ec849 100644 --- a/tools/pipeline_studio/web/editor.js +++ b/tools/pipeline_studio/web/editor.js @@ -1,5 +1,5 @@ -// Document history stores applied edits only. Form buffers stay separate until -// Apply, so selection, validation and repainting cannot discard user input. +// 文档历史只保存已应用的编辑。表单缓冲在 Apply 之前独立存放, +// 因此选择、校验和重绘都不会丢弃用户输入。 export function createHistory(limit = 50) { let entries = [], cursor = -1; const copy = value => structuredClone(value); @@ -31,8 +31,8 @@ export function createDrafts() { }; } -// Validator text may include user-provided identifiers and values. Keep it as -// text while retaining the node navigation offered by the diagnostic card. +// Validator 文本可能包含用户提供的标识符和值。按纯文本保留, +// 同时保留诊断卡片提供的 Node 导航。 export function appendDiagnostic(container, item, selectNode, onPreviewFix) { const block = document.createElement("div"); block.className = "diagnostic"; const appendText = (tag, value) => { @@ -88,7 +88,7 @@ export function appendDiagnostic(container, item, selectNode, onPreviewFix) { container.append(block); } -// Config fields share the same editor for Node, Model and Backend parameters. +// Node、Model 和 Backend 参数共用同一个配置字段编辑器。 export function appendConfigField(container, field, values, modelChoices = null) { const label = document.createElement("label"); label.textContent = field.name; const hasDefault = field.default !== undefined && field.default !== null; @@ -112,7 +112,7 @@ export function appendConfigField(container, field, values, modelChoices = null) input.dataset.unsetOption = "true"; } input.dataset.field = field.name; input.dataset.type = field.type; - // Definition.required concerns field presence; a required string may be empty. + // Definition.required 关注字段是否存在;必填字符串可以为空。 input.required = Boolean(field.required) && field.type !== "string"; const present = Object.hasOwn(values, field.name); const value = present ? values[field.name] : field.default; @@ -126,8 +126,8 @@ export function appendConfigField(container, field, values, modelChoices = null) } } if (field.type === "string" && input.tagName === "TEXTAREA") { - // Browsers normalize CR/CRLF in textarea.value. Preserve the original string - // when applying an untouched field, including after a draft repaint. + // 浏览器会规范化 textarea.value 中的 CR/CRLF。应用未改动的字段时 + // 保留原始字符串,草稿重绘后也一样。 if (present || field.required) input.dataset.originalValue = text; input.dataset.displayValue = input.value; input.rows = Math.min(4, input.value.split("\n").length); diff --git a/tools/pipeline_studio/web/graph.js b/tools/pipeline_studio/web/graph.js index 21cc0d21..d383e1cb 100644 --- a/tools/pipeline_studio/web/graph.js +++ b/tools/pipeline_studio/web/graph.js @@ -14,7 +14,7 @@ export function nodeSize(definition = {}) { return { width: WIDTH, height: rows ? PORT_TOP + (rows - 1) * PORT_STEP + 24 : 76 }; } -// Layout uses the displayed node dimensions; no business or validation semantics live here. +// 布局使用显示出的 Node 尺寸;此处不含业务或校验语义。 export function layeredPositions(nodes, sizes = {}) { const byId = new Map(nodes.map(node => [node.id, node])); const indegree = new Map(nodes.map(node => [node.id, 0])); @@ -104,8 +104,8 @@ function simplify(points) { return result; } -// A visibility grid routes horizontal/vertical segments around padded node rectangles. -// Direction is part of the search state so a shorter but zigzagging path loses to a clean one. +// 可见性网格让水平/垂直线段绕开加了边距的 Node 矩形。 +// 方向是搜索状态的一部分,因此较短但曲折的路径会输给整洁的路径。 export function routeOrthogonal(source, target, obstacles, lane = 0) { const clearance = 12 + lane % 3 * 4; const start = { x: source.x + clearance + 10, y: source.y }; @@ -118,7 +118,7 @@ export function routeOrthogonal(source, target, obstacles, lane = 0) { const startIndex = ys.indexOf(start.y) * nx + xs.indexOf(start.x); const endIndex = ys.indexOf(end.y) * nx + xs.indexOf(end.x); const pointAt = index => ({ x: xs[index % nx], y: ys[Math.floor(index / nx)] }); - const startState = startIndex * 2; // horizontal start/end stubs + const startState = startIndex * 2; // 水平的起止短线 const costs = new Map([[startState, 0]]), parents = new Map(); const heap = []; const push = entry => { @@ -169,7 +169,7 @@ export function routeOrthogonal(source, target, obstacles, lane = 0) { } } if (finalState === undefined) { - // Manually overlapping cards can leave a port completely enclosed. Keep that binding visible. + // 手动重叠的卡片可能把端口完全遮住,此时仍保持该连线可见。 const y = Math.min(source.y, target.y, ...rectangles.map(rect => rect.top)) - 24; return simplify([source, start, { x: start.x, y }, { x: end.x, y }, end, target]); } @@ -183,7 +183,7 @@ function edgeKey(binding) { } function textLabel(value, maxWidth, fontSize) { - // A conservative character estimate also works before SVG is attached or fonts finish loading. + // 保守的字符宽度估算在 SVG 挂载或字体加载完成前也能使用。 let width = 0, label = ""; for (const char of String(value || "")) { width += /[^\x00-\x7f]/.test(char) ? fontSize : fontSize * .59; @@ -330,7 +330,7 @@ export class GraphView { const reopened = this.lastPositions !== this.positions; if (!this.editable) this.cancelConnection(); const renderKey = JSON.stringify([nodes, definitions, edges, [...errorIds], [...modelIds], this.editable]); - // Keep DOM targets stable during selection so double-click and keyboard focus survive. + // 选择期间保持 DOM 目标稳定,使双击和键盘焦点得以保留。 if (!reopened && !this.needsFit && this.renderKey === renderKey) { for (const group of this.nodeLayer.children) group.classList.toggle("selected", group.dataset.nodeId === selectedId); this.setSelectedEdge(selectedEdge); diff --git a/tools/pipeline_studio/web/workbench.js b/tools/pipeline_studio/web/workbench.js index 475628ba..43d2d7b7 100644 --- a/tools/pipeline_studio/web/workbench.js +++ b/tools/pipeline_studio/web/workbench.js @@ -146,8 +146,8 @@ export function modelAvailability(backends, modelDefinition) { }; } -// Check only the container shapes consumed by the viewer. Catalog/Validator -// still owns IDs, ports, fields, graph legality and model compatibility. +// 只检查查看器使用的容器结构。ID、端口、字段、图合法性和模型兼容性 +// 仍由 Catalog/Validator 负责。 export function assertBrowsablePipeline(pipeline) { const object = value => value !== null && typeof value === "object" && !Array.isArray(value); if (!object(pipeline) || !Array.isArray(pipeline.pipeline)) throw new Error("方案必须是包含 pipeline 数组的 JSON 对象"); diff --git a/tools/pipeline_studio/web/workflow.js b/tools/pipeline_studio/web/workflow.js index a5a43266..b0de0765 100644 --- a/tools/pipeline_studio/web/workflow.js +++ b/tools/pipeline_studio/web/workflow.js @@ -1,4 +1,4 @@ -// Run snapshots describe the submitted draft, independently of later edits or saves. +// 运行快照描述提交时的草稿,不受之后的编辑或保存影响。 export function captureRun({ documentVersion, pipeline, filename, profile, modelRoot }) { return { documentVersion, pipeline: JSON.stringify(pipeline), filename, profile, modelRoot, startedAt: new Date().toISOString(), job: { status: "starting" }, id: "" }; } @@ -13,7 +13,7 @@ export function runSummary(job) { const lines = [labels[job.status] || job.status]; const summary = job.result?.["summary.json"]; if (summary && typeof summary === "object") { - // Counts come from Demo's sample statuses, never from the process exit code. + // 计数来自 Demo 的样本状态,而非进程退出码。 if (Number.isFinite(summary.total_samples)) lines.push(`样本 ${summary.total_samples} 条`); if (Number.isFinite(summary.success_count)) lines.push(`成功 ${summary.success_count} 条`); if (Number.isFinite(summary.failed_count)) lines.push(`失败 ${summary.failed_count} 条`); diff --git a/tools/scaffold_custom_node.py b/tools/scaffold_custom_node.py index adff3875..61e6523b 100755 --- a/tools/scaffold_custom_node.py +++ b/tools/scaffold_custom_node.py @@ -28,7 +28,7 @@ def find_template(rel_path): STARTER_CONTROL_TEMPLATE = find_template("dev_support/node_authoring/starter_control_node.cpp") -# Compile-time capability signatures; availability still comes from the Catalog. +# 编译期能力签名;可用性仍以 Catalog 为准。 CAPABILITY_MAP = { "llm": ("LlmCall", "TextBatch", "TextBatch", "Generate"), "embedding": ("EmbeddingCall", "TextBatch", "EmbeddingBatch", "Embed"), @@ -48,7 +48,7 @@ def cpp_string(value): def parse_port_spec(spec, default_role="input"): - """Parse name:Batch[:1:1[:preserve]], keeping both halves of cardinality.""" + """解析 name:Batch[:1:1[:preserve]],保留 cardinality 的两侧。""" parts = [part.strip() for part in spec.split(":")] if len(parts) not in (2, 4, 5): raise ValueError(f"Invalid {default_role} port: expected name:Batch[:1:1[:preserve]]") @@ -67,7 +67,7 @@ def parse_port_spec(spec, default_role="input"): def get_item_type_for_batch(batch): - # No parallel table of payload names: derive them from the actual batch. + # 不维护 payload 名称的平行表,而是从实际批类型推导。 return f"decltype({batch}::value_type{{}}.data)" @@ -82,9 +82,9 @@ def render_map_node(name, description, in_port, out_port): namespace llm_edgeflow {{ namespace custom_nodes {{ namespace {{ -// Map starter: transforms each input item independently while preserving provenance. +// Map 入门模板:逐条独立转换输入,并保留来源信息。 static std::string Transform(const std::string& input) {{ - // TODO: Replace with your domain logic. + // TODO: 替换为你的领域逻辑。 return input; }} @@ -133,7 +133,7 @@ def render_embedding_node(name, description, in_name, out_name): static NodeResult Run(const Inputs& input, const NoParameters&, const Models& models) {{ - // TODO: Add domain preprocessing or postprocessing as needed. + // TODO: 按需添加领域预处理或后处理。 return models.encoder.Embed(*input.texts); }} @@ -206,8 +206,8 @@ def render_node(name, description, kind, capability, in_port, out_port, control_ processing = f" return NodeResult<{out_type}>::Success(*inputs.items);" else: processing = f''' (void)inputs; - // TODO: Implement the declared transformation and provenance policy. - // Do not publish default payloads as successful business results. + // TODO: 实现声明的转换和来源策略。 + // 不要把默认 payload 当作成功的业务结果发布。 return NodeResult<{out_type}>::Failure( NodeErrorKind::kBusinessError, "domain transformation is not implemented", -8102);''' return f'''#include "nodes/authoring.h" @@ -339,7 +339,7 @@ def sample_value_for_type(t, index=1): sample_in_3 = sample_value_for_type(in_type, 3) def payload_check(actual, batch_type, expected, label): - # A named value keeps aggregate commas out of GoogleTest macro arguments. + # 用具名值避免聚合初始化的逗号进入 GoogleTest 宏参数。 fields = { "AudioPcmBatch": ("pcm_data", "sample_rate"), "QueryCandidatesBatch": ("query", "candidate"), @@ -436,7 +436,7 @@ def payload_check(actual, batch_type, expected, label): NodeHarness harness({cpp_string(name)}); harness.Config({{{{"prefix", "biz:"}}}}); - // Editable business input with independent expected output. + // 可编辑的业务输入,以及独立的期望输出。 TextBatch input; input.emplace_back(101, 1, "task_sample"); harness.CustomInput({cpp_string(in_name)}, std::move(input)); @@ -597,7 +597,7 @@ def payload_check(actual, batch_type, expected, label): }} // namespace llm_edgeflow """ - # Model calls + # 模型调用 count_error = "node_error::author_node::kOutputCountMismatch" provenance_error = "node_error::author_node::kOutputProvenanceMismatch" mock_class = { @@ -683,14 +683,14 @@ def payload_check(actual, batch_type, expected, label): harness.BindModel("test_model", mock_model); harness.Config({{{{"bind_model", "test_model"}}}}); - // 1. Missing input fails without publishing output + // 1. 缺少输入时失败,且不发布输出 {{ auto result = harness.Run(); EXPECT_FALSE(result.ok()); EXPECT_EQ(result.Output<{out_type}>({cpp_string(out_name)}), nullptr); }} - // 2. Normal execution succeeds + // 2. 正常执行成功 {{ {in_type} input; input.emplace_back(101, 1, {sample_in_1}); @@ -705,7 +705,7 @@ def payload_check(actual, batch_type, expected, label): EXPECT_EQ(output->at(0).sub_id, 1u); }} - // 3. Model failure fails without publishing output + // 3. 模型失败时失败,且不发布输出 {{ mock_model->fail_ = true; @@ -718,7 +718,7 @@ def payload_check(actual, batch_type, expected, label): mock_model->fail_ = false; }} - // 4. Output count mismatch fails without publishing + // 4. 输出数量不一致时失败,且不发布 {{ mock_model->return_wrong_count_ = true; @@ -732,7 +732,7 @@ def payload_check(actual, batch_type, expected, label): mock_model->return_wrong_count_ = false; }} - // 5. Corrupted provenance fails without publishing + // 5. 来源信息损坏时失败,且不发布 {{ mock_model->corrupt_provenance_ = true; @@ -768,12 +768,11 @@ def payload_check(actual, batch_type, expected, label): class ChangePlan: - """Publish without clobbering targets; retain displaced edits on conflict. + """发布时不覆盖目标;冲突时保留被挪走的编辑。 - Directory locks serialize generators. Existing files are moved aside before - checking their content, then new versions are linked with no-replace semantics. - A concurrent editor creating the destination wins; displaced content is restored - without overwriting that editor, or retained at an explicitly reported path. + 目录锁使生成器串行执行。检查内容前先把现有文件挪开,再以不替换语义链接新版本。 + 并发编辑者先创建目标时以其为准;被挪走的内容会在不覆盖该编辑者的前提下恢复, + 或保留在明确报告的路径上。 """ def __init__(self): self.new_files = {} @@ -815,7 +814,7 @@ def _stage(path, content): @staticmethod def _restore(displaced, path): - # link() atomically refuses an existing destination, including symlinks. + # link() 遇到已存在的目标 (含符号链接) 会原子地拒绝。 try: os.link(displaced, path, follow_symlinks=False) except FileExistsError: @@ -840,7 +839,7 @@ def commit(self): staged = self._stage(path, content) try: os.chmod(staged, 0o644) - os.link(staged, path) # Never replace a concurrent new file. + os.link(staged, path) # 绝不替换并发新建的文件。 self.created_paths.append((path, content)) finally: staged.unlink(missing_ok=True) @@ -848,8 +847,8 @@ def commit(self): staged = self._stage(path, content) backup = self._stage(path, "") try: - # Capture the actual current file atomically; check the captured - # version, not a pathname which may change before publication. + # 原子地捕获当前实际文件;检查捕获到的版本, + # 而不是发布前可能变化的路径名。 os.replace(path, backup) self.backups[path] = backup if backup.is_symlink() or backup.read_text(encoding="utf-8") != original: @@ -861,7 +860,7 @@ def commit(self): staged.unlink(missing_ok=True) if path not in self.backups: backup.unlink(missing_ok=True) - # Detect edits through an open descriptor to the displaced inode. + # 通过指向被挪走 inode 的已打开描述符检测编辑。 for path, backup in self.backups.items(): if backup.read_text(encoding="utf-8") != self.modified_files[path][0]: raise RuntimeError(f"Concurrent edit detected in displaced file {backup}") diff --git a/tools/verify_selection.py b/tools/verify_selection.py index d488b0ec..363f910f 100644 --- a/tools/verify_selection.py +++ b/tools/verify_selection.py @@ -77,7 +77,7 @@ def pipeline_binding(pipeline): def build_run_conf(pipeline, outputs, pipe_path, model_root, bundle_root, pipeline_root=None): - """Preserve model paths, rebasing only when the explicit host root changes.""" + """保留模型路径,仅在显式指定的宿主根目录变化时重定位。""" bundle_root = Path(bundle_root).resolve() within(bundle_root, model_root) source_root = Path(pipeline_root).resolve() if pipeline_root is not None else bundle_root @@ -110,7 +110,7 @@ def validate_manifest(manifest): def build_variants(): - # Build-only presets and hidden inheritance defaults are not asset selections. + # 仅用于构建的 preset 和隐藏的继承默认值不属于资源选型。 return [item for item in read_json(PRESETS)["configurePresets"] if not item.get("hidden", False) and "llm-edgeflow/selection" in item.get("vendor", {})] @@ -258,8 +258,8 @@ def evaluate(pipeline, selection, tool, model_root, spec_path, conf_path, demo, model_root = Path(model_root).resolve() bundle_root = Path(pipeline_root).resolve() execution_pipeline = copy.deepcopy(pipeline) - # Use a temporary configuration inside the existing asset bundle; no model - # weights are copied and no path escapes the Operator deployment root. + # 在现有资源包内使用临时配置;不复制模型权重, + # 也没有路径越出 Operator 部署根目录。 with tempfile.TemporaryDirectory(prefix=".selection-", dir=bundle_root) as directory: temporary = Path(directory) relative = temporary.relative_to(bundle_root) @@ -322,7 +322,7 @@ def main(): if not args.effects or not args.output: parser.error("evaluate requires --effects and --output") report = evaluate(pipeline, report, args.tool, args.model_root, args.effects, conf, args.demo, args.pipeline_root) - # Re-hash assets after inference to detect mid-run changes. + # 推理后重新计算资源哈希,以检测运行期间的变更。 if inspect_selection(pipeline, args.tool, args.model_root, args.manifest, args.variant, args.pipeline_root)["selection_fingerprint"] != report["selection_fingerprint"]: raise ValueError("Selection assets changed during execution") ok = report["metrics"]["status"] == "passed"