nemo_relay/api/event.rs
1// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Event types for Agent Trajectory Observability Format (ATOF) runtime events.
5
6use std::borrow::Cow;
7
8pub use nemo_relay_types::api::event::*;
9use nemo_relay_types::api::llm::LlmRequest;
10use nemo_relay_types::codec::request::AnnotatedLlmRequest;
11use nemo_relay_types::codec::response::AnnotatedLlmResponse;
12
13use crate::codec::resolve;
14
15/// Core-only normalized LLM accessors for ATOF events.
16///
17/// These helpers use built-in codec resolution, so they live in the runtime
18/// crate rather than the shared DTO crate.
19pub trait EventNormalizationExt {
20 /// Normalized LLM request: the codec annotation when present, otherwise a
21 /// best-effort decode of the start-event input payload.
22 ///
23 /// The fallback decode requires the start-event input to be the serialized
24 /// [`LlmRequest`] wire shape (`{headers, content}`) emitted by the managed
25 /// LLM pipeline; events whose input is a bare payload or a non-LLM shape
26 /// yield `None`.
27 #[must_use]
28 fn normalized_llm_request(&self) -> Option<Cow<'_, AnnotatedLlmRequest>>;
29
30 /// Normalized LLM response: the codec annotation when present, otherwise a
31 /// best-effort decode of the end-event output payload.
32 #[must_use]
33 fn normalized_llm_response(&self) -> Option<Cow<'_, AnnotatedLlmResponse>>;
34}
35
36impl EventNormalizationExt for Event {
37 fn normalized_llm_request(&self) -> Option<Cow<'_, AnnotatedLlmRequest>> {
38 if let Some(annotated) = self.annotated_request() {
39 return Some(Cow::Borrowed(annotated.as_ref()));
40 }
41 let request: LlmRequest = serde_json::from_value(self.input()?.clone()).ok()?;
42 // Managed LLM events use the provider route as the event name (for
43 // example, "anthropic.messages"), which doubles as the codec hint for
44 // shape-identical request bodies.
45 resolve::normalize_request_with_hint(&request, Some(self.name())).map(Cow::Owned)
46 }
47
48 fn normalized_llm_response(&self) -> Option<Cow<'_, AnnotatedLlmResponse>> {
49 if let Some(annotated) = self.annotated_response() {
50 return Some(Cow::Borrowed(annotated.as_ref()));
51 }
52 resolve::normalize_response(self.output()?).map(Cow::Owned)
53 }
54}