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
57 changes: 9 additions & 48 deletions desktop/src-tauri/src/mesh_llm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ mod discovery;
pub use discovery::{availability_from_events, mesh_status_filter};
use discovery::{device_name_from_status, endpoint_id_from_status, enrich_status_payload_identity};

mod preset;
pub use preset::{agent_preset, MeshAgentPreset, MeshAgentPresetRequest};

use mesh_llm_sdk::{client, serve, EmbeddedNodeHandle, MeshDiscoveryMode};
use serde::{Deserialize, Serialize};

Expand All @@ -13,6 +16,12 @@ const MESH_STATUS_KIND: u64 = 30_621;
const MESH_API_PORT_ENV: &str = "SPROUT_MESH_API_PORT";
const MESH_CONSOLE_PORT_ENV: &str = "SPROUT_MESH_CONSOLE_PORT";
const RELAY_MESH_API_KEY_PLACEHOLDER: &str = "sprout-mesh-local";
/// ACP provider relay-mesh agents run on. Sources of truth for its command +
/// MCP live in the provider catalog (`known_acp_provider_exact`); these are
/// only the fallbacks. `sprout-agent` reads the `SPROUT_AGENT_PROVIDER` /
/// `OPENAI_COMPAT_*` env vars below — goose (the global default) does not.
const MESH_AGENT_PROVIDER_ID: &str = "sprout-agent";
const MESH_AGENT_MCP_COMMAND: &str = "sprout-dev-mcp";

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -442,54 +451,6 @@ pub(super) fn dedupe_models(models: Vec<MeshModelOption>) -> Vec<MeshModelOption
.collect()
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MeshAgentPresetRequest {
pub model_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MeshAgentPreset {
pub provider_id: String,
pub label: String,
pub acp_command: String,
pub agent_command: String,
pub agent_args: Vec<String>,
pub mcp_command: String,
pub model: String,
pub env_vars: BTreeMap<String, String>,
}

pub fn agent_preset(request: MeshAgentPresetRequest) -> Result<MeshAgentPreset, String> {
let model = request.model_id.trim();
if model.is_empty() {
return Err("modelId is required".to_string());
}
Ok(MeshAgentPreset {
provider_id: "relay-mesh".to_string(),
label: "Relay mesh".to_string(),
acp_command: crate::managed_agents::DEFAULT_ACP_COMMAND.to_string(),
agent_command: crate::managed_agents::DEFAULT_AGENT_COMMAND.to_string(),
agent_args: Vec::new(),
mcp_command: crate::managed_agents::DEFAULT_MCP_COMMAND.to_string(),
model: model.to_string(),
env_vars: BTreeMap::from([
("SPROUT_AGENT_PROVIDER".to_string(), "openai".to_string()),
(
"OPENAI_COMPAT_BASE_URL".to_string(),
relay_mesh_api_base_url()?,
),
("OPENAI_COMPAT_MODEL".to_string(), model.to_string()),
(
"OPENAI_COMPAT_API_KEY".to_string(),
RELAY_MESH_API_KEY_PLACEHOLDER.to_string(),
),
("OPENAI_COMPAT_API".to_string(), "chat".to_string()),
]),
})
}

#[cfg(test)]
#[path = "mod_tests.rs"]
mod mod_tests;
32 changes: 32 additions & 0 deletions desktop/src-tauri/src/mesh_llm/mod_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,35 @@ fn model_ref_is_family_agnostic() {
assert!(!looks_like_model_ref("Qwen3-35B"));
assert!(!looks_like_model_ref(""));
}

#[test]
fn agent_preset_runs_on_sprout_agent_not_goose() {
// Regression (Tyler): the relay-mesh preset used to hand the agent the
// global default runtime (goose), which ignores the OpenAI-compat env
// vars and falls back to its own provider. Mesh agents must run on
// sprout-agent, which reads those vars.
let preset = super::agent_preset(super::MeshAgentPresetRequest {
model_id: "Qwen3-8B-Q4_K_M".to_string(),
})
.expect("preset for a valid model id");

assert_eq!(preset.agent_command, "sprout-agent");
assert_ne!(preset.agent_command, "goose");
assert_eq!(preset.mcp_command, "sprout-dev-mcp");

// The env vars sprout-agent's config layer reads (crates/sprout-agent).
assert_eq!(
preset
.env_vars
.get("SPROUT_AGENT_PROVIDER")
.map(String::as_str),
Some("openai")
);
assert_eq!(
preset
.env_vars
.get("OPENAI_COMPAT_MODEL")
.map(String::as_str),
Some("Qwen3-8B-Q4_K_M")
);
}
69 changes: 69 additions & 0 deletions desktop/src-tauri/src/mesh_llm/preset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! Relay-mesh "Run on relay mesh" agent preset. Kept in a sibling file so
//! `mod.rs` stays under the 500-line budget; `#[path]`-included from there.
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use super::{
relay_mesh_api_base_url, MESH_AGENT_MCP_COMMAND, MESH_AGENT_PROVIDER_ID,
RELAY_MESH_API_KEY_PLACEHOLDER,
};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MeshAgentPresetRequest {
pub model_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MeshAgentPreset {
pub provider_id: String,
pub label: String,
pub acp_command: String,
pub agent_command: String,
pub agent_args: Vec<String>,
pub mcp_command: String,
pub model: String,
pub env_vars: BTreeMap<String, String>,
}

pub fn agent_preset(request: MeshAgentPresetRequest) -> Result<MeshAgentPreset, String> {
let model = request.model_id.trim();
if model.is_empty() {
return Err("modelId is required".to_string());
}
// Run on sprout-agent, not the global default (goose). Source command +
// MCP from the catalog so this can't drift from the provider definition.
let sprout_agent = crate::managed_agents::known_acp_provider_exact(MESH_AGENT_PROVIDER_ID);
let agent_command = sprout_agent
.and_then(|p| p.commands.first().copied())
.unwrap_or(MESH_AGENT_PROVIDER_ID)
.to_string();
let mcp_command = sprout_agent
.and_then(|p| p.mcp_command)
.unwrap_or(MESH_AGENT_MCP_COMMAND)
.to_string();
Ok(MeshAgentPreset {
provider_id: "relay-mesh".to_string(),
label: "Relay mesh".to_string(),
acp_command: crate::managed_agents::DEFAULT_ACP_COMMAND.to_string(),
agent_command,
agent_args: Vec::new(),
mcp_command,
model: model.to_string(),
env_vars: BTreeMap::from([
("SPROUT_AGENT_PROVIDER".to_string(), "openai".to_string()),
(
"OPENAI_COMPAT_BASE_URL".to_string(),
relay_mesh_api_base_url()?,
),
("OPENAI_COMPAT_MODEL".to_string(), model.to_string()),
(
"OPENAI_COMPAT_API_KEY".to_string(),
RELAY_MESH_API_KEY_PLACEHOLDER.to_string(),
),
("OPENAI_COMPAT_API".to_string(), "chat".to_string()),
]),
})
}