Skip to content
Draft
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ urlencoding = "2.1"
version-compare = "0.2.1"

[target.'cfg(target_os = "macos")'.dependencies]
tvm-runtime = { git = "https://github.com/brekkylab/tvm-runtime-rs", rev = "ee8c5626e5309827b00af9e46111fe23b0307308", features = ["metal"] }
tvm-runtime = { git = "https://github.com/brekkylab/tvm-runtime-rs", rev = "cea927a", features = ["metal"] }

[target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies]
tvm-runtime = { git = "https://github.com/brekkylab/tvm-runtime-rs", rev = "ee8c5626e5309827b00af9e46111fe23b0307308", features = ["vulkan"] }
tvm-runtime = { git = "https://github.com/brekkylab/tvm-runtime-rs", rev = "cea927a", features = ["vulkan"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
ailoy-faiss-sys = { path = "./crates/faiss-sys" }
Expand All @@ -96,7 +96,7 @@ parking_lot = "0.12.4"
rmcp = { version = "0.11.0", features = ["client", "reqwest", "transport-child-process", "transport-streamable-http-client", "transport-streamable-http-client-reqwest"] }
tokenizers = { version = "0.22.2", default-features = false, features = ["onig"] }
tokio = { version = "1.0", default-features = false, features = ["macros", "rt-multi-thread", "sync"] }
tvm-ffi = { git = "https://github.com/brekkylab/tvm-runtime-rs", rev = "ee8c5626e5309827b00af9e46111fe23b0307308" }
tvm-ffi = { git = "https://github.com/brekkylab/tvm-runtime-rs", rev = "cea927a" }
uuid = { version = "1.18.0", features = ["v4"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
Expand Down
20 changes: 20 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ fn build_native() {
if std::env::var_os("CARGO_FEATURE_NODEJS").is_some() {
napi_build::setup();
}

// Link libmlc_llm_module so its TVM_FFI_STATIC_INIT_BLOCKs run at process
// load. We deliberately link the *_module variant rather than libmlc_llm:
// mlc-llm's Python package loads libmlc_llm_module via tvm.ffi.load_module,
// so linking the same dylib keeps a single GlobalFunctionTable in process
// when both ailoy and mlc-llm are imported together. Linking libmlc_llm
// directly would put two copies of every mlc.json_ffi.* function in the
// global registry and the second registration aborts at process load.
println!("cargo:rerun-if-env-changed=MLC_LLM_LIB_DIR");
if let Ok(mlc_dir) = std::env::var("MLC_LLM_LIB_DIR") {
println!("cargo:rustc-link-search=native={}", mlc_dir);
println!("cargo:rustc-link-lib=dylib=mlc_llm_module");
}

// Make the resulting cdylib portable: tell the linker to look for runtime
// dependencies right next to itself (`@loader_path`). Combined with the
// build/install step that copies libmlc_llm_module / libtvm{,_runtime,_ffi
// [_testing]} into bindings/python/ailoy/, this gives ailoy a self-
// contained dylib closure with no hard-coded venv paths.
println!("cargo:rustc-link-arg=-Wl,-rpath,@loader_path");
}

fn build_wasm() {
Expand Down
19 changes: 18 additions & 1 deletion src/model/local/chat_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,25 @@ impl ChatTemplate {
ThinkEffort::High => "high",
_ => "",
};
// HuggingFace-style chat templates address message body as `message.content`
// (singular), but our Message struct serializes the field as `contents`.
// Re-serialize each message into a serde_json::Value so we can also expose
// `content` as an alias, otherwise jinja sees `content == undefined` and
// emits empty user/system bodies which collapses the model.
let messages_for_template: Vec<serde_json::Value> = messages
.iter()
.map(|m| {
let mut v = serde_json::to_value(m).unwrap_or(serde_json::Value::Null);
if let Some(obj) = v.as_object_mut() {
if let Some(c) = obj.get("contents").cloned() {
obj.insert("content".to_string(), c);
}
}
v
})
.collect();
let ctx = context!(
messages => messages,
messages => messages_for_template,
tools => if !tools.is_empty() { Some(tools) } else { None::<_> },
documents => if !documents.is_empty() { Some(documents) } else { None::<_> },
add_generation_prompt => add_generation_prompt,
Expand Down
Loading