Skip to content
Merged
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
1 change: 1 addition & 0 deletions crates/mesh-llm-embedded-runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![forbid(unsafe_code)]

pub use mesh_llm_host_runtime::initialize_host_runtime;
pub use mesh_llm_host_runtime::sdk::{
EmbeddedChatMessage, EmbeddedMeshAdmissionConfig, EmbeddedMeshDiscoveryMode,
EmbeddedMeshHttpConfig, EmbeddedMeshLogFormat, EmbeddedMeshNetworkConfig,
Expand Down
2 changes: 2 additions & 0 deletions crates/mesh-llm-host-runtime/src/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use tempfile::NamedTempFile;
use tokio::sync::Mutex;

mod embedded_config;
mod embedded_startup;

pub use embedded_config::*;

Expand Down Expand Up @@ -138,6 +139,7 @@ impl Drop for EmbeddedServeHandle {
pub async fn start_embedded_node(
mut config: EmbeddedMeshNodeConfig,
) -> Result<EmbeddedServeHandle> {
embedded_startup::prepare_embedded_native_runtime(&config.mode)?;
let isolated_config = prepare_isolated_config(&mut config)?;
let (control_tx, control_rx) = tokio::sync::mpsc::unbounded_channel();
let runtime_options = embedded_runtime_options(&config, Some(control_rx));
Expand Down
101 changes: 101 additions & 0 deletions crates/mesh-llm-host-runtime/src/sdk/embedded_startup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use super::EmbeddedMeshNodeMode;
use anyhow::Result;
#[cfg(any(feature = "dynamic-native-runtime", test))]
use std::path::Path;

pub(super) fn prepare_embedded_native_runtime(mode: &EmbeddedMeshNodeMode) -> Result<()> {
#[cfg(feature = "dynamic-native-runtime")]
{
if *mode == EmbeddedMeshNodeMode::Client || skippy_runtime::native_runtime_loaded() {
return Ok(());
}
let cache = crate::system::native_runtime_install::default_native_runtime_cache()?;
let skippy_abi = crate::system::native_runtime_install::current_skippy_abi_version();
let requirement = EmbeddedNativeRuntimeRequirement {
mesh_version: crate::RELEASE_VERSION,
skippy_abi: &skippy_abi,
cache_root: cache.root(),
};
let loaded =
crate::system::native_runtime::load_cached_native_runtime_for_embedded_serving()?
.is_some()
|| skippy_runtime::native_runtime_loaded();
ensure_embedded_native_runtime_ready(mode, loaded, requirement)?;
}
#[cfg(not(feature = "dynamic-native-runtime"))]
{
let _ = mode;
}
Ok(())
}

#[cfg(any(feature = "dynamic-native-runtime", test))]
struct EmbeddedNativeRuntimeRequirement<'a> {
mesh_version: &'a str,
skippy_abi: &'a str,
cache_root: &'a Path,
}

#[cfg(any(feature = "dynamic-native-runtime", test))]
fn ensure_embedded_native_runtime_ready(
mode: &EmbeddedMeshNodeMode,
loaded: bool,
requirement: EmbeddedNativeRuntimeRequirement<'_>,
) -> Result<()> {
if *mode == EmbeddedMeshNodeMode::Client || loaded {
return Ok(());
}
anyhow::bail!(missing_native_runtime_message(requirement));
}

#[cfg(any(feature = "dynamic-native-runtime", test))]
fn missing_native_runtime_message(requirement: EmbeddedNativeRuntimeRequirement<'_>) -> String {
format!(
"embedded serving requires a compatible MeshLLM native runtime for MeshLLM {} / Skippy ABI {}, but none is loaded or installed in {}; install it explicitly with `mesh_llm_sdk::native_runtime::install_native_runtime(NativeRuntimeInstallOptions {{ mesh_version: CURRENT_MESH_VERSION.to_string(), skippy_abi_version: Some(current_skippy_abi_version()), ..Default::default() }})`, then retry embedded serving (embedded startup never downloads native runtimes automatically)",
requirement.mesh_version,
requirement.skippy_abi,
requirement.cache_root.display()
)
}

#[cfg(test)]
mod tests {
use super::*;

fn requirement() -> EmbeddedNativeRuntimeRequirement<'static> {
EmbeddedNativeRuntimeRequirement {
mesh_version: "0.72.1",
skippy_abi: "0.1.26",
cache_root: Path::new("/cache/mesh-llm/native-runtimes"),
}
}

#[test]
fn missing_embedded_serve_runtime_error_names_versions_cache_and_fix() {
let error = ensure_embedded_native_runtime_ready(
&EmbeddedMeshNodeMode::Serve,
false,
requirement(),
)
.expect_err("missing native runtime should fail before embedded serving starts");
let message = error.to_string();

assert!(message.contains("MeshLLM 0.72.1 / Skippy ABI 0.1.26"));
assert!(message.contains("/cache/mesh-llm/native-runtimes"));
assert!(message.contains("install_native_runtime"));
assert!(message.contains("CURRENT_MESH_VERSION"));
assert!(message.contains("embedded startup never downloads"));
}

#[test]
fn embedded_client_does_not_require_native_serving_runtime() {
ensure_embedded_native_runtime_ready(&EmbeddedMeshNodeMode::Client, false, requirement())
.expect("client-only embedding should not require a serving runtime");
}

#[test]
fn loaded_native_runtime_allows_embedded_serve() {
ensure_embedded_native_runtime_ready(&EmbeddedMeshNodeMode::Serve, true, requirement())
.expect("loaded runtime should allow embedded serving");
}
}
5 changes: 3 additions & 2 deletions crates/mesh-llm-host-runtime/src/sdk/native_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ pub use crate::system::native_runtime_install::{
CURRENT_MESH_VERSION, NATIVE_RUNTIME_MANIFEST_URL_ENV, NativeRuntimeDownloadProgress,
NativeRuntimeDownloadProgressCallback, NativeRuntimeInstallOptions,
NativeRuntimeInstallOutcome, NativeRuntimeInstallStatus, NativeRuntimeManifestOptions,
NativeRuntimeVerificationPolicy, default_native_runtime_cache, default_release_manifest_url,
host_runtime_profile, install_native_runtime, load_release_manifest, native_runtime_cache,
NativeRuntimeVerificationPolicy, current_skippy_abi_version, default_native_runtime_cache,
default_release_manifest_url, host_runtime_profile, install_native_runtime,
load_release_manifest, native_runtime_cache, native_runtime_versions_match_current_sdk,
};
pub use mesh_llm_native_runtime::{
CachePrunePlan, CandidateEvaluation, CandidateRejection, HostGpuProfile, HostRuntimeProfile,
Expand Down
32 changes: 32 additions & 0 deletions crates/mesh-llm-host-runtime/src/system/native_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,38 @@ mod dynamic {
}
}

pub(crate) fn load_cached_native_runtime_for_embedded_serving()
-> Result<Option<LoadedNativeRuntime>> {
if skippy_runtime::native_runtime_loaded() {
return Ok(None);
}
let cache = default_native_runtime_cache()?;
let Some(plan) = resolve_installed_native_runtime_plan(
&cache,
&host_runtime_profile(),
crate::BUILD_VERSION,
crate::RELEASE_VERSION,
Some(&crate::system::native_runtime_install::current_skippy_abi_version()),
&RuntimeSelection::Recommended,
)?
else {
return Ok(None);
};
unsafe { skippy_runtime::load_native_runtime_libraries(&plan.libraries) }
.map_err(anyhow::Error::from)
.with_context(|| {
format!(
"load cached native runtime {} from {} for embedded serving",
plan.native_runtime_id,
plan.root.display()
)
})?;
Ok(Some(LoadedNativeRuntime {
native_runtime_id: plan.native_runtime_id,
libraries: plan.libraries,
}))
}

pub(crate) async fn try_load_installed_native_runtime(
startup_selection: NativeRuntimeStartupSelection,
) -> Result<Option<LoadedNativeRuntime>> {
Expand Down
23 changes: 23 additions & 0 deletions crates/mesh-llm-runtime-install/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ pub fn current_skippy_abi_version() -> String {
)
}

/// Returns whether native-runtime metadata matches the exact MeshLLM and
/// Skippy ABI versions linked into this SDK build.
pub fn native_runtime_versions_match_current_sdk(mesh_version: &str, skippy_abi: &str) -> bool {
mesh_version == CURRENT_MESH_VERSION && skippy_abi == current_skippy_abi_version()
}

pub fn default_native_runtime_cache() -> Result<NativeRuntimeCache> {
native_runtime_cache(None)
}
Expand Down Expand Up @@ -773,6 +779,23 @@ mod tests {
assert_eq!(CURRENT_MESH_VERSION, mesh_llm_build_info::RELEASE_VERSION);
}

#[test]
fn sdk_runtime_version_check_requires_exact_mesh_and_skippy_versions() {
let current_abi = current_skippy_abi_version();
assert!(native_runtime_versions_match_current_sdk(
CURRENT_MESH_VERSION,
&current_abi
));
assert!(!native_runtime_versions_match_current_sdk(
"0.0.0",
&current_abi
));
assert!(!native_runtime_versions_match_current_sdk(
CURRENT_MESH_VERSION,
"0.0.0"
));
}

#[test]
fn load_release_manifest_prefers_explicit_path_over_env_and_default() {
let _guard = MANIFEST_ENV_LOCK.lock().unwrap();
Expand Down
47 changes: 42 additions & 5 deletions crates/mesh-llm-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,15 @@ client.disconnect().await;
```toml
[dependencies]
mesh-llm-sdk = { version = "0.72.1", features = ["serving"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
anyhow = "1"
```

```rust,no_run
use mesh_llm_sdk::MeshNode;

// Complete the explicit version-check/install flow below before starting.

let node = MeshNode::builder()
.serve()
.model("unsloth/Qwen3-0.6B-GGUF:Q4_K_M")
Expand All @@ -65,9 +69,14 @@ let status = node.status().await?;
node.shutdown().await?;
```

## Native Runtime Install Example
## Check and Install the Required Native Runtime

Embedded serving requires a native runtime matching both the exact
`mesh-llm-sdk` release and its linked Skippy ABI. Re-run this check whenever the
SDK is upgraded: `CURRENT_MESH_VERSION` changes with the crate release, so a
runtime cached for the previous SDK version is not sufficient.

Enable `serving` to use native-runtime install/update APIs:
Enable `serving` to use native-runtime cache and install APIs:

```toml
[dependencies]
Expand All @@ -76,18 +85,46 @@ mesh-llm-sdk = { version = "0.72.1", features = ["serving"] }

```rust,no_run
use mesh_llm_sdk::native_runtime::{
NativeRuntimeInstallOptions, RuntimeSelection, install_native_runtime,
CURRENT_MESH_VERSION, NativeRuntimeInstallOptions, RuntimeSelection,
current_skippy_abi_version, install_native_runtime, native_runtime_versions_match_current_sdk,
};
use mesh_llm_sdk::{MeshNode, initialize_host_runtime};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let required_abi = current_skippy_abi_version();
let outcome = install_native_runtime(NativeRuntimeInstallOptions {
mesh_version: CURRENT_MESH_VERSION.to_string(),
skippy_abi_version: Some(required_abi),
selection: RuntimeSelection::Recommended,
..Default::default()
})
.await?;

println!("runtime: {}", outcome.runtime.path.display());
let runtime = outcome.runtime;

anyhow::ensure!(
native_runtime_versions_match_current_sdk(
&runtime.mesh_version,
&runtime.manifest.runtime.skippy_abi,
),
"native runtime does not match this SDK build"
);
runtime.load_plan()?;

println!("runtime: {}", runtime.path.display());
initialize_host_runtime().await?;

let node = MeshNode::builder().serve().start().await?;
node.shutdown().await?;
Ok(())
}
```

`install_native_runtime` is the explicit network/install step. It resolves the
recommended runtime against the current host profile, MeshLLM version, and
Skippy ABI, returning the compatible cached runtime when already installed or
installing it otherwise. The `load_plan` check verifies its declared libraries
are present before startup. Embedded node startup only loads a compatible cached
runtime; it never downloads one. If the cache has no matching runtime, startup
reports the required MeshLLM version, Skippy ABI, cache directory, and the
install API to call before retrying.
7 changes: 5 additions & 2 deletions crates/mesh-llm-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub mod embedded_node;
#[cfg(feature = "serving")]
pub use embedded_node::{MeshNode, MeshNodeBuilder, MeshNodeStatus, OpenAiClient};

#[cfg(feature = "serving")]
pub use mesh_llm_embedded_runtime::initialize_host_runtime;

#[cfg(feature = "serving")]
pub mod embedded_runtime {
pub use mesh_llm_embedded_runtime::{
Expand All @@ -39,8 +42,8 @@ pub mod embedded_runtime {
EmbeddedMeshNodeMode, EmbeddedMeshNodeStatus, EmbeddedMeshRequirementsConfig,
EmbeddedMeshServingConfig, EmbeddedMeshStorageConfig, EmbeddedServeConfig,
EmbeddedServeHandle, EmbeddedServeMode, EmbeddedServeStatus, EmbeddedServingController,
EmbeddedTrustPolicy, SIGNED_JOIN_TOKEN_MIN_PROTOCOL_VERSION, start_embedded_node,
start_embedded_serve,
EmbeddedTrustPolicy, SIGNED_JOIN_TOKEN_MIN_PROTOCOL_VERSION, initialize_host_runtime,
start_embedded_node, start_embedded_serve,
};
}

Expand Down
49 changes: 49 additions & 0 deletions crates/mesh-llm-sdk/tests/native_runtime_guidance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#![cfg(feature = "serving")]

use mesh_llm_sdk::native_runtime::{
CURRENT_MESH_VERSION, current_skippy_abi_version, native_runtime_versions_match_current_sdk,
};

#[test]
fn documented_version_check_tracks_sdk_release_and_skippy_abi() {
let required_abi = current_skippy_abi_version();
assert!(native_runtime_versions_match_current_sdk(
CURRENT_MESH_VERSION,
&required_abi
));
assert!(!native_runtime_versions_match_current_sdk(
"previous-sdk-release",
&required_abi
));
assert!(!native_runtime_versions_match_current_sdk(
CURRENT_MESH_VERSION,
"previous-skippy-abi"
));
}

#[test]
fn readme_keeps_explicit_check_install_initialize_start_order() {
let readme = include_str!("../README.md");
let check = readme
.find("native_runtime_versions_match_current_sdk")
.expect("README should check cached runtime versions");
let install = readme[check..]
.find("install_native_runtime(NativeRuntimeInstallOptions")
.map(|offset| check + offset)
.expect("README should explicitly install a missing runtime");
let initialize = readme[install..]
.find("initialize_host_runtime().await")
.map(|offset| install + offset)
.expect("README should initialize the installed runtime");
let start = readme[initialize..]
.find("MeshNode::builder().serve().start().await")
.map(|offset| initialize + offset)
.expect("README should start embedded serving after initialization");

assert!(check < install && install < initialize && initialize < start);
assert!(!readme.contains("cache.installed()?.into_iter().find"));
assert!(readme.contains("resolves the\nrecommended runtime against the current host profile"));
assert!(readme.contains("runtime.load_plan()?"));
assert!(readme.contains("startup only loads a compatible cached\nruntime"));
assert!(readme.contains("it never downloads one"));
}
Loading