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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ jobs:
- uses: actions/checkout@v4
- name: Copyright notice on every file
run: python3 tools/copyright.py --check
- name: Install build dependencies
run: sudo apt-get update && sudo apt-get install -y ninja-build libcurl4-openssl-dev libzstd-dev libssl-dev libdrm-dev
- name: Configure
run: cmake -B build -DENGINE_NPU=OFF -DENGINE_GPU=OFF
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build -j
run: cmake --build build --target onebit -j
- name: Test
run: ctest --test-dir build --output-on-failure --no-tests=ignore
run: ctest --test-dir build --output-on-failure
29 changes: 29 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2026 bong-water-water-bong
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# HRX + Vulkan (docs/hrx.md). Only needed with -DONEBIT_HRX=ON.
[submodule "third_party/hrx"]
path = third_party/hrx
url = https://github.com/ROCm/hrx.git
shallow = true
[submodule "third_party/hrx-system"]
path = third_party/hrx-system
url = https://github.com/ROCm/hrx-system.git
shallow = true
[submodule "third_party/llama.cpp"]
path = third_party/llama.cpp
url = https://github.com/bong-water-water-bong/llama.cpp.git
branch = 1bit-engine/hrx-vulkan
shallow = true
26 changes: 26 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,29 @@ endif()
enable_testing()

# Components are ported in the order listed in docs/PORTING.md.

# ── Step 1: Lemonade's server core, embedded ───────────────────────────────
# Vendored at third_party/lemonade (v11.9.0 plus the local deltas listed in
# its UPSTREAM.md, including the `onebit` backend).
set(BUILD_WEB_APP OFF CACHE BOOL "" FORCE)
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
add_subdirectory(third_party/lemonade EXCLUDE_FROM_ALL)

add_executable(onebit app/main.cpp)
set_target_properties(onebit PROPERTIES OUTPUT_NAME 1bit)
target_link_libraries(onebit PRIVATE lemonade-server-core)

# ── Step 2: HRX + Vulkan in one llama.cpp build (docs/hrx.md) ─────────────
# Off by default: it needs TheRock/ROCm and the three HRX submodules, and it
# only runs on AMD GPUs. `1bit lemonade` then uses this llama-server for both
# the llamacpp-hrx recipe (HRX20) and the llamacpp recipe's Vulkan backend.
option(ONEBIT_HRX "Build HRX2 + Vulkan llama-server and wire it into Lemonade" OFF)
if(ONEBIT_HRX)
include(cmake/hrx.cmake)
add_dependencies(onebit llama_hrx)
target_compile_definitions(onebit PRIVATE ONEBIT_HRX_SERVER="${ONEBIT_HRX_SERVER}")
add_test(NAME hrx_lemonade_e2e
COMMAND ${CMAKE_SOURCE_DIR}/tests/hrx_lemonade_e2e.sh $<TARGET_FILE:onebit> ${ONEBIT_HRX_SERVER})
endif()

add_test(NAME smoke_lemonade COMMAND ${CMAKE_SOURCE_DIR}/tests/smoke_lemonade.sh $<TARGET_FILE:onebit>)
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ with AMD Ryzen AI hardware behind it:
- the XDNA 2 NPU engine
- HRX and Vulkan on the Radeon iGPU, compiled together in one llama.cpp build
- Laya, which decides where each request runs
- every Hugging Face model architecture, kept current by a daily census

> **Status:** porting the working engine from
> [1bit-MONSTER](https://github.com/1bit-MONSTER/1bit-MONSTER) in four steps; see
> **Status:** steps 1–2 of 5 have landed: embedded Lemonade ([docs/lemonade.md](docs/lemonade.md))
> and HRX + Vulkan in one build ([docs/hrx.md](docs/hrx.md)). The working engine is being ported from
> [1bit-MONSTER](https://github.com/1bit-MONSTER/1bit-MONSTER) in five steps; see
> [docs/PORTING.md](docs/PORTING.md). This repository holds the verified code
> without the development history.

Expand Down
130 changes: 130 additions & 0 deletions app/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2026 bong-water-water-bong
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 1bit: the engine binary. Subcommands:
//
// 1bit lemonade [lemond options] Lemonade's server core, embedded in-process,
// with the engine's `onebit` backend.
//
// Ported from 1bit-MONSTER tools/onebin.cpp and tools/unified_server.cpp
// (run_embedded_lemonade). See docs/PORTING.md for what lands next.
#include <lemon/backends/onebit/onebit_server.h>
#include <lemon/cli_parser.h>
#include <lemon/config_file.h>
#include <lemon/logging_config.h>
#include <lemon/runtime_config.h>
#include <lemon/server.h>
#include <lemon/utils/path_utils.h>

#include <nlohmann/json.hpp>

#include <cstdio>
#include <cstring>
#include <memory>
#include <string>
#include <vector>

namespace {

constexpr const char* kVersion = "0.0.1";

void usage(FILE* out) {
std::fprintf(out,
"usage: 1bit <command> [options]\n"
"\n"
"commands:\n"
" lemonade [lemond options] run Lemonade's server with the engine behind it\n"
" version print the version\n"
" help show this help\n");
}

#ifdef ONEBIT_HRX_SERVER
// Points Lemonade at this build's llama-server (HRX2 + Vulkan in one binary,
// docs/hrx.md): the llamacpp-hrx recipe runs Q4NX on HRX20, and the llamacpp
// recipe runs GGUF on Vulkan0, the faster device for standard quants. The
// config arrives merged with Lemonade's defaults, so only default values
// ("builtin", "auto", unset) are replaced; anything else the user set wins.
void use_engine_hrx_build(nlohmann::json& config) {
auto set_default = [&](const char* section, const char* key, const char* default_value, const char* value) {
auto& sec = config[section];
if (!sec.is_object()) sec = nlohmann::json::object();
if (!sec.contains(key) || sec[key] == "" || sec[key] == default_value) sec[key] = value;
};
set_default("hrx", "hrx_bin", "builtin", ONEBIT_HRX_SERVER);
set_default("hrx", "device", "", "HRX20");
set_default("llamacpp", "vulkan_bin", "builtin", ONEBIT_HRX_SERVER);
set_default("llamacpp", "backend", "auto", "vulkan");
set_default("llamacpp", "device", "", "Vulkan0");
}
#endif

// Hands argv to Lemonade's own CLI and runs its server in this process. The
// engine's native models reach Lemonade through the `onebit` backend
// (third_party/lemonade/src/cpp/include/lemon/backends/onebit/onebit.h); they
// are injected here before the server is constructed, so ModelManager's
// dynamic discovery sees them on its first cache build.
int run_lemonade(int argc, char** argv) {
lemon::CLIParser parser;
parser.parse(argc, argv);
if (!parser.should_continue()) return parser.get_exit_code();
auto cli_config = parser.get_config();

lemon::utils::set_cache_dir(cli_config.cache_dir);
auto config_json = lemon::ConfigFile::load(cli_config.cache_dir);

#ifdef ONEBIT_HRX_SERVER
use_engine_hrx_build(config_json);
#endif

// Native NPU artifacts are registered by the NPU engine port (docs/PORTING.md
// step 3); until then the onebit backend lists none.
lemon::backends::onebit::set_onebit_models({});

if (cli_config.port != -1) config_json["port"] = cli_config.port;
if (!cli_config.host.empty()) config_json["host"] = cli_config.host;
auto config = std::make_shared<lemon::RuntimeConfig>(config_json);
lemon::RuntimeConfig::set_global(config.get());
lemon::configure_application_logging(config->log_level(), lemon::LoggingMode::direct_server);

lemon::Server server(config, cli_config.cache_dir, cli_config.config_dir);
server.run();
return 0;
}

} // namespace

int main(int argc, char** argv) {
if (argc < 2) {
usage(stderr);
return 2;
}
const std::string cmd = argv[1];
if (cmd == "lemonade") {
// Lemonade's parser sees "1bit" followed by the lemond options.
std::vector<char*> args{argv[0]};
for (int i = 2; i < argc; ++i) args.push_back(argv[i]);
return run_lemonade(static_cast<int>(args.size()), args.data());
}
if (cmd == "version" || cmd == "--version") {
std::printf("1bit %s\n", kVersion);
return 0;
}
if (cmd == "help" || cmd == "--help" || cmd == "-h") {
usage(stdout);
return 0;
}
std::fprintf(stderr, "1bit: unknown command '%s'\n\n", cmd.c_str());
usage(stderr);
return 2;
}
39 changes: 39 additions & 0 deletions cmake/hrx-config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2026 bong-water-water-bong
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Package config for the HRX prefix assembled by cmake/hrx_assemble_prefix.cmake.
# Provides the targets llama.cpp's ggml-hrx2 expects: hrx::hrx,
# loom::binding::c::loomc and loom::binding::c::target::amdgpu.
get_filename_component(PREFIX "${CMAKE_CURRENT_LIST_DIR}/../../" ABSOLUTE)
if(NOT TARGET hrx::hrx)
add_library(hrx::hrx SHARED IMPORTED)
set_target_properties(hrx::hrx PROPERTIES
IMPORTED_LOCATION "${PREFIX}/lib/libhrx.so"
INTERFACE_INCLUDE_DIRECTORIES "${PREFIX}/include")
endif()
if(NOT TARGET loom::binding::c::loomc)
add_library(loom::binding::c::loomc SHARED IMPORTED)
set_target_properties(loom::binding::c::loomc PROPERTIES
IMPORTED_LOCATION "${PREFIX}/lib/libloomc.so"
INTERFACE_INCLUDE_DIRECTORIES "${PREFIX}/include")
# The AMDGPU binding uses loomc internals (exported by patches/hrx/0002).
target_link_libraries(loom::binding::c::loomc INTERFACE "${PREFIX}/lib/libloomc_amdgpu.a")
endif()
if(NOT TARGET loom::binding::c::target::amdgpu)
add_library(loom::binding::c::target::amdgpu STATIC IMPORTED)
set_target_properties(loom::binding::c::target::amdgpu PROPERTIES
IMPORTED_LOCATION "${PREFIX}/lib/libloomc_amdgpu.a"
INTERFACE_INCLUDE_DIRECTORIES "${PREFIX}/include")
endif()
126 changes: 126 additions & 0 deletions cmake/hrx.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Copyright 2026 bong-water-water-bong
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# HRX + Vulkan in one llama.cpp build (docs/hrx.md). Three pinned sources,
# each cloned from its submodule into the build tree and patched there, so
# the submodules stay clean:
#
# hrx_runtime ROCm/hrx 0bc22fb + patches/hrx/0001..0003: libhrx, libloomc,
# the Loom AMDGPU binding, assembled into ${ONEBIT_HRX_PREFIX}
# loom_link ROCm/hrx-system 6743075f + patches/hrx/0002: the loom-link the
# fork's kernel catalog is linked with (the only version that
# still has --mode=selective; its artifacts are byte-identical
# to the working build's)
# llama_hrx bong-water-water-bong/llama.cpp 1bit-engine/hrx-vulkan with
# GGML_HRX2 and GGML_VULKAN: one llama-server exposing HRX20
# and Vulkan0
include(ExternalProject)
find_package(Git REQUIRED)

set(ONEBIT_HRX_TOOLCHAIN "/opt/rocm-therock" CACHE PATH "ROCm/TheRock root whose amdclang builds the HRX runtime")
set(ONEBIT_HRX_PREFIX "${CMAKE_BINARY_DIR}/hrx/prefix")
set(_hrx_patches "${CMAKE_SOURCE_DIR}/patches/hrx")

foreach(_sub hrx hrx-system llama.cpp)
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/third_party/${_sub}/.git")
message(FATAL_ERROR "ONEBIT_HRX needs third_party/${_sub}: git submodule update --init third_party/${_sub}")
endif()
endforeach()

# Clone a submodule into the build tree at its pinned commit, then apply patches.
function(_hrx_fetch_command out submodule)
set(_src "${CMAKE_SOURCE_DIR}/third_party/${submodule}")
execute_process(COMMAND ${GIT_EXECUTABLE} -C "${_src}" rev-parse HEAD
OUTPUT_VARIABLE _rev OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY)
set(_cmd ${CMAKE_COMMAND} -E rm -rf <SOURCE_DIR>
COMMAND ${GIT_EXECUTABLE} clone --quiet --shared --no-checkout "${_src}" <SOURCE_DIR>
COMMAND ${GIT_EXECUTABLE} -C <SOURCE_DIR> checkout --quiet ${_rev})
foreach(_patch ${ARGN})
list(APPEND _cmd COMMAND ${GIT_EXECUTABLE} -C <SOURCE_DIR> apply "${_hrx_patches}/${_patch}")
endforeach()
set(${out} ${_cmd} PARENT_SCOPE)
endfunction()

# The HRX build configuration of the working Strix Halo setup, verbatim.
set(_hrx_flags
-DCMAKE_C_COMPILER=${ONEBIT_HRX_TOOLCHAIN}/bin/amdclang
-DCMAKE_CXX_COMPILER=${ONEBIT_HRX_TOOLCHAIN}/bin/amdclang++
-DCMAKE_ASM_COMPILER=${ONEBIT_HRX_TOOLCHAIN}/bin/amdclang
-DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release
-DHRX_ENABLE_IPO=OFF -DHRX_INSTALL_TESTS=OFF
-DIREE_ALLOCATOR_SYSTEM=libc -DIREE_BUILD_BENCHMARKS=OFF -DIREE_BUILD_DOCS=OFF
-DIREE_BUILD_PYTHON_BINDINGS=OFF -DIREE_BUILD_SAMPLES=OFF -DIREE_BUILD_TESTS=OFF
-DIREE_DEPENDENCY_MODE=pinned -DIREE_DEV_MODE=OFF -DIREE_ENABLE_ASSERTIONS=OFF
-DIREE_ENABLE_LIBBACKTRACE=OFF -DIREE_ENABLE_LLD=OFF -DIREE_ENABLE_POSITION_INDEPENDENT_CODE=ON
-DIREE_ENABLE_RUNTIME_TRACING=OFF -DIREE_ENABLE_WERROR_FLAG=OFF
-DIREE_HAL_AMDGPU_DEVICE_BINARY_BUILD_MODE=prebuilt -DIREE_HAL_AMDGPU_LIBHSA_STATIC=OFF
"-DIREE_HAL_AMDGPU_TARGETS=gfx9-generic|gfx90a|gfx9-4-generic|gfx10-1-generic|gfx10-3-generic|gfx11-generic|gfx12-generic"
-DIREE_HAL_DRIVER_AMDGPU=ON -DIREE_HAL_DRIVER_DEFAULTS=OFF -DIREE_HAL_DRIVER_TASK=ON
-DIREE_HAL_DRIVER_LOCAL_TASK=ON -DIREE_HAL_DRIVER_VULKAN=OFF -DIREE_HAL_DRIVER_WEBGPU=OFF
-DIREE_HAL_EXECUTABLE_LOADER_DEFAULTS=ON -DIREE_VISIBILITY_HIDDEN=ON
-DLIBHRX_BUILD_CTS=OFF -DLIBHRX_BUILD_CUDA_BINDING=OFF -DLIBHRX_BUILD_HIP_BINDING=ON
-DLIBHRX_BUILD_PASSTHROUGH=ON
-DLOOM_BUILD=ON -DLOOM_EMIT_AMDGPU=OFF -DLOOM_EMIT_LLVMIR=OFF -DLOOM_EMIT_SPIRV=OFF -DLOOM_EMIT_WASM=OFF
-DLOOM_EXECUTE_DEFAULTS=ON -DLOOM_EXECUTE_IREE_HAL=ON -DLOOM_IMPORT_MLIR=OFF -DLOOM_IMPORT_TILELANG=OFF
-DLOOM_TARGET_AMDGPU=ON -DLOOM_TARGET_AMDGPU_TARGETS=loom_defaults -DLOOM_TARGET_ARCH_AMDGPU=OFF
-DLOOM_TARGET_ARCH_LLVMIR=OFF -DLOOM_TARGET_ARCH_SPIRV=OFF -DLOOM_TARGET_ARCH_WASM=OFF
-DLOOM_TARGET_ARCH_X86=OFF -DLOOM_TARGET_DEFAULTS=ON -DLOOM_TARGET_LLVMIR=ON -DLOOM_TARGET_SPIRV=ON
-DLOOM_TARGET_WASM=OFF -DLOOM_TARGET_X86=ON)

_hrx_fetch_command(_fetch_runtime hrx
0001-libhrx-skip-install-export.patch
0002-loomc-drop-export-restriction.patch
0003-amdgpu-tolerate-missing-pm4-emulation-probe.patch)
ExternalProject_Add(hrx_runtime
PREFIX ${CMAKE_BINARY_DIR}/hrx/runtime
DOWNLOAD_COMMAND ${_fetch_runtime}
LIST_SEPARATOR |
CMAKE_GENERATOR Ninja
CMAKE_ARGS ${_hrx_flags} -DLIBHRX_BUILD=ON
# Only what llama.cpp's ggml-hrx2 links; the full tree has unrelated targets.
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target
libhrx_src_libhrx_hrx loomc_shared loom_binding_c_target_amdgpu_amdgpu.objects
INSTALL_COMMAND ${CMAKE_COMMAND} -DSRC=<SOURCE_DIR> -DBIN=<BINARY_DIR> -DPREFIX=${ONEBIT_HRX_PREFIX}
-DAR=${CMAKE_AR} -DCONFIG_IN=${CMAKE_SOURCE_DIR}/cmake/hrx-config.cmake.in
-P ${CMAKE_SOURCE_DIR}/cmake/hrx_assemble_prefix.cmake
USES_TERMINAL_BUILD ON)

_hrx_fetch_command(_fetch_system hrx-system 0002-loomc-drop-export-restriction.patch)
ExternalProject_Add(loom_link
PREFIX ${CMAKE_BINARY_DIR}/hrx/loom-link
DOWNLOAD_COMMAND ${_fetch_system}
LIST_SEPARATOR |
CMAKE_GENERATOR Ninja
CMAKE_ARGS ${_hrx_flags} -DLIBHRX_BUILD=OFF
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target loom_tools_loom-link_loom-link
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy <BINARY_DIR>/loom/src/loom/tools/loom-link/loom-link
${ONEBIT_HRX_PREFIX}/bin/loom-link
USES_TERMINAL_BUILD ON)

set(ONEBIT_HRX_SERVER "${CMAKE_BINARY_DIR}/hrx/llama/bin/llama-server")
ExternalProject_Add(llama_hrx
SOURCE_DIR ${CMAKE_SOURCE_DIR}/third_party/llama.cpp
BINARY_DIR ${CMAKE_BINARY_DIR}/hrx/llama
DOWNLOAD_COMMAND ""
CMAKE_GENERATOR Ninja
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -Dhrx_DIR=${ONEBIT_HRX_PREFIX}/cmake/hrx
-DGGML_HRX2=ON -DGGML_VULKAN=ON -DGGML_CUDA=OFF -DGGML_HIP=OFF -DGGML_NATIVE=ON -DGGML_CPU=ON
-DLLAMA_BUILD_SERVER=ON -DLLAMA_CURL=OFF
-DGGML_HRX2_LOOM_LINK_EXECUTABLE=${ONEBIT_HRX_PREFIX}/bin/loom-link
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target llama-server llama-bench
INSTALL_COMMAND ""
BUILD_ALWAYS ON
DEPENDS hrx_runtime loom_link
USES_TERMINAL_BUILD ON)
Loading
Loading