Skip to main content

mnem_bench/
adapter.rs

1//! `BenchAdapter` trait - what every system-under-test (mnem, mem0,
2//! MemPalace) implements so the scorers can drive it.
3
4use serde::{Deserialize, Serialize};
5use std::error::Error as StdError;
6
7/// One document staged for ingest. The scorer assigns the
8/// `external_id` (e.g. session id, dialog id) and reads it back from
9/// the retrieved hits via [`Hit::external_id`] so the adapter is
10/// free to mint any internal id it likes.
11#[derive(Clone, Debug, Serialize, Deserialize)]
12pub struct IngestDoc {
13    /// Stable per-bench identifier the scorer attaches to this doc.
14    /// Echoed back unchanged on the matching [`Hit`].
15    pub external_id: String,
16    /// Scope label used to keep the corpus per-question / per-conv
17    /// isolated. mnem encodes this as the `Node.ntype` so the
18    /// retriever's label filter scopes to a single question's
19    /// haystack.
20    pub label: String,
21    /// Free-form natural-language text the embedder consumes.
22    pub text: String,
23    /// Optional structured property bag. Echoed onto the node /
24    /// document for downstream filtering. Values are stringified by
25    /// adapters that cannot store arbitrary JSON.
26    pub props: serde_json::Map<String, serde_json::Value>,
27}
28
29/// One hit from a retrieve. `external_id` matches the value the
30/// scorer staged on the corresponding [`IngestDoc`]. Adapters that
31/// re-rank or filter still preserve `external_id` round-trip.
32#[derive(Clone, Debug, Serialize, Deserialize)]
33pub struct Hit {
34    /// External id originally staged with [`IngestDoc::external_id`].
35    pub external_id: String,
36    /// Adapter-internal ranking score. Higher is better. The scorer
37    /// only consumes the rank order, but the score is logged for
38    /// debugging.
39    pub score: f32,
40}
41
42/// Adapter trait. One instance handles a single benchmark run.
43pub trait BenchAdapter {
44    /// Drop all per-question / per-conv state so the next ingest is
45    /// fresh. mnem's in-memory adapter rotates a new `Repo`; HTTP
46    /// adapters POST a label-scoped delete.
47    ///
48    /// # Errors
49    ///
50    /// Returns the adapter's own error type when the reset fails.
51    fn reset(&mut self) -> Result<(), Box<dyn StdError>>;
52
53    /// Ingest the given documents under their staged labels. Caller
54    /// is responsible for [`Self::reset`] between unrelated batches.
55    ///
56    /// # Errors
57    ///
58    /// Returns adapter-specific errors when the underlying store
59    /// rejects a document.
60    fn ingest(&mut self, docs: &[IngestDoc]) -> Result<(), Box<dyn StdError>>;
61
62    /// Retrieve the top-K matches for `query` within `label`.
63    /// Returned hits MUST be ordered score-desc. The scorer only
64    /// looks at the first `top_k` entries.
65    ///
66    /// # Errors
67    ///
68    /// Returns adapter-specific errors when the underlying store
69    /// rejects the query.
70    fn retrieve(
71        &mut self,
72        label: &str,
73        query: &str,
74        top_k: usize,
75    ) -> Result<Vec<Hit>, Box<dyn StdError>>;
76
77    /// Free-form adapter name for logs + RESULTS.md. e.g. `"mnem"`.
78    fn name(&self) -> &str;
79}