use std::sync::Arc;
use async_trait::async_trait;
use everruns_contracts::error::{AgentLoopError, Result};
use everruns_contracts::runtime::connection_services::{
DecisionModelExecutorExt, SystemDecisionModel,
};
use everruns_contracts::runtime::events::EventContext;
use everruns_contracts::runtime::execution_context::ExecutionContext;
use crate::tool_context::{ToolContext, ToolContextServices};
use crate::{DecisionOutcome, DecisionRequest, DecisionsService};
pub fn for_context(context: &ToolContext) -> Option<Arc<dyn DecisionsService>> {
if context.provider_credential_store.is_none() {
return context.decisions.clone();
}
Some(Arc::new(SessionSystemDecisions {
context: context.clone(),
}))
}
pub fn for_turn(
services: &ToolContextServices,
execution: &ExecutionContext,
) -> Option<Arc<dyn DecisionsService>> {
let mut context = ToolContext::from_services(execution.session_id, services);
context.event_context = Some(EventContext::from_execution_context(execution));
for_context(&context)
}
struct SessionSystemDecisions {
context: ToolContext,
}
#[async_trait]
impl DecisionsService for SessionSystemDecisions {
fn is_configured(&self) -> bool {
self.context
.decisions
.as_ref()
.is_some_and(|service| service.is_configured())
|| self
.context
.extensions
.get::<DecisionModelExecutorExt>()
.is_some()
}
async fn evaluate(&self, request: DecisionRequest) -> Result<DecisionOutcome> {
let store = self
.context
.provider_credential_store
.as_ref()
.ok_or_else(|| AgentLoopError::tool("Decision source is unavailable"))?;
match store
.get_system_decision_model(self.context.session_id)
.await?
{
SystemDecisionModel::Deployment => match &self.context.decisions {
Some(service) if service.is_configured() => service.evaluate(request).await,
_ => Err(AgentLoopError::tool(
"The deployment has no decisions configured",
)),
},
SystemDecisionModel::Organization(binding) => {
let executor = self
.context
.extensions
.get::<DecisionModelExecutorExt>()
.ok_or_else(|| {
AgentLoopError::tool("Organization decision models are unavailable")
})?;
executor.0.evaluate(binding, request, &self.context).await
}
}
}
fn name(&self) -> &'static str {
"SessionSystemDecisions"
}
}
#[cfg(test)]
#[path = "system_decisions_tests.rs"]
mod tests;