mod child;
mod feed;
pub mod spec;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, OnceLock, Weak};
use oino_agent::StreamFn;
pub use child::ChildAgent;
pub use feed::{AgentInfo, AgentRunState};
pub(crate) use feed::{changes_state, forward_to_root};
pub use spec::{
AgentSpec, ExtensionPolicy, Persistence, Seed, SystemPromptSpec, ToolPolicy, UiPolicy,
};
use crate::extensions::runner::ExtensionSet;
use crate::services::HostRouter;
use crate::session::{Inner, lock};
#[derive(Debug, Default)]
pub(crate) struct Tree {
live: AtomicUsize,
pub(crate) follow: AtomicBool,
}
impl Tree {
fn try_acquire(&self, max: usize) -> bool {
self.live
.try_update(Ordering::SeqCst, Ordering::SeqCst, |n| {
(n < max).then_some(n + 1)
})
.is_ok()
}
fn release(&self) {
let _ = self
.live
.try_update(Ordering::SeqCst, Ordering::SeqCst, |n| n.checked_sub(1));
}
}
#[derive(Clone)]
pub(crate) struct ChildLink {
pub(crate) parent: Weak<Inner>,
pub(crate) label: String,
pub(crate) role: Option<String>,
pub(crate) ui: UiPolicy,
pub(crate) prompt: SystemPromptSpec,
}
#[derive(Clone)]
pub struct ChildConfig {
pub(crate) link: ChildLink,
pub(crate) all_extensions: Arc<ExtensionSet>,
pub(crate) base_stream: Arc<dyn StreamFn>,
pub(crate) tree: Arc<Tree>,
pub(crate) depth: u8,
pub(crate) max_depth: u8,
pub(crate) router: Option<Weak<HostRouter>>,
}
impl std::fmt::Debug for ChildConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChildConfig")
.field("label", &self.link.label)
.field("depth", &self.depth)
.finish_non_exhaustive()
}
}
pub(crate) struct AgentsState {
pub(crate) router: OnceLock<Weak<HostRouter>>,
pub(crate) all_extensions: Arc<ExtensionSet>,
pub(crate) base_stream: Arc<dyn StreamFn>,
pub(crate) link: Option<ChildLink>,
pub(crate) children: Mutex<Vec<Arc<ChildAgent>>>,
pub(crate) tree: Arc<Tree>,
pub(crate) depth: u8,
pub(crate) max_depth: u8,
}
impl AgentsState {
pub(crate) fn root(all_extensions: Arc<ExtensionSet>, base_stream: Arc<dyn StreamFn>) -> Self {
Self {
router: OnceLock::new(),
all_extensions,
base_stream,
link: None,
children: Mutex::new(Vec::new()),
tree: Arc::new(Tree::default()),
depth: 0,
max_depth: spec::HARD_MAX_DEPTH,
}
}
pub(crate) fn child(config: ChildConfig) -> Self {
let router = config.router;
let state = Self {
router: OnceLock::new(),
all_extensions: config.all_extensions,
base_stream: config.base_stream,
link: Some(config.link),
children: Mutex::new(Vec::new()),
tree: config.tree,
depth: config.depth,
max_depth: config.max_depth,
};
if let Some(router) = router {
let _ = state.router.set(router);
}
state
}
pub(crate) fn router(&self) -> Option<Arc<HostRouter>> {
self.router.get().and_then(Weak::upgrade)
}
pub(crate) fn remove_child(&self, id: &str) {
lock(&self.children).retain(|c| c.id() != id);
}
}