92 lines
3.0 KiB
Rust
92 lines
3.0 KiB
Rust
//! 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 tracing::instrument;
|
|
|
|
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.
|
|
#[instrument(skip(client))]
|
|
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.
|
|
#[tracing::instrument(skip(self, messages))]
|
|
pub async fn chat(
|
|
&self,
|
|
messages: &[ChatMessage],
|
|
) -> Result<(ChatMessage, Option<(u64, u64)>)> {
|
|
use zesdex_application::ports::ProviderService;
|
|
self.client
|
|
.chat(messages, None, Some(4096), None)
|
|
.await
|
|
}
|
|
|
|
/// Send messages with available tool definitions.
|
|
///
|
|
/// Automatically converts the `&[Box<dyn Tool>]` slice to
|
|
/// `Vec<ToolDef>` before passing to the underlying client.
|
|
#[tracing::instrument(skip(self, messages, tools))]
|
|
pub async fn chat_with_tools(
|
|
&self,
|
|
messages: &[ChatMessage],
|
|
tools: &[Box<dyn Tool>],
|
|
) -> Result<(ChatMessage, Option<(u64, u64)>)> {
|
|
let defs = tool_defs(tools);
|
|
use zesdex_application::ports::ProviderService;
|
|
self.client
|
|
.chat(messages, Some(defs), Some(4096), None)
|
|
.await
|
|
}
|
|
}
|
|
|
|
/// 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 the domain default model constant.
|
|
#[instrument]
|
|
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(|| zesdex_domain::agent::defaults::DEFAULT_MODEL.to_string())
|
|
} else {
|
|
model
|
|
};
|
|
|
|
(provider, model)
|
|
}
|