Skip to content

Commit 3059e6a

Browse files
author
ONNX GenAI Assistant
committed
refactor: Migrate Python bindings from pybind11 to nanobind
This commit migrates the Python bindings from pybind11 to nanobind with intrusive reference counting for better memory management. Key Changes: - Updated CMakeLists.txt to use nanobind instead of pybind11 - Added intrusive_counter.cpp for nanobind's intrusive ref counting - Migrated all Python bindings in python.cpp to use nanobind API - Extracted wrapper classes to separate header files for modularity - Added test coverage for all public APIs Bug Fixes: - Fixed tokenizer.encode() to use pure C API (avoid lifetime issues) - Fixed NamedTensors.__setitem__ pointer dereference (was causing segfault) Technical Details: - Uses nb::intrusive_ptr for all Python-exposed C++ objects - Follows pure C API pattern to avoid issues with non-copyable classes - Intrusive reference counting eliminates shared_ptr overhead - Compatible with nanobind's memory model Files changed: - cmake/deps.txt: Added nanobind dependency - cmake/external/onnxruntime_external_deps.cmake: Fetch nanobind - src/python/CMakeLists.txt: Build system changes - src/python/python.cpp: Complete nanobind migration - src/python/intrusive_counter.cpp: Reference counting support - src/python/wrappers/*.h: Modular wrapper class headers - test/python/test_onnxruntime_genai_api_coverage.py: API coverage tests
1 parent 6903a36 commit 3059e6a

17 files changed

+1520
-396
lines changed

cmake/deps.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
#not affect built binaries.
1111
#
1212
# NOTE: You must run deps_update_and_upload.py and generate_cgmanifest.py when ready to test your changes in a CI.
13-
pybind11;https://github.com/pybind/pybind11/archive/refs/tags/v2.13.6.zip;f780292da9db273c8ef06ccf5fd4b623624143e9
13+
nanobind;https://github.com/wjakob/nanobind/archive/refs/tags/v2.9.2.zip;aeae2737eaac0013a806adb479a82e3b65ddab98
1414
googletest;https://github.com/google/googletest/archive/530d5c8c84abd2a46f38583ee817743c9b3a42b4.zip;5e3a61db2aa975cfd0f97ba92c818744e7fa7034
1515
microsoft_wil;https://github.com/microsoft/wil/archive/refs/tags/v1.0.230629.1.zip;e4a542a323c070376f7c2d1973d0f7ddbc1d2fa5
1616
directx_headers;https://github.com/microsoft/DirectX-Headers/archive/refs/tags/v1.613.1.zip;47653509a3371eabb156360f42faf582f314bf2e
1717
onnxruntime_extensions;https://github.com/microsoft/onnxruntime-extensions.git;68ee0397c002ef197ef5e9075c1c0f92b0db539a
1818

1919
# These two dependencies are for the optional constrained decoding feature (USE_GUIDANCE)
20-
llguidance;https://github.com/microsoft/llguidance.git;94fa39128ef184ffeda33845f6d333f332a34b4d
20+
llguidance;https://github.com/microsoft/llguidance.git;2d2f1de3c87e3289528affc346f734f7471216d9
2121
corrosion;https://github.com/corrosion-rs/corrosion.git;b1fab721655c5c4b1b08a083d3cd29f163af75d0

cmake/external/onnxruntime_external_deps.cmake

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,29 @@ endforeach()
2020
message("Loading Dependencies ...")
2121

2222
if(ENABLE_PYTHON)
23+
# Fetch robin_map dependency for nanobind (header-only library)
2324
FetchContent_Declare(
24-
pybind11_project
25-
URL ${DEP_URL_pybind11}
26-
URL_HASH SHA1=${DEP_SHA1_pybind11}
27-
FIND_PACKAGE_ARGS 2.6 NAMES pybind11
25+
robin_map
26+
GIT_REPOSITORY https://github.com/Tessil/robin-map.git
27+
GIT_TAG v1.4.0
28+
GIT_SHALLOW TRUE
2829
)
29-
onnxruntime_fetchcontent_makeavailable(pybind11_project)
30-
31-
if(TARGET pybind11::module)
32-
set(pybind11_lib pybind11::module)
33-
else()
34-
set(pybind11_dep pybind11::pybind11)
30+
FetchContent_GetProperties(robin_map)
31+
if(NOT robin_map_POPULATED)
32+
FetchContent_Populate(robin_map)
33+
endif()
34+
35+
FetchContent_Declare(
36+
nanobind
37+
URL ${DEP_URL_nanobind}
38+
URL_HASH SHA1=${DEP_SHA1_nanobind}
39+
)
40+
FetchContent_GetProperties(nanobind)
41+
if(NOT nanobind_POPULATED)
42+
FetchContent_Populate(nanobind)
43+
# Copy robin_map into nanobind's ext directory before adding subdirectory
44+
file(COPY ${robin_map_SOURCE_DIR}/ DESTINATION ${nanobind_SOURCE_DIR}/ext/robin_map)
45+
add_subdirectory(${nanobind_SOURCE_DIR} ${nanobind_BINARY_DIR})
3546
endif()
3647
endif()
3748

src/python/CMakeLists.txt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ file(GLOB python_srcs CONFIGURE_DEPENDS
44
"${CMAKE_CURRENT_SOURCE_DIR}/*.h"
55
"${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
66
)
7-
find_package(Python COMPONENTS Interpreter Development)
8-
pybind11_add_module(python ${python_srcs})
7+
8+
# Ensure intrusive_counter.cpp is included for nanobind intrusive reference counting
9+
list(APPEND python_srcs "${CMAKE_CURRENT_SOURCE_DIR}/intrusive_counter.cpp")
10+
11+
# Find nanobind and create Python module
12+
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
13+
nanobind_add_module(python STABLE_ABI NB_STATIC ${python_srcs})
914
target_include_directories(python PRIVATE ${ORT_HEADER_DIR})
1015
target_link_directories(python PRIVATE ${ORT_LIB_DIR})
1116
target_link_libraries(python PRIVATE onnxruntime-genai)
@@ -103,7 +108,7 @@ if(BUILD_WHEEL)
103108

104109
if(MANYLINUX)
105110
add_custom_target(PyPackageBuild
106-
COMMAND ${PYTHON_EXECUTABLE} -m pip wheel --no-deps .
111+
COMMAND ${Python_EXECUTABLE} -m pip wheel --no-deps .
107112
COMMAND ${CMAKE_COMMAND} -E remove ${WHEEL_TARGET_NAME}/onnxruntime_genai.cpython-*
108113
COMMAND auditwheel repair onnxruntime_genai*linux_x86_64.whl -w ${WHEEL_FILES_DIR} ${modified_exclude_list}
109114
WORKING_DIRECTORY "${WHEEL_FILES_DIR}"
@@ -112,7 +117,7 @@ if(BUILD_WHEEL)
112117
)
113118
else()
114119
add_custom_target(PyPackageBuild
115-
COMMAND ${PYTHON_EXECUTABLE} -m pip wheel --no-deps .
120+
COMMAND ${Python_EXECUTABLE} -m pip wheel --no-deps .
116121
WORKING_DIRECTORY "${WHEEL_FILES_DIR}"
117122
COMMENT "Building wheel on ${WHEEL_FILES_DIR}"
118123
EXCLUDE_FROM_ALL

src/python/intrusive_counter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
// This file must be compiled exactly once to provide the intrusive reference
5+
// counting implementation for nanobind
6+
#include <nanobind/intrusive/counter.inl>

0 commit comments

Comments
 (0)