zeph_core/
json_event_layer.rs1use std::future::Future;
15use std::pin::Pin;
16use std::sync::Arc;
17
18use zeph_tools::ToolError;
19use zeph_tools::executor::{ToolCall, ToolOutput};
20
21use crate::json_event_sink::{JsonEvent, JsonEventSink};
22use crate::runtime_layer::{BeforeToolResult, LayerContext, RuntimeLayer};
23
24pub struct JsonEventLayer {
26 sink: Arc<JsonEventSink>,
27}
28
29impl JsonEventLayer {
30 #[must_use]
32 pub fn new(sink: Arc<JsonEventSink>) -> Self {
33 Self { sink }
34 }
35}
36
37impl RuntimeLayer for JsonEventLayer {
38 fn before_tool<'a>(
39 &'a self,
40 _ctx: &'a LayerContext<'_>,
41 call: &'a ToolCall,
42 ) -> Pin<Box<dyn Future<Output = BeforeToolResult> + Send + 'a>> {
43 let raw = serde_json::Value::Object(call.params.clone());
45 let raw_str = raw.to_string();
46 let scrubbed_str = crate::redact::scrub_content(&raw_str);
47 let args_value: serde_json::Value =
48 serde_json::from_str(&scrubbed_str).unwrap_or(serde_json::Value::Null);
49 self.sink.emit(&JsonEvent::ToolCall {
50 tool: call.tool_id.as_ref(),
51 args: &args_value,
52 id: call.tool_id.as_ref(),
53 });
54 Box::pin(std::future::ready(None))
55 }
56
57 fn after_tool<'a>(
58 &'a self,
59 _ctx: &'a LayerContext<'_>,
60 call: &'a ToolCall,
61 result: &'a Result<Option<ToolOutput>, ToolError>,
62 ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
63 let err_str;
64 let scrubbed_err;
65 let scrubbed_out;
66 let (output, is_error) = match result {
67 Ok(Some(out)) => {
68 scrubbed_out = crate::redact::scrub_content(&out.summary);
69 (scrubbed_out.as_ref(), false)
70 }
71 Ok(None) => ("", false),
72 Err(e) => {
73 err_str = e.to_string();
74 scrubbed_err = crate::redact::scrub_content(&err_str);
75 (scrubbed_err.as_ref(), true)
76 }
77 };
78 self.sink.emit(&JsonEvent::ToolResult {
79 tool: call.tool_id.as_ref(),
80 id: call.tool_id.as_ref(),
81 output,
82 is_error,
83 });
84 Box::pin(std::future::ready(()))
85 }
86}