mirror of
https://github.com/Helldez/BigMoeOnEdge.git
synced 2026-10-19 15:33:46 +00:00
Some checks failed
ci / changes (push) Has been cancelled
ci / host-windows (push) Has been cancelled
ci / version (push) Has been cancelled
ci / host-macos (push) Has been cancelled
ci / ios-harness (push) Has been cancelled
ci / android-apk (push) Has been cancelled
ci / format (push) Has been cancelled
ci / host-linux (push) Has been cancelled
ci / ui (push) Has been cancelled
A second front-end next to bmoe-cli. bmoe-server keeps one model loaded in one Session and serves a browser UI on 127.0.0.1:8765: chat with the reasoning channel, live engine metrics per token, model management and every engine setting. The settings form is rendered from the parameter table alone (GET /api/params), so a knob added to the engine appears in the UI with no UI change, and the UI names no engine parameter. The same server speaks an OpenAI-compatible /v1/chat/completions, streaming, so any OpenAI client can drive the engine. The API is in docs/server-api.md. Layout. server/ holds HTTP, files and the platform, none of it in core/: conversation, settings_store, event_bus and json_util are pure policy (bmoe_server_core, unit-tested without a model); engine_host owns the one Session; model_library the models folder, the catalog and resumable downloads; planner_adapter offers /api/plan; platform.cpp is the only file with platform conditionals. ui/ is Svelte and TypeScript. The model catalog is data, catalog/models.json. Behaviour. Settings live in layers (defaults, plan, launch flags, user) and only the user's are saved. A chat continues the engine's KV cache when the request extends the previous conversation by one message. With the planner built in every load plans first, with the previous model unloaded so the probes measure the machine and not the model in it. Security model, stated in docs/server-api.md. The server binds to loopback unless --host says otherwise and answers only to loopback host names. A request to /api/* or /v1/* from another site is refused on every method, whether it carries a foreign Origin or is marked Sec-Fetch-Site: cross-site. Loads, unloads and plan measurements are serialised, request bodies are capped at 8 MiB, and a parameter that makes the engine write a file is refused over the API. It does not protect against other local processes or users, and --host on a network has neither authentication nor TLS. Build and release. BMOE_BUILD_SERVER (on by default, off for Android) builds bmoe-server and stages ui/dist and the catalog beside it. CI formats server/ and compile-checks the server on macOS; release-host builds the UI and attaches a bmoe-desktop archive per host target next to bmoe-cli. Tool calling and conversations. /v1/chat/completions takes tools, tool_choice and tool messages and returns tool_calls, streamed or not; the model's own chat template writes the tools and llama.cpp's chat layer parses the calls back, so the server knows no tool syntax. The conversation is the client's: every request carries the whole history and the engine reuses the leading tokens its KV cache holds. Requests that arrive while one runs wait their turn. A coding agent (opencode) fixes a file through it. Hardening. Every POST, PUT and DELETE to /api/* or /v1/* must declare Content-Type: application/json, checked ahead of routing, and the chat view drops form controls and styles from model output: together they stop markup in an answer from posting to the API from the UI's own origin. max_tokens is clamped to the n-predict bound. CI runs the UI's unit tests. The plan's arguments reach /api/plan as the list the plan renders from the parameter table, not as text parsed back. A split model's storage is probed where its experts are.
105 lines
5.7 KiB
CMake
105 lines
5.7 KiB
CMake
# BigMoeOnEdge — top-level build.
|
|
#
|
|
# The engine (`bmoe_core`) depends only on its own public headers under include/bmoe
|
|
# and on llama.cpp's PUBLIC C API (llama.h / ggml.h). The serial streaming path never
|
|
# touches llama.cpp internals: MoE expert streaming is driven entirely through the public
|
|
# eval-callback and public gguf accessors, so it tracks upstream with nothing more than a
|
|
# submodule pointer bump — no in-tree patch. The one exception is the optional `--overlap`
|
|
# feature, which needs a per-expert wait point the public API does not expose: the submodule
|
|
# pins a fork branch (`bmoe/expert-ready-hook` on Helldez/llama.cpp) adding one ~25-line
|
|
# readiness hook, detected here as BMOE_HAVE_EXPERT_READY_HOOK. It is zero-cost when absent,
|
|
# the serial path still builds against stock upstream, and it sunsets the moment upstream
|
|
# ships an equivalent callback. See docs/seam.md § 3.
|
|
#
|
|
# llama.cpp is an optional dependency at configure time: when the submodule is absent
|
|
# only the pure-policy code (config validation) compiles, so the scaffold and a subset
|
|
# of CI stay green before the native dependency is fetched.
|
|
cmake_minimum_required(VERSION 3.21)
|
|
|
|
# The version is declared here and nowhere else in the build: the engine reports it (bmoe-cli
|
|
# --version, and the run-parameter preamble of every metrics CSV), so a committed benchmark file
|
|
# says which engine produced it. Keep it in step with CHANGELOG.md and the app's versionName:
|
|
# scripts/check-version.sh compares the three, in CI and against the tag before a release builds.
|
|
project(bigmoeonedge VERSION 0.28.0 LANGUAGES CXX)
|
|
|
|
set(CMAKE_CXX_STANDARD 17)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
|
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
|
|
endif()
|
|
|
|
# Everything runnable lands beside the libraries it needs. llama.cpp sets this for its own targets
|
|
# but only inside its subdirectory scope, so ours were landing elsewhere - which broke two things
|
|
# that look unrelated and are not. Running a test needed the library directory on PATH by hand; and
|
|
# `ggml_backend_load_all()` looks for dynamically loadable backends NEXT TO THE EXECUTABLE, so a
|
|
# GGML_BACKEND_DL build would have found none of them and reported a machine with no accelerator.
|
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
|
|
|
option(BMOE_BUILD_TESTS "Build byte-identity gates and unit tests" ON)
|
|
option(BMOE_BUILD_CLI "Build the bmoe-cli host tool" ON)
|
|
option(BMOE_BUILD_SERVER "Build bmoe-server, the engine behind the local web UI" ON)
|
|
# Off by default: diagnostics, not part of the product. They need no llama.cpp, so they can be
|
|
# configured and cross-compiled on their own without building the engine.
|
|
option(BMOE_BUILD_TOOLS "Build standalone diagnostic tools (bmoe-iobench)" OFF)
|
|
|
|
# --- Optional native dependency: llama.cpp ----------------------------------------
|
|
# The streaming seam uses only the public C API (llama.h). We additionally link llama.cpp's
|
|
# `common` layer for one thing: chat-template rendering + reasoning parsing (common_chat_*),
|
|
# so prompt formatting and thinking-extraction are template-driven per model instead of
|
|
# hardcoded. Unlike the streaming seam, `common` is NOT a stable API — it can change between
|
|
# upstream versions, so a submodule bump may need the chat glue in runtime.cpp updated (the
|
|
# gates catch it at build time). That trade-off is deliberate; see docs/seam.md.
|
|
set(BMOE_HAVE_LLAMA OFF)
|
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/llama.cpp/CMakeLists.txt")
|
|
set(BMOE_HAVE_LLAMA ON)
|
|
# Upstream examples/tests/server stay off; build the common utils we link for chat.
|
|
set(LLAMA_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|
set(LLAMA_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
|
set(LLAMA_BUILD_SERVER OFF CACHE BOOL "" FORCE)
|
|
set(LLAMA_BUILD_COMMON ON CACHE BOOL "" FORCE)
|
|
set(LLAMA_CURL OFF CACHE BOOL "" FORCE) # common's model-download path unused
|
|
# The prefill-device gate (G16) needs a second device to move weights onto, and the one every
|
|
# host has is a loopback rpc-server fronting the CPU. A test fixture, not a feature: host test
|
|
# builds only (releases build without tests), no front-end accepts an RPC endpoint, and it is
|
|
# not forced: -DGGML_RPC=OFF still wins, and G16 then reports itself skipped.
|
|
if(BMOE_BUILD_TESTS AND NOT ANDROID)
|
|
set(GGML_RPC ON CACHE BOOL "ggml: use RPC")
|
|
endif()
|
|
# Apple: Metal is in every build. It is what gives the planner a device to measure and
|
|
# `--prefill-device` one to run on; without it the binary reports a machine with no accelerator,
|
|
# which on this hardware is never true. Carrying it costs a host-only run nothing, because a
|
|
# device with nothing placed on it is not handed to llama.cpp (RunConfig::device_use). Said here
|
|
# rather than left to llama.cpp's own default so that it is this project's decision; not forced,
|
|
# so -DGGML_METAL=OFF still wins.
|
|
if(APPLE)
|
|
set(GGML_METAL ON CACHE BOOL "ggml: use Metal")
|
|
endif()
|
|
add_subdirectory(third_party/llama.cpp EXCLUDE_FROM_ALL)
|
|
endif()
|
|
|
|
add_subdirectory(core)
|
|
|
|
# An app is the only thing that runs on iOS, so there the CLI is built as one: the same main.cpp,
|
|
# inside the harness's bundle, and no command-line binary.
|
|
if(BMOE_HAVE_LLAMA AND IOS)
|
|
add_subdirectory(examples/ios)
|
|
elseif(BMOE_BUILD_CLI AND BMOE_HAVE_LLAMA)
|
|
add_subdirectory(cli)
|
|
endif()
|
|
|
|
if(BMOE_BUILD_SERVER AND BMOE_HAVE_LLAMA)
|
|
add_subdirectory(server)
|
|
endif()
|
|
|
|
if(BMOE_BUILD_TESTS AND BMOE_HAVE_LLAMA)
|
|
enable_testing()
|
|
add_subdirectory(tests)
|
|
endif()
|
|
|
|
if(BMOE_BUILD_TOOLS)
|
|
add_subdirectory(tools)
|
|
endif()
|
|
|
|
message(STATUS "BigMoeOnEdge: llama.cpp present = ${BMOE_HAVE_LLAMA}")
|