Skip to main content

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}