use std::env;
use std::path::PathBuf;
use std::time::Duration;
use thiserror::Error;
use crate::artifact::{ArtifactFetcher, DownloadPolicy};
use crate::hardware::{HardwareProfile, RuntimeBackend};
use crate::inference::{ModelCapabilities, OpenAiCompatibleProvider, RuntimeProvenance};
use crate::model::{ModelAcquireError, ModelCacheInspection, ModelInstall, NeoHorseModelManager};
use crate::runtime::{
LLAMA_CPP_COMMIT, LLAMA_CPP_VERSION, LlamaRuntimeManager, RuntimeCacheInspection, RuntimeError,
RuntimeInstall,
};
use crate::server::{ManagedServerEndpoint, ServerError, ServerLaunchConfig, ServerManager};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ManagedRuntimeConfig {
pub cache_root: PathBuf,
pub preferred_backend: Option<RuntimeBackend>,
pub download_policy: DownloadPolicy,
pub startup_timeout: Duration,
pub health_timeout: Duration,
}
impl ManagedRuntimeConfig {
#[must_use]
pub fn new(cache_root: impl Into<PathBuf>) -> Self {
Self {
cache_root: cache_root.into(),
preferred_backend: None,
download_policy: DownloadPolicy::default(),
startup_timeout: Duration::from_secs(10 * 60),
health_timeout: Duration::from_secs(2),
}
}
pub fn discover() -> Result<Self, ManagedRuntimeError> {
Ok(Self::new(default_cache_root()?))
}
pub fn inspect(
&self,
verify_digests: bool,
) -> Result<ManagedRuntimeInspection, ManagedRuntimeError> {
if !self.cache_root.is_absolute() {
return Err(ManagedRuntimeError::RelativeCacheRoot(
self.cache_root.clone(),
));
}
let hardware = HardwareProfile::detect();
let backend = self
.preferred_backend
.unwrap_or_else(|| hardware.recommended_backend());
let fetcher = ArtifactFetcher::new(self.download_policy);
let runtime = LlamaRuntimeManager::new(&self.cache_root, fetcher.clone()).inspect_for(
hardware.platform,
backend,
verify_digests,
)?;
let model = NeoHorseModelManager::new(&self.cache_root, fetcher).inspect(verify_digests)?;
let disk_probe = self
.cache_root
.ancestors()
.find(|path| path.exists())
.map(std::path::Path::to_path_buf)
.unwrap_or_else(|| PathBuf::from("/"));
let available_disk_bytes = fs2::available_space(&disk_probe).map_err(|source| {
ManagedRuntimeError::CacheInspection {
path: disk_probe,
source,
}
})?;
Ok(ManagedRuntimeInspection {
cache_root: self.cache_root.clone(),
hardware,
backend,
runtime,
model,
available_disk_bytes,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ManagedRuntimeInspection {
pub cache_root: PathBuf,
pub hardware: HardwareProfile,
pub backend: RuntimeBackend,
pub runtime: RuntimeCacheInspection,
pub model: ModelCacheInspection,
pub available_disk_bytes: u64,
}
#[derive(Debug)]
pub struct ManagedRuntime {
config: ManagedRuntimeConfig,
hardware: HardwareProfile,
server: ServerManager,
}
impl ManagedRuntime {
#[must_use]
pub fn new(config: ManagedRuntimeConfig) -> Self {
Self {
config,
hardware: HardwareProfile::detect(),
server: ServerManager::new(),
}
}
#[must_use]
pub fn with_hardware(mut self, hardware: HardwareProfile) -> Self {
self.hardware = hardware;
self
}
pub fn prepare(&mut self) -> Result<PreparedRuntime, ManagedRuntimeError> {
if !self.config.cache_root.is_absolute() {
return Err(ManagedRuntimeError::RelativeCacheRoot(
self.config.cache_root.clone(),
));
}
let fetcher = ArtifactFetcher::new(self.config.download_policy);
let runtime = LlamaRuntimeManager::new(&self.config.cache_root, fetcher.clone())
.ensure(&self.hardware, self.config.preferred_backend)?;
let model = NeoHorseModelManager::new(&self.config.cache_root, fetcher).ensure()?;
let launch_config = server_config(
&runtime,
&model,
&self.hardware,
self.config.startup_timeout,
self.config.health_timeout,
);
let endpoint = self.server.ensure_running(launch_config.clone())?;
let runtime_provenance = managed_runtime_provenance(
&runtime,
&model.capabilities,
&launch_config.arguments(endpoint.port),
&self.hardware,
);
Ok(PreparedRuntime {
endpoint,
runtime,
model_path: model.path,
capabilities: managed_capabilities(model.capabilities),
runtime_provenance,
hardware: self.hardware.clone(),
})
}
pub fn shutdown(&mut self) -> Result<(), ManagedRuntimeError> {
self.server.shutdown()?;
Ok(())
}
#[must_use]
pub fn diagnostics(&self) -> String {
self.server.diagnostics()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PreparedRuntime {
pub endpoint: ManagedServerEndpoint,
pub runtime: RuntimeInstall,
pub model_path: PathBuf,
pub capabilities: ModelCapabilities,
pub runtime_provenance: RuntimeProvenance,
pub hardware: HardwareProfile,
}
impl PreparedRuntime {
#[must_use]
pub fn provider(&self, inference_timeout: Duration) -> OpenAiCompatibleProvider {
OpenAiCompatibleProvider::new(
&self.endpoint.endpoint,
&self.capabilities.identifier,
None,
inference_timeout,
)
.with_model_capabilities(self.capabilities.clone())
.with_runtime_provenance(self.runtime_provenance.clone())
}
}
#[derive(Debug, Error)]
pub enum ManagedRuntimeError {
#[error(transparent)]
Runtime(#[from] RuntimeError),
#[error(transparent)]
Model(#[from] ModelAcquireError),
#[error(transparent)]
Server(#[from] ServerError),
#[error("managed cache root must be absolute: {0}")]
RelativeCacheRoot(PathBuf),
#[error("could not determine a user cache directory; set FALSEGREEN_AGENT_CACHE_DIR")]
MissingCacheDirectory,
#[error("could not inspect managed cache space at {path}: {source}")]
CacheInspection {
path: PathBuf,
#[source]
source: std::io::Error,
},
}
fn managed_capabilities(mut capabilities: ModelCapabilities) -> ModelCapabilities {
capabilities.qualification = None;
capabilities
}
fn managed_runtime_provenance(
runtime: &RuntimeInstall,
model: &ModelCapabilities,
launch_arguments: &[String],
hardware: &HardwareProfile,
) -> RuntimeProvenance {
RuntimeProvenance {
runtime: "llama.cpp llama-server".to_owned(),
version: LLAMA_CPP_VERSION.to_owned(),
commit: LLAMA_CPP_COMMIT.to_owned(),
distribution: format!("official GitHub release {}", runtime.release),
artifact: runtime.artifact_file_name.clone(),
artifact_sha256: Some(runtime.artifact_sha256.clone()),
platform: runtime.platform.to_string(),
backend: runtime.backend.cache_key().to_owned(),
accelerator: hardware.gpus.first().map(|gpu| {
format!(
"{} device={} architecture={} subsystem={}:{}",
gpu.vendor,
gpu.device_id.as_deref().unwrap_or("unknown"),
gpu.architecture.as_deref().unwrap_or("unknown"),
gpu.subsystem_vendor_id.as_deref().unwrap_or("unknown"),
gpu.subsystem_device_id.as_deref().unwrap_or("unknown")
)
}),
driver: hardware.graphics_driver.clone(),
launch_arguments: launch_arguments.to_vec(),
context_tokens: model.context_window_tokens,
chat_template: model.chat_template.clone(),
mtp_enabled: Some(false),
qualified_stack: false,
qualification_note: "NeoHorse V1's frozen qualification used the Ollama-bundled ROCm 7.2 backend on an AMD Radeon RX 7900 XTX (gfx1100); this managed distribution is separately pinned but not that qualified stack".to_owned(),
}
}
fn server_config(
runtime: &RuntimeInstall,
model: &ModelInstall,
hardware: &HardwareProfile,
startup_timeout: Duration,
health_timeout: Duration,
) -> ServerLaunchConfig {
ServerLaunchConfig {
executable: runtime.server_executable.clone(),
model: model.path.clone(),
model_identifier: model.capabilities.identifier.clone(),
backend: runtime.backend,
context_tokens: model.capabilities.context_window_tokens.unwrap_or(8_192),
logical_cpus: hardware.logical_cpus,
startup_timeout,
health_timeout,
}
}
fn default_cache_root() -> Result<PathBuf, ManagedRuntimeError> {
if let Some(path) = env::var_os("FALSEGREEN_AGENT_CACHE_DIR") {
let path = PathBuf::from(path);
return absolute_cache_path(path);
}
if cfg!(target_os = "windows") {
if let Some(path) = env::var_os("LOCALAPPDATA") {
return Ok(PathBuf::from(path).join("falsegreen-agent"));
}
} else if let Some(path) = env::var_os("XDG_CACHE_HOME") {
return Ok(PathBuf::from(path).join("falsegreen-agent"));
}
let home = env::var_os("HOME")
.map(PathBuf::from)
.ok_or(ManagedRuntimeError::MissingCacheDirectory)?;
if cfg!(target_os = "macos") {
Ok(home.join("Library/Caches/falsegreen-agent"))
} else {
Ok(home.join(".cache/falsegreen-agent"))
}
}
fn absolute_cache_path(path: PathBuf) -> Result<PathBuf, ManagedRuntimeError> {
if path.is_absolute() {
Ok(path)
} else {
Err(ManagedRuntimeError::RelativeCacheRoot(path))
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use std::time::Duration;
use super::{
ManagedRuntimeConfig, managed_capabilities, managed_runtime_provenance, server_config,
};
use crate::hardware::{
Architecture, HardwareProfile, OperatingSystem, RuntimeBackend, RuntimePlatform,
};
use crate::inference::{
InferenceProvider, ModelCapabilities, ModelQualification, OpenAiCompatibleProvider,
};
use crate::model::ModelInstall;
use crate::runtime::{LLAMA_CPP_COMMIT, LLAMA_CPP_RELEASE, RuntimeInstall};
#[test]
fn server_seam_uses_detected_threads_and_pinned_inputs() {
let runtime = RuntimeInstall {
root: PathBuf::from("/cache/runtime"),
server_executable: PathBuf::from("/cache/runtime/llama-server"),
artifact_file_name: "llama-b10630-bin-ubuntu-rocm-7.14-x64.tar.gz".to_owned(),
artifact_sha256: "runtime-digest".to_owned(),
platform: RuntimePlatform {
os: OperatingSystem::Linux,
architecture: Architecture::X86_64,
},
backend: RuntimeBackend::Rocm,
release: LLAMA_CPP_RELEASE.to_owned(),
commit: LLAMA_CPP_COMMIT.to_owned(),
reused: false,
};
let model = ModelInstall {
path: PathBuf::from("/cache/model.gguf"),
capabilities: ModelCapabilities {
identifier: "neohorse".to_owned(),
repository: None,
artifact: None,
artifact_sha256: None,
quantization: None,
chat_template: None,
context_window_tokens: Some(8_192),
native_tools: true,
qualification: None,
},
reused: false,
};
let hardware = HardwareProfile {
platform: runtime.platform,
kernel_release: Some("test-kernel".to_owned()),
logical_cpus: 24,
total_memory_bytes: None,
gpus: Vec::new(),
rocm_available: true,
vulkan_available: true,
graphics_driver: Some("amdgpu on kernel test-kernel".to_owned()),
diagnostics: Vec::new(),
};
let config = server_config(
&runtime,
&model,
&hardware,
Duration::from_secs(10),
Duration::from_secs(1),
);
assert_eq!(config.logical_cpus, 24);
assert_eq!(config.backend, RuntimeBackend::Rocm);
assert_eq!(config.context_tokens, 8_192);
}
#[test]
fn explicit_cache_configuration_is_deterministic() {
let config = ManagedRuntimeConfig::new("/var/cache/falsegreen-test");
assert_eq!(
config.cache_root,
PathBuf::from("/var/cache/falsegreen-test")
);
assert!(config.preferred_backend.is_none());
}
#[test]
fn every_managed_backend_fails_closed_on_frozen_stack_qualification() {
let qualified = ModelCapabilities {
identifier: "neohorse".to_owned(),
repository: None,
artifact: None,
artifact_sha256: None,
quantization: None,
chat_template: None,
context_window_tokens: Some(8_192),
native_tools: true,
qualification: Some(ModelQualification {
profile_name: "neohorse-v1".to_owned(),
revision: "revision".to_owned(),
expected_artifact: "model.gguf".to_owned(),
runtime: "Ollama-bundled ROCm HIP backend".to_owned(),
runtime_version: "ROCm 7.2 / llama-server 0.3.0-dev".to_owned(),
runtime_commit: "d222767c7".to_owned(),
accelerator: "AMD Radeon RX 7900 XTX".to_owned(),
architecture: "gfx1100".to_owned(),
mtp_enabled: false,
artifact_validated: true,
}),
};
for backend in [
RuntimeBackend::Cpu,
RuntimeBackend::Metal,
RuntimeBackend::Vulkan,
RuntimeBackend::Rocm,
] {
let runtime = RuntimeInstall {
root: PathBuf::from("/cache/runtime"),
server_executable: PathBuf::from("/cache/runtime/llama-server"),
artifact_file_name: format!("managed-{backend:?}"),
artifact_sha256: "runtime-digest".to_owned(),
platform: RuntimePlatform {
os: if backend == RuntimeBackend::Metal {
OperatingSystem::Macos
} else {
OperatingSystem::Linux
},
architecture: Architecture::X86_64,
},
backend,
release: LLAMA_CPP_RELEASE.to_owned(),
commit: LLAMA_CPP_COMMIT.to_owned(),
reused: false,
};
let capabilities = managed_capabilities(qualified.clone());
let hardware = HardwareProfile {
platform: runtime.platform,
kernel_release: Some("test-kernel".to_owned()),
logical_cpus: 8,
total_memory_bytes: None,
gpus: Vec::new(),
rocm_available: backend == RuntimeBackend::Rocm,
vulkan_available: backend == RuntimeBackend::Vulkan,
graphics_driver: None,
diagnostics: Vec::new(),
};
let provenance = managed_runtime_provenance(&runtime, &capabilities, &[], &hardware);
assert!(capabilities.qualification.is_none(), "backend {backend:?}");
assert!(!provenance.qualified_stack, "backend {backend:?}");
assert!(provenance.qualification_note.contains("ROCm 7.2"));
assert_eq!(provenance.backend, backend.cache_key());
let provider = OpenAiCompatibleProvider::new(
"http://127.0.0.1:1",
"neohorse",
None,
Duration::from_secs(1),
)
.with_model_capabilities(capabilities)
.with_runtime_provenance(provenance.clone());
let reported = provider.capabilities();
assert!(reported.model.qualification.is_none());
assert_eq!(reported.runtime_provenance, Some(provenance));
}
}
}