diff --git a/crates/skippy-model-package/src/main.rs b/crates/skippy-model-package/src/main.rs index 954a23ab1b..f530e1f147 100644 --- a/crates/skippy-model-package/src/main.rs +++ b/crates/skippy-model-package/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use clap::Parser; mod cli; @@ -38,9 +38,26 @@ fn prepare_model_download_directories() { unsafe { prepared.apply_to_process_environment() }; } +// ponytail: main runs on a child thread because the Windows main thread has a +// 1 MB stack. sha256 over a multi-GB GGUF plus FFI slice writing blows that +// stack in debug builds. 8 MB matches the mesh-llm runtime default. If a real +// recursion sink appears, raise this or fix the recursion — don't go lower. +const MAIN_STACK_SIZE: usize = 8 * 1024 * 1024; + fn main() -> Result<()> { prepare_model_download_directories(); let args = Args::parse(); + + let handle = std::thread::Builder::new() + .stack_size(MAIN_STACK_SIZE) + .spawn(move || run(args)) + .context("spawn skippy-model-package worker thread")?; + handle.join().unwrap_or_else(|panic| { + std::panic::resume_unwind(panic); + }) +} + +fn run(args: Args) -> Result<()> { match args.command { Command::Inspect { model } => inspect::inspect(model), Command::Plan { model, stages } => plan::build_plan(&model, stages).and_then(|output| { diff --git a/third_party/llama.cpp/patches/0005-Add-Skippy-model-lifecycle-and-package-support.patch b/third_party/llama.cpp/patches/0005-Add-Skippy-model-lifecycle-and-package-support.patch index 7051b42da9..9a43f2d971 100644 --- a/third_party/llama.cpp/patches/0005-Add-Skippy-model-lifecycle-and-package-support.patch +++ b/third_party/llama.cpp/patches/0005-Add-Skippy-model-lifecycle-and-package-support.patch @@ -1445,10 +1445,10 @@ index 000000000..ee1e43035 +} // extern "C" diff --git a/src/skippy/model_package.cpp b/src/skippy/model_package.cpp new file mode 100644 -index 000000000..3d199add8 +index 000000000..65d15b717 --- /dev/null +++ b/src/skippy/model_package.cpp -@@ -0,0 +1,705 @@ +@@ -0,0 +1,727 @@ +#include "skippy/model_package.h" +#include "skippy/errors.h" + @@ -1812,8 +1812,30 @@ index 000000000..3d199add8 + break; + } + -+ const size_t source_data_offset = gguf_get_data_offset(item.source->ctx); -+ if (std::fseek(input, static_cast(source_data_offset + item.tensor->offset), SEEK_SET) != 0) { ++ // std::fseek takes a long, which is 32-bit on Win32 MSVC, so GGUF files ++ // over 4 GB wrap and tensor reads land at the wrong offset. Seek with a ++ // 64-bit API where available, and validate the absolute offset first so ++ // an out-of-range value fails loudly instead of wrapping. ++ static_assert(std::numeric_limits::digits <= std::numeric_limits::digits, ++ "size_t wider than uint64_t is not supported here"); ++ const uint64_t source_data_offset = ++ static_cast(gguf_get_data_offset(item.source->ctx)); ++ const uint64_t tensor_offset = static_cast(item.tensor->offset); ++ bool seek_ok = false; ++ if (tensor_offset <= (std::numeric_limits::max)() - source_data_offset) { ++ const uint64_t absolute_offset = source_data_offset + tensor_offset; ++#if defined(_MSC_VER) ++ seek_ok = absolute_offset <= static_cast((std::numeric_limits<__int64>::max)()) && ++ _fseeki64(input, static_cast<__int64>(absolute_offset), SEEK_SET) == 0; ++#elif defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 ++ seek_ok = absolute_offset <= static_cast((std::numeric_limits::max)()) && ++ fseeko(input, static_cast(absolute_offset), SEEK_SET) == 0; ++#else ++ seek_ok = absolute_offset <= static_cast((std::numeric_limits::max)()) && ++ std::fseek(input, static_cast(absolute_offset), SEEK_SET) == 0; ++#endif ++ } ++ if (!seek_ok) { + std::fclose(input); + ok = false; + break;