feat: 集成专家提示词到网关配置、依赖注入与HTTP API
- config: 新增 ExpertsConfig,默认 sources=[user,project], enabled=true - AgentFactory: 新增 experts 字段,providers 顺序为 Agent→Skill→Expert→Subagent→Todo - GatewayState: 持有 Arc<ExpertRuntime>,from_config 中初始化 - runtime: build_session_manager_with_sender 接收 experts 参数 - 路由: 注册 7 个专家 API (list/toggle/create/update/delete/selected/select) - http: 实现 7 个处理器,支持专家 CRUD 与 session 级选择 - session/cli: 测试便利构造器与 empty_config/build_config 补齐 experts 字段
This commit is contained in:
parent
c2d9bf8b5e
commit
9d7c1f2e52
@ -79,6 +79,7 @@ impl InitWizard {
|
|||||||
mcp_servers: HashMap::new(),
|
mcp_servers: HashMap::new(),
|
||||||
image_context: crate::config::ImageContextConfig::default(),
|
image_context: crate::config::ImageContextConfig::default(),
|
||||||
subagents: crate::config::SubagentsConfig::default(),
|
subagents: crate::config::SubagentsConfig::default(),
|
||||||
|
experts: crate::config::ExpertsConfig::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -832,6 +833,7 @@ impl InitWizard {
|
|||||||
mcp_servers: existing.mcp_servers.clone(),
|
mcp_servers: existing.mcp_servers.clone(),
|
||||||
image_context: existing.image_context.clone(),
|
image_context: existing.image_context.clone(),
|
||||||
subagents: existing.subagents.clone(),
|
subagents: existing.subagents.clone(),
|
||||||
|
experts: existing.experts.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -38,6 +38,8 @@ pub struct Config {
|
|||||||
pub image_context: ImageContextConfig,
|
pub image_context: ImageContextConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub subagents: SubagentsConfig,
|
pub subagents: SubagentsConfig,
|
||||||
|
#[serde(default)]
|
||||||
|
pub experts: ExpertsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 图片上下文限制配置
|
/// 图片上下文限制配置
|
||||||
@ -169,6 +171,34 @@ impl Default for SubagentsConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 专家提示词配置
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct ExpertsConfig {
|
||||||
|
/// 是否启用专家发现与注入
|
||||||
|
#[serde(default = "default_experts_enabled")]
|
||||||
|
pub enabled: bool,
|
||||||
|
/// 定义来源优先级
|
||||||
|
#[serde(default = "default_experts_sources")]
|
||||||
|
pub sources: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_experts_enabled() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_experts_sources() -> Vec<String> {
|
||||||
|
vec!["user".to_string(), "project".to_string()]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ExpertsConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: default_experts_enabled(),
|
||||||
|
sources: default_experts_sources(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||||
pub struct ToolsConfig {
|
pub struct ToolsConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|||||||
@ -2,6 +2,8 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use crate::agent::{AgentError, AgentLoop, CompositeSystemPromptProvider};
|
use crate::agent::{AgentError, AgentLoop, CompositeSystemPromptProvider};
|
||||||
use crate::config::LLMProviderConfig;
|
use crate::config::LLMProviderConfig;
|
||||||
|
use crate::experts::ExpertPromptProvider;
|
||||||
|
use crate::experts::ExpertRuntime;
|
||||||
use crate::gateway::agent_prompt_provider::AgentPromptProvider;
|
use crate::gateway::agent_prompt_provider::AgentPromptProvider;
|
||||||
use crate::gateway::todo_prompt_provider::TodoPromptProvider;
|
use crate::gateway::todo_prompt_provider::TodoPromptProvider;
|
||||||
use crate::skills::{SkillPromptProvider, SkillRuntime};
|
use crate::skills::{SkillPromptProvider, SkillRuntime};
|
||||||
@ -14,6 +16,7 @@ use crate::tools::{ToolContext, ToolRegistry};
|
|||||||
pub(crate) struct AgentFactory {
|
pub(crate) struct AgentFactory {
|
||||||
tools: Arc<ToolRegistry>,
|
tools: Arc<ToolRegistry>,
|
||||||
skills: Arc<SkillRuntime>,
|
skills: Arc<SkillRuntime>,
|
||||||
|
experts: Arc<ExpertRuntime>,
|
||||||
subagent_runtime: Arc<SubagentRuntime>,
|
subagent_runtime: Arc<SubagentRuntime>,
|
||||||
reinject_every: usize,
|
reinject_every: usize,
|
||||||
prompt_repository: Arc<dyn PromptInjectionRepository>,
|
prompt_repository: Arc<dyn PromptInjectionRepository>,
|
||||||
@ -38,6 +41,7 @@ impl AgentFactory {
|
|||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
tools: Arc<ToolRegistry>,
|
tools: Arc<ToolRegistry>,
|
||||||
skills: Arc<SkillRuntime>,
|
skills: Arc<SkillRuntime>,
|
||||||
|
experts: Arc<ExpertRuntime>,
|
||||||
subagent_runtime: Arc<SubagentRuntime>,
|
subagent_runtime: Arc<SubagentRuntime>,
|
||||||
reinject_every: usize,
|
reinject_every: usize,
|
||||||
prompt_repository: Arc<dyn PromptInjectionRepository>,
|
prompt_repository: Arc<dyn PromptInjectionRepository>,
|
||||||
@ -52,6 +56,7 @@ impl AgentFactory {
|
|||||||
Self {
|
Self {
|
||||||
tools,
|
tools,
|
||||||
skills,
|
skills,
|
||||||
|
experts,
|
||||||
subagent_runtime,
|
subagent_runtime,
|
||||||
reinject_every,
|
reinject_every,
|
||||||
prompt_repository,
|
prompt_repository,
|
||||||
@ -74,6 +79,7 @@ impl AgentFactory {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 创建组合的系统提示词提供者
|
// 创建组合的系统提示词提供者
|
||||||
|
// 顺序:AgentPrompt → SkillPrompt → ExpertPrompt → SubagentPrompt → TodoPrompt
|
||||||
let system_prompt_provider = Arc::new(CompositeSystemPromptProvider::new(vec![
|
let system_prompt_provider = Arc::new(CompositeSystemPromptProvider::new(vec![
|
||||||
Box::new(AgentPromptProvider::new(
|
Box::new(AgentPromptProvider::new(
|
||||||
self.reinject_every,
|
self.reinject_every,
|
||||||
@ -81,6 +87,7 @@ impl AgentFactory {
|
|||||||
self.prompt_repository.clone(),
|
self.prompt_repository.clone(),
|
||||||
)),
|
)),
|
||||||
Box::new(SkillPromptProvider::new(self.skills.clone())),
|
Box::new(SkillPromptProvider::new(self.skills.clone())),
|
||||||
|
Box::new(ExpertPromptProvider::new(self.experts.clone())),
|
||||||
Box::new(SubagentPromptProvider::new(self.subagent_runtime.clone())),
|
Box::new(SubagentPromptProvider::new(self.subagent_runtime.clone())),
|
||||||
Box::new(TodoPromptProvider::new()),
|
Box::new(TodoPromptProvider::new()),
|
||||||
]));
|
]));
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
use axum::{Json, extract::State};
|
use axum::{Json, extract::{Query, State}};
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::GatewayState;
|
use super::GatewayState;
|
||||||
use crate::config::{Config, get_default_config_path};
|
use crate::config::{Config, get_default_config_path};
|
||||||
|
use crate::experts::{Expert, ExpertScope, ExpertWithStatus};
|
||||||
use crate::skills::SkillWithStatus;
|
use crate::skills::SkillWithStatus;
|
||||||
use crate::tools::task::runtime::{SubagentScope, SubagentWithStatus};
|
use crate::tools::task::runtime::{SubagentScope, SubagentWithStatus};
|
||||||
|
|
||||||
@ -410,3 +411,340 @@ pub async fn subagents_toggle(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================== Experts =====================
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ExpertToggleRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub scope: String,
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ExpertToggleResponse {
|
||||||
|
success: bool,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
changed: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
available: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
disabled_in_scopes: Option<Vec<String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ExpertListResponse {
|
||||||
|
experts_system_enabled: bool,
|
||||||
|
total: usize,
|
||||||
|
experts: Vec<ExpertWithStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ExpertCreateRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub body: String,
|
||||||
|
pub scope: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ExpertUpdateRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub scope: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub body: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ExpertDeleteRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub scope: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ExpertSelectedQuery {
|
||||||
|
pub session_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ExpertSelectedResponse {
|
||||||
|
pub expert_name: Option<String>,
|
||||||
|
pub expert: Option<ExpertWithStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ExpertSelectRequest {
|
||||||
|
pub session_id: String,
|
||||||
|
pub expert_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ExpertSelectResponse {
|
||||||
|
pub success: bool,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ExpertResponse {
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub body: String,
|
||||||
|
pub source: String,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Expert> for ExpertResponse {
|
||||||
|
fn from(expert: Expert) -> Self {
|
||||||
|
Self {
|
||||||
|
name: expert.name,
|
||||||
|
description: expert.description,
|
||||||
|
body: expert.body,
|
||||||
|
source: expert.source.as_str().to_string(),
|
||||||
|
path: expert.path.display().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ExpertDeleteResponse {
|
||||||
|
pub success: bool,
|
||||||
|
pub path: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /api/experts — Return all discovered experts with their disabled status
|
||||||
|
pub async fn experts_list(
|
||||||
|
State(state): State<Arc<GatewayState>>,
|
||||||
|
) -> Json<ExpertListResponse> {
|
||||||
|
let experts_enabled = state.config.read().await.experts.enabled;
|
||||||
|
|
||||||
|
if !experts_enabled {
|
||||||
|
return Json(ExpertListResponse {
|
||||||
|
experts_system_enabled: false,
|
||||||
|
total: 0,
|
||||||
|
experts: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let experts = state.experts.list_experts_with_status();
|
||||||
|
let total = experts.len();
|
||||||
|
|
||||||
|
Json(ExpertListResponse {
|
||||||
|
experts_system_enabled: true,
|
||||||
|
total,
|
||||||
|
experts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /api/experts/toggle — Enable or disable a specific expert
|
||||||
|
pub async fn experts_toggle(
|
||||||
|
State(state): State<Arc<GatewayState>>,
|
||||||
|
Json(req): Json<ExpertToggleRequest>,
|
||||||
|
) -> (StatusCode, Json<ExpertToggleResponse>) {
|
||||||
|
let scope = match ExpertScope::parse(&req.scope) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
return (
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Json(ExpertToggleResponse {
|
||||||
|
success: false,
|
||||||
|
changed: None,
|
||||||
|
available: None,
|
||||||
|
disabled_in_scopes: None,
|
||||||
|
error: Some(format!("invalid scope: {}", req.scope)),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = if req.enabled {
|
||||||
|
state.experts.enable_expert(scope, &req.name)
|
||||||
|
} else {
|
||||||
|
state.experts.disable_expert(scope, &req.name)
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(change) => (
|
||||||
|
StatusCode::OK,
|
||||||
|
Json(ExpertToggleResponse {
|
||||||
|
success: true,
|
||||||
|
changed: Some(change.changed),
|
||||||
|
available: Some(change.available),
|
||||||
|
disabled_in_scopes: Some(
|
||||||
|
change
|
||||||
|
.disabled_in_scopes
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_str().to_string())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
error: None,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Err(msg) => {
|
||||||
|
let status = if msg.contains("not found") {
|
||||||
|
StatusCode::NOT_FOUND
|
||||||
|
} else {
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
};
|
||||||
|
(
|
||||||
|
status,
|
||||||
|
Json(ExpertToggleResponse {
|
||||||
|
success: false,
|
||||||
|
changed: None,
|
||||||
|
available: None,
|
||||||
|
disabled_in_scopes: None,
|
||||||
|
error: Some(msg),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /api/experts/create — Create a new expert
|
||||||
|
pub async fn experts_create(
|
||||||
|
State(state): State<Arc<GatewayState>>,
|
||||||
|
Json(req): Json<ExpertCreateRequest>,
|
||||||
|
) -> Result<Json<ExpertResponse>, (StatusCode, String)> {
|
||||||
|
let scope = ExpertScope::parse(&req.scope)
|
||||||
|
.ok_or_else(|| (StatusCode::BAD_REQUEST, format!("invalid scope: {}", req.scope)))?;
|
||||||
|
|
||||||
|
let expert = state
|
||||||
|
.experts
|
||||||
|
.create_expert(scope, &req.name, &req.description, &req.body, true)
|
||||||
|
.map_err(|err| {
|
||||||
|
let status = if err.contains("already exists") {
|
||||||
|
StatusCode::CONFLICT
|
||||||
|
} else {
|
||||||
|
StatusCode::BAD_REQUEST
|
||||||
|
};
|
||||||
|
(status, err)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Json(ExpertResponse::from(expert)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PUT /api/experts/update — Update an existing expert
|
||||||
|
pub async fn experts_update(
|
||||||
|
State(state): State<Arc<GatewayState>>,
|
||||||
|
Json(req): Json<ExpertUpdateRequest>,
|
||||||
|
) -> Result<Json<ExpertResponse>, (StatusCode, String)> {
|
||||||
|
let scope = ExpertScope::parse(&req.scope)
|
||||||
|
.ok_or_else(|| (StatusCode::BAD_REQUEST, format!("invalid scope: {}", req.scope)))?;
|
||||||
|
|
||||||
|
let expert = state
|
||||||
|
.experts
|
||||||
|
.update_expert(
|
||||||
|
scope,
|
||||||
|
&req.name,
|
||||||
|
req.description.as_deref(),
|
||||||
|
req.body.as_deref(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.map_err(|err| {
|
||||||
|
let status = if err.contains("not found") {
|
||||||
|
StatusCode::NOT_FOUND
|
||||||
|
} else {
|
||||||
|
StatusCode::BAD_REQUEST
|
||||||
|
};
|
||||||
|
(status, err)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Json(ExpertResponse::from(expert)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DELETE /api/experts/delete?name=&scope= — Delete an expert
|
||||||
|
pub async fn experts_delete(
|
||||||
|
State(state): State<Arc<GatewayState>>,
|
||||||
|
Query(req): Query<ExpertDeleteRequest>,
|
||||||
|
) -> Result<Json<ExpertDeleteResponse>, (StatusCode, Json<ExpertDeleteResponse>)> {
|
||||||
|
let scope = match ExpertScope::parse(&req.scope) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
return Err((
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Json(ExpertDeleteResponse {
|
||||||
|
success: false,
|
||||||
|
path: String::new(),
|
||||||
|
error: Some(format!("invalid scope: {}", req.scope)),
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match state.experts.delete_expert(scope, &req.name, true) {
|
||||||
|
Ok(path) => Ok(Json(ExpertDeleteResponse {
|
||||||
|
success: true,
|
||||||
|
path: path.display().to_string(),
|
||||||
|
error: None,
|
||||||
|
})),
|
||||||
|
Err(msg) => {
|
||||||
|
let status = if msg.contains("not found") {
|
||||||
|
StatusCode::NOT_FOUND
|
||||||
|
} else {
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
};
|
||||||
|
Err((
|
||||||
|
status,
|
||||||
|
Json(ExpertDeleteResponse {
|
||||||
|
success: false,
|
||||||
|
path: String::new(),
|
||||||
|
error: Some(msg),
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /api/experts/selected?session_id=... — Return the currently selected expert for a session
|
||||||
|
pub async fn experts_selected(
|
||||||
|
State(state): State<Arc<GatewayState>>,
|
||||||
|
Query(q): Query<ExpertSelectedQuery>,
|
||||||
|
) -> Json<ExpertSelectedResponse> {
|
||||||
|
let expert_name = state.experts.selected_expert_name_for(&q.session_id);
|
||||||
|
|
||||||
|
let expert = expert_name.as_ref().and_then(|name| {
|
||||||
|
// Build an ExpertWithStatus from the discovered catalog.
|
||||||
|
state
|
||||||
|
.experts
|
||||||
|
.list_experts_with_status()
|
||||||
|
.into_iter()
|
||||||
|
.find(|e| &e.name == name)
|
||||||
|
});
|
||||||
|
|
||||||
|
Json(ExpertSelectedResponse {
|
||||||
|
expert_name,
|
||||||
|
expert,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /api/experts/select — Select (or clear) the expert for a session
|
||||||
|
pub async fn experts_select(
|
||||||
|
State(state): State<Arc<GatewayState>>,
|
||||||
|
Json(req): Json<ExpertSelectRequest>,
|
||||||
|
) -> (StatusCode, Json<ExpertSelectResponse>) {
|
||||||
|
let result = match req.expert_name {
|
||||||
|
Some(name) => state.experts.select_expert(&req.session_id, &name),
|
||||||
|
None => state.experts.clear_expert(&req.session_id),
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(()) => (
|
||||||
|
StatusCode::OK,
|
||||||
|
Json(ExpertSelectResponse {
|
||||||
|
success: true,
|
||||||
|
error: None,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Err(msg) => (
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Json(ExpertSelectResponse {
|
||||||
|
success: false,
|
||||||
|
error: Some(msg),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -65,6 +65,7 @@ pub struct GatewayState {
|
|||||||
pub restart_tx: watch::Sender<bool>,
|
pub restart_tx: watch::Sender<bool>,
|
||||||
pub mcp_manager: Option<Arc<crate::mcp::client::McpClientManager>>,
|
pub mcp_manager: Option<Arc<crate::mcp::client::McpClientManager>>,
|
||||||
pub skills: Arc<SkillRuntime>,
|
pub skills: Arc<SkillRuntime>,
|
||||||
|
pub experts: Arc<crate::experts::ExpertRuntime>,
|
||||||
pub subagent_runtime: Arc<SubagentRuntime>,
|
pub subagent_runtime: Arc<SubagentRuntime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ impl GatewayState {
|
|||||||
let session_ttl_hours = config.gateway.session_ttl_hours;
|
let session_ttl_hours = config.gateway.session_ttl_hours;
|
||||||
|
|
||||||
let skills = Arc::new(SkillRuntime::from_config(config.skills.clone()));
|
let skills = Arc::new(SkillRuntime::from_config(config.skills.clone()));
|
||||||
|
let experts = Arc::new(crate::experts::ExpertRuntime::from_config(config.experts.clone()));
|
||||||
let channel_manager = ChannelManager::new();
|
let channel_manager = ChannelManager::new();
|
||||||
let bus = channel_manager.bus();
|
let bus = channel_manager.bus();
|
||||||
|
|
||||||
@ -97,6 +99,7 @@ impl GatewayState {
|
|||||||
provider_config,
|
provider_config,
|
||||||
provider_configs,
|
provider_configs,
|
||||||
skills.clone(),
|
skills.clone(),
|
||||||
|
experts.clone(),
|
||||||
Arc::new(BusSessionMessageSender::new(bus.clone())),
|
Arc::new(BusSessionMessageSender::new(bus.clone())),
|
||||||
std::collections::HashSet::new(),
|
std::collections::HashSet::new(),
|
||||||
config.tools.task.clone(),
|
config.tools.task.clone(),
|
||||||
@ -125,6 +128,7 @@ impl GatewayState {
|
|||||||
restart_tx,
|
restart_tx,
|
||||||
mcp_manager,
|
mcp_manager,
|
||||||
skills,
|
skills,
|
||||||
|
experts,
|
||||||
subagent_runtime,
|
subagent_runtime,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -239,6 +243,13 @@ pub async fn run(
|
|||||||
.route("/api/skills/toggle", routing::post(http::skills_toggle))
|
.route("/api/skills/toggle", routing::post(http::skills_toggle))
|
||||||
.route("/api/subagents", routing::get(http::subagents_list))
|
.route("/api/subagents", routing::get(http::subagents_list))
|
||||||
.route("/api/subagents/toggle", routing::post(http::subagents_toggle))
|
.route("/api/subagents/toggle", routing::post(http::subagents_toggle))
|
||||||
|
.route("/api/experts", routing::get(http::experts_list))
|
||||||
|
.route("/api/experts/toggle", routing::post(http::experts_toggle))
|
||||||
|
.route("/api/experts/create", routing::post(http::experts_create))
|
||||||
|
.route("/api/experts/update", routing::put(http::experts_update))
|
||||||
|
.route("/api/experts/delete", routing::delete(http::experts_delete))
|
||||||
|
.route("/api/experts/selected", routing::get(http::experts_selected))
|
||||||
|
.route("/api/experts/select", routing::post(http::experts_select))
|
||||||
.route("/ws", routing::get(ws::ws_handler))
|
.route("/ws", routing::get(ws::ws_handler))
|
||||||
.fallback(static_handler)
|
.fallback(static_handler)
|
||||||
.with_state(state.clone())
|
.with_state(state.clone())
|
||||||
@ -253,6 +264,13 @@ pub async fn run(
|
|||||||
.route("/api/skills/toggle", routing::post(http::skills_toggle))
|
.route("/api/skills/toggle", routing::post(http::skills_toggle))
|
||||||
.route("/api/subagents", routing::get(http::subagents_list))
|
.route("/api/subagents", routing::get(http::subagents_list))
|
||||||
.route("/api/subagents/toggle", routing::post(http::subagents_toggle))
|
.route("/api/subagents/toggle", routing::post(http::subagents_toggle))
|
||||||
|
.route("/api/experts", routing::get(http::experts_list))
|
||||||
|
.route("/api/experts/toggle", routing::post(http::experts_toggle))
|
||||||
|
.route("/api/experts/create", routing::post(http::experts_create))
|
||||||
|
.route("/api/experts/update", routing::put(http::experts_update))
|
||||||
|
.route("/api/experts/delete", routing::delete(http::experts_delete))
|
||||||
|
.route("/api/experts/selected", routing::get(http::experts_selected))
|
||||||
|
.route("/api/experts/select", routing::post(http::experts_select))
|
||||||
.route("/ws", routing::get(ws::ws_handler))
|
.route("/ws", routing::get(ws::ws_handler))
|
||||||
.fallback_service(ServeDir::new(&static_dir))
|
.fallback_service(ServeDir::new(&static_dir))
|
||||||
.with_state(state.clone())
|
.with_state(state.clone())
|
||||||
|
|||||||
@ -45,6 +45,7 @@ pub(crate) fn build_session_manager(
|
|||||||
provider_config: LLMProviderConfig,
|
provider_config: LLMProviderConfig,
|
||||||
provider_configs: HashMap<String, LLMProviderConfig>,
|
provider_configs: HashMap<String, LLMProviderConfig>,
|
||||||
skills: Arc<SkillRuntime>,
|
skills: Arc<SkillRuntime>,
|
||||||
|
experts: Arc<crate::experts::ExpertRuntime>,
|
||||||
disabled_tools: HashSet<String>,
|
disabled_tools: HashSet<String>,
|
||||||
task_config: TaskConfig,
|
task_config: TaskConfig,
|
||||||
subagents_config: SubagentsConfig,
|
subagents_config: SubagentsConfig,
|
||||||
@ -60,6 +61,7 @@ pub(crate) fn build_session_manager(
|
|||||||
provider_config,
|
provider_config,
|
||||||
provider_configs,
|
provider_configs,
|
||||||
skills,
|
skills,
|
||||||
|
experts,
|
||||||
Arc::new(NoopSessionMessageSender),
|
Arc::new(NoopSessionMessageSender),
|
||||||
disabled_tools,
|
disabled_tools,
|
||||||
task_config,
|
task_config,
|
||||||
@ -79,6 +81,7 @@ pub(crate) fn build_session_manager_with_sender(
|
|||||||
provider_config: LLMProviderConfig,
|
provider_config: LLMProviderConfig,
|
||||||
provider_configs: HashMap<String, LLMProviderConfig>,
|
provider_configs: HashMap<String, LLMProviderConfig>,
|
||||||
skills: Arc<SkillRuntime>,
|
skills: Arc<SkillRuntime>,
|
||||||
|
experts: Arc<crate::experts::ExpertRuntime>,
|
||||||
session_message_sender: Arc<dyn SessionMessageSender>,
|
session_message_sender: Arc<dyn SessionMessageSender>,
|
||||||
disabled_tools: HashSet<String>,
|
disabled_tools: HashSet<String>,
|
||||||
task_config: TaskConfig,
|
task_config: TaskConfig,
|
||||||
@ -269,6 +272,7 @@ pub(crate) fn build_session_manager_with_sender(
|
|||||||
let agent_factory = AgentFactory::new(
|
let agent_factory = AgentFactory::new(
|
||||||
tools.clone(),
|
tools.clone(),
|
||||||
skills.clone(),
|
skills.clone(),
|
||||||
|
experts.clone(),
|
||||||
subagent_runtime.clone(),
|
subagent_runtime.clone(),
|
||||||
agent_prompt_reinject_every as usize,
|
agent_prompt_reinject_every as usize,
|
||||||
prompt_repository.clone(),
|
prompt_repository.clone(),
|
||||||
|
|||||||
@ -255,9 +255,13 @@ impl Session {
|
|||||||
let conversations: Arc<dyn ConversationRepository> = store.clone();
|
let conversations: Arc<dyn ConversationRepository> = store.clone();
|
||||||
let skill_events: Arc<dyn SkillEventRepository> = store.clone();
|
let skill_events: Arc<dyn SkillEventRepository> = store.clone();
|
||||||
let prompt_repository: Arc<dyn PromptInjectionRepository> = store.clone();
|
let prompt_repository: Arc<dyn PromptInjectionRepository> = store.clone();
|
||||||
|
let experts = Arc::new(crate::experts::ExpertRuntime::from_config(
|
||||||
|
crate::config::ExpertsConfig::default(),
|
||||||
|
));
|
||||||
let agent_factory = AgentFactory::new(
|
let agent_factory = AgentFactory::new(
|
||||||
tools,
|
tools,
|
||||||
skills.clone(),
|
skills.clone(),
|
||||||
|
experts,
|
||||||
subagent_runtime,
|
subagent_runtime,
|
||||||
agent_prompt_reinject_every as usize,
|
agent_prompt_reinject_every as usize,
|
||||||
prompt_repository.clone(),
|
prompt_repository.clone(),
|
||||||
@ -670,6 +674,9 @@ impl SessionManager {
|
|||||||
session_ttl_hours: Option<u64>,
|
session_ttl_hours: Option<u64>,
|
||||||
mcp_config: crate::mcp::McpConfig,
|
mcp_config: crate::mcp::McpConfig,
|
||||||
) -> Result<Self, AgentError> {
|
) -> Result<Self, AgentError> {
|
||||||
|
let experts = Arc::new(crate::experts::ExpertRuntime::from_config(
|
||||||
|
crate::config::ExpertsConfig::default(),
|
||||||
|
));
|
||||||
super::runtime::build_session_manager(
|
super::runtime::build_session_manager(
|
||||||
agent_prompt_reinject_every,
|
agent_prompt_reinject_every,
|
||||||
show_tool_results,
|
show_tool_results,
|
||||||
@ -677,6 +684,7 @@ impl SessionManager {
|
|||||||
provider_config,
|
provider_config,
|
||||||
provider_configs,
|
provider_configs,
|
||||||
skills,
|
skills,
|
||||||
|
experts,
|
||||||
disabled_tools,
|
disabled_tools,
|
||||||
task_config,
|
task_config,
|
||||||
subagents_config,
|
subagents_config,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user