diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa31c8fc..b1512c06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,7 +116,7 @@ jobs: test "$(uname -m)" = arm64 python -m pip install cmake==3.28.4 ninja==1.11.1.1 clang-format==18.1.8 jsonschema==4.23.0 xcrun --show-sdk-path - - name: Cache verified macOS dependencies + - name: Cache macOS dependencies uses: actions/cache@v4 with: path: | @@ -454,7 +454,7 @@ jobs: - name: Fetch and Verify Pinned Text and Vision Models run: ./scripts/fetch_real_test_models.sh --kite - - name: Download Verified Private Release and Configure kiteLLM + - name: Download Private Release and Configure kiteLLM env: GH_TOKEN: ${{ secrets.KITELLM_GITHUB_TOKEN }} run: | diff --git a/3rdparty/README.md b/3rdparty/README.md index 35c69557..5c7b13fb 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -1,9 +1,9 @@ # 3rdparty 依赖库目录规范 -本项目使用 `3rdparty/` 持久缓存固定版本、经过 SHA-256 校验的依赖。原始发布归档可以共享, +本项目使用 `3rdparty/` 持久缓存固定版本的依赖,CMake 不校验下载文件的内容 hash。原始发布归档可以共享, 本地编译产物按目标平台和构建身份分目录,避免 Linux/macOS、CPU 架构及 Debug/Release 相互覆盖。 -用户指定的私有 GitHub kiteLLM 依赖使用 `kite_llm///` 缓存已校验 +用户指定的私有 GitHub kiteLLM 依赖使用 `kite_llm///` 缓存 的 Release 归档;启用方式、读取授权和平台限制见 [kiteLLM 接入说明](../doc/kitellm.md)。 > **注意**:除本 `README.md` 外,`3rdparty/` 下的所有自动生成与下载内容均已被 `.gitignore` 忽略,不会提交至 Git 仓库。 @@ -19,13 +19,13 @@ ├── pcre2/// ├── googletest/// ├── onnxruntime/// # 固定平台发行包 -├── inferlet/// # 已校验下载归档,在各 build/_deps 解压 +├── inferlet/// # 下载归档,在各 build/_deps 解压 ├── kite_llm/// ├── nlohmann_json/include/ # 与平台无关的头文件 └── stb_image/ # 与平台无关的头文件 ``` -库缓存目录包含 `.edgeflow-cache-fingerprint`,记录版本、来源散列和目标 OS/架构。 +库缓存目录包含 `.edgeflow-cache-fingerprint`,记录版本和目标 OS/架构。 源码编译的静态库另记录编译器、工具链、SDK/sysroot、当前构建类型的编译参数和 sanitizer 设置; 官方预编译包不因本机 SDK 更新而改变身份。`target` 例如 `linux-arm64`、`macos-arm64`。 项目使用 Ninja 等单配置生成器;`cmake_ext/third_party/ThirdPartyCacheMetadata.cmake` @@ -39,10 +39,11 @@ - 首次执行 CMake 构建(如 `cmake -B build`)且本地 `3rdparty/` 为空时,构建系统会自动拉取依赖并完成编译/规整,将产物保存到 `3rdparty/` 对应子目录中。 2. **零编译 / 零下载秒级复用**: - 单配置构建在所选身份目录存在完整库、头文件和匹配指纹时,使用 `IMPORTED` 目标复用。 - - 指纹缺失或与当前固定源码、平台、编译器、ABI 选项不匹配时,CMake 会 fail-closed 地忽略旧缓存,并从已校验的固定来源重新生成和归档。 + - 指纹缺失或与当前源码版本、平台、编译器、ABI 选项不匹配时,CMake 会忽略旧缓存,并从固定版本来源重新生成和归档。 - llama.cpp 只缓存项目提前确定的 GGML 选项组合。出现未纳入身份的 GGML/LLAMA 构建变量时,llama/Whisper 均使用源码增量构建且不读写共享归档;同一源码构建目录中上游新增的默认选项也按此处理,身份目录保持稳定。配置日志会列出禁用共享归档的变量名称,或需要保留源码目标依赖的 Metal/BLAS 组合。 3. **强制重新编译 / 刷新**: - 如需重新编译 llama.cpp,可指定 `-DLLAMA_CPP_FORCE_REBUILD=ON`。不同身份目录可以并存,无需删除其他平台的缓存。 + - 下载归档与单文件缓存存在时直接复用,不比较内容 hash;需要刷新时删除对应下载文件再重新配置。版本号和 URL 不变时,上游替换文件不会使已有缓存自动失效。 --- diff --git a/AGENTS.md b/AGENTS.md index 8b2ed513..cf5748b9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,7 +118,8 @@ Do not copy entire design discussions or logs into handoffs unless necessary to ## Repository guardrails Preserve unrelated user changes; no destructive Git operations or direct pushes to `main`. -Do not bundle third-party sources/binaries; dependencies stay pinned and verified in `cmake_ext/`. +Do not bundle third-party sources/binaries; dependencies stay version-pinned in `cmake_ext/`. +CMake dependency downloads and cached dependency files do not undergo content-hash verification. The external workspace cannot access the company-internal SDK: do not request, infer, copy, or commit its headers, libraries, models, configuration, or credentials. Prepare only neutral integration seams; real SDK integration and target-hardware acceptance start only after the diff --git a/README.md b/README.md index 2b4c0437..00faa3e2 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,14 @@ [![C++17](https://img.shields.io/badge/C%2B%2B-17-blue)](CMakeLists.txt) [![License: MIT](https://img.shields.io/badge/License-MIT-green)](LICENSE) -LLM-EdgeFlow 用算法节点处理规则、检索、文本、图像和语音。Pipeline JSON 描述节点之间的连接。宿主程序通过 C++ Operator SDK 调用方案。 - -项目目前处于实际业务接入前的开发与验证阶段。仓库提供可运行示例和自动化测试。模型效果、性能及目标设备适配需按具体场景验收。 +LLM-EdgeFlow 用算法节点处理规则、检索、文本、图像和语音,由 Pipeline JSON 描述节点连线,供宿主程序通过 C++ Operator SDK 调用。 +项目目前处于实际业务接入前的开发与验证阶段,仓库提供可运行示例和自动化测试,模型效果、性能及目标设备适配需按具体场景验收。 [快速开始](#快速开始) · [架构与扩展](#架构与扩展) · [示例方案](#示例方案) · [开发文档](#开发文档) ## 按任务开始 -首次使用时,先完成快速开始。然后按任务选择教程。同一方案可使用多份教程。 +首次使用请先完成[快速开始](#快速开始),再按任务选择入口: | 你要做什么 | 主要修改 | 入口 | | :--- | :--- | :--- | @@ -30,10 +29,8 @@ LLM-EdgeFlow 用算法节点处理规则、检索、文本、图像和语音。P ## 快速开始 -本例使用 Linux CPU 环境。在仓库根目录执行所有命令。 -macOS Apple Silicon(含 M3)使用 `macos-arm64-cpu` 预设,见[跨平台构建](doc/VERIFIABLE_SELECTION.md#跨平台构建)。 -构建需要 C++17 编译器、CMake 3.19+、Ninja、Python 3 和 Git。 -首次构建需要联网下载固定版本的依赖。模型权重需单独准备。 +以下命令在仓库根目录执行(Linux CPU;macOS Apple Silicon 使用 `macos-arm64-cpu` 预设,见[跨平台构建](doc/VERIFIABLE_SELECTION.md#跨平台构建))。 +构建需要 C++17 编译器、CMake 3.19+、Ninja、Python 3 和 Git;首次构建需联网拉取固定版本依赖,模型权重需单独准备。 ### 1. 获取并构建 @@ -45,20 +42,16 @@ cmake --preset=inferlet-cpu -B build cmake --build build --target alg_sdk alg_demo alg_pipeline_tool alg_show --parallel 4 ``` -Linux 默认构建只启用 Inferlet,也可运行无需模型的示例。其他 Backend 须按[构建变体](doc/VERIFIABLE_SELECTION.md#构建变体) -显式启用;使用 Inferlet 在 CPU 上验证时见 [Inferlet 接入](doc/VERIFIABLE_SELECTION.md#inferlet-cpu-验证)。 -主要产物如下: +上述 Linux 构建启用 Inferlet,也可运行无模型示例。其他 Backend 见[构建变体](doc/VERIFIABLE_SELECTION.md#构建变体);Inferlet 模型运行见 [CPU 验证](doc/VERIFIABLE_SELECTION.md#inferlet-cpu-验证)。主要产物: -| 产物 | 用途 | -| :--- | :--- | -| `build/libllm_edgeflow_sdk.so` | 集成到宿主程序的算法共享库 | -| `build/alg_demo` | 使用统一入口运行示例方案 | -| `build/alg_pipeline_tool` | 查询能力、校验 Pipeline、查看执行计划 | -| `build/alg_show` | 在终端查看 Pipeline 的节点与连线 | +- `build/libllm_edgeflow_sdk.so`:集成到宿主程序的算法共享库 +- `build/alg_demo`:统一示例运行入口 +- `build/alg_pipeline_tool`:查询 Catalog、校验 Pipeline、查看执行计划 +- `build/alg_show`:在终端查看 Pipeline 节点与连线 ### 2. 运行第一个方案 -从无需模型权重的关键词匹配开始。下面使用仓库自带的三条测试文本,执行配置中定义的规则: +运行无需模型权重的关键词匹配示例: ```bash ./build/alg_pipeline_tool validate configs/pipeline_keyword_match_rules.json @@ -67,120 +60,85 @@ Linux 默认构建只启用 Inferlet,也可运行无需模型的示例。其 --output-dir results/quickstart ``` -预期三条输入均处理成功,第一、三条命中 `SYSTEM_INIT`,第二条未命中。结果位于: - -- `results/quickstart/keyword_match_rules/results.jsonl`:逐条请求的状态与匹配结果。 -- `results/quickstart/keyword_match_rules/summary.json`:样本数、成功数、失败数与耗时。 - -`keyword_match_rules` 是 Demo 运行预设。Demo 使用 Pipeline 中的规则。 -如需练习运行时更新规则,执行 `./build/alg_demo --profile keyword_match_control`。 +三条输入均处理成功(第一、三条命中 `SYSTEM_INIT`,第二条未命中),逐条结果与统计写入 `results/quickstart/keyword_match_rules/` 下的 `results.jsonl` 和 `summary.json`。如需体验运行时更新规则,可执行 `./build/alg_demo --profile keyword_match_control`。 ### 3. 查看与编辑流程 ```bash -# 查询当前构建中可用的业务契约与节点 +# 查询当前构建可用的业务契约与节点 ./build/alg_pipeline_tool catalog ./build/alg_pipeline_tool describe-node text_rule_match # 查看经过校验的执行计划 ./build/alg_pipeline_tool plan configs/pipeline_keyword_match_rules.json -# 打开本地 Web 工作台 +# 启动本地 Web 工作台 ./tools/pipeline_studio/server.py configs/pipeline_keyword_match_rules.json --web ``` -编辑器监听 `127.0.0.1`,直接编辑和保存 Pipeline JSON,支持连线、参数编辑、检查与试运行;无需管理 `.conf`。 -接下来,按 [Studio 编排练习](tools/pipeline_studio/README.md#第一次编排)修改规则。用 Demo 验证修改后的方案。 +本地 Web 工作台监听 `127.0.0.1`,支持连线、参数编辑、检查与试运行。后续可按 [Studio 编排练习](tools/pipeline_studio/README.md#第一次编排)修改规则并用 Demo 验证。 ## 架构与扩展 -框架按职责划分为四层。通常先复用节点并编写 Pipeline,出现能力缺口后再扩展对应层。 - -```mermaid -flowchart TD - Integration["接入适配层 · Integration
外部契约与数据转换"] - Orchestration["流程编排层 · Orchestration
配置校验、调度与上下文"] - Nodes["能力节点层 · Capability Nodes
通用操作与领域算法"] - Execution["模型执行层 · Model Execution
模型语义与推理后端"] - Integration --> Orchestration --> Nodes --> Execution -``` +框架自上而下分为四层,通常先复用节点编排 Pipeline,出现能力缺口时再扩展对应层(详见[架构设计](doc/architecture.md)): | 职责 | 何时扩展 | 主要入口 | | :--- | :--- | :--- | -| 接入适配层 | 请求/响应的字段、格式或含义变化,或新增宿主载体与调用约定 | `include/adapter/`、`include/edgeflow/operator/`、`src/adapter/` | -| 流程编排层 | 现有校验、调度或上下文机制无法满足需求 | `include/core/`、`src/core/` | -| 能力节点层 | 增加领域算法、数据处理或模型调用组合 | `src/custom_nodes/`;通用操作位于 `src/common_nodes/` | -| 模型执行层 | 增加模型语义或接入新的推理运行时 | `src/engine/models/`、`src/engine/backends/` | +| 接入适配层 · Integration | 请求/响应格式或语义变化,或新增宿主载体 | `include/adapter/`、`include/edgeflow/operator/`、`src/adapter/` | +| 流程编排层 · Orchestration | 现有配置校验、调度或上下文无法满足需求 | `include/core/`、`src/core/` | +| 能力节点层 · Capability Nodes | 新增领域算法、数据处理或模型调用组合 | `src/custom_nodes/`(通用操作在 `src/common_nodes/`) | +| 模型执行层 · Model Execution | 新增模型前后处理语义或接入推理后端 | `src/engine/models/`、`src/engine/backends/` | -完整职责、编译依赖和运行时数据流见[架构设计](doc/architecture.md)。首次编写算法见[自定义 Node 入门](doc/dev_guide/first_custom_node.md)。 +### 一个方案由哪些文件组成 -## 一个方案由哪些文件组成 +一个可运行方案通常包含三类配置(详见[运行当前方案](tools/pipeline_studio/README.md#运行当前方案)与[配置目录](configs/README.md)): -| 文件 | 负责什么 | 示例 | -| :--- | :--- | :--- | -| Pipeline JSON | 节点、依赖、类型端口、模型路径与算法参数;根 `io.input` / `io.output` 声明转换器、响应来源与输出容量 | [pipeline_keyword_match_rules.json](configs/pipeline_keyword_match_rules.json) | -| 部署 `.conf` | 仅用 `pipe_path` 指向 Pipeline JSON | [pipeline_keyword_match_rules.conf](configs/pipeline_keyword_match_rules.conf) | -| Demo Profile(可选) | 运行预设:业务、配置、数据集和批大小等 | [demo/profiles.json](demo/profiles.json) | - -Profile 保存运行预设。Demo 参数也可直接指定配置和数据集。 -保存新 Pipeline 后,将 `.conf` 指向新文件。步骤见[运行当前方案](tools/pipeline_studio/README.md#运行当前方案)。 +- **Pipeline JSON**:声明 `io.input` / `io.output` 转换器、节点连线、模型路径与参数,如 [pipeline_keyword_match_rules.json](configs/pipeline_keyword_match_rules.json)。 +- **部署 `.conf`**:通过 `pipe_path` 指向目标 Pipeline JSON,如 [pipeline_keyword_match_rules.conf](configs/pipeline_keyword_match_rules.conf)。 +- **Demo Profile(可选)**:在 [demo/profiles.json](demo/profiles.json) 中保存业务、配置与数据集预设。 ## 示例方案 -以下配置展示已有能力的组合方式。涉及模型的方案需要匹配的 Backend、权重及运行资源。实际可用的节点、模型和后端以目标构建的 `alg_pipeline_tool catalog` 为准。 +涉及模型的方案需准备匹配的构建变体与模型权重,实际可用能力以目标构建的 `./build/alg_pipeline_tool catalog` 为准。 | 场景 | 处理方式 | 配置与运行条件 | | :--- | :--- | :--- | | 关注词匹配 | 文本规则匹配与分类 | [配置](configs/pipeline_keyword_match_rules.json);无需模型权重 | -| 实体抽取 | LLM 生成与结构化结果解析 | [Inferlet 配置](configs/pipeline_entity_extract_inferlet.json);默认构建与匹配的语言模型,见 [运行命令](doc/VERIFIABLE_SELECTION.md#inferlet-cpu-验证) | -| 文档问答 | 文本分块、向量检索与 LLM 回答 | [配置](configs/pipeline_doc_qa_default.json);`full-cpu`,ONNX Runtime、llama.cpp 与对应模型 | -| 对话合规审核 | 检索、精排与 LLM 分析 | [配置](configs/pipeline_dialogue_audit_default.json);`full-cpu`,向量、精排和语言模型 | -| Cross-Encoder 精排 | 对问题与候选文本进行相关性评分 | [配置](configs/pipeline_cross_rerank_cpu.json);启用 ONNX Runtime 并准备精排模型 | -| OCR 票据问答 | 票据图像转写后抽取字段并问答 | [配置](configs/pipeline_ocr_invoice_qa_kite.json);Kite 与视觉、语言模型 | -| 语音识别与意图槽位 | Whisper 转写与规则分类 | [配置](configs/pipeline_audio_asr_intent_cpu.json);`full-cpu`,whisper.cpp 与语音模型 | -| 翻译 | 完整 JSON 请求中的 `query` 翻译为中文 | [配置](configs/pipeline_translate_cpu.json);`full-cpu`,llama.cpp 与语言模型 | - -Kite 的图像转写输出文本,不提供检测框或置信度。Kite、Whisper 等可选后端需单独选择构建配置。参考[构建变体与模型资产](doc/VERIFIABLE_SELECTION.md)和 [kiteLLM 接入说明](doc/kitellm.md)。 +| 实体抽取 | LLM 生成与结构化解析 | [Inferlet 配置](configs/pipeline_entity_extract_inferlet.json);Linux 默认构建 + 语言模型 | +| 文档问答 | 分块、向量检索与 LLM 回答 | [配置](configs/pipeline_doc_qa_default.json);`full-cpu`,ONNX Runtime + llama.cpp | +| 对话合规审核 | 检索、精排与 LLM 分析 | [配置](configs/pipeline_dialogue_audit_default.json);`full-cpu`,向量/精排/语言模型 | +| Cross-Encoder 精排 | 问题与候选文本相关性评分 | [配置](configs/pipeline_cross_rerank_cpu.json);ONNX Runtime + 精排模型 | +| OCR 票据问答 | 图像转写(纯文本)、字段抽取与问答 | [配置](configs/pipeline_ocr_invoice_qa_kite.json);Kite + 视觉/语言模型,见 [kiteLLM 说明](doc/kitellm.md) | +| 语音识别与意图 | Whisper 转写与规则分类 | [配置](configs/pipeline_audio_asr_intent_cpu.json);`full-cpu`,whisper.cpp | +| 翻译 | JSON 请求中 `query` 翻译为中文 | [配置](configs/pipeline_translate_cpu.json);`full-cpu`,llama.cpp | -查看全部 Demo 预设,或运行无需真实模型的 Smoke 套件: +查看全部预设或运行无需真实模型的 Smoke 套件(真实模型效果评估见[业务效果验收](doc/VERIFIABLE_SELECTION.md#业务效果验收)): ```bash ./build/alg_demo --list ./build/alg_demo --suite smoke ``` -Smoke 验证执行链路。真实模型的业务效果需使用目标数据集另行验证,方法见[效果验收](doc/VERIFIABLE_SELECTION.md#业务效果验收)。 - ## 开发文档 -日常业务开发从[按任务开始](#按任务开始)选择教程。遇到具体问题时再查阅以下参考。 - | 目标 | 入口 | | :--- | :--- | -| 理解节点端口、模型绑定与并发 | [Node 常用写法与按需参考](doc/dev_guide/custom_node_concepts.md) | -| 给节点增加运行时控制 | [Control 入门](doc/dev_guide/first_control.md) | +| 查阅全部文档与开发 Skills | [文档目录](doc/README.md) | +| 理解节点端口、模型绑定与运行时控制 | [Node 常用写法](doc/dev_guide/custom_node_concepts.md) · [Control 入门](doc/dev_guide/first_control.md) | | 扩展框架、模型或后端 | [开发者指南](doc/developer_guide.md) · [架构设计](doc/architecture.md) | -| 准备模型并验证效果 | [模型、构建与效果验收](doc/VERIFIABLE_SELECTION.md) | -| 查看发布说明 | [发布记录](doc/CHANGELOG.md) | -| 查阅全部文档 | [文档目录](doc/README.md) | +| 源码目录、头文件边界与命名约定 | [源码布局与命名](doc/dev_guide/source_layout.md) · [配置目录](configs/README.md) | +| 准备模型、构建变体与效果验收 | [模型、构建与效果验收](doc/VERIFIABLE_SELECTION.md) · [发布记录](doc/CHANGELOG.md) | ## 参与开发 -在独立分支上修改,并在交付前执行完整质量门禁: +在独立分支上开发,交付前运行完整质量门禁(需 `clang-format 18`、Java 17+ 及 CMake 所选 Python 环境中的 `jsonschema`): ```bash ./scripts/run_all_tests.sh ``` -该命令执行格式检查、静态检查、构建和 CTest 测试。 -环境需要 clang-format 18、Java 17+ 和 Python `jsonschema`。 -Debian/Ubuntu 的对应 Python 包为 `python3-jsonschema`。 -开发流程见 [CONTRIBUTING.md](CONTRIBUTING.md),测试说明见 [tests/README.md](tests/README.md),Agent 规则见 [AGENTS.md](AGENTS.md)。 - -`jsonschema` 需安装在 CMake 选中的 Python 环境中。项目尚未正式发布。发布说明见[发布记录](doc/CHANGELOG.md)。 +开发与交付规约见 [CONTRIBUTING.md](CONTRIBUTING.md),测试说明见 [tests/README.md](tests/README.md),Agent 规则见 [AGENTS.md](AGENTS.md)。 ## 许可证 -项目采用 [MIT License](LICENSE)。第三方组件及其许可见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。 - -仓库目录、源码头文件和命名约定见[源码布局与命名](doc/dev_guide/source_layout.md)。示例配置和 Profile 的组织见[配置目录](configs/README.md)。 +采用 [MIT License](LICENSE),第三方组件及许可见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。 diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 38cb629b..f28591d5 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -1,7 +1,7 @@ # Third-Party Notices LLM-EdgeFlow uses the following third-party components. Sources are obtained -by checksum-verified CMake dependency declarations and are not vendored in +by version-pinned CMake dependency declarations and are not vendored in this repository. ## nlohmann/json 3.11.3 diff --git a/cmake_ext/engines/Inferlet.cmake b/cmake_ext/engines/Inferlet.cmake index e8712ce1..c3426cea 100644 --- a/cmake_ext/engines/Inferlet.cmake +++ b/cmake_ext/engines/Inferlet.cmake @@ -4,10 +4,8 @@ if(NOT ENABLE_INFERLET) endif() if(LLM_EDGEFLOW_TARGET_ARCH STREQUAL "x86_64") set(_inferlet_platform "x64") - set(_inferlet_sha256 "3c5e09b43053cc0bddc9ff4c9353be59e810ecf0a41ccd0bb0fed1ddfa77d807") else() set(_inferlet_platform "arm") - set(_inferlet_sha256 "de7aa0078191e5c4450c057de89206dcb164f5d01892f3b13e6aac83633bf841") endif() include(cmake_ext/third_party/ThirdPartyCacheMetadata.cmake) @@ -16,7 +14,7 @@ set(_inferlet_asset "inferlet-${_inferlet_platform}.tar.gz") # 内网可预置同一归档以离线配置。 set(_inferlet_archive "${CMAKE_SOURCE_DIR}/3rdparty/inferlet/${_inferlet_version}/${_inferlet_platform}/${_inferlet_asset}") -edgeflow_download_verified_file("${_inferlet_archive}" "${_inferlet_sha256}" +edgeflow_download_file("${_inferlet_archive}" URL "https://github.com/chamsechan/Inferlet/releases/download/${_inferlet_version}/${_inferlet_asset}") edgeflow_import_release_package(Inferlet 0.1.1 "${_inferlet_archive}" "${CMAKE_BINARY_DIR}/_deps/inferlet_release" "inferlet-${_inferlet_platform}" libinferlet.a) diff --git a/cmake_ext/engines/KiteLlm.cmake b/cmake_ext/engines/KiteLlm.cmake index 3b33f086..618b9fe1 100644 --- a/cmake_ext/engines/KiteLlm.cmake +++ b/cmake_ext/engines/KiteLlm.cmake @@ -10,19 +10,16 @@ include(cmake_ext/third_party/ThirdPartyCacheMetadata.cmake) set(_kite_release "v0.1.0") if(LLM_EDGEFLOW_TARGET_ARCH STREQUAL "x86_64") set(_kite_platform "x64") - set(_kite_sha256 "bddaa55abe37cfc87f481139c41aadf79ad579b7cc20fe2b12c01978a2d3b1a0") else() set(_kite_platform "arm") - set(_kite_sha256 "1388da99c8fd4a45d0f85a489d29b2a12189e4955302ed75fd8bbddd1ebfc70d") endif() set(_kite_asset "kiteLLM-${_kite_platform}.tar.gz") set(_kite_cache "${CMAKE_SOURCE_DIR}/3rdparty/kite_llm/${_kite_release}/${_kite_platform}") set(_kite_archive "${_kite_cache}/${_kite_asset}") # 不同构建目录共享此归档缓存;只有缺少归档时才调用 gh。 -# 即使 tag 被移动,也以固定校验和为准。 find_program(KITELLM_GH_EXECUTABLE NAMES gh) file(MAKE_DIRECTORY "${_kite_cache}/download") -edgeflow_download_verified_file("${_kite_archive}" "${_kite_sha256}" +edgeflow_download_file("${_kite_archive}" COMMAND "${KITELLM_GH_EXECUTABLE}" release download "${_kite_release}" --repo chamsechan/kiteLLM --pattern "${_kite_asset}" --dir "${_kite_cache}/download" --clobber diff --git a/cmake_ext/engines/LlamaCpp.cmake b/cmake_ext/engines/LlamaCpp.cmake index 48bed9b7..b9f06097 100644 --- a/cmake_ext/engines/LlamaCpp.cmake +++ b/cmake_ext/engines/LlamaCpp.cmake @@ -13,8 +13,6 @@ if(ENABLE_LLAMACPP) set(LLAMA_3RDPARTY_DIR "${CMAKE_SOURCE_DIR}/3rdparty/llama_cpp") set(LLM_EDGEFLOW_LLAMACPP_COMMIT "70adb1b4cea5ee39f867792c78dc59320921eda7") - set(_LLAMA_SOURCE_SHA256 - "94d215f1fd85ded40f4674eccdbd3caf4a9b0daa00b6d72255efec922c6d94a4") # 先解析平台对应的 GGML 默认值,再检查缓存。 # 这些选项须同时描述源码构建和导入的归档:macOS 使用 Apple Accelerate 并关闭 OpenMP。 if(LLM_EDGEFLOW_TARGET_OS STREQUAL "macos") @@ -86,7 +84,6 @@ if(ENABLE_LLAMACPP) edgeflow_prepare_third_party_cache( NAME llama_cpp VERSION "${LLM_EDGEFLOW_LLAMACPP_COMMIT}" - SOURCE_SHA256 "${_LLAMA_SOURCE_SHA256}" CACHE_DIR "${LLAMA_3RDPARTY_DIR}" KIND STATIC ABI_OPTIONS ${_llama_abi_options} @@ -146,7 +143,6 @@ if(ENABLE_LLAMACPP) FetchContent_Declare( llama_cpp_source URL https://github.com/ggml-org/llama.cpp/archive/${LLM_EDGEFLOW_LLAMACPP_COMMIT}.tar.gz - URL_HASH SHA256=${_LLAMA_SOURCE_SHA256} ${EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS} ) FetchContent_MakeAvailable(llama_cpp_source) diff --git a/cmake_ext/engines/OnnxRuntime.cmake b/cmake_ext/engines/OnnxRuntime.cmake index 0c44768a..4d3c23de 100644 --- a/cmake_ext/engines/OnnxRuntime.cmake +++ b/cmake_ext/engines/OnnxRuntime.cmake @@ -11,22 +11,17 @@ message(STATUS "[Model Execution] Enabling ONNX Runtime Backend support...") set(_ort_version 1.17.3) if(LLM_EDGEFLOW_TARGET_PLATFORM STREQUAL "macos-arm64") set(_ort_package osx-arm64) - set(_ort_sha256 "236c49c9065213b0ec9dec874e3619da3d01cbc8b984bb24291247293454d0f4") elseif(LLM_EDGEFLOW_TARGET_PLATFORM STREQUAL "macos-x86_64") set(_ort_package osx-x86_64) - set(_ort_sha256 "6292ad3d2e095b54b012a9fce7361f39fbac0b75fb6e9b1d9c320874515182e8") elseif(LLM_EDGEFLOW_TARGET_PLATFORM STREQUAL "linux-arm64") set(_ort_package linux-aarch64) - set(_ort_sha256 "9f801577bd99676d1d821022e52b1f4554f56339ae3606c7b5ff3155f443c921") else() set(_ort_package linux-x64) - set(_ort_sha256 "f2f11f9da1e3e19b22a8b378b9af57a58433f40e3db6a803e75c0ec0eba97a20") endif() set(_ort_library "lib/libonnxruntime${CMAKE_SHARED_LIBRARY_SUFFIX}") edgeflow_prepare_third_party_cache( NAME onnxruntime VERSION ${_ort_version} - SOURCE_SHA256 "${_ort_sha256}" CACHE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/onnxruntime" KIND PREBUILT REQUIRED_FILES "${_ort_library}" include/onnxruntime_c_api.h @@ -40,7 +35,6 @@ else() FetchContent_Declare( onnxruntime_prebuilt URL "https://github.com/microsoft/onnxruntime/releases/download/v${_ort_version}/onnxruntime-${_ort_package}-${_ort_version}.tgz" - URL_HASH SHA256=${_ort_sha256} ${EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS} ) FetchContent_MakeAvailable(onnxruntime_prebuilt) diff --git a/cmake_ext/engines/WhisperCpp.cmake b/cmake_ext/engines/WhisperCpp.cmake index 1a4d5cb6..5882b05f 100644 --- a/cmake_ext/engines/WhisperCpp.cmake +++ b/cmake_ext/engines/WhisperCpp.cmake @@ -14,12 +14,10 @@ message(STATUS "[Model Execution] Enabling whisper.cpp Backend support...") set(WHISPER_3RDPARTY_DIR "${CMAKE_SOURCE_DIR}/3rdparty/whisper_cpp") set(LLM_EDGEFLOW_WHISPERCPP_COMMIT "371b5a7561823ab2bb32142d2751e35e7534727b") -set(_WHISPER_SOURCE_SHA256 "89051d8fca516a3ad1f5c2f8f9d2fccb089afbaec338fca3f8731999babc6f81") edgeflow_prepare_third_party_cache( NAME whisper_cpp VERSION "${LLM_EDGEFLOW_WHISPERCPP_COMMIT}" - SOURCE_SHA256 "${_WHISPER_SOURCE_SHA256}" CACHE_DIR "${WHISPER_3RDPARTY_DIR}" KIND STATIC ABI_OPTIONS GGML_PROVIDER=llama_cpp GGML_SOURCE=${LLM_EDGEFLOW_LLAMACPP_COMMIT} @@ -48,7 +46,6 @@ else() FetchContent_Declare( whisper_cpp_source URL https://github.com/ggml-org/whisper.cpp/archive/${LLM_EDGEFLOW_WHISPERCPP_COMMIT}.tar.gz - URL_HASH SHA256=${_WHISPER_SOURCE_SHA256} ${EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS} ) # MakeAvailable 在 CMake 3.28 前不支持 EXCLUDE_FROM_ALL,此处只构建被链接的 whisper。 diff --git a/cmake_ext/third_party/GoogleTest.cmake b/cmake_ext/third_party/GoogleTest.cmake index e8f43f55..a056d3fa 100644 --- a/cmake_ext/third_party/GoogleTest.cmake +++ b/cmake_ext/third_party/GoogleTest.cmake @@ -5,7 +5,6 @@ include(cmake_ext/third_party/ThirdPartyCacheMetadata.cmake) edgeflow_prepare_third_party_cache( NAME googletest VERSION 1.14.0 - SOURCE_SHA256 8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 CACHE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/googletest" KIND STATIC ABI_OPTIONS BUILD_GMOCK=OFF @@ -32,7 +31,6 @@ else() FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz - URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 ${EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS} ) set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) diff --git a/cmake_ext/third_party/NlohmannJson.cmake b/cmake_ext/third_party/NlohmannJson.cmake index 86b73c55..2c60182a 100644 --- a/cmake_ext/third_party/NlohmannJson.cmake +++ b/cmake_ext/third_party/NlohmannJson.cmake @@ -5,7 +5,6 @@ include(cmake_ext/third_party/ThirdPartyCacheMetadata.cmake) edgeflow_prepare_third_party_cache( NAME nlohmann_json VERSION 3.11.3 - SOURCE_SHA256 0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406 CACHE_DIR "${JSON_3RDPARTY_DIR}" KIND HEADER_ONLY REQUIRED_FILES include/nlohmann/json.hpp @@ -24,7 +23,6 @@ else() FetchContent_Declare( nlohmann_json URL https://github.com/nlohmann/json/archive/refs/tags/v3.11.3.tar.gz - URL_HASH SHA256=0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406 ${EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS} ) diff --git a/cmake_ext/third_party/Pcre2.cmake b/cmake_ext/third_party/Pcre2.cmake index 4073d17f..1f3ebe62 100644 --- a/cmake_ext/third_party/Pcre2.cmake +++ b/cmake_ext/third_party/Pcre2.cmake @@ -5,7 +5,6 @@ include(cmake_ext/third_party/ThirdPartyCacheMetadata.cmake) edgeflow_prepare_third_party_cache( NAME pcre2 VERSION 10.47 - SOURCE_SHA256 c08ae2388ef333e8403e670ad70c0a11f1eed021fd88308d7e02f596fcd9dc16 CACHE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/pcre2" KIND STATIC ABI_OPTIONS PCRE2_BUILD_PCRE2_8=ON PCRE2_STATIC_PIC=ON @@ -33,7 +32,7 @@ else() include(FetchContent) # 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) @@ -46,7 +45,6 @@ else() FetchContent_Declare( pcre2 URL https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.47/pcre2-10.47.tar.gz - URL_HASH SHA256=c08ae2388ef333e8403e670ad70c0a11f1eed021fd88308d7e02f596fcd9dc16 ${EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS} ) FetchContent_MakeAvailable(pcre2) diff --git a/cmake_ext/third_party/StbImage.cmake b/cmake_ext/third_party/StbImage.cmake index cb78f945..2f321685 100644 --- a/cmake_ext/third_party/StbImage.cmake +++ b/cmake_ext/third_party/StbImage.cmake @@ -1,9 +1,8 @@ -# 版本固定且经校验的图像解码器。不提交任何第三方源码。 +# 版本固定的图像解码器。不提交任何第三方源码。 include(cmake_ext/third_party/ThirdPartyCacheMetadata.cmake) set(_stb_commit "013ac3beddff3dbffafd5177e7972067cd2b5083") set(_stb_dir "${CMAKE_SOURCE_DIR}/3rdparty/stb_image/${_stb_commit}") -edgeflow_download_verified_file("${_stb_dir}/stb_image.h" - "594c2fe35d49488b4382dbfaec8f98366defca819d916ac95becf3e75f4200b3" +edgeflow_download_file("${_stb_dir}/stb_image.h" URL "https://raw.githubusercontent.com/nothings/stb/${_stb_commit}/stb_image.h") add_library(edgeflow_stb_image INTERFACE) target_include_directories(edgeflow_stb_image SYSTEM INTERFACE "${_stb_dir}") diff --git a/cmake_ext/third_party/ThirdPartyCacheMetadata.cmake b/cmake_ext/third_party/ThirdPartyCacheMetadata.cmake index b7c5a731..9c612f1f 100644 --- a/cmake_ext/third_party/ThirdPartyCacheMetadata.cmake +++ b/cmake_ext/third_party/ThirdPartyCacheMetadata.cmake @@ -9,16 +9,16 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24") set(EDGEFLOW_FETCHCONTENT_TIMESTAMP_ARGS DOWNLOAD_EXTRACT_TIMESTAMP TRUE) endif() -# 准备可检查的缓存标记,并报告现有缓存是否由相同的固定源码 +# 准备可检查的缓存标记,并报告现有缓存是否由相同的源码版本 # 和 ABI 相关构建环境生成;REQUIRED_FILES 缺失时同样不可复用。 function(edgeflow_prepare_third_party_cache) set(_one_value_args - NAME VERSION SOURCE_SHA256 CACHE_DIR KIND MARKER_ROOT OUT_VALID OUT_MARKER OUT_CACHE_DIR) + NAME VERSION CACHE_DIR KIND MARKER_ROOT OUT_VALID OUT_MARKER OUT_CACHE_DIR) set(_multi_value_args ABI_OPTIONS REQUIRED_FILES) cmake_parse_arguments(ARG "" "${_one_value_args}" "${_multi_value_args}" ${ARGN}) - foreach(_required NAME VERSION SOURCE_SHA256 CACHE_DIR KIND OUT_VALID OUT_MARKER) + foreach(_required NAME VERSION CACHE_DIR KIND OUT_VALID OUT_MARKER) if(NOT ARG_${_required}) message(FATAL_ERROR "edgeflow_prepare_third_party_cache requires ${_required}") @@ -30,7 +30,7 @@ function(edgeflow_prepare_third_party_cache) endif() set(_basis - "schema=1\nname=${ARG_NAME}\nversion=${ARG_VERSION}\nsource_sha256=${ARG_SOURCE_SHA256}\nkind=${ARG_KIND}\n") + "schema=1\nname=${ARG_NAME}\nversion=${ARG_VERSION}\nkind=${ARG_KIND}\n") if(ARG_KIND STREQUAL "PREBUILT" OR ARG_KIND STREQUAL "STATIC") set(_target_arch "${CMAKE_SYSTEM_PROCESSOR}") if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_ARCHITECTURES) @@ -110,7 +110,7 @@ function(edgeflow_prepare_third_party_cache) endif() elseif(EXISTS "${_cache_dir}") message(STATUS - "[3rdparty] Ignoring unverified ${ARG_NAME} cache: ${_cache_dir}") + "[3rdparty] Ignoring unmarked ${ARG_NAME} cache: ${_cache_dir}") endif() # 指纹只证明身份,归档仍须完整才能导入。 foreach(_required IN LISTS ARG_REQUIRED_FILES) @@ -156,9 +156,9 @@ function(edgeflow_publish_third_party_cache name) VERBATIM) endfunction() -# 先下载到暂存文件并校验 SHA-256 再改名,共享缓存只出现完整文件; -# 已有文件每次配置都重新校验。URL 直接下载,COMMAND 用于需要认证的下载工具。 -function(edgeflow_download_verified_file path sha256) +# 先下载到暂存文件,下载成功后再改名,避免共享缓存使用中断的下载。 +# 已有文件直接复用;URL 直接下载,COMMAND 用于需要认证的下载工具。 +function(edgeflow_download_file path) cmake_parse_arguments(ARG "" "URL;DOWNLOADED;FAILURE_MESSAGE" "COMMAND" ${ARGN}) get_filename_component(_directory "${path}" DIRECTORY) file(MAKE_DIRECTORY "${_directory}") @@ -182,19 +182,11 @@ function(edgeflow_download_verified_file path sha256) endif() message(FATAL_ERROR "Cannot download ${path}: ${_status}") endif() - file(SHA256 "${_downloaded}" _actual) - if(NOT _actual STREQUAL sha256) - message(FATAL_ERROR "Downloaded SHA-256 mismatch: ${path}") - endif() file(RENAME "${_downloaded}" "${path}") endif() - file(SHA256 "${path}" _actual) - if(NOT _actual STREQUAL sha256) - message(FATAL_ERROR "Cached SHA-256 mismatch. Remove ${path} and configure again.") - endif() endfunction() -# 每次配置把已校验归档重新解压到当前构建,避免残留文件和旧环境的绝对路径; +# 每次配置把缓存归档重新解压到当前构建,避免残留文件和旧环境的绝对路径; # 只接受解压目录内与固定版本完全一致的 CMake 包及其静态库。 function(edgeflow_import_release_package package version archive extract_dir package_root library) file(REMOVE_RECURSE "${extract_dir}") @@ -202,7 +194,7 @@ function(edgeflow_import_release_package package version archive extract_dir pac execute_process(COMMAND "${CMAKE_COMMAND}" -E tar xzf "${archive}" WORKING_DIRECTORY "${extract_dir}" RESULT_VARIABLE _result) if(NOT _result EQUAL 0) - message(FATAL_ERROR "Cannot extract verified ${package} release: ${archive}") + message(FATAL_ERROR "Cannot extract ${package} release: ${archive}") endif() set(_root "${extract_dir}/${package_root}") unset(${package}_DIR CACHE) @@ -210,7 +202,7 @@ function(edgeflow_import_release_package package version archive extract_dir pac PATHS "${_root}/lib/cmake/${package}" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) get_target_property(_library ${package}::${package} IMPORTED_LOCATION_RELEASE) if(NOT _library STREQUAL "${_root}/lib/${library}") - message(FATAL_ERROR "${package} must resolve to the verified release archive") + message(FATAL_ERROR "${package} must resolve to the selected release archive") endif() - message(STATUS "[3rdparty] Verified ${package} ${version}: ${_library}") + message(STATUS "[3rdparty] Imported ${package} ${version}: ${_library}") endfunction() diff --git a/doc/VERIFIABLE_SELECTION.md b/doc/VERIFIABLE_SELECTION.md index ecbc41a9..e134ea55 100644 --- a/doc/VERIFIABLE_SELECTION.md +++ b/doc/VERIFIABLE_SELECTION.md @@ -126,7 +126,7 @@ sanitizer 的 `--fast` 复用 `minimal`,`--full` / `--ci-runtime` 复用 `dev- 这些入口不会依赖另一次构建遗留的 Backend 开关。 CMake 缓存包含绝对路径。如果更换构建目录,在新目录重新配置。 -保留已校验的 `3rdparty/` 缓存可减少重复下载。 +保留版本和构建身份匹配的 `3rdparty/` 缓存可减少重复下载。 ### 跨平台构建 @@ -193,10 +193,10 @@ python3 tools/verify_selection.py check \ ### Inferlet CPU 验证 `inferlet-cpu` 使用固定版本 Inferlet v0.1.1 CPU 静态包,支持 Linux x86_64/aarch64。 -依赖的 URL、SHA-256 和编译隔离由 [Inferlet 构建模块](../cmake_ext/engines/Inferlet.cmake) +依赖的版本、URL 和编译隔离由 [Inferlet 构建模块](../cmake_ext/engines/Inferlet.cmake) 维护。首次配置自动下载;离线环境可预置同一发布归档到 `3rdparty/inferlet/v0.1.1/x64/inferlet-x64.tar.gz` 或对应的 `arm/inferlet-arm.tar.gz`, -仍须通过同一 SHA 校验。该包包含自己的 llama.cpp/ggml,不能与 `ENABLE_LLAMACPP`、 +配置时直接复用已有归档,不校验内容 hash。该包包含自己的 llama.cpp/ggml,不能与 `ENABLE_LLAMACPP`、 `ENABLE_WHISPERCPP` 或 `ENABLE_KITELLM` 同时启用。 复用现有实体抽取 I/O、节点和 Model,新增样例只选择 Inferlet 及其运行配置: diff --git a/doc/dev_guide/source_layout.md b/doc/dev_guide/source_layout.md index 5820f5a4..8a5c1001 100644 --- a/doc/dev_guide/source_layout.md +++ b/doc/dev_guide/source_layout.md @@ -50,7 +50,7 @@ | 子目录 | 内容 | | :--- | :--- | | `engines/` | 推理运行时的依赖接入模块 | -| `third_party/` | JSON、PCRE2、stb_image、GoogleTest 等通用依赖,以及引擎和通用依赖共用的校验下载、缓存身份与归档发布工具 | +| `third_party/` | JSON、PCRE2、stb_image、GoogleTest 等通用依赖,以及引擎和通用依赖共用的暂存下载、缓存身份与归档发布工具 | | `support/` | 构建加速、Operator 壳选择和分层头文件视图 | | `toolchains/` | 在 `project()` 之前生效的编译器、sysroot 和搜索规则;当前提供中性的 GNU/Linux arm64 示例 | | `templates/` | SDK 导出符号脚本、Demo Profile 字段头等生成文件的输入模板 | @@ -73,7 +73,7 @@ CMake 链接方继续使用 `llm_edgeflow::sdk`,脚本和测试通过目标路 推理引擎的依赖接入统一放在 `engines/`,包括 ONNX Runtime、llama.cpp、Whisper、Inferlet 和 Kite。 目录按运行时职责划分,不按作者、仓库公开性或接口来源划分;各模块只维护固定版本、上游选项 -和编译链接探针。校验下载、发布包导入、缓存身份与归档发布由 +和编译链接探针。暂存下载、发布包导入、缓存身份与归档发布由 `third_party/ThirdPartyCacheMetadata.cmake` 共用。引擎模块把 Backend 的 SYSTEM 头目录、编译定义和 链接目标分别追加到 `LLM_EDGEFLOW_BACKEND_INCLUDE_DIRS`、`LLM_EDGEFLOW_BACKEND_COMPILE_DEFINITIONS`、 `LLM_EDGEFLOW_BACKEND_LIBRARIES`,由 `RuntimeGraph.cmake` 统一接入 Backend 实现;新增 Backend 不修改它。 diff --git a/doc/kitellm.md b/doc/kitellm.md index 694cf6b9..d41b9652 100644 --- a/doc/kitellm.md +++ b/doc/kitellm.md @@ -27,21 +27,21 @@ CI 使用仓库 Actions secret `KITELLM_GITHUB_TOKEN`,仅在配置下载步骤 轮换时在仓库 Settings → Secrets and variables → Actions 更新 `KITELLM_GITHUB_TOKEN`。新建凭据只需选择 kiteLLM 仓库并授予 Contents 读取权限。 -`kiteLLM Private Release & Real GGUF` 独立任务在 Linux x64 上下载校验发布包,构建 +`kiteLLM Private Release & Real GGUF` 独立任务在 Linux x64 上下载发布包,构建 kiteLLM + ONNX,确认 Catalog 注册并运行带 `kite` 标签的 CTest。随后通过 Demo 分别执行 真实文本、生成向量问答与视觉方案。缺少或失效的凭据会使任务明确失败,结果写入验收记录。 外部 fork PR 和 Dependabot 跳过此私有任务, 其余默认 CI 保持执行。跳过状态在验收记录中保留。私有库、头文件和链接产物不上传为 公开 artifact,也不进入 Actions cache。模型缓存包含公开 GGUF、ONNX 权重和 tokenizer。 -支持 Linux x86_64 与 aarch64。版本、平台包和 SHA-256 固定在 +支持 Linux x86_64 与 aarch64。版本和平台包固定在 [`cmake_ext/engines/KiteLlm.cmake`](../cmake_ext/engines/KiteLlm.cmake)。上游 v0.1.0 对应提交 -`5e58820f39919fce2046e2fd703c62601a5df59b`。修改发布包后即使 tag 不变,校验也会拒绝。 +`5e58820f39919fce2046e2fd703c62601a5df59b`。下载和复用缓存时不校验内容 hash。 -归档缓存位于 `3rdparty/kite_llm/v0.1.0//`,每次配置都校验归档 SHA-256, -再解包到当前 build 的 `_deps/kite_llm_release/`。已有完整缓存时不调用 gh、不访问网络。 -如果缓存损坏,工具会报错。删除报错指定的归档,再重新配置。 -工具会重新下载归档。Git 忽略这些第三方文件。 +归档缓存位于 `3rdparty/kite_llm/v0.1.0//`,每次配置解包到当前 build 的 +`_deps/kite_llm_release/`。已有归档时不调用 gh、不访问网络。 +如果解包或包导入失败,可删除对应归档再重新配置,工具会重新下载。 +Git 忽略这些第三方文件。 ## 使用与限制 diff --git a/tests/contract/architecture/llama_cache_checks.cmake b/tests/contract/architecture/llama_cache_checks.cmake index 03bace5c..2bdd4369 100644 --- a/tests/contract/architecture/llama_cache_checks.cmake +++ b/tests/contract/architecture/llama_cache_checks.cmake @@ -76,7 +76,7 @@ if(EXPECT_UNCACHEABLE AND LLM_EDGEFLOW_LLAMACPP_CACHEABLE) endif() get_target_property(whisper_imported whisper IMPORTED) if(EXPECT_IMPORTED AND NOT whisper_imported) - message(FATAL_ERROR "Expected verified Whisper cache import") + message(FATAL_ERROR "Expected matching Whisper cache import") elseif(EXPECT_UNCACHEABLE AND whisper_imported) message(FATAL_ERROR "Source GGML policy must also select source Whisper") endif() @@ -86,7 +86,7 @@ if(NOT LLM_EDGEFLOW_HAS_LLAMACPP) endif() get_target_property(imported llama IMPORTED) if(EXPECT_IMPORTED AND NOT imported) - message(FATAL_ERROR "Expected verified cache import") + message(FATAL_ERROR "Expected matching cache import") elseif(EXPECT_SOURCE AND imported) message(FATAL_ERROR "Incomplete/incompatible cache must select source") endif() @@ -114,7 +114,6 @@ if(NOT EXISTS "${cache}/.edgeflow-cache-fingerprint") endif() file(READ "${cache}/.edgeflow-cache-fingerprint" metadata) if(NOT metadata MATCHES "version=70adb1b4cea5ee39f867792c78dc59320921eda7" OR - NOT metadata MATCHES "source_sha256=94d215f1fd85ded40f4674eccdbd3caf4a9b0daa00b6d72255efec922c6d94a4" OR NOT metadata MATCHES "BUILD_SHARED_LIBS=OFF") message(FATAL_ERROR "Published llama cache must identify the current source pin and ABI") endif() diff --git a/tests/contract/architecture/test_third_party_cache_metadata.cmake b/tests/contract/architecture/test_third_party_cache_metadata.cmake index 30e60069..bab0ea9c 100644 --- a/tests/contract/architecture/test_third_party_cache_metadata.cmake +++ b/tests/contract/architecture/test_third_party_cache_metadata.cmake @@ -13,9 +13,10 @@ set(CMAKE_C_COMPILER_VERSION 13) set(CMAKE_CXX_COMPILER_ID GNU) set(CMAKE_CXX_COMPILER_VERSION 13) set(CMAKE_CXX_STANDARD 17) +set(cache_version pinned-source) function(probe kind) edgeflow_prepare_third_party_cache( - NAME neutral VERSION pinned-source SOURCE_SHA256 abc123 + NAME neutral VERSION "${cache_version}" CACHE_DIR "${TEST_ROOT}/cache" KIND "${kind}" MARKER_ROOT "${TEST_ROOT}/markers" ABI_OPTIONS ${ARGN} OUT_VALID valid OUT_MARKER marker) @@ -35,6 +36,12 @@ probe(STATIC a=1 z=2) if(NOT valid) message(FATAL_ERROR "Matching marker should be reusable regardless of option order") endif() +set(cache_version next-version) +probe(STATIC a=1 z=2) +if(valid) + message(FATAL_ERROR "Changed dependency version reused stale cache") +endif() +set(cache_version pinned-source) set(CMAKE_CXX_COMPILER_VERSION 14) probe(STATIC a=1 z=2) if(valid) @@ -48,7 +55,7 @@ endif() # 任一 ABI 输入变化时,身份目录保留原有缓存。 function(identity_probe) edgeflow_prepare_third_party_cache( - NAME identity VERSION pin SOURCE_SHA256 abc CACHE_DIR "${TEST_ROOT}/identities" + NAME identity VERSION pin CACHE_DIR "${TEST_ROOT}/identities" KIND STATIC MARKER_ROOT "${TEST_ROOT}/markers" OUT_VALID identity_valid OUT_MARKER identity_marker OUT_CACHE_DIR identity_dir) set(identity_valid "${identity_valid}" PARENT_SCOPE) @@ -140,7 +147,7 @@ endif() # 标记匹配也不能让不完整的缓存变得可导入。 function(required_probe) edgeflow_prepare_third_party_cache( - NAME required VERSION pin SOURCE_SHA256 abc CACHE_DIR "${TEST_ROOT}/required" + NAME required VERSION pin CACHE_DIR "${TEST_ROOT}/required" KIND HEADER_ONLY MARKER_ROOT "${TEST_ROOT}/markers" REQUIRED_FILES include/required.h OUT_VALID required_valid OUT_MARKER required_marker) set(required_valid "${required_valid}" PARENT_SCOPE) @@ -158,20 +165,23 @@ if(NOT required_valid) message(FATAL_ERROR "Complete cache with a matching marker must be reusable") endif() -# 经校验的下载只发布完整文件,并重新校验已缓存的字节。 -file(WRITE "${TEST_ROOT}/source/payload.txt" "verified payload\n") -file(SHA256 "${TEST_ROOT}/source/payload.txt" payload_sha) +# 下载成功后才发布文件,已有缓存不需要访问来源。 +file(WRITE "${TEST_ROOT}/source/payload.txt" "download payload\n") set(url_download "${TEST_ROOT}/downloads/url/payload.txt") -edgeflow_download_verified_file("${url_download}" "${payload_sha}" +edgeflow_download_file("${url_download}" URL "file://${TEST_ROOT}/source/payload.txt") set(command_download "${TEST_ROOT}/downloads/command/payload.txt") -edgeflow_download_verified_file("${command_download}" "${payload_sha}" +edgeflow_download_file("${command_download}" COMMAND "${CMAKE_COMMAND}" -E copy "${TEST_ROOT}/source/payload.txt" "${TEST_ROOT}/downloads/command/staged.txt" DOWNLOADED "${TEST_ROOT}/downloads/command/staged.txt") foreach(download IN ITEMS "${url_download}" "${command_download}") if(NOT EXISTS "${download}") - message(FATAL_ERROR "Verified download was not published: ${download}") + message(FATAL_ERROR "Successful download was not published: ${download}") + endif() + file(READ "${download}" content) + if(NOT content STREQUAL "download payload\n") + message(FATAL_ERROR "Published download does not match its source: ${download}") endif() endforeach() function(expect_download_failure name script diagnostic) @@ -183,16 +193,30 @@ function(expect_download_failure name script diagnostic) message(FATAL_ERROR "${name} must fail with '${diagnostic}':\n${output}${error}") endif() endfunction() -expect_download_failure(wrong-hash - "edgeflow_download_verified_file(\"${TEST_ROOT}/downloads/wrong/payload.txt\" 0000 URL \"file://${TEST_ROOT}/source/payload.txt\")" - "Downloaded SHA-256 mismatch") -if(EXISTS "${TEST_ROOT}/downloads/wrong/payload.txt") - message(FATAL_ERROR "Unverified download must not become a cached file") -endif() -file(WRITE "${url_download}" "tampered\n") -expect_download_failure(tampered-cache - "edgeflow_download_verified_file(\"${url_download}\" ${payload_sha} URL \"file://${TEST_ROOT}/source/payload.txt\")" - "Cached SHA-256 mismatch") +expect_download_failure(missing-url + "edgeflow_download_file(\"${TEST_ROOT}/downloads/missing/payload.txt\" URL \"file://${TEST_ROOT}/source/missing.txt\")" + "Cannot download") +set(retry_download "${TEST_ROOT}/downloads/retry/payload.txt") +file(WRITE "${retry_download}.download" "interrupted download\n") +expect_download_failure(failed-command + "edgeflow_download_file(\"${retry_download}\" COMMAND \"${CMAKE_COMMAND}\" -E false FAILURE_MESSAGE \"Download command failed\")" + "Download command failed") +if(EXISTS "${TEST_ROOT}/downloads/missing/payload.txt" OR EXISTS "${retry_download}") + message(FATAL_ERROR "Failed download must not become a cached file") +endif() +edgeflow_download_file("${retry_download}" URL "file://${TEST_ROOT}/source/payload.txt") +file(READ "${retry_download}" content) +if(NOT content STREQUAL "download payload\n" OR EXISTS "${retry_download}.download") + message(FATAL_ERROR "Retry must publish the complete download and consume the staging file") +endif() +file(REMOVE "${TEST_ROOT}/source/payload.txt") +file(WRITE "${url_download}" "local cached payload\n") +edgeflow_download_file("${url_download}" URL "file://${TEST_ROOT}/source/payload.txt") +edgeflow_download_file("${command_download}" COMMAND "${CMAKE_COMMAND}" -E false) +file(READ "${url_download}" content) +if(NOT content STREQUAL "local cached payload\n") + message(FATAL_ERROR "Existing download must be reused without content verification") +endif() file(WRITE "${TEST_ROOT}/invalid.cmake" "include(\"${PROJECT_SOURCE_DIR}/cmake_ext/third_party/ThirdPartyCacheMetadata.cmake\")\nedgeflow_prepare_third_party_cache(NAME invalid KIND UNKNOWN)\n")