mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-10-01 05:56:52 +02:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15eeceabcb | ||
|
|
9ae5bba0fe | ||
|
|
39be55c97e | ||
|
|
34af94cd9a | ||
|
|
666f8898a2 | ||
|
|
805984d676 | ||
|
|
9cd719af21 |
@@ -1439,7 +1439,9 @@ jobs:
|
||||
- name: xcodebuild for swift package
|
||||
id: xcodebuild
|
||||
run: |
|
||||
./build-xcframework.sh
|
||||
# note: only macos and ios-device due to long build time
|
||||
# ref: https://github.com/ggml-org/llama.cpp/pull/27252
|
||||
./build-xcframework.sh macos ios-device
|
||||
|
||||
- name: Build Xcode project
|
||||
run: xcodebuild -project examples/llama.swiftui/llama.swiftui.xcodeproj -scheme llama.swiftui -sdk iphoneos CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'generic/platform=iOS' FRAMEWORK_FOLDER_PATH=./build-ios build
|
||||
@@ -1660,6 +1662,16 @@ jobs:
|
||||
run: |
|
||||
tar -czvf release/llama-${{ steps.tag.outputs.name }}-ui.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./ui-dist .
|
||||
|
||||
- name: Create and push git tag
|
||||
run: |
|
||||
TAG="${{ steps.tag.outputs.name }}"
|
||||
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null 2>&1; then
|
||||
echo "Tag ${TAG} already exists, skipping creation"
|
||||
else
|
||||
git tag "${TAG}"
|
||||
git push origin "${TAG}"
|
||||
fi
|
||||
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: ggml-org/action-create-release@v1
|
||||
|
||||
@@ -2,6 +2,7 @@ You are a coding agent. Here are some very important rules that you must follow:
|
||||
|
||||
General:
|
||||
- Be very precise and concise when writing code, comments, explanations, etc.
|
||||
- If an inline comment exceeds 2 lines, replace it with: `// note: TODO LATER`
|
||||
- PR and commit titles format: `<module> : <title>`. Lookup recents for examples
|
||||
- Don't try to build or run the code unless you are explicitly asked to do so
|
||||
- Use the `gh` CLI tool when querying PRs, issues, or other GitHub resources
|
||||
|
||||
@@ -224,6 +224,9 @@ add_subdirectory(src)
|
||||
# utils, programs, examples and tests
|
||||
#
|
||||
|
||||
# mtmd needs this even when common is not built
|
||||
add_subdirectory(vendor/hash)
|
||||
|
||||
if (LLAMA_BUILD_COMMON)
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(vendor/cpp-httplib)
|
||||
|
||||
+209
-128
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# usage: ./build-xcframework.sh [BUILD ...] (default: all builds)
|
||||
# builds: ios-sim ios-device macos visionos visionos-sim tvos-sim tvos-device
|
||||
#
|
||||
# Options
|
||||
IOS_MIN_OS_VERSION=16.4
|
||||
MACOS_MIN_OS_VERSION=13.3
|
||||
@@ -19,6 +22,43 @@ GGML_METAL_EMBED_LIBRARY=ON
|
||||
GGML_BLAS_DEFAULT=ON
|
||||
GGML_OPENMP=OFF
|
||||
|
||||
# Max number of concurrent platform builds
|
||||
MAX_PARALLEL_BUILDS=1
|
||||
|
||||
# Split the available cores between the concurrent builds (min 1)
|
||||
JOBS_PER_BUILD=$(( $(sysctl -n hw.logicalcpu) / MAX_PARALLEL_BUILDS ))
|
||||
if [[ "$JOBS_PER_BUILD" -lt 1 ]]; then
|
||||
JOBS_PER_BUILD=1
|
||||
fi
|
||||
|
||||
# echo "build_fn build_dir release_dir platform is_simulator min_os" for a build name
|
||||
build_spec() {
|
||||
case "$1" in
|
||||
ios-sim) echo "build_ios_sim build-ios-sim Release-iphonesimulator ios true ${IOS_MIN_OS_VERSION}" ;;
|
||||
ios-device) echo "build_ios_device build-ios-device Release-iphoneos ios false ${IOS_MIN_OS_VERSION}" ;;
|
||||
macos) echo "build_macos build-macos Release macos false ${MACOS_MIN_OS_VERSION}" ;;
|
||||
visionos) echo "build_visionos build-visionos Release-xros visionos false ${VISIONOS_MIN_OS_VERSION}" ;;
|
||||
visionos-sim) echo "build_visionos_sim build-visionos-sim Release-xrsimulator visionos true ${VISIONOS_MIN_OS_VERSION}" ;;
|
||||
tvos-sim) echo "build_tvos_sim build-tvos-sim Release-appletvsimulator tvos true ${TVOS_MIN_OS_VERSION}" ;;
|
||||
tvos-device) echo "build_tvos_device build-tvos-device Release-appletvos tvos false ${TVOS_MIN_OS_VERSION}" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Default: build everything
|
||||
if [[ $# -eq 0 ]]; then
|
||||
BUILDS=(ios-sim ios-device macos visionos visionos-sim tvos-sim tvos-device)
|
||||
else
|
||||
BUILDS=("$@")
|
||||
fi
|
||||
for b in "${BUILDS[@]}"; do
|
||||
if ! build_spec "$b" >/dev/null; then
|
||||
echo "Error: unknown build '$b'" >&2
|
||||
echo "Valid builds: ios-sim ios-device macos visionos visionos-sim tvos-sim tvos-device" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
COMMON_C_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument -g"
|
||||
COMMON_CXX_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument -g"
|
||||
|
||||
@@ -401,148 +441,189 @@ combine_static_libraries() {
|
||||
rm -rf "${temp_dir}"
|
||||
}
|
||||
|
||||
echo "Building for iOS simulator..."
|
||||
cmake -B build-ios-sim -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_MIN_OS_VERSION} \
|
||||
-DIOS=ON \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
-DCMAKE_OSX_SYSROOT=iphonesimulator \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphonesimulator \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-ios-sim --config Release -j $(sysctl -n hw.logicalcpu) -- -quiet
|
||||
build_ios_sim() {
|
||||
echo "Building for iOS simulator..."
|
||||
cmake -B build-ios-sim -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_MIN_OS_VERSION} \
|
||||
-DIOS=ON \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
-DCMAKE_OSX_SYSROOT=iphonesimulator \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphonesimulator \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-ios-sim --config Release -j "${JOBS_PER_BUILD}" -- -quiet
|
||||
}
|
||||
|
||||
echo "Building for iOS devices..."
|
||||
cmake -B build-ios-device -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
-DCMAKE_OSX_SYSROOT=iphoneos \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64" \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphoneos \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-ios-device --config Release -j $(sysctl -n hw.logicalcpu) -- -quiet
|
||||
build_ios_device() {
|
||||
echo "Building for iOS devices..."
|
||||
cmake -B build-ios-device -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
-DCMAKE_OSX_SYSROOT=iphoneos \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64" \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphoneos \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-ios-device --config Release -j "${JOBS_PER_BUILD}" -- -quiet
|
||||
}
|
||||
|
||||
echo "Building for macOS..."
|
||||
cmake -B build-macos -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-S .
|
||||
cmake --build build-macos --config Release -j $(sysctl -n hw.logicalcpu) -- -quiet
|
||||
build_macos() {
|
||||
echo "Building for macOS..."
|
||||
cmake -B build-macos -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-S .
|
||||
cmake --build build-macos --config Release -j "${JOBS_PER_BUILD}" -- -quiet
|
||||
}
|
||||
|
||||
echo "Building for visionOS..."
|
||||
cmake -B build-visionos -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${VISIONOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64" \
|
||||
-DCMAKE_SYSTEM_NAME=visionOS \
|
||||
-DCMAKE_OSX_SYSROOT=xros \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=xros \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-visionos --config Release -j $(sysctl -n hw.logicalcpu) -- -quiet
|
||||
build_visionos() {
|
||||
echo "Building for visionOS..."
|
||||
cmake -B build-visionos -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${VISIONOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64" \
|
||||
-DCMAKE_SYSTEM_NAME=visionOS \
|
||||
-DCMAKE_OSX_SYSROOT=xros \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=xros \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-visionos --config Release -j "${JOBS_PER_BUILD}" -- -quiet
|
||||
}
|
||||
|
||||
echo "Building for visionOS simulator..."
|
||||
cmake -B build-visionos-sim -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${VISIONOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DCMAKE_SYSTEM_NAME=visionOS \
|
||||
-DCMAKE_OSX_SYSROOT=xrsimulator \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=xrsimulator \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-visionos-sim --config Release -j $(sysctl -n hw.logicalcpu) -- -quiet
|
||||
build_visionos_sim() {
|
||||
echo "Building for visionOS simulator..."
|
||||
cmake -B build-visionos-sim -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${VISIONOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DCMAKE_SYSTEM_NAME=visionOS \
|
||||
-DCMAKE_OSX_SYSROOT=xrsimulator \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=xrsimulator \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-visionos-sim --config Release -j "${JOBS_PER_BUILD}" -- -quiet
|
||||
}
|
||||
|
||||
# Add tvOS builds (might need the same u_int definitions as watchOS and visionOS)
|
||||
echo "Building for tvOS simulator..."
|
||||
cmake -B build-tvos-sim -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${TVOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||
-DCMAKE_OSX_SYSROOT=appletvsimulator \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DGGML_METAL=ON \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=appletvsimulator \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-tvos-sim --config Release -j $(sysctl -n hw.logicalcpu) -- -quiet
|
||||
build_tvos_sim() {
|
||||
echo "Building for tvOS simulator..."
|
||||
cmake -B build-tvos-sim -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${TVOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||
-DCMAKE_OSX_SYSROOT=appletvsimulator \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
||||
-DGGML_METAL=ON \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=appletvsimulator \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-tvos-sim --config Release -j "${JOBS_PER_BUILD}" -- -quiet
|
||||
}
|
||||
|
||||
echo "Building for tvOS devices..."
|
||||
cmake -B build-tvos-device -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${TVOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||
-DCMAKE_OSX_SYSROOT=appletvos \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64" \
|
||||
-DGGML_METAL=ON \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=appletvos \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-tvos-device --config Release -j $(sysctl -n hw.logicalcpu) -- -quiet
|
||||
build_tvos_device() {
|
||||
echo "Building for tvOS devices..."
|
||||
cmake -B build-tvos-device -G Xcode \
|
||||
"${COMMON_CMAKE_ARGS[@]}" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${TVOS_MIN_OS_VERSION} \
|
||||
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||
-DCMAKE_OSX_SYSROOT=appletvos \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64" \
|
||||
-DGGML_METAL=ON \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=appletvos \
|
||||
-DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DMTMD_VIDEO=OFF \
|
||||
-S .
|
||||
cmake --build build-tvos-device --config Release -j "${JOBS_PER_BUILD}" -- -quiet
|
||||
}
|
||||
|
||||
run_builds_parallel() {
|
||||
local -a pids=()
|
||||
local -a names=()
|
||||
local name i
|
||||
for name in "$@"; do
|
||||
# Wait for the oldest running build to free a slot
|
||||
if [[ "${#pids[@]}" -ge "$MAX_PARALLEL_BUILDS" ]]; then
|
||||
if ! wait "${pids[0]}"; then
|
||||
echo "ERROR: build '${names[0]}' failed, log follows (${names[0]}.log):" >&2
|
||||
kill "${pids[@]}" 2>/dev/null || true
|
||||
cat "${names[0]}.log" >&2
|
||||
exit 1
|
||||
fi
|
||||
pids=("${pids[@]:1}")
|
||||
names=("${names[@]:1}")
|
||||
fi
|
||||
echo "Starting build: $name (log: ${name}.log, -j ${JOBS_PER_BUILD})"
|
||||
"$name" > "${name}.log" 2>&1 &
|
||||
pids+=("$!")
|
||||
names+=("$name")
|
||||
done
|
||||
# Wait for the remaining builds
|
||||
for i in "${!pids[@]}"; do
|
||||
if ! wait "${pids[$i]}"; then
|
||||
echo "ERROR: build '${names[$i]}' failed, log follows (${names[$i]}.log):" >&2
|
||||
kill "${pids[@]}" 2>/dev/null || true
|
||||
cat "${names[$i]}.log" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
BUILD_FNS=()
|
||||
for b in "${BUILDS[@]}"; do
|
||||
read -r fn _ < <(build_spec "$b")
|
||||
BUILD_FNS+=("$fn")
|
||||
done
|
||||
echo "Building: ${BUILDS[*]} (max ${MAX_PARALLEL_BUILDS} at a time, -j ${JOBS_PER_BUILD} each)..."
|
||||
run_builds_parallel "${BUILD_FNS[@]}"
|
||||
|
||||
# Setup frameworks and copy binaries and headers
|
||||
echo "Setting up framework structures..."
|
||||
setup_framework_structure "build-ios-sim" ${IOS_MIN_OS_VERSION} "ios"
|
||||
setup_framework_structure "build-ios-device" ${IOS_MIN_OS_VERSION} "ios"
|
||||
setup_framework_structure "build-macos" ${MACOS_MIN_OS_VERSION} "macos"
|
||||
setup_framework_structure "build-visionos" ${VISIONOS_MIN_OS_VERSION} "visionos"
|
||||
setup_framework_structure "build-visionos-sim" ${VISIONOS_MIN_OS_VERSION} "visionos"
|
||||
setup_framework_structure "build-tvos-sim" ${TVOS_MIN_OS_VERSION} "tvos"
|
||||
setup_framework_structure "build-tvos-device" ${TVOS_MIN_OS_VERSION} "tvos"
|
||||
for b in "${BUILDS[@]}"; do
|
||||
read -r _ bdir _ platform _ min_os < <(build_spec "$b")
|
||||
setup_framework_structure "$bdir" "$min_os" "$platform"
|
||||
done
|
||||
|
||||
# Create dynamic libraries from static libraries
|
||||
echo "Creating dynamic libraries from static libraries..."
|
||||
combine_static_libraries "build-ios-sim" "Release-iphonesimulator" "ios" "true"
|
||||
combine_static_libraries "build-ios-device" "Release-iphoneos" "ios" "false"
|
||||
combine_static_libraries "build-macos" "Release" "macos" "false"
|
||||
combine_static_libraries "build-visionos" "Release-xros" "visionos" "false"
|
||||
combine_static_libraries "build-visionos-sim" "Release-xrsimulator" "visionos" "true"
|
||||
combine_static_libraries "build-tvos-sim" "Release-appletvsimulator" "tvos" "true"
|
||||
combine_static_libraries "build-tvos-device" "Release-appletvos" "tvos" "false"
|
||||
for b in "${BUILDS[@]}"; do
|
||||
read -r _ bdir rdir platform is_sim _ < <(build_spec "$b")
|
||||
combine_static_libraries "$bdir" "$rdir" "$platform" "$is_sim"
|
||||
done
|
||||
|
||||
# Create XCFramework with correct debug symbols paths
|
||||
echo "Creating XCFramework..."
|
||||
XCFW_ARGS=()
|
||||
for b in "${BUILDS[@]}"; do
|
||||
read -r _ bdir _ _ _ _ < <(build_spec "$b")
|
||||
XCFW_ARGS+=(-framework "$(pwd)/${bdir}/framework/llama.framework")
|
||||
XCFW_ARGS+=(-debug-symbols "$(pwd)/${bdir}/dSYMs/llama.dSYM")
|
||||
done
|
||||
xcrun xcodebuild -create-xcframework \
|
||||
-framework $(pwd)/build-ios-sim/framework/llama.framework \
|
||||
-debug-symbols $(pwd)/build-ios-sim/dSYMs/llama.dSYM \
|
||||
-framework $(pwd)/build-ios-device/framework/llama.framework \
|
||||
-debug-symbols $(pwd)/build-ios-device/dSYMs/llama.dSYM \
|
||||
-framework $(pwd)/build-macos/framework/llama.framework \
|
||||
-debug-symbols $(pwd)/build-macos/dSYMs/llama.dSYM \
|
||||
-framework $(pwd)/build-visionos/framework/llama.framework \
|
||||
-debug-symbols $(pwd)/build-visionos/dSYMs/llama.dSYM \
|
||||
-framework $(pwd)/build-visionos-sim/framework/llama.framework \
|
||||
-debug-symbols $(pwd)/build-visionos-sim/dSYMs/llama.dSYM \
|
||||
-framework $(pwd)/build-tvos-device/framework/llama.framework \
|
||||
-debug-symbols $(pwd)/build-tvos-device/dSYMs/llama.dSYM \
|
||||
-framework $(pwd)/build-tvos-sim/framework/llama.framework \
|
||||
-debug-symbols $(pwd)/build-tvos-sim/dSYMs/llama.dSYM \
|
||||
-output $(pwd)/build-apple/llama.xcframework
|
||||
"${XCFW_ARGS[@]}" \
|
||||
-output "$(pwd)/build-apple/llama.xcframework"
|
||||
|
||||
+1
-1
@@ -3362,7 +3362,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
{"--tools"}, "TOOL1,TOOL2,...",
|
||||
"experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)\n"
|
||||
"specify \"all\" to enable all tools\n"
|
||||
"available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, get_datetime, get_info\n"
|
||||
"available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, get_info\n"
|
||||
"note: for security reasons, this will limit --cors-origins to localhost by default",
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.server_tools = parse_csv_row(value);
|
||||
|
||||
+15
-8
@@ -926,6 +926,9 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
// draft-dspark: the draft carries a Markov head and uses an anchor-first block layout
|
||||
const bool is_dspark;
|
||||
|
||||
// dspark speculators
|
||||
bool sample_from_anchor = true;
|
||||
|
||||
const int32_t * target_layer_ids = nullptr; // model_dft's extract layer indices
|
||||
uint32_t target_layer_ids_n = 0;
|
||||
|
||||
@@ -960,16 +963,20 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
if (llama_model_meta_val_str(model_dft, "dflash.block_size", buf, sizeof(buf)) >= 0) {
|
||||
block_size = std::atoi(buf);
|
||||
}
|
||||
if (llama_model_meta_val_str(model_dft, "dflash.sample_from_anchor", buf, sizeof(buf)) >= 0) {
|
||||
sample_from_anchor = std::strcmp(buf, "true") == 0;
|
||||
}
|
||||
}
|
||||
mask_token_id = llama_vocab_mask(llama_model_get_vocab(model_dft));
|
||||
|
||||
LOG_INF("%s: adding speculative implementation '%s'\n", __func__, common_speculative_type_to_str(type).c_str());
|
||||
LOG_INF("%s: - n_max=%d, n_min=%d, p_min=%.2f\n", __func__, this->params.n_max, this->params.n_min, this->params.p_min);
|
||||
LOG_INF("%s: - block_size=%d, mask_token_id=%d, n_extract=%u\n", __func__, block_size, mask_token_id, target_layer_ids_n);
|
||||
LOG_INF("%s: - block_size=%d, mask_token_id=%d, n_extract=%u, sample_from_anchor=%s\n", __func__,
|
||||
block_size, mask_token_id, target_layer_ids_n, sample_from_anchor ? "true" : "false");
|
||||
|
||||
// DFlash input is [id_last, <mask> * (block_size-1)]: in-place denoising yields at most
|
||||
// block_size-1 draft tokens, DSpark yield a full block_size draft tokens
|
||||
const int32_t n_draft_max = is_dspark ? block_size : block_size - 1;
|
||||
// block_size-1 draft tokens, anchor-first DSpark yields a full block_size draft tokens
|
||||
const int32_t n_draft_max = is_dspark && sample_from_anchor ? block_size : block_size - 1;
|
||||
if (this->params.n_max > n_draft_max || this->params.n_min > n_draft_max) {
|
||||
LOG_WRN("%s: requested draft size (n_max=%d, n_min=%d) exceeds the trained block size %d -- clamping to %d\n",
|
||||
__func__, this->params.n_max, this->params.n_min, block_size, n_draft_max);
|
||||
@@ -1175,7 +1182,7 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
|
||||
const int32_t n_draft = params.n_max;
|
||||
|
||||
const int32_t n_block_tokens = n_draft + (is_dspark ? 0 : 1);
|
||||
const int32_t n_block_tokens = n_draft + (is_dspark && sample_from_anchor ? 0 : 1);
|
||||
i_block_beg[seq_id] = batch.n_tokens;
|
||||
n_block [seq_id] = n_block_tokens;
|
||||
for (int32_t i = 0; i < n_block_tokens; ++i) {
|
||||
@@ -1208,11 +1215,11 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
auto & result = *dp.result;
|
||||
|
||||
if (is_dspark) {
|
||||
// DSpark predicts the next token from position 0 and optionally truncates
|
||||
// at the first position below the confidence threshold.
|
||||
// DSpark: read from the first draft slot, truncate below the confidence threshold
|
||||
const float * conf = params.p_min > 0.0f ? llama_get_embeddings_nextn(ctx_dft) : nullptr;
|
||||
|
||||
for (int32_t i = 0; i < n_block_tokens; ++i) {
|
||||
// bonus-anchor drafts read the mask positions only, like DFlash
|
||||
const int32_t i_draft_beg = sample_from_anchor ? 0 : 1;
|
||||
for (int32_t i = i_draft_beg; i < n_block_tokens; ++i) {
|
||||
const int32_t idx = beg + i;
|
||||
|
||||
if (conf && conf[(size_t) idx * n_embd_dec] < params.p_min) {
|
||||
|
||||
@@ -55,6 +55,8 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"DeepseekV32ForCausalLM": "deepseek",
|
||||
"DFlashDraftModel": "qwen",
|
||||
"Qwen3DSparkModel": "qwen",
|
||||
"DSparkDraftModel": "qwen",
|
||||
"DSparkSpeculator": "qwen",
|
||||
"DeepseekV4ForCausalLM": "deepseek",
|
||||
"DeepseekV4DSparkModel": "deepseek",
|
||||
"DistilBertForMaskedLM": "bert",
|
||||
|
||||
+73
-12
@@ -4,12 +4,13 @@ import json
|
||||
|
||||
from typing import Any, Callable, Iterable, TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
from .base import ModelBase, TextModel, gguf, logger
|
||||
from .base import LazyTorchTensor, ModelBase, TextModel, gguf, logger
|
||||
|
||||
|
||||
@ModelBase.register("QWenLMHeadModel")
|
||||
@@ -708,22 +709,82 @@ class DFlashModel(Qwen3Model):
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
|
||||
@ModelBase.register("Qwen3DSparkModel")
|
||||
@ModelBase.register("Qwen3DSparkModel", "DSparkDraftModel", "DSparkSpeculator")
|
||||
@ModelBase.example("satgeze/Qwen3.6-27B-DSpark")
|
||||
class DSparkModel(DFlashModel):
|
||||
# DSpark = DFlash + a semi-autoregressive Markov head
|
||||
# DSpark = DFlash + a semi-autoregressive Markov head.
|
||||
model_arch = gguf.MODEL_ARCH.DFLASH
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# normalize the flat DeepSpec schema to DFlash's nested dflash_config
|
||||
self.hparams.setdefault("dflash_config", {
|
||||
k: self.hparams[k] for k in ("target_layer_ids", "mask_token_id") if k in self.hparams
|
||||
})
|
||||
def __init__(self, dir_model, *args, **kwargs):
|
||||
hparams = kwargs.pop("hparams", None)
|
||||
if hparams is None:
|
||||
hparams = ModelBase.load_hparams(dir_model, False)
|
||||
|
||||
# EAGLE3-style exports use the 1+N bonus-anchor block, DFlash-lineage exports sample from the anchor
|
||||
self._sample_from_anchor = hparams.get(
|
||||
"sample_from_anchor",
|
||||
"transformer_layer_config" not in hparams and "aux_hidden_state_layer_ids" not in hparams)
|
||||
if "transformer_layer_config" in hparams:
|
||||
hparams = {**hparams, **hparams["transformer_layer_config"]}
|
||||
|
||||
super().__init__(dir_model, *args, hparams=hparams, **kwargs)
|
||||
|
||||
# normalize both schemas to DFlash's nested dflash_config
|
||||
if "aux_hidden_state_layer_ids" in self.hparams:
|
||||
self.hparams.setdefault("dflash_config", {
|
||||
"mask_token_id": self.hparams.get("mask_token_id"),
|
||||
"target_layer_ids": [i - 1 for i in self.hparams["aux_hidden_state_layer_ids"]],
|
||||
})
|
||||
else:
|
||||
self.hparams.setdefault("dflash_config", {
|
||||
k: self.hparams[k] for k in ("target_layer_ids", "mask_token_id") if k in self.hparams
|
||||
})
|
||||
|
||||
if (markov_head_type := self.hparams.get("markov_head_type", "vanilla")) != "vanilla":
|
||||
raise ValueError(f"unsupported markov_head_type {markov_head_type!r} (only 'vanilla' is supported)")
|
||||
|
||||
n_vocab = self.hparams["vocab_size"]
|
||||
self._n_vocab_draft = self.hparams.get("draft_vocab_size") or n_vocab
|
||||
if self._n_vocab_draft > n_vocab:
|
||||
raise ValueError(f"draft_vocab_size {self._n_vocab_draft} exceeds vocab_size {n_vocab}")
|
||||
self._d2t: Tensor | None = None
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_sample_from_anchor(self._sample_from_anchor)
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, gen = item
|
||||
if name.endswith(("embed_tokens.weight", "lm_head.weight")):
|
||||
if item[0] == "t2d": # not used at runtime
|
||||
return None
|
||||
return super().filter_tensors((name, gen))
|
||||
return super().filter_tensors(item)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name == "model.d2t":
|
||||
self._d2t = data_torch
|
||||
return
|
||||
|
||||
if self._n_vocab_draft == self.hparams["vocab_size"] and name.endswith(("embed_tokens.weight", "lm_head.weight")):
|
||||
return
|
||||
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
def prepare_tensors(self):
|
||||
super().prepare_tensors()
|
||||
|
||||
n_vocab = self.hparams["vocab_size"]
|
||||
if self._n_vocab_draft < n_vocab and self._d2t is None:
|
||||
raise ValueError(f"draft_vocab_size {self._n_vocab_draft} < vocab_size {n_vocab} but no d2t table found")
|
||||
|
||||
# write d2t as absolute target token ids
|
||||
if self._d2t is not None:
|
||||
data = LazyTorchTensor.to_eager(self._d2t).to(torch.int64).cpu().numpy().reshape(-1)
|
||||
if data.size != self._n_vocab_draft:
|
||||
raise ValueError(f"d2t size {data.size} does not match draft_vocab_size {self._n_vocab_draft}")
|
||||
data = data + np.arange(data.size, dtype=np.int64)
|
||||
if np.any((data < 0) | (data >= n_vocab)):
|
||||
raise ValueError(f"d2t target ids out of range for target vocab size {n_vocab}")
|
||||
if np.unique(data).size != data.size:
|
||||
raise ValueError("d2t contains duplicate target ids")
|
||||
logger.info(f"{'d2t,':<30} --> I64, shape = {{{data.size}}}")
|
||||
self.gguf_writer.add_tensor("d2t", data, raw_dtype=gguf.GGMLQuantizationType.I64)
|
||||
|
||||
@@ -106,6 +106,10 @@ acceptance (from the draft's confidence head, if present) falls below `P` (defau
|
||||
Currently only drafts with a Qwen3 backbone are supported; support for other backbones
|
||||
(e.g. Gemma4) is planned.
|
||||
|
||||
DSpark drafts exported in the [speculators](https://github.com/vllm-project/speculators) format
|
||||
(for example [`RedHatAI/gemma-4-31B-it-speculator.dspark`](https://huggingface.co/RedHatAI/gemma-4-31B-it-speculator.dspark))
|
||||
convert the same way.
|
||||
|
||||
See:
|
||||
|
||||
- #25173
|
||||
|
||||
@@ -2,21 +2,5 @@ set(TARGET llama-gguf-hash)
|
||||
add_executable(${TARGET} gguf-hash.cpp)
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
|
||||
# clibs dependencies
|
||||
include_directories(deps/)
|
||||
|
||||
add_library(xxhash OBJECT deps/xxhash/xxhash.c deps/xxhash/xxhash.h)
|
||||
target_link_libraries(${TARGET} PRIVATE xxhash)
|
||||
|
||||
add_library(sha1 OBJECT deps/sha1/sha1.c deps/sha1/sha1.h)
|
||||
target_link_libraries(${TARGET} PRIVATE sha1)
|
||||
if (NOT MSVC)
|
||||
# disable warnings in 3rd party code
|
||||
target_compile_options(sha1 PRIVATE -w)
|
||||
endif()
|
||||
|
||||
add_library(sha256 OBJECT deps/sha256/sha256.c deps/sha256/sha256.h)
|
||||
target_link_libraries(${TARGET} PRIVATE sha256)
|
||||
|
||||
target_link_libraries(${TARGET} PRIVATE ggml ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(${TARGET} PRIVATE vendor-hash ggml ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "rotate-bits",
|
||||
"version": "0.1.1",
|
||||
"repo": "jb55/rotate-bits.h",
|
||||
"description": "rotate bits",
|
||||
"keywords": ["rotl", "rotr"],
|
||||
"src": ["rotate-bits.h"],
|
||||
"license": "Public Domain",
|
||||
"development": {
|
||||
"thlorenz/tap.c": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "sha1",
|
||||
"version": "0.0.1",
|
||||
"repo": "clibs/sha1",
|
||||
"description": "sha1 hash algorithm",
|
||||
"keywords": ["sha1", "hash"],
|
||||
"license": "public domain",
|
||||
"src": ["sha1.c", "sha1.h"]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "sha256",
|
||||
"version": "0.0.2",
|
||||
"repo": "jb55/sha256.c",
|
||||
"description": "sha256 in c",
|
||||
"keywords": ["sha256", "sha2"],
|
||||
"src": ["sha256.c", "sha256.h"],
|
||||
"dependencies": {
|
||||
"jb55/rotate-bits.h": "0.1.1"
|
||||
},
|
||||
"development": {
|
||||
"thlorenz/tap.c": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "xxhash",
|
||||
"version": "0.8.2",
|
||||
"repo": "Cyan4973/xxhash",
|
||||
"description": "Extremely fast non-cryptographic hash algorithm",
|
||||
"keywords": ["xxhash", "hashing"],
|
||||
"license": "BSD-2-Clause",
|
||||
"src": [
|
||||
"xxhash.c",
|
||||
"xxhash.h"
|
||||
]
|
||||
}
|
||||
@@ -18,13 +18,16 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include "xxhash/xxhash.h"
|
||||
#include "sha1/sha1.h"
|
||||
#include "sha256/sha256.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// sha1 is compiled as C++ and lives in a namespace, see scripts/sync_vendor.py
|
||||
#include "sha1/sha1.h"
|
||||
using namespace vendor_hash;
|
||||
|
||||
|
||||
// uuid.uuid5(uuid.NAMESPACE_URL, 'en.wikipedia.org/wiki/Llama.cpp')
|
||||
#define UUID_NAMESPACE_LLAMA_CPP "ef001206-dadc-5f6d-a15f-3359e577d4e5"
|
||||
|
||||
@@ -162,6 +162,7 @@ class Keys:
|
||||
TARGET_LAYERS = "{arch}.target_layers"
|
||||
TARGET_HIDDEN_SIZE = "{arch}.target_hidden_size"
|
||||
BLOCK_SIZE = "{arch}.block_size"
|
||||
SAMPLE_FROM_ANCHOR = "{arch}.sample_from_anchor"
|
||||
NORM_BEFORE_RESIDUAL = "{arch}.norm_before_residual"
|
||||
NORM_BEFORE_FC = "{arch}.norm_before_fc"
|
||||
|
||||
@@ -4819,6 +4820,7 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
],
|
||||
MODEL_ARCH.DFLASH: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
MODEL_TENSOR.ATTN_NORM,
|
||||
MODEL_TENSOR.ATTN_Q,
|
||||
@@ -4858,6 +4860,7 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.FFN_UP_SHEXP,
|
||||
MODEL_TENSOR.FC,
|
||||
MODEL_TENSOR.ENC_OUTPUT_NORM,
|
||||
MODEL_TENSOR.D2T,
|
||||
# optional DSpark heads
|
||||
MODEL_TENSOR.DSPARK_MARKOV_W1,
|
||||
MODEL_TENSOR.DSPARK_MARKOV_W2,
|
||||
|
||||
@@ -981,6 +981,9 @@ class GGUFWriter:
|
||||
def add_block_size(self, value: int) -> None:
|
||||
self.add_uint32(Keys.LLM.BLOCK_SIZE.format(arch=self.arch), value)
|
||||
|
||||
def add_sample_from_anchor(self, value: bool) -> None:
|
||||
self.add_bool(Keys.LLM.SAMPLE_FROM_ANCHOR.format(arch=self.arch), value)
|
||||
|
||||
def add_target_layers(self, value: Sequence[int]) -> None:
|
||||
self.add_array(Keys.LLM.TARGET_LAYERS.format(arch=self.arch), value)
|
||||
|
||||
|
||||
@@ -76,14 +76,14 @@ class TensorNameMap:
|
||||
# Output
|
||||
MODEL_TENSOR.OUTPUT: (
|
||||
"embed_out", # gptneox
|
||||
"lm_head", # gpt2 mpt falcon llama-hf baichuan qwen mamba dbrx jais nemotron exaone olmoe olmo2 phimoe plamo2
|
||||
"lm_head", # gpt2 mpt falcon llama-hf baichuan qwen mamba dbrx jais nemotron exaone olmoe olmo2 phimoe plamo2 llama4
|
||||
"output", # llama-pth bloom internlm2
|
||||
"word_embeddings_for_head", # persimmon
|
||||
"lm_head.linear", # phi2
|
||||
"output_layer", # chatglm
|
||||
"head", # rwkv
|
||||
"head.out", # wavtokenizer
|
||||
"lm_head", # llama4
|
||||
"model.lm_head", # dflash
|
||||
"model.transformer.ff_out", # llada
|
||||
"head.decoder", # modern-bert
|
||||
),
|
||||
|
||||
@@ -7,6 +7,12 @@ import subprocess
|
||||
|
||||
HTTPLIB_VERSION = "refs/tags/v0.53.1"
|
||||
|
||||
# used by examples/gguf-hash, these repos have no release tag, so we pin a commit
|
||||
XXHASH_COMMIT = "9f465f1ea932d6ad9a26cd77496311ffa544cd68"
|
||||
SHA1_COMMIT = "e1e2536fcf6a8f9703be8c85d58724b408552287"
|
||||
SHA256_COMMIT = "5e637272c13f200872d55ff579f7e2ab6c3f252f"
|
||||
ROTATE_BITS_COMMIT = "27e784942f67db44abf2115c6638e735b579acd1"
|
||||
|
||||
vendor = {
|
||||
"https://github.com/nlohmann/json/releases/latest/download/json.hpp": "vendor/nlohmann/json.hpp",
|
||||
"https://github.com/nlohmann/json/releases/latest/download/json_fwd.hpp": "vendor/nlohmann/json_fwd.hpp",
|
||||
@@ -22,12 +28,95 @@ vendor = {
|
||||
f"https://raw.githubusercontent.com/yhirose/cpp-httplib/{HTTPLIB_VERSION}/LICENSE": "vendor/cpp-httplib/LICENSE",
|
||||
|
||||
"https://raw.githubusercontent.com/sheredom/subprocess.h/9ce0d701b6fb10f8f8c4445edd31e7c60a1237e3/subprocess.h": "vendor/sheredom/subprocess.h",
|
||||
|
||||
f"https://raw.githubusercontent.com/Cyan4973/xxHash/{XXHASH_COMMIT}/xxhash.c": "vendor/hash/xxhash/xxhash.c",
|
||||
f"https://raw.githubusercontent.com/Cyan4973/xxHash/{XXHASH_COMMIT}/xxhash.h": "vendor/hash/xxhash/xxhash.h",
|
||||
f"https://raw.githubusercontent.com/Cyan4973/xxHash/{XXHASH_COMMIT}/LICENSE": "vendor/hash/xxhash/LICENSE",
|
||||
|
||||
# clibs/sha1 ships no license file, the source header says public domain
|
||||
f"https://raw.githubusercontent.com/clibs/sha1/{SHA1_COMMIT}/sha1.c": "vendor/hash/sha1/sha1.c",
|
||||
f"https://raw.githubusercontent.com/clibs/sha1/{SHA1_COMMIT}/sha1.h": "vendor/hash/sha1/sha1.h",
|
||||
|
||||
f"https://raw.githubusercontent.com/jb55/sha256.c/{SHA256_COMMIT}/sha256.c": "vendor/hash/sha256/sha256.c",
|
||||
f"https://raw.githubusercontent.com/jb55/sha256.c/{SHA256_COMMIT}/sha256.h": "vendor/hash/sha256/sha256.h",
|
||||
f"https://raw.githubusercontent.com/jb55/sha256.c/{SHA256_COMMIT}/LICENSE": "vendor/hash/sha256/LICENSE",
|
||||
|
||||
f"https://raw.githubusercontent.com/jb55/rotate-bits.h/{ROTATE_BITS_COMMIT}/rotate-bits.h": "vendor/hash/rotate-bits/rotate-bits.h",
|
||||
f"https://raw.githubusercontent.com/jb55/rotate-bits.h/{ROTATE_BITS_COMMIT}/LICENSE.md": "vendor/hash/rotate-bits/LICENSE.md",
|
||||
}
|
||||
|
||||
# local changes kept on top of the upstream sources
|
||||
patches = {
|
||||
"vendor/hash/xxhash/xxhash.h": [(
|
||||
'#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */\n',
|
||||
'/* Windows SDK under 10.0.22000 is missing stdalign.h so we add a check\n'
|
||||
' before allowing the windows compiler to use the C11 form.\n'
|
||||
' Reference: https://github.com/Cyan4973/xxHash/issues/955 */\n'
|
||||
'#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) \\\n'
|
||||
' && (defined(_MSC_VER) && (_MSC_VER >= 1000) || !defined(_MSC_VER)) /* >= C11 */\n'
|
||||
)],
|
||||
|
||||
# sha1 exports a bare "SHA1" symbol, which clashes with the boringssl one at link time.
|
||||
# we compile it as C++ (see vendor/hash/CMakeLists.txt) and put it in a namespace.
|
||||
"vendor/hash/sha1/sha1.h": [
|
||||
(
|
||||
'#if defined(__cplusplus)\n'
|
||||
'extern "C" {\n'
|
||||
'#endif\n',
|
||||
|
||||
'namespace vendor_hash {\n'
|
||||
),
|
||||
(
|
||||
'#if defined(__cplusplus)\n'
|
||||
'}\n'
|
||||
'#endif\n',
|
||||
|
||||
'} // namespace vendor_hash\n'
|
||||
),
|
||||
],
|
||||
|
||||
"vendor/hash/sha1/sha1.c": [
|
||||
(
|
||||
'#include "sha1.h"\n',
|
||||
|
||||
'#include "sha1.h"\n'
|
||||
'\n'
|
||||
'namespace vendor_hash {\n'
|
||||
),
|
||||
(
|
||||
' SHA1Final((unsigned char *)hash_out, &ctx);\n'
|
||||
'}\n',
|
||||
|
||||
' SHA1Final((unsigned char *)hash_out, &ctx);\n'
|
||||
'}\n'
|
||||
'\n'
|
||||
'} // namespace vendor_hash\n'
|
||||
),
|
||||
],
|
||||
|
||||
# silence a maybe-uninitialized warning
|
||||
"vendor/hash/sha256/sha256.c": [(
|
||||
" uint32_t W[16];\n",
|
||||
" uint32_t W[16] = {0};\n"
|
||||
)],
|
||||
}
|
||||
|
||||
for url, filename in vendor.items():
|
||||
print(f"downloading {url} to {filename}") # noqa: NP100
|
||||
urllib.request.urlretrieve(url, filename)
|
||||
|
||||
for filename, replacements in patches.items():
|
||||
print(f"patching {filename}") # noqa: NP100
|
||||
with open(filename, "r", encoding="utf-8", newline="") as f:
|
||||
content = f.read()
|
||||
for old, new in replacements:
|
||||
if content.count(old) != 1:
|
||||
print(f"Error: cannot apply patch on {filename}, upstream code has changed") # noqa: NP100
|
||||
sys.exit(1)
|
||||
content = content.replace(old, new)
|
||||
with open(filename, "w", encoding="utf-8", newline="") as f:
|
||||
f.write(content)
|
||||
|
||||
print("Splitting httplib.h...") # noqa: NP100
|
||||
try:
|
||||
subprocess.check_call([
|
||||
|
||||
+52
-3
@@ -85,6 +85,16 @@ void llama_model_dflash::load_arch_tensors(llama_model_loader &) {
|
||||
const int64_t n_embd_inp = hparams.n_embd_inp_enc();
|
||||
|
||||
tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), { n_embd, n_vocab }, TENSOR_NOT_REQUIRED);
|
||||
|
||||
// reduced draft vocab (optional): d2t maps draft rows to target token ids
|
||||
int64_t n_vocab_draft = n_vocab;
|
||||
const struct ggml_tensor * d2t_meta = ml->get_tensor_meta("d2t");
|
||||
if (d2t_meta) {
|
||||
n_vocab_draft = d2t_meta->ne[0];
|
||||
d2t = create_tensor(tn(LLM_TENSOR_D2T), { n_vocab_draft }, 0);
|
||||
LLAMA_LOG_INFO("%s: DFlash using d2t mapping (draft_vocab_size = %lld)\n", __func__, (long long) n_vocab_draft);
|
||||
}
|
||||
|
||||
// DSpark = DFlash + a semi-autoregressive Markov head and Confidence head
|
||||
//
|
||||
// TODO: only Qwen3-style backbones are supported for now; other backbones (e.g. Gemma4)
|
||||
@@ -94,7 +104,7 @@ void llama_model_dflash::load_arch_tensors(llama_model_loader &) {
|
||||
const int64_t dspark_markov_rank = markov_meta->ne[0];
|
||||
|
||||
dspark_markov_w1 = create_tensor(tn(LLM_TENSOR_DSPARK_MARKOV_W1, "weight"), { dspark_markov_rank, n_vocab }, 0);
|
||||
dspark_markov_w2 = create_tensor(tn(LLM_TENSOR_DSPARK_MARKOV_W2, "weight"), { dspark_markov_rank, n_vocab }, 0);
|
||||
dspark_markov_w2 = create_tensor(tn(LLM_TENSOR_DSPARK_MARKOV_W2, "weight"), { dspark_markov_rank, n_vocab_draft }, 0);
|
||||
|
||||
dspark_conf_proj = create_tensor(tn(LLM_TENSOR_DSPARK_CONF_PROJ, "weight"), { n_embd + dspark_markov_rank, 1 }, 0);
|
||||
dspark_conf_proj_b = create_tensor(tn(LLM_TENSOR_DSPARK_CONF_PROJ, "bias"), { 1 }, TENSOR_NOT_REQUIRED);
|
||||
@@ -157,6 +167,9 @@ void llama_model_dflash::load_arch_tensors(llama_model_loader &) {
|
||||
return;
|
||||
}
|
||||
|
||||
// optional: reduced-vocab drafts ship their own, full-vocab drafts share the target's via ctx_other
|
||||
output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), { n_embd, n_vocab_draft }, TENSOR_NOT_REQUIRED);
|
||||
|
||||
for (int i = 0; i < n_layer; ++i) {
|
||||
auto & layer = layers[i];
|
||||
|
||||
@@ -242,6 +255,11 @@ static void build_dspark_markov_head(llm_graph_context & g, const llama_model &
|
||||
const int64_t block_size = std::stoi(it->second);
|
||||
GGML_ASSERT(block_size > 0);
|
||||
|
||||
// bonus anchor (SpecForge exports): slot 0 is a bonus token, not a prediction slot
|
||||
const auto it_anchor = model.gguf_kv.find("dflash.sample_from_anchor");
|
||||
const bool sample_from_anchor = it_anchor == model.gguf_kv.end() || it_anchor->second == "true";
|
||||
const int64_t i_draft_beg = sample_from_anchor ? 0 : 1;
|
||||
|
||||
const int64_t n_blocks = g.ubatch.n_seqs_unq;
|
||||
GGML_ASSERT(n_blocks > 0 && n_tok % n_blocks == 0 && "DSpark markov head requires equal-size blocks");
|
||||
// runtime tokens per block in this ubatch (anchor + drafted positions), bounded by training block_size
|
||||
@@ -263,11 +281,26 @@ static void build_dspark_markov_head(llm_graph_context & g, const llama_model &
|
||||
ggml_tensor * cat = nullptr;
|
||||
ggml_tensor * cat_conf = nullptr;
|
||||
|
||||
if (!sample_from_anchor) {
|
||||
// bonus anchor slot: pass the logits through unbiased, pad the (unread) confidence column
|
||||
cat = ggml_cont(ctx0, ggml_view_2d(ctx0, base, n_vocab, n_blocks, base_stride, 0));
|
||||
cat_conf = ggml_sigmoid(ctx0, ggml_cont(ctx0, ggml_view_2d(ctx0, base, 1, n_blocks, base_stride, 0)));
|
||||
}
|
||||
|
||||
// TODO: the in-graph chain is greedy (argmax); sampling params affect only the final
|
||||
// token pick, not the Markov conditioning path
|
||||
for (int64_t i = 0; i < block_drafts; ++i) {
|
||||
for (int64_t i = i_draft_beg; i < block_drafts; ++i) {
|
||||
ggml_tensor * w1_prev = ggml_get_rows(ctx0, w1, prev); // [R, n_blocks]
|
||||
ggml_tensor * bias = ggml_mul_mat(ctx0, w2, w1_prev); // [n_vocab, n_blocks]
|
||||
ggml_tensor * bias = ggml_mul_mat(ctx0, w2, w1_prev); // [n_vocab_draft, n_blocks]
|
||||
if (model.d2t) {
|
||||
// reduced draft vocab: scatter the bias to the target rows (base is -inf on the others)
|
||||
const int64_t n_draft_vocab = bias->ne[0];
|
||||
ggml_tensor * full = ggml_fill(ctx0, ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, 1, n_vocab, n_blocks), 0.0f);
|
||||
bias = ggml_set_rows(ctx0, full,
|
||||
ggml_reshape_3d(ctx0, bias, 1, n_draft_vocab, n_blocks),
|
||||
ggml_reshape_3d(ctx0, model.d2t, n_draft_vocab, 1, 1));
|
||||
bias = ggml_reshape_2d(ctx0, bias, n_vocab, n_blocks);
|
||||
}
|
||||
|
||||
// position i of every block: strided view [n_vocab, n_blocks]
|
||||
ggml_tensor * base_i = ggml_view_2d(ctx0, base, n_vocab, n_blocks, base_stride, i*base->nb[1]);
|
||||
@@ -497,6 +530,22 @@ llama_model_dflash::graph<false>::graph(const llama_model & model, const llm_gra
|
||||
}
|
||||
|
||||
cur = build_lora_mm(output, cur, output_s);
|
||||
|
||||
// reduced-draft-vocab exports: scatter the draft logits to the target vocabulary via d2t
|
||||
if (model.d2t) {
|
||||
const int64_t n_draft_vocab = cur->ne[0];
|
||||
const int64_t n_outputs = cur->ne[1];
|
||||
const int64_t n_vocab = (int64_t) model.vocab.n_tokens();
|
||||
|
||||
GGML_ASSERT(model.d2t->type == GGML_TYPE_I64);
|
||||
GGML_ASSERT(model.d2t->ne[0] == n_draft_vocab);
|
||||
|
||||
ggml_tensor * logits = ggml_fill(ctx0, ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, 1, n_vocab, n_outputs), -INFINITY);
|
||||
cur = ggml_set_rows(ctx0, logits,
|
||||
ggml_reshape_3d(ctx0, cur, 1, n_draft_vocab, n_outputs),
|
||||
ggml_reshape_3d(ctx0, model.d2t, n_draft_vocab, 1, 1));
|
||||
cur = ggml_reshape_2d(ctx0, cur, n_vocab, n_outputs);
|
||||
}
|
||||
cb(cur, "result_output", -1);
|
||||
res->t_logits = cur;
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ set_target_properties(mtmd PROPERTIES
|
||||
)
|
||||
|
||||
target_link_libraries (mtmd PUBLIC ggml llama)
|
||||
target_link_libraries (mtmd PRIVATE Threads::Threads)
|
||||
target_link_libraries (mtmd PRIVATE Threads::Threads vendor-hash)
|
||||
target_include_directories(mtmd PUBLIC .)
|
||||
target_include_directories(mtmd PRIVATE ../..)
|
||||
target_include_directories(mtmd PRIVATE ../../vendor)
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "mtmd-helper-common.h"
|
||||
#include "llama.h"
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
@@ -356,25 +358,14 @@ static bool decode_audio_from_buf(const unsigned char * buf_in, size_t len, int
|
||||
|
||||
} // namespace audio_helpers
|
||||
|
||||
// Computes FNV-1a hash of the data
|
||||
static std::string fnv_hash(const uint8_t * data, size_t len) {
|
||||
const uint64_t fnv_prime = 0x100000001b3ULL;
|
||||
uint64_t hash = 0xcbf29ce484222325ULL;
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
hash ^= data[i];
|
||||
hash *= fnv_prime;
|
||||
}
|
||||
return std::to_string(hash);
|
||||
}
|
||||
|
||||
mtmd_helper_bitmap_wrapper mtmd_helper_bitmap_init_from_buf(mtmd_context * ctx, const unsigned char * buf, size_t len, bool placeholder) {
|
||||
// calculate the hash if needed
|
||||
std::string id;
|
||||
mtmd_bitmap * result = nullptr;
|
||||
|
||||
if (!placeholder) {
|
||||
id = fnv_hash(buf, len);
|
||||
// use sha256 to prevent cache poisoning
|
||||
id = hash_sha256_hex(buf, len);
|
||||
}
|
||||
|
||||
if (audio_helpers::is_audio_file((const char *)buf, len)) {
|
||||
|
||||
@@ -49,7 +49,7 @@ MTMD_API struct mtmd_helper_bitmap_wrapper mtmd_helper_bitmap_init_from_file(mtm
|
||||
// note:
|
||||
// - for now, video input is only supported via C++ helper functions
|
||||
// - audio files will be auto-detected based on magic bytes
|
||||
// - output bitmap will have FNV hash as the ID
|
||||
// - output bitmap will have SHA-256 hash (hex string) as the ID
|
||||
// returns nullptr on failure
|
||||
// this function is thread-safe
|
||||
MTMD_API struct mtmd_helper_bitmap_wrapper mtmd_helper_bitmap_init_from_buf(mtmd_context * ctx, const unsigned char * buf, size_t len, bool placeholder);
|
||||
|
||||
@@ -196,7 +196,7 @@ For the full list of features, please refer to [server's changelog](https://gith
|
||||
| `--ui-config, --webui-config JSON` | JSON that provides default UI settings (overrides UI defaults)<br/>(env: LLAMA_ARG_UI_CONFIG) |
|
||||
| `--ui-config-file, --webui-config-file PATH` | JSON file that provides default UI settings (overrides UI defaults)<br/>(env: LLAMA_ARG_UI_CONFIG_FILE) |
|
||||
| `--ui-mcp-proxy, --webui-mcp-proxy, --no-ui-mcp-proxy, --no-webui-mcp-proxy` | experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: disabled)<br/>(env: LLAMA_ARG_UI_MCP_PROXY) |
|
||||
| `--tools TOOL1,TOOL2,...` | experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)<br/>specify "all" to enable all tools<br/>available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, get_datetime, get_info<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_TOOLS) |
|
||||
| `--tools TOOL1,TOOL2,...` | experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)<br/>specify "all" to enable all tools<br/>available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, get_info<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_TOOLS) |
|
||||
| `--tools-runtime OPTION` | experimental: run tools in a separate runtime environment (default: none, use host environment)<br/>available options:<br/> 'docker:<image>', 'podman:<image>': spin up a new container and reuse it for all invocations, clean up on server exit<br/> 'docker-container:<id>', 'podman-container:<id>': use an existing container by ID, won't stop on server exit<br/> 'ssh:<target>': run tools on a remote POSIX host over SSH, key-based auth and a trusted host key are required<br/><br/>(env: LLAMA_ARG_TOOLS_RUNTIME) |
|
||||
| `--mcp-servers-config PATH` | experimental: path to JSON file with MCP server definitions (Cursor-compatible format) - do not enable in untrusted environments (default: none)<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_MCP_SERVERS_CONFIG) |
|
||||
| `--mcp-servers-json JSON` | experimental: inline JSON with MCP server definitions (Cursor-compatible format) - do not enable in untrusted environments (default: none)<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_MCP_SERVERS_JSON) |
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
@@ -1692,61 +1691,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// get_datetime: returns the current date and time
|
||||
//
|
||||
|
||||
struct server_tool_get_datetime : server_tool {
|
||||
server_tool_get_datetime() {
|
||||
name = "get_datetime";
|
||||
display_name = "Get Date & Time";
|
||||
permission_write = false;
|
||||
}
|
||||
|
||||
json get_definition() const override {
|
||||
return {
|
||||
{"type", "function"},
|
||||
{"function", {
|
||||
{"name", name},
|
||||
{"description", "Returns the current date and time in UTC"},
|
||||
{"parameters", {
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"format", {
|
||||
{"type", "string"},
|
||||
{"description",
|
||||
"strftime()-style format string for the output (default: \"%Y-%m-%dT%H:%M:%SZ\", "
|
||||
"e.g. ISO 8601). Choose your own format if you need something else, "
|
||||
"e.g. \"%A, %B %d %Y\" for a human-readable date."},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
};
|
||||
}
|
||||
|
||||
json invoke(json params, server_tool::stream *) const override {
|
||||
std::string format = json_value(params, "format", std::string("%Y-%m-%dT%H:%M:%SZ"));
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto time = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm tm_utc;
|
||||
#ifdef _WIN32
|
||||
gmtime_s(&tm_utc, &time);
|
||||
#else
|
||||
gmtime_r(&time, &tm_utc);
|
||||
#endif
|
||||
|
||||
char buf[256];
|
||||
size_t len = std::strftime(buf, sizeof(buf), format.c_str(), &tm_utc);
|
||||
if (len == 0) {
|
||||
return {{"error", "invalid format string"}};
|
||||
}
|
||||
|
||||
return {{"result", std::string(buf, len)}};
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// get_info: returns runtime info (OS name/version and cwd)
|
||||
//
|
||||
@@ -2005,6 +1949,10 @@ static server_tool & find_tool(std::vector<std::unique_ptr<server_tool>> & tools
|
||||
//
|
||||
|
||||
static std::vector<std::unique_ptr<server_tool>> build_tools() {
|
||||
// IMPORTANT: for contributors, please keep this array of tools as minimal as possible
|
||||
// we only accept minimal i/o and shell command tools here
|
||||
// for example, do not add: web search, get date time, etc.
|
||||
// high-level functionality should be added either via MCP or web UI
|
||||
std::vector<std::unique_ptr<server_tool>> tools;
|
||||
tools.push_back(std::make_unique<server_tool_read_file>());
|
||||
tools.push_back(std::make_unique<server_tool_file_glob_search>());
|
||||
@@ -2012,7 +1960,6 @@ static std::vector<std::unique_ptr<server_tool>> build_tools() {
|
||||
tools.push_back(std::make_unique<server_tool_exec_shell_command>());
|
||||
tools.push_back(std::make_unique<server_tool_write_file>());
|
||||
tools.push_back(std::make_unique<server_tool_edit_file>());
|
||||
tools.push_back(std::make_unique<server_tool_get_datetime>());
|
||||
tools.push_back(std::make_unique<server_tool_get_info>());
|
||||
return tools;
|
||||
}
|
||||
|
||||
@@ -259,6 +259,8 @@ int main(int argc, char ** argv) {
|
||||
}
|
||||
cpp += fmt("static const unsigned char asset_%d_data[] = {", i);
|
||||
append_bytes_hex(cpp, bytes);
|
||||
|
||||
// note: this is a simple hash for cache busting, not a cryptographic hash; fnv is enough here
|
||||
const auto hash = fnv_hash(bytes.data(), bytes.size());
|
||||
|
||||
cpp += fmt("};\nstatic const std::size_t asset_%d_size = %zu;\n",
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@
|
||||
if (typeof obj.result === 'string') return { dateString: obj.result.trim() };
|
||||
}
|
||||
} catch {
|
||||
return { dateString: toolResultString.trim() };
|
||||
// not JSON - nothing to show
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@@ -34,7 +34,7 @@ export const BUILTIN_TOOL_UI: Readonly<Record<BuiltInTool, BuiltinToolUiEntry>>
|
||||
label: 'Search files',
|
||||
source: ToolSource.BUILTIN
|
||||
},
|
||||
[BuiltInTool.GET_DATETIME]: { icon: Clock, label: 'Current time', source: ToolSource.BUILTIN },
|
||||
[BuiltInTool.GET_DATETIME]: { icon: Clock, label: 'Current time', source: ToolSource.FRONTEND },
|
||||
[BuiltInTool.GET_INFO]: { icon: Info, label: 'Runtime info', source: ToolSource.BUILTIN },
|
||||
[BuiltInTool.GREP_SEARCH]: {
|
||||
icon: SearchCode,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { BuiltInTool, JsonSchemaType, ToolCallType } from '$lib/enums';
|
||||
import type { OpenAIToolDefinition } from '$lib/types';
|
||||
|
||||
export const GET_DATETIME_TOOL_NAME = BuiltInTool.GET_DATETIME;
|
||||
|
||||
export function buildGetDatetimeToolDefinition(): OpenAIToolDefinition {
|
||||
return {
|
||||
function: {
|
||||
description:
|
||||
'Returns the current local date and time in ISO 8601 format, with the IANA time zone name',
|
||||
name: GET_DATETIME_TOOL_NAME,
|
||||
parameters: {
|
||||
properties: {},
|
||||
required: [],
|
||||
type: JsonSchemaType.OBJECT
|
||||
}
|
||||
},
|
||||
type: ToolCallType.FUNCTION
|
||||
};
|
||||
}
|
||||
@@ -59,4 +59,5 @@ export * from './uri-template.constants';
|
||||
export * from './url.constants';
|
||||
export * from './working-directory.constants';
|
||||
export * from './read-media';
|
||||
export * from './get-datetime';
|
||||
export * from './browser-info';
|
||||
|
||||
@@ -84,7 +84,12 @@ import type {
|
||||
DatabaseMessageExtraAudioFile,
|
||||
DatabaseMessageExtraImageFile
|
||||
} from '$lib/types/database';
|
||||
import { executeBrowserInfoTool, getAudioInputFormat, isAbortError } from '$lib/utils';
|
||||
import {
|
||||
executeBrowserInfoTool,
|
||||
executeGetDatetimeTool,
|
||||
getAudioInputFormat,
|
||||
isAbortError
|
||||
} from '$lib/utils';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
|
||||
function createDefaultSession(): AgenticSession {
|
||||
@@ -946,7 +951,9 @@ class AgenticStore {
|
||||
|
||||
let executionResult: ToolExecutionResult;
|
||||
|
||||
if (toolName === BuiltInTool.GET_INFO) {
|
||||
if (toolName === BuiltInTool.GET_DATETIME) {
|
||||
executionResult = executeGetDatetimeTool();
|
||||
} else if (toolName === BuiltInTool.GET_INFO) {
|
||||
executionResult = executeBrowserInfoTool();
|
||||
} else if (toolName === BuiltInTool.READ_MEDIA) {
|
||||
executionResult = await ReadMediaService.executeTool(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { browser } from '$app/environment';
|
||||
import {
|
||||
buildBrowserInfoToolDefinition,
|
||||
buildGetDatetimeToolDefinition,
|
||||
buildReadMediaToolDefinition,
|
||||
DISABLED_TOOL_KEYS_LOCALSTORAGE_KEY,
|
||||
HOME_TILDE,
|
||||
@@ -176,7 +177,7 @@ class ToolsStore {
|
||||
}
|
||||
|
||||
get frontendTools(): OpenAIToolDefinition[] {
|
||||
const tools: OpenAIToolDefinition[] = [];
|
||||
const tools: OpenAIToolDefinition[] = [buildGetDatetimeToolDefinition()];
|
||||
|
||||
if (settingsStore.config.jsSandboxEnabled) {
|
||||
tools.push(buildSandboxToolDefinition(!!settingsStore.config.symbolicMathEnabled));
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Frontend executor for the `get_datetime` tool. It runs in the browser, so it
|
||||
* reports the user's own clock and time zone instead of the server's UTC time -
|
||||
* a chat about "tomorrow" means the user's tomorrow, not the host's.
|
||||
*
|
||||
* @see buildGetDatetimeToolDefinition in constants/get-datetime.ts - tool schema sent to the LLM
|
||||
*/
|
||||
|
||||
import type { ToolExecutionResult } from '$lib/types';
|
||||
|
||||
function pad(value: number): string {
|
||||
return String(value).padStart(2, '0');
|
||||
}
|
||||
|
||||
/** ISO 8601 in local time, e.g. `2026-08-17T14:05:09+02:00` */
|
||||
function localIsoString(date: Date): string {
|
||||
// getTimezoneOffset() counts minutes behind UTC, ISO 8601 counts them ahead
|
||||
const offset = -date.getTimezoneOffset();
|
||||
const sign = offset < 0 ? '-' : '+';
|
||||
const absOffset = Math.abs(offset);
|
||||
const day = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
||||
const time = `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
||||
|
||||
return `${day}T${time}${sign}${pad(Math.floor(absOffset / 60))}:${pad(absOffset % 60)}`;
|
||||
}
|
||||
|
||||
/** The `result` field keeps the shape the `get_datetime` renderer already reads. */
|
||||
export function executeGetDatetimeTool(): ToolExecutionResult {
|
||||
const now = new Date();
|
||||
|
||||
return {
|
||||
content: JSON.stringify({
|
||||
result: localIsoString(now),
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
}),
|
||||
isError: false
|
||||
};
|
||||
}
|
||||
@@ -331,6 +331,9 @@ export { getChatCommands } from './chat-commands';
|
||||
// SANDBOX_TOOL_DEFINITION is deprecated; kept for backward compatibility.
|
||||
export { buildSandboxToolDefinition, SANDBOX_TOOL_DEFINITION } from './sandbox-tool';
|
||||
|
||||
// Frontend `get_datetime` executor (the browser clock, not the server's)
|
||||
export { executeGetDatetimeTool } from './get-datetime';
|
||||
|
||||
// Browser fallback for the server's get_info tool
|
||||
export { executeBrowserInfoTool } from './browser-info';
|
||||
|
||||
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
set(TARGET vendor-hash)
|
||||
|
||||
llama_add_compile_flags()
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(VENDOR_SRCS
|
||||
xxhash/xxhash.c
|
||||
sha1/sha1.c
|
||||
sha256/sha256.c
|
||||
)
|
||||
|
||||
add_library(${TARGET} STATIC
|
||||
hash.cpp
|
||||
hash.h
|
||||
${VENDOR_SRCS}
|
||||
)
|
||||
|
||||
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
||||
|
||||
# disable warnings in 3rd party code, but keep them for hash.cpp
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
set(NO_WARN_FLAG /w)
|
||||
else()
|
||||
set(NO_WARN_FLAG -w)
|
||||
endif()
|
||||
set_source_files_properties(${VENDOR_SRCS} PROPERTIES COMPILE_OPTIONS ${NO_WARN_FLAG})
|
||||
|
||||
# sha1 lives in a namespace to avoid a clash with boringssl, see scripts/sync_vendor.py
|
||||
set_source_files_properties(sha1/sha1.c PROPERTIES LANGUAGE CXX)
|
||||
|
||||
# sha256.c includes "rotate-bits/rotate-bits.h", so consumers get this dir too
|
||||
target_include_directories(${TARGET} PUBLIC .)
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
#include "hash.h"
|
||||
|
||||
extern "C" {
|
||||
#include "sha256/sha256.h"
|
||||
}
|
||||
|
||||
static std::string to_hex(const unsigned char * digest, size_t len) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
|
||||
std::string out;
|
||||
out.reserve(2*len);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
out += hex[digest[i] >> 4];
|
||||
out += hex[digest[i] & 0xf];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string hash_sha256_hex(const void * data, size_t len) {
|
||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||
sha256_hash(digest, (const unsigned char *) data, len);
|
||||
return to_hex(digest, SHA256_DIGEST_SIZE);
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
// C++ wrapper for the vendored hash functions
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
// returns the SHA-256 digest as a lowercase hex string
|
||||
std::string hash_sha256_hex(const void * data, size_t len);
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 William Casarin <jb55@jb55.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -25,6 +25,8 @@ A million repetitions of "a"
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
namespace vendor_hash {
|
||||
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
@@ -293,3 +295,5 @@ void SHA1(
|
||||
SHA1Final((unsigned char *)hash_out, &ctx);
|
||||
}
|
||||
|
||||
} // namespace vendor_hash
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
namespace vendor_hash {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -45,8 +43,6 @@ void SHA1(
|
||||
const char *str,
|
||||
uint32_t len);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
} // namespace vendor_hash
|
||||
|
||||
#endif /* SHA1_H */
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
2010-06-11 : Igor Pavlov : Public domain
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
xxHash Library
|
||||
Copyright (c) 2012-2021 Yann Collet
|
||||
All rights reserved.
|
||||
|
||||
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Reference in New Issue
Block a user