Skip to main content

kernel/event/record/
modality.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! The two faces of an attached endpoint that are not the conversation:
7//! what one embedding or rerank call asked, and what came back.
8//!
9//! One line per settled call rather than a called/returned pair, because
10//! neither answer is part of a conversation: what a reader asks of these
11//! lines is what was bought - which model read how many texts, and how
12//! many vectors or ranks came back for them. The vectors themselves are
13//! absent for the reason `ModelCalled` carries no request body: they are
14//! derived values a replay recomputes from the recorded inputs, and a
15//! second copy here would be a second answer to the same question.
16
17use serde::{Deserialize, Serialize};
18
19use crate::budget::Tokens;
20
21/// `embedding_called`: one call to an attached endpoint's embeddings
22/// face.
23///
24/// The two counts are recorded rather than one being derived from the
25/// other: an answer that returned fewer vectors than it was given texts
26/// is a retrieval defect, and this is the line that shows it.
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
29pub struct EmbeddingCalled {
30    /// The endpoint's own model id, as the request spelled it.
31    pub model: String,
32    /// How many texts the call asked to embed.
33    pub inputs: u64,
34    /// How many vectors the answer carried.
35    pub vectors: u64,
36    /// The width the caller asked for. Absent when it named none, which
37    /// is not the same fact as a width of zero.
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    pub dimensions: Option<u64>,
40    /// What the provider said it read. Absent when it said nothing.
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub prompt_tokens: Option<Tokens>,
43}
44
45/// `rerank_called`: one call to an attached endpoint's rerank face.
46///
47/// The order the service returned is the answer and is not repeated
48/// here; what this line adds is the size of what was asked and of what
49/// came back, so a rerank that quietly scored half the passages can be
50/// found afterwards. The line states no token count, because the rerank
51/// faces this city writes to report none, and a column nothing can fill
52/// is a promise to the ledger's reader that is never kept.
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
55pub struct RerankCalled {
56    /// The endpoint's own model id, as the request spelled it.
57    pub model: String,
58    /// How many passages the call offered to rank.
59    pub passages: u64,
60    /// How many ranks the answer carried.
61    pub ranks: u64,
62}
63
64#[cfg(test)]
65#[allow(
66    clippy::unwrap_used,
67    clippy::expect_used,
68    clippy::panic,
69    clippy::indexing_slicing,
70    reason = "test code"
71)]
72mod tests {
73    use super::*;
74    use crate::event::Payload;
75
76    /// The keys are the contract: a ledger already written cannot be
77    /// re-spelled, so the shape of a line is pinned here.
78    #[test]
79    fn an_embedding_line_keeps_the_four_keys_and_omits_what_was_not_stated() {
80        let call = EmbeddingCalled {
81            model: "bge-m3".to_owned(),
82            inputs: 12,
83            vectors: 12,
84            dimensions: None,
85            prompt_tokens: Some(Tokens::new(77)),
86        };
87        let payload = Payload::of(&call).unwrap();
88        assert_eq!(
89            serde_json::to_string(&payload).unwrap(),
90            "{\"inputs\":12,\"model\":\"bge-m3\",\"prompt_tokens\":77,\"vectors\":12}"
91        );
92        assert_eq!(
93            serde_json::to_string(
94                &Payload::of(&EmbeddingCalled {
95                    dimensions: Some(1024),
96                    ..call.clone()
97                })
98                .unwrap()
99            )
100            .unwrap(),
101            "{\"dimensions\":1024,\"inputs\":12,\"model\":\"bge-m3\",\"prompt_tokens\":77,\"vectors\":12}"
102        );
103        assert_eq!(payload.read::<EmbeddingCalled>().unwrap(), call);
104    }
105
106    #[test]
107    fn a_rerank_line_carries_the_two_counts_and_no_token_column() {
108        let call = RerankCalled {
109            model: "bge-reranker".to_owned(),
110            passages: 40,
111            ranks: 40,
112        };
113        let payload = Payload::of(&call).unwrap();
114        assert_eq!(
115            serde_json::to_string(&payload).unwrap(),
116            "{\"model\":\"bge-reranker\",\"passages\":40,\"ranks\":40}"
117        );
118        assert_eq!(payload.read::<RerankCalled>().unwrap(), call);
119    }
120}