添加qiming-rcoder模块

This commit is contained in:
Codex
2026-06-01 13:54:52 +08:00
parent 8092c4b1f8
commit 4b1a580132
539 changed files with 151650 additions and 0 deletions

View File

@@ -0,0 +1,348 @@
//! 默认 Agent 配置定义
//!
//! 此模块从编译时嵌入的 JSON 配置文件加载默认配置。
//! 支持两种服务类型的配置:
//! - RCoder: `configs/default_agents.json`
//! - ComputerAgentRunner: `configs/computer_agent_default.json`
use std::collections::HashMap;
use std::sync::LazyLock;
use crate::types::agent_config::AgentConfig;
use crate::types::mcp_config::ContextServerConfig;
/// 编译时嵌入的 RCoder 默认配置 JSON
const DEFAULT_CONFIG_JSON: &str = include_str!("../../configs/default_agents.json");
/// 编译时嵌入的 ComputerAgentRunner 默认配置 JSON
const COMPUTER_AGENT_CONFIG_JSON: &str = include_str!("../../configs/computer_agent_default.json");
/// Claude Code ACP Agent 的默认 ID
pub const CLAUDE_CODE_ACP_AGENT_ID: &str = "claude-code-acp-ts";
/// 默认配置的内部结构(用于 JSON 反序列化)
#[derive(serde::Deserialize)]
pub struct EmbeddedConfig {
agent_servers: HashMap<String, AgentConfig>,
#[serde(default)]
context_servers: HashMap<String, ContextServerConfig>,
}
/// 懒加载的默认配置
///
/// 在首次访问时解析 JSON之后复用解析结果。
/// 如果 JSON 解析失败,程序会 panic这是编译时嵌入的文件不应失败
static DEFAULT_CONFIG: LazyLock<EmbeddedConfig> = LazyLock::new(|| {
serde_json::from_str(DEFAULT_CONFIG_JSON).expect(
"Failed to parse embedded default_agents.json. This is a bug - please check the JSON syntax.",
)
});
/// 懒加载的 ComputerAgentRunner 默认配置
///
/// 在首次访问时解析 JSON之后复用解析结果。
static COMPUTER_AGENT_CONFIG: LazyLock<EmbeddedConfig> = LazyLock::new(|| {
serde_json::from_str(COMPUTER_AGENT_CONFIG_JSON).expect(
"Failed to parse embedded computer_agent_default.json. This is a bug - please check the JSON syntax.",
)
});
/// 获取默认的 Claude Code ACP Agent 配置
///
/// 从 `configs/default_agents.json` 加载配置。
/// 修改 JSON 文件后重新编译即可生效。
///
/// # Panics
/// 如果 JSON 中不存在 `claude-code-acp-ts` 配置,会 panic。
pub fn default_claude_code_agent() -> AgentConfig {
DEFAULT_CONFIG
.agent_servers
.get(CLAUDE_CODE_ACP_AGENT_ID)
.cloned()
.expect("claude-code-acp-ts not found in default_agents.json")
}
/// 获取默认的 Context Servers 配置
///
/// 从 `configs/default_agents.json` 加载配置。
/// 修改 JSON 文件后重新编译即可生效。
pub fn default_context_servers() -> HashMap<String, ContextServerConfig> {
DEFAULT_CONFIG.context_servers.clone()
}
/// 获取默认的 Agent Servers 配置
///
/// 从 `configs/default_agents.json` 加载配置。
/// 修改 JSON 文件后重新编译即可生效。
pub fn default_agent_servers() -> HashMap<String, AgentConfig> {
DEFAULT_CONFIG.agent_servers.clone()
}
/// 获取指定 Agent 的默认配置
///
/// # 参数
/// - `agent_id`: Agent 标识符
///
/// # 返回
/// 如果存在返回配置的克隆,否则返回 None
pub fn get_default_agent(agent_id: &str) -> Option<AgentConfig> {
DEFAULT_CONFIG.agent_servers.get(agent_id).cloned()
}
/// 获取指定 Context Server 的默认配置
///
/// # 参数
/// - `name`: Context Server 名称
///
/// # 返回
/// 如果存在返回配置的克隆,否则返回 None
pub fn get_default_context_server(name: &str) -> Option<ContextServerConfig> {
DEFAULT_CONFIG.context_servers.get(name).cloned()
}
/// 根据服务类型获取默认配置
///
/// # 参数
/// - `service_type`: 服务类型RCoder 或 ComputerAgentRunner
///
/// # 返回
/// 对应服务类型的默认配置引用
pub fn get_default_config_by_service_type(
service_type: &shared_types::ServiceType,
) -> &'static EmbeddedConfig {
match service_type {
shared_types::ServiceType::RCoder => &DEFAULT_CONFIG,
shared_types::ServiceType::ComputerAgentRunner => &COMPUTER_AGENT_CONFIG,
}
}
/// 根据服务类型获取默认 Agent Servers 配置
///
/// # 参数
/// - `service_type`: 服务类型
///
/// # 返回
/// Agent Servers 配置的克隆
pub fn default_agent_servers_for_service(
service_type: &shared_types::ServiceType,
) -> HashMap<String, AgentConfig> {
get_default_config_by_service_type(service_type)
.agent_servers
.clone()
}
/// 根据服务类型获取默认 Context Servers 配置
///
/// # 参数
/// - `service_type`: 服务类型
///
/// # 返回
/// Context Servers 配置的克隆
pub fn default_context_servers_for_service(
service_type: &shared_types::ServiceType,
) -> HashMap<String, ContextServerConfig> {
get_default_config_by_service_type(service_type)
.context_servers
.clone()
}
/// 根据服务类型获取指定 Agent 的默认配置
///
/// # 参数
/// - `agent_id`: Agent 标识符
/// - `service_type`: 服务类型
///
/// # 返回
/// 如果存在返回配置的克隆,否则返回 None
pub fn get_default_agent_for_service(
agent_id: &str,
service_type: &shared_types::ServiceType,
) -> Option<AgentConfig> {
get_default_config_by_service_type(service_type)
.agent_servers
.get(agent_id)
.cloned()
}
/// 根据服务类型获取指定 Context Server 的默认配置
///
/// # 参数
/// - `name`: Context Server 名称
/// - `service_type`: 服务类型
///
/// # 返回
/// 如果存在返回配置的克隆,否则返回 None
pub fn get_default_context_server_for_service(
name: &str,
service_type: &shared_types::ServiceType,
) -> Option<ContextServerConfig> {
get_default_config_by_service_type(service_type)
.context_servers
.get(name)
.cloned()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config_loads_successfully() {
// 确保 JSON 能成功解析
let _ = &*DEFAULT_CONFIG;
}
#[test]
fn test_default_claude_code_agent() {
let agent = default_claude_code_agent();
assert_eq!(agent.agent_id, CLAUDE_CODE_ACP_AGENT_ID);
assert_eq!(agent.command, "claude-code-acp-ts");
assert!(agent.enabled);
assert!(agent.env.contains_key("ANTHROPIC_API_KEY"));
}
#[test]
fn test_default_context_servers() {
let servers = default_context_servers();
// 验证 JSON 中定义的 context servers
assert!(servers.contains_key("fetch") || servers.contains_key("context7"));
}
#[test]
fn test_default_agent_servers() {
let agents = default_agent_servers();
assert!(agents.contains_key(CLAUDE_CODE_ACP_AGENT_ID));
}
#[test]
fn test_get_default_agent() {
let agent = get_default_agent(CLAUDE_CODE_ACP_AGENT_ID);
assert!(agent.is_some());
assert_eq!(agent.unwrap().agent_id, CLAUDE_CODE_ACP_AGENT_ID);
let non_existent = get_default_agent("non-existent-agent");
assert!(non_existent.is_none());
}
#[test]
fn test_get_default_context_server() {
// 测试存在的 server
let servers = default_context_servers();
if let Some(first_server_name) = servers.keys().next() {
let server = get_default_context_server(first_server_name);
assert!(server.is_some());
}
// 测试不存在的 server
let non_existent = get_default_context_server("non-existent-server");
assert!(non_existent.is_none());
}
#[test]
fn test_computer_agent_config_loads_successfully() {
// 确保 ComputerAgentRunner 配置能成功解析
let _ = &*COMPUTER_AGENT_CONFIG;
}
#[test]
fn test_get_default_config_by_service_type() {
// 测试 RCoder 配置
let rcoder_config = get_default_config_by_service_type(&shared_types::ServiceType::RCoder);
assert!(
rcoder_config
.agent_servers
.contains_key(CLAUDE_CODE_ACP_AGENT_ID)
);
// 测试 ComputerAgentRunner 配置
let car_config =
get_default_config_by_service_type(&shared_types::ServiceType::ComputerAgentRunner);
assert!(
car_config
.agent_servers
.contains_key(CLAUDE_CODE_ACP_AGENT_ID)
);
// ComputerAgentRunner 应该有更多 context servers
assert!(
car_config.context_servers.contains_key("chrome-devtools")
|| car_config.context_servers.contains_key("puppeteer")
);
}
#[test]
fn test_default_agent_servers_for_service() {
// RCoder
let rcoder_servers = default_agent_servers_for_service(&shared_types::ServiceType::RCoder);
assert!(!rcoder_servers.is_empty());
// ComputerAgentRunner
let car_servers =
default_agent_servers_for_service(&shared_types::ServiceType::ComputerAgentRunner);
assert!(!car_servers.is_empty());
}
#[test]
fn test_default_context_servers_for_service() {
// RCoder - 基础 MCP servers
let rcoder_contexts =
default_context_servers_for_service(&shared_types::ServiceType::RCoder);
assert!(rcoder_contexts.contains_key("fetch") || rcoder_contexts.contains_key("context7"));
// ComputerAgentRunner - 增强 MCP servers
let car_contexts =
default_context_servers_for_service(&shared_types::ServiceType::ComputerAgentRunner);
// 至少应该包含浏览器相关的 server
assert!(car_contexts.len() >= rcoder_contexts.len());
}
#[test]
fn test_get_default_agent_for_service() {
// 测试 RCoder
let agent = get_default_agent_for_service(
CLAUDE_CODE_ACP_AGENT_ID,
&shared_types::ServiceType::RCoder,
);
assert!(agent.is_some());
// 测试 ComputerAgentRunner
let agent = get_default_agent_for_service(
CLAUDE_CODE_ACP_AGENT_ID,
&shared_types::ServiceType::ComputerAgentRunner,
);
assert!(agent.is_some());
// 测试不存在的 agent
let agent =
get_default_agent_for_service("non-existent-agent", &shared_types::ServiceType::RCoder);
assert!(agent.is_none());
}
#[test]
fn test_get_default_context_server_for_service() {
// RCoder context server
let rcoder_contexts =
default_context_servers_for_service(&shared_types::ServiceType::RCoder);
if let Some(first_server_name) = rcoder_contexts.keys().next() {
let server = get_default_context_server_for_service(
first_server_name,
&shared_types::ServiceType::RCoder,
);
assert!(server.is_some());
}
// ComputerAgentRunner context server
let _server = get_default_context_server_for_service(
"chrome-devtools",
&shared_types::ServiceType::ComputerAgentRunner,
);
// 可能存在或不存在,取决于配置
// 测试不存在的 server
let server = get_default_context_server_for_service(
"non-existent-server",
&shared_types::ServiceType::RCoder,
);
assert!(server.is_none());
}
}

View File

@@ -0,0 +1,13 @@
//! Configuration management module.
//!
//! - `default_agent_config`: 默认 Agent 配置定义
//! - `servers_config`: 配置文件加载和管理
//! - `prompt_assembler`: 提示词配置组装工具
pub mod default_agent_config;
pub mod prompt_assembler;
pub mod servers_config;
pub use default_agent_config::*;
pub use prompt_assembler::PromptConfigAssembler;
pub use servers_config::*;

View File

@@ -0,0 +1,384 @@
//! 提示词配置组装工具
//!
//! 将用户入参组装为内部使用的配置结构。
//! 简化设计:直接使用入参,无优先级冲突。
use shared_types::{ChatAgentConfig, ChatAgentServerConfig};
use std::collections::HashMap;
use super::servers_config::AgentServersConfig;
use crate::types::agent_config::AgentConfig;
use crate::types::mcp_config::ContextServerConfig;
/// 提示词配置组装器
///
/// 职责:
/// 1. 组装系统提示词(入参 > 默认配置)
/// 2. 应用用户提示词模板
/// 3. 组装 Agent 服务器配置
/// 4. 合并 MCP 服务器配置
pub struct PromptConfigAssembler {
/// 系统提示词入参
system_prompt: Option<String>,
/// 用户提示词模板入参
user_prompt_template: Option<String>,
/// Agent 运行时配置入参
agent_config: Option<ChatAgentConfig>,
/// 默认配置
default_config: AgentServersConfig,
}
impl PromptConfigAssembler {
/// 创建新的配置组装器
///
/// # 参数
/// - `default_config`: 默认配置(从文件或内置配置加载)
pub fn new(default_config: AgentServersConfig) -> Self {
Self {
system_prompt: None,
user_prompt_template: None,
agent_config: None,
default_config,
}
}
/// 设置系统提示词覆盖
pub fn with_system_prompt(mut self, system_prompt: Option<String>) -> Self {
self.system_prompt = system_prompt;
self
}
/// 设置用户提示词模板覆盖
pub fn with_user_prompt_template(mut self, template: Option<String>) -> Self {
self.user_prompt_template = template;
self
}
/// 设置 Agent 运行时配置覆盖
pub fn with_agent_config(mut self, config: Option<ChatAgentConfig>) -> Self {
self.agent_config = config;
self
}
/// 获取最终的系统提示词
///
/// 逻辑:入参有值则使用入参,否则使用默认配置
pub fn get_system_prompt(&self, agent_id: &str) -> String {
// 入参有值且非空,直接使用
if let Some(ref sp) = self.system_prompt {
if !sp.is_empty() {
return sp.clone();
}
}
// 使用默认配置
self.default_config.get_system_prompt(agent_id)
}
/// 应用用户提示词模板
///
/// 逻辑:
/// 1. 如果有模板入参,使用模板替换 `{user_prompt}`
/// 2. 如果没有模板入参,检查默认配置
/// 3. 都没有,直接返回原始输入
pub fn apply_user_prompt(&self, agent_id: &str, user_input: &str) -> String {
// 入参有模板且非空,使用入参模板
if let Some(ref template) = self.user_prompt_template {
if !template.is_empty() {
return template.replace("{user_prompt}", user_input);
}
}
// 检查默认配置中的 user_prompt 模板
if let Some(agent) = self.default_config.get_agent(agent_id) {
if let Some(ref prompt_config) = agent.user_prompt {
if prompt_config.enabled {
return prompt_config.apply(user_input);
}
}
}
// 无模板,直接返回原始输入
user_input.to_string()
}
/// 获取最终的 Agent 服务器配置
///
/// 逻辑:
/// 1. 如果入参有 agent_server与默认配置合并入参字段覆盖默认值
/// 2. 如果入参没有 agent_server使用默认配置
pub fn get_agent_server_config(&self, default_agent_id: &str) -> AgentConfig {
// 获取默认的 Agent 配置
let default_agent = self
.default_config
.get_agent(default_agent_id)
.cloned()
.unwrap_or_default();
// 如果入参有 agent_server 配置,合并覆盖
if let Some(ref config) = self.agent_config {
if let Some(ref agent_server) = config.agent_server {
return self.merge_agent_config(&default_agent, agent_server);
}
}
// 使用默认配置
default_agent
}
/// 合并 Agent 配置(入参覆盖默认值)
fn merge_agent_config(
&self,
default: &AgentConfig,
override_config: &ChatAgentServerConfig,
) -> AgentConfig {
let mut merged = default.clone();
// agent_id: 入参有值则覆盖
if let Some(ref agent_id) = override_config.agent_id {
merged.agent_id = agent_id.clone();
}
// command: 入参有值则覆盖
if let Some(ref command) = override_config.command {
merged.command = command.clone();
}
// args: 入参有值则覆盖(替换而非追加)
if let Some(ref args) = override_config.args {
merged.args = args.clone();
}
// env: 入参有值则合并(入参优先)
if let Some(ref env) = override_config.env {
for (key, value) in env {
merged.env.insert(key.clone(), value.clone());
}
}
// metadata: 入参有值则合并
if let Some(ref metadata) = override_config.metadata {
for (key, value) in metadata {
merged.metadata.insert(key.clone(), value.clone());
}
}
merged
}
/// 获取最终的 MCP 服务器配置
///
/// 逻辑:
/// 1. 如果入参明确提供了非空的 context_servers使用入参
/// 2. 否则使用默认配置
///
/// 注意:即使提供了 agent_config但 context_servers 为空时,仍使用默认配置
pub fn get_context_servers(&self) -> HashMap<String, ContextServerConfig> {
// 入参有非空的 MCP 配置,使用入参
if let Some(ref config) = self.agent_config {
if config.has_context_servers() {
return config
.context_servers
.iter()
.map(|(name, chat_config)| {
let ctx_config = ContextServerConfig {
source: chat_config.source.clone(),
enabled: chat_config.enabled,
command: chat_config.command.clone(),
args: chat_config.args.clone(),
env: chat_config.env.clone(),
};
(name.clone(), ctx_config)
})
.collect();
}
}
// context_servers 为空或未提供,使用默认配置
self.default_config.context_servers.clone()
}
/// 获取使用的 Agent ID
///
/// 逻辑:入参有指定则使用入参,否则使用默认
pub fn get_agent_id(&self, default_agent_id: &str) -> String {
if let Some(ref config) = self.agent_config {
if let Some(ref agent_server) = config.agent_server {
return agent_server.get_agent_id().to_string();
}
}
default_agent_id.to_string()
}
/// 检查是否有系统提示词覆盖
pub fn has_system_prompt_override(&self) -> bool {
self.system_prompt.as_ref().is_some_and(|s| !s.is_empty())
}
/// 检查是否有用户提示词模板覆盖
pub fn has_user_prompt_template_override(&self) -> bool {
self.user_prompt_template
.as_ref()
.is_some_and(|s| !s.is_empty())
}
/// 检查是否有 Agent 配置覆盖
pub fn has_agent_config_override(&self) -> bool {
self.agent_config.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_default_config() -> AgentServersConfig {
AgentServersConfig::default()
}
#[test]
fn test_system_prompt_with_override() {
let config = create_test_default_config();
let assembler = PromptConfigAssembler::new(config)
.with_system_prompt(Some("自定义系统提示词".to_string()));
let result = assembler.get_system_prompt("claude-code-acp-ts");
assert_eq!(result, "自定义系统提示词");
}
#[test]
fn test_system_prompt_without_override() {
let config = create_test_default_config();
let assembler = PromptConfigAssembler::new(config);
let result = assembler.get_system_prompt("claude-code-acp-ts");
// 应该返回默认系统提示词
assert!(!result.is_empty());
}
#[test]
fn test_system_prompt_empty_override() {
let config = create_test_default_config();
let assembler = PromptConfigAssembler::new(config).with_system_prompt(Some("".to_string()));
let result = assembler.get_system_prompt("claude-code-acp-ts");
// 空字符串应该回退到默认值
assert!(!result.is_empty());
}
#[test]
fn test_user_prompt_with_template() {
let config = create_test_default_config();
let assembler = PromptConfigAssembler::new(config)
.with_user_prompt_template(Some("请用 Rust 完成:{user_prompt}".to_string()));
let result = assembler.apply_user_prompt("claude-code-acp-ts", "Hello World");
assert_eq!(result, "请用 Rust 完成Hello World");
}
#[test]
fn test_user_prompt_without_template() {
let config = create_test_default_config();
let assembler = PromptConfigAssembler::new(config);
let result = assembler.apply_user_prompt("claude-code-acp-ts", "Hello World");
// 没有模板时应该返回原始输入
assert_eq!(result, "Hello World");
}
#[test]
fn test_context_servers_with_override() {
let config = create_test_default_config();
let mut context_servers = HashMap::new();
context_servers.insert(
"my-mcp".to_string(),
shared_types::ChatContextServerConfig {
source: "custom".to_string(),
enabled: true,
command: Some("bunx".to_string()),
args: Some(vec!["-y".to_string(), "my-mcp-server".to_string()]),
env: None,
},
);
let agent_config = ChatAgentConfig {
agent_server: None,
context_servers,
resource_limits: None,
};
let assembler = PromptConfigAssembler::new(config).with_agent_config(Some(agent_config));
let result = assembler.get_context_servers();
assert!(result.contains_key("my-mcp"));
assert_eq!(
result.get("my-mcp").unwrap().command,
Some("bunx".to_string())
);
}
#[test]
fn test_context_servers_without_override() {
let config = create_test_default_config();
let assembler = PromptConfigAssembler::new(config);
let result = assembler.get_context_servers();
// 应该返回默认的 context servers
assert!(!result.is_empty());
}
#[test]
fn test_agent_id_with_override() {
let config = create_test_default_config();
let agent_config = ChatAgentConfig {
agent_server: Some(shared_types::ChatAgentServerConfig {
agent_id: Some("custom-agent".to_string()),
command: None,
args: None,
env: None,
metadata: None,
}),
context_servers: HashMap::new(),
resource_limits: None,
};
let assembler = PromptConfigAssembler::new(config).with_agent_config(Some(agent_config));
let result = assembler.get_agent_id("claude-code-acp-ts");
assert_eq!(result, "custom-agent");
}
#[test]
fn test_agent_id_without_override() {
let config = create_test_default_config();
let assembler = PromptConfigAssembler::new(config);
let result = assembler.get_agent_id("claude-code-acp-ts");
assert_eq!(result, "claude-code-acp-ts");
}
#[test]
fn test_merge_agent_config_env() {
let config = create_test_default_config();
let mut override_env = HashMap::new();
override_env.insert("NEW_VAR".to_string(), "new_value".to_string());
let agent_config = ChatAgentConfig {
agent_server: Some(shared_types::ChatAgentServerConfig {
agent_id: None,
command: None,
args: None,
env: Some(override_env),
metadata: None,
}),
context_servers: HashMap::new(),
resource_limits: None,
};
let assembler = PromptConfigAssembler::new(config).with_agent_config(Some(agent_config));
let result = assembler.get_agent_server_config("claude-code-acp-ts");
assert!(result.env.contains_key("NEW_VAR"));
assert_eq!(result.env.get("NEW_VAR"), Some(&"new_value".to_string()));
}
}

View File

@@ -0,0 +1,226 @@
//! Agent servers configuration structure.
//!
//! 此模块负责配置的查询和管理。
//! 默认配置从 `default_agent_config` 模块加载(来源于 `configs/default_agents.json`)。
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use super::default_agent_config::{
default_agent_servers, default_agent_servers_for_service, default_context_servers,
default_context_servers_for_service,
};
use crate::types::agent_config::AgentConfig;
use crate::types::error::{ConfigError, Result};
use crate::types::mcp_config::ContextServerConfig;
use crate::types::system_prompt::DEFAULT_SYSTEM_PROMPT;
/// Agent servers configuration
///
/// 包含所有 Agent 和 Context Server 的配置集合。
/// 默认配置来自 `configs/default_agents.json`(编译时嵌入)。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentServersConfig {
/// Agent servers configuration
pub agent_servers: HashMap<String, AgentConfig>,
/// Context servers configuration (MCP servers)
#[serde(default)]
pub context_servers: HashMap<String, ContextServerConfig>,
}
impl AgentServersConfig {
/// 获取默认配置
///
/// 返回从 `configs/default_agents.json` 加载的配置。
///
/// # 已弃用
///
/// 此方法已弃用,请使用 `load_or_default_for_service` 以支持多服务类型。
#[deprecated(
since = "0.2.0",
note = "请使用 load_or_default_for_service 以支持多服务类型"
)]
pub async fn load_or_default() -> Self {
Self::default()
}
/// 根据服务类型加载或使用默认配置
///
/// # 参数
/// - `service_type`: 服务类型RCoder 或 ComputerAgentRunner
///
/// # 返回
/// 对应服务类型的默认配置
pub async fn load_or_default_for_service(service_type: &shared_types::ServiceType) -> Self {
Self::default_for_service(service_type)
}
/// 根据服务类型创建默认配置
///
/// # 参数
/// - `service_type`: 服务类型
///
/// # 返回
/// 对应服务类型的默认配置实例
pub fn default_for_service(service_type: &shared_types::ServiceType) -> Self {
Self {
agent_servers: default_agent_servers_for_service(service_type),
context_servers: default_context_servers_for_service(service_type),
}
}
/// 从外部文件加载配置
///
/// 用于加载用户自定义的配置文件,覆盖默认配置。
pub async fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
if !path.exists() {
return Err(ConfigError::file_not_found(path.display()).into());
}
let content = tokio::fs::read_to_string(path)
.await
.map_err(|e| ConfigError::Read(format!("{}: {}", path.display(), e)))?;
Self::from_json(&content)
}
/// 从 JSON 字符串加载配置
///
/// 用于解析用户传入的 JSON 配置。
pub fn from_json(json: &str) -> Result<Self> {
serde_json::from_str(json).map_err(|e| ConfigError::Deserialization(e.to_string()).into())
}
/// Validate configuration
pub fn validate(&self) -> Result<()> {
// Validate agent servers
for (name, config) in &self.agent_servers {
if config.agent_id.is_empty() {
return Err(
ConfigError::missing_field(format!("agent_servers.{}.agent_id", name)).into(),
);
}
if config.command.is_empty() {
return Err(
ConfigError::missing_field(format!("agent_servers.{}.command", name)).into(),
);
}
}
// Validate context servers
for (name, config) in &self.context_servers {
if config.command.is_none() {
return Err(ConfigError::missing_field(format!(
"context_servers.{}.command",
name
))
.into());
}
}
Ok(())
}
/// Get all enabled agents
pub fn get_enabled_agents(&self) -> Vec<&AgentConfig> {
self.agent_servers
.values()
.filter(|config| config.enabled)
.collect()
}
/// Get an agent configuration by ID
pub fn get_agent(&self, agent_id: &str) -> Option<&AgentConfig> {
self.agent_servers.get(agent_id)
}
/// 获取系统提示词(优先使用配置,否则使用默认值)
///
/// # 参数
/// - `agent_id`: Agent 标识符
///
/// # 返回
/// 根据配置的 source 字段决定返回内容:
/// - `source: "embedded"` -> 返回编译时嵌入的默认提示词
/// - `source: "custom"` 且 template 非空 -> 返回自定义模板内容
/// - 其他情况 -> 回退到编译时嵌入的默认提示词
///
/// 注意:此方法始终返回有效的系统提示词,不受 enabled 字段影响。
/// 如需检查是否启用,请直接调用 `SystemPromptConfig::get_prompt()`。
pub fn get_system_prompt(&self, agent_id: &str) -> String {
if let Some(agent) = self.get_agent(agent_id) {
// 如果有系统提示词配置,使用 get_prompt_or_default() 确保始终返回有效提示词
if let Some(ref prompt_config) = agent.system_prompt {
return prompt_config.get_prompt_or_default().to_string();
}
}
// 回退到编译时嵌入的默认值
DEFAULT_SYSTEM_PROMPT.to_string()
}
/// Get all enabled context servers
pub fn get_enabled_context_servers(&self) -> Vec<&ContextServerConfig> {
self.context_servers
.values()
.filter(|config| config.enabled)
.collect()
}
/// Get a context server configuration by name
pub fn get_context_server(&self, name: &str) -> Option<&ContextServerConfig> {
self.context_servers.get(name)
}
/// 创建默认配置
///
/// 从 `configs/default_agents.json` 加载的配置(编译时嵌入)。
/// 修改 JSON 文件后重新编译即可生效。
pub fn default() -> Self {
Self {
agent_servers: default_agent_servers(),
context_servers: default_context_servers(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_load_or_default_for_service() {
// 测试 RCoder
let rcoder_config =
AgentServersConfig::load_or_default_for_service(&shared_types::ServiceType::RCoder)
.await;
assert!(!rcoder_config.agent_servers.is_empty());
// 测试 ComputerAgentRunner
let car_config = AgentServersConfig::load_or_default_for_service(
&shared_types::ServiceType::ComputerAgentRunner,
)
.await;
assert!(!car_config.agent_servers.is_empty());
}
#[test]
fn test_default_for_service() {
// 测试 RCoder
let rcoder_config =
AgentServersConfig::default_for_service(&shared_types::ServiceType::RCoder);
assert!(!rcoder_config.agent_servers.is_empty());
// 测试 ComputerAgentRunner
let car_config = AgentServersConfig::default_for_service(
&shared_types::ServiceType::ComputerAgentRunner,
);
assert!(!car_config.agent_servers.is_empty());
// 验证配置不同
// ComputerAgentRunner 应该有更多或不同的 context servers
assert!(car_config.context_servers.len() >= rcoder_config.context_servers.len());
}
}