Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions .github/workflows/npm-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ on:
required: true
default: false
type: boolean
npu_sdk_run_id:
description: Optional run containing npu-release-sdks (AMD requires explicit provider configuration)
required: false
default: ""
type: string

permissions:
contents: read
actions: read

concurrency:
group: npm-release-${{ inputs.version }}
Expand Down Expand Up @@ -83,6 +89,17 @@ jobs:
- name: Bootstrap pinned native dependencies
shell: bash
run: python tools/bootstrap_dependencies.py --cache-dir .cache/dependencies
- name: Download distributable NPU SDKs
if: matrix.id == 'linux-x64' && inputs.npu_sdk_run_id != ''
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: npu-release-sdks
path: dist/npu-sdk
run-id: ${{ inputs.npu_sdk_run_id }}
github-token: ${{ github.token }}
- name: Verify NPU SDK inventory and runtime identities
if: matrix.id == 'linux-x64' && inputs.npu_sdk_run_id != ''
run: python tools/npu/prepare_release.py --sdk-root dist/npu-sdk --github-env "$GITHUB_ENV"
- name: Fetch the pinned PDF renderer for npm bundling (Unix)
if: runner.os != 'Windows'
shell: bash
Expand Down Expand Up @@ -145,6 +162,8 @@ jobs:
-DLIGHT_OCR_DEPENDENCY_CACHE_DIR="$PWD/.cache/dependencies"
-DLIGHT_OCR_ONNXRUNTIME_FLAVOR=${{ matrix.runtime_flavor }}
-DLIGHT_OCR_WEBGPU_SDK_DIR="$PWD/dist/webgpu-sdk/${{ matrix.id }}"
-DLIGHT_OCR_OPENVINO_SDK_DIR="$OPENVINO_SDK_DIR"
-DLIGHT_OCR_AMDNPU_SDK_DIR="$AMDNPU_SDK_DIR"
-DLIGHT_OCR_BUILD_NODE=ON
-DLIGHT_OCR_BUILD_TESTS=OFF
-DLIGHT_OCR_BUILD_TOOLS=OFF
Expand Down Expand Up @@ -176,17 +195,24 @@ jobs:
--model-free
- name: Stage native package input
shell: bash
run: >-
python tools/npm_release.py stage-native
--platform-id "${{ matrix.id }}"
--build-dir build-npm
--configuration Release
--metadata-dir "reports/npm/${{ matrix.id }}"
--pdfium-dir .cache/pdfium-package/node_modules/pdfium-native
--output-dir "dist/native-input/${{ matrix.id }}"
--runtime-flavor "${{ matrix.runtime_flavor }}"
--webgpu-artifact-manifest
"dist/webgpu-sdk/${{ matrix.id }}/artifact-manifest.json"
run: |
npu_arguments=()
if [[ -n "${OPENVINO_SDK_DIR:-}" ]]; then
npu_arguments+=(--openvino-sdk-dir "$OPENVINO_SDK_DIR")
fi
if [[ -n "${AMDNPU_SDK_DIR:-}" ]]; then
npu_arguments+=(--amdnpu-sdk-dir "$AMDNPU_SDK_DIR")
fi
python tools/npm_release.py stage-native \
--platform-id "${{ matrix.id }}" \
--build-dir build-npm \
--configuration Release \
--metadata-dir "reports/npm/${{ matrix.id }}" \
--pdfium-dir .cache/pdfium-package/node_modules/pdfium-native \
--output-dir "dist/native-input/${{ matrix.id }}" \
--runtime-flavor "${{ matrix.runtime_flavor }}" \
--webgpu-artifact-manifest "dist/webgpu-sdk/${{ matrix.id }}/artifact-manifest.json" \
"${npu_arguments[@]}"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: native-${{ matrix.id }}
Expand Down
99 changes: 99 additions & 0 deletions .github/workflows/npu-native.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: NPU native candidates

on:
workflow_dispatch:
inputs:
amd_sdk_run_id:
description: Optional run containing amdnpu-candidate-sdk imported from Ryzen AI 1.8
required: false
default: ""
type: string
reviewed_reports_run_id:
description: Optional device reports for metadata; not required to ship NPU support
required: false
default: ""
type: string

permissions:
contents: read
actions: read

jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: "22"
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.11"
- name: Assemble pinned Intel native SDK
run: python tools/npu/build_openvino.py --package-cache .cache/npu --output-dir dist/npu-sdk/openvino
- name: Import acquired AMD candidate
if: inputs.amd_sdk_run_id != ''
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: amdnpu-candidate-sdk
path: dist/npu-sdk/amdnpu
run-id: ${{ inputs.amd_sdk_run_id }}
github-token: ${{ github.token }}
- name: Build the native addon with packaged NPU SDKs
shell: bash
run: |
python tools/bootstrap_dependencies.py --cache-dir .cache/dependencies
node_version="$(node -p process.versions.node)"
npx --yes node-gyp@11.4.2 install "$node_version" --devdir "$PWD/.cache/node-gyp"
amd_arguments=()
if [[ -d dist/npu-sdk/amdnpu ]]; then
amd_arguments+=(-DLIGHT_OCR_AMDNPU_SDK_DIR="$PWD/dist/npu-sdk/amdnpu")
fi
cmake -S . -B build-npu -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DLIGHT_OCR_DEPENDENCY_CACHE_DIR="$PWD/.cache/dependencies" \
-DLIGHT_OCR_OPENVINO_SDK_DIR="$PWD/dist/npu-sdk/openvino" \
-DLIGHT_OCR_BUILD_NODE=ON -DLIGHT_OCR_BUILD_TESTS=OFF -DLIGHT_OCR_BUILD_TOOLS=OFF \
-DLIGHT_OCR_NODE_INCLUDE_DIR="$PWD/.cache/node-gyp/$node_version/include/node" \
-DLIGHT_OCR_NODE_EXECUTABLE="$(command -v node)" "${amd_arguments[@]}"
cmake --build build-npu --parallel 2
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: npu-candidates
path: |
dist/npu-sdk/
build-npu/node-runtime/
if-no-files-found: error
- name: Fetch reviewed device reports
if: inputs.reviewed_reports_run_id != ''
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: npu-reviewed-reports
path: reports/npu-reviewed
run-id: ${{ inputs.reviewed_reports_run_id }}
github-token: ${{ github.token }}
- name: Prepare distributable NPU SDKs
shell: bash
run: |
for provider in openvino amdnpu; do
[[ -d "dist/npu-sdk/$provider" ]] || continue
reports=()
for report in "reports/npu-reviewed/$provider/"*.json; do
[[ -f "$report" ]] || continue
reports+=(--report "$report")
done
if [[ ${#reports[@]} -gt 0 ]]; then
python tools/npu/accept_qualification.py \
--sdk-dir "dist/npu-sdk/$provider" "${reports[@]}" \
--qualification-id "$provider-linux-x64-reviewed-v1" \
--output-dir "dist/npu-release/$provider"
else
mkdir -p dist/npu-release
cp -R "dist/npu-sdk/$provider" "dist/npu-release/$provider"
fi
done
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: npu-release-sdks
path: dist/npu-release/
if-no-files-found: error
23 changes: 14 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,23 @@ This file records user-visible changes to `light-ocr`. Published artifact detail
- Added a source-level Intel NPU backend (`provider: "openvino"`) for Linux x64.
Builds configured with `LIGHT_OCR_OPENVINO_SDK_DIR` load the OpenVINO C
runtime at run time, run detection and 20-bucket recognition on the NPU, and
place `openvino` first in a qualification-only Auto policy
place `openvino` first in the package Auto policy
(`openvino → webgpu → cpu`); hosts without an NPU skip it with
`adapter_unavailable`. Published npm packages do not include OpenVINO yet, so
`adapter_unavailable`. Added a pinned native SDK extractor, package-local
runtime descriptor 2.1, numeric driver/compiler checks, and reviewed-evidence
release staging with optional device reports. Published npm packages do not include OpenVINO yet, so
`provider: "openvino"` returns `unsupported_capability` there. See
[Intel NPU 加速技术方案](docs/intel-npu-acceleration.md).
- Reserved the AMD NPU execution provider name (`provider: "amdnpu"`) across the
C++ enum, Node addon, TypeScript types, CLI flags, and the server
`EXECUTION_MODE`. No build ships an AMD NPU backend yet, so any runtime that
lists it fails fast with `provider_abi_mismatch` and explicit requests on
current packages return `unsupported_capability`; the contract keeps only the
default precision (`precision: "auto"`) until the INT8/BF16 route is chosen.
See [AMD NPU 加速技术方案(预研)](docs/amd-npu-acceleration.md).
- Added a source-level AMD NPU backend (`provider: "amdnpu"`) for Linux x64
glibc and Ryzen AI STX/KRK devices. It isolates the vendor ORT runtime,
loads 20 immutable BF16 recognition contexts, and runs detection on CPU.
Added model compilation and SDK import tools, descriptor validation,
license/SBOM staging, and candidate/release workflows. AMD is excluded
from Auto and enabled only through `execution.provider: "amdnpu"` (or
`--provider amdnpu`). NPU device reports are optional for release;
`deviceValidated` remains false. Current published packages
return `unsupported_capability`. See [AMD NPU 加速技术方案](docs/amd-npu-acceleration.md)
and [NPU SDK 构建与发布](docs/npu-runtime-release.md).

## [0.5.8] - 2026-09-24

Expand Down
34 changes: 32 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ set(LIGHT_OCR_OPENVINO_SDK_DIR "" CACHE PATH
"OpenVINO SDK root with include/openvino/c; enables the Intel NPU backend on Linux x64")
set(LIGHT_OCR_OPENVINO_C_LIBRARY "" CACHE FILEPATH
"OpenVINO C runtime loaded by direct C++ callers; defaults to <SDK>/lib/libopenvino_c.so")
set(LIGHT_OCR_AMDNPU_SDK_DIR "" CACHE PATH
"Verified Ryzen AI runtime SDK with precompiled BF16 recognition buckets (Linux x64 glibc)")

if(LIGHT_OCR_ENABLE_SANITIZERS AND LIGHT_OCR_ENABLE_THREAD_SANITIZER)
message(FATAL_ERROR "Address/Undefined sanitizers and ThreadSanitizer require separate builds")
Expand Down Expand Up @@ -143,6 +145,9 @@ if(LIGHT_OCR_HAS_WEBGPU)
endif()

set(LIGHT_OCR_HAS_OPENVINO OFF)
include(NpuRuntime)
set(LIGHT_OCR_OPENVINO_QUALIFICATION_BUILD ON)
set(LIGHT_OCR_OPENVINO_QUALIFICATION_ID "builtin-openvino-v1")
if(LIGHT_OCR_OPENVINO_SDK_DIR)
if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$"))
Expand All @@ -154,7 +159,19 @@ if(LIGHT_OCR_OPENVINO_SDK_DIR)
"LIGHT_OCR_OPENVINO_SDK_DIR has no include/openvino/c/openvino.h: ${LIGHT_OCR_OPENVINO_SDK_DIR}")
endif()
if(NOT LIGHT_OCR_OPENVINO_C_LIBRARY)
set(LIGHT_OCR_OPENVINO_C_LIBRARY "${LIGHT_OCR_OPENVINO_SDK_DIR}/lib/libopenvino_c.so")
if(EXISTS "${LIGHT_OCR_OPENVINO_SDK_DIR}/sdk-manifest.json")
light_ocr_validate_npu_sdk(openvino "${LIGHT_OCR_OPENVINO_SDK_DIR}")
set(LIGHT_OCR_OPENVINO_C_LIBRARY "${LIGHT_OCR_OPENVINO_VERIFIED_LIBRARY}")
elseif(LIGHT_OCR_BUILD_NODE)
message(FATAL_ERROR "Node NPU builds require a verified SDK manifest")
else()
set(LIGHT_OCR_OPENVINO_C_LIBRARY "${LIGHT_OCR_OPENVINO_SDK_DIR}/lib/libopenvino_c.so")
endif()
elseif(LIGHT_OCR_BUILD_NODE)
light_ocr_validate_npu_sdk(openvino "${LIGHT_OCR_OPENVINO_SDK_DIR}")
if(NOT LIGHT_OCR_OPENVINO_C_LIBRARY STREQUAL LIGHT_OCR_OPENVINO_VERIFIED_LIBRARY)
message(FATAL_ERROR "The OpenVINO runtime must match the verified SDK")
endif()
endif()
if(NOT EXISTS "${LIGHT_OCR_OPENVINO_C_LIBRARY}")
message(FATAL_ERROR "OpenVINO C runtime not found: ${LIGHT_OCR_OPENVINO_C_LIBRARY}")
Expand All @@ -166,10 +183,21 @@ if(LIGHT_OCR_OPENVINO_SDK_DIR)
target_include_directories(light_ocr_core SYSTEM PRIVATE "${_light_ocr_openvino_include}")
target_compile_definitions(light_ocr_core PRIVATE
LIGHT_OCR_HAS_OPENVINO=1
LIGHT_OCR_OPENVINO_QUALIFICATION_BUILD=$<BOOL:${LIGHT_OCR_OPENVINO_QUALIFICATION_BUILD}>
LIGHT_OCR_OPENVINO_DEFAULT_LIBRARY="${LIGHT_OCR_OPENVINO_C_LIBRARY}")
target_link_libraries(light_ocr_core PRIVATE ${CMAKE_DL_LIBS})
endif()

set(LIGHT_OCR_HAS_AMDNPU OFF)
if(LIGHT_OCR_AMDNPU_SDK_DIR)
light_ocr_validate_npu_sdk(amdnpu "${LIGHT_OCR_AMDNPU_SDK_DIR}")
set(LIGHT_OCR_HAS_AMDNPU ON)
target_sources(light_ocr_core PRIVATE src/inference/amdnpu/backend.cpp)
target_compile_definitions(light_ocr_core PRIVATE LIGHT_OCR_HAS_AMDNPU=1
LIGHT_OCR_AMDNPU_QUALIFICATION_BUILD=$<BOOL:${LIGHT_OCR_AMDNPU_QUALIFICATION_BUILD}>)
target_link_libraries(light_ocr_core PRIVATE ${CMAKE_DL_LIBS})
endif()

if(MSVC)
target_compile_options(light_ocr_core PRIVATE /W4 /WX /permissive- /EHsc /fp:strict)
else()
Expand Down Expand Up @@ -243,7 +271,9 @@ if(LIGHT_OCR_BUILD_TESTS)
LIGHT_OCR_WEBGPU_QUALIFICATION_BUILD=1)
endif()
if(LIGHT_OCR_HAS_OPENVINO)
target_compile_definitions(light_ocr_unit_tests PRIVATE LIGHT_OCR_HAS_OPENVINO=1)
target_compile_definitions(light_ocr_unit_tests PRIVATE
LIGHT_OCR_HAS_OPENVINO=1
LIGHT_OCR_OPENVINO_QUALIFICATION_BUILD=$<BOOL:${LIGHT_OCR_OPENVINO_QUALIFICATION_BUILD}>)
endif()
light_ocr_stage_onnxruntime(light_ocr_unit_tests)
add_test(NAME light_ocr_unit_tests COMMAND light_ocr_unit_tests)
Expand Down
19 changes: 18 additions & 1 deletion bindings/node/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ else()
set(_light_ocr_node_runtime_version "1.22.0")
set(_light_ocr_node_runtime_abi "onnxruntime-c-api-22")
endif()
if(LIGHT_OCR_HAS_WEBGPU AND LIGHT_OCR_WEBGPU_QUALIFICATION_BUILD)
if((LIGHT_OCR_HAS_WEBGPU AND LIGHT_OCR_WEBGPU_QUALIFICATION_BUILD) OR
(LIGHT_OCR_HAS_OPENVINO AND LIGHT_OCR_OPENVINO_QUALIFICATION_BUILD) OR
(LIGHT_OCR_HAS_AMDNPU AND LIGHT_OCR_AMDNPU_QUALIFICATION_BUILD))
set(_light_ocr_node_qualification_only 1)
set(_light_ocr_node_released 0)
else()
Expand All @@ -165,9 +167,13 @@ target_compile_definitions(light_ocr_node PRIVATE
LIGHT_OCR_NODE_CPU_QUALIFICATION_ID="cpu-baseline-v1"
LIGHT_OCR_NODE_APPLE_QUALIFICATION_ID="apple-open-macos-v1"
LIGHT_OCR_NODE_WEBGPU_QUALIFICATION_ID="${LIGHT_OCR_WEBGPU_QUALIFICATION_ID}"
LIGHT_OCR_NODE_OPENVINO_QUALIFICATION_ID="${LIGHT_OCR_OPENVINO_QUALIFICATION_ID}"
LIGHT_OCR_NODE_AMDNPU_QUALIFICATION_ID="${LIGHT_OCR_AMDNPU_QUALIFICATION_ID}"
LIGHT_OCR_NODE_QUALIFICATION_ONLY=${_light_ocr_node_qualification_only}
LIGHT_OCR_NODE_RELEASED=${_light_ocr_node_released}
LIGHT_OCR_NODE_HAS_APPLE=${_light_ocr_node_has_apple_provider}
LIGHT_OCR_NODE_HAS_OPENVINO=$<BOOL:${LIGHT_OCR_HAS_OPENVINO}>
LIGHT_OCR_NODE_HAS_AMDNPU=$<BOOL:${LIGHT_OCR_HAS_AMDNPU}>
LIGHT_OCR_NODE_HAS_WEBGPU=$<BOOL:${LIGHT_OCR_HAS_WEBGPU}>)
add_custom_command(TARGET light_ocr_node POST_BUILD
COMMAND "${CMAKE_COMMAND}"
Expand All @@ -185,6 +191,17 @@ add_custom_command(TARGET light_ocr_node POST_BUILD
-P "${PROJECT_SOURCE_DIR}/cmake/StageNodeRuntime.cmake"
VERBATIM)

foreach(_provider openvino amdnpu)
string(TOUPPER "${_provider}" _upper)
if(LIGHT_OCR_HAS_${_upper})
add_custom_command(TARGET light_ocr_node POST_BUILD
COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/tools/npu/sdk.py"
--sdk-dir "${LIGHT_OCR_${_upper}_SDK_DIR}"
--provider "${_provider}" --stage-dir "${_light_ocr_node_stage_dir}"
VERBATIM)
endif()
endforeach()

if(LIGHT_OCR_BUILD_TESTS)
if(NOT LIGHT_OCR_NODE_EXECUTABLE)
find_program(LIGHT_OCR_NODE_EXECUTABLE NAMES node)
Expand Down
Loading
Loading