use crate::goal::types::{GoalStatus, SessionGoal};
use crate::session::SessionStore;
use std::collections::HashMap;
use std::path::Path;
use std::sync::{Arc, RwLock};
use super::runtime::GoalRuntimeHandle;
pub struct GoalService {
runtimes: RwLock<HashMap<String, Arc<GoalRuntimeHandle>>>,
}
impl GoalService {
pub fn new() -> Self {
Self {
runtimes: RwLock::new(HashMap::new()),
}
}
pub fn set_goal(
&self,
session_id: String,
objective: String,
token_budget: Option<i64>,
) -> SessionGoal {
let runtimes = self.runtimes.read().unwrap_or_else(|e| e.into_inner());
if let Some(runtime) = runtimes.get(&session_id) {
runtime.set_objective(objective, token_budget)
} else {
drop(runtimes);
SessionGoal::new(session_id, objective, token_budget)
}
}
pub fn update_goal_status(&self, session_id: &str, status: GoalStatus) -> Option<SessionGoal> {
let runtimes = self.runtimes.read().unwrap_or_else(|e| e.into_inner());
if let Some(runtime) = runtimes.get(session_id) {
let goal = runtime.get_goal();
if let Some(mut goal) = goal {
goal.transition_to(status);
runtime.update_goal(goal.clone());
return Some(goal);
}
}
None
}
pub fn get_goal(&self, session_id: &str) -> Option<SessionGoal> {
let runtimes = self.runtimes.read().unwrap_or_else(|e| e.into_inner());
if let Some(runtime) = runtimes.get(session_id) {
runtime.get_goal()
} else {
None
}
}
pub fn clear_goal(&self, session_id: &str) -> bool {
let mut runtimes = self.runtimes.write().unwrap_or_else(|e| e.into_inner());
if let Some(runtime) = runtimes.get(session_id) {
runtime.clear_goal();
}
runtimes.remove(session_id);
true
}
pub fn register_runtime(&self, session_id: String, runtime: Arc<GoalRuntimeHandle>) {
let mut runtimes = self.runtimes.write().unwrap_or_else(|e| e.into_inner());
runtimes.insert(session_id, runtime);
}
pub fn unregister_runtime(&self, session_id: &str) {
let mut runtimes = self.runtimes.write().unwrap_or_else(|e| e.into_inner());
runtimes.remove(session_id);
}
pub fn persist_goal(
&self,
session_id: &str,
_project_dir: &Path,
session_store: &SessionStore,
) -> anyhow::Result<()> {
if let Some(goal) = self.get_goal(session_id) {
let json = serde_json::to_string_pretty(&goal)?;
let dir = session_store.root().join(session_id);
std::fs::create_dir_all(&dir)?;
let path = dir.join("goal.json");
std::fs::write(&path, json)?;
}
Ok(())
}
pub fn load_goal(&self, session_id: &str, session_store: &SessionStore) -> Option<SessionGoal> {
if let Ok(snapshot) = session_store.load(session_id)
&& snapshot.goal.is_some()
{
return snapshot.goal;
}
let path = session_store.root().join(session_id).join("goal.json");
if path.exists() {
let json = std::fs::read_to_string(&path).ok()?;
serde_json::from_str(&json).ok()
} else {
None
}
}
}
impl Default for GoalService {
fn default() -> Self {
Self::new()
}
}