use std::collections::{BTreeMap, BTreeSet};
use async_trait::async_trait;
use serde_json::Value;
use crate::journal::{Record, RecordKind};
const REFUSAL: &str = "a strict replay reached for the world: every call it makes must be \
served from the recorded history, and this one was not";
#[derive(Debug, Default, Clone)]
pub struct Provider {
profiles: BTreeMap<(String, String), Value>,
}
impl Provider {
#[must_use]
pub fn from_history(history: &[Record]) -> Self {
let mut profiles = BTreeMap::new();
for record in history {
let RecordKind::EffectStarted { descriptor, .. } = record.kind() else {
continue;
};
if descriptor.kind != "model.complete" {
continue;
}
let field = |name: &str| {
descriptor
.args
.get(name)
.and_then(Value::as_str)
.map(str::to_owned)
};
if let (Some(provider), Some(model)) = (field("provider"), field("model")) {
profiles.entry((provider, model)).or_insert_with(|| {
descriptor
.args
.get("provider_profile")
.cloned()
.unwrap_or(Value::Null)
});
}
}
Self { profiles }
}
}
#[async_trait]
impl crate::model::ModelProvider for Provider {
fn request_profile(&self, model: &crate::model::ModelId) -> Value {
self.profiles
.get(&(model.provider.clone(), model.model.clone()))
.cloned()
.unwrap_or(Value::Null)
}
async fn complete(
&self,
request: crate::model::Request<'_>,
) -> Result<crate::model::Completion, crate::model::ModelError> {
Err(crate::model::ModelError::Refused {
model: request.model.clone(),
detail: REFUSAL.to_owned(),
})
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Tools;
#[async_trait]
impl crate::tools::ToolClient for Tools {
async fn call(
&self,
tool: &crate::tools::ToolId,
_arguments: &Value,
_provenance: Option<&crate::core::Provenance>,
) -> Result<Value, crate::tools::ToolError> {
Err(crate::tools::ToolError::Refused {
tool: tool.clone(),
detail: REFUSAL.to_owned(),
})
}
fn destination(&self, _tool: &crate::tools::ToolId) -> crate::tools::Destination {
crate::tools::Destination::Local
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Peers;
#[async_trait]
impl crate::peers::PeerClient for Peers {
async fn send(
&self,
peer: &crate::peers::PeerId,
_capability: &str,
_payload: &Value,
_acting_as: &crate::core::Delegation,
_credential: Option<&crate::peers::PeerCredential>,
_provenance: Option<&crate::core::Provenance>,
) -> Result<Value, crate::peers::PeerError> {
Err(crate::peers::PeerError::Refused {
peer: peer.clone(),
detail: REFUSAL.to_owned(),
})
}
}
#[must_use]
pub fn peers_called(history: &[Record]) -> BTreeSet<String> {
history
.iter()
.filter_map(|record| match record.kind() {
RecordKind::EffectStarted { descriptor, .. } if descriptor.kind.starts_with("a2a.") => {
descriptor
.args
.get("peer")
.and_then(Value::as_str)
.map(str::to_owned)
}
_ => None,
})
.collect()
}
#[cfg(feature = "manifest")]
#[must_use]
pub fn wire(
mut builder: super::RuntimeBuilder,
manifests: &[crate::manifest::Manifest],
history: &[Record],
) -> super::RuntimeBuilder {
use std::sync::Arc;
let provider: Arc<dyn crate::model::ModelProvider> = Arc::new(Provider::from_history(history));
let mut named = BTreeSet::new();
for m in manifests {
let Some(models) = &m.spec.models else {
continue;
};
for role in [models.privileged.as_ref(), models.quarantined.as_ref()]
.into_iter()
.flatten()
{
named.insert(role.provider.clone());
}
}
for name in named {
builder = builder.provider(name, Arc::clone(&provider));
}
let peers = peers_called(history);
let mut grants: BTreeMap<String, Vec<(String, bool)>> = BTreeMap::new();
for grant in manifests.iter().flat_map(|m| &m.spec.tools) {
if let Some(id) = crate::tools::ToolId::parse(&grant.reference)
&& id.server != crate::tools::AGENT_SERVER
{
grants
.entry(id.server)
.or_default()
.push((id.tool, grant.mutates));
}
}
let mut registry = crate::peers::PeerRegistry::new();
let mut any_peer = false;
for (server, tools) in grants {
if peers.contains(&server) {
let mut grant = crate::peers::PeerGrant::new(crate::core::Scope::of(
tools.iter().map(|(tool, _)| tool.clone()),
));
if tools.iter().all(|(_, mutates)| !mutates) {
grant = grant.read_only();
}
registry = registry.allow(crate::peers::PeerId::new(server), grant);
any_peer = true;
} else {
builder = builder.tool_server(server, Arc::new(Tools));
}
}
if any_peer {
builder = builder.peers(registry, Arc::new(Peers));
}
builder
}