feat: 引入 AgentRuntimeConfig,重构相关模块以支持运行时配置

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
ooodc 2026-04-30 22:34:22 +08:00
parent 3111abf4db
commit 716d92a618
11 changed files with 241 additions and 95 deletions

View File

@ -1,6 +1,6 @@
use crate::agent::AgentRuntimeConfig;
use crate::bus::ChatMessage; use crate::bus::ChatMessage;
use crate::bus::message::ToolMessageState; use crate::bus::message::ToolMessageState;
use crate::config::LLMProviderConfig;
use crate::domain::messages::{ContentBlock, ToolCall}; use crate::domain::messages::{ContentBlock, ToolCall};
use crate::observability::{ use crate::observability::{
Observer, ObserverEvent, ToolExecutionOutcome, ToolExecutionState, truncate_args, Observer, ObserverEvent, ToolExecutionOutcome, ToolExecutionState, truncate_args,
@ -292,7 +292,7 @@ fn chat_message_to_llm_message(m: &ChatMessage) -> Message {
/// AgentLoop - Stateless agent that processes messages with tool calling support. /// AgentLoop - Stateless agent that processes messages with tool calling support.
/// History is managed externally by SessionManager. /// History is managed externally by SessionManager.
pub struct AgentLoop { pub struct AgentLoop {
provider_config: LLMProviderConfig, runtime_config: AgentRuntimeConfig,
provider: Box<dyn LLMProvider>, provider: Box<dyn LLMProvider>,
tools: Arc<ToolRegistry>, tools: Arc<ToolRegistry>,
skills: Arc<dyn SkillProvider>, skills: Arc<dyn SkillProvider>,
@ -327,13 +327,14 @@ impl SkillProvider for EmptySkillProvider {
} }
impl AgentLoop { impl AgentLoop {
pub fn new(provider_config: LLMProviderConfig) -> Result<Self, AgentError> { pub fn new(config: impl Into<AgentRuntimeConfig>) -> Result<Self, AgentError> {
let max_iterations = provider_config.max_tool_iterations; let runtime_config = config.into();
let provider = create_provider(provider_config.clone()) let max_iterations = runtime_config.max_tool_iterations;
let provider = create_provider(runtime_config.provider.clone())
.map_err(|e| AgentError::ProviderCreation(e.to_string()))?; .map_err(|e| AgentError::ProviderCreation(e.to_string()))?;
Ok(Self { Ok(Self {
provider_config, runtime_config,
provider, provider,
tools: Arc::new(ToolRegistry::new()), tools: Arc::new(ToolRegistry::new()),
skills: Arc::new(EmptySkillProvider), skills: Arc::new(EmptySkillProvider),
@ -345,15 +346,16 @@ impl AgentLoop {
} }
pub fn with_tools( pub fn with_tools(
provider_config: LLMProviderConfig, config: impl Into<AgentRuntimeConfig>,
tools: Arc<ToolRegistry>, tools: Arc<ToolRegistry>,
) -> Result<Self, AgentError> { ) -> Result<Self, AgentError> {
let max_iterations = provider_config.max_tool_iterations; let runtime_config = config.into();
let provider = create_provider(provider_config.clone()) let max_iterations = runtime_config.max_tool_iterations;
let provider = create_provider(runtime_config.provider.clone())
.map_err(|e| AgentError::ProviderCreation(e.to_string()))?; .map_err(|e| AgentError::ProviderCreation(e.to_string()))?;
Ok(Self { Ok(Self {
provider_config, runtime_config,
provider, provider,
tools, tools,
skills: Arc::new(EmptySkillProvider), skills: Arc::new(EmptySkillProvider),
@ -365,16 +367,17 @@ impl AgentLoop {
} }
pub fn with_tools_and_skill_provider( pub fn with_tools_and_skill_provider(
provider_config: LLMProviderConfig, config: impl Into<AgentRuntimeConfig>,
tools: Arc<ToolRegistry>, tools: Arc<ToolRegistry>,
skills: Arc<dyn SkillProvider>, skills: Arc<dyn SkillProvider>,
) -> Result<Self, AgentError> { ) -> Result<Self, AgentError> {
let max_iterations = provider_config.max_tool_iterations; let runtime_config = config.into();
let provider = create_provider(provider_config.clone()) let max_iterations = runtime_config.max_tool_iterations;
let provider = create_provider(runtime_config.provider.clone())
.map_err(|e| AgentError::ProviderCreation(e.to_string()))?; .map_err(|e| AgentError::ProviderCreation(e.to_string()))?;
Ok(Self { Ok(Self {
provider_config, runtime_config,
provider, provider,
tools, tools,
skills, skills,
@ -543,10 +546,8 @@ impl AgentLoop {
tracing::info!(tool = %tool_call.name, args = %args_str, "Calling tool"); tracing::info!(tool = %tool_call.name, args = %args_str, "Calling tool");
// Truncate tool result if too large // Truncate tool result if too large
let truncated_output = truncate_tool_result( let truncated_output =
&result.output, truncate_tool_result(&result.output, self.runtime_config.tool_result_max_chars);
self.provider_config.tool_result_max_chars,
);
// Record tool call and check for loops // Record tool call and check for loops
let loop_result = loop_detector.record(&tool_call.name, &tool_call.arguments); let loop_result = loop_detector.record(&tool_call.name, &tool_call.arguments);

View File

@ -6,7 +6,7 @@ use crate::config::LLMProviderConfig;
use crate::providers::{ChatCompletionRequest, LLMProvider, Message, create_provider}; use crate::providers::{ChatCompletionRequest, LLMProvider, Message, create_provider};
use crate::text::{char_count, take_prefix_chars}; use crate::text::{char_count, take_prefix_chars};
use crate::agent::AgentError; use crate::agent::{AgentError, AgentRuntimeConfig};
/// Token estimation using ~4 chars/token heuristic with 1.2x safety margin. /// Token estimation using ~4 chars/token heuristic with 1.2x safety margin.
pub fn estimate_tokens(messages: &[ChatMessage]) -> usize { pub fn estimate_tokens(messages: &[ChatMessage]) -> usize {
@ -263,10 +263,14 @@ Be concise, aim for {} characters or less.
} }
pub fn from_provider_config(provider_config: &LLMProviderConfig) -> Self { pub fn from_provider_config(provider_config: &LLMProviderConfig) -> Self {
Self::from_runtime_config(&AgentRuntimeConfig::from(provider_config.clone()))
}
pub fn from_runtime_config(config: &AgentRuntimeConfig) -> Self {
Self::with_config( Self::with_config(
provider_config.context_window_tokens(), config.context_window_tokens,
ContextCompressionConfig { ContextCompressionConfig {
summary_max_chars: provider_config.context_summary_char_budget(), summary_max_chars: config.context_summary_char_budget,
..ContextCompressionConfig::default() ..ContextCompressionConfig::default()
}, },
) )
@ -434,7 +438,8 @@ Be concise, aim for {} characters or less.
return Ok(String::new()); return Ok(String::new());
} }
let provider = create_provider(provider_config.clone()) let runtime_config = AgentRuntimeConfig::from(provider_config.clone());
let provider = create_provider(runtime_config.provider)
.map_err(|e| AgentError::ProviderCreation(e.to_string()))?; .map_err(|e| AgentError::ProviderCreation(e.to_string()))?;
let transcript = Self::build_transcript(messages); let transcript = Self::build_transcript(messages);

View File

@ -1,7 +1,9 @@
pub mod agent_loop; pub mod agent_loop;
pub mod context_compressor; pub mod context_compressor;
pub mod runtime_config;
pub use agent_loop::{ pub use agent_loop::{
AgentError, AgentLoop, AgentProcessResult, EmittedMessageHandler, SkillProvider, AgentError, AgentLoop, AgentProcessResult, EmittedMessageHandler, SkillProvider,
}; };
pub use context_compressor::ContextCompressor; pub use context_compressor::ContextCompressor;
pub use runtime_config::AgentRuntimeConfig;

View File

@ -0,0 +1,39 @@
use crate::config::LLMProviderConfig;
use crate::providers::ProviderRuntimeConfig;
#[derive(Debug, Clone)]
pub struct AgentRuntimeConfig {
pub provider: ProviderRuntimeConfig,
pub context_window_tokens: usize,
pub context_summary_char_budget: usize,
pub max_tool_iterations: usize,
pub tool_result_max_chars: usize,
pub context_tool_result_trim_chars: usize,
}
impl From<LLMProviderConfig> for AgentRuntimeConfig {
fn from(config: LLMProviderConfig) -> Self {
let context_window_tokens = config.context_window_tokens();
let context_summary_char_budget = config.context_summary_char_budget();
Self {
provider: ProviderRuntimeConfig {
provider_type: config.provider_type,
name: config.name,
base_url: config.base_url,
api_key: config.api_key,
extra_headers: config.extra_headers,
llm_timeout_secs: config.llm_timeout_secs,
model_id: config.model_id,
temperature: config.temperature,
max_tokens: config.max_tokens,
model_extra: config.model_extra,
},
context_window_tokens,
context_summary_char_budget,
max_tool_iterations: config.max_tool_iterations,
tool_result_max_chars: config.tool_result_max_chars,
context_tool_result_trim_chars: config.context_tool_result_trim_chars,
}
}
}

View File

@ -4,7 +4,7 @@ use std::time::Duration;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::agent::AgentError; use crate::agent::{AgentError, AgentRuntimeConfig};
use crate::config::LLMProviderConfig; use crate::config::LLMProviderConfig;
use crate::providers::{ChatCompletionRequest, Message, create_provider}; use crate::providers::{ChatCompletionRequest, Message, create_provider};
use crate::storage::{MemoryRecord, SessionStore}; use crate::storage::{MemoryRecord, SessionStore};
@ -114,7 +114,8 @@ impl MemoryMaintenanceService {
scope_key: &str, scope_key: &str,
plan: &MemoryMaintenancePlan, plan: &MemoryMaintenancePlan,
) -> Result<MemoryMaintenanceModelOutput, AgentError> { ) -> Result<MemoryMaintenanceModelOutput, AgentError> {
let provider = create_provider(self.provider_config.clone()).map_err(|err| { let runtime_config = AgentRuntimeConfig::from(self.provider_config.clone());
let provider = create_provider(runtime_config.provider).map_err(|err| {
AgentError::Other(format!("create maintenance provider error: {}", err)) AgentError::Other(format!("create maintenance provider error: {}", err))
})?; })?;

View File

@ -13,6 +13,7 @@ pub mod processor;
pub mod prompt; pub mod prompt;
pub mod prompt_injector; pub mod prompt_injector;
pub mod provider_config_service; pub mod provider_config_service;
pub mod runtime;
pub mod scheduled_agent_task_service; pub mod scheduled_agent_task_service;
pub mod session; pub mod session;
pub mod session_factory; pub mod session_factory;
@ -38,6 +39,7 @@ use crate::skills::SkillRuntime;
use agent_task_executor::{AgentTaskExecutor, SchedulerMaintenanceService}; use agent_task_executor::{AgentTaskExecutor, SchedulerMaintenanceService};
use outbound_dispatcher::OutboundDispatcher; use outbound_dispatcher::OutboundDispatcher;
use processor::InboundProcessor; use processor::InboundProcessor;
use runtime::build_session_manager;
use session::SessionManager; use session::SessionManager;
pub struct GatewayState { pub struct GatewayState {
@ -63,7 +65,7 @@ impl GatewayState {
let skills = Arc::new(SkillRuntime::from_config(config.skills.clone())); let skills = Arc::new(SkillRuntime::from_config(config.skills.clone()));
let session_manager = SessionManager::new( let session_manager = build_session_manager(
session_ttl_hours, session_ttl_hours,
agent_prompt_reinject_every, agent_prompt_reinject_every,
show_tool_results, show_tool_results,

96
src/gateway/runtime.rs Normal file
View File

@ -0,0 +1,96 @@
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use crate::agent::AgentError;
use crate::config::LLMProviderConfig;
use crate::skills::SkillRuntime;
use crate::storage::{
ConversationRepository, MemoryRepository, PromptInjectionRepository, SchedulerJobRepository,
SessionStore, SkillEventRepository,
};
use crate::tools::ToolRegistry;
use super::agent_factory::AgentFactory;
use super::cli_session::CliSessionService;
use super::memory_maintenance_coordinator::MemoryMaintenanceCoordinator;
use super::prompt_injector::PromptInjector;
use super::provider_config_service::ProviderConfigService;
use super::scheduled_agent_task_service::ScheduledAgentTaskService;
use super::session::{SessionManager, SessionManagerServices};
use super::session_factory::SessionFactory;
use super::session_lifecycle::SessionLifecycleService;
use super::session_message_service::SessionMessageService;
use super::tool_registry_factory::ToolRegistryFactory;
pub(crate) fn build_session_manager(
session_ttl_hours: u64,
agent_prompt_reinject_every: u64,
show_tool_results: bool,
default_timezone: String,
provider_config: LLMProviderConfig,
provider_configs: HashMap<String, LLMProviderConfig>,
skills: Arc<SkillRuntime>,
) -> Result<SessionManager, AgentError> {
let store = Arc::new(
SessionStore::new()
.map_err(|err| AgentError::Other(format!("session store init error: {}", err)))?,
);
let known_agents = provider_configs.keys().cloned().collect::<HashSet<_>>();
let provider_configs = ProviderConfigService::new(provider_config.clone(), provider_configs);
if let Err(err) =
store.append_skill_event(None, "discovered", None, &skills.discovery_event_payload())
{
tracing::warn!(error = %err, "Failed to record skill discovery event");
}
let memories: Arc<dyn MemoryRepository> = store.clone();
let scheduler_jobs: Arc<dyn SchedulerJobRepository> = store.clone();
let skill_events: Arc<dyn SkillEventRepository> = store.clone();
let tools = Arc::new(
ToolRegistryFactory::new(
skills.clone(),
memories,
scheduler_jobs,
skill_events.clone(),
known_agents,
default_timezone,
)
.build(),
);
let agent_factory = AgentFactory::new(tools.clone(), skills.clone());
let conversations: Arc<dyn ConversationRepository> = store.clone();
let prompt_repository: Arc<dyn PromptInjectionRepository> = store.clone();
let prompt_injector = PromptInjector::new(prompt_repository, agent_prompt_reinject_every);
let session_factory = SessionFactory::new(
provider_config.clone(),
skills.clone(),
agent_factory,
prompt_injector,
conversations,
skill_events,
);
let lifecycle = SessionLifecycleService::new(session_ttl_hours, session_factory);
let cli_sessions = CliSessionService::new(store.clone());
let messages = SessionMessageService::new(lifecycle.clone(), show_tool_results);
let scheduled_tasks = ScheduledAgentTaskService::new(
lifecycle.clone(),
provider_configs.clone(),
show_tool_results,
);
let memory_maintenance =
MemoryMaintenanceCoordinator::new(store.clone(), provider_configs.clone());
Ok(SessionManager::from_services(SessionManagerServices {
tools: tools as Arc<ToolRegistry>,
skills,
store,
show_tool_results,
lifecycle,
cli_sessions,
messages,
scheduled_tasks,
memory_maintenance,
}))
}

View File

@ -9,7 +9,7 @@ use crate::skills::SkillRuntime;
use crate::storage::{ConversationRepository, SessionRecord, SessionStore, SkillEventRepository}; use crate::storage::{ConversationRepository, SessionRecord, SessionStore, SkillEventRepository};
use crate::tools::ToolRegistry; use crate::tools::ToolRegistry;
use async_trait::async_trait; use async_trait::async_trait;
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::{Mutex, mpsc}; use tokio::sync::{Mutex, mpsc};
use uuid::Uuid; use uuid::Uuid;
@ -26,13 +26,10 @@ use super::memory_maintenance::{
use super::memory_maintenance::{MemoryMaintenanceModelOutput, MemoryMaintenanceScopeResult}; use super::memory_maintenance::{MemoryMaintenanceModelOutput, MemoryMaintenanceScopeResult};
use super::memory_maintenance_coordinator::MemoryMaintenanceCoordinator; use super::memory_maintenance_coordinator::MemoryMaintenanceCoordinator;
use super::prompt_injector::PromptInjector; use super::prompt_injector::PromptInjector;
use super::provider_config_service::ProviderConfigService;
use super::scheduled_agent_task_service::ScheduledAgentTaskService; use super::scheduled_agent_task_service::ScheduledAgentTaskService;
use super::session_factory::SessionFactory;
use super::session_history::SessionHistory; use super::session_history::SessionHistory;
use super::session_lifecycle::SessionLifecycleService; use super::session_lifecycle::SessionLifecycleService;
use super::session_message_service::SessionMessageService; use super::session_message_service::SessionMessageService;
use super::tool_registry_factory::ToolRegistryFactory;
/// Session 按 channel 隔离,每个 channel 一个 Session /// Session 按 channel 隔离,每个 channel 一个 Session
/// History 按 chat_id 隔离,由 Session 统一管理 /// History 按 chat_id 隔离,由 Session 统一管理
@ -346,7 +343,33 @@ pub struct SessionManager {
memory_maintenance: MemoryMaintenanceCoordinator, memory_maintenance: MemoryMaintenanceCoordinator,
} }
pub(crate) struct SessionManagerServices {
pub(crate) tools: Arc<ToolRegistry>,
pub(crate) skills: Arc<SkillRuntime>,
pub(crate) store: Arc<SessionStore>,
pub(crate) show_tool_results: bool,
pub(crate) lifecycle: SessionLifecycleService,
pub(crate) cli_sessions: CliSessionService,
pub(crate) messages: SessionMessageService,
pub(crate) scheduled_tasks: ScheduledAgentTaskService,
pub(crate) memory_maintenance: MemoryMaintenanceCoordinator,
}
impl SessionManager { impl SessionManager {
pub(crate) fn from_services(services: SessionManagerServices) -> Self {
Self {
tools: services.tools,
skills: services.skills,
store: services.store,
show_tool_results: services.show_tool_results,
lifecycle: services.lifecycle,
cli_sessions: services.cli_sessions,
messages: services.messages,
scheduled_tasks: services.scheduled_tasks,
memory_maintenance: services.memory_maintenance,
}
}
pub fn new( pub fn new(
session_ttl_hours: u64, session_ttl_hours: u64,
agent_prompt_reinject_every: u64, agent_prompt_reinject_every: u64,
@ -356,63 +379,15 @@ impl SessionManager {
provider_configs: HashMap<String, LLMProviderConfig>, provider_configs: HashMap<String, LLMProviderConfig>,
skills: Arc<SkillRuntime>, skills: Arc<SkillRuntime>,
) -> Result<Self, AgentError> { ) -> Result<Self, AgentError> {
let store = Arc::new( super::runtime::build_session_manager(
SessionStore::new() session_ttl_hours,
.map_err(|err| AgentError::Other(format!("session store init error: {}", err)))?, agent_prompt_reinject_every,
);
let known_agents = provider_configs.keys().cloned().collect::<HashSet<_>>();
let provider_configs =
ProviderConfigService::new(provider_config.clone(), provider_configs);
if let Err(err) =
store.append_skill_event(None, "discovered", None, &skills.discovery_event_payload())
{
tracing::warn!(error = %err, "Failed to record skill discovery event");
}
let tools = Arc::new(
ToolRegistryFactory::new(
skills.clone(),
store.clone(),
known_agents,
default_timezone,
)
.build(),
);
let agent_factory = AgentFactory::new(tools.clone(), skills.clone());
let conversations: Arc<dyn ConversationRepository> = store.clone();
let skill_events: Arc<dyn SkillEventRepository> = store.clone();
let prompt_injector = PromptInjector::new(store.clone(), agent_prompt_reinject_every);
let session_factory = SessionFactory::new(
provider_config.clone(),
skills.clone(),
agent_factory,
prompt_injector,
conversations,
skill_events,
);
let lifecycle = SessionLifecycleService::new(session_ttl_hours, session_factory);
let cli_sessions = CliSessionService::new(store.clone());
let messages = SessionMessageService::new(lifecycle.clone(), show_tool_results);
let scheduled_tasks = ScheduledAgentTaskService::new(
lifecycle.clone(),
provider_configs.clone(),
show_tool_results, show_tool_results,
); default_timezone,
let memory_maintenance = provider_config,
MemoryMaintenanceCoordinator::new(store.clone(), provider_configs.clone()); provider_configs,
Ok(Self {
tools,
skills, skills,
store, )
show_tool_results,
lifecycle,
cli_sessions,
messages,
scheduled_tasks,
memory_maintenance,
})
} }
pub fn tools(&self) -> Arc<ToolRegistry> { pub fn tools(&self) -> Arc<ToolRegistry> {

View File

@ -2,7 +2,7 @@ use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
use crate::skills::SkillRuntime; use crate::skills::SkillRuntime;
use crate::storage::SessionStore; use crate::storage::{MemoryRepository, SchedulerJobRepository, SkillEventRepository};
use crate::tools::{ use crate::tools::{
BashTool, CalculatorTool, FileEditTool, FileReadTool, FileWriteTool, HttpRequestTool, BashTool, CalculatorTool, FileEditTool, FileReadTool, FileWriteTool, HttpRequestTool,
MemoryManageTool, MemorySearchTool, SchedulerManageTool, SkillActivateTool, SkillListTool, MemoryManageTool, MemorySearchTool, SchedulerManageTool, SkillActivateTool, SkillListTool,
@ -11,7 +11,9 @@ use crate::tools::{
pub(crate) struct ToolRegistryFactory { pub(crate) struct ToolRegistryFactory {
skills: Arc<SkillRuntime>, skills: Arc<SkillRuntime>,
store: Arc<SessionStore>, memories: Arc<dyn MemoryRepository>,
scheduler_jobs: Arc<dyn SchedulerJobRepository>,
skill_events: Arc<dyn SkillEventRepository>,
known_agents: HashSet<String>, known_agents: HashSet<String>,
default_timezone: String, default_timezone: String,
} }
@ -19,13 +21,17 @@ pub(crate) struct ToolRegistryFactory {
impl ToolRegistryFactory { impl ToolRegistryFactory {
pub(crate) fn new( pub(crate) fn new(
skills: Arc<SkillRuntime>, skills: Arc<SkillRuntime>,
store: Arc<SessionStore>, memories: Arc<dyn MemoryRepository>,
scheduler_jobs: Arc<dyn SchedulerJobRepository>,
skill_events: Arc<dyn SkillEventRepository>,
known_agents: HashSet<String>, known_agents: HashSet<String>,
default_timezone: String, default_timezone: String,
) -> Self { ) -> Self {
Self { Self {
skills, skills,
store, memories,
scheduler_jobs,
skill_events,
known_agents, known_agents,
default_timezone, default_timezone,
} }
@ -38,15 +44,15 @@ impl ToolRegistryFactory {
registry.register(FileReadTool::new()); registry.register(FileReadTool::new());
registry.register(FileWriteTool::new()); registry.register(FileWriteTool::new());
registry.register(FileEditTool::new()); registry.register(FileEditTool::new());
registry.register(MemorySearchTool::new(self.store.clone())); registry.register(MemorySearchTool::new(self.memories.clone()));
registry.register(MemoryManageTool::new(self.store.clone())); registry.register(MemoryManageTool::new(self.memories.clone()));
registry.register(SchedulerManageTool::new( registry.register(SchedulerManageTool::new(
self.store.clone(), self.scheduler_jobs.clone(),
self.known_agents.clone(), self.known_agents.clone(),
)); ));
registry.register(SkillActivateTool::new( registry.register(SkillActivateTool::new(
self.skills.clone(), self.skills.clone(),
self.store.clone(), self.skill_events.clone(),
)); ));
registry.register(SkillListTool::new(self.skills.clone())); registry.register(SkillListTool::new(self.skills.clone()));
registry.register(SkillManageTool::new(self.skills.clone())); registry.register(SkillManageTool::new(self.skills.clone()));

View File

@ -5,12 +5,16 @@ pub mod traits;
pub use self::anthropic::AnthropicProvider; pub use self::anthropic::AnthropicProvider;
pub use self::openai::OpenAIProvider; pub use self::openai::OpenAIProvider;
use crate::config::LLMProviderConfig;
pub use crate::domain::messages::ToolCall; pub use crate::domain::messages::ToolCall;
pub use crate::domain::tools::{Tool, ToolFunction}; pub use crate::domain::tools::{Tool, ToolFunction};
pub use traits::{ChatCompletionRequest, ChatCompletionResponse, LLMProvider, Message, Usage}; pub use traits::{
ChatCompletionRequest, ChatCompletionResponse, LLMProvider, Message, ProviderRuntimeConfig,
Usage,
};
pub fn create_provider(config: LLMProviderConfig) -> Result<Box<dyn LLMProvider>, ProviderError> { pub fn create_provider(
config: ProviderRuntimeConfig,
) -> Result<Box<dyn LLMProvider>, ProviderError> {
match config.provider_type.as_str() { match config.provider_type.as_str() {
"openai" => Ok(Box::new(OpenAIProvider::new( "openai" => Ok(Box::new(OpenAIProvider::new(
config.name, config.name,

View File

@ -2,6 +2,21 @@ use crate::domain::messages::{ContentBlock, ToolCall};
use crate::domain::tools::Tool; use crate::domain::tools::Tool;
use async_trait::async_trait; use async_trait::async_trait;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ProviderRuntimeConfig {
pub provider_type: String,
pub name: String,
pub base_url: String,
pub api_key: String,
pub extra_headers: HashMap<String, String>,
pub llm_timeout_secs: u64,
pub model_id: String,
pub temperature: Option<f32>,
pub max_tokens: Option<u32>,
pub model_extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message { pub struct Message {