Skip to main content

bge_m3_embedding_server/probe/
corpus.rs

1// Copyright (c) 2026 J. Patrick Fulton
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Probe text synthesis helpers.
16//!
17//! The probe sweeps `(batch, seq)` shapes by submitting synthesized texts to
18//! the leader worker. Texts come from the curated benchmark corpus; we
19//! repeat/trim corpus entries to hit the target token count for each shape.
20
21/// Loads the benchmark corpus for use as probe text material.
22///
23/// Falls back to a tiny built-in sentence if the corpus file is not found.
24pub(super) fn load_probe_texts() -> Vec<String> {
25    let corpus_path =
26        std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("benches/fixtures/corpus.json");
27    if let Ok(raw) = std::fs::read_to_string(&corpus_path)
28        && let Ok(json) = serde_json::from_str::<serde_json::Value>(&raw)
29        && let Some(scenarios) = json["scenarios"].as_object()
30    {
31        let mut texts: Vec<String> = Vec::new();
32        for scenario in scenarios.values() {
33            if let Some(arr) = scenario["texts"].as_array() {
34                texts.extend(arr.iter().filter_map(|v| v.as_str().map(String::from)));
35            }
36        }
37        if !texts.is_empty() {
38            return texts;
39        }
40    }
41    // Fallback: minimal probe text.
42    vec![
43        "The embedding server startup probe synthesizes texts to measure workspace cost."
44            .to_string(),
45    ]
46}
47
48/// Synthesizes `batch` texts each of approximately `target_seq` tokens.
49///
50/// Token estimation: ~4 chars/token for natural English text.
51/// We repeat/trim corpus texts to hit the target character count.
52pub(super) fn synthesize_texts(corpus: &[String], batch: usize, target_seq: usize) -> Vec<String> {
53    let target_chars = target_seq.saturating_mul(4).max(16);
54    (0..batch)
55        .map(|i| {
56            let base = &corpus[i % corpus.len()];
57            // Repeat the base text until we have enough characters.
58            let repeated = base.repeat((target_chars / base.len().max(1)).max(2) + 1);
59            // Trim to target_chars bytes (not chars, but close enough for probing).
60            let trimmed = if repeated.len() > target_chars {
61                &repeated[..target_chars]
62            } else {
63                &repeated
64            };
65            trimmed.to_string()
66        })
67        .collect()
68}