Files
zesdex/apps/infrastructure/src/subagent/provider.rs
T

83 lines
2.7 KiB
Rust
Raw Normal View History

//! Subagent LLM provider — resolves provider/model from settings and wraps
//! `LlmClient` in a higher-level API for subagent use.
//!
//! Flow: `resolve_subagent_provider` is called at startup to pick a provider
//! + model → `SubagentProvider` wraps that pair around an `LlmClient` for use
//! inside the subagent engine loop.
use anyhow::Result;
use crate::llm::provider::LlmClient;
use crate::tools::{tool_defs, Tool};
use zesdex_domain::core::ChatMessage;
/// Provider wrapper for subagent LLM interactions.
///
/// Provides two convenience methods (`chat`, `chat_with_tools`) that
/// abstract away the raw `LlmClient` parameter plumbing so the engine
/// loop only deals with messages and tools.
///
/// The model identifier is already embedded in the `LlmClient` itself
/// (its `model` field), so `SubagentProvider` does not duplicate it.
pub struct SubagentProvider {
client: LlmClient,
}
impl SubagentProvider {
/// Wrap an existing `LlmClient` for higher-level use.
pub fn new(client: LlmClient) -> Self {
Self { client }
}
/// Send messages to the LLM without any tool definitions.
///
/// Use this for a plain text-in/text-out conversation.
pub fn chat(
&self,
messages: &[ChatMessage],
) -> Result<(ChatMessage, Option<(u64, u64)>)> {
self.client
.chat_with_tools_non_streaming(messages, None, Some(4096), None, None)
}
/// Send messages with available tool definitions.
///
/// Automatically converts the `&[Box<dyn Tool>]` slice to
/// `Vec<ToolDef>` before passing to the underlying client.
pub fn chat_with_tools(
&self,
messages: &[ChatMessage],
tools: &[Box<dyn Tool>],
) -> Result<(ChatMessage, Option<(u64, u64)>)> {
let defs = tool_defs(tools);
self.client
.chat_with_tools_non_streaming(messages, Some(defs), Some(4096), None, None)
}
}
/// Resolve subagent provider and model from settings.
///
/// Flow: reads `settings.provider` and `settings.model` → if model is empty,
/// falls back to the provider config's `default_model` → if that is also
/// empty, uses `"deepseek-v4-flash-free"` as the ultimate default.
pub fn resolve_subagent_provider(
settings: &zesdex_domain::cms::Settings,
app_config: &zesdex_domain::cms::AppConfig,
) -> (String, String) {
let provider = settings.provider.clone();
let model = settings.model.clone();
// Use the default model from the provider config if available
let model = if model.is_empty() {
app_config
.providers
.get(&provider)
.and_then(|p| p.default_model.clone())
.unwrap_or_else(|| "deepseek-v4-flash-free".to_string())
} else {
model
};
(provider, model)
}