mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-28 09:08:54 -04:00
* feat(llama-cpp): add slot prompt-cache save and restore API Add POST /v1/slots/save and POST /v1/slots/restore. They write one llama.cpp slot's prompt cache (KV state) to a file and load it back, so a client can warm a long system prompt in a few hundred milliseconds instead of re-running prefill. Refs #12685. The new SlotCache gRPC call posts the same slot save and restore tasks the llama-server /slots endpoint uses. It is off unless the model sets the new slot_save_path option, so existing models are not affected. The /slots monitoring endpoint is not needed here: the backend has no HTTP server. The file name comes from the caller, so it is checked twice, in Go and again in the backend. It must be one path component: no empty name, "/", "\", "..", NUL, control characters or absolute path. The resolved path, with symbolic links followed, must be a direct child of slot_save_path. The routes are admin only because they write files on the backend host. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(llama-cpp): answer 501 when a backend lacks slot cache A backend that does not implement the SlotCache rpc returns gRPC UNIMPLEMENTED. The API passed that text through, so the client saw "unknown method SlotCache". Map the status to a typed error in core/backend and return 501 with a fixed message instead. Every other error path is unchanged. Make the SlotCache comments in backend.proto backend-neutral: slot is the backend's state slot, filename must stay inside the save directory, and an empty model_identity means the backend does not check it. Only comments change, no field or name. Add tests that the identity sent is the configured model, and for the error mapping. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(llama-cpp): issue slot cache IDs on the server The slot save and restore API let the client pick the file name. Restore could load any file in slot_save_path, and the symlink check ran before llama.cpp opened the file. The backend now issues the name. A save returns a random 128-bit ID (32 lowercase hex characters, read from /dev/urandom) and never overwrites an existing file. Files live in a 0700 directory per model, named from the SHA-256 of the model identity, with a manifest.json that records each save. The manifest is written atomically and a corrupt one stops both save and restore. A restore takes the ID, finds it in the manifest, refuses an entry of another model, builds the path from the entry and checks with lstat that it is a plain file of the recorded size. The directory mode is the main defence against another local user, since llama.cpp still opens the path itself. Each model keeps at most 32 saves and the oldest is deleted first. A file over 8 GiB is deleted after the save, because llama.cpp does not report the size before it writes. Responses carry no host path. The HTTP bodies change: save takes {model, slot}, restore takes {model, slot, id}, and an unknown ID returns 404. In the proto, SlotCacheRequest.filename and SlotCacheResponse.filename become id. SlotCache no longer calls checkModelIdentity, which reads a field this request does not have. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(llama-cpp): copy slot_cache files in prepare.sh grpc-server.cpp includes slot_cache.h, but prepare.sh copies the helper headers into llama.cpp/tools/grpc-server/ one by one and skipped it. The llama-cpp, turboquant and bonsai builds share this step, so they all failed with "slot_cache.h: No such file or directory". Copy slot_cache.h and slot_cache_test.cpp, which CMakeLists.txt builds when LLAMA_GRPC_BUILD_TESTS is on. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * chore(deps): bump LocalAGI to pick up the scheduler start/stop fix LocalAGI's scheduler could hit a nil pointer in run when Start and Stop raced. That made the core/services/agentpool tests panic at random and turned tests-linux red. Move to mudler/LocalAGI@7aa0c8fea0, the merge of mudler/LocalAGI#519 ("make Start and Stop safe to call concurrently"). The new module version also adds a few indirect requirements to go.mod and go.sum. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
163 lines
7.8 KiB
CMake
163 lines
7.8 KiB
CMake
set(TARGET grpc-server)
|
|
set(CMAKE_CXX_STANDARD 17)
|
|
cmake_minimum_required(VERSION 3.15)
|
|
set(TARGET grpc-server)
|
|
set(_PROTOBUF_LIBPROTOBUF libprotobuf)
|
|
set(_REFLECTION grpc++_reflection)
|
|
|
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
|
# Set correct Homebrew install folder for Apple Silicon and Intel Macs
|
|
if (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64")
|
|
set(HOMEBREW_DEFAULT_PREFIX "/opt/homebrew")
|
|
else()
|
|
set(HOMEBREW_DEFAULT_PREFIX "/usr/local")
|
|
endif()
|
|
|
|
link_directories("${HOMEBREW_DEFAULT_PREFIX}/lib")
|
|
include_directories("${HOMEBREW_DEFAULT_PREFIX}/include")
|
|
endif()
|
|
|
|
find_package(absl CONFIG REQUIRED)
|
|
find_package(Protobuf CONFIG REQUIRED)
|
|
find_package(gRPC CONFIG REQUIRED)
|
|
|
|
find_program(_PROTOBUF_PROTOC protoc)
|
|
set(_GRPC_GRPCPP grpc++)
|
|
find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
|
|
|
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
|
include_directories(${Protobuf_INCLUDE_DIRS})
|
|
|
|
message(STATUS "Using protobuf version ${Protobuf_VERSION} | Protobuf_INCLUDE_DIRS: ${Protobuf_INCLUDE_DIRS} | CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
|
|
|
|
# Proto file
|
|
get_filename_component(hw_proto "../../../../../../backend/backend.proto" ABSOLUTE)
|
|
get_filename_component(hw_proto_path "${hw_proto}" PATH)
|
|
|
|
# Generated sources
|
|
set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/backend.pb.cc")
|
|
set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/backend.pb.h")
|
|
set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/backend.grpc.pb.cc")
|
|
set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/backend.grpc.pb.h")
|
|
|
|
add_custom_command(
|
|
OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}" "${hw_grpc_hdrs}"
|
|
COMMAND ${_PROTOBUF_PROTOC}
|
|
ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
|
|
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
|
|
-I "${hw_proto_path}"
|
|
--plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
|
|
"${hw_proto}"
|
|
DEPENDS "${hw_proto}")
|
|
|
|
# hw_grpc_proto: force STATIC. Under the CPU_ALL_VARIANTS build BUILD_SHARED_LIBS=ON
|
|
# (ggml/llama become shared), which would otherwise make this glue library a DSO. As a
|
|
# DSO it references the hidden-visibility symbols in the static libprotobuf.a, which the
|
|
# linker cannot satisfy ("hidden symbol ... in libprotobuf.a is referenced by DSO").
|
|
# Keeping it STATIC links protobuf/gRPC directly into the grpc-server executable while
|
|
# only ggml/llama stay shared. No effect on the static variants (already BUILD_SHARED_LIBS=OFF).
|
|
add_library(hw_grpc_proto STATIC
|
|
${hw_grpc_srcs}
|
|
${hw_grpc_hdrs}
|
|
${hw_proto_srcs}
|
|
${hw_proto_hdrs} )
|
|
|
|
add_executable(${TARGET} grpc-server.cpp json.hpp httplib.h)
|
|
|
|
target_include_directories(${TARGET} PRIVATE ../llava)
|
|
target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR})
|
|
|
|
# Upstream llama.cpp renamed the `common` helpers library to `llama-common`.
|
|
# Forks that branched before the rename (e.g. llama-cpp-turboquant) still
|
|
# expose it as `common`. Detect which one is present so the same CMakeLists
|
|
# drives both builds — otherwise an unresolved name silently degrades to a
|
|
# plain `-l` flag and the PUBLIC include dir (where common.h lives) is lost.
|
|
if (TARGET llama-common)
|
|
set(_LLAMA_COMMON_TARGET llama-common)
|
|
else()
|
|
set(_LLAMA_COMMON_TARGET common)
|
|
endif()
|
|
|
|
target_link_libraries(${TARGET} PRIVATE ${_LLAMA_COMMON_TARGET} llama mtmd ${CMAKE_THREAD_LIBS_INIT} absl::flags hw_grpc_proto
|
|
absl::flags_parse
|
|
gRPC::${_REFLECTION}
|
|
gRPC::${_GRPC_GRPCPP}
|
|
protobuf::${_PROTOBUF_LIBPROTOBUF})
|
|
# Match grpc-server.cpp's native-decision guard: older forks do not need
|
|
# strict decision image decoders and must not acquire new dependencies.
|
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../server/server-decision.cpp")
|
|
find_package(ZLIB REQUIRED)
|
|
find_package(JPEG REQUIRED)
|
|
target_link_libraries(${TARGET} PRIVATE ZLIB::ZLIB JPEG::JPEG)
|
|
endif()
|
|
target_compile_features(${TARGET} PRIVATE cxx_std_11)
|
|
if(TARGET BUILD_INFO)
|
|
add_dependencies(${TARGET} BUILD_INFO)
|
|
endif()
|
|
|
|
# Unit test for the message-content normalization helper (message_content.h).
|
|
# Off by default so the normal backend build is untouched; enable with
|
|
# -DLLAMA_GRPC_BUILD_TESTS=ON and run via ctest. It reuses llama.cpp's vendored
|
|
# <nlohmann/json.hpp> (propagated by the common helpers library) so it has no
|
|
# extra dependency beyond what the backend already builds against.
|
|
option(LLAMA_GRPC_BUILD_TESTS "Build grpc-server unit tests" OFF)
|
|
if(LLAMA_GRPC_BUILD_TESTS)
|
|
enable_testing()
|
|
add_executable(message_content_test message_content_test.cpp message_content.h)
|
|
target_include_directories(message_content_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_link_libraries(message_content_test PRIVATE ${_LLAMA_COMMON_TARGET})
|
|
target_compile_features(message_content_test PRIVATE cxx_std_17)
|
|
add_test(NAME message_content_test COMMAND message_content_test)
|
|
|
|
# Parent-death watcher test (parent_watch.h) — standard library only, but
|
|
# needs a threading runtime for std::thread.
|
|
find_package(Threads REQUIRED)
|
|
add_executable(parent_watch_test parent_watch_test.cpp parent_watch.h)
|
|
target_include_directories(parent_watch_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_link_libraries(parent_watch_test PRIVATE Threads::Threads)
|
|
target_compile_features(parent_watch_test PRIVATE cxx_std_17)
|
|
add_test(NAME parent_watch_test COMMAND parent_watch_test)
|
|
|
|
add_executable(passthrough_options_test passthrough_options_test.cpp passthrough_options.h)
|
|
target_include_directories(passthrough_options_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_compile_features(passthrough_options_test PRIVATE cxx_std_17)
|
|
add_test(NAME passthrough_options_test COMMAND passthrough_options_test)
|
|
|
|
add_executable(tts_request_options_test tts_request_options_test.cpp tts_request_options.h)
|
|
target_include_directories(tts_request_options_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_compile_features(tts_request_options_test PRIVATE cxx_std_17)
|
|
add_test(NAME tts_request_options_test COMMAND tts_request_options_test)
|
|
|
|
add_executable(thread_params_test thread_params_test.cpp thread_params.h)
|
|
target_include_directories(thread_params_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_compile_features(thread_params_test PRIVATE cxx_std_17)
|
|
add_test(NAME thread_params_test COMMAND thread_params_test)
|
|
|
|
add_executable(moe_cache_option_test moe_cache_option_test.cpp moe_cache_option.h)
|
|
target_include_directories(moe_cache_option_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_compile_features(moe_cache_option_test PRIVATE cxx_std_17)
|
|
add_test(NAME moe_cache_option_test COMMAND moe_cache_option_test)
|
|
|
|
add_executable(parallel_params_test parallel_params_test.cpp parallel_params.h)
|
|
target_include_directories(parallel_params_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_compile_features(parallel_params_test PRIVATE cxx_std_17)
|
|
add_test(NAME parallel_params_test COMMAND parallel_params_test)
|
|
|
|
add_executable(slot_cache_test slot_cache_test.cpp slot_cache.h)
|
|
target_include_directories(slot_cache_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_link_libraries(slot_cache_test PRIVATE ${_LLAMA_COMMON_TARGET}) # vendored nlohmann/json
|
|
target_compile_features(slot_cache_test PRIVATE cxx_std_17)
|
|
add_test(NAME slot_cache_test COMMAND slot_cache_test)
|
|
|
|
add_executable(model_load_error_test model_load_error_test.cpp model_load_error.h)
|
|
target_include_directories(model_load_error_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_compile_features(model_load_error_test PRIVATE cxx_std_17)
|
|
add_test(NAME model_load_error_test COMMAND model_load_error_test)
|
|
|
|
# Dead-stream tracker (standard library only).
|
|
add_executable(stream_peer_test stream_peer_test.cpp stream_peer.h)
|
|
target_include_directories(stream_peer_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_compile_features(stream_peer_test PRIVATE cxx_std_17)
|
|
add_test(NAME stream_peer_test COMMAND stream_peer_test)
|
|
endif()
|