use std::sync::Arc;
use autoagents_core::tool::ToolT;
use autoagents_llm::LLMProvider;
use temporalio_client::Client;
use temporalio_sdk::{Worker, WorkerOptions};
use temporalio_sdk_core::CoreRuntime;
use tokio::sync::{OnceCell, SetError};
use crate::activities::AgentActivities;
use crate::error::AgentError;
use crate::memory::{MemoryProvider, SlidingWindowMemory};
use crate::state::ToolSchema;
use crate::tool::{ToolRegistry, ToolRegistryBuilder};
use crate::workflow::AgentWorkflow;
pub(crate) static WORKER_TOOL_CATALOG: OnceCell<Vec<ToolSchema>> = OnceCell::const_new();
pub(crate) static WORKER_MEMORY: OnceCell<Arc<dyn MemoryProvider>> = OnceCell::const_new();
pub struct AgentWorkerBuilder {
client: Client,
llm: Option<Arc<dyn LLMProvider>>,
tools: ToolRegistryBuilder,
queue: String,
memory: Option<Arc<dyn MemoryProvider>>,
}
impl AgentWorkerBuilder {
#[must_use]
pub fn new(client: Client) -> Self {
Self {
client,
llm: None,
tools: ToolRegistry::builder(),
queue: "agents".to_string(),
memory: None,
}
}
#[must_use]
pub fn llm(mut self, llm: Arc<dyn LLMProvider>) -> Self {
self.llm = Some(llm);
self
}
#[must_use]
pub fn tool(mut self, tool: Arc<dyn ToolT>) -> Self {
self.tools = self.tools.add(tool);
self
}
#[must_use]
pub fn queue(mut self, queue: impl Into<String>) -> Self {
self.queue = queue.into();
self
}
#[must_use]
pub fn memory(mut self, memory: Arc<dyn MemoryProvider>) -> Self {
self.memory = Some(memory);
self
}
pub fn build_worker(self, runtime: &CoreRuntime) -> Result<Worker, AgentError> {
let llm = self
.llm
.expect("AgentWorkerBuilder::llm(...) must be called before build_worker()");
let registry = self.tools.build();
let catalog = registry.to_schemas();
if let Err(set_err) = WORKER_TOOL_CATALOG.set(catalog) {
let rejected = match set_err {
SetError::AlreadyInitializedError(v) => v,
SetError::InitializingError(_) => {
return Err(AgentError::Other(
"WORKER_TOOL_CATALOG is being initialized concurrently".into(),
));
}
};
let existing = WORKER_TOOL_CATALOG
.get()
.expect("AlreadyInitialized so get must succeed");
if existing != &rejected {
return Err(AgentError::Other(
"WORKER_TOOL_CATALOG was previously initialized with a different tool \
catalog; multiple workers in the same process must register identical \
tools (and in the same order)"
.into(),
));
}
}
let memory: Arc<dyn MemoryProvider> = self
.memory
.unwrap_or_else(|| Arc::new(SlidingWindowMemory::default()));
if let Err(set_err) = WORKER_MEMORY.set(memory.clone()) {
let rejected = match set_err {
SetError::AlreadyInitializedError(v) => v,
SetError::InitializingError(_) => {
return Err(AgentError::Other(
"WORKER_MEMORY is being initialized concurrently".into(),
));
}
};
let existing = WORKER_MEMORY
.get()
.expect("AlreadyInitialized so get must succeed");
if !Arc::ptr_eq(existing, &rejected) {
return Err(AgentError::Other(
"WORKER_MEMORY was previously initialized with a different provider \
instance; multiple workers in the same process must share the same \
Arc<dyn MemoryProvider>"
.into(),
));
}
}
let activities = AgentActivities::new(llm, registry);
let opts = WorkerOptions::new(&self.queue)
.register_workflow::<AgentWorkflow>()
.register_activities(activities)
.build();
Worker::new(runtime, self.client, opts)
.map_err(|e| AgentError::Other(format!("worker init: {e}")))
}
}