harn_vm/composition/
hosts.rs1use std::collections::BTreeMap;
2use std::rc::Rc;
3
4use serde_json::Value;
5
6use crate::agent_events::ToolCallErrorCategory;
7use crate::value::VmValue;
8use crate::vm::Vm;
9use crate::VmClosure;
10
11use super::manifest::BindingManifestEntry;
12use super::types::{CompositionToolHost, CompositionToolOutput};
13
14pub struct StaticCompositionToolHost {
15 outputs: BTreeMap<String, Value>,
16}
17
18impl StaticCompositionToolHost {
19 pub fn new(outputs: BTreeMap<String, Value>) -> Self {
20 Self { outputs }
21 }
22}
23
24#[async_trait::async_trait(?Send)]
25impl CompositionToolHost for StaticCompositionToolHost {
26 async fn call(&self, binding: &BindingManifestEntry, input: Value) -> CompositionToolOutput {
27 if let Some(value) = self.outputs.get(&binding.name) {
28 return CompositionToolOutput::ok(value.clone());
29 }
30 if let Some(value) = binding.metadata.get("mock_output") {
31 return CompositionToolOutput::ok(value.clone());
32 }
33 CompositionToolOutput::ok(serde_json::json!({
34 "tool": binding.name,
35 "input": input,
36 }))
37 }
38}
39
40pub struct ClosureCompositionToolHost {
49 closure: VmClosure,
50 outer_vm: Vm,
51}
52
53impl ClosureCompositionToolHost {
54 pub fn new(closure: VmClosure, outer_vm: Vm) -> Self {
55 Self { closure, outer_vm }
56 }
57}
58
59#[async_trait::async_trait(?Send)]
60impl CompositionToolHost for ClosureCompositionToolHost {
61 async fn call(&self, binding: &BindingManifestEntry, input: Value) -> CompositionToolOutput {
62 let mut vm = self.outer_vm.child_vm();
63 let args = vec![
64 VmValue::String(Rc::from(binding.name.as_str())),
65 crate::json_to_vm_value(&input),
66 ];
67 match vm.call_closure_pub(&self.closure, &args).await {
68 Ok(value) => {
69 let json = crate::llm::vm_value_to_json(&value);
70 CompositionToolOutput::ok(json)
71 }
72 Err(error) => {
73 CompositionToolOutput::error(error.to_string(), ToolCallErrorCategory::ToolError)
74 }
75 }
76 }
77}