use std::sync::Arc;
use async_trait::async_trait;
use serde_json::json;
use crate::schemas::Message;
use super::{Middleware, MiddlewareContext, MiddlewareError};
use crate::agent::deep_agent::skills::{load_skill_full_content, match_skills, SkillMeta};
use crate::agent::runtime::RuntimeRequest;
use crate::schemas::agent::AgentAction;
pub struct SkillsMiddleware {
index: Vec<SkillMeta>,
}
impl SkillsMiddleware {
pub fn new(index: Vec<SkillMeta>) -> Self {
Self { index }
}
fn get_user_message_text(input: &crate::prompt::PromptArgs) -> String {
if let Some(v) = input.get("input") {
if let Some(s) = v.as_str() {
return s.to_string();
}
}
if let Some(chat_history) = input.get("chat_history") {
if let Some(arr) = chat_history.as_array() {
for msg in arr.iter().rev() {
if let Some(role) = msg.get("message_type").and_then(|r| r.as_str()) {
if role == "human" {
if let Some(content) = msg.get("content").and_then(|c| c.as_str()) {
return content.to_string();
}
}
}
}
}
}
String::new()
}
fn inject_skills_into_input(
&self,
mut input: crate::prompt::PromptArgs,
user_text: &str,
) -> Result<crate::prompt::PromptArgs, MiddlewareError> {
let matched = match_skills(&self.index, user_text);
if matched.is_empty() {
return Ok(input);
}
let mut parts = Vec::new();
for meta in &matched {
match load_skill_full_content(meta) {
Ok(body) => {
parts.push(format!("### {}\n\n{}", meta.name, body));
}
Err(e) => {
log::warn!("Failed to load skill {}: {}", meta.name, e);
}
}
}
if parts.is_empty() {
return Ok(input);
}
let skills_block = format!("\n\n## Skills\n\n{}", parts.join("\n\n"));
let skills_message = Message::new_system_message(&skills_block);
let chat_history = input
.get("chat_history")
.and_then(|v| v.as_array())
.map(|a| a.to_vec())
.unwrap_or_default();
let mut new_history = vec![json!(skills_message)];
new_history.extend(chat_history);
input.insert("chat_history".to_string(), json!(new_history));
Ok(input)
}
}
#[async_trait]
impl Middleware for SkillsMiddleware {
async fn before_agent_plan_with_runtime(
&self,
request: &RuntimeRequest,
steps: &[(AgentAction, String)],
context: &mut MiddlewareContext,
) -> Result<Option<crate::prompt::PromptArgs>, MiddlewareError> {
let _ = context;
if self.index.is_empty() {
return Ok(None);
}
if !steps.is_empty() {
return Ok(None);
}
let user_text = Self::get_user_message_text(&request.input);
let modified = self.inject_skills_into_input(request.input.clone(), &user_text)?;
Ok(Some(modified))
}
}
pub fn build_skills_middleware(
skill_dirs: &[std::path::PathBuf],
) -> Result<Option<Arc<dyn Middleware>>, std::io::Error> {
let index = crate::agent::deep_agent::skills::load_skill_index(skill_dirs)?;
if index.is_empty() {
return Ok(None);
}
Ok(Some(Arc::new(SkillsMiddleware::new(index))))
}