Refactor IPC and DTO structures; remove unused code and streamline message handling
- Removed unused structs and methods from `response.rs`, `usage.rs`, and `client.rs`. - Simplified `Connection` handling in `conn.rs` to only support Unix sockets. - Updated `IpcServer` to exclusively use Unix sockets and removed TCP handling. - Cleaned up `editlog.rs` by removing loading and recent entry methods. - Refactored `memory.rs` to eliminate unused functions related to lesson promotion and retrospective creation. - Enhanced `search.rs` to support multiple search providers and improved error handling. - Updated chat view logic to simplify message display and improve user experience. - Removed deprecated modules and constants from various files to streamline the codebase.
This commit is contained in:
+182
-48
@@ -1,8 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::script::{ScriptPrimitive, WorkflowScript};
|
||||
|
||||
static FINDINGS: std::sync::Mutex<Vec<String>> = std::sync::Mutex::new(Vec::new());
|
||||
static FINDINGS: Mutex<Vec<String>> = Mutex::new(Vec::new());
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum AgentState {
|
||||
@@ -20,17 +21,6 @@ pub struct AgentStatus {
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl AgentStatus {
|
||||
pub fn new() -> Self {
|
||||
AgentStatus {
|
||||
state: AgentState::Idle,
|
||||
started_at: None,
|
||||
completed_at: None,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WorkflowAgent {
|
||||
pub id: String,
|
||||
@@ -38,20 +28,9 @@ pub struct WorkflowAgent {
|
||||
pub status: AgentStatus,
|
||||
}
|
||||
|
||||
impl WorkflowAgent {
|
||||
pub fn new(id: String, name: String) -> Self {
|
||||
WorkflowAgent {
|
||||
id,
|
||||
name,
|
||||
status: AgentStatus::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WorkflowEngine {
|
||||
pub agents: Vec<WorkflowAgent>,
|
||||
pub concurrency_cap: usize,
|
||||
pub findings: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -59,55 +38,167 @@ impl WorkflowEngine {
|
||||
pub fn new() -> Self {
|
||||
WorkflowEngine {
|
||||
agents: Vec::new(),
|
||||
concurrency_cap: 5,
|
||||
findings: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_concurrency_cap(mut self, cap: usize) -> Self {
|
||||
self.concurrency_cap = cap;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_agent(&mut self, agent: WorkflowAgent) {
|
||||
self.agents.push(agent);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_primitive(primitive: &ScriptPrimitive, args: &HashMap<String, String>) -> anyhow::Result<()> {
|
||||
fn spawn_single_agent(prompt: &str, findings_snapshot: Vec<String>) -> anyhow::Result<String> {
|
||||
use crate::app::subagent::context::build_subagent_context;
|
||||
use crate::app::subagent::engine::run_subagent;
|
||||
use crate::app::subagent::spawn::AgentDefinition;
|
||||
|
||||
let def = AgentDefinition::new("workflow-agent".to_string(), "coder".to_string())
|
||||
.with_max_steps(20);
|
||||
let mut ctx = build_subagent_context(def);
|
||||
|
||||
let findings_section = if findings_snapshot.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(
|
||||
"\n\nFindings from sibling agents in this workflow run:\n{}",
|
||||
findings_snapshot
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, f)| format!("{}. {}", i + 1, f))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)
|
||||
};
|
||||
|
||||
ctx.system_prompt = format!("{}{}", prompt, findings_section);
|
||||
|
||||
let (tx, _rx) = tokio::sync::mpsc::channel(32);
|
||||
run_subagent(ctx, tx)
|
||||
}
|
||||
|
||||
type ParallelResult = (usize, anyhow::Result<Vec<String>>);
|
||||
|
||||
pub fn execute_primitive(
|
||||
primitive: &ScriptPrimitive,
|
||||
args: &HashMap<String, String>,
|
||||
concurrency_cap: usize,
|
||||
) -> anyhow::Result<Vec<String>> {
|
||||
match primitive {
|
||||
ScriptPrimitive::Agent(name) => {
|
||||
let _agent_name = name;
|
||||
let _args = args;
|
||||
Ok(())
|
||||
ScriptPrimitive::Agent(prompt) => {
|
||||
let resolved = resolve_template(prompt, args);
|
||||
let findings_snapshot = FINDINGS.lock().map(|f| f.clone()).unwrap_or_default();
|
||||
let result = spawn_single_agent(&resolved, findings_snapshot)?;
|
||||
Ok(vec![result])
|
||||
}
|
||||
|
||||
ScriptPrimitive::Parallel(scripts) => {
|
||||
for script in scripts {
|
||||
execute_primitive(script, args)?;
|
||||
let semaphore = Arc::new(Semaphore::new(concurrency_cap.max(1)));
|
||||
let results: Arc<Mutex<Vec<ParallelResult>>> =
|
||||
Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let handles: Vec<_> = scripts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, script)| {
|
||||
let script = script.clone();
|
||||
let args = args.clone();
|
||||
let sem = Arc::clone(&semaphore);
|
||||
let results = Arc::clone(&results);
|
||||
let cap = concurrency_cap;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let _permit = sem.acquire();
|
||||
let result = execute_primitive(&script, &args, cap);
|
||||
if let Ok(mut locked) = results.lock() {
|
||||
locked.push((idx, result));
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
for handle in handles {
|
||||
let _ = handle.join();
|
||||
}
|
||||
Ok(())
|
||||
|
||||
let mut locked = results.lock().map_err(|_| anyhow::anyhow!("parallel results lock poisoned"))?;
|
||||
locked.sort_by_key(|(idx, _)| *idx);
|
||||
let mut all = Vec::new();
|
||||
for (_, res) in locked.drain(..) {
|
||||
match res {
|
||||
Ok(outputs) => all.extend(outputs),
|
||||
Err(e) => all.push(format!("agent error: {}", e)),
|
||||
}
|
||||
}
|
||||
Ok(all)
|
||||
}
|
||||
|
||||
ScriptPrimitive::Pipeline(scripts) => {
|
||||
for script in scripts {
|
||||
execute_primitive(script, args)?;
|
||||
let results_store: Arc<Mutex<Vec<Option<Vec<String>>>>> =
|
||||
Arc::new(Mutex::new(vec![None; scripts.len()]));
|
||||
let args_arc = Arc::new(args.clone());
|
||||
|
||||
let handles: Vec<_> = scripts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, script)| {
|
||||
let script = script.clone();
|
||||
let args = Arc::clone(&args_arc);
|
||||
let store = Arc::clone(&results_store);
|
||||
let cap = concurrency_cap;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let result = execute_primitive(&script, &args, cap);
|
||||
if let Ok(mut locked) = store.lock() {
|
||||
locked[idx] = Some(result.unwrap_or_else(|e| vec![format!("pipeline stage {} error: {}", idx, e)]));
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
for handle in handles {
|
||||
let _ = handle.join();
|
||||
}
|
||||
Ok(())
|
||||
|
||||
let locked = results_store.lock().map_err(|_| anyhow::anyhow!("pipeline results lock poisoned"))?;
|
||||
let mut all = Vec::new();
|
||||
for outputs in locked.iter().flatten() {
|
||||
all.extend(outputs.iter().cloned());
|
||||
}
|
||||
Ok(all)
|
||||
}
|
||||
|
||||
ScriptPrimitive::Phase { name: _name, script } => {
|
||||
execute_primitive(script, args)
|
||||
execute_primitive(script, args, concurrency_cap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_workflow(script: &WorkflowScript, args: &HashMap<String, String>) -> anyhow::Result<String> {
|
||||
if let Ok(mut findings) = FINDINGS.lock() {
|
||||
findings.clear();
|
||||
}
|
||||
|
||||
let concurrency_cap = if script.options.max_concurrency > 0 {
|
||||
script.options.max_concurrency.min(5)
|
||||
} else {
|
||||
5
|
||||
};
|
||||
let _cap = concurrency_cap;
|
||||
execute_primitive(&script.script, args)?;
|
||||
Ok("workflow completed".to_string())
|
||||
|
||||
let results = execute_primitive(&script.script, args, concurrency_cap)?;
|
||||
|
||||
let summary = if results.is_empty() {
|
||||
"workflow completed with no output".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"workflow '{}' completed. {} agent result(s):\n{}",
|
||||
script.name,
|
||||
results.len(),
|
||||
results
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, r)| format!("[{}] {}", i + 1, r.lines().next().unwrap_or(r)))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)
|
||||
};
|
||||
|
||||
Ok(summary)
|
||||
}
|
||||
|
||||
pub fn note_finding(text: &str) {
|
||||
@@ -115,3 +206,46 @@ pub fn note_finding(text: &str) {
|
||||
findings.push(text.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_template(template: &str, args: &HashMap<String, String>) -> String {
|
||||
let mut result = template.to_string();
|
||||
for (key, value) in args {
|
||||
result = result.replace(&format!("{{{{{}}}}}", key), value);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
struct Semaphore {
|
||||
count: Mutex<usize>,
|
||||
condvar: std::sync::Condvar,
|
||||
}
|
||||
|
||||
impl Semaphore {
|
||||
fn new(count: usize) -> Self {
|
||||
Semaphore {
|
||||
count: Mutex::new(count),
|
||||
condvar: std::sync::Condvar::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn acquire(&self) -> SemaphoreGuard<'_> {
|
||||
let mut count = self.count.lock().unwrap();
|
||||
while *count == 0 {
|
||||
count = self.condvar.wait(count).unwrap();
|
||||
}
|
||||
*count -= 1;
|
||||
SemaphoreGuard { sem: self }
|
||||
}
|
||||
}
|
||||
|
||||
struct SemaphoreGuard<'a> {
|
||||
sem: &'a Semaphore,
|
||||
}
|
||||
|
||||
impl<'a> Drop for SemaphoreGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
let mut count = self.sem.count.lock().unwrap();
|
||||
*count += 1;
|
||||
self.sem.condvar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user