yams/tests/benchmarks/search/retrieval_quality_bench.cpp
Trevon 211db15606
fix(daemon): bound semantic neighbor scores and report startup phase (#334)
Semantic-neighbor maintenance aborted the daemon during startup:
std::clamp<float> asserted '!(__hi < __lo)' because the adaptive cosine
threshold could exceed 1.0 for a legal pair of identical vectors. The same
work was also admitted while the daemon was still initializing, so the
300-second starvation guard forced a pass before readiness.

+ semanticInverseNorm/semanticCosineSimilarity/semanticExplicitThreshold in the
  existing embed policy header, shared by the streamed and corpus branches;
  non-finite vectors or scores produce no neighbor and legal scores are bounded
  before ranking and threshold selection
+ startupMaintenanceEligible() requires ServiceManagerState::Ready, not
  stopping and idle, and is evaluated before
  ResourceGovernor::admitDeferrable(), so an elapsed deferral deadline can no
  longer manufacture missing readiness
+ StartupPhaseTracker (two atomics, no mutex) plus a warn-level init-waiter
  line reporting state, phase and phase_elapsed_ms instead of a debug-level
  state number; a 53-minute startup previously logged nothing actionable
+ regression through the public backfill and corpus-rebuild APIs using a 64-D
  vector whose old float inverse norm rounds its self-cosine above 1
- removed the duplicated inverse-norm and cosine lambdas from EmbeddingService
- renamed __test_setModelProvider/__test_setKgStore to testing_* because they
  used reserved identifiers, across 11 files
- re-anchored 13 pre-existing raw-environment allowlist entries to their new
  line numbers; no new exception was added

Startup is still blocked by corpus-sized memory-sync scans; this change stops
the abort and names the blocking phase, it does not make startup fast.

Signed-off-by: trvon <git@trevon.dev>
2026-10-22 16:48:49 -06:00

9549 lines
468 KiB
C++

/*
retrieval_quality_bench.cpp - RAG Retrieval Quality Benchmark
Uses daemon-based ingestion to properly test retrieval quality with IR metrics.
Usage:
YAMS_TEST_SAFE_SINGLE_INSTANCE=1 ./builddir/tests/benchmarks/retrieval_quality_bench
Environment variables:
YAMS_TEST_SAFE_SINGLE_INSTANCE=1 - Required to prevent GlobalIOContext reset
YAMS_BENCH_CORPUS_SIZE=N - Number of documents to generate (default: 50)
YAMS_BENCH_NUM_QUERIES=N - Number of test queries (default: 10)
YAMS_BENCH_TOPK=N - Retrieve top K results (default: 10)
YAMS_BENCH_DATASET=<name> - Use synthetic, BEIR, or local-manifest dataset
YAMS_BENCH_DATASET_PATH=... - Path to BEIR directory or benchmark_manifest.json root
YAMS_BENCH_EMBED_BACKEND=simeon|onnx
Embedding backend (default: simeon)
YAMS_BENCH_ENABLE_GLINT=1 - Opt into Glint/GLiNER entity extraction on simeon runs
YAMS_BENCH_ENABLE_ONNX_RERANK=1 - Opt into ONNX reranker on simeon runs
YAMS_BENCH_TOPOLOGY_MODE=disabled|weak_query_only|hybrid_assist
YAMS_BENCH_TOPOLOGY_EXPANSION=clusters|graph_neighbors
Cluster partition vs pure semantic_neighbor expansion
YAMS_BENCH_TOPOLOGY_ENGINE=connected|louvain|kmeans
Write topology engine into isolated config
YAMS_BENCH_TOPOLOGY_REPRESENTATIVE_RULE=degree|medoid
Write topology.representative_rule into isolated config
YAMS_BENCH_TOPOLOGY_BQ_ROTATION=none|fwht
Write search.topology.bq_rotation into isolated config
YAMS_BENCH_TOPOLOGY_ROUTE_SCORING=current|size_weighted|seed_coverage
Topology route scoring policy
YAMS_BENCH_TOPOLOGY_SPARSE_DENSE_ALPHA=0..1
Sparse seed overlap vs dense centroid blend
YAMS_BENCH_TOPOLOGY_MIN_ROUTE_SCORE=N.N
Reject routed clusters below score
YAMS_BENCH_TOPOLOGY_MEDOID_ONLY_EXPANSION=1
Expand only route medoids, not full clusters
YAMS_BENCH_TOPOLOGY_MIN_CLUSTERS=N Minimum adaptive routed-cluster prefix
YAMS_BENCH_TOPOLOGY_MAX_SEED_DOCUMENTS=N
Cap ranked lexical routing evidence
YAMS_BENCH_TOPOLOGY_ROAD_HOPS=N Road beam hops for route_only/augment (0 = off)
YAMS_BENCH_TOPOLOGY_ROAD_BEAM_WIDTH=N Clusters kept per road hop
YAMS_BENCH_TOPOLOGY_ROAD_WALK_WINDOW=N Positions walked around each routed hit along the
cluster walk order (route_only/augment; 0 = off)
YAMS_BENCH_TOPOLOGY_ROAD_MAX_PER_CLUSTER=N
Roads kept per cluster at topology build (0 = none)
YAMS_BENCH_TOPOLOGY_ROAD_ORDER=none|time|session|similarity_chain
Cluster walk order stored at topology build
YAMS_BENCH_TOPOLOGY_ADAPTIVE_PROBE_SCORE_GAP=N.N
Grow route prefix while scores remain near the best
YAMS_BENCH_TOPOLOGY_NARROW_MIN_BOUNDARY_MARGIN=N.N
Abstain when selected/excluded route margin is smaller
YAMS_BENCH_TOPOLOGY_CLUSTER_OUTPUT=... Export evaluation-only cluster memberships as JSON
YAMS_BENCH_TOPOLOGY_SOURCE=vector|fts5|keyphrase|segment_keyphrase|kg|gliner|header|theme
Benchmark semantic_neighbor seeding source for topology
YAMS_SEARCH_ENABLE_TOPOLOGY_WEAK_ROUTING=1 - Legacy opt-in topology weak-query routing
YAMS_SEARCH_TOPOLOGY_MAX_CLUSTERS=N - Cap routed topology clusters (default: 2)
YAMS_SEARCH_TOPOLOGY_MAX_DOCS=N - Cap routed topology docs (default: 64)
YAMS_SEARCH_TOPOLOGY_GRAPH_VECTOR_SEED_PROBE=N
GraphNeighbors seed ANN k (0=off/product default; opt-in
e.g. 16)
Tuning for faster ingestion (recommended for large corpora):
YAMS_POST_EMBED_CONCURRENT=12 - Parallel embedding workers (default: 4-8)
YAMS_POST_EXTRACTION_CONCURRENT=12- Parallel content extraction (default: 4)
YAMS_POST_KG_CONCURRENT=12 - Parallel KG extraction (default: 4)
YAMS_POST_INGEST_BATCH_SIZE=24 - Documents per batch (default: 8, adaptive)
YAMS_EMBED_CHANNEL_CAPACITY=16384 - Embedding queue capacity (default: 8192)
YAMS_DB_POOL_MAX=48 - Database connection pool (default: 20)
Supported BEIR datasets (download from
https://public.ukp.informatik.tu-darmstadt.de/thakur/BEIR/datasets/): scifact - Scientific
fact verification (300 queries, 5K docs) cqadupstack - Community Q&A including programming (13K
queries, 457K docs) nfcorpus - NutritionFacts corpus (323 queries, 3.6K docs) arguana -
Argument retrieval (1.4K queries, 8.7K docs) scidocs - Scientific document similarity (1K
queries, 25K docs) fiqa - Financial question answering (648 queries, 57K docs) quora -
Duplicate question retrieval (10K queries, 523K docs) hotpotqa - Multi-hop question answering
(7.4K queries, 5.2M docs) fever - Fact verification (6.7K queries, 5.4M docs)
climate-fever - Climate claim verification (1.5K queries, 5.4M docs)
dbpedia-entity- Entity retrieval (400 queries, 4.6M docs)
trec-covid - COVID-19 retrieval (50 queries, 171K docs)
touche-2020 - Argument retrieval (49 queries, 382K docs)
Example (code-related benchmark):
mkdir -p ~/.cache/yams/benchmarks && cd ~/.cache/yams/benchmarks
curl -L -o cqadupstack.zip
https://public.ukp.informatik.tu-darmstadt.de/thakur/BEIR/datasets/cqadupstack.zip unzip
cqadupstack.zip YAMS_TEST_SAFE_SINGLE_INSTANCE=1 YAMS_BENCH_DATASET=cqadupstack
./builddir/tests/benchmarks/retrieval_quality_bench
Example (repo fixture / local manifest):
YAMS_TEST_SAFE_SINGLE_INSTANCE=1 \
YAMS_BENCH_DATASET=local-manifest \
YAMS_BENCH_DATASET_PATH=tests/benchmarks/data/retrieval_manifest_mini \
./builddir/tests/benchmarks/retrieval_quality_bench --benchmark_filter=BM_RetrievalQuality
*/
#include <sqlite3.h>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
#include <benchmark/benchmark.h>
#include "retrieval_benchmark_support.h"
#include "tests/common/test_helpers_catch2.h"
#include "tests/integration/daemon/test_daemon_harness.h"
#include <yams/cli/cli_sync.h>
#include <yams/cli/search_runner.h>
#include <yams/common/fs_utils.h>
#include <yams/crypto/hasher.h>
#include <yams/daemon/client/asio_connection_pool.h>
#include <yams/daemon/client/daemon_client.h>
#include <yams/daemon/components/ServiceManager.h>
#include <yams/daemon/resource/model_provider.h>
#include <yams/daemon/resource/plugin_trust.h>
#include <yams/search/internal_benchmark.h>
#include <yams/search/topology_routing_session.h>
#include <yams/topology/topology_metadata_store.h>
#include <yams/vector/vector_database.h>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <memory>
#include <numeric>
#include <optional>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <yams/compat/unistd.h>
namespace fs = std::filesystem;
using namespace std::chrono_literals;
using namespace yams;
using json = nlohmann::json;
class BenchmarkMockModelProvider final : public yams::daemon::IModelProvider {
public:
explicit BenchmarkMockModelProvider(std::size_t dim) : dim_(dim) {}
void setProgressCallback(std::function<void(const yams::daemon::ModelLoadEvent&)> cb) override {
progress_ = std::move(cb);
}
Result<std::vector<float>> generateEmbedding(const std::string& text) override {
return generateEmbeddingFor(defaultModelName_, text);
}
Result<std::vector<std::vector<float>>>
generateBatchEmbeddings(const std::vector<std::string>& texts) override {
return generateBatchEmbeddingsFor(defaultModelName_, texts);
}
Result<std::vector<float>> generateEmbeddingFor(const std::string& modelName,
const std::string& text) override {
if (!isModelLoaded(modelName)) {
return Error{ErrorCode::NotFound, "model not loaded"};
}
std::vector<float> embedding(dim_, 0.0f);
std::hash<std::string> hasher;
const auto seed = hasher(text);
for (std::size_t i = 0; i < embedding.size(); ++i) {
embedding[i] = static_cast<float>((seed ^ (i * 131)) % 1000) / 1000.0f;
}
return embedding;
}
Result<std::vector<std::vector<float>>>
generateBatchEmbeddingsFor(const std::string& modelName,
const std::vector<std::string>& texts) override {
if (!isModelLoaded(modelName)) {
return Error{ErrorCode::NotFound, "model not loaded"};
}
std::vector<std::vector<float>> batch;
batch.reserve(texts.size());
for (const auto& text : texts) {
auto embedding = generateEmbeddingFor(modelName, text);
if (!embedding) {
return embedding.error();
}
batch.push_back(std::move(embedding.value()));
}
return batch;
}
Result<void> loadModel(const std::string& modelName) override {
if (progress_) {
yams::daemon::ModelLoadEvent ev;
ev.modelName = modelName;
ev.phase = "loading";
ev.message = "benchmark mock loading";
progress_(ev);
}
loadedModels_.insert(modelName);
defaultModelName_ = modelName;
if (progress_) {
yams::daemon::ModelLoadEvent ev;
ev.modelName = modelName;
ev.phase = "completed";
ev.message = "benchmark mock ready";
progress_(ev);
}
return Result<void>();
}
Result<void> unloadModel(const std::string& modelName) override {
loadedModels_.erase(modelName);
return Result<void>();
}
bool isModelLoaded(const std::string& modelName) const override {
return loadedModels_.contains(modelName);
}
std::vector<std::string> getLoadedModels() const override {
return std::vector<std::string>(loadedModels_.begin(), loadedModels_.end());
}
size_t getLoadedModelCount() const override { return loadedModels_.size(); }
Result<yams::daemon::ModelInfo> getModelInfo(const std::string& modelName) const override {
if (!isModelLoaded(modelName)) {
return Error{ErrorCode::NotFound, "model not loaded"};
}
yams::daemon::ModelInfo info;
info.name = modelName;
info.embeddingDim = dim_;
return info;
}
size_t getEmbeddingDim(const std::string&) const override { return dim_; }
std::shared_ptr<yams::vector::EmbeddingGenerator>
getEmbeddingGenerator(const std::string& = "") override {
return nullptr;
}
std::string getProviderName() const override { return "BenchmarkMockProvider"; }
std::string getProviderVersion() const override { return "1.0"; }
bool isAvailable() const override { return true; }
size_t getMemoryUsage() const override { return 0; }
void releaseUnusedResources() override {}
void shutdown() override { loadedModels_.clear(); }
private:
std::size_t dim_;
std::string defaultModelName_;
std::unordered_set<std::string> loadedModels_;
std::function<void(const yams::daemon::ModelLoadEvent&)> progress_;
};
template <typename T, typename Rep, typename Period>
static Result<T> benchRunSync(boost::asio::awaitable<Result<T>> aw,
const std::chrono::duration<Rep, Period>& timeout) {
return yams::cli::run_sync<T, Rep, Period>(std::move(aw), timeout);
}
static std::uint64_t parseAdditionalStat(const std::map<std::string, std::string>& stats,
std::string_view key) {
const auto it = stats.find(std::string(key));
if (it == stats.end() || it->second.empty()) {
return 0;
}
try {
return std::stoull(it->second);
} catch (...) {
return 0;
}
}
static Result<yams::search::BenchmarkDaemonGraphState>
observeDaemonGraphState(yams::daemon::DaemonClient& client) {
yams::daemon::GetStatsRequest request;
request.includeHealth = true;
auto statsResult = benchRunSync(client.getStats(request), 30s);
if (!statsResult) {
return statsResult.error();
}
const auto& stats = statsResult.value().additionalStats;
yams::search::BenchmarkDaemonGraphState state;
state.semanticDocumentsProcessed = parseAdditionalStat(stats, "embed_semantic_docs_processed");
state.semanticEdgesCreated = parseAdditionalStat(stats, "embed_semantic_edges_created");
state.semanticUpdateErrors = parseAdditionalStat(stats, "embed_semantic_update_errors");
state.topologyDocumentNodes = parseAdditionalStat(stats, "kg_topology_document_nodes");
state.topologySemanticEdges = parseAdditionalStat(stats, "kg_topology_semantic_edges");
state.topologyDocumentsWithNeighbors =
parseAdditionalStat(stats, "kg_topology_documents_with_neighbors");
state.topologyIsolatedDocuments = parseAdditionalStat(stats, "kg_topology_isolated_documents");
state.postIngestQueued = parseAdditionalStat(stats, "post_ingest_queued");
state.postIngestInFlight = parseAdditionalStat(stats, "post_ingest_inflight");
state.embeddingQueued = parseAdditionalStat(stats, "embed_svc_queued");
state.embeddingInFlight = parseAdditionalStat(stats, "embed_in_flight");
state.writeQueued = parseAdditionalStat(stats, "write_queue_depth");
state.writeInFlight = parseAdditionalStat(stats, "write_in_flight");
return state;
}
static std::string describeDaemonGraphState(const yams::search::BenchmarkDaemonGraphState& state) {
std::ostringstream message;
message << "semantic_docs_processed=" << state.semanticDocumentsProcessed
<< ", semantic_edges_created=" << state.semanticEdgesCreated
<< ", topology_document_nodes=" << state.topologyDocumentNodes
<< ", topology_semantic_edges=" << state.topologySemanticEdges
<< ", topology_documents_with_neighbors=" << state.topologyDocumentsWithNeighbors
<< ", topology_isolated_documents=" << state.topologyIsolatedDocuments
<< ", semantic_update_errors=" << state.semanticUpdateErrors
<< ", post_ingest_queued=" << state.postIngestQueued
<< ", post_ingest_in_flight=" << state.postIngestInFlight
<< ", embed_queued=" << state.embeddingQueued
<< ", embed_in_flight=" << state.embeddingInFlight
<< ", write_queued=" << state.writeQueued
<< ", write_in_flight=" << state.writeInFlight;
return message.str();
}
static Result<yams::search::BenchmarkDaemonGraphState>
waitForDaemonNativeGraph(yams::daemon::DaemonClient& client,
std::uint64_t minimumSemanticDocuments) {
spdlog::info("Waiting for daemon-native semantic graph production before query evaluation...");
const auto deadline = std::chrono::steady_clock::now() + 300s;
// Semantic-neighbor backfill is intentionally idle-only. A tight getStats loop keeps a
// non-health IPC connection active and prevents TuningManager from publishing an idle
// snapshot, so observing readiness too frequently can stop the work itself.
constexpr auto kObservationInterval = 15s;
auto observationAt = std::chrono::steady_clock::now() + kObservationInterval;
yams::search::BenchmarkDaemonGraphState state;
std::optional<yams::search::BenchmarkDaemonGraphState> previousReadyState;
while (std::chrono::steady_clock::now() < deadline) {
const auto now = std::chrono::steady_clock::now();
if (now < observationAt) {
std::this_thread::sleep_for(std::min(
500ms, std::chrono::duration_cast<std::chrono::milliseconds>(observationAt - now)));
continue;
}
observationAt = now + kObservationInterval;
auto observation = observeDaemonGraphState(client);
if (!observation) {
previousReadyState.reset();
spdlog::warn("Daemon graph observation failed: {}", observation.error().message);
continue;
}
state = std::move(observation.value());
spdlog::info("Daemon graph wait: {}", describeDaemonGraphState(state));
if (!yams::search::benchmarkDaemonGraphReady(state, minimumSemanticDocuments)) {
previousReadyState.reset();
continue;
}
if (!previousReadyState.has_value() ||
!yams::search::benchmarkDaemonGraphProductionStable(*previousReadyState, state)) {
previousReadyState = state;
continue;
}
spdlog::info("Daemon-native semantic graph ready: {}", describeDaemonGraphState(state));
return state;
}
return Error{ErrorCode::Timeout,
"Daemon-native semantic graph not ready before benchmark queries (" +
describeDaemonGraphState(state) + ")."};
}
static Result<void> exportTopologyClusters(yams::daemon::ServiceManager& serviceManager,
std::string_view snapshotId) {
const char* rawOutput = std::getenv("YAMS_BENCH_TOPOLOGY_CLUSTER_OUTPUT");
if (rawOutput == nullptr || *rawOutput == '\0') {
return {};
}
yams::topology::MetadataKgTopologyArtifactStore store(serviceManager.getMetadataRepo(),
serviceManager.getKgStore());
auto loaded = store.loadLatest(snapshotId);
if (!loaded) {
return loaded.error();
}
if (!loaded.value().has_value()) {
return Error{ErrorCode::NotFound, "topology snapshot unavailable for cluster export"};
}
const auto& snapshot = *loaded.value();
json memberships = json::array();
for (const auto& membership : snapshot.memberships) {
memberships.push_back({{"document_hash", membership.documentHash},
{"cluster_id", membership.clusterId},
{"overlap_cluster_ids", membership.overlapClusterIds}});
}
json clusters = json::array();
for (const auto& cluster : snapshot.clusters) {
clusters.push_back({{"cluster_id", cluster.clusterId},
{"member_count", cluster.memberCount},
{"overlap_cluster_ids", cluster.overlapClusterIds}});
}
const fs::path outputPath(rawOutput);
std::error_code ec;
if (!outputPath.parent_path().empty()) {
fs::create_directories(outputPath.parent_path(), ec);
if (ec) {
return Error{ErrorCode::WriteError,
"failed to create topology cluster output directory: " + ec.message()};
}
}
std::ofstream output(outputPath, std::ios::trunc);
if (!output) {
return Error{ErrorCode::WriteError, "failed to open topology cluster output"};
}
output << json{{"algorithm", snapshot.algorithm},
{"snapshot_id", snapshot.snapshotId},
{"topology_epoch", snapshot.topologyEpoch},
{"memberships", std::move(memberships)},
{"clusters", std::move(clusters)}}
.dump(2)
<< '\n';
if (!output) {
return Error{ErrorCode::WriteError, "failed to write topology cluster output"};
}
return {};
}
static Result<yams::daemon::TopologyManager::RebuildStats>
rebuildCanonicalDaemonTopology(yams::daemon::ServiceManager& serviceManager,
std::string_view topologyAlgorithm) {
auto& topologyManager = serviceManager.getTopologyManager();
const auto deadline = std::chrono::steady_clock::now() + 60s;
do {
if (topologyManager.isRebuildInProgress()) {
std::this_thread::sleep_for(50ms);
continue;
}
auto rebuilt =
topologyManager.rebuildArtifacts("benchmark_daemon_native_graph_canonicalization",
false, {}, std::string(topologyAlgorithm));
if (!rebuilt) {
return rebuilt.error();
}
if (!rebuilt.value().skipped) {
if (!rebuilt.value().stored) {
return Error{ErrorCode::InvalidState,
"canonical topology rebuild did not publish a stored snapshot"};
}
return std::move(rebuilt.value());
}
std::this_thread::sleep_for(50ms);
} while (std::chrono::steady_clock::now() < deadline);
return Error{ErrorCode::Timeout, "timed out waiting for canonical topology rebuild"};
}
static Result<std::string>
loadTopologyRoutingConstructionFingerprint(yams::daemon::ServiceManager& serviceManager,
std::string_view snapshotId) {
yams::topology::MetadataKgTopologyArtifactStore store(serviceManager.getMetadataRepo(),
serviceManager.getKgStore());
auto loaded = store.loadLatest(snapshotId);
if (!loaded) {
return loaded.error();
}
if (!loaded.value().has_value()) {
return Error{ErrorCode::NotFound,
"canonical topology snapshot unavailable for fingerprinting"};
}
return yams::search::topologyRoutingConstructionFingerprint(*loaded.value());
}
// Helper to discover GLiNER model path for entity extraction
// Searches standard locations where yams init downloads models
static std::string discoverGlinerModelPath() {
// Preferred model names in order
const std::vector<std::string> model_names = {
"gliner_small-v2.1-quantized",
"gliner_small-v2.1",
"gliner_medium-v2.1-quantized",
"gliner_medium-v2.1",
};
// Build list of base directories to search
std::vector<fs::path> search_dirs;
// YAMS_GLINT_MODEL_PATH - explicit override
if (const char* env_path = std::getenv("YAMS_GLINT_MODEL_PATH")) {
if (fs::exists(env_path)) {
spdlog::info("[Bench] Using GLiNER model from YAMS_GLINT_MODEL_PATH: {}", env_path);
return env_path;
}
}
// YAMS_DATA_DIR
if (const char* data_dir = std::getenv("YAMS_DATA_DIR")) {
search_dirs.emplace_back(fs::path(data_dir) / "models" / "gliner");
}
// YAMS_STORAGE
if (const char* storage = std::getenv("YAMS_STORAGE")) {
search_dirs.emplace_back(fs::path(storage) / "models" / "gliner");
}
// ~/.local/share/yams (XDG default)
if (const char* home = std::getenv("HOME")) {
search_dirs.emplace_back(fs::path(home) / ".local" / "share" / "yams" / "models" /
"gliner");
}
// Development path
search_dirs.emplace_back("/Volumes/picaso/yams/models/gliner");
// Homebrew path
search_dirs.emplace_back("/opt/homebrew/share/yams/models/gliner");
// Search for models
for (const auto& base_dir : search_dirs) {
if (!fs::exists(base_dir)) {
continue;
}
// Try preferred model names first
for (const auto& model_name : model_names) {
fs::path model_path = base_dir / model_name / "model.onnx";
if (fs::exists(model_path)) {
spdlog::info("[Bench] Found GLiNER model at: {}", model_path.string());
return model_path.string();
}
}
// Fall back to scanning directory for any model
std::error_code ec;
for (const auto& entry : fs::directory_iterator(base_dir, ec)) {
if (!entry.is_directory())
continue;
fs::path model_path = entry.path() / "model.onnx";
if (fs::exists(model_path)) {
spdlog::info("[Bench] Found GLiNER model at: {}", model_path.string());
return model_path.string();
}
}
}
spdlog::warn("[Bench] No GLiNER model found - entity extraction will use mock mode");
return "";
}
static bool forceMockEmbeddingsForBench() {
const char* env = std::getenv("YAMS_BENCH_FORCE_MOCK_EMBEDDINGS");
return env && std::string_view(env) == "1";
}
static void ensureBenchmarkEmbeddingsReady(yams::test::DaemonHarness* harness, bool vectorsDisabled,
bool forceMockEmbeddings, bool embedBackendSimeon) {
if (!harness || !harness->daemon() || vectorsDisabled) {
return;
}
auto serviceManager = harness->daemon()->getServiceManager();
if (!serviceManager) {
return;
}
if (forceMockEmbeddings) {
std::size_t dim = 768;
if (auto vectorDb = serviceManager->getVectorDatabase()) {
dim = vectorDb->getConfig().embedding_dim;
}
auto sharedProvider = std::make_shared<BenchmarkMockModelProvider>(dim);
serviceManager->testing_setModelProvider(sharedProvider);
}
if (embedBackendSimeon) {
auto ready = serviceManager->ensureEmbeddingModelReadySync("simeon-default", {}, 10000,
false, false);
if (!ready) {
spdlog::warn("[Bench] Simeon provider not ready for retrieval bench: {}",
ready.error().message);
}
return;
}
auto ready =
serviceManager->ensureEmbeddingModelReadySync("all-MiniLM-L6-v2", {}, 10000, false, false);
if (!ready) {
spdlog::warn("[Bench] Embedding model not ready for retrieval bench: {}",
ready.error().message);
}
}
using yams::bench::BEIRCorpusLoader;
using yams::bench::BEIRDataset;
using yams::bench::BEIRDocument;
using yams::bench::BEIRQuery;
using yams::bench::CorpusGenerator;
using yams::bench::RetrievalMetrics;
using yams::bench::StageMetricAccumulator;
using yams::bench::SyntheticCorpusMode;
using yams::bench::TestQuery;
static Result<BEIRDataset> loadSciFactDataset(const fs::path& cacheDir) {
const char* home = std::getenv("HOME");
if (!home) {
return Error{ErrorCode::InvalidArgument, "HOME environment variable not set"};
}
fs::path scifactDir =
cacheDir.empty() ? fs::path(home) / ".cache" / "yams" / "benchmarks" / "scifact" : cacheDir;
BEIRDataset dataset;
dataset.name = "SciFact";
dataset.basePath = scifactDir;
fs::path corpusFile = scifactDir / "corpus.jsonl";
fs::path queriesFile = scifactDir / "queries.jsonl";
fs::path qrelsFile = scifactDir / "qrels" / "test.tsv"; // Standard BEIR structure
if (!fs::exists(corpusFile) || !fs::exists(queriesFile) || !fs::exists(qrelsFile)) {
return Error{
ErrorCode::NotFound,
"SciFact dataset not found. Please download manually:\n"
" mkdir -p ~/.cache/yams/benchmarks && cd ~/.cache/yams/benchmarks\n"
" curl -L -o scifact.zip "
"https://public.ukp.informatik.tu-darmstadt.de/thakur/BEIR/datasets/scifact.zip\n"
" unzip scifact.zip"};
}
std::ifstream corpusIn(corpusFile);
std::string line;
while (std::getline(corpusIn, line)) {
if (line.empty())
continue;
try {
auto j = json::parse(line);
BEIRDocument doc;
doc.id = j.value("_id", "");
if (doc.id.empty())
doc.id = j.value("id", "");
doc.title = j.value("title", "");
doc.text = j.value("text", "");
yams::bench::readBEIRDocumentMetadata(j, doc);
if (!doc.id.empty() && !doc.text.empty()) {
dataset.documents[doc.id] = doc;
}
} catch (const json::exception& e) {
spdlog::warn("Failed to parse corpus line: {}", e.what());
}
}
corpusIn.close();
std::ifstream queriesIn(queriesFile);
while (std::getline(queriesIn, line)) {
if (line.empty())
continue;
try {
auto j = json::parse(line);
BEIRQuery q;
q.id = j.value("_id", "");
if (q.id.empty())
q.id = j.value("id", "");
q.text = j.value("text", "");
if (!q.id.empty() && !q.text.empty()) {
dataset.queries[q.id] = q;
}
} catch (const json::exception& e) {
spdlog::warn("Failed to parse query line: {}", e.what());
}
}
queriesIn.close();
std::ifstream qrelsIn(qrelsFile);
bool firstLine = true;
while (std::getline(qrelsIn, line)) {
if (line.empty() || line[0] == '#')
continue;
// Skip header row
if (firstLine) {
firstLine = false;
if (line.find("query-id") != std::string::npos)
continue;
}
std::istringstream iss(line);
std::string queryId, docId, scoreStr;
// BEIR format: query-id, corpus-id, score (3 columns, no iteration)
if (std::getline(iss, queryId, '\t') && std::getline(iss, docId, '\t') &&
std::getline(iss, scoreStr, '\t')) {
int score = std::stoi(scoreStr);
dataset.qrels.emplace(queryId, yams::bench::BEIRQrel{.docId = docId, .score = score});
}
}
qrelsIn.close();
spdlog::info("Loaded BEIR dataset: {} documents, {} queries, {} qrels",
dataset.documents.size(), dataset.queries.size(), dataset.qrels.size());
return dataset;
}
// Generic BEIR dataset loader — delegates to yams::bench::loadBEIRDataset.
static Result<BEIRDataset> loadBEIRDataset(const std::string& datasetName,
const fs::path& cacheDir) {
if (datasetName == "local-manifest") {
return yams::bench::loadBenchmarkManifestDataset(datasetName, cacheDir);
}
return yams::bench::loadBEIRDataset(datasetName, cacheDir);
}
static Result<BEIRDataset> selectBenchmarkBEIRDataset(const std::string& datasetName,
const fs::path& cacheDir,
const std::optional<int>& maxDocsOpt,
const std::optional<int>& maxQueriesOpt) {
Result<BEIRDataset> dsResult;
if (datasetName == "scifact") {
dsResult = loadSciFactDataset(cacheDir);
} else {
dsResult = loadBEIRDataset(datasetName, cacheDir);
}
if (!dsResult) {
return dsResult;
}
const BEIRDataset& fullDataset = dsResult.value();
BEIRDataset dataset;
dataset.name = fullDataset.name;
dataset.basePath = fullDataset.basePath;
const int maxDocs =
maxDocsOpt.has_value() ? *maxDocsOpt : static_cast<int>(fullDataset.documents.size());
const int maxQueries =
maxQueriesOpt.has_value() ? *maxQueriesOpt : static_cast<int>(fullDataset.queries.size());
std::set<std::string> qrelDocIds;
for (const auto& [queryId, docScore] : fullDataset.qrels) {
(void)queryId;
qrelDocIds.insert(docScore.docId);
}
spdlog::info("BEIR dataset has {} documents referenced by qrels", qrelDocIds.size());
std::set<std::string> includedDocIds;
for (const auto& docId : qrelDocIds) {
if (static_cast<int>(includedDocIds.size()) >= maxDocs) {
break;
}
if (fullDataset.documents.count(docId) > 0) {
dataset.documents[docId] = fullDataset.documents.at(docId);
includedDocIds.insert(docId);
}
}
spdlog::info("Included {} qrel-referenced documents", includedDocIds.size());
for (const auto& [id, doc] : fullDataset.documents) {
if (static_cast<int>(includedDocIds.size()) >= maxDocs) {
break;
}
if (includedDocIds.count(id) == 0) {
dataset.documents[id] = doc;
includedDocIds.insert(id);
}
}
int queryCount = 0;
for (const auto& [qid, query] : fullDataset.queries) {
if (queryCount >= maxQueries) {
break;
}
auto range = fullDataset.qrels.equal_range(qid);
bool hasRelevantDoc = false;
for (auto it = range.first; it != range.second; ++it) {
if (includedDocIds.count(it->second.docId) > 0) {
hasRelevantDoc = true;
break;
}
}
if (!hasRelevantDoc) {
continue;
}
dataset.queries[qid] = query;
for (auto it = range.first; it != range.second; ++it) {
if (includedDocIds.count(it->second.docId) > 0) {
dataset.qrels.emplace(qid, it->second);
}
}
queryCount++;
}
for (const auto& entity : fullDataset.kgEntities) {
if (includedDocIds.contains(entity.docId)) {
dataset.kgEntities.push_back(entity);
}
}
spdlog::debug(
"Limited BEIR dataset to {} docs, {} queries, {} qrels, {} kg entities (from {} docs, {} "
"queries)",
dataset.documents.size(), dataset.queries.size(), dataset.qrels.size(),
dataset.kgEntities.size(), fullDataset.documents.size(), fullDataset.queries.size());
return dataset;
}
using yams::test::DaemonHarness;
struct QueryDiagnosticsSummary {
std::uint64_t queryCount = 0;
std::uint64_t queryWithResultsCount = 0;
std::uint64_t queryWithoutResultsCount = 0;
std::uint64_t queryWithoutRelevantHitCount = 0;
std::uint64_t queryWithRelevantInPreFusionCount = 0;
std::uint64_t queryWithRelevantInPostFusionCount = 0;
std::uint64_t queryWithRelevantInGraphlessPostFusionCount = 0;
std::uint64_t queryWithRelevantInFinalWindowCount = 0;
std::uint64_t queryWithGraphAddedRelevantPostFusionCount = 0;
std::uint64_t queryWithGraphDisplacedRelevantPostFusionCount = 0;
std::uint64_t queryWithTopologyAddedRelevantPostFusionCount = 0;
std::uint64_t queryWithTopologyAddedRelevantFusionDroppedCount = 0;
std::uint64_t queryWithTopKDuplicateCount = 0;
std::uint64_t degradedQueryCount = 0;
std::uint64_t traceEnabledQueryCount = 0;
std::uint64_t graphRerankAppliedQueryCount = 0;
std::uint64_t compressedAnnEnabledQueryCount = 0;
std::uint64_t compressedAnnAppliedQueryCount = 0;
std::uint64_t semanticRescueNonZeroQueryCount = 0;
std::uint64_t missMissingPreFusionCount = 0;
std::uint64_t missFusionCutoffCount = 0;
std::uint64_t missRerankWindowDropCount = 0;
std::uint64_t missTopKDropCount = 0;
std::vector<double> semanticRescueRateSamples;
std::vector<double> semanticRescueFinalCountSamples;
std::vector<double> semanticRescueTargetSamples;
std::vector<double> compressedAnnResultCountSamples;
std::unordered_map<std::string, std::uint64_t> compressedAnnSkipReasonCounts;
std::vector<double> fusionPreFusionUniqueCountSamples;
std::vector<double> fusionPostFusionCountSamples;
std::vector<double> fusionDroppedCountSamples;
std::vector<double> fusionDroppedRateSamples;
std::vector<double> anchoredPreFusionCountSamples;
std::vector<double> anchoredFusionDroppedCountSamples;
std::vector<double> anchoredFusionDroppedRateSamples;
std::vector<double> topTextPreFusionCountSamples;
std::vector<double> topTextFusionDroppedCountSamples;
std::vector<double> topTextFusionDroppedRateSamples;
std::vector<double> vectorOnlyDocsSamples;
std::vector<double> vectorOnlyBelowThresholdSamples;
std::vector<double> vectorOnlyAboveThresholdSamples;
std::vector<double> vectorOnlyNearMissEligibleSamples;
std::uint64_t adaptiveFusionEnabledQueryCount = 0;
std::vector<double> strongVectorOnlyDocsSamples;
std::vector<double> strongVectorOnlyScoreEligibleDocsSamples;
std::vector<double> strongVectorOnlyRankEligibleDocsSamples;
std::vector<double> graphAddedPostFusionSamples;
std::vector<double> graphDisplacedPostFusionSamples;
std::vector<double> topologyRoutedDocsSamples;
std::vector<double> topologyRouteBestScoreSamples;
std::vector<double> topologyRouteMeanAcceptedScoreSamples;
std::vector<double> topologyRouteAcceptedCountSamples;
std::vector<double> topologySeedCountSamples;
std::vector<double> topologySeedCoverageCountSamples;
std::vector<double> topologyCoreCandidateCoverageSamples;
std::vector<double> topologyAddedCandidatesSamples;
std::vector<double> topologyDuplicateCandidatesSamples;
std::vector<double> topologyStaleCandidatesSamples;
std::vector<double> graphCommunitySupportedDocsSamples;
std::vector<double> graphCommunityEdgeCountSamples;
std::vector<double> graphCommunityLargestSizeSamples;
std::vector<double> graphCommunitySignalMassSamples;
std::vector<double> graphCommunityBoostedDocsSamples;
std::vector<double> graphCommunityWeightSamples;
std::vector<double> multiVectorGeneratedPhraseSamples;
std::vector<double> multiVectorRawHitSamples;
std::vector<double> multiVectorAddedNewSamples;
std::vector<double> multiVectorReplacedBaseSamples;
std::vector<double> subPhraseGeneratedSamples;
std::vector<double> subPhraseClauseSamples;
std::vector<double> subPhraseFtsHitSamples;
std::vector<double> subPhraseFtsAddedSamples;
std::vector<double> aggressiveFtsClauseSamples;
std::vector<double> aggressiveFtsHitSamples;
std::vector<double> aggressiveFtsAddedSamples;
std::unordered_map<std::string, std::vector<double>> timingSamplesMs;
std::unordered_map<std::string, std::uint64_t> stageEnabledCount;
std::unordered_map<std::string, std::uint64_t> stageAttemptedCount;
std::unordered_map<std::string, std::uint64_t> stageContributedCount;
std::unordered_map<std::string, std::uint64_t> stageSkippedCount;
std::unordered_map<std::string, std::uint64_t> stageRelevantPresenceCount;
std::unordered_map<std::string, std::vector<std::string>> stageSkipReasons;
std::unordered_map<std::string, std::vector<double>> fusionSourceScoreMassSamples;
std::unordered_map<std::string, std::vector<double>> fusionSourceFinalDocSamples;
std::unordered_map<std::string, std::vector<double>> stageUniqueDocCountSamples;
std::unordered_map<std::string, std::vector<double>> stageRelevantDocCountSamples;
std::map<std::string, StageMetricAccumulator> stageRetrievalMetrics;
std::unordered_map<std::string, std::vector<double>> tunerSignalSamples;
std::unordered_map<std::string, std::uint64_t> tunerDecisionCounts;
std::unordered_map<std::string, std::uint64_t> zoomLevelCounts;
};
static const std::vector<std::string>& trackedTimingKeys() {
static const std::vector<std::string> kKeys = {
"latency",
"embedding",
"concepts",
"text",
"vector",
"entity_vector",
"kg",
"path",
"tag",
"metadata",
"multi_vector",
"graph_rerank",
"topology_weak_query",
"topology_load",
"topology_validate",
"topology_request_prep",
"topology_route",
"topology_cluster_lookup",
"topology_doc_lookup",
"topology_candidate_insert",
};
return kKeys;
}
static std::string timingStatKey(const std::string& name) {
return std::string("timing_") + name + "_ms";
}
static std::unordered_set<std::string> splitTabSet(const std::string& value) {
std::unordered_set<std::string> out;
if (value.empty()) {
return out;
}
size_t start = 0;
while (start <= value.size()) {
const size_t end = value.find('\t', start);
const size_t len = (end == std::string::npos) ? (value.size() - start) : (end - start);
if (len > 0) {
out.insert(value.substr(start, len));
}
if (end == std::string::npos) {
break;
}
start = end + 1;
}
return out;
}
static std::vector<std::string> splitTabList(const std::string& value, size_t limit) {
std::vector<std::string> out;
if (value.empty() || limit == 0) {
return out;
}
size_t start = 0;
while (start <= value.size() && out.size() < limit) {
const size_t end = value.find('\t', start);
const size_t len = (end == std::string::npos) ? (value.size() - start) : (end - start);
if (len > 0) {
out.push_back(value.substr(start, len));
}
if (end == std::string::npos) {
break;
}
start = end + 1;
}
return out;
}
static std::optional<size_t> findRankInTabList(const std::string& value,
const std::string& needle) {
if (value.empty() || needle.empty()) {
return std::nullopt;
}
size_t start = 0;
size_t rank = 0;
while (start <= value.size()) {
const size_t end = value.find('\t', start);
const size_t len = (end == std::string::npos) ? (value.size() - start) : (end - start);
if (len > 0) {
++rank;
if (value.compare(start, len, needle) == 0 && len == needle.size()) {
return rank;
}
}
if (end == std::string::npos) {
break;
}
start = end + 1;
}
return std::nullopt;
}
static json buildRelevantDecisionTrace(const std::vector<std::string>& relevantDocIds,
const std::vector<std::string>& returnedDocIds,
const std::map<std::string, std::string>& searchStats) {
const std::string preFusionDocIds = searchStats.contains("trace_pre_fusion_doc_ids")
? searchStats.at("trace_pre_fusion_doc_ids")
: "";
const std::string postFusionDocIds = searchStats.contains("trace_post_fusion_doc_ids")
? searchStats.at("trace_post_fusion_doc_ids")
: "";
const std::string graphlessPostFusionDocIds =
searchStats.contains("trace_graphless_post_fusion_doc_ids")
? searchStats.at("trace_graphless_post_fusion_doc_ids")
: "";
const std::string finalWindowDocIds =
searchStats.contains("trace_final_doc_ids") ? searchStats.at("trace_final_doc_ids") : "";
const auto preSet = splitTabSet(searchStats.contains("trace_pre_fusion_doc_ids")
? searchStats.at("trace_pre_fusion_doc_ids")
: "");
const auto postSet = splitTabSet(searchStats.contains("trace_post_fusion_doc_ids")
? searchStats.at("trace_post_fusion_doc_ids")
: "");
const auto graphlessPostSet =
splitTabSet(searchStats.contains("trace_graphless_post_fusion_doc_ids")
? searchStats.at("trace_graphless_post_fusion_doc_ids")
: "");
const auto finalWindowSet = splitTabSet(
searchStats.contains("trace_final_doc_ids") ? searchStats.at("trace_final_doc_ids") : "");
std::string topologyAddedIds;
for (const std::string_view key : {
std::string_view{"topology_candidate_rescue_added_candidate_doc_ids"},
std::string_view{"topology_candidate_rescue_added_candidate_hashes"},
std::string_view{"topology_weak_query_added_candidate_doc_ids"},
std::string_view{"topology_weak_query_added_candidate_hashes"},
}) {
if (const auto it = searchStats.find(std::string(key));
it != searchStats.end() && !it->second.empty()) {
topologyAddedIds = it->second;
break;
}
}
const auto topologyAddedSet = splitTabSet(topologyAddedIds);
const auto topologyNovelSet =
splitTabSet(searchStats.contains("topology_candidate_rescue_novel_candidate_doc_ids")
? searchStats.at("topology_candidate_rescue_novel_candidate_doc_ids")
: "");
const auto topologyEvidenceRescueSet =
splitTabSet(searchStats.contains("topology_candidate_rescue_evidence_rescue_doc_ids")
? searchStats.at("topology_candidate_rescue_evidence_rescue_doc_ids")
: "");
const std::unordered_set<std::string> returnedTopKSet(returnedDocIds.begin(),
returnedDocIds.end());
json fusionTop = json::array();
if (auto it = searchStats.find("trace_fusion_top_json"); it != searchStats.end()) {
try {
fusionTop = json::parse(it->second);
} catch (...) {
}
}
json finalTop = json::array();
if (auto it = searchStats.find("trace_final_top_json"); it != searchStats.end()) {
try {
finalTop = json::parse(it->second);
} catch (...) {
}
}
json componentHits = json::object();
if (auto it = searchStats.find("trace_component_hits_json"); it != searchStats.end()) {
try {
componentHits = json::parse(it->second);
} catch (...) {
}
}
json stageSummary = json::object();
if (auto it = searchStats.find("trace_stage_summary_json"); it != searchStats.end()) {
try {
stageSummary = json::parse(it->second);
} catch (...) {
}
}
const auto findDoc = [](const json& docs, const std::string& docId) -> json {
if (!docs.is_array()) {
return json();
}
for (const auto& item : docs) {
if (item.is_object() && item.value("doc_id", "") == docId) {
return item;
}
}
return json();
};
const auto findComponentHit = [](const json& componentHitsJson,
const std::string& docId) -> json {
if (!componentHitsJson.is_object()) {
return json::array();
}
json matches = json::array();
for (const auto& [component, details] : componentHitsJson.items()) {
if (!details.is_object()) {
continue;
}
const auto hitsIt = details.find("top_hits");
if (hitsIt == details.end() || !hitsIt->is_array()) {
continue;
}
for (const auto& hit : *hitsIt) {
if (!hit.is_object() || hit.value("doc_id", "") != docId) {
continue;
}
json entry = hit;
entry["component"] = component;
matches.push_back(std::move(entry));
break;
}
}
return matches;
};
json relevant = json::array();
bool anyPre = false;
bool anyPost = false;
bool anyGraphlessPost = false;
bool anyFinalWindow = false;
bool anyReturnedTopK = false;
json graphAddedRelevant = json::array();
json graphDisplacedRelevant = json::array();
json topologyAddedRelevantPostFusion = json::array();
json topologyAddedRelevantDroppedByFusion = json::array();
json stageRelevantPresence = json::object();
if (stageSummary.is_object()) {
for (const auto& [stageName, stageData] : stageSummary.items()) {
if (!stageData.is_object()) {
continue;
}
bool anyRelevant = false;
std::size_t relevantDocCount = 0;
if (auto idsIt = stageData.find("unique_doc_ids");
idsIt != stageData.end() && idsIt->is_array()) {
for (const auto& id : *idsIt) {
if (!id.is_string()) {
continue;
}
if (std::find(relevantDocIds.begin(), relevantDocIds.end(),
id.get<std::string>()) != relevantDocIds.end()) {
anyRelevant = true;
++relevantDocCount;
}
}
}
stageRelevantPresence[stageName] = {
{"enabled", stageData.value("enabled", false)},
{"attempted", stageData.value("attempted", false)},
{"contributed", stageData.value("contributed", false)},
{"unique_doc_count", stageData.value("unique_doc_count", 0UL)},
{"any_relevant", anyRelevant},
{"relevant_doc_count", relevantDocCount},
};
}
}
for (const auto& docId : relevantDocIds) {
const bool inPre = preSet.contains(docId);
const bool inPost = postSet.contains(docId);
const bool inGraphlessPost = graphlessPostSet.contains(docId);
const bool inFinalWindow = finalWindowSet.contains(docId);
const bool inReturnedTopK = returnedTopKSet.contains(docId);
anyPre = anyPre || inPre;
anyPost = anyPost || inPost;
anyGraphlessPost = anyGraphlessPost || inGraphlessPost;
anyFinalWindow = anyFinalWindow || inFinalWindow;
anyReturnedTopK = anyReturnedTopK || inReturnedTopK;
if (inPost && !inGraphlessPost) {
graphAddedRelevant.push_back(docId);
}
if (inGraphlessPost && !inPost) {
graphDisplacedRelevant.push_back(docId);
}
const bool topologyAddedCandidate = topologyAddedSet.contains(docId);
const bool topologyNovelCandidate = topologyNovelSet.contains(docId);
const bool topologyEvidenceRescue = topologyEvidenceRescueSet.contains(docId);
if (topologyAddedCandidate && inPost) {
topologyAddedRelevantPostFusion.push_back(docId);
}
if (topologyAddedCandidate && !inPost) {
topologyAddedRelevantDroppedByFusion.push_back(docId);
}
json componentSources = json::array();
if (componentHits.is_object()) {
for (const auto& [component, details] : componentHits.items()) {
if (!details.is_object()) {
continue;
}
const auto topIdsIt = details.find("unique_top_doc_ids");
if (topIdsIt == details.end() || !topIdsIt->is_array()) {
continue;
}
for (const auto& id : *topIdsIt) {
if (id.is_string() && id.get<std::string>() == docId) {
componentSources.push_back(component);
break;
}
}
}
}
json entry = {
{"doc_id", docId},
{"in_pre_fusion", inPre},
{"in_post_fusion", inPost},
{"in_graphless_post_fusion", inGraphlessPost},
{"in_final_window", inFinalWindow},
{"in_returned_topk", inReturnedTopK},
{"topology_added_candidate", topologyAddedCandidate},
{"topology_novel_candidate", topologyNovelCandidate},
{"topology_evidence_rescue", topologyEvidenceRescue},
{"component_top_hits", componentSources},
{"component_top_hit_details", findComponentHit(componentHits, docId)},
};
const auto assignOptionalRank = [&entry](std::string_view key,
const std::optional<size_t>& rank) {
if (rank.has_value()) {
entry[std::string(key)] = *rank;
} else {
entry[std::string(key)] = nullptr;
}
};
assignOptionalRank("pre_fusion_rank", findRankInTabList(preFusionDocIds, docId));
assignOptionalRank("post_fusion_rank", findRankInTabList(postFusionDocIds, docId));
assignOptionalRank("graphless_post_fusion_rank",
findRankInTabList(graphlessPostFusionDocIds, docId));
assignOptionalRank("final_window_rank", findRankInTabList(finalWindowDocIds, docId));
if (auto match = findDoc(fusionTop, docId); !match.is_null()) {
entry["post_fusion"] = match;
}
if (auto match = findDoc(finalTop, docId); !match.is_null()) {
entry["final_window"] = match;
}
relevant.push_back(std::move(entry));
}
std::string missStage = "hit";
if (!anyReturnedTopK) {
if (!anyPre) {
missStage = "missing_pre_fusion";
} else if (!anyPost) {
missStage = "dropped_before_fusion_window";
} else if (!anyFinalWindow) {
missStage = "dropped_during_rerank_window";
} else {
missStage = "dropped_before_topk";
}
}
return {
{"trace_enabled",
searchStats.contains("trace_enabled") && searchStats.at("trace_enabled") == "1"},
{"graph_rerank_applied", searchStats.contains("trace_graph_rerank_applied") &&
searchStats.at("trace_graph_rerank_applied") == "1"},
{"cross_rerank_applied", searchStats.contains("trace_cross_rerank_applied") &&
searchStats.at("trace_cross_rerank_applied") == "1"},
{"any_relevant_in_pre_fusion", anyPre},
{"any_relevant_in_post_fusion", anyPost},
{"any_relevant_in_graphless_post_fusion", anyGraphlessPost},
{"any_relevant_in_final_window", anyFinalWindow},
{"any_relevant_in_returned_topk", anyReturnedTopK},
{"graph_added_relevant_post_fusion_count", graphAddedRelevant.size()},
{"graph_displaced_relevant_post_fusion_count", graphDisplacedRelevant.size()},
{"graph_added_relevant_post_fusion_doc_ids", graphAddedRelevant},
{"graph_displaced_relevant_post_fusion_doc_ids", graphDisplacedRelevant},
{"topology_added_relevant_post_fusion_count", topologyAddedRelevantPostFusion.size()},
{"topology_added_relevant_fusion_dropped_count",
topologyAddedRelevantDroppedByFusion.size()},
{"topology_added_relevant_post_fusion_doc_ids", topologyAddedRelevantPostFusion},
{"topology_added_relevant_fusion_dropped_doc_ids", topologyAddedRelevantDroppedByFusion},
{"stage_relevant_presence", stageRelevantPresence},
{"miss_stage", missStage},
{"relevant_docs", relevant},
};
}
struct DebugLogEntry {
std::string query;
int queryIndex = -1;
std::string searchType;
std::vector<std::string> relevantDocIds;
std::vector<std::string> relevantFiles;
std::vector<std::string> returnedPaths;
std::vector<std::string> returnedDocIds;
std::vector<float> returnedScores;
std::vector<int> returnedGrades;
std::vector<std::string> diagnostics;
std::uint64_t attempts = 0;
bool usedStreaming = false;
bool usedFuzzyFlag = false;
bool usedLiteralFlag = false;
bool usedFuzzyRetry = false;
bool usedLiteralTextRetry = false;
std::map<std::string, std::string> searchStats;
json extraFields;
};
struct DebugRunContext {
std::string runId;
std::string candidate;
std::string dataset;
int corpusSize = 0;
int numQueries = 0;
int topK = 0;
int traceTopN = 0;
int traceComponentTopN = 0;
bool stageTraceEnabled = false;
std::string debugFile;
};
static std::ostream* g_debugOut = nullptr;
static std::unique_ptr<std::ofstream> g_debugFile;
static std::mutex g_debugMutex;
static std::optional<DebugRunContext> g_debugRunContext;
static std::optional<double> parseDoubleLoose(const std::string& value) {
try {
return std::stod(value);
} catch (...) {
return std::nullopt;
}
}
static std::optional<double> parseDoubleStat(const std::map<std::string, std::string>& stats,
const std::string& key) {
if (auto it = stats.find(key); it != stats.end()) {
return parseDoubleLoose(it->second);
}
return std::nullopt;
}
static std::optional<bool> parseBoolStat(const std::map<std::string, std::string>& stats,
const std::string& key) {
if (auto it = stats.find(key); it != stats.end()) {
std::string normalized = it->second;
std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
return normalized == "1" || normalized == "true" || normalized == "yes" ||
normalized == "on";
}
return std::nullopt;
}
static std::optional<json> parseJsonStat(const std::map<std::string, std::string>& stats,
const std::string& key) {
if (auto it = stats.find(key); it != stats.end() && !it->second.empty()) {
try {
return json::parse(it->second);
} catch (...) {
}
}
return std::nullopt;
}
static double computePercentile(std::vector<double> samples, double percentile) {
if (samples.empty()) {
return 0.0;
}
percentile = std::clamp(percentile, 0.0, 1.0);
std::sort(samples.begin(), samples.end());
const double idx = percentile * static_cast<double>(samples.size() - 1);
const auto lo = static_cast<std::size_t>(std::floor(idx));
const auto hi = static_cast<std::size_t>(std::ceil(idx));
if (lo == hi) {
return samples[lo];
}
const double t = idx - static_cast<double>(lo);
return samples[lo] * (1.0 - t) + samples[hi] * t;
}
static json summarizeSamples(const std::vector<double>& samples) {
json out = {
{"count", samples.size()},
{"mean", 0.0},
{"min", 0.0},
{"p50", 0.0},
{"p95", 0.0},
{"max", 0.0},
};
if (samples.empty()) {
return out;
}
const double sum = std::accumulate(samples.begin(), samples.end(), 0.0);
out["mean"] = sum / static_cast<double>(samples.size());
out["min"] = *std::min_element(samples.begin(), samples.end());
out["p50"] = computePercentile(samples, 0.50);
out["p95"] = computePercentile(samples, 0.95);
out["max"] = *std::max_element(samples.begin(), samples.end());
return out;
}
static void ingestQueryDiagnostics(QueryDiagnosticsSummary& summary,
const std::map<std::string, std::string>& searchStats,
const json& relevantDecisionTrace, bool degraded,
bool hadResults, bool hadTopKDuplicate, int firstRelevantRank) {
summary.queryCount++;
if (hadResults) {
summary.queryWithResultsCount++;
} else {
summary.queryWithoutResultsCount++;
}
if (firstRelevantRank < 0) {
summary.queryWithoutRelevantHitCount++;
}
if (hadTopKDuplicate) {
summary.queryWithTopKDuplicateCount++;
}
if (degraded) {
summary.degradedQueryCount++;
}
if (relevantDecisionTrace.is_object()) {
const bool anyPre = relevantDecisionTrace.value("any_relevant_in_pre_fusion", false);
const bool anyPost = relevantDecisionTrace.value("any_relevant_in_post_fusion", false);
const bool anyGraphlessPost =
relevantDecisionTrace.value("any_relevant_in_graphless_post_fusion", false);
const bool anyFinalWindow =
relevantDecisionTrace.value("any_relevant_in_final_window", false);
if (anyPre) {
summary.queryWithRelevantInPreFusionCount++;
}
if (anyPost) {
summary.queryWithRelevantInPostFusionCount++;
}
if (anyGraphlessPost) {
summary.queryWithRelevantInGraphlessPostFusionCount++;
}
if (anyFinalWindow) {
summary.queryWithRelevantInFinalWindowCount++;
}
if (relevantDecisionTrace.value("graph_added_relevant_post_fusion_count", 0) > 0) {
summary.queryWithGraphAddedRelevantPostFusionCount++;
}
if (relevantDecisionTrace.value("graph_displaced_relevant_post_fusion_count", 0) > 0) {
summary.queryWithGraphDisplacedRelevantPostFusionCount++;
}
if (relevantDecisionTrace.value("topology_added_relevant_post_fusion_count", 0) > 0) {
summary.queryWithTopologyAddedRelevantPostFusionCount++;
}
if (relevantDecisionTrace.value("topology_added_relevant_fusion_dropped_count", 0) > 0) {
summary.queryWithTopologyAddedRelevantFusionDroppedCount++;
}
if (firstRelevantRank < 0) {
const std::string missStage = relevantDecisionTrace.value("miss_stage", "unknown");
if (missStage == "missing_pre_fusion") {
summary.missMissingPreFusionCount++;
} else if (missStage == "dropped_before_fusion_window") {
summary.missFusionCutoffCount++;
} else if (missStage == "dropped_during_rerank_window") {
summary.missRerankWindowDropCount++;
} else if (missStage == "dropped_before_topk") {
summary.missTopKDropCount++;
}
}
}
if (parseBoolStat(searchStats, "trace_enabled").value_or(false)) {
summary.traceEnabledQueryCount++;
}
if (parseBoolStat(searchStats, "trace_graph_rerank_applied").value_or(false)) {
summary.graphRerankAppliedQueryCount++;
}
if (auto it = searchStats.find("trace_zoom_level");
it != searchStats.end() && !it->second.empty()) {
summary.zoomLevelCounts[it->second]++;
}
bool compressedAnnEnabled =
parseBoolStat(searchStats, "compressed_ann_enabled").value_or(false);
bool compressedAnnApplied =
parseBoolStat(searchStats, "compressed_ann_applied").value_or(false);
std::optional<std::string> compressedAnnSkipReason;
if (auto it = searchStats.find("compressed_ann_skip_reason");
it != searchStats.end() && !it->second.empty()) {
compressedAnnSkipReason = it->second;
}
if (auto stageSummary = parseJsonStat(searchStats, "trace_stage_summary_json");
stageSummary && stageSummary->is_object()) {
if (auto stageIt = stageSummary->find("compressed_ann");
stageIt != stageSummary->end() && stageIt->is_object()) {
compressedAnnEnabled = stageIt->value("enabled", compressedAnnEnabled);
compressedAnnApplied = stageIt->value("contributed", compressedAnnApplied);
if (stageIt->value("skipped", false)) {
auto stageSkipReason = stageIt->value("skip_reason", std::string{});
if (!stageSkipReason.empty()) {
compressedAnnSkipReason = stageSkipReason;
}
} else if (stageIt->value("attempted", false) &&
compressedAnnSkipReason == "adaptive_vector_skip") {
compressedAnnSkipReason.reset();
}
}
}
if (compressedAnnEnabled) {
summary.compressedAnnEnabledQueryCount++;
}
if (compressedAnnApplied) {
summary.compressedAnnAppliedQueryCount++;
}
if (auto v = parseDoubleStat(searchStats, "compressed_ann_result_count")) {
summary.compressedAnnResultCountSamples.push_back(*v);
}
if (compressedAnnSkipReason.has_value() && !compressedAnnSkipReason->empty()) {
summary.compressedAnnSkipReasonCounts[*compressedAnnSkipReason]++;
}
if (auto v = parseDoubleStat(searchStats, "semantic_rescue_rate")) {
summary.semanticRescueRateSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "semantic_rescue_final_count")) {
summary.semanticRescueFinalCountSamples.push_back(*v);
if (*v > 0.0) {
summary.semanticRescueNonZeroQueryCount++;
}
}
if (auto v = parseDoubleStat(searchStats, "semantic_rescue_target")) {
summary.semanticRescueTargetSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "fusion_pre_fusion_unique_count")) {
summary.fusionPreFusionUniqueCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "fusion_post_fusion_count")) {
summary.fusionPostFusionCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "fusion_dropped_count")) {
summary.fusionDroppedCountSamples.push_back(*v);
} else if (auto v = parseDoubleStat(searchStats, "trace_fusion_dropped_count")) {
summary.fusionDroppedCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "fusion_dropped_rate")) {
summary.fusionDroppedRateSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_weak_query_routed_docs")) {
summary.topologyRoutedDocsSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_route_best_score")) {
summary.topologyRouteBestScoreSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_route_mean_accepted_score")) {
summary.topologyRouteMeanAcceptedScoreSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_route_accepted_count")) {
summary.topologyRouteAcceptedCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_seed_count")) {
summary.topologySeedCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_seed_coverage_count")) {
summary.topologySeedCoverageCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_core_candidate_coverage")) {
summary.topologyCoreCandidateCoverageSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_weak_query_added_candidates")) {
summary.topologyAddedCandidatesSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_weak_query_duplicate_candidates")) {
summary.topologyDuplicateCandidatesSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "topology_weak_query_stale_candidates")) {
summary.topologyStaleCandidatesSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "anchored_pre_fusion_count")) {
summary.anchoredPreFusionCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "anchored_fusion_dropped_count")) {
summary.anchoredFusionDroppedCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "anchored_fusion_dropped_rate")) {
summary.anchoredFusionDroppedRateSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "top_text_pre_fusion_count")) {
summary.topTextPreFusionCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "top_text_fusion_dropped_count")) {
summary.topTextFusionDroppedCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "top_text_fusion_dropped_rate")) {
summary.topTextFusionDroppedRateSamples.push_back(*v);
}
const bool hasDirectPreFusionStats = searchStats.find("vector_only_docs") != searchStats.end();
if (auto v = parseDoubleStat(searchStats, "vector_only_docs")) {
summary.vectorOnlyDocsSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "vector_only_below_threshold")) {
summary.vectorOnlyBelowThresholdSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "vector_only_above_threshold")) {
summary.vectorOnlyAboveThresholdSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "vector_only_near_miss_eligible")) {
summary.vectorOnlyNearMissEligibleSamples.push_back(*v);
}
if (parseBoolStat(searchStats, "adaptive_fusion_enabled").value_or(false)) {
summary.adaptiveFusionEnabledQueryCount++;
}
if (auto v = parseDoubleStat(searchStats, "multi_vector_generated_phrases")) {
summary.multiVectorGeneratedPhraseSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "multi_vector_raw_hit_count")) {
summary.multiVectorRawHitSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "multi_vector_added_new_count")) {
summary.multiVectorAddedNewSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "multi_vector_replaced_base_count")) {
summary.multiVectorReplacedBaseSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "subphrase_generated_count")) {
summary.subPhraseGeneratedSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "subphrase_clause_count")) {
summary.subPhraseClauseSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "subphrase_fts_hit_count")) {
summary.subPhraseFtsHitSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "subphrase_fts_added_count")) {
summary.subPhraseFtsAddedSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "aggressive_fts_clause_count")) {
summary.aggressiveFtsClauseSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "aggressive_fts_hit_count")) {
summary.aggressiveFtsHitSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "aggressive_fts_added_count")) {
summary.aggressiveFtsAddedSamples.push_back(*v);
}
for (const auto& timingName : trackedTimingKeys()) {
const auto key = timingStatKey(timingName);
if (auto v = parseDoubleStat(searchStats, key)) {
summary.timingSamplesMs[timingName].push_back(*v);
}
}
auto prefusion = searchStats.find("trace_prefusion_signal_summary_json");
if (prefusion != searchStats.end()) {
try {
auto parsed = json::parse(prefusion->second);
if (parsed.is_object()) {
if (!hasDirectPreFusionStats) {
if (auto it = parsed.find("vector_only_docs"); it != parsed.end()) {
if (it->is_number()) {
summary.vectorOnlyDocsSamples.push_back(it->get<double>());
}
}
if (auto it = parsed.find("vector_only_below_threshold"); it != parsed.end()) {
if (it->is_number()) {
summary.vectorOnlyBelowThresholdSamples.push_back(it->get<double>());
}
}
if (auto it = parsed.find("vector_only_above_threshold"); it != parsed.end()) {
if (it->is_number()) {
summary.vectorOnlyAboveThresholdSamples.push_back(it->get<double>());
}
}
if (auto it = parsed.find("vector_only_near_miss_eligible");
it != parsed.end()) {
if (it->is_number()) {
summary.vectorOnlyNearMissEligibleSamples.push_back(it->get<double>());
}
}
}
if (auto it = parsed.find("strong_vector_only_docs"); it != parsed.end()) {
if (it->is_number()) {
summary.strongVectorOnlyDocsSamples.push_back(it->get<double>());
}
}
if (auto it = parsed.find("strong_vector_only_score_eligible_docs");
it != parsed.end()) {
if (it->is_number()) {
summary.strongVectorOnlyScoreEligibleDocsSamples.push_back(
it->get<double>());
}
}
if (auto it = parsed.find("strong_vector_only_rank_eligible_docs");
it != parsed.end()) {
if (it->is_number()) {
summary.strongVectorOnlyRankEligibleDocsSamples.push_back(
it->get<double>());
}
}
}
} catch (...) {
}
}
auto graphDisplacement = searchStats.find("trace_graph_displacement_summary_json");
if (graphDisplacement != searchStats.end()) {
try {
auto parsed = json::parse(graphDisplacement->second);
if (parsed.is_object()) {
if (auto it = parsed.find("graph_added_post_fusion_count");
it != parsed.end() && it->is_number()) {
summary.graphAddedPostFusionSamples.push_back(it->get<double>());
}
if (auto it = parsed.find("graph_displaced_post_fusion_count");
it != parsed.end() && it->is_number()) {
summary.graphDisplacedPostFusionSamples.push_back(it->get<double>());
}
}
} catch (...) {
}
}
if (auto v = parseDoubleStat(searchStats, "graph_community_supported_docs")) {
summary.graphCommunitySupportedDocsSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "graph_community_edge_count")) {
summary.graphCommunityEdgeCountSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "graph_community_largest_size")) {
summary.graphCommunityLargestSizeSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "graph_community_signal_mass")) {
summary.graphCommunitySignalMassSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "graph_community_boosted_docs")) {
summary.graphCommunityBoostedDocsSamples.push_back(*v);
}
if (auto v = parseDoubleStat(searchStats, "graph_community_weight")) {
summary.graphCommunityWeightSamples.push_back(*v);
}
if (auto stageSummary = searchStats.find("trace_stage_summary_json");
stageSummary != searchStats.end()) {
try {
auto parsed = json::parse(stageSummary->second);
if (parsed.is_object()) {
for (const auto& [stageName, stageData] : parsed.items()) {
if (!stageData.is_object()) {
continue;
}
if (stageData.value("enabled", false)) {
summary.stageEnabledCount[stageName]++;
}
if (stageData.value("attempted", false)) {
summary.stageAttemptedCount[stageName]++;
}
if (stageData.value("contributed", false)) {
summary.stageContributedCount[stageName]++;
}
if (stageData.value("skipped", false)) {
summary.stageSkippedCount[stageName]++;
auto reason = stageData.value("skip_reason", std::string{});
if (!reason.empty()) {
summary.stageSkipReasons[stageName].push_back(reason);
}
}
if (auto it = stageData.find("unique_doc_count");
it != stageData.end() && it->is_number()) {
summary.stageUniqueDocCountSamples[stageName].push_back(it->get<double>());
}
}
}
} catch (...) {
}
}
if (auto stagePresence = relevantDecisionTrace.find("stage_relevant_presence");
relevantDecisionTrace.is_object() && stagePresence != relevantDecisionTrace.end() &&
stagePresence->is_object()) {
for (const auto& [stageName, stageData] : stagePresence->items()) {
if (!stageData.is_object()) {
continue;
}
if (stageData.value("any_relevant", false)) {
summary.stageRelevantPresenceCount[stageName]++;
}
if (auto it = stageData.find("relevant_doc_count");
it != stageData.end() && it->is_number()) {
summary.stageRelevantDocCountSamples[stageName].push_back(it->get<double>());
}
}
}
if (auto fusionSummary = searchStats.find("trace_fusion_source_summary_json");
fusionSummary != searchStats.end()) {
try {
auto parsed = json::parse(fusionSummary->second);
if (parsed.is_object()) {
for (const auto& [sourceName, sourceData] : parsed.items()) {
if (!sourceData.is_object()) {
continue;
}
if (auto it = sourceData.find("final_score_mass");
it != sourceData.end() && it->is_number()) {
summary.fusionSourceScoreMassSamples[sourceName].push_back(
it->get<double>());
}
if (auto it = sourceData.find("final_top_doc_count");
it != sourceData.end() && it->is_number()) {
summary.fusionSourceFinalDocSamples[sourceName].push_back(
it->get<double>());
}
}
}
} catch (...) {
}
}
if (auto tunerSummary = searchStats.find("tuner_adjustments_json");
tunerSummary != searchStats.end()) {
try {
auto parsed = json::parse(tunerSummary->second);
if (parsed.is_object()) {
if (auto decision = parsed.find("last_decision");
decision != parsed.end() && decision->is_string()) {
summary.tunerDecisionCounts[decision->get<std::string>()]++;
}
static const std::vector<std::string> kKeys = {
"ewma_latency_ms",
"ewma_kg_latency_share",
"ewma_kg_utility",
"ewma_kg_score_mass_share",
"ewma_kg_final_doc_share",
"ewma_kg_contribution_rate",
"ewma_graph_rerank_latency_ms",
"ewma_graph_rerank_skip_rate",
"ewma_graph_rerank_contribution_rate",
"ewma_fusion_dropped_rate",
"ewma_anchored_fusion_dropped_rate",
"ewma_top_text_fusion_dropped_rate",
"ewma_vector_only_share",
"ewma_semantic_rescue_rate",
};
for (const auto& key : kKeys) {
if (auto it = parsed.find(key); it != parsed.end() && it->is_number()) {
summary.tunerSignalSamples[key].push_back(it->get<double>());
}
}
}
} catch (...) {
}
}
if (auto runtimeConfig = searchStats.find("tuner_runtime_config_json");
runtimeConfig != searchStats.end()) {
try {
auto parsed = json::parse(runtimeConfig->second);
if (parsed.is_object()) {
static const std::vector<std::string> kWeightKeys = {
"text_weight", "simeon_text_weight", "vector_weight",
"entity_vector_weight", "path_tree_weight", "kg_weight",
"tag_weight", "metadata_weight",
};
for (const auto& key : kWeightKeys) {
if (auto it = parsed.find(key); it != parsed.end() && it->is_number()) {
summary.tunerSignalSamples[key].push_back(it->get<double>());
}
}
}
} catch (...) {
}
}
}
static json queryDiagnosticsToJson(const QueryDiagnosticsSummary& summary) {
const double queryCount = static_cast<double>(std::max<std::uint64_t>(summary.queryCount, 1));
json out = {
{"query_count", summary.queryCount},
{"query_with_results_count", summary.queryWithResultsCount},
{"query_without_results_count", summary.queryWithoutResultsCount},
{"query_without_relevant_hit_count", summary.queryWithoutRelevantHitCount},
{"query_with_relevant_in_pre_fusion_count", summary.queryWithRelevantInPreFusionCount},
{"query_with_relevant_in_post_fusion_count", summary.queryWithRelevantInPostFusionCount},
{"query_with_relevant_in_graphless_post_fusion_count",
summary.queryWithRelevantInGraphlessPostFusionCount},
{"query_with_relevant_in_final_window_count", summary.queryWithRelevantInFinalWindowCount},
{"query_with_graph_added_relevant_post_fusion_count",
summary.queryWithGraphAddedRelevantPostFusionCount},
{"query_with_graph_displaced_relevant_post_fusion_count",
summary.queryWithGraphDisplacedRelevantPostFusionCount},
{"query_with_topology_added_relevant_post_fusion_count",
summary.queryWithTopologyAddedRelevantPostFusionCount},
{"query_with_topology_added_relevant_fusion_dropped_count",
summary.queryWithTopologyAddedRelevantFusionDroppedCount},
{"query_with_topk_duplicate_count", summary.queryWithTopKDuplicateCount},
{"degraded_query_count", summary.degradedQueryCount},
{"trace_enabled_query_count", summary.traceEnabledQueryCount},
{"graph_rerank_applied_query_count", summary.graphRerankAppliedQueryCount},
{"compressed_ann_enabled_query_count", summary.compressedAnnEnabledQueryCount},
{"compressed_ann_applied_query_count", summary.compressedAnnAppliedQueryCount},
{"semantic_rescue_nonzero_query_count", summary.semanticRescueNonZeroQueryCount},
{"miss_missing_pre_fusion_count", summary.missMissingPreFusionCount},
{"miss_fusion_cutoff_count", summary.missFusionCutoffCount},
{"miss_rerank_window_drop_count", summary.missRerankWindowDropCount},
{"miss_topk_drop_count", summary.missTopKDropCount},
{"query_with_results_rate",
static_cast<double>(summary.queryWithResultsCount) / queryCount},
{"query_without_relevant_hit_rate",
static_cast<double>(summary.queryWithoutRelevantHitCount) / queryCount},
{"query_with_relevant_in_pre_fusion_rate",
static_cast<double>(summary.queryWithRelevantInPreFusionCount) / queryCount},
{"query_with_relevant_in_post_fusion_rate",
static_cast<double>(summary.queryWithRelevantInPostFusionCount) / queryCount},
{"query_with_relevant_in_graphless_post_fusion_rate",
static_cast<double>(summary.queryWithRelevantInGraphlessPostFusionCount) / queryCount},
{"query_with_relevant_in_final_window_rate",
static_cast<double>(summary.queryWithRelevantInFinalWindowCount) / queryCount},
{"query_with_graph_added_relevant_post_fusion_rate",
static_cast<double>(summary.queryWithGraphAddedRelevantPostFusionCount) / queryCount},
{"query_with_graph_displaced_relevant_post_fusion_rate",
static_cast<double>(summary.queryWithGraphDisplacedRelevantPostFusionCount) / queryCount},
{"query_with_topology_added_relevant_post_fusion_rate",
static_cast<double>(summary.queryWithTopologyAddedRelevantPostFusionCount) / queryCount},
{"query_with_topology_added_relevant_fusion_dropped_rate",
static_cast<double>(summary.queryWithTopologyAddedRelevantFusionDroppedCount) /
queryCount},
{"query_with_topk_duplicate_rate",
static_cast<double>(summary.queryWithTopKDuplicateCount) / queryCount},
{"degraded_query_rate", static_cast<double>(summary.degradedQueryCount) / queryCount},
{"trace_coverage", static_cast<double>(summary.traceEnabledQueryCount) / queryCount},
{"graph_rerank_apply_rate",
static_cast<double>(summary.graphRerankAppliedQueryCount) / queryCount},
{"compressed_ann_enabled_rate",
static_cast<double>(summary.compressedAnnEnabledQueryCount) / queryCount},
{"compressed_ann_apply_rate",
static_cast<double>(summary.compressedAnnAppliedQueryCount) / queryCount},
{"adaptive_fusion_enabled_query_count", summary.adaptiveFusionEnabledQueryCount},
{"adaptive_fusion_enabled_rate",
static_cast<double>(summary.adaptiveFusionEnabledQueryCount) / queryCount},
{"semantic_rescue_nonzero_rate",
static_cast<double>(summary.semanticRescueNonZeroQueryCount) / queryCount},
{"miss_missing_pre_fusion_rate",
static_cast<double>(summary.missMissingPreFusionCount) / queryCount},
{"miss_fusion_cutoff_rate",
static_cast<double>(summary.missFusionCutoffCount) / queryCount},
{"miss_rerank_window_drop_rate",
static_cast<double>(summary.missRerankWindowDropCount) / queryCount},
{"miss_topk_drop_rate", static_cast<double>(summary.missTopKDropCount) / queryCount},
{"compressed_ann_result_count", summarizeSamples(summary.compressedAnnResultCountSamples)},
{"compressed_ann_skip_reasons", summary.compressedAnnSkipReasonCounts},
{"stage_metrics", yams::bench::stageRetrievalMetricsToJson(summary.stageRetrievalMetrics)},
{"semantic_rescue_rate", summarizeSamples(summary.semanticRescueRateSamples)},
{"semantic_rescue_final_count", summarizeSamples(summary.semanticRescueFinalCountSamples)},
{"semantic_rescue_target", summarizeSamples(summary.semanticRescueTargetSamples)},
{"fusion_pre_fusion_unique_count",
summarizeSamples(summary.fusionPreFusionUniqueCountSamples)},
{"fusion_post_fusion_count", summarizeSamples(summary.fusionPostFusionCountSamples)},
{"fusion_dropped_count", summarizeSamples(summary.fusionDroppedCountSamples)},
{"fusion_dropped_rate", summarizeSamples(summary.fusionDroppedRateSamples)},
{"anchored_pre_fusion_count", summarizeSamples(summary.anchoredPreFusionCountSamples)},
{"anchored_fusion_dropped_count",
summarizeSamples(summary.anchoredFusionDroppedCountSamples)},
{"anchored_fusion_dropped_rate",
summarizeSamples(summary.anchoredFusionDroppedRateSamples)},
{"top_text_pre_fusion_count", summarizeSamples(summary.topTextPreFusionCountSamples)},
{"top_text_fusion_dropped_count",
summarizeSamples(summary.topTextFusionDroppedCountSamples)},
{"top_text_fusion_dropped_rate", summarizeSamples(summary.topTextFusionDroppedRateSamples)},
{"vector_only_docs", summarizeSamples(summary.vectorOnlyDocsSamples)},
{"vector_only_below_threshold", summarizeSamples(summary.vectorOnlyBelowThresholdSamples)},
{"vector_only_above_threshold", summarizeSamples(summary.vectorOnlyAboveThresholdSamples)},
{"vector_only_near_miss_eligible",
summarizeSamples(summary.vectorOnlyNearMissEligibleSamples)},
{"strong_vector_only_docs", summarizeSamples(summary.strongVectorOnlyDocsSamples)},
{"strong_vector_only_score_eligible_docs",
summarizeSamples(summary.strongVectorOnlyScoreEligibleDocsSamples)},
{"strong_vector_only_rank_eligible_docs",
summarizeSamples(summary.strongVectorOnlyRankEligibleDocsSamples)},
{"graph_added_post_fusion_count", summarizeSamples(summary.graphAddedPostFusionSamples)},
{"graph_displaced_post_fusion_count",
summarizeSamples(summary.graphDisplacedPostFusionSamples)},
{"topology_weak_query_routed_docs", summarizeSamples(summary.topologyRoutedDocsSamples)},
{"topology_route_best_score", summarizeSamples(summary.topologyRouteBestScoreSamples)},
{"topology_route_mean_accepted_score",
summarizeSamples(summary.topologyRouteMeanAcceptedScoreSamples)},
{"topology_route_accepted_count",
summarizeSamples(summary.topologyRouteAcceptedCountSamples)},
{"topology_seed_count", summarizeSamples(summary.topologySeedCountSamples)},
{"topology_seed_coverage_count",
summarizeSamples(summary.topologySeedCoverageCountSamples)},
{"topology_core_candidate_coverage",
summarizeSamples(summary.topologyCoreCandidateCoverageSamples)},
{"topology_weak_query_added_candidates",
summarizeSamples(summary.topologyAddedCandidatesSamples)},
{"topology_weak_query_duplicate_candidates",
summarizeSamples(summary.topologyDuplicateCandidatesSamples)},
{"topology_weak_query_stale_candidates",
summarizeSamples(summary.topologyStaleCandidatesSamples)},
{"graph_community_supported_docs",
summarizeSamples(summary.graphCommunitySupportedDocsSamples)},
{"graph_community_edge_count", summarizeSamples(summary.graphCommunityEdgeCountSamples)},
{"graph_community_largest_size",
summarizeSamples(summary.graphCommunityLargestSizeSamples)},
{"graph_community_signal_mass", summarizeSamples(summary.graphCommunitySignalMassSamples)},
{"graph_community_boosted_docs",
summarizeSamples(summary.graphCommunityBoostedDocsSamples)},
{"graph_community_weight", summarizeSamples(summary.graphCommunityWeightSamples)},
{"multi_vector_generated_phrases",
summarizeSamples(summary.multiVectorGeneratedPhraseSamples)},
{"multi_vector_raw_hit_count", summarizeSamples(summary.multiVectorRawHitSamples)},
{"multi_vector_added_new_count", summarizeSamples(summary.multiVectorAddedNewSamples)},
{"multi_vector_replaced_base_count",
summarizeSamples(summary.multiVectorReplacedBaseSamples)},
{"subphrase_generated_count", summarizeSamples(summary.subPhraseGeneratedSamples)},
{"subphrase_clause_count", summarizeSamples(summary.subPhraseClauseSamples)},
{"subphrase_fts_hit_count", summarizeSamples(summary.subPhraseFtsHitSamples)},
{"subphrase_fts_added_count", summarizeSamples(summary.subPhraseFtsAddedSamples)},
{"aggressive_fts_clause_count", summarizeSamples(summary.aggressiveFtsClauseSamples)},
{"aggressive_fts_hit_count", summarizeSamples(summary.aggressiveFtsHitSamples)},
{"aggressive_fts_added_count", summarizeSamples(summary.aggressiveFtsAddedSamples)},
};
json timings = json::object();
for (const auto& timingName : trackedTimingKeys()) {
auto it = summary.timingSamplesMs.find(timingName);
if (it != summary.timingSamplesMs.end() && !it->second.empty()) {
timings[timingName] = summarizeSamples(it->second);
}
}
out["timings_ms"] = timings;
json stageUniqueDocCounts = json::object();
for (const auto& [stageName, samples] : summary.stageUniqueDocCountSamples) {
stageUniqueDocCounts[stageName] = summarizeSamples(samples);
}
out["stage_unique_doc_counts"] = stageUniqueDocCounts;
json stageRelevantDocCounts = json::object();
for (const auto& [stageName, samples] : summary.stageRelevantDocCountSamples) {
stageRelevantDocCounts[stageName] = summarizeSamples(samples);
}
out["stage_relevant_doc_counts"] = stageRelevantDocCounts;
json stages = json::object();
for (const auto& [stageName, enabledCount] : summary.stageEnabledCount) {
const double enabled = static_cast<double>(enabledCount);
const auto attemptedIt = summary.stageAttemptedCount.find(stageName);
const auto contributedIt = summary.stageContributedCount.find(stageName);
const auto skippedIt = summary.stageSkippedCount.find(stageName);
const double attempted = attemptedIt != summary.stageAttemptedCount.end()
? static_cast<double>(attemptedIt->second)
: 0.0;
const double contributed = contributedIt != summary.stageContributedCount.end()
? static_cast<double>(contributedIt->second)
: 0.0;
const double skipped = skippedIt != summary.stageSkippedCount.end()
? static_cast<double>(skippedIt->second)
: 0.0;
const auto relevantIt = summary.stageRelevantPresenceCount.find(stageName);
const double relevantPresence = relevantIt != summary.stageRelevantPresenceCount.end()
? static_cast<double>(relevantIt->second)
: 0.0;
stages[stageName] = {
{"enabled_rate", enabled / queryCount},
{"attempt_rate", attempted / queryCount},
{"contribution_rate", contributed / queryCount},
{"relevant_presence_rate", relevantPresence / queryCount},
{"skip_rate", skipped / queryCount},
{"skip_reasons", summary.stageSkipReasons.contains(stageName)
? json(summary.stageSkipReasons.at(stageName))
: json::array()},
};
}
out["stage_contributions"] = stages;
json fusionSources = json::object();
for (const auto& [sourceName, masses] : summary.fusionSourceScoreMassSamples) {
auto finalDocIt = summary.fusionSourceFinalDocSamples.find(sourceName);
fusionSources[sourceName] = {
{"final_score_mass", summarizeSamples(masses)},
{"final_top_doc_count", finalDocIt != summary.fusionSourceFinalDocSamples.end()
? summarizeSamples(finalDocIt->second)
: json{{"count", 0},
{"mean", 0.0},
{"p50", 0.0},
{"p95", 0.0},
{"min", 0.0},
{"max", 0.0}}},
};
}
out["fusion_sources"] = fusionSources;
json tunerSignals = json::object();
for (const auto& [signalName, samples] : summary.tunerSignalSamples) {
tunerSignals[signalName] = summarizeSamples(samples);
}
out["tuner_signals"] = tunerSignals;
out["tuner_decisions"] = summary.tunerDecisionCounts;
out["zoom_level_counts"] = summary.zoomLevelCounts;
return out;
}
static void debugLogWriteJsonLine(const DebugLogEntry& e) {
if (!g_debugOut)
return;
json j;
j["query"] = e.query;
if (e.queryIndex >= 0) {
j["query_index"] = e.queryIndex;
}
j["search_type"] = e.searchType;
j["relevant_doc_ids"] = e.relevantDocIds;
j["relevant_files"] = e.relevantFiles;
j["returned_paths"] = e.returnedPaths;
j["returned_doc_ids"] = e.returnedDocIds;
j["returned_scores"] = e.returnedScores;
j["returned_grades"] = e.returnedGrades;
j["attempts"] = e.attempts;
j["used_streaming"] = e.usedStreaming;
j["used_fuzzy_flag"] = e.usedFuzzyFlag;
j["used_literal_flag"] = e.usedLiteralFlag;
j["used_fuzzy_retry"] = e.usedFuzzyRetry;
j["used_literal_retry"] = e.usedLiteralTextRetry;
j["search_stats"] = e.searchStats;
j["diag"] = e.diagnostics;
if (const char* embBackend = std::getenv("YAMS_EMBED_BACKEND"); embBackend && *embBackend) {
j["embedding_backend"] = embBackend;
} else {
j["embedding_backend"] = "daemon";
}
if (g_debugRunContext.has_value()) {
j["run_id"] = g_debugRunContext->runId;
j["candidate"] = g_debugRunContext->candidate;
j["dataset"] = g_debugRunContext->dataset;
j["corpus_size"] = g_debugRunContext->corpusSize;
j["num_queries"] = g_debugRunContext->numQueries;
j["top_k"] = g_debugRunContext->topK;
j["trace_top_n"] = g_debugRunContext->traceTopN;
j["trace_component_top_n"] = g_debugRunContext->traceComponentTopN;
j["stage_trace_enabled"] = g_debugRunContext->stageTraceEnabled;
if (!g_debugRunContext->debugFile.empty()) {
j["debug_file"] = g_debugRunContext->debugFile;
}
}
if (e.extraFields.is_object()) {
for (const auto& [k, v] : e.extraFields.items()) {
j[k] = v;
}
}
std::lock_guard<std::mutex> lock(g_debugMutex);
(*g_debugOut) << j.dump() << "\n";
g_debugOut->flush();
}
// Store final metrics globally for summary after teardown
static RetrievalMetrics g_final_metrics;
static RetrievalMetrics g_keyword_metrics; // FTS5-only metrics for component isolation
static RetrievalMetrics g_grep_metrics; // grep literal baseline for exact retrieval comparison
static QueryDiagnosticsSummary g_final_hybrid_diagnostics;
static QueryDiagnosticsSummary g_final_keyword_diagnostics;
struct EnvSetting {
std::string key;
std::optional<std::string> value;
};
class ScopedEnvOverrides {
public:
explicit ScopedEnvOverrides(const std::vector<EnvSetting>& overrides) {
environment_.reserve(overrides.size());
for (const auto& setting : overrides) {
environment_.emplace_back(setting.key, setting.value);
}
}
private:
std::vector<yams::test::ScopedEnvVar> environment_;
};
struct OptimizationCandidate {
std::string name;
std::string description;
std::vector<EnvSetting> envOverrides;
bool enginePolicyCandidate = false;
bool diagnosticCandidate = false;
};
struct OptimizationRunResult {
OptimizationCandidate candidate;
RetrievalMetrics hybridMetrics;
RetrievalMetrics keywordMetrics;
QueryDiagnosticsSummary hybridDiagnostics;
QueryDiagnosticsSummary keywordDiagnostics;
std::string tuningState;
std::string tuningReason;
std::string runId;
std::string debugFile;
int traceTopN = 0;
int traceComponentTopN = 0;
bool stageTraceEnabled = false;
double objectiveScore = 0.0;
double hybridEvalMs = 0.0;
double keywordEvalMs = 0.0;
std::string searchEngine =
yams::vector::vectorSearchEngineName(yams::vector::VectorSearchEngine::SimeonPqAdc);
std::uintmax_t vectorsDbBytes = 0;
std::uintmax_t vectorsWalBytes = 0;
std::uintmax_t persistedHnswNodes = 0;
std::uintmax_t persistedHnswMetaRows = 0;
std::uintmax_t persistedVec0Tables = 0;
std::uintmax_t persistedVec0Rows = 0;
std::uintmax_t persistedPqCodes = 0;
std::uintmax_t persistedPqMeta = 0;
bool success = false;
std::string errorMessage;
};
struct BenchCacheMetadata {
std::string dataset = "synthetic";
std::string datasetPath;
int corpusSize = 0;
int numQueries = 0;
int topK = 0;
bool useBEIR = false;
bool vectorsDisabled = false;
bool requireKgReady = false;
bool graphRerankRequested = false;
std::string searchEngine =
yams::vector::vectorSearchEngineName(yams::vector::VectorSearchEngine::SimeonPqAdc);
int expectedDocs = 0;
int expectedQueries = 0;
std::uintmax_t corpusFingerprint = 0;
std::string status = "priming";
int embeddedDocs = 0;
std::uintmax_t vectorCount = 0;
bool vectorIndexReady = false;
std::uintmax_t vectorsDbBytes = 0;
std::uintmax_t vectorsWalBytes = 0;
std::uintmax_t persistedHnswNodes = 0;
std::uintmax_t persistedHnswMetaRows = 0;
std::uintmax_t persistedVec0Tables = 0;
std::uintmax_t persistedVec0Rows = 0;
std::uintmax_t persistedPqCodes = 0;
std::uintmax_t persistedPqMeta = 0;
};
struct PersistedHnswState {
bool vectorsDbPresent = false;
std::uintmax_t vectorsDbBytes = 0;
std::uintmax_t vectorsWalBytes = 0;
std::uintmax_t nodeRows = 0;
std::uintmax_t metaRows = 0;
std::uintmax_t vec0Tables = 0;
std::uintmax_t vec0Rows = 0;
std::uintmax_t pqCodeRows = 0;
std::uintmax_t pqMetaRows = 0;
};
static std::string g_benchmark_search_engine =
yams::vector::vectorSearchEngineName(yams::vector::VectorSearchEngine::SimeonPqAdc);
static std::string g_simeonEncoderProfile;
static std::string g_simeonFragmentEncoderProfile;
static std::optional<bool> g_simeonFragmentGeometryEnabled;
static std::optional<bool> g_simeonBm25Enabled;
static std::optional<bool> g_simeonRouterEnabled;
static bool g_requireDaemonNativeGraph = false;
static PersistedHnswState g_final_vector_index_state;
static void consumeXplanConfigArguments(int& argc, char** argv) {
const auto takeValue = [](std::string_view argument,
std::string_view prefix) -> std::optional<std::string> {
if (!argument.starts_with(prefix)) {
return std::nullopt;
}
return std::string{argument.substr(prefix.size())};
};
int outputIndex = 1;
for (int inputIndex = 1; inputIndex < argc; ++inputIndex) {
const std::string_view argument{argv[inputIndex]};
if (auto value = takeValue(argument, "--yams-simeon-encoder-profile="); value.has_value()) {
g_simeonEncoderProfile = std::move(*value);
continue;
}
if (auto value = takeValue(argument, "--yams-simeon-fragment-encoder-profile=");
value.has_value()) {
g_simeonFragmentEncoderProfile = std::move(*value);
continue;
}
if (auto value = takeValue(argument, "--yams-simeon-fragment-geometry=");
value.has_value()) {
g_simeonFragmentGeometryEnabled = *value == "true" || *value == "1";
continue;
}
if (auto value = takeValue(argument, "--yams-simeon-bm25="); value.has_value()) {
g_simeonBm25Enabled = *value == "true" || *value == "1";
continue;
}
if (auto value = takeValue(argument, "--yams-simeon-router="); value.has_value()) {
g_simeonRouterEnabled = *value == "true" || *value == "1";
continue;
}
if (auto value = takeValue(argument, "--yams-require-daemon-native-graph=");
value.has_value()) {
g_requireDaemonNativeGraph = *value == "true" || *value == "1";
continue;
}
argv[outputIndex++] = argv[inputIndex];
}
argc = outputIndex;
}
static bool envTruthy(const char* value) {
if (!value) {
return false;
}
std::string normalized(value);
std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
return normalized == "1" || normalized == "true" || normalized == "yes" || normalized == "on";
}
static yams::vector::VectorSearchEngine benchmarkVectorSearchEngine() {
if (const char* raw = std::getenv("YAMS_VECTOR_SEARCH_ENGINE"); raw && *raw) {
std::string normalized(raw);
std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
if (auto parsed = yams::vector::parseVectorSearchEngine(normalized)) {
return *parsed;
}
spdlog::warn(
"[Bench] Invalid YAMS_VECTOR_SEARCH_ENGINE='{}'; using default {}", raw,
yams::vector::vectorSearchEngineName(yams::vector::VectorSearchEngine::SimeonPqAdc));
}
return yams::vector::VectorSearchEngine::SimeonPqAdc;
}
static std::string benchmarkEngineCacheLabel() {
const auto engine = benchmarkVectorSearchEngine();
std::string label = yams::vector::vectorSearchEngineName(engine);
return label;
}
static bool benchUseStreamingSearch() {
// Retrieval quality benchmarks measure ranking quality, not IPC chunking behavior.
// Default to unary search so streaming transport stalls do not skew IR evaluation,
// while preserving an opt-in path for transport debugging.
return envTruthy(std::getenv("YAMS_BENCH_ENABLE_STREAMING_SEARCH"));
}
static bool benchEvaluateGrepBaseline() {
const char* value = std::getenv("YAMS_BENCH_EVALUATE_GREP_BASELINE");
return value == nullptr || envTruthy(value);
}
static int parseIntEnvOrDefault(const char* key, int defaultValue, int minValue, int maxValue) {
const int clampedDefault = std::clamp(defaultValue, minValue, maxValue);
const char* raw = std::getenv(key);
if (!(raw && *raw)) {
return clampedDefault;
}
try {
return std::clamp(std::stoi(raw), minValue, maxValue);
} catch (...) {
return clampedDefault;
}
}
static std::size_t parseSizeEnvOrDefault(const char* key, std::size_t defaultValue) {
const char* raw = std::getenv(key);
if (!(raw && *raw)) {
return defaultValue;
}
try {
return static_cast<std::size_t>(std::max<long long>(0, std::stoll(raw)));
} catch (...) {
return defaultValue;
}
}
static std::size_t parseSizeEnvOrFallback(const char* key, const char* fallbackKey,
std::size_t defaultValue) {
if (const char* raw = std::getenv(key); raw && *raw) {
return parseSizeEnvOrDefault(key, defaultValue);
}
return parseSizeEnvOrDefault(fallbackKey, defaultValue);
}
static float parseFloatEnvOrDefault(const char* key, float defaultValue) {
const char* raw = std::getenv(key);
if (!(raw && *raw)) {
return defaultValue;
}
try {
return std::stof(raw);
} catch (...) {
return defaultValue;
}
}
static std::string sanitizeLabelForFilename(const std::string& value) {
std::string out;
out.reserve(value.size());
for (char c : value) {
if (std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_') {
out.push_back(c);
} else {
out.push_back('_');
}
}
return out;
}
static std::string makeRunId(const std::string& candidate) {
(void)candidate;
const auto now = std::chrono::system_clock::now().time_since_epoch();
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(now).count();
return std::to_string(us);
}
static std::uintmax_t fileSizeOrZero(const fs::path& path) {
std::error_code ec;
const auto size = fs::file_size(path, ec);
return ec ? 0 : size;
}
static std::uintmax_t computePathFingerprint(const fs::path& path) {
std::error_code ec;
if (!fs::exists(path, ec)) {
return 0;
}
if (fs::is_regular_file(path, ec)) {
return fileSizeOrZero(path);
}
if (!fs::is_directory(path, ec)) {
return 0;
}
std::uintmax_t fingerprint = 1469598103934665603ull;
for (fs::recursive_directory_iterator it(path, ec), end; !ec && it != end; it.increment(ec)) {
const auto entryPath = it->path();
const auto relative = fs::relative(entryPath, path, ec);
const std::string relativeString = ec ? entryPath.filename().string() : relative.string();
for (unsigned char c : relativeString) {
fingerprint ^= static_cast<std::uintmax_t>(c);
fingerprint *= 1099511628211ull;
}
if (it->is_regular_file(ec)) {
fingerprint ^= fileSizeOrZero(entryPath);
fingerprint *= 1099511628211ull;
}
ec.clear();
}
return fingerprint;
}
static PersistedHnswState inspectPersistedHnswState(const fs::path& dataDir) {
PersistedHnswState state;
const fs::path vectorsDbPath = dataDir / "vectors.db";
if (!fs::exists(vectorsDbPath)) {
return state;
}
state.vectorsDbPresent = true;
std::error_code sizeEc;
state.vectorsDbBytes = fs::file_size(vectorsDbPath, sizeEc);
if (sizeEc) {
state.vectorsDbBytes = 0;
sizeEc.clear();
}
const fs::path walPath = vectorsDbPath.string() + "-wal";
if (fs::exists(walPath)) {
state.vectorsWalBytes = fs::file_size(walPath, sizeEc);
if (sizeEc) {
state.vectorsWalBytes = 0;
sizeEc.clear();
}
}
sqlite3* db = nullptr;
if (sqlite3_open_v2(vectorsDbPath.string().c_str(), &db, SQLITE_OPEN_READONLY, nullptr) !=
SQLITE_OK) {
spdlog::warn("[Bench] Failed to open persisted HNSW DB {}: {}", vectorsDbPath.string(),
db ? sqlite3_errmsg(db) : "open failed");
if (db) {
sqlite3_close(db);
}
return state;
}
sqlite3_busy_timeout(db, 5000);
auto closeDb = [&]() {
if (db) {
sqlite3_close(db);
db = nullptr;
}
};
sqlite3_stmt* stmt = nullptr;
const char* tableSql =
"SELECT name FROM sqlite_master WHERE type='table' AND "
"(name='vectors_hnsw_meta' OR name='vectors_hnsw_nodes' OR "
" name LIKE 'vectors_%_hnsw_meta' OR name LIKE 'vectors_%_hnsw_nodes' OR "
" name LIKE 'vectors_%_vec0' OR "
" name='simeon_pq_codes' OR name='simeon_pq_meta' OR "
" name LIKE 'vectors_%_simeon_pq_codes' OR name LIKE 'vectors_%_simeon_pq_meta')";
if (sqlite3_prepare_v2(db, tableSql, -1, &stmt, nullptr) != SQLITE_OK) {
spdlog::warn("[Bench] Failed to inspect persisted HNSW tables in {}: {}",
vectorsDbPath.string(), sqlite3_errmsg(db));
closeDb();
return state;
}
std::vector<std::string> tables;
while (sqlite3_step(stmt) == SQLITE_ROW) {
const auto* text = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
if (text && *text) {
tables.emplace_back(text);
}
}
sqlite3_finalize(stmt);
for (const auto& table : tables) {
std::string countTarget = table;
if (table.find("_vec0") != std::string::npos) {
// vec0 virtual tables require the module to be loaded on the inspecting connection.
// For benchmark readiness/footprint checks, count the shadow storage table directly.
countTarget += "_vectors";
}
const std::string countSql = "SELECT COUNT(*) FROM \"" + countTarget + "\"";
if (sqlite3_prepare_v2(db, countSql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
spdlog::warn("[Bench] Failed to count persisted vector table {} in {}: {}", countTarget,
vectorsDbPath.string(), sqlite3_errmsg(db));
continue;
}
if (sqlite3_step(stmt) == SQLITE_ROW) {
const auto count = static_cast<std::uintmax_t>(sqlite3_column_int64(stmt, 0));
if (table.find("_hnsw_nodes") != std::string::npos) {
state.nodeRows += count;
} else if (table.find("_hnsw_meta") != std::string::npos) {
state.metaRows += count;
} else if (table.find("_vec0") != std::string::npos) {
state.vec0Tables += 1;
state.vec0Rows += count;
} else if (table.find("simeon_pq_codes") != std::string::npos) {
state.pqCodeRows += count;
} else if (table.find("simeon_pq_meta") != std::string::npos) {
state.pqMetaRows += count;
}
}
sqlite3_finalize(stmt);
stmt = nullptr;
}
closeDb();
return state;
}
static json benchCacheMetadataToJson(const BenchCacheMetadata& metadata) {
return {
{"dataset", metadata.dataset},
{"dataset_path", metadata.datasetPath},
{"corpus_size", metadata.corpusSize},
{"num_queries", metadata.numQueries},
{"top_k", metadata.topK},
{"use_beir", metadata.useBEIR},
{"vectors_disabled", metadata.vectorsDisabled},
{"require_kg_ready", metadata.requireKgReady},
{"graph_rerank_requested", metadata.graphRerankRequested},
{"search_engine", metadata.searchEngine},
{"expected_docs", metadata.expectedDocs},
{"expected_queries", metadata.expectedQueries},
{"corpus_fingerprint", metadata.corpusFingerprint},
{"status", metadata.status},
{"embedded_docs", metadata.embeddedDocs},
{"vector_count", metadata.vectorCount},
{"vector_index_ready", metadata.vectorIndexReady},
{"vectors_db_bytes", metadata.vectorsDbBytes},
{"vectors_wal_bytes", metadata.vectorsWalBytes},
{"persisted_hnsw_nodes", metadata.persistedHnswNodes},
{"persisted_hnsw_meta_rows", metadata.persistedHnswMetaRows},
{"persisted_vec0_tables", metadata.persistedVec0Tables},
{"persisted_vec0_rows", metadata.persistedVec0Rows},
{"persisted_pq_codes", metadata.persistedPqCodes},
{"persisted_pq_meta", metadata.persistedPqMeta},
};
}
static std::uintmax_t jsonUintValue(const json& j, const char* key, std::uintmax_t fallback = 0) {
const auto it = j.find(key);
if (it == j.end() || it->is_null()) {
return fallback;
}
if (it->is_number_unsigned()) {
return it->get<std::uintmax_t>();
}
if (it->is_number_integer()) {
const auto value = it->get<std::intmax_t>();
return value >= 0 ? static_cast<std::uintmax_t>(value) : fallback;
}
if (it->is_string()) {
try {
return static_cast<std::uintmax_t>(std::stoull(it->get<std::string>()));
} catch (...) {
return fallback;
}
}
return fallback;
}
static std::optional<BenchCacheMetadata> parseBenchCacheMetadata(const json& j) {
if (!j.is_object()) {
return std::nullopt;
}
BenchCacheMetadata metadata;
metadata.dataset = j.value("dataset", "synthetic");
metadata.datasetPath = j.value("dataset_path", "");
metadata.corpusSize = j.value("corpus_size", 0);
metadata.numQueries = j.value("num_queries", 0);
metadata.topK = j.value("top_k", 0);
metadata.useBEIR = j.value("use_beir", false);
metadata.vectorsDisabled = j.value("vectors_disabled", false);
metadata.requireKgReady = j.value("require_kg_ready", false);
metadata.graphRerankRequested = j.value("graph_rerank_requested", false);
metadata.searchEngine =
j.value("search_engine", std::string(yams::vector::vectorSearchEngineName(
yams::vector::VectorSearchEngine::SimeonPqAdc)));
metadata.expectedDocs = j.value("expected_docs", 0);
metadata.expectedQueries = j.value("expected_queries", 0);
metadata.corpusFingerprint = jsonUintValue(j, "corpus_fingerprint");
metadata.status = j.value("status", std::string("priming"));
metadata.embeddedDocs = j.value("embedded_docs", 0);
metadata.vectorCount = jsonUintValue(j, "vector_count");
metadata.vectorIndexReady = j.value("vector_index_ready", false);
metadata.vectorsDbBytes = jsonUintValue(j, "vectors_db_bytes");
metadata.vectorsWalBytes = jsonUintValue(j, "vectors_wal_bytes");
metadata.persistedHnswNodes = jsonUintValue(j, "persisted_hnsw_nodes");
metadata.persistedHnswMetaRows = jsonUintValue(j, "persisted_hnsw_meta_rows");
metadata.persistedVec0Tables = jsonUintValue(j, "persisted_vec0_tables");
metadata.persistedVec0Rows = jsonUintValue(j, "persisted_vec0_rows");
metadata.persistedPqCodes = jsonUintValue(j, "persisted_pq_codes");
metadata.persistedPqMeta = jsonUintValue(j, "persisted_pq_meta");
return metadata;
}
static std::string canonicalPathOrEmpty(const fs::path& path) {
if (path.empty()) {
return {};
}
std::error_code ec;
const auto canonical = fs::weakly_canonical(path, ec);
if (!ec) {
return canonical.string();
}
return path.lexically_normal().string();
}
static fs::path benchCacheMetadataPath(const fs::path& cacheDir) {
return cacheDir / "retrieval_bench_cache.json";
}
static Result<BenchCacheMetadata> readBenchCacheMetadata(const fs::path& cacheDir) {
const auto metadataPath = benchCacheMetadataPath(cacheDir);
std::error_code ec;
if (!fs::exists(metadataPath, ec)) {
return Error{ErrorCode::NotFound, "Cache metadata not found: " + metadataPath.string()};
}
std::ifstream in(metadataPath);
if (!in) {
return Error{ErrorCode::IOError, "Failed to open cache metadata: " + metadataPath.string()};
}
json j;
try {
in >> j;
} catch (const std::exception& e) {
return Error{ErrorCode::InvalidArgument,
"Failed to parse cache metadata: " + std::string(e.what())};
}
auto parsed = parseBenchCacheMetadata(j);
if (!parsed) {
return Error{ErrorCode::InvalidArgument,
"Cache metadata schema invalid: " + metadataPath.string()};
}
return *parsed;
}
static Result<void> writeBenchCacheMetadata(const fs::path& cacheDir,
const BenchCacheMetadata& metadata) {
std::error_code ec;
fs::create_directories(cacheDir, ec);
if (ec) {
return Error{ErrorCode::IOError,
"Failed to create cache dir: " + cacheDir.string() + ": " + ec.message()};
}
const auto metadataPath = benchCacheMetadataPath(cacheDir);
std::ofstream out(metadataPath);
if (!out) {
return Error{ErrorCode::IOError,
"Failed to write cache metadata: " + metadataPath.string()};
}
out << benchCacheMetadataToJson(metadata).dump(2) << "\n";
return Result<void>{};
}
static bool benchCacheMatches(const BenchCacheMetadata& expected, const BenchCacheMetadata& actual,
std::string* mismatchReason = nullptr) {
auto mismatch = [&](std::string reason) {
if (mismatchReason) {
*mismatchReason = std::move(reason);
}
return false;
};
if (expected.dataset != actual.dataset)
return mismatch("dataset mismatch");
if (expected.datasetPath != actual.datasetPath)
return mismatch("dataset path mismatch");
if (expected.corpusSize != actual.corpusSize)
return mismatch("corpus size mismatch");
if (expected.numQueries != actual.numQueries)
return mismatch("query count mismatch");
if (expected.topK != actual.topK)
return mismatch("top-k mismatch");
if (expected.useBEIR != actual.useBEIR)
return mismatch("beir flag mismatch");
if (expected.vectorsDisabled != actual.vectorsDisabled)
return mismatch("vectors disabled mismatch");
if (expected.requireKgReady != actual.requireKgReady)
return mismatch("kg requirement mismatch");
if (expected.graphRerankRequested != actual.graphRerankRequested)
return mismatch("graph rerank mismatch");
if (expected.searchEngine != actual.searchEngine)
return mismatch("search engine mismatch");
if (expected.expectedDocs != actual.expectedDocs)
return mismatch("expected docs mismatch");
if (expected.expectedQueries != actual.expectedQueries)
return mismatch("expected queries mismatch");
if (expected.corpusFingerprint != 0 && actual.corpusFingerprint != 0 &&
expected.corpusFingerprint != actual.corpusFingerprint) {
return mismatch("corpus fingerprint mismatch");
}
if (actual.status != "primed") {
return mismatch("cache status not reusable");
}
return true;
}
static BenchCacheMetadata
preferStrongerBenchCacheMetadata(BenchCacheMetadata preferred,
const std::optional<BenchCacheMetadata>& candidate) {
if (!candidate) {
return preferred;
}
const auto& c = *candidate;
if (preferred.datasetPath.empty())
preferred.datasetPath = c.datasetPath;
preferred.corpusFingerprint = std::max(preferred.corpusFingerprint, c.corpusFingerprint);
preferred.expectedDocs = std::max(preferred.expectedDocs, c.expectedDocs);
preferred.expectedQueries = std::max(preferred.expectedQueries, c.expectedQueries);
preferred.embeddedDocs = std::max(preferred.embeddedDocs, c.embeddedDocs);
preferred.vectorCount = std::max(preferred.vectorCount, c.vectorCount);
preferred.vectorIndexReady = preferred.vectorIndexReady || c.vectorIndexReady;
preferred.vectorsDbBytes = std::max(preferred.vectorsDbBytes, c.vectorsDbBytes);
preferred.vectorsWalBytes = std::max(preferred.vectorsWalBytes, c.vectorsWalBytes);
preferred.persistedHnswNodes = std::max(preferred.persistedHnswNodes, c.persistedHnswNodes);
preferred.persistedHnswMetaRows =
std::max(preferred.persistedHnswMetaRows, c.persistedHnswMetaRows);
preferred.persistedVec0Tables = std::max(preferred.persistedVec0Tables, c.persistedVec0Tables);
preferred.persistedVec0Rows = std::max(preferred.persistedVec0Rows, c.persistedVec0Rows);
preferred.persistedPqCodes = std::max(preferred.persistedPqCodes, c.persistedPqCodes);
preferred.persistedPqMeta = std::max(preferred.persistedPqMeta, c.persistedPqMeta);
if (preferred.status != "primed" && c.status == "primed") {
preferred.status = c.status;
}
return preferred;
}
static BenchCacheMetadata currentBenchCacheMetadata(BenchCacheMetadata metadata,
const yams::daemon::StatusResponse& status,
const fs::path& dataDir,
bool validatedIndexReusable) {
auto getCount = [&](std::string_view key) -> std::uintmax_t {
auto it = status.requestCounts.find(std::string(key));
return it == status.requestCounts.end() ? 0 : it->second;
};
const auto persistedHnsw = inspectPersistedHnswState(dataDir);
metadata.vectorIndexReady = validatedIndexReusable;
metadata.vectorsDbBytes = persistedHnsw.vectorsDbBytes;
metadata.vectorsWalBytes = persistedHnsw.vectorsWalBytes;
metadata.persistedHnswNodes = persistedHnsw.nodeRows;
metadata.persistedHnswMetaRows = persistedHnsw.metaRows;
metadata.persistedVec0Tables = persistedHnsw.vec0Tables;
metadata.persistedVec0Rows = persistedHnsw.vec0Rows;
metadata.persistedPqCodes = persistedHnsw.pqCodeRows;
metadata.persistedPqMeta = persistedHnsw.pqMetaRows;
metadata.embeddedDocs = static_cast<int>(getCount("documents_embedded"));
metadata.vectorCount = getCount("vector_count");
const bool queueDrained = getCount("embed_svc_queued") == 0 && getCount("embed_in_flight") == 0;
const bool fullCoverage =
metadata.embeddedDocs >= metadata.expectedDocs && metadata.expectedDocs > 0;
const bool cacheIndexReady = metadata.vectorsDisabled || metadata.vectorIndexReady;
metadata.status = (queueDrained && fullCoverage && cacheIndexReady) ? "primed" : "partial";
return metadata;
}
static fs::path makeCandidateDebugPath(const fs::path& base, const std::string& candidate,
const std::string& runId) {
const auto safeCandidate = sanitizeLabelForFilename(candidate);
const auto stem = base.stem().string();
const auto ext = base.extension().string();
const auto suffix = "_" + safeCandidate + "_" + runId;
if (!stem.empty()) {
return base.parent_path() / (stem + suffix + ext);
}
return base.parent_path() / (base.filename().string() + suffix);
}
static bool hasOverrideKey(const std::vector<EnvSetting>& settings, const std::string& key) {
return std::any_of(settings.begin(), settings.end(),
[&](const EnvSetting& setting) { return setting.key == key; });
}
static std::vector<OptimizationCandidate> defaultOptimizationCandidates() {
return {
{"auto_baseline",
"Auto tuner baseline with graph rerank enabled",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_DISABLE_SEARCH_REBUILDS", "0"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", std::nullopt},
{"YAMS_CANDIDATE_MULTIPLIER", std::nullopt},
},
true},
{"mixed_precision_profile",
"MIXED precision preset with lexical and vector guardrails",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
}},
{"mixed_precision_zoom_map",
"MIXED_PRECISION with map-level navigation bias",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ZOOM_LEVEL", "MAP"},
}},
{"mixed_precision_zoom_neighborhood",
"MIXED_PRECISION with neighborhood navigation bias",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ZOOM_LEVEL", "NEIGHBORHOOD"},
}},
{"mixed_precision_zoom_street",
"MIXED_PRECISION with street-level precision bias",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ZOOM_LEVEL", "STREET"},
}},
{"mixed_precision_semantic_recall_v1",
"MIXED_PRECISION + looser vector gate + extra semantic rescue",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
}},
{"mixed_precision_semantic_recall_lexical14_v1",
"Semantic recall v1 + stronger lexical floor/tiebreak guardrails",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
}},
{"mixed_default",
"Code/mixed tuned state with benchmark defaults",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
}},
{"mixed_precision_lexical_floor",
"MIXED defaults + lexical floor to preserve top text hits",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "8"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.12"},
}},
{"mixed_precision_lexical_floor_strong",
"MIXED defaults + stronger lexical floor",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "12"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.20"},
}},
{"mixed_precision_lexical_floor_strong_tiebreak",
"MIXED stronger lexical floor with lexical tie-break",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "12"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.20"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
}},
{"small_code_precision",
"Code-heavy state with stronger lexical/entity bias",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "SMALL_CODE"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
}},
{"mixed_recall_push",
"MIXED state with larger candidate pool and no vector skip",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_CANDIDATE_MULTIPLIER", "2.0"},
}},
{"mixed_baseline",
"MIXED state baseline",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
}},
{"mixed_precision_guardrails",
"MIXED + weighted reciprocal with vector-only precision guardrails",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "10"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.93"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.70"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.07"},
}},
{"mixed_precision_mrr_recovery_rrf",
"MIXED weighted reciprocal with softer vector-only guardrails",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "16"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.91"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.82"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
}},
{"mixed_precision_rrf_neighbor_t92_p80_k14",
"MIXED weighted reciprocal near-neighbor: stricter threshold, lower RRF k",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "14"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.80"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
}},
{"mixed_precision_rrf_neighbor_t92_p78_k16",
"MIXED weighted reciprocal near-neighbor: stricter threshold and penalty",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "16"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.78"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
}},
{"mixed_precision_rrf_neighbor_t90_p84_k18",
"MIXED weighted reciprocal near-neighbor: looser threshold, higher RRF k",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "18"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.90"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.84"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
}},
{"mixed_precision_rrf_rerank10",
"MIXED weighted reciprocal with broader rerank window",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "16"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.91"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.82"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "10"},
}},
{"mixed_ablation_disable_vector_gate",
"Ablation: disable vector-only threshold/penalty",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "16"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.00"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "1.00"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
}},
{"mixed_ablation_disable_intent_adaptive",
"Ablation: disable query intent weighting",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "16"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.91"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.82"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
{"YAMS_SEARCH_ENABLE_INTENT_ADAPTIVE", "0"},
}},
{"mixed_ablation_disable_graph_rerank",
"Ablation: disable graph reranking",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "0"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "16"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.91"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.82"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
}},
{"mixed_ablation_disable_reranker",
"Ablation: disable cross-encoder reranking stage",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "16"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.91"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.82"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.05"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
{"YAMS_SEARCH_ENABLE_RERANKING", "0"},
}},
{"scientific_precision_guardrails",
"SCIENTIFIC state with strict vector-only thresholding",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "SCIENTIFIC"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "0"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_RRF_K", "10"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.94"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.68"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.08"},
},
true},
// ── SCIENTIFIC retrieval candidates ────────────────────────────
// Purpose: optimize pre-fusion recall and vector-text balance for
// scientific/benchmark corpora (SciFact, TREC-COVID, NFCorpus).
{"scientific_ablation_graph_off",
"SCIENTIFIC auto baseline with graph rerank disabled",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "0"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_ablation_no_semantic_rescue",
"SCIENTIFIC auto baseline with semantic rescue disabled",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "0"},
},
true},
{"scientific_semantic_recall_v1",
"SCIENTIFIC auto + extra semantic rescue + looser vector gate",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.82"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "4"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.55"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_vector_fanout_2x",
"SCIENTIFIC auto + 2x vector fanout (vectorMaxResults 150->300)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_CANDIDATE_MULTIPLIER", "2.0"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_rrf_k6",
"SCIENTIFIC auto + tighter RRF k=6 (stronger top-rank preference)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_RRF_K", "6"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_rrf_k24",
"SCIENTIFIC auto + looser RRF k=24 (smoother cross-component fusion)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_RRF_K", "24"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
// ── Aggressive recall-push candidates (corpus-state-aware) ────────
{"scientific_recall_aggressive_v1",
"SCI scientific auto + recall push: vec200 sim0.10 rescue6 strong_vec0.60",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_SIMILARITY_THRESHOLD", "0.10"},
{"YAMS_SEARCH_VECTOR_MAX_RESULTS", "200"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "6"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.50"},
{"YAMS_SEARCH_ENABLE_STRONG_VECTOR_ONLY_RELIEF", "1"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_MIN_SCORE", "0.60"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_TOPRANK", "6"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_PENALTY", "0.94"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.82"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.90"},
{"YAMS_SEARCH_WEAK_QUERY_VECTOR_FANOUT_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_recall_max_v1",
"SCI max recall: vec250 sim0.05 rescue8 rerank20 strong_vec0.55 fanout3.5",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_SIMILARITY_THRESHOLD", "0.05"},
{"YAMS_SEARCH_VECTOR_MAX_RESULTS", "250"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "8"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.45"},
{"YAMS_SEARCH_ENABLE_STRONG_VECTOR_ONLY_RELIEF", "1"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_MIN_SCORE", "0.55"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_TOPRANK", "8"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_PENALTY", "0.95"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.80"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.92"},
{"YAMS_SEARCH_WEAK_QUERY_VECTOR_FANOUT_MULTIPLIER", "3.5"},
{"YAMS_SEARCH_RERANK_TOPK", "20"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_recall_vec300",
"SCI max pool: vec300 sim0.05 rescue10 rerank25",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_SIMILARITY_THRESHOLD", "0.05"},
{"YAMS_SEARCH_VECTOR_MAX_RESULTS", "300"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "10"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.40"},
{"YAMS_SEARCH_ENABLE_STRONG_VECTOR_ONLY_RELIEF", "1"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_MIN_SCORE", "0.50"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_TOPRANK", "10"},
{"YAMS_SEARCH_STRONG_VECTOR_ONLY_PENALTY", "0.95"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.78"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.94"},
{"YAMS_SEARCH_WEAK_QUERY_VECTOR_FANOUT_MULTIPLIER", "4.0"},
{"YAMS_SEARCH_RERANK_TOPK", "25"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
// ── Retrieval-lane candidates (enable disabled lanes for recall) ──
{"scientific_entity_vec_on",
"SCI scientific auto + entity vector retrieval enabled (GLiNER concepts)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENTITY_VECTOR_WEIGHT", "0.15"},
{"YAMS_SEARCH_ENTITY_VECTOR_MAX_RESULTS", "80"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_concept_boost_on",
"SCI scientific auto + concept boosting enabled (mined concepts)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.12"},
{"YAMS_SEARCH_CONCEPT_MIN_CONFIDENCE", "0.35"},
{"YAMS_SEARCH_CONCEPT_MAX_COUNT", "8"},
{"YAMS_SEARCH_CONCEPT_MAX_BOOST", "0.28"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_entity_concept_combined",
"SCI entity_vec + concept_boost + rescue=4 (multi-lane recall)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENTITY_VECTOR_WEIGHT", "0.12"},
{"YAMS_SEARCH_ENTITY_VECTOR_MAX_RESULTS", "60"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.10"},
{"YAMS_SEARCH_CONCEPT_MIN_CONFIDENCE", "0.35"},
{"YAMS_SEARCH_CONCEPT_MAX_COUNT", "6"},
{"YAMS_SEARCH_CONCEPT_MAX_BOOST", "0.22"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "4"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_rrf_k8",
"SCI scientific auto + RRF k=8 (tight top-rank focus)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_RRF_K", "8"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_subphrase_aggressive",
"SCI + aggressive sub-phrase expansion + rescue=4",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENABLE_SUB_PHRASE_EXPANSION", "1"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION_MIN_HITS", "2"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION_PENALTY", "0.60"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "4"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_combined_v1",
"entity_vec + concept_boost + rrf_k6 + rescue=4 + subphrase aggressive",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_ENTITY_VECTOR_WEIGHT", "0.12"},
{"YAMS_SEARCH_ENTITY_VECTOR_MAX_RESULTS", "60"},
{"YAMS_SEARCH_CONCEPT_BOOST_WEIGHT", "0.10"},
{"YAMS_SEARCH_CONCEPT_MIN_CONFIDENCE", "0.35"},
{"YAMS_SEARCH_CONCEPT_MAX_COUNT", "6"},
{"YAMS_SEARCH_CONCEPT_MAX_BOOST", "0.22"},
{"YAMS_SEARCH_RRF_K", "6"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "4"},
{"YAMS_SEARCH_ENABLE_SUB_PHRASE_EXPANSION", "1"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION_MIN_HITS", "2"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION_PENALTY", "0.60"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_text_heavy",
"SCI text weight 0.55 + simeon 0.15 + vector 0.20 (lexical-first)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_TEXT_WEIGHT", "0.55"},
{"YAMS_SEARCH_SIMEON_TEXT_WEIGHT", "0.15"},
{"YAMS_SEARCH_VECTOR_WEIGHT", "0.20"},
{"YAMS_SEARCH_RRF_K", "6"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_strategy_router",
"SCI + simeon EntropyRouter (BM25/Keyphrase/LeadField per-query strategy)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SIMEON_STRATEGY_ROUTER_ENABLED", "1"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"scientific_strategy_router_rrf6",
"SCI EntropyRouter + RRF k=6 + rescue=4 + subphrase aggressive",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_TUNING_OVERRIDE", std::nullopt},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SIMEON_STRATEGY_ROUTER_ENABLED", "1"},
{"YAMS_SEARCH_RRF_K", "6"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "4"},
{"YAMS_SEARCH_ENABLE_SUB_PHRASE_EXPANSION", "1"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION_MIN_HITS", "2"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION_PENALTY", "0.60"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
},
true},
{"mixed_graph_off",
"MIXED state with graph rerank disabled",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "0"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
}},
// ── Tier-1 diagnostic candidates ──────────────────────────────
// Purpose: determine if the 14 both-miss queries are recoverable
// by expanding the candidate pool and/or the evaluation window.
{"diag_winner_3x_candidates",
"Winner config + 3x candidate multiplier (vector pool 150->450)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
},
false,
true},
{"diag_winner_topk20",
"Winner config + topK=20 (recall@20 diagnostic)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_BENCH_TOPK", "20"},
},
false,
true},
{"diag_winner_topk50",
"Winner config + topK=50 (recall@50 diagnostic)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_BENCH_TOPK", "50"},
},
false,
true},
{"diag_winner_3x_topk50",
"Winner + 3x candidates + topK=50 (maximum retrieval diagnostic)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_BENCH_TOPK", "50"},
},
false,
true},
// ── Tier-2 cross-encoder reranker candidates ──────────────────
// Strategy: expand pool (3x) to get ~50 candidates, then use the
// bge-reranker-large cross-encoder to rescore and promote relevant
// documents that the bi-encoder ranked at positions 11-49 into
// the top-10. Tier-1 diagnostics showed 7 of 14 both-miss
// queries have relevant docs at ranks 11-49 — reranking should
// recover these.
// Current benchmark evidence: blend-60 beats blend-70 on both q20 and q300,
// while graph rerank / semantic rescue stay effectively inactive on SciFact.
// Treat rerank_3x_blend60_top50 as the engine-policy baseline; heavier recall probes
// below remain diagnostics unless they win on both quality and latency.
{"rerank_3x_replace_top50",
"3x pool + cross-encoder rerank top-50, replace scores (max reranker impact)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
// Cross-encoder reranking: rerank the top-50 candidates
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "1"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_blend60_top50",
"Current q300 winner: 3x pool + cross-encoder rerank top-50, blend 60/40 with fusion "
"scores",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
// Cross-encoder reranking: rerank the top-50, blend scores
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
},
true,
false},
{"rerank_4x_blend60_top50",
"Pre-fusion recall probe: winner + larger candidate multiplier (4x), rerank window stays "
"at top-50",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "4.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
},
false,
true},
{"rerank_3x_blend60_top50_fuse100",
"Pre-fusion recall probe: winner + moderately wider fusion window (fusion limit 100)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_FUSION_CANDIDATE_LIMIT", "100"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
},
false,
true},
{"rerank_3x_blend60_top50_lexical16",
"Pre-fusion recall probe: winner + milder lexical floor (top-16 / +0.24)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "16"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.24"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
},
false,
true},
{"rerank_3x_blend60_top50_fuse200",
"Pre-fusion recall probe: winner + wider fusion window (fusion limit 200), rerank window "
"stays at top-50",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_FUSION_CANDIDATE_LIMIT", "200"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_blend60_top50_lexical20",
"Pre-fusion recall probe: winner + stronger lexical floor (top-20 / +0.30)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "20"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.30"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"kg_budget_5ms",
"KG tradeoff probe: keep winner policy but tighten graph scoring budget to 5ms",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_GRAPH_BUDGET_MS", "5"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
{"YAMS_SEARCH_INCLUDE_COMPONENT_TIMING", "1"},
},
false,
true},
{"kg_topn_12",
"KG tradeoff probe: keep winner policy but limit graph rerank window to top-12",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_GRAPH_RERANK_TOPN", "12"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
{"YAMS_SEARCH_INCLUDE_COMPONENT_TIMING", "1"},
},
false,
true},
{"kg_weight_002_topn12",
"KG tradeoff probe: keep KG enabled with lighter weight 0.02 and top-12 rerank window",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_KG_WEIGHT", "0.02"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_GRAPH_RERANK_TOPN", "12"},
{"YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "0.12"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
{"YAMS_SEARCH_INCLUDE_COMPONENT_TIMING", "1"},
},
false,
true},
{"rerank_3x_blend40_top50",
"3x pool + cross-encoder rerank top-50, blend 40/60 (conservative)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
// Cross-encoder reranking: rerank the top-50, conservative blend
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.40"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_replace_top50_snippet512",
"3x pool + cross-encoder rerank top-50, replace scores, 512-char snippets",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
// Cross-encoder reranking: rerank top-50, larger snippets for more context
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "1"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "512"},
}},
// --- Faster rerank candidates (top-20) for quicker iteration ---
{"rerank_3x_replace_top20",
"3x pool + cross-encoder rerank top-20, replace scores",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "20"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "1"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_blend40_top20",
"3x pool + cross-encoder rerank top-20, blend 40/60 (conservative)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "20"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.40"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_blend60_top20",
"3x pool + cross-encoder rerank top-20, blend 60/40",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "20"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
// Adaptive blend: scale reranker weight by max-score confidence
{"rerank_3x_adaptive80_top20",
"3x pool + cross-encoder rerank top-20, adaptive blend (base 0.80, floor 0.10)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "20"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.80"},
{"YAMS_SEARCH_RERANK_ADAPTIVE_BLEND", "1"},
{"YAMS_SEARCH_RERANK_ADAPTIVE_BLEND_FLOOR", "0.10"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_adaptive60_top20",
"3x pool + cross-encoder rerank top-20, adaptive blend (base 0.60, floor 0.05)",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "20"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.60"},
{"YAMS_SEARCH_RERANK_ADAPTIVE_BLEND", "1"},
{"YAMS_SEARCH_RERANK_ADAPTIVE_BLEND_FLOOR", "0.05"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_blend70_top20",
"3x pool + cross-encoder rerank top-20, blend 70/30 with fusion scores",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
// Cross-encoder reranking: rerank the top-20, stronger blend
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "20"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.70"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_blend80_top20",
"3x pool + cross-encoder rerank top-20, blend 80/20 with fusion scores",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
// Cross-encoder reranking: rerank the top-20, aggressive blend
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "20"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.80"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
{"rerank_3x_blend70_top50",
"3x pool + cross-encoder rerank top-50, blend 70/30 with fusion scores",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
// Cross-encoder reranking: rerank the top-50, strong blend
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.70"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
}},
// Wide fusion window: pass many more fused results through the reranker
{"rerank_3x_blend70_top200_fuse200",
"3x pool + cross-encoder rerank top-200, blend 70/30, fusion window 200",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
// Cross-encoder reranking: rerank the top-200, strong blend
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "200"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.70"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
// Wider fusion window: keep 200 fused results (vs default 50)
{"YAMS_SEARCH_FUSION_CANDIDATE_LIMIT", "200"},
}},
// ── Tier 3: query expansion before rerank ───────────────────────────
{"rerank_3x_blend70_top50_multivec",
"3x pool + rerank top-50, blend 70/30, multi-vector sub-phrase search",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.70"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
{"YAMS_SEARCH_MULTI_VECTOR_QUERY", "1"},
{"YAMS_SEARCH_MULTI_VECTOR_MAX_PHRASES", "3"},
{"YAMS_SEARCH_MULTI_VECTOR_SCORE_DECAY", "0.85"},
}},
{"rerank_3x_blend70_top50_subphrase_fts",
"3x pool + rerank top-50, blend 70/30, sub-phrase FTS expansion",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.70"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION", "1"},
{"YAMS_SEARCH_SUB_PHRASE_MIN_HITS", "5"},
{"YAMS_SEARCH_SUB_PHRASE_PENALTY", "0.70"},
}},
{"rerank_3x_blend70_top50_multivec_subphrase",
"3x pool + rerank top-50, blend 70/30, multi-vector + sub-phrase FTS",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.70"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
{"YAMS_SEARCH_MULTI_VECTOR_QUERY", "1"},
{"YAMS_SEARCH_MULTI_VECTOR_MAX_PHRASES", "3"},
{"YAMS_SEARCH_MULTI_VECTOR_SCORE_DECAY", "0.85"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION", "1"},
{"YAMS_SEARCH_SUB_PHRASE_MIN_HITS", "5"},
{"YAMS_SEARCH_SUB_PHRASE_PENALTY", "0.70"},
}},
{"rerank_3x_blend50_top50_multivec_subphrase",
"3x pool + rerank top-50, blend 50/50, multi-vector + sub-phrase FTS",
{
{"YAMS_ENABLE_ENV_OVERRIDES", "1"},
{"YAMS_BENCH_FORCE_TUNING_OVERRIDE", std::nullopt},
{"YAMS_TUNING_OVERRIDE", "MIXED_PRECISION"},
{"YAMS_SEARCH_ENABLE_GRAPH_RERANK", "1"},
{"YAMS_SEARCH_VECTOR_ONLY_THRESHOLD", "0.92"},
{"YAMS_SEARCH_VECTOR_ONLY_PENALTY", "0.65"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_SLOTS", "2"},
{"YAMS_SEARCH_SEMANTIC_RESCUE_MIN_VECTOR_SCORE", "0.30"},
{"YAMS_SEARCH_ENABLE_ADAPTIVE_FALLBACK", "0"},
{"YAMS_SEARCH_LEXICAL_FLOOR_TOPN", "14"},
{"YAMS_SEARCH_LEXICAL_FLOOR_BOOST", "0.22"},
{"YAMS_SEARCH_ENABLE_LEXICAL_TIEBREAK", "1"},
{"YAMS_SEARCH_LEXICAL_TIEBREAK_EPS", "0.010"},
{"YAMS_CANDIDATE_MULTIPLIER", "3.0"},
{"YAMS_SEARCH_ENABLE_RERANKING", "1"},
{"YAMS_SEARCH_RERANK_TOPK", "50"},
{"YAMS_SEARCH_RERANK_REPLACE_SCORES", "0"},
{"YAMS_SEARCH_RERANK_WEIGHT", "0.50"},
{"YAMS_SEARCH_RERANK_SCORE_GAP_THRESHOLD", "0.0"},
{"YAMS_SEARCH_RERANK_SNIPPET_MAX_CHARS", "256"},
{"YAMS_SEARCH_MULTI_VECTOR_QUERY", "1"},
{"YAMS_SEARCH_MULTI_VECTOR_MAX_PHRASES", "3"},
{"YAMS_SEARCH_MULTI_VECTOR_SCORE_DECAY", "0.85"},
{"YAMS_SEARCH_SUB_PHRASE_EXPANSION", "1"},
{"YAMS_SEARCH_SUB_PHRASE_MIN_HITS", "5"},
{"YAMS_SEARCH_SUB_PHRASE_PENALTY", "0.70"},
}},
};
}
static std::set<std::string> parseCandidateFilter(const char* raw) {
std::set<std::string> selected;
if (!raw || !*raw) {
return selected;
}
std::string value(raw);
std::stringstream ss(value);
std::string token;
while (std::getline(ss, token, ',')) {
auto trim = [](std::string& s) {
const auto first = s.find_first_not_of(" \t\n\r");
if (first == std::string::npos) {
s.clear();
return;
}
const auto last = s.find_last_not_of(" \t\n\r");
s = s.substr(first, last - first + 1);
};
trim(token);
if (!token.empty()) {
selected.insert(token);
}
}
return selected;
}
enum class OptimizationProfile {
EnginePolicy,
Diagnostics,
All,
};
static OptimizationProfile parseOptimizationProfile(const char* raw) {
if (!raw || !*raw) {
return OptimizationProfile::EnginePolicy;
}
std::string value(raw);
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
if (value == "diagnostic" || value == "diagnostics" || value == "probe" || value == "probes") {
return OptimizationProfile::Diagnostics;
}
if (value == "all") {
return OptimizationProfile::All;
}
return OptimizationProfile::EnginePolicy;
}
static const char* optimizationProfileName(OptimizationProfile profile) {
switch (profile) {
case OptimizationProfile::EnginePolicy:
return "engine-policy";
case OptimizationProfile::Diagnostics:
return "diagnostics";
case OptimizationProfile::All:
return "all";
}
return "engine-policy";
}
static double computeHybridRegressionPenalty(const RetrievalMetrics& hybrid,
const RetrievalMetrics& keyword) {
const double hybridMrrLoss = std::max(0.0, keyword.mrr - hybrid.mrr);
return std::min(0.12, hybridMrrLoss * 2.0);
}
static double computeDuplicatePenalty(const RetrievalMetrics& hybrid) {
return std::min(0.08, std::max(0.0, hybrid.duplicateRateAtK) * 0.30);
}
static double computeLatencyPenalty(double avgHybridQueryMs) {
// Keep latency meaningful in the 3-7s/query range seen in real SciFact runs.
// The previous cap saturated far too early and made materially slower candidates
// look effectively free once they crossed ~800ms/query.
return std::clamp(std::max(0.0, avgHybridQueryMs) / 25000.0, 0.0, 0.25);
}
static double computePrecisionFailurePenalty(const QueryDiagnosticsSummary& summary) {
const double queryCount = static_cast<double>(std::max<std::uint64_t>(summary.queryCount, 1));
const double noRelevantHitRate =
static_cast<double>(summary.queryWithoutRelevantHitCount) / queryCount;
const double missPreFusionRate =
static_cast<double>(summary.missMissingPreFusionCount) / queryCount;
const double fusionCutoffRate = static_cast<double>(summary.missFusionCutoffCount) / queryCount;
const double rerankDropRate =
static_cast<double>(summary.missRerankWindowDropCount) / queryCount;
const double topKDropRate = static_cast<double>(summary.missTopKDropCount) / queryCount;
return std::min(0.25, missPreFusionRate * 0.25 + noRelevantHitRate * 0.10 +
fusionCutoffRate * 0.04 + rerankDropRate * 0.05 +
topKDropRate * 0.03);
}
static std::string summarizeTimingTradeoffs(const json& hybridDiag) {
if (!hybridDiag.is_object()) {
return "timings=unavailable";
}
const auto timingsIt = hybridDiag.find("timings_ms");
if (timingsIt == hybridDiag.end() || !timingsIt->is_object()) {
return "timings=unavailable";
}
const json& timings = *timingsIt;
std::vector<std::pair<std::string, double>> ranked;
ranked.reserve(timings.size());
for (const auto& [name, stats] : timings.items()) {
if (!stats.is_object()) {
continue;
}
auto meanIt = stats.find("mean");
if (meanIt == stats.end() || !meanIt->is_number()) {
continue;
}
ranked.emplace_back(name, meanIt->get<double>());
}
if (ranked.empty()) {
return "timings=unavailable";
}
std::sort(ranked.begin(), ranked.end(),
[](const auto& a, const auto& b) { return a.second > b.second; });
std::ostringstream oss;
oss << std::fixed << std::setprecision(1);
const size_t limit = std::min<size_t>(3, ranked.size());
for (size_t i = 0; i < limit; ++i) {
if (i > 0) {
oss << " ";
}
oss << ranked[i].first << '=' << ranked[i].second << "ms";
}
return oss.str();
}
static std::string summarizeStageTradeoffs(const json& hybridDiag) {
if (!hybridDiag.is_object()) {
return "stages=unavailable";
}
const auto stagesIt = hybridDiag.find("stage_contributions");
if (stagesIt == hybridDiag.end() || !stagesIt->is_object()) {
return "stages=unavailable";
}
const json& stages = *stagesIt;
std::vector<std::string> parts;
for (const auto& [name, stats] : stages.items()) {
if (!stats.is_object()) {
continue;
}
const double contributionRate = stats.value("contribution_rate", 0.0);
const double skipRate = stats.value("skip_rate", 0.0);
if (contributionRate <= 0.0 && skipRate <= 0.0) {
continue;
}
std::ostringstream oss;
oss << name << ":contrib=" << std::fixed << std::setprecision(2) << contributionRate
<< ",skip=" << skipRate;
parts.push_back(oss.str());
}
if (parts.empty()) {
return "stages=unavailable";
}
std::sort(parts.begin(), parts.end());
if (parts.size() > 3) {
parts.resize(3);
}
std::ostringstream out;
for (size_t i = 0; i < parts.size(); ++i) {
if (i > 0) {
out << " ";
}
out << parts[i];
}
return out.str();
}
static std::string summarizeStageMetricGaps(const json& hybridDiag) {
if (!hybridDiag.is_object()) {
return "stage_metrics=unavailable";
}
const auto metricsIt = hybridDiag.find("stage_metrics");
if (metricsIt == hybridDiag.end() || !metricsIt->is_object() || metricsIt->empty()) {
return "stage_metrics=unavailable";
}
static const std::vector<std::string> kStageOrder = {
"pre_fusion", "graphless_post_fusion", "post_fusion", "post_graph", "final",
};
std::vector<std::string> parts;
for (const auto& stage : kStageOrder) {
auto stageIt = metricsIt->find(stage);
if (stageIt == metricsIt->end() || !stageIt->is_object()) {
continue;
}
const double mrr = stageIt->value("mrr", 0.0);
const double recall = stageIt->value("recall_at_k", 0.0);
const double ndcg = stageIt->value("ndcg_at_k", 0.0);
const double mrrDelta = stageIt->value("mrr_delta_vs_final", 0.0);
const double ndcgDelta = stageIt->value("ndcg_delta_vs_final", 0.0);
std::ostringstream oss;
oss << stage << ":mrr=" << std::fixed << std::setprecision(3) << mrr
<< "(d=" << std::showpos << mrrDelta << std::noshowpos << ")"
<< ",rec=" << recall << ",ndcg=" << ndcg << "(d=" << std::showpos << ndcgDelta
<< std::noshowpos << ")";
parts.push_back(oss.str());
}
if (parts.empty()) {
return "stage_metrics=unavailable";
}
std::ostringstream out;
for (size_t i = 0; i < parts.size(); ++i) {
if (i > 0) {
out << " ";
}
out << parts[i];
}
return out.str();
}
static double computeOptimizationObjective(const RetrievalMetrics& hybrid,
const RetrievalMetrics& keyword,
const QueryDiagnosticsSummary& hybridDiagnostics,
double avgHybridQueryMs) {
// Precision-first objective for machine-first navigation: keep top-K correctness high while
// still rewarding rank quality and recall. Miss-stage penalties keep the loop focused on the
// actual places where relevant documents are lost.
const double quality = 0.32 * hybrid.mrr + 0.24 * hybrid.ndcgAtK + 0.24 * hybrid.precisionAtK +
0.12 * hybrid.recallAtK + 0.08 * hybrid.map;
const double hybridGain = std::max(0.0, hybrid.mrr - keyword.mrr);
const double regressionPenalty = computeHybridRegressionPenalty(hybrid, keyword);
const double duplicatePenalty = computeDuplicatePenalty(hybrid);
const double latencyPenalty = computeLatencyPenalty(avgHybridQueryMs);
const double precisionFailurePenalty = computePrecisionFailurePenalty(hybridDiagnostics);
return quality + 0.10 * hybridGain - regressionPenalty - duplicatePenalty - latencyPenalty -
precisionFailurePenalty;
}
static void appendOptimizationResultJson(const fs::path& outputFile,
const OptimizationRunResult& result) {
std::ofstream out(outputFile, std::ios::out | std::ios::app);
if (!out.is_open()) {
spdlog::warn("[OptLoop] Failed to open results file: {}", outputFile.string());
return;
}
json j;
j["candidate"] = result.candidate.name;
j["description"] = result.candidate.description;
j["success"] = result.success;
j["objective"] = result.objectiveScore;
j["hybrid_eval_ms"] = result.hybridEvalMs;
j["keyword_eval_ms"] = result.keywordEvalMs;
j["search_engine"] = result.searchEngine;
if (const char* embBackend = std::getenv("YAMS_EMBED_BACKEND"); embBackend && *embBackend) {
j["embedding_backend"] = embBackend;
} else {
j["embedding_backend"] = "daemon";
}
j["vectors_db_bytes"] = result.vectorsDbBytes;
j["vectors_wal_bytes"] = result.vectorsWalBytes;
j["persisted_hnsw_nodes"] = result.persistedHnswNodes;
j["persisted_hnsw_meta_rows"] = result.persistedHnswMetaRows;
j["persisted_vec0_tables"] = result.persistedVec0Tables;
j["persisted_vec0_rows"] = result.persistedVec0Rows;
j["persisted_pq_codes"] = result.persistedPqCodes;
j["persisted_pq_meta"] = result.persistedPqMeta;
j["tuning_state"] = result.tuningState;
j["tuning_reason"] = result.tuningReason;
j["error"] = result.errorMessage;
j["run_id"] = result.runId;
j["debug_file"] = result.debugFile;
j["trace_top_n"] = result.traceTopN;
j["trace_component_top_n"] = result.traceComponentTopN;
j["stage_trace_enabled"] = result.stageTraceEnabled;
j["hybrid_keyword_mrr_delta"] = result.hybridMetrics.mrr - result.keywordMetrics.mrr;
const double objectiveRegressionPenalty =
computeHybridRegressionPenalty(result.hybridMetrics, result.keywordMetrics);
const double objectiveDuplicatePenalty = computeDuplicatePenalty(result.hybridMetrics);
const double objectivePrecisionFailurePenalty =
computePrecisionFailurePenalty(result.hybridDiagnostics);
j["objective_penalties"] = {
{"hybrid_regression", objectiveRegressionPenalty},
{"duplicate_rate", objectiveDuplicatePenalty},
{"precision_failures", objectivePrecisionFailurePenalty},
};
j["hybrid"] = {
{"mrr", result.hybridMetrics.mrr},
{"recall_at_k", result.hybridMetrics.recallAtK},
{"precision_at_k", result.hybridMetrics.precisionAtK},
{"ndcg_at_k", result.hybridMetrics.ndcgAtK},
{"map", result.hybridMetrics.map},
{"duplicate_rate_at_k", result.hybridMetrics.duplicateRateAtK},
{"num_queries", result.hybridMetrics.numQueries},
};
j["keyword"] = {
{"mrr", result.keywordMetrics.mrr},
{"recall_at_k", result.keywordMetrics.recallAtK},
{"precision_at_k", result.keywordMetrics.precisionAtK},
{"ndcg_at_k", result.keywordMetrics.ndcgAtK},
{"map", result.keywordMetrics.map},
{"duplicate_rate_at_k", result.keywordMetrics.duplicateRateAtK},
{"num_queries", result.keywordMetrics.numQueries},
};
{
auto sampleMean = [](const std::vector<double>& v) -> double {
if (v.empty())
return 0.0;
double sum = 0.0;
for (double x : v)
sum += x;
return sum / static_cast<double>(v.size());
};
auto meanSignal = [&](const std::string& key) -> double {
auto it = result.hybridDiagnostics.tunerSignalSamples.find(key);
return it == result.hybridDiagnostics.tunerSignalSamples.end() ? 0.0
: sampleMean(it->second);
};
auto meanScoreMass = [&](const std::string& src) -> double {
auto it = result.hybridDiagnostics.fusionSourceScoreMassSamples.find(src);
return it == result.hybridDiagnostics.fusionSourceScoreMassSamples.end()
? 0.0
: sampleMean(it->second);
};
const double denseMass = meanScoreMass("vector") + meanScoreMass("compressed_ann") +
meanScoreMass("graph_vector") + meanScoreMass("entity_vector");
const double lexicalMass =
meanScoreMass("text") + meanScoreMass("graph_text") + meanScoreMass("path_tree");
const double otherMass = meanScoreMass("kg") + meanScoreMass("tag") +
meanScoreMass("metadata") + meanScoreMass("symbol");
const double totalMass = denseMass + lexicalMass + otherMass;
const double denseContrib = totalMass > 0.0 ? denseMass / totalMass : 0.0;
const double bm25Contrib = totalMass > 0.0 ? lexicalMass / totalMass : 0.0;
j["fusion_alpha"] = meanSignal("vector_weight");
j["text_weight"] = meanSignal("text_weight");
j["dense_contrib"] = denseContrib;
j["bm25_contrib"] = bm25Contrib;
j["other_contrib"] = totalMass > 0.0 ? otherMass / totalMass : 0.0;
j["ngram_range_used"] = "3-5";
j["idf_weighted"] = false;
}
j["hybrid_debug_summary"] = queryDiagnosticsToJson(result.hybridDiagnostics);
j["keyword_debug_summary"] = queryDiagnosticsToJson(result.keywordDiagnostics);
json env = json::object();
for (const auto& setting : result.candidate.envOverrides) {
if (setting.value.has_value()) {
env[setting.key] = *setting.value;
} else {
env[setting.key] = nullptr;
}
}
j["env_overrides"] = env;
out << j.dump() << "\n";
}
static std::chrono::milliseconds defaultBenchQueryTimeout(const std::string& searchType) {
if (searchType == "hybrid") {
// Hybrid benchmark runs exercise graph rerank, ONNX reranking, and stage tracing.
// The default 20s request timeout is too short for cold-ish benchmark slices and
// causes partial JSONL output because timed-out queries are skipped entirely.
return std::chrono::milliseconds{120000};
}
return std::chrono::milliseconds{20000};
}
static std::chrono::milliseconds configuredBenchQueryTimeout(const std::string& searchType) {
if (const char* env = std::getenv("YAMS_BENCH_QUERY_TIMEOUT_MS")) {
return std::chrono::milliseconds{
static_cast<std::chrono::milliseconds::rep>(std::stoll(env))};
}
return defaultBenchQueryTimeout(searchType);
}
static void ingestStageRetrievalMetrics(QueryDiagnosticsSummary& summary,
const std::map<std::string, std::string>& searchStats,
const TestQuery& tq, int k) {
static const std::vector<std::pair<std::string, std::string>> kStageDocIdStats = {
{"pre_fusion", "trace_pre_fusion_doc_ids"},
{"graphless_post_fusion", "trace_graphless_post_fusion_doc_ids"},
{"post_fusion", "trace_post_fusion_doc_ids"},
{"post_graph", "trace_post_graph_doc_ids"},
{"final", "trace_final_doc_ids"},
};
for (const auto& [stage, statKey] : kStageDocIdStats) {
auto it = searchStats.find(statKey);
if (it == searchStats.end() || it->second.empty()) {
continue;
}
auto rankedDocIds = splitTabList(it->second, static_cast<size_t>(std::max(0, k)));
yams::bench::addStageMetricSample(summary.stageRetrievalMetrics[stage], rankedDocIds, tq,
k);
}
}
RetrievalMetrics evaluateQueries(yams::daemon::DaemonClient& client, const fs::path& corpusDir,
const std::vector<TestQuery>& queries, int k,
const std::string& searchType = "hybrid",
QueryDiagnosticsSummary* diagnostics = nullptr,
bool emitDebug = true,
std::vector<double>* latencySamplesMs = nullptr,
std::vector<TestQuery>* failedQueries = nullptr) {
RetrievalMetrics metrics;
metrics.numQueries = static_cast<int>(queries.size());
double totalMRR = 0.0, totalRecall = 0.0, totalPrecision = 0.0, totalNDCG = 0.0, totalMAP = 0.0;
std::uint64_t totalAttempts = 0;
std::uint64_t streamingCount = 0;
std::uint64_t fuzzyRetryCount = 0;
std::uint64_t literalRetryCount = 0;
std::uint64_t duplicateTopKCount = 0;
std::uint64_t totalTopKCount = 0;
for (std::size_t queryIndex = 0; queryIndex < queries.size(); ++queryIndex) {
const auto& tq = queries[queryIndex];
yams::cli::search_runner::DaemonSearchOptions opts;
opts.query = tq.query;
opts.searchType = searchType;
opts.limit = static_cast<std::size_t>(k);
const auto queryTimeout = configuredBenchQueryTimeout(searchType);
opts.timeout = queryTimeout;
opts.allowFuzzyRetry = false;
opts.allowLiteralTextRetry = false;
const bool benchDiagEnabled = []() -> bool {
if (const char* env = std::getenv("YAMS_BENCH_DIAG"); env && std::string(env) == "1") {
return true;
}
return false;
}();
const bool benchVerbose = []() -> bool {
if (const char* env = std::getenv("YAMS_BENCH_VERBOSE"); env) {
std::string value(env);
std::transform(value.begin(), value.end(), value.begin(), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
return value == "1" || value == "true" || value == "yes" || value == "on";
}
return false;
}();
if (benchVerbose) {
spdlog::debug("Executing search query: '{}'", tq.query);
}
// Wait at least as long as the query timeout, plus a small margin.
// Using a shorter run_sync deadline can leave in-flight requests behind,
// which can cascade into timeouts/backpressure across subsequent queries.
auto waitBudget = std::chrono::duration_cast<std::chrono::milliseconds>(opts.timeout) +
std::chrono::seconds(10);
if (waitBudget < std::chrono::seconds(10)) {
waitBudget = std::chrono::seconds(10);
}
auto run = benchRunSync(
yams::cli::search_runner::daemon_search(client, opts, benchUseStreamingSearch()),
waitBudget);
if (!run) {
spdlog::warn("Search failed for query '{}': {}", tq.query, run.error().message);
if (failedQueries) {
failedQueries->push_back(tq);
}
DebugLogEntry failedEntry;
failedEntry.query = tq.query;
failedEntry.queryIndex = static_cast<int>(queryIndex);
failedEntry.searchType = searchType;
failedEntry.attempts = 0;
failedEntry.usedStreaming = false;
failedEntry.usedFuzzyFlag = false;
failedEntry.usedLiteralFlag = false;
failedEntry.usedFuzzyRetry = false;
failedEntry.usedLiteralTextRetry = false;
failedEntry.relevantDocIds.assign(tq.relevantDocIds.begin(), tq.relevantDocIds.end());
failedEntry.relevantFiles.assign(tq.relevantFiles.begin(), tq.relevantFiles.end());
failedEntry.diagnostics.push_back("search_error=" + run.error().message);
failedEntry.diagnostics.push_back("searchType=" + opts.searchType);
failedEntry.diagnostics.push_back("limit=" + std::to_string(opts.limit));
failedEntry.diagnostics.push_back(
"timeout_ms=" +
std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(opts.timeout).count()));
const json relevantDecisionTrace = buildRelevantDecisionTrace(
failedEntry.relevantDocIds, failedEntry.returnedDocIds, failedEntry.searchStats);
failedEntry.extraFields["relevant_decision_trace"] = relevantDecisionTrace;
if (emitDebug) {
debugLogWriteJsonLine(failedEntry);
}
if (diagnostics) {
ingestQueryDiagnostics(*diagnostics, failedEntry.searchStats, relevantDecisionTrace,
true, false, false, -1);
}
continue;
}
totalAttempts += run.value().attempts;
if (run.value().usedStreaming)
streamingCount++;
if (run.value().usedFuzzyRetry)
fuzzyRetryCount++;
if (run.value().usedLiteralTextRetry)
literalRetryCount++;
const auto& results = run.value().response.results;
if (latencySamplesMs) {
const auto latencyIt = run.value().response.searchStats.find("latency_ms");
if (latencyIt != run.value().response.searchStats.end()) {
try {
const double latencyMs = std::stod(latencyIt->second);
if (std::isfinite(latencyMs) && latencyMs >= 0.0) {
latencySamplesMs->push_back(latencyMs);
}
} catch (const std::exception& e) {
if (benchVerbose) {
spdlog::debug("Ignoring malformed benchmark latency '{}': {}",
latencyIt->second, e.what());
}
}
}
}
if (benchVerbose) {
spdlog::debug("Search returned {} results for query '{}' (attempts={}, streaming={}, "
"fuzzy_retry={}, literal_retry={})",
results.size(), tq.query, run.value().attempts, run.value().usedStreaming,
run.value().usedFuzzyRetry, run.value().usedLiteralTextRetry);
}
// Detailed result logging for debugging retrieval quality
if (benchDiagEnabled) {
spdlog::debug(" [{}] Expected relevant: {} docs", searchType,
tq.relevantDocIds.size());
for (size_t i = 0; i < std::min((size_t)5, results.size()); ++i) {
const std::string filename = fs::path(results[i].path).filename().string();
const std::string docId = yams::bench::canonicalResultDocId(tq, filename);
const bool relevant = yams::bench::isRelevantDocForQuery(tq, docId);
spdlog::debug(" [{}] Result {}: path='{}' docId='{}' score={:.4f} {}", searchType,
i, results[i].path, docId, results[i].score,
relevant ? "RELEVANT" : "");
}
}
DebugLogEntry debugEntry;
debugEntry.query = tq.query;
debugEntry.queryIndex = static_cast<int>(queryIndex);
debugEntry.searchType = searchType;
debugEntry.attempts = run.value().attempts;
debugEntry.usedStreaming = run.value().usedStreaming;
debugEntry.usedFuzzyFlag = false;
debugEntry.usedLiteralFlag = false;
debugEntry.usedFuzzyRetry = run.value().usedFuzzyRetry;
debugEntry.usedLiteralTextRetry = run.value().usedLiteralTextRetry;
debugEntry.searchStats = run.value().response.searchStats;
debugEntry.relevantDocIds.assign(tq.relevantDocIds.begin(), tq.relevantDocIds.end());
debugEntry.relevantFiles.assign(tq.relevantFiles.begin(), tq.relevantFiles.end());
if (benchDiagEnabled) {
debugEntry.diagnostics.push_back("searchType=" + opts.searchType);
debugEntry.diagnostics.push_back("limit=" + std::to_string(opts.limit));
debugEntry.diagnostics.push_back(
"timeout_ms=" +
std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(opts.timeout).count()));
}
for (size_t i = 0; i < std::min((size_t)k, results.size()); ++i) {
debugEntry.returnedPaths.push_back(results[i].path);
debugEntry.returnedScores.push_back(static_cast<float>(results[i].score));
const std::string filename = fs::path(results[i].path).filename().string();
debugEntry.returnedDocIds.push_back(yams::bench::canonicalResultDocId(tq, filename));
totalTopKCount++;
}
const auto scoreSample = yams::bench::scoreRankedDocIds(tq, debugEntry.returnedDocIds, k);
debugEntry.returnedGrades = scoreSample.retrievedGrades;
for (const auto& docId : scoreSample.duplicateDocIds) {
duplicateTopKCount++;
debugEntry.diagnostics.push_back("duplicate_topk_doc=" + docId);
}
const json relevantDecisionTrace = buildRelevantDecisionTrace(
debugEntry.relevantDocIds, debugEntry.returnedDocIds, debugEntry.searchStats);
debugEntry.extraFields["relevant_decision_trace"] = relevantDecisionTrace;
debugEntry.extraFields["query_metrics"] = {
{"mrr", scoreSample.reciprocalRank},
{"recall_at_k", scoreSample.recallAtK},
{"precision_at_k", scoreSample.precisionAtK},
{"ndcg_at_k", scoreSample.ndcgAtK},
{"map", scoreSample.averagePrecision},
{"first_relevant_rank", scoreSample.firstRelevantRank},
};
if (benchDiagEnabled && results.empty() && tq.useDocIds && !tq.relevantDocIds.empty()) {
// Opt-in diagnostic: show a snippet of the relevant BEIR doc file(s).
std::size_t printed = 0;
for (const auto& docId : tq.relevantDocIds) {
if (printed >= 2)
break;
const fs::path p = corpusDir / (docId + ".txt");
if (!fs::exists(p)) {
debugEntry.diagnostics.push_back("relevant_doc_missing=" + p.string());
continue;
}
std::ifstream in(p);
if (!in.is_open()) {
debugEntry.diagnostics.push_back("relevant_doc_open_failed=" + p.string());
continue;
}
std::string snippet;
snippet.resize(240);
in.read(snippet.data(), static_cast<std::streamsize>(snippet.size()));
snippet.resize(static_cast<std::size_t>(in.gcount()));
for (auto& c : snippet) {
if (c == '\n' || c == '\r' || c == '\t')
c = ' ';
}
debugEntry.diagnostics.push_back("relevant_doc_snippet[" + docId + "]=" + snippet);
++printed;
}
}
// Opt-in diagnostic: run a few "shadow" queries for failing cases.
// This does not affect scoring; it only emits diagnostic entries.
if (emitDebug && benchDiagEnabled && results.empty()) {
// 1) Try a shorter query with a few salient tokens to test whether the full
// sentence query is failing due to strict AND semantics / tokenization.
std::istringstream iss(tq.query);
std::string token;
std::vector<std::string> picked;
picked.reserve(3);
while (iss >> token && picked.size() < 3) {
// Strip trailing punctuation for the diagnostic query.
while (!token.empty() && std::ispunct(static_cast<unsigned char>(token.back()))) {
token.pop_back();
}
if (token.size() < 4)
continue;
picked.push_back(token);
}
if (!picked.empty()) {
std::string diagQuery;
for (size_t i = 0; i < picked.size(); ++i) {
if (i)
diagQuery += ' ';
diagQuery += picked[i];
}
yams::cli::search_runner::DaemonSearchOptions shadow;
shadow.query = diagQuery;
shadow.searchType = opts.searchType;
shadow.limit = opts.limit;
shadow.timeout = opts.timeout;
shadow.allowFuzzyRetry = false;
shadow.allowLiteralTextRetry = false;
DebugLogEntry shadowEntry;
shadowEntry.query = "__diag_shadow_query__";
shadowEntry.queryIndex = static_cast<int>(queryIndex);
shadowEntry.searchType = searchType;
shadowEntry.relevantDocIds.assign(tq.relevantDocIds.begin(),
tq.relevantDocIds.end());
shadowEntry.diagnostics.push_back("original_query=" + tq.query);
shadowEntry.diagnostics.push_back("shadow_query=" + diagQuery);
auto shadowRun = benchRunSync(
yams::cli::search_runner::daemon_search(client, shadow,
benchUseStreamingSearch()),
std::chrono::duration_cast<std::chrono::milliseconds>(shadow.timeout) +
std::chrono::seconds(10));
if (shadowRun) {
shadowEntry.attempts = shadowRun.value().attempts;
shadowEntry.usedStreaming = shadowRun.value().usedStreaming;
shadowEntry.usedFuzzyRetry = shadowRun.value().usedFuzzyRetry;
shadowEntry.usedLiteralTextRetry = shadowRun.value().usedLiteralTextRetry;
shadowEntry.returnedDocIds.push_back(
"result_count=" +
std::to_string(shadowRun.value().response.results.size()));
for (size_t i = 0;
i < std::min<size_t>(5, shadowRun.value().response.results.size()); ++i) {
shadowEntry.returnedPaths.push_back(
shadowRun.value().response.results[i].path);
}
} else {
shadowEntry.returnedDocIds.push_back("shadow_error=" +
shadowRun.error().message);
}
debugLogWriteJsonLine(shadowEntry);
}
}
if (emitDebug) {
debugLogWriteJsonLine(debugEntry);
}
if (diagnostics) {
ingestQueryDiagnostics(*diagnostics, run.value().response.searchStats,
relevantDecisionTrace, false, !results.empty(),
scoreSample.hadDuplicateInTopK, scoreSample.firstRelevantRank);
ingestStageRetrievalMetrics(*diagnostics, run.value().response.searchStats, tq, k);
}
totalMRR += scoreSample.reciprocalRank;
totalRecall += scoreSample.recallAtK;
totalPrecision += scoreSample.precisionAtK;
totalMAP += scoreSample.averagePrecision;
totalNDCG += scoreSample.ndcgAtK;
}
if (metrics.numQueries > 0) {
metrics.mrr = totalMRR / metrics.numQueries;
metrics.recallAtK = totalRecall / metrics.numQueries;
metrics.precisionAtK = totalPrecision / metrics.numQueries;
metrics.ndcgAtK = totalNDCG / metrics.numQueries;
metrics.map = totalMAP / metrics.numQueries;
if (totalTopKCount > 0) {
metrics.duplicateRateAtK =
static_cast<double>(duplicateTopKCount) / static_cast<double>(totalTopKCount);
}
spdlog::info("Search execution stats: queries={} total_attempts={} avg_attempts={:.2f} "
"streaming={} fuzzy_retries={} literal_retries={} duplicate_rate@k={:.4f}",
metrics.numQueries, totalAttempts,
static_cast<double>(totalAttempts) / metrics.numQueries, streamingCount,
fuzzyRetryCount, literalRetryCount, metrics.duplicateRateAtK);
}
return metrics;
}
RetrievalMetrics evaluateGrepQueries(yams::daemon::DaemonClient& client,
const std::vector<TestQuery>& queries, int k) {
RetrievalMetrics metrics;
metrics.numQueries = static_cast<int>(queries.size());
double totalMRR = 0.0;
double totalRecall = 0.0;
double totalPrecision = 0.0;
double totalNDCG = 0.0;
double totalMAP = 0.0;
std::uint64_t totalTopKCount = 0;
std::uint64_t duplicateTopKCount = 0;
for (const auto& tq : queries) {
yams::daemon::GrepRequest req;
req.pattern = tq.query;
req.literalText = true;
req.pathsOnly = true;
req.maxMatches = 1;
req.useSession = false;
const auto queryTimeout = configuredBenchQueryTimeout("grep");
auto waitBudget = queryTimeout + std::chrono::seconds(10);
if (waitBudget < std::chrono::seconds(10)) {
waitBudget = std::chrono::seconds(10);
}
auto run = benchRunSync(client.grep(req), waitBudget);
if (!run) {
spdlog::warn("Grep failed for query '{}': {}", tq.query, run.error().message);
continue;
}
std::vector<std::string> rankedDocIds;
rankedDocIds.reserve(std::min<std::size_t>(static_cast<std::size_t>(std::max(0, k)),
run.value().pathsOnly.size()));
for (size_t i = 0; i < std::min<std::size_t>(static_cast<std::size_t>(std::max(0, k)),
run.value().pathsOnly.size());
++i) {
const std::string filename = fs::path(run.value().pathsOnly[i]).filename().string();
rankedDocIds.push_back(yams::bench::canonicalResultDocId(tq, filename));
totalTopKCount++;
}
const auto scoreSample = yams::bench::scoreRankedDocIds(tq, rankedDocIds, k);
duplicateTopKCount += scoreSample.duplicateDocIds.size();
totalMRR += scoreSample.reciprocalRank;
totalRecall += scoreSample.recallAtK;
totalPrecision += scoreSample.precisionAtK;
totalMAP += scoreSample.averagePrecision;
totalNDCG += scoreSample.ndcgAtK;
}
if (metrics.numQueries > 0) {
const double queryCount = static_cast<double>(metrics.numQueries);
metrics.mrr = totalMRR / queryCount;
metrics.recallAtK = totalRecall / queryCount;
metrics.precisionAtK = totalPrecision / queryCount;
metrics.ndcgAtK = totalNDCG / queryCount;
metrics.map = totalMAP / queryCount;
if (totalTopKCount > 0) {
metrics.duplicateRateAtK =
static_cast<double>(duplicateTopKCount) / static_cast<double>(totalTopKCount);
}
}
return metrics;
}
struct BenchFixture {
struct SetupTimingMetrics {
bool coldIngestPerformed = false;
int64_t admissionMs = 0;
int64_t storageReadyMs = 0;
int64_t pipelineDrainMs = 0;
int64_t searchabilityReadyMs = 0;
int64_t enrichmentReadyMs = 0;
bool vectorIndexPrepared = false;
bool vectorIndexPersisted = false;
bool vectorIndexReusableBefore = false;
bool vectorIndexReusableAfter = false;
bool vectorQueryReadyAfter = false;
int64_t vectorIndexPrepareMs = 0;
int64_t vectorIndexPersistMs = 0;
int64_t vectorIndexReusableProbeMs = 0;
std::uint64_t vectorIndexReusableProbeCount = 0;
int64_t setupTotalMs = 0;
};
std::unique_ptr<DaemonHarness> harness;
std::unique_ptr<ScopedEnvOverrides> scopedEnv;
std::unique_ptr<ScopedEnvOverrides> pluginScopedEnv;
std::unique_ptr<yams::daemon::DaemonClient> client;
std::unique_ptr<CorpusGenerator> corpus;
std::unique_ptr<BEIRCorpusLoader> beirCorpus;
fs::path benchCorpusDir;
fs::path warmDataDirPath;
std::optional<BenchCacheMetadata> warmCacheMetadata;
std::vector<TestQuery> queries;
int corpusSize = 50, numQueries = 10, topK = 10;
bool useBEIR = false;
SyntheticCorpusMode syntheticCorpusMode = SyntheticCorpusMode::Generic;
bool warmDataDir = false; // True when reusing a pre-ingested data directory
SetupTimingMetrics setupTimings;
std::string datasetName = "synthetic";
std::string beirDatasetName; // Name of BEIR dataset (scifact, cqadupstack, etc.)
bool useSyntheticCommunityCorpus() const {
return syntheticCorpusMode == SyntheticCorpusMode::CommunityGraph;
}
std::string datasetDisplayName() const {
if (useBEIR) {
return beirDatasetName == "local-manifest" ? std::string("local manifest")
: beirDatasetName + " BEIR";
}
if (syntheticCorpusMode == SyntheticCorpusMode::CommunityGraph) {
return "synthetic-community";
}
if (syntheticCorpusMode == SyntheticCorpusMode::HardLexical) {
return "synthetic-hard";
}
return datasetName;
}
bool benchmarkSemanticNeighborSeedingEnabled() const {
const char* raw = std::getenv("YAMS_BENCH_SEED_SEMANTIC_NEIGHBORS");
if (!(raw && *raw)) {
return false;
}
std::string value(raw);
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return value == "1" || value == "true" || value == "yes" || value == "on";
}
void seedSyntheticCommunityEdges() {
if (!useSyntheticCommunityCorpus()) {
return;
}
auto* daemon = harness ? harness->daemon() : nullptr;
auto* serviceManager = daemon ? daemon->getServiceManager() : nullptr;
auto metadataRepo = serviceManager ? serviceManager->getMetadataRepo() : nullptr;
auto kgStore = metadataRepo ? metadataRepo->getKnowledgeGraphStore() : nullptr;
if (!metadataRepo || !kgStore) {
throw std::runtime_error(
"Synthetic community corpus requires metadata and KG stores after ingest");
}
const std::array<std::string, 3> docNames = {
"community_target.txt", "community_partner.txt", "community_rival.txt"};
std::vector<std::int64_t> nodeIds;
nodeIds.reserve(docNames.size());
for (const auto& docName : docNames) {
const auto docPath = (benchCorpusDir / docName).string();
auto docResult = metadataRepo->findDocumentByExactPath(docPath);
if (!docResult) {
throw std::runtime_error("Failed to resolve synthetic community document " +
docPath + ": " + docResult.error().message);
}
if (!docResult.value().has_value()) {
throw std::runtime_error("Synthetic community document missing from metadata: " +
docPath);
}
const auto& doc = *docResult.value();
auto nodeIdResult = kgStore->ensureDocumentNode(doc.sha256Hash, doc.fileName);
if (!nodeIdResult) {
throw std::runtime_error("Failed to ensure synthetic community KG node: " +
nodeIdResult.error().message);
}
nodeIds.push_back(nodeIdResult.value());
}
std::vector<metadata::KGEdge> edges;
metadata::KGEdge forward;
forward.srcNodeId = nodeIds[0];
forward.dstNodeId = nodeIds[1];
forward.relation = "semantic_neighbor";
forward.weight = 1.0f;
edges.push_back(forward);
metadata::KGEdge reverse;
reverse.srcNodeId = nodeIds[1];
reverse.dstNodeId = nodeIds[0];
reverse.relation = "semantic_neighbor";
reverse.weight = 1.0f;
edges.push_back(reverse);
auto addEdgesResult = kgStore->addEdgesUnique(edges);
if (!addEdgesResult) {
throw std::runtime_error("Failed to seed synthetic community KG edges: " +
addEdgesResult.error().message);
}
spdlog::info("Seeded synthetic community KG edges for {} documents", docNames.size());
}
bool seedBenchmarkSemanticNeighbors() {
if (!benchmarkSemanticNeighborSeedingEnabled()) {
return false;
}
auto* daemon = harness ? harness->daemon() : nullptr;
auto* serviceManager = daemon ? daemon->getServiceManager() : nullptr;
auto metadataRepo = serviceManager ? serviceManager->getMetadataRepo() : nullptr;
auto kgStore = metadataRepo ? metadataRepo->getKnowledgeGraphStore() : nullptr;
auto vectorDb = serviceManager ? serviceManager->getVectorDatabase() : nullptr;
if (!metadataRepo || !kgStore) {
throw std::runtime_error("Semantic benchmark seeding requires metadata and KG stores");
}
auto parseSizeEnv = [](const char* primary, const char* fallback,
std::size_t defaultValue) {
for (const char* key : {primary, fallback}) {
if (!key) {
continue;
}
if (const char* raw = std::getenv(key); raw && *raw) {
try {
return static_cast<std::size_t>(std::max(1, std::stoi(raw)));
} catch (...) {
}
}
}
return defaultValue;
};
auto parseFloatEnv = [](const char* primary, const char* fallback, float defaultValue) {
for (const char* key : {primary, fallback}) {
if (!key) {
continue;
}
if (const char* raw = std::getenv(key); raw && *raw) {
try {
return std::clamp(std::stof(raw), 0.0f, 1.0f);
} catch (...) {
}
}
}
return defaultValue;
};
auto parseBoolEnv = [](const char* key, bool defaultValue) {
if (!(key && *key)) {
return defaultValue;
}
if (const char* raw = std::getenv(key); raw && *raw) {
std::string value(raw);
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return value == "1" || value == "true" || value == "yes" || value == "on";
}
return defaultValue;
};
std::string topologySource = "vector";
if (const char* raw = std::getenv("YAMS_BENCH_TOPOLOGY_SOURCE"); raw && *raw) {
topologySource = raw;
std::transform(topologySource.begin(), topologySource.end(), topologySource.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
}
if (topologySource == "lexical" || topologySource == "bm25") {
topologySource = "fts5";
}
if (topologySource == "graph") {
topologySource = "kg";
}
if (topologySource == "title" || topologySource == "heading") {
topologySource = "header";
}
if (topologySource == "segment-keyphrase" || topologySource == "segmentkeyphrase") {
topologySource = "segment_keyphrase";
}
if (topologySource != "vector" && topologySource != "fts5" &&
topologySource != "keyphrase" && topologySource != "segment_keyphrase" &&
topologySource != "kg" && topologySource != "gliner" && topologySource != "header" &&
topologySource != "theme") {
throw std::runtime_error("YAMS_BENCH_TOPOLOGY_SOURCE must be vector, fts5, "
"keyphrase, segment_keyphrase, kg, gliner, header, or theme");
}
if (topologySource == "vector" && !vectorDb) {
throw std::runtime_error("Vector topology-source seeding requires vector store");
}
const std::size_t semanticTopK =
parseSizeEnv("YAMS_BENCH_SEED_SEMANTIC_TOPK", "YAMS_GRAPH_SEMANTIC_TOPK", 6);
const float semanticThreshold = parseFloatEnv("YAMS_BENCH_SEED_SEMANTIC_THRESHOLD",
"YAMS_GRAPH_SEMANTIC_THRESHOLD", 0.30f);
const float semanticWeightFloor =
parseFloatEnv("YAMS_BENCH_SEED_SEMANTIC_WEIGHT_FLOOR", nullptr, 0.10f);
// Off: mutual pairs only (the historical seeding). On: also one-way kNN edges, as the
// daemon writes them; the connected engine's roads need those (see
// buildSemanticNeighborSeedEdges).
const auto seedingMode = parseBoolEnv("YAMS_BENCH_SEED_SEMANTIC_DIRECTED", false)
? yams::bench::SemanticNeighborSeeding::DirectedKnn
: yams::bench::SemanticNeighborSeeding::MutualPairs;
const bool seedQrelCommunities = parseBoolEnv("YAMS_BENCH_SEED_QREL_COMMUNITIES", false) &&
useBEIR && static_cast<bool>(beirCorpus);
const std::size_t qrelTopK = parseSizeEnv("YAMS_BENCH_SEED_QREL_TOPK", nullptr, 4);
const std::size_t kgFeatureLimit =
parseSizeEnv("YAMS_BENCH_SEED_KG_FEATURE_LIMIT", nullptr, 512);
const std::size_t keyphraseFeatureLimit =
parseSizeEnv("YAMS_BENCH_KEYPHRASE_FEATURE_LIMIT", nullptr, 16);
const std::size_t keyphraseMaxDocumentFrequency =
parseSizeEnv("YAMS_BENCH_KEYPHRASE_MAX_DF", nullptr, 64);
const std::size_t segmentTokenBudget =
parseSizeEnv("YAMS_BENCH_SEGMENT_TOKENS", nullptr, 192);
const std::size_t segmentTokenOverlap =
parseSizeEnv("YAMS_BENCH_SEGMENT_OVERLAP", nullptr, 32);
const std::size_t segmentFeatureLimit =
parseSizeEnv("YAMS_BENCH_SEGMENT_FEATURE_LIMIT", nullptr, 12);
const std::size_t segmentMaxDocumentFrequency =
parseSizeEnv("YAMS_BENCH_SEGMENT_MAX_DF", nullptr, keyphraseMaxDocumentFrequency);
const std::size_t segmentMaxEdgesPerFeature =
parseSizeEnv("YAMS_BENCH_SEGMENT_FEATURE_EDGE_BUDGET", nullptr, 64);
const std::size_t topologyMaxComponentDocs =
parseSizeEnv("YAMS_BENCH_TOPOLOGY_MAX_COMPONENT_DOCS", nullptr, 64);
auto validateAndLogTopologyCertificate =
[&](const yams::bench::BenchmarkTopologyConstructionCertificate& cert) {
auto checkedCert = cert;
checkedCert.gates.maxComponentDocs = topologyMaxComponentDocs;
const auto validation =
yams::bench::validateTopologyConstructionCertificate(checkedCert);
if (g_debugOut) {
DebugLogEntry certificateEntry;
certificateEntry.searchType = "benchmark_setup";
certificateEntry.extraFields = {
{"event", "topology_construction_certificate"},
{"topology_source", topologySource},
{"certificate", yams::bench::topologyConstructionCertificateToJson(
checkedCert, validation)},
};
debugLogWriteJsonLine(certificateEntry);
}
if (!validation.ok) {
const std::string firstError =
validation.errors.empty()
? "unknown topology construction certificate error"
: validation.errors.front();
throw std::runtime_error(
"Benchmark topology construction certificate failed: " + firstError);
}
};
std::vector<std::string> documentPaths;
if (useBEIR && beirCorpus) {
documentPaths = beirCorpus->getDocumentPaths();
} else if (corpus) {
documentPaths.reserve(corpus->createdFiles.size());
for (const auto& filename : corpus->createdFiles) {
documentPaths.push_back((benchCorpusDir / filename).string());
}
}
if (documentPaths.empty()) {
throw std::runtime_error("Semantic benchmark seeding found no document paths");
}
struct DocSeedInfo {
std::string hash;
std::string benchmarkDocId;
std::string text;
std::string headerText;
std::string themeText;
std::vector<float> embedding;
std::int64_t documentId = 0;
std::int64_t nodeId = 0;
};
std::vector<DocSeedInfo> docs;
docs.reserve(documentPaths.size());
yams::crypto::SHA256Hasher contentHasher;
std::size_t warmPathRebases = 0;
auto trimCopy = [](std::string value) {
const auto first = std::find_if_not(
value.begin(), value.end(), [](unsigned char ch) { return std::isspace(ch) != 0; });
const auto last = std::find_if_not(value.rbegin(), value.rend(), [](unsigned char ch) {
return std::isspace(ch) != 0;
}).base();
if (first >= last) {
return std::string{};
}
return std::string(first, last);
};
auto deriveHeaderText = [&](std::string_view benchmarkDocId, std::string_view body,
std::string_view fallbackName) {
if (useBEIR && beirCorpus && !benchmarkDocId.empty()) {
if (auto it = beirCorpus->dataset.documents.find(std::string(benchmarkDocId));
it != beirCorpus->dataset.documents.end()) {
auto title = trimCopy(it->second.title);
if (!title.empty()) {
return title;
}
}
}
std::istringstream lines{std::string(body)};
std::string line;
while (std::getline(lines, line)) {
line = trimCopy(line);
if (line.empty()) {
continue;
}
while (!line.empty() && line.front() == '#') {
line.erase(line.begin());
}
line = trimCopy(line);
if (!line.empty()) {
return line;
}
}
return trimCopy(std::string(fallbackName));
};
std::unordered_set<std::string> topologyInputHashes;
std::size_t duplicateContentDocuments = 0;
for (const auto& docPath : documentPaths) {
auto docResult = metadataRepo->findDocumentByExactPath(docPath);
if (!docResult) {
throw std::runtime_error("Failed to resolve benchmark document " + docPath + ": " +
docResult.error().message);
}
auto resolvedDoc = std::move(docResult.value());
// Content-addressed ingest stores identical files once (warm caches rebase paths,
// and corpora such as nfcorpus contain duplicate texts), so fall back to the
// content hash to find the surviving document.
if (!resolvedDoc.has_value()) {
const auto contentHash = contentHasher.hashFile(docPath);
auto hashResult = metadataRepo->getDocumentByHash(contentHash);
if (!hashResult) {
throw std::runtime_error("Failed to resolve benchmark document " + docPath +
" by content hash: " + hashResult.error().message);
}
resolvedDoc = std::move(hashResult.value());
if (resolvedDoc.has_value() && warmDataDir) {
++warmPathRebases;
}
}
if (!resolvedDoc.has_value()) {
throw std::runtime_error("Benchmark document missing from metadata: " + docPath);
}
// A duplicate-content file resolves to a document already added as a topology input.
if (!topologyInputHashes.insert(resolvedDoc->sha256Hash).second) {
++duplicateContentDocuments;
continue;
}
const auto& doc = *resolvedDoc;
std::vector<float> embedding;
if (topologySource == "vector") {
const auto vectors = vectorDb->getVectorsByDocument(doc.sha256Hash);
if (vectors.empty()) {
continue;
}
const auto documentLevel = std::find_if(
vectors.begin(), vectors.end(), [](const yams::vector::VectorRecord& record) {
return record.level == yams::vector::EmbeddingLevel::DOCUMENT &&
!record.embedding.empty();
});
const auto chosen =
(documentLevel != vectors.end() && !documentLevel->embedding.empty())
? documentLevel
: std::find_if(vectors.begin(), vectors.end(),
[](const yams::vector::VectorRecord& record) {
return !record.embedding.empty();
});
if (chosen == vectors.end() || chosen->embedding.empty()) {
continue;
}
embedding = chosen->embedding;
}
std::string text;
std::string headerText;
std::string themeText;
if (topologySource == "fts5" || topologySource == "keyphrase" ||
topologySource == "segment_keyphrase" || topologySource == "header" ||
topologySource == "theme") {
auto contentResult = metadataRepo->getContent(doc.id);
if (contentResult && contentResult.value().has_value()) {
text = contentResult.value()->contentText;
}
if (text.empty()) {
std::ifstream in(docPath, std::ios::binary);
if (in) {
std::ostringstream buffer;
buffer << in.rdbuf();
text = buffer.str();
}
}
if (text.empty()) {
continue;
}
headerText =
deriveHeaderText(fs::path(docPath).stem().string(), text, doc.fileName);
themeText = headerText;
const std::size_t snippetSize = std::min<std::size_t>(text.size(), 512);
if (snippetSize > 0) {
if (!themeText.empty()) {
themeText.push_back('\n');
}
themeText.append(text.data(), snippetSize);
}
if (topologySource == "header" && headerText.empty()) {
continue;
}
if (topologySource == "theme" && themeText.empty()) {
continue;
}
}
auto nodeIdResult = kgStore->ensureDocumentNode(doc.sha256Hash, doc.fileName);
if (!nodeIdResult) {
throw std::runtime_error("Failed to ensure benchmark semantic KG node: " +
nodeIdResult.error().message);
}
docs.push_back({doc.sha256Hash, fs::path(docPath).stem().string(), text, headerText,
themeText, embedding, doc.id, nodeIdResult.value()});
}
if (duplicateContentDocuments > 0) {
spdlog::info("Topology benchmark skipped {} duplicate-content documents",
duplicateContentDocuments);
}
if (warmPathRebases > 0) {
spdlog::info("Resolved {} cloned warm-cache documents by stable content hash",
warmPathRebases);
}
if (docs.size() < 2) {
spdlog::warn("Skipping {} topology-source benchmark seeding: only {} usable documents",
topologySource, docs.size());
return false;
}
if ((topologySource == "kg" || topologySource == "gliner") && useBEIR && beirCorpus &&
!beirCorpus->dataset.kgEntities.empty()) {
std::unordered_map<std::string, const DocSeedInfo*> docByBenchmarkId;
docByBenchmarkId.reserve(docs.size());
for (const auto& doc : docs) {
if (!doc.benchmarkDocId.empty()) {
docByBenchmarkId.emplace(doc.benchmarkDocId, &doc);
}
}
std::vector<metadata::DocEntity> docEntities;
std::vector<metadata::KGEdge> manifestEdges;
std::size_t manifestEntitiesSeeded = 0;
for (const auto& entity : beirCorpus->dataset.kgEntities) {
const auto docIt = docByBenchmarkId.find(entity.docId);
if (docIt == docByBenchmarkId.end()) {
continue;
}
const auto* doc = docIt->second;
metadata::KGNode entityNode;
entityNode.nodeKey = "bench:entity:" + entity.entityKey;
entityNode.label = entity.label.empty() ? entity.entityKey : entity.label;
entityNode.type = entity.type.empty() ? "benchmark_entity" : entity.type;
entityNode.properties = json{
{"source", "benchmark_manifest"},
{"doc_id", entity.docId},
{"entity_key",
entity.entityKey}}.dump();
auto entityNodeResult = kgStore->upsertNode(entityNode);
if (!entityNodeResult) {
throw std::runtime_error(
"Failed to seed benchmark KG entity node for topology source: " +
entityNodeResult.error().message);
}
const float weight = std::clamp(entity.weight, 0.05F, 1.0F);
metadata::DocEntity docEntity;
docEntity.documentId = doc->documentId;
docEntity.entityText = entity.label.empty() ? entity.entityKey : entity.label;
docEntity.nodeId = entityNodeResult.value();
docEntity.confidence = weight;
docEntity.extractor =
entity.extractor.empty() ? "benchmark_manifest" : entity.extractor;
docEntities.push_back(docEntity);
metadata::KGEdge edge;
edge.srcNodeId = entityNodeResult.value();
edge.dstNodeId = doc->nodeId;
edge.relation = entity.relation.empty() || entity.relation == "semantic_neighbor"
? "manifest_entity_of"
: entity.relation;
edge.weight = weight;
edge.properties = json{{"source", "benchmark_manifest"},
{"doc_id", entity.docId},
{"entity_key", entity.entityKey}}
.dump();
manifestEdges.push_back(edge);
++manifestEntitiesSeeded;
}
if (!docEntities.empty()) {
auto addDocEntitiesResult = kgStore->addDocEntities(docEntities);
if (!addDocEntitiesResult) {
throw std::runtime_error(
"Failed to seed benchmark KG doc entities for topology source: " +
addDocEntitiesResult.error().message);
}
}
if (!manifestEdges.empty()) {
auto addEdgesResult = kgStore->addEdgesUnique(manifestEdges);
if (!addEdgesResult) {
throw std::runtime_error(
"Failed to seed benchmark KG relation edges for topology source: " +
addEdgesResult.error().message);
}
}
spdlog::info("Seeded {} manifest KG entity annotations and {} relation edges for "
"topology-source graph features",
manifestEntitiesSeeded, manifestEdges.size());
}
std::unordered_map<std::string, std::unordered_map<std::string, float>> neighborScores;
neighborScores.reserve(docs.size());
if (topologySource == "vector") {
yams::vector::VectorSearchParams params;
params.k = semanticTopK + 4;
params.similarity_threshold = semanticThreshold;
for (const auto& doc : docs) {
const auto neighbors = vectorDb->searchSimilar(doc.embedding, params);
auto& scores = neighborScores[doc.hash];
for (const auto& rec : neighbors) {
if (rec.level != yams::vector::EmbeddingLevel::DOCUMENT) {
continue;
}
if (rec.document_hash.empty() || rec.document_hash == doc.hash) {
continue;
}
if (rec.relevance_score < semanticThreshold) {
continue;
}
auto [it, inserted] = scores.emplace(rec.document_hash, rec.relevance_score);
if (!inserted) {
it->second = std::max(it->second, rec.relevance_score);
}
if (scores.size() >= semanticTopK) {
break;
}
}
}
} else if (topologySource == "keyphrase" || topologySource == "segment_keyphrase") {
std::vector<yams::bench::KeyphraseTopologyDocument> keyphraseDocs;
keyphraseDocs.reserve(docs.size());
for (const auto& doc : docs) {
keyphraseDocs.push_back({.documentHash = doc.hash, .text = doc.text});
}
yams::bench::BenchmarkTopologyConstructionCertificate certificate;
std::vector<yams::bench::LexicalTopologyNeighbor> keyphraseNeighbors;
if (topologySource == "segment_keyphrase") {
keyphraseNeighbors = yams::bench::buildSegmentKeyphraseTopologyNeighbors(
keyphraseDocs, semanticTopK, semanticThreshold, segmentTokenBudget,
segmentTokenOverlap, segmentFeatureLimit, segmentMaxDocumentFrequency,
topologyMaxComponentDocs, segmentMaxEdgesPerFeature, true, &certificate);
} else {
keyphraseNeighbors = yams::bench::buildKeyphraseTopologyNeighbors(
keyphraseDocs, semanticTopK, semanticThreshold, keyphraseFeatureLimit,
keyphraseMaxDocumentFrequency, true, &certificate);
}
validateAndLogTopologyCertificate(certificate);
for (const auto& neighbor : keyphraseNeighbors) {
neighborScores[neighbor.sourceHash][neighbor.targetHash] = neighbor.score;
}
} else if (topologySource == "fts5" || topologySource == "header" ||
topologySource == "theme") {
std::vector<yams::bench::LexicalTopologyDocument> lexicalDocs;
lexicalDocs.reserve(docs.size());
for (const auto& doc : docs) {
std::string_view sourceText = doc.text;
if (topologySource == "header") {
sourceText = doc.headerText;
} else if (topologySource == "theme") {
sourceText = doc.themeText;
}
lexicalDocs.push_back({.documentHash = doc.hash, .text = std::string(sourceText)});
}
yams::bench::BenchmarkTopologyConstructionCertificate certificate;
const auto lexicalNeighbors = yams::bench::buildLexicalTopologyNeighbors(
lexicalDocs, semanticTopK, semanticThreshold, true, &certificate);
validateAndLogTopologyCertificate(certificate);
for (const auto& neighbor : lexicalNeighbors) {
neighborScores[neighbor.sourceHash][neighbor.targetHash] = neighbor.score;
}
} else {
const bool glinerOnly = topologySource == "gliner";
std::vector<yams::bench::GraphTopologyDocument> graphDocs;
graphDocs.reserve(docs.size());
std::size_t docsWithGraphFeatures = 0;
std::size_t docEntitiesScanned = 0;
std::size_t graphEdgesScanned = 0;
for (const auto& doc : docs) {
std::unordered_map<std::int64_t, float> featureByNode;
auto entityResult =
kgStore->getDocEntitiesForDocument(doc.documentId, kgFeatureLimit, 0);
if (!entityResult) {
throw std::runtime_error(
"Failed to read KG doc entities for topology source: " +
entityResult.error().message);
}
docEntitiesScanned += entityResult.value().size();
for (const auto& entity : entityResult.value()) {
if (glinerOnly) {
const auto extractor = entity.extractor.value_or(std::string{});
if (extractor.find("gliner") == std::string::npos &&
extractor.find("title_nl") == std::string::npos) {
continue;
}
}
if (!entity.nodeId.has_value() || entity.nodeId.value() <= 0 ||
entity.nodeId.value() == doc.nodeId) {
continue;
}
const float weight = std::clamp(entity.confidence.value_or(1.0F), 0.05F, 1.0F);
auto [it, inserted] = featureByNode.emplace(entity.nodeId.value(), weight);
if (!inserted) {
it->second = std::max(it->second, weight);
}
}
auto edgeResult =
kgStore->getEdgesBidirectional(doc.nodeId, std::nullopt, kgFeatureLimit);
if (!edgeResult) {
throw std::runtime_error("Failed to read KG edges for topology source: " +
edgeResult.error().message);
}
graphEdgesScanned += edgeResult.value().size();
for (const auto& edge : edgeResult.value()) {
if (glinerOnly && edge.relation != "title_mentions" &&
edge.relation != "primary_topic_of" && edge.relation != "mentioned_in") {
continue;
}
if (edge.relation == "semantic_neighbor") {
continue;
}
const std::int64_t otherNodeId =
edge.srcNodeId == doc.nodeId ? edge.dstNodeId : edge.srcNodeId;
if (otherNodeId <= 0 || otherNodeId == doc.nodeId) {
continue;
}
const float weight = std::clamp(edge.weight, 0.05F, 1.0F);
auto [it, inserted] = featureByNode.emplace(otherNodeId, weight);
if (!inserted) {
it->second = std::max(it->second, weight);
}
}
yams::bench::GraphTopologyDocument graphDoc;
graphDoc.documentHash = doc.hash;
graphDoc.relatedNodes.reserve(featureByNode.size());
for (const auto& [nodeId, weight] : featureByNode) {
graphDoc.relatedNodes.push_back({.nodeId = nodeId, .weight = weight});
}
if (!graphDoc.relatedNodes.empty()) {
++docsWithGraphFeatures;
}
graphDocs.push_back(graphDoc);
}
spdlog::info("{} topology-source scanned {} doc-entity rows and {} doc-adjacent KG "
"edges; {} / {} documents had graph features",
topologySource, docEntitiesScanned, graphEdgesScanned,
docsWithGraphFeatures, graphDocs.size());
yams::bench::BenchmarkTopologyConstructionCertificate certificate;
const auto graphNeighbors = yams::bench::buildGraphTopologyNeighbors(
graphDocs, semanticTopK, semanticThreshold, true, &certificate);
validateAndLogTopologyCertificate(certificate);
for (const auto& neighbor : graphNeighbors) {
neighborScores[neighbor.sourceHash][neighbor.targetHash] = neighbor.score;
}
}
std::unordered_map<std::string, std::int64_t> nodeIdByHash;
nodeIdByHash.reserve(docs.size());
std::unordered_map<std::string, std::string> hashByBenchmarkDocId;
hashByBenchmarkDocId.reserve(docs.size());
for (const auto& doc : docs) {
nodeIdByHash.emplace(doc.hash, doc.nodeId);
if (!doc.benchmarkDocId.empty()) {
hashByBenchmarkDocId.emplace(doc.benchmarkDocId, doc.hash);
}
}
std::vector<metadata::KGEdge> edges;
std::size_t mutualEdges = 0;
std::size_t oneWayEdges = 0;
for (const auto& seed : yams::bench::buildSemanticNeighborSeedEdges(
neighborScores, semanticWeightFloor, seedingMode)) {
const auto srcNodeIt = nodeIdByHash.find(seed.sourceHash);
const auto dstNodeIt = nodeIdByHash.find(seed.targetHash);
if (srcNodeIt == nodeIdByHash.end() || dstNodeIt == nodeIdByHash.end()) {
continue;
}
metadata::KGEdge edge;
edge.srcNodeId = srcNodeIt->second;
edge.dstNodeId = dstNodeIt->second;
edge.relation = "semantic_neighbor";
edge.weight = seed.weight;
edges.push_back(edge);
(seed.mutual ? mutualEdges : oneWayEdges) += 1;
}
const std::size_t reciprocalPairs = mutualEdges / 2;
std::size_t qrelPairs = 0;
if (seedQrelCommunities) {
std::unordered_map<std::string, std::unordered_map<std::string, int>> overlapCounts;
for (auto it = beirCorpus->dataset.qrels.begin();
it != beirCorpus->dataset.qrels.end();) {
const auto queryId = it->first;
std::vector<std::string> docsForQuery;
for (; it != beirCorpus->dataset.qrels.end() && it->first == queryId; ++it) {
if (it->second.score <= 0 || !hashByBenchmarkDocId.contains(it->second.docId)) {
continue;
}
docsForQuery.push_back(it->second.docId);
}
std::sort(docsForQuery.begin(), docsForQuery.end());
docsForQuery.erase(std::unique(docsForQuery.begin(), docsForQuery.end()),
docsForQuery.end());
for (std::size_t i = 0; i < docsForQuery.size(); ++i) {
for (std::size_t j = i + 1; j < docsForQuery.size(); ++j) {
overlapCounts[docsForQuery[i]][docsForQuery[j]] += 1;
overlapCounts[docsForQuery[j]][docsForQuery[i]] += 1;
}
}
}
std::set<std::pair<std::string, std::string>> addedPairs;
for (const auto& [docId, neighbors] : overlapCounts) {
const auto srcHashIt = hashByBenchmarkDocId.find(docId);
if (srcHashIt == hashByBenchmarkDocId.end()) {
continue;
}
const auto srcNodeIt = nodeIdByHash.find(srcHashIt->second);
if (srcNodeIt == nodeIdByHash.end()) {
continue;
}
std::vector<std::pair<std::string, int>> ranked(neighbors.begin(), neighbors.end());
std::sort(ranked.begin(), ranked.end(), [](const auto& left, const auto& right) {
if (left.second != right.second) {
return left.second > right.second;
}
return left.first < right.first;
});
if (ranked.size() > qrelTopK) {
ranked.resize(qrelTopK);
}
for (const auto& [neighborDocId, sharedCount] : ranked) {
const auto dstHashIt = hashByBenchmarkDocId.find(neighborDocId);
if (dstHashIt == hashByBenchmarkDocId.end()) {
continue;
}
const auto dstNodeIt = nodeIdByHash.find(dstHashIt->second);
if (dstNodeIt == nodeIdByHash.end() || srcNodeIt->second == dstNodeIt->second) {
continue;
}
const auto ordered = std::minmax(srcHashIt->second, dstHashIt->second);
if (!addedPairs.insert(ordered).second) {
continue;
}
const float weight = std::clamp(
std::max(semanticWeightFloor, 0.25f * static_cast<float>(sharedCount)),
0.0f, 1.0f);
metadata::KGEdge forward;
forward.srcNodeId = srcNodeIt->second;
forward.dstNodeId = dstNodeIt->second;
forward.relation = "semantic_neighbor";
forward.weight = weight;
edges.push_back(forward);
metadata::KGEdge reverse;
reverse.srcNodeId = dstNodeIt->second;
reverse.dstNodeId = srcNodeIt->second;
reverse.relation = "semantic_neighbor";
reverse.weight = weight;
edges.push_back(reverse);
qrelPairs += 1;
}
}
}
if (edges.empty()) {
spdlog::warn("{} topology-source benchmark seeding produced no reciprocal neighbors "
"(docs={}, topk={}, threshold={:.3f})",
topologySource, docs.size(), semanticTopK, semanticThreshold);
return false;
}
auto deleteExistingResult = kgStore->deleteEdgesByRelation("semantic_neighbor");
if (!deleteExistingResult) {
throw std::runtime_error("Failed to clear pre-existing benchmark semantic KG edges: " +
deleteExistingResult.error().message);
}
auto addEdgesResult = kgStore->addEdgesUnique(edges);
if (!addEdgesResult) {
throw std::runtime_error("Failed to seed benchmark semantic KG edges: " +
addEdgesResult.error().message);
}
spdlog::info("Seeded {} {} topology-source neighbor pairs, {} one-way kNN edges and {} "
"qrel-neighbor pairs ({} directed edges) for benchmark corpus after clearing "
"{} prior semantic edges",
reciprocalPairs, topologySource, oneWayEdges, qrelPairs, edges.size(),
deleteExistingResult.value());
return true;
}
void setup() {
using SetupClock = std::chrono::steady_clock;
const auto setupStartedAt = SetupClock::now();
std::optional<SetupClock::time_point> coldIngestStartedAt;
std::optional<SetupClock::time_point> admissionCompletedAt;
const auto elapsedMsSince = [](SetupClock::time_point startedAt) {
return std::chrono::duration_cast<std::chrono::milliseconds>(SetupClock::now() -
startedAt)
.count();
};
const char* env_dataset = std::getenv("YAMS_BENCH_DATASET");
const char* env_debug_file = std::getenv("YAMS_BENCH_DEBUG_FILE");
if (env_debug_file && std::strlen(env_debug_file) > 0) {
g_debugFile =
std::make_unique<std::ofstream>(env_debug_file, std::ios::out | std::ios::app);
if (g_debugFile->is_open()) {
g_debugOut = g_debugFile.get();
spdlog::info("Benchmark debug logging enabled: {}", env_debug_file);
DebugLogEntry startEntry;
startEntry.searchType = "benchmark_setup";
startEntry.extraFields = {
{"event", "setup"},
{"debug_file", env_debug_file},
{"topology_source", std::getenv("YAMS_BENCH_TOPOLOGY_SOURCE")
? std::getenv("YAMS_BENCH_TOPOLOGY_SOURCE")
: "vector"},
{"seed_semantic_neighbors",
std::getenv("YAMS_BENCH_SEED_SEMANTIC_NEIGHBORS")
? std::getenv("YAMS_BENCH_SEED_SEMANTIC_NEIGHBORS")
: "0"},
// Requested arm values; per-query search_stats carry the effective
// topology_representative_rule / topology_route_bq_rotation.
{"topology_representative_rule",
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_REPRESENTATIVE_RULE")
.value_or("degree")},
{"topology_bq_rotation",
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_BQ_ROTATION")
.value_or("none")},
};
debugLogWriteJsonLine(startEntry);
} else {
spdlog::warn("Failed to open YAMS_BENCH_DEBUG_FILE={} for writing", env_debug_file);
g_debugFile.reset();
g_debugOut = nullptr;
}
}
const char* env_path = std::getenv("YAMS_BENCH_DATASET_PATH");
const char* env_size = std::getenv("YAMS_BENCH_CORPUS_SIZE");
const char* env_queries = std::getenv("YAMS_BENCH_NUM_QUERIES");
const char* env_topk = std::getenv("YAMS_BENCH_TOPK");
if (env_dataset) {
std::string requestedDatasetName = env_dataset;
// Supported BEIR datasets from
// https://public.ukp.informatik.tu-darmstadt.de/thakur/BEIR/datasets/
static const std::set<std::string> SUPPORTED_BEIR_DATASETS = {
"scifact", "nfcorpus", "arguana", "scidocs", "fiqa",
"quora", "cqadupstack", "hotpotqa", "fever", "climate-fever",
"dbpedia-entity", "trec-covid", "touche-2020", "longmemeval_s", "local-manifest"};
if (SUPPORTED_BEIR_DATASETS.count(requestedDatasetName) > 0) {
useBEIR = true;
beirDatasetName = requestedDatasetName;
datasetName = requestedDatasetName;
// Default to SearchTuner auto mode so benchmark reflects dynamic tuning behavior.
// Keep explicit override support for controlled experiments:
// YAMS_BENCH_FORCE_TUNING_OVERRIDE=SCIENTIFIC
const char* forcedOverride = std::getenv("YAMS_BENCH_FORCE_TUNING_OVERRIDE");
const char* existingOverride = std::getenv("YAMS_TUNING_OVERRIDE");
if (forcedOverride && std::strlen(forcedOverride) > 0) {
spdlog::info(
"Will set YAMS_TUNING_OVERRIDE={} via YAMS_BENCH_FORCE_TUNING_OVERRIDE "
"for dataset-backed benchmark ({})",
forcedOverride, requestedDatasetName);
} else if (existingOverride && std::strlen(existingOverride) > 0) {
spdlog::info(
"Using pre-set YAMS_TUNING_OVERRIDE={} for dataset-backed benchmark ({})",
existingOverride, requestedDatasetName);
} else {
spdlog::info("Using SearchTuner auto mode for dataset-backed benchmark ({})",
requestedDatasetName);
}
} else if (requestedDatasetName == "synthetic-community") {
datasetName = requestedDatasetName;
syntheticCorpusMode = SyntheticCorpusMode::CommunityGraph;
} else if (requestedDatasetName == "synthetic-hard") {
datasetName = requestedDatasetName;
syntheticCorpusMode = SyntheticCorpusMode::HardLexical;
} else {
spdlog::warn("Unknown dataset '{}', using synthetic. Supported: scifact, "
"cqadupstack, nfcorpus, synthetic-hard, local-manifest, etc.",
requestedDatasetName);
}
}
if (env_size)
corpusSize = std::stoi(env_size);
if (env_queries)
numQueries = std::stoi(env_queries);
if (env_topk)
topK = std::stoi(env_topk);
std::vector<EnvSetting> benchmarkEnvOverrides;
benchmarkEnvOverrides.reserve(24);
auto addEnvDefault = [&](const char* key, const char* value) {
const char* current = std::getenv(key);
if (!(current && std::strlen(current) > 0)) {
benchmarkEnvOverrides.push_back({key, std::string(value)});
}
};
if (useSyntheticCommunityCorpus()) {
corpusSize = 3;
if (!(env_queries && *env_queries)) {
numQueries = 1;
}
if (!(env_topk && *env_topk)) {
topK = 1;
}
// Force the graph rerank stage fully on for the synthetic community A/B.
// Tiny synthetic corpora are still classified as no_kg by the tuner, so enabling the
// stage alone is not enough to produce an actual rerank window.
addEnvDefault("YAMS_ENABLE_ENV_OVERRIDES", "1");
addEnvDefault("YAMS_SEARCH_KG_WEIGHT", "0.00");
addEnvDefault("YAMS_SEARCH_GRAPH_RERANK_TOPN", "3");
addEnvDefault("YAMS_SEARCH_GRAPH_RERANK_WEIGHT", "1.00");
addEnvDefault("YAMS_SEARCH_GRAPH_RERANK_MAX_BOOST", "1.00");
addEnvDefault("YAMS_SEARCH_GRAPH_RERANK_MIN_SIGNAL", "0.01");
addEnvDefault("YAMS_SEARCH_KG_MAX_RESULTS", "3");
addEnvDefault("YAMS_SEARCH_GRAPH_SCORING_BUDGET_MS", "8");
}
auto optionalPositiveInt = [](const char* raw) -> std::optional<int> {
if (!(raw && *raw)) {
return std::nullopt;
}
const int parsed = std::stoi(raw);
if (parsed <= 0) {
return std::nullopt;
}
return parsed;
};
const std::optional<int> corpusLimit = optionalPositiveInt(env_size);
const std::optional<int> queryLimit = optionalPositiveInt(env_queries);
spdlog::info("Setting up RAG benchmark: {} dataset, {} docs, {} queries, k={}",
datasetDisplayName(), corpusSize, numQueries, topK);
// PBI-05b: Create summary log file for important embedding metrics
fs::path summaryLogPath = fs::temp_directory_path() / "yams_bench_summary.log";
std::ofstream summaryLog(summaryLogPath, std::ios::trunc);
if (summaryLog) {
auto now = std::chrono::system_clock::now();
auto time_t_now = std::chrono::system_clock::to_time_t(now);
summaryLog << "=== YAMS Retrieval Quality Benchmark ===" << std::endl;
summaryLog << "Started: " << std::ctime(&time_t_now);
summaryLog << "Dataset: " << datasetDisplayName() << std::endl;
summaryLog << "Corpus size: " << corpusSize << std::endl;
summaryLog << "Num queries: " << numQueries << std::endl;
summaryLog << "Top K: " << topK << std::endl;
summaryLog << std::endl;
summaryLog.flush();
}
spdlog::info("Summary log: {}", summaryLogPath.string());
const char* disableVectors = std::getenv("YAMS_DISABLE_VECTORS");
bool vectorsDisabled = disableVectors && std::string(disableVectors) == "1";
const bool forceMockEmbeddings = forceMockEmbeddingsForBench();
auto envFlagEnabled = [](const char* value) -> bool {
if (!value) {
return false;
}
std::string normalized(value);
std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
return normalized == "1" || normalized == "true" || normalized == "yes" ||
normalized == "on";
};
if (useBEIR) {
const char* forcedOverride = std::getenv("YAMS_BENCH_FORCE_TUNING_OVERRIDE");
if (forcedOverride && std::strlen(forcedOverride) > 0) {
benchmarkEnvOverrides.push_back({"YAMS_ENABLE_ENV_OVERRIDES", std::string("1")});
benchmarkEnvOverrides.push_back(
{"YAMS_TUNING_OVERRIDE", std::string(forcedOverride)});
}
}
// Benchmark determinism defaults (can still be overridden by explicit env values).
// Real-ingest graph validation needs the post-ingest rebuild so search tuning sees the
// final corpus stats instead of the startup-empty corpus.
addEnvDefault("YAMS_DISABLE_SEARCH_REBUILDS", "0");
addEnvDefault("YAMS_SEARCH_WAIT_FOR_CONCEPTS", "1");
addEnvDefault("YAMS_HNSW_RANDOM_SEED", "42");
addEnvDefault("YAMS_HNSW_PARALLEL_BUILD_THRESHOLD", "0");
// Use higher concurrency for embedding/extraction to reduce ingestion time.
// HNSW seed + wait-for-concepts provide sufficient determinism for ranking;
// single-threaded embedding only added wall-clock cost without improving reproducibility.
addEnvDefault("YAMS_POST_EMBED_CONCURRENT", "4");
addEnvDefault("YAMS_POST_EXTRACTION_CONCURRENT", "4");
addEnvDefault("YAMS_POST_KG_CONCURRENT", "1");
addEnvDefault("YAMS_POST_TITLE_CONCURRENT", "1");
// Retrieval-quality benchmark default: use Simeon's built-in embedding backend. This avoids
// loading ONNX Runtime on the common benchmark path; operators can still request the legacy
// ONNX/ORT path with YAMS_BENCH_EMBED_BACKEND=onnx or YAMS_EMBED_BACKEND=onnx.
if (const char* benchEmbedBackend = std::getenv("YAMS_BENCH_EMBED_BACKEND");
benchEmbedBackend && *benchEmbedBackend) {
benchmarkEnvOverrides.push_back({"YAMS_EMBED_BACKEND", std::string(benchEmbedBackend)});
} else {
addEnvDefault("YAMS_EMBED_BACKEND", "simeon");
}
// Adaptive sub-batch tuning: the default 15s warning threshold causes premature
// batch-cap collapse (8→4→1) on machines where ONNX inference is legitimately slow.
// Raise to 60s so the adaptive logic only shrinks on true stalls, not normal latency.
addEnvDefault("YAMS_EMBED_SUBBATCH_WARN_MS", "60000");
// Benchmark default: graph rerank is ON unless explicitly overridden.
// Canonical env key used by SearchEngineBuilder is YAMS_SEARCH_ENABLE_GRAPH_RERANK.
const char* graphRerankCanonical = std::getenv("YAMS_SEARCH_ENABLE_GRAPH_RERANK");
if (!(graphRerankCanonical && *graphRerankCanonical)) {
benchmarkEnvOverrides.push_back({"YAMS_SEARCH_ENABLE_GRAPH_RERANK", std::string("1")});
graphRerankCanonical = "1";
}
if (env_debug_file && std::strlen(env_debug_file) > 0) {
addEnvDefault("YAMS_SEARCH_STAGE_TRACE", "1");
const int traceTopNDefault =
parseIntEnvOrDefault("YAMS_BENCH_TRACE_TOP_N", 50, 1, 10000);
addEnvDefault("YAMS_SEARCH_STAGE_TRACE_TOP_N",
std::to_string(traceTopNDefault).c_str());
const int traceComponentDefault = parseIntEnvOrDefault(
"YAMS_BENCH_TRACE_COMPONENT_TOP_N", std::min(traceTopNDefault, 50), 1, 10000);
addEnvDefault("YAMS_SEARCH_STAGE_TRACE_COMPONENT_TOP_N",
std::to_string(traceComponentDefault).c_str());
}
const bool graphRerankRequested = envFlagEnabled(graphRerankCanonical);
std::optional<bool> explicitRequireKgReady;
if (const char* envRequireKgReady = std::getenv("YAMS_BENCH_REQUIRE_KG_READY");
envRequireKgReady && *envRequireKgReady) {
explicitRequireKgReady = envFlagEnabled(envRequireKgReady);
}
const auto kgReadinessPolicy = yams::search::resolveBenchmarkKgReadinessPolicy(
graphRerankRequested, explicitRequireKgReady, useBEIR);
const bool requireKgReady =
kgReadinessPolicy != yams::search::BenchmarkKgReadinessPolicy::Skip;
scopedEnv = std::make_unique<ScopedEnvOverrides>(benchmarkEnvOverrides);
spdlog::info("[Bench] KG readiness policy: {} (graph_rerank_requested={}, beir={}, "
"explicit_override={})",
yams::search::benchmarkKgReadinessPolicyToString(kgReadinessPolicy),
graphRerankRequested ? "true" : "false", useBEIR ? "true" : "false",
explicitRequireKgReady.has_value()
? (*explicitRequireKgReady ? "true" : "false")
: "none");
fs::path datasetPathForMetadata = env_path ? fs::path(env_path) : fs::path();
Result<BEIRDataset> preparedBeirDataset =
Error{ErrorCode::Unknown, "BEIR dataset not requested"};
if (useBEIR) {
preparedBeirDataset = selectBenchmarkBEIRDataset(
beirDatasetName, datasetPathForMetadata, corpusLimit, queryLimit);
if (!preparedBeirDataset) {
throw std::runtime_error("Failed to load BEIR dataset: " +
preparedBeirDataset.error().message);
}
corpusSize = static_cast<int>(preparedBeirDataset.value().documents.size());
numQueries = static_cast<int>(preparedBeirDataset.value().queries.size());
}
BenchCacheMetadata expectedCacheMetadata;
expectedCacheMetadata.dataset = useBEIR ? beirDatasetName : datasetName;
expectedCacheMetadata.datasetPath = canonicalPathOrEmpty(datasetPathForMetadata);
expectedCacheMetadata.corpusSize = corpusSize;
expectedCacheMetadata.numQueries = numQueries;
expectedCacheMetadata.topK = topK;
expectedCacheMetadata.useBEIR = useBEIR;
expectedCacheMetadata.vectorsDisabled = vectorsDisabled;
expectedCacheMetadata.requireKgReady = requireKgReady;
expectedCacheMetadata.graphRerankRequested = graphRerankRequested;
expectedCacheMetadata.searchEngine =
std::string(yams::vector::vectorSearchEngineName(benchmarkVectorSearchEngine()));
expectedCacheMetadata.expectedDocs = corpusSize;
expectedCacheMetadata.expectedQueries = numQueries;
spdlog::info("[Bench] Effective graph rerank env: YAMS_SEARCH_ENABLE_GRAPH_RERANK={}",
graphRerankCanonical ? graphRerankCanonical : "<unset>");
spdlog::info(
"[Bench] Determinism envs: YAMS_DISABLE_SEARCH_REBUILDS={} "
"YAMS_SEARCH_WAIT_FOR_CONCEPTS={} YAMS_HNSW_RANDOM_SEED={} "
"YAMS_HNSW_PARALLEL_BUILD_THRESHOLD={} YAMS_POST_EMBED_CONCURRENT={} "
"YAMS_POST_EXTRACTION_CONCURRENT={} YAMS_POST_KG_CONCURRENT={}",
std::getenv("YAMS_DISABLE_SEARCH_REBUILDS")
? std::getenv("YAMS_DISABLE_SEARCH_REBUILDS")
: "<unset>",
std::getenv("YAMS_SEARCH_WAIT_FOR_CONCEPTS")
? std::getenv("YAMS_SEARCH_WAIT_FOR_CONCEPTS")
: "<unset>",
std::getenv("YAMS_HNSW_RANDOM_SEED") ? std::getenv("YAMS_HNSW_RANDOM_SEED") : "<unset>",
std::getenv("YAMS_HNSW_PARALLEL_BUILD_THRESHOLD")
? std::getenv("YAMS_HNSW_PARALLEL_BUILD_THRESHOLD")
: "<unset>",
std::getenv("YAMS_POST_EMBED_CONCURRENT") ? std::getenv("YAMS_POST_EMBED_CONCURRENT")
: "<unset>",
std::getenv("YAMS_POST_EXTRACTION_CONCURRENT")
? std::getenv("YAMS_POST_EXTRACTION_CONCURRENT")
: "<unset>",
std::getenv("YAMS_POST_KG_CONCURRENT") ? std::getenv("YAMS_POST_KG_CONCURRENT")
: "<unset>");
const bool embedBackendSimeon = [] {
const char* v = std::getenv("YAMS_EMBED_BACKEND");
return v && std::string{v} == "simeon";
}();
DaemonHarness::Options harnessOptions;
harnessOptions.isolateState = true;
harnessOptions.useMockModelProvider = vectorsDisabled || forceMockEmbeddings;
harnessOptions.autoLoadPlugins =
!vectorsDisabled && !forceMockEmbeddings && !embedBackendSimeon;
harnessOptions.configureModelPool = !vectorsDisabled && !forceMockEmbeddings;
harnessOptions.modelPoolLazyLoading = false;
harnessOptions.pluginDirStrict =
!vectorsDisabled && !forceMockEmbeddings && !embedBackendSimeon;
if (embedBackendSimeon) {
harnessOptions.enableModelProvider = true;
harnessOptions.useMockModelProvider = false;
}
// YAMS_BENCH_DATA_DIR: reuse a pre-ingested data directory to skip
// corpus ingestion + embedding wait on repeated benchmark runs.
// YAMS_BENCH_WARM_CACHE_DIR: like YAMS_BENCH_DATA_DIR, but validates a metadata file and
// auto-writes one after a cold run so repeated full-corpus benchmarks are practical.
const char* envWarmCacheDir = std::getenv("YAMS_BENCH_WARM_CACHE_DIR");
const char* envDataDir = std::getenv("YAMS_BENCH_DATA_DIR");
if (envWarmCacheDir && *envWarmCacheDir) {
fs::path cacheDir = fs::path(envWarmCacheDir);
warmDataDirPath = cacheDir;
if (fs::exists(cacheDir)) {
auto metadataResult = readBenchCacheMetadata(cacheDir);
if (metadataResult) {
const auto& cachedMetadata = metadataResult.value();
std::string mismatchReason;
if (benchCacheMatches(expectedCacheMetadata, cachedMetadata, &mismatchReason)) {
harnessOptions.dataDir = cacheDir;
warmDataDir = true;
warmCacheMetadata = cachedMetadata;
spdlog::info(
"[Bench] Reusing warm cache dir: {} (status={}, embedded_docs={}, "
"vectors={})",
cacheDir.string(), cachedMetadata.status, cachedMetadata.embeddedDocs,
cachedMetadata.vectorCount);
} else {
std::error_code removeEc;
const bool canResumePartial = cachedMetadata.status == "partial" &&
mismatchReason == "cache status not reusable";
if (!canResumePartial) {
fs::remove_all(cacheDir, removeEc);
if (removeEc) {
spdlog::warn("[Bench] Failed to clear stale warm cache dir {}: {}",
cacheDir.string(), removeEc.message());
}
}
harnessOptions.dataDir = cacheDir;
if (canResumePartial) {
warmDataDir = true;
warmCacheMetadata = metadataResult.value();
spdlog::warn(
"[Bench] Warm cache dir {} is partially primed; reusing it "
"to resume embedding completion",
cacheDir.string());
} else {
spdlog::warn(
"[Bench] Warm cache dir {} metadata mismatch ({}); running "
"cold and refreshing cache",
cacheDir.string(), mismatchReason);
}
}
} else {
harnessOptions.dataDir = cacheDir;
spdlog::warn("[Bench] Warm cache dir {} missing/invalid metadata ({}); "
"running cold and refreshing cache",
cacheDir.string(), metadataResult.error().message);
}
} else {
harnessOptions.dataDir = cacheDir;
spdlog::info("[Bench] Warm cache dir {} does not exist yet; cold run will create "
"it",
cacheDir.string());
}
} else if (envDataDir && *envDataDir) {
fs::path dataDirPath(envDataDir);
if (fs::exists(dataDirPath)) {
harnessOptions.dataDir = dataDirPath;
warmDataDir = true;
warmDataDirPath = dataDirPath;
spdlog::info("[Bench] Reusing warm data dir: {} (skip ingestion + embed wait)",
dataDirPath.string());
} else {
spdlog::warn("[Bench] YAMS_BENCH_DATA_DIR={} does not exist, ignoring", envDataDir);
}
}
if (!vectorsDisabled && !forceMockEmbeddings) {
bool resetPluginTrustFile = harnessOptions.dataDir.has_value();
bool useIsolatedBenchmarkConfig = true;
const char* topologyModeEnv = std::getenv("YAMS_BENCH_TOPOLOGY_MODE");
const bool topologyDisabled =
topologyModeEnv && std::string_view{topologyModeEnv} == "disabled";
const char* topologyEngineEnv = std::getenv("YAMS_BENCH_TOPOLOGY_ENGINE");
const char* topologyRepresentativesEnv =
std::getenv("YAMS_BENCH_TOPOLOGY_ROUTING_REPRESENTATIVES");
const char* topologyBoundarySpillEnv =
std::getenv("YAMS_BENCH_TOPOLOGY_BOUNDARY_SPILL");
const char* topologyRepresentativeLimitEnv =
std::getenv("YAMS_BENCH_TOPOLOGY_ROUTE_REPRESENTATIVE_LIMIT");
const char* topologyAnnCandidateLimitEnv =
std::getenv("YAMS_BENCH_TOPOLOGY_ROUTE_ANN_CANDIDATE_LIMIT");
const auto envSet = [](const std::optional<std::string>& value) {
return value.has_value() && !value->empty();
};
const auto topologyRoadHopsEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_ROAD_HOPS");
const auto topologyRoadBeamWidthEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_ROAD_BEAM_WIDTH");
const auto topologyRoadWalkWindowEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_ROAD_WALK_WINDOW");
const auto topologyRoadMaxPerClusterEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_ROAD_MAX_PER_CLUSTER");
const auto topologyRoadOrderEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_ROAD_ORDER");
const auto topologyBqCandidateLimitEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_ROUTE_BQ_CANDIDATE_LIMIT");
const auto topologyBqPrefixDimEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_ROUTE_BQ_PREFIX_DIM");
const auto topologyBqRotationEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_BQ_ROTATION");
const auto topologyRepresentativeRuleEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_REPRESENTATIVE_RULE");
const auto topologySgcHopsEnv =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_SGC_HOPS");
const auto simeonOuterMaxSimEnv =
yams::config::getenv_optional("YAMS_BENCH_SIMEON_OUTER_MAXSIM");
const auto graphCommunitySourceEnv =
yams::config::getenv_optional("YAMS_BENCH_GRAPH_COMMUNITY_SOURCE");
const char* topologyExpansionOutputLimitEnv =
std::getenv("YAMS_BENCH_TOPOLOGY_EXPANSION_OUTPUT_LIMIT");
const char* topologyGraphWeightedSeedRankingEnv =
std::getenv("YAMS_BENCH_TOPOLOGY_GRAPH_WEIGHTED_SEED_RANKING");
const auto hasBenchmarkSetting = [](const char* name) {
const char* value = std::getenv(name);
return value && *value;
};
const bool writeTopologyAdmissionConfig =
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_FINGERPRINT") ||
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_QUERIES") ||
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_PROTECTED_CANDIDATES") ||
hasBenchmarkSetting(
"YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_MISSED_PROTECTED_CANDIDATES") ||
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_MIN_CALIBRATION_QUERIES") ||
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_MAX_MISSES_PER_THOUSAND") ||
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_MIN_BOUNDARY_MARGIN") ||
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_MIN_SEED_HITS") ||
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_WORK_MAX_ROWS_VISITED") ||
hasBenchmarkSetting(
"YAMS_BENCH_TOPOLOGY_ROUTE_WORK_MAX_EXACT_DISTANCE_EVALUATIONS") ||
hasBenchmarkSetting("YAMS_BENCH_TOPOLOGY_ROUTE_WORK_MAX_ANN_CANDIDATES");
const bool writeTopologyEngineConfig =
(topologyEngineEnv && *topologyEngineEnv) ||
(topologyRepresentativesEnv && *topologyRepresentativesEnv) ||
(topologyBoundarySpillEnv && *topologyBoundarySpillEnv) ||
envSet(topologySgcHopsEnv) || envSet(topologyRepresentativeRuleEnv) ||
envSet(topologyRoadMaxPerClusterEnv) || envSet(topologyRoadOrderEnv);
const bool writeTopologyRouteConfig =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_VECTOR_POLICY").has_value() ||
(std::getenv("YAMS_BENCH_TOPOLOGY_ROUTE_SCORING") != nullptr) ||
(std::getenv("YAMS_BENCH_TOPOLOGY_SPARSE_DENSE_ALPHA") != nullptr) ||
(std::getenv("YAMS_BENCH_TOPOLOGY_MIN_ROUTE_SCORE") != nullptr) ||
(std::getenv("YAMS_BENCH_TOPOLOGY_MEDOID_ONLY_EXPANSION") != nullptr) ||
(std::getenv("YAMS_BENCH_TOPOLOGY_MIN_CLUSTERS") != nullptr) ||
(std::getenv("YAMS_BENCH_TOPOLOGY_MAX_SEED_DOCUMENTS") != nullptr) ||
envSet(topologyRoadHopsEnv) || envSet(topologyRoadBeamWidthEnv) ||
envSet(topologyRoadWalkWindowEnv) ||
(topologyRepresentativeLimitEnv && *topologyRepresentativeLimitEnv) ||
(topologyAnnCandidateLimitEnv && *topologyAnnCandidateLimitEnv) ||
envSet(topologyBqCandidateLimitEnv) || envSet(topologyBqPrefixDimEnv) ||
envSet(topologyBqRotationEnv) || envSet(graphCommunitySourceEnv) ||
(topologyExpansionOutputLimitEnv && *topologyExpansionOutputLimitEnv) ||
(topologyGraphWeightedSeedRankingEnv && *topologyGraphWeightedSeedRankingEnv) ||
(std::getenv("YAMS_BENCH_TOPOLOGY_ADAPTIVE_PROBE_SCORE_GAP") != nullptr) ||
(std::getenv("YAMS_BENCH_TOPOLOGY_NARROW_MIN_BOUNDARY_MARGIN") != nullptr) ||
(std::getenv("YAMS_BENCH_TOPOLOGY_EXPANSION") != nullptr) ||
(std::getenv("YAMS_SEARCH_TOPOLOGY_EXPANSION_SOURCE") != nullptr) ||
writeTopologyAdmissionConfig;
// Select a plugin root that satisfies the plugins the benchmark enables.
// Preferring the first ONNX-capable tree can silently disable entity extraction
// when that tree lacks the Glint plugin.
const bool enableGlintPlugin =
!embedBackendSimeon || envTruthy(std::getenv("YAMS_BENCH_ENABLE_GLINT"));
std::string glinerModelPath;
if (enableGlintPlugin) {
glinerModelPath = discoverGlinerModelPath();
} else {
spdlog::info("Simeon benchmark path: Glint/GLiNER disabled by default");
}
const bool needsGlintPlugin = !glinerModelPath.empty();
const bool needsOnnxPlugin = !embedBackendSimeon;
const char* envPluginDir = std::getenv("YAMS_PLUGIN_DIR");
if (envPluginDir) {
harnessOptions.pluginDir = fs::path(envPluginDir);
} else {
const fs::path cwd = fs::current_path();
const fs::path installedPluginDir = fs::path("/opt/homebrew/lib/yams/plugins");
const fs::path installedOnnxPlugin =
installedPluginDir / "libyams_onnx_plugin.dylib";
const fs::path installedGlintPlugin = installedPluginDir / "yams_glint.dylib";
const fs::path localPluginDir = cwd / "plugins";
const fs::path localOnnxPlugin =
localPluginDir / "onnx" / "libyams_onnx_plugin.dylib";
const fs::path localGlintPlugin = localPluginDir / "glint" / "yams_glint.dylib";
const fs::path root = cwd;
const fs::path nosanPluginDir = root / "builddir-nosan" / "plugins";
const fs::path nosanOnnxPlugin =
nosanPluginDir / "onnx" / "libyams_onnx_plugin.dylib";
const fs::path nosanGlintPlugin = nosanPluginDir / "glint" / "yams_glint.dylib";
const fs::path defaultPluginDir = root / "builddir" / "plugins";
const fs::path defaultOnnxPlugin =
defaultPluginDir / "onnx" / "libyams_onnx_plugin.dylib";
const fs::path defaultGlintPlugin = defaultPluginDir / "glint" / "yams_glint.dylib";
const auto choosePreferredPluginDir = [&]() -> std::optional<fs::path> {
struct Candidate {
fs::path dir;
fs::path onnxPlugin;
fs::path glintPlugin;
};
const std::array<Candidate, 3> candidates = {
{{localPluginDir, localOnnxPlugin, localGlintPlugin},
{nosanPluginDir, nosanOnnxPlugin, nosanGlintPlugin},
{defaultPluginDir, defaultOnnxPlugin, defaultGlintPlugin}}};
for (const auto& candidate : candidates) {
if (needsOnnxPlugin && !fs::exists(candidate.onnxPlugin)) {
continue;
}
if (needsGlintPlugin && !fs::exists(candidate.glintPlugin)) {
continue;
}
return candidate.dir;
}
return std::nullopt;
};
if (auto preferredPluginDir = choosePreferredPluginDir();
preferredPluginDir.has_value()) {
harnessOptions.pluginDir = *preferredPluginDir;
} else if (needsOnnxPlugin && fs::exists(installedOnnxPlugin) &&
(!needsGlintPlugin || fs::exists(installedGlintPlugin))) {
const fs::path stagedPluginDir =
fs::temp_directory_path() / "yams_retrieval_bench_plugins";
const fs::path stagedOnnxPlugin =
stagedPluginDir / installedOnnxPlugin.filename();
const fs::path stagedGlintPlugin =
stagedPluginDir / installedGlintPlugin.filename();
std::error_code ec;
fs::path installedOnnxSource = fs::weakly_canonical(installedOnnxPlugin, ec);
if (ec || installedOnnxSource.empty()) {
ec.clear();
installedOnnxSource = installedOnnxPlugin;
}
fs::path installedGlintSource = installedGlintPlugin;
if (needsGlintPlugin) {
installedGlintSource = fs::weakly_canonical(installedGlintPlugin, ec);
if (ec || installedGlintSource.empty()) {
ec.clear();
installedGlintSource = installedGlintPlugin;
}
}
yams::common::ensureDirectories(stagedPluginDir);
if (ec) {
spdlog::warn("Failed to create staged plugin dir {}: {}",
stagedPluginDir.string(), ec.message());
harnessOptions.pluginDir = installedPluginDir;
} else {
fs::remove(stagedOnnxPlugin, ec);
ec.clear();
fs::copy_file(installedOnnxSource, stagedOnnxPlugin,
fs::copy_options::overwrite_existing, ec);
if (ec) {
spdlog::warn("Failed to stage installed ONNX plugin {} -> {}: {}",
installedOnnxSource.string(), stagedOnnxPlugin.string(),
ec.message());
harnessOptions.pluginDir = installedPluginDir;
} else if (needsGlintPlugin) {
fs::remove(stagedGlintPlugin, ec);
ec.clear();
fs::copy_file(installedGlintSource, stagedGlintPlugin,
fs::copy_options::overwrite_existing, ec);
if (ec) {
throw std::runtime_error("Failed to stage installed Glint plugin " +
installedGlintSource.string() + " -> " +
stagedGlintPlugin.string() + ": " +
ec.message());
}
harnessOptions.pluginDir = stagedPluginDir;
spdlog::info("Using isolated installed ONNX+Glint plugin dir: {}",
stagedPluginDir.string());
} else {
harnessOptions.pluginDir = stagedPluginDir;
spdlog::info("Using isolated installed ONNX plugin dir: {}",
stagedPluginDir.string());
}
}
} else if (needsOnnxPlugin && needsGlintPlugin) {
throw std::runtime_error(
"Glint plugin required for benchmark entity extraction but no plugin root "
"contains both ONNX and Glint plugins. Build with -Dplugin-glint=true or "
"set YAMS_PLUGIN_DIR to a directory containing libyams_onnx_plugin.dylib "
"and yams_glint.dylib.");
} else if (needsGlintPlugin) {
// Glint-only (simeon backend): find any dir with Glint
if (fs::exists(localGlintPlugin))
harnessOptions.pluginDir = localPluginDir;
else if (fs::exists(nosanGlintPlugin))
harnessOptions.pluginDir = nosanPluginDir;
else if (fs::exists(defaultGlintPlugin))
harnessOptions.pluginDir = defaultPluginDir;
else
spdlog::warn("[Bench] Glint plugin not found; entity extraction disabled");
} else if (fs::exists(localPluginDir)) {
harnessOptions.pluginDir = localPluginDir;
} else if (fs::exists(nosanPluginDir)) {
harnessOptions.pluginDir = nosanPluginDir;
} else {
harnessOptions.pluginDir = defaultPluginDir;
}
}
if (embedBackendSimeon) {
// Simeon backend is the preferred fast, built-in benchmark path.
// Keep live-ish runs off ONNX entirely unless the operator explicitly
// opts in via YAMS_BENCH_ENABLE_ONNX_RERANK=1.
const bool enableOnnxRerank =
envTruthy(std::getenv("YAMS_BENCH_ENABLE_ONNX_RERANK"));
if (enableOnnxRerank) {
json onnxConfig;
onnxConfig["reranker_model"] = "bge-reranker-base";
harnessOptions.pluginConfigs["onnx_plugin"] = onnxConfig.dump();
spdlog::info("Configured ONNX plugin: reranker=bge-reranker-base (explicit "
"opt-in on simeon backend)");
} else {
spdlog::info("Simeon benchmark path: ONNX reranker disabled");
}
} else {
json onnxPluginConfig;
onnxPluginConfig["preferred_model"] = "embeddinggemma-300m";
onnxPluginConfig["reranker_model"] = "bge-reranker-base";
harnessOptions.pluginConfigs["onnx_plugin"] = onnxPluginConfig.dump();
spdlog::info("Configured ONNX plugin models: embedding=embeddinggemma-300m "
"reranker=bge-reranker-base");
}
std::vector<EnvSetting> pluginEnvOverrides;
if (harnessOptions.pluginConfigs.contains("onnx_plugin") &&
!std::getenv("YAMS_ONNX_RERANK_FORCE_CPU") &&
!std::getenv("YAMS_ONNX_RERANK_FORCE_GPU")) {
pluginEnvOverrides.push_back({"YAMS_ONNX_RERANK_FORCE_CPU", std::string("1")});
spdlog::info("Enabled CPU-preferred ONNX reranker for benchmark evaluation");
}
if (!pluginEnvOverrides.empty()) {
pluginScopedEnv = std::make_unique<ScopedEnvOverrides>(pluginEnvOverrides);
}
// Configure Glint plugin with GLiNER model path for NL entity extraction
if (!glinerModelPath.empty()) {
harnessOptions.autoLoadPlugins = true;
harnessOptions.pluginDirStrict = false;
json glintConfig;
glintConfig["model_path"] = glinerModelPath;
glintConfig["threshold"] = 0.5; // Default confidence threshold
harnessOptions.pluginConfigs["glint"] = glintConfig.dump();
spdlog::info("Configured Glint plugin with model: {}", glinerModelPath);
}
if (resetPluginTrustFile && harnessOptions.dataDir) {
const fs::path trustFile = *harnessOptions.dataDir / "plugins.trust";
std::set<fs::path> trusted;
if (harnessOptions.pluginDir) {
trusted.insert(
yams::daemon::plugin_trust::normalizePath(*harnessOptions.pluginDir));
}
if (!yams::daemon::plugin_trust::writeTrustStore(trustFile, trusted)) {
spdlog::warn("Failed to rewrite plugin trust file {}", trustFile.string());
} else {
spdlog::info("Rewrote plugin trust file for isolated benchmark plugins: {}",
trustFile.string());
}
}
if (useIsolatedBenchmarkConfig) {
std::string configSuffix = "yams_retrieval_bench_config.toml";
const std::string engineLabel = benchmarkEngineCacheLabel();
configSuffix = "yams_retrieval_bench_config_" + engineLabel + ".toml";
if (g_debugRunContext.has_value() && !g_debugRunContext->candidate.empty()) {
configSuffix = "yams_retrieval_bench_config_" + engineLabel + "_" +
g_debugRunContext->candidate + ".toml";
}
const fs::path configPath = fs::temp_directory_path() / configSuffix;
std::ofstream configOut(configPath, std::ios::trunc);
if (!configOut) {
spdlog::warn("Failed to write isolated benchmark config: {}",
configPath.string());
} else {
configOut << "# Isolated benchmark config\n";
if (harnessOptions.dataDir) {
configOut << "[core]\n";
configOut << "data_dir = \"" << harnessOptions.dataDir->string()
<< "\"\n\n";
configOut << "[storage]\n";
configOut << "base_path = \"" << harnessOptions.dataDir->string()
<< "\"\n\n";
}
configOut << "[embeddings]\n";
if (embedBackendSimeon) {
configOut << "preferred_model = \"simeon-default\"\n";
const bool fixedHash384 = g_simeonEncoderProfile == "fixed_hash_384";
configOut << "embedding_dim = " << (fixedHash384 ? 384 : 1024) << "\n\n";
if (!g_simeonEncoderProfile.empty()) {
configOut << "[embeddings.simeon]\n";
configOut << "encoder_profile = \"" << g_simeonEncoderProfile
<< "\"\n\n";
}
if (g_simeonBm25Enabled.has_value()) {
configOut << "[embeddings.simeon.bm25]\n";
configOut << "enabled = " << (*g_simeonBm25Enabled ? "true" : "false")
<< "\n\n";
}
if (g_simeonRouterEnabled.has_value()) {
configOut << "[embeddings.simeon.bm25.router]\n";
configOut << "enabled = " << (*g_simeonRouterEnabled ? "true" : "false")
<< "\n\n";
}
if (g_simeonFragmentGeometryEnabled.has_value() ||
!g_simeonFragmentEncoderProfile.empty()) {
configOut << "[embeddings.simeon.bm25.fragment_geometry]\n";
if (g_simeonFragmentGeometryEnabled.has_value()) {
configOut << "enabled = "
<< (*g_simeonFragmentGeometryEnabled ? "true" : "false")
<< "\n";
}
if (!g_simeonFragmentEncoderProfile.empty()) {
configOut << "encoder_profile = \""
<< g_simeonFragmentEncoderProfile << "\"\n";
}
configOut << "\n";
}
configOut << "[daemon.models]\n";
configOut << "preload_models = []\n";
} else {
configOut << "preferred_model = \"embeddinggemma-300m\"\n";
configOut << "embedding_dim = 768\n\n";
configOut << "[daemon.models]\n";
configOut << "preload_models = [\"embeddinggemma-300m\"]\n";
}
yams::bench::writeTopologyDisabledEmbeddingSelection(configOut,
topologyDisabled);
if (envSet(simeonOuterMaxSimEnv)) {
configOut << "\n[search]\n";
configOut << "simeon_rerank_outer_maxsim = "
<< (envTruthy(simeonOuterMaxSimEnv->c_str()) ? "true" : "false")
<< "\n";
}
configOut << "\n[vector_database]\n";
configOut << "search_engine = \"" << g_benchmark_search_engine << "\"\n";
if (std::getenv("YAMS_BENCH_SIMEON_PQ_RERANK_FACTOR") != nullptr) {
configOut << "simeon_pq_rerank_factor = "
<< std::max<std::size_t>(
1, parseSizeEnvOrDefault(
"YAMS_BENCH_SIMEON_PQ_RERANK_FACTOR", 2))
<< "\n";
}
if ((topologyModeEnv && *topologyModeEnv) || writeTopologyRouteConfig) {
configOut << "\n[search.topology]\n";
if (topologyModeEnv && *topologyModeEnv) {
configOut << "mode = \"" << topologyModeEnv << "\"\n";
}
if (const char* policy = std::getenv("YAMS_BENCH_TOPOLOGY_VECTOR_POLICY");
policy && *policy) {
configOut << "vector_policy = \"" << policy << "\"\n";
}
if (const char* selector =
std::getenv("YAMS_BENCH_TOPOLOGY_RESCUE_SELECTOR");
selector && *selector) {
configOut << "rescue_selector = \"" << selector << "\"\n";
}
configOut << "min_clusters = "
<< parseSizeEnvOrDefault("YAMS_BENCH_TOPOLOGY_MIN_CLUSTERS", 1)
<< "\n";
configOut << "max_clusters = "
<< parseSizeEnvOrFallback("YAMS_BENCH_TOPOLOGY_MAX_CLUSTERS",
"YAMS_SEARCH_TOPOLOGY_MAX_CLUSTERS", 2)
<< "\n";
configOut << "max_docs = "
<< parseSizeEnvOrFallback("YAMS_BENCH_TOPOLOGY_MAX_DOCS",
"YAMS_SEARCH_TOPOLOGY_MAX_DOCS", 64)
<< "\n";
if (topologyExpansionOutputLimitEnv && *topologyExpansionOutputLimitEnv) {
configOut << "expansion_output_limit = "
<< parseSizeEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_EXPANSION_OUTPUT_LIMIT", 0)
<< "\n";
}
configOut << "max_seed_documents = "
<< parseSizeEnvOrDefault("YAMS_BENCH_TOPOLOGY_MAX_SEED_DOCUMENTS",
32)
<< "\n";
// Road beam arms (route_only / augment); unset keeps the product default
// of no road hops.
if (envSet(topologyRoadHopsEnv)) {
configOut << "road_hops = "
<< parseSizeEnvOrDefault("YAMS_BENCH_TOPOLOGY_ROAD_HOPS", 0)
<< "\n";
}
if (envSet(topologyRoadBeamWidthEnv)) {
configOut
<< "road_beam_width = "
<< parseSizeEnvOrDefault("YAMS_BENCH_TOPOLOGY_ROAD_BEAM_WIDTH", 4)
<< "\n";
}
// Road walk arm (route_only / augment); unset keeps the product default
// of no walk.
if (envSet(topologyRoadWalkWindowEnv)) {
configOut
<< "road_walk_window = "
<< parseSizeEnvOrDefault("YAMS_BENCH_TOPOLOGY_ROAD_WALK_WINDOW", 0)
<< "\n";
}
if (topologyRepresentativeLimitEnv && *topologyRepresentativeLimitEnv) {
configOut << "representative_limit = "
<< parseSizeEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_ROUTE_REPRESENTATIVE_LIMIT", 0)
<< "\n";
}
if (topologyAnnCandidateLimitEnv && *topologyAnnCandidateLimitEnv) {
configOut << "ann_candidate_limit = "
<< parseSizeEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_ROUTE_ANN_CANDIDATE_LIMIT", 64)
<< "\n";
}
if (envSet(topologyBqCandidateLimitEnv)) {
configOut << "bq_candidate_limit = "
<< parseSizeEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_ROUTE_BQ_CANDIDATE_LIMIT", 0)
<< "\n";
}
if (envSet(graphCommunitySourceEnv)) {
configOut << "graph_community_source = \"" << *graphCommunitySourceEnv
<< "\"\n";
}
if (envSet(topologyBqPrefixDimEnv)) {
configOut << "bq_prefix_dim = "
<< parseSizeEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_ROUTE_BQ_PREFIX_DIM", 0)
<< "\n";
}
if (envSet(topologyBqRotationEnv)) {
configOut << "bq_rotation = \"" << *topologyBqRotationEnv << "\"\n";
}
configOut << "adaptive_probe_score_gap = "
<< parseFloatEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_ADAPTIVE_PROBE_SCORE_GAP", 0.0F)
<< "\n";
configOut << "narrow_min_boundary_margin = "
<< parseFloatEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_NARROW_MIN_BOUNDARY_MARGIN", 0.0F)
<< "\n";
if (const char* scoring = std::getenv("YAMS_BENCH_TOPOLOGY_ROUTE_SCORING");
scoring && *scoring) {
configOut << "route_scoring = \"" << scoring << "\"\n";
}
if (std::getenv("YAMS_BENCH_TOPOLOGY_SPARSE_DENSE_ALPHA") != nullptr) {
configOut << "sparse_dense_alpha = "
<< parseFloatEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_SPARSE_DENSE_ALPHA", 0.5F)
<< "\n";
}
if (std::getenv("YAMS_BENCH_TOPOLOGY_MIN_ROUTE_SCORE") != nullptr) {
configOut << "min_route_score = "
<< parseFloatEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_MIN_ROUTE_SCORE", 0.0F)
<< "\n";
}
if (writeTopologyAdmissionConfig) {
const auto writeSizeSetting = [&](const char* envName,
const char* key) {
if (hasBenchmarkSetting(envName)) {
configOut << key << " = " << parseSizeEnvOrDefault(envName, 0)
<< "\n";
}
};
const auto writeFloatSetting = [&](const char* envName,
const char* key) {
if (hasBenchmarkSetting(envName)) {
configOut << key << " = "
<< parseFloatEnvOrDefault(envName, 0.0F) << "\n";
}
};
const auto writeStringSetting = [&](const char* envName,
const char* key) {
if (const auto value = yams::config::getenv_optional(envName);
value && !value->empty()) {
configOut << key << " = " << nlohmann::json(*value).dump()
<< "\n";
}
};
writeStringSetting("YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_FINGERPRINT",
"route_calibration_fingerprint");
writeStringSetting(
"YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_POLICY_FINGERPRINT",
"route_calibration_policy_fingerprint");
writeStringSetting(
"YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_DATASET_IDENTITY",
"route_calibration_dataset_identity");
writeSizeSetting("YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_QUERIES",
"route_calibration_queries");
writeSizeSetting(
"YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_PROTECTED_CANDIDATES",
"route_calibration_protected_candidates");
writeSizeSetting(
"YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_MISSED_PROTECTED_CANDIDATES",
"route_calibration_missed_protected_candidates");
writeSizeSetting("YAMS_BENCH_TOPOLOGY_ROUTE_MIN_CALIBRATION_QUERIES",
"route_min_calibration_queries");
writeSizeSetting("YAMS_BENCH_TOPOLOGY_ROUTE_MAX_MISSES_PER_THOUSAND",
"route_max_misses_per_thousand");
writeFloatSetting(
"YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_MIN_BOUNDARY_MARGIN",
"route_calibration_min_boundary_margin");
writeSizeSetting("YAMS_BENCH_TOPOLOGY_ROUTE_CALIBRATION_MIN_SEED_HITS",
"route_calibration_min_seed_hits");
writeSizeSetting("YAMS_BENCH_TOPOLOGY_ROUTE_WORK_MAX_ROWS_VISITED",
"route_work_max_rows_visited");
writeSizeSetting(
"YAMS_BENCH_TOPOLOGY_ROUTE_WORK_MAX_EXACT_DISTANCE_EVALUATIONS",
"route_work_max_exact_distance_evaluations");
writeSizeSetting("YAMS_BENCH_TOPOLOGY_ROUTE_WORK_MAX_ANN_CANDIDATES",
"route_work_max_ann_candidates");
}
if (const char* exp = std::getenv("YAMS_BENCH_TOPOLOGY_EXPANSION");
exp && *exp) {
configOut << "expansion_source = \"" << exp << "\"\n";
} else if (const char* exp =
std::getenv("YAMS_SEARCH_TOPOLOGY_EXPANSION_SOURCE");
exp && *exp) {
configOut << "expansion_source = \"" << exp << "\"\n";
}
if (std::getenv("YAMS_SEARCH_TOPOLOGY_GRAPH_NEIGHBOR_MIN_SCORE") !=
nullptr) {
configOut << "graph_neighbor_min_score = "
<< parseFloatEnvOrDefault(
"YAMS_SEARCH_TOPOLOGY_GRAPH_NEIGHBOR_MIN_SCORE", 0.25F)
<< "\n";
}
if (topologyGraphWeightedSeedRankingEnv &&
*topologyGraphWeightedSeedRankingEnv) {
configOut << "graph_weighted_seed_ranking = "
<< (envTruthy(topologyGraphWeightedSeedRankingEnv) ? "true"
: "false")
<< "\n";
}
}
// route_only is an evaluation arm: it serves uncertified routes, so the daemon
// only runs it behind the explicit gate. The bench opts in whenever the arm is
// selected; without this the policy downgrades to Shadow.
if (const auto policy =
yams::config::getenv_optional("YAMS_BENCH_TOPOLOGY_VECTOR_POLICY");
policy && !policy->empty()) {
std::string normalizedPolicy(*policy);
std::transform(normalizedPolicy.begin(), normalizedPolicy.end(),
normalizedPolicy.begin(), [](unsigned char c) {
return c == '-' ? '_'
: static_cast<char>(std::tolower(c));
});
if (normalizedPolicy == "route_only") {
configOut << "\n[search.evaluation]\n";
configOut << "allow_uncertified_routes = true\n";
}
}
if (writeTopologyEngineConfig) {
configOut << "\n[topology]\n";
if (topologyEngineEnv && *topologyEngineEnv) {
configOut << "engine = \"" << topologyEngineEnv << "\"\n";
}
if (topologyRepresentativesEnv && *topologyRepresentativesEnv) {
configOut << "routing_representatives = "
<< parseSizeEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_ROUTING_REPRESENTATIVES", 1)
<< "\n";
}
if (envSet(topologyRepresentativeRuleEnv)) {
configOut << "representative_rule = \""
<< *topologyRepresentativeRuleEnv << "\"\n";
}
if (envSet(topologySgcHopsEnv)) {
configOut << "sgc_hops = "
<< parseSizeEnvOrDefault("YAMS_BENCH_TOPOLOGY_SGC_HOPS", 0)
<< "\n";
}
// Road network arms: roads per cluster and the walk order kind the build
// stores (Eval 3 sets the order per arm).
if (envSet(topologyRoadMaxPerClusterEnv)) {
configOut << "road_max_per_cluster = "
<< parseSizeEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_ROAD_MAX_PER_CLUSTER", 0)
<< "\n";
}
if (envSet(topologyRoadOrderEnv)) {
configOut << "road_order = \"" << *topologyRoadOrderEnv << "\"\n";
}
if (topologyBoundarySpillEnv && *topologyBoundarySpillEnv) {
configOut << "boundary_spill = "
<< (envTruthy(topologyBoundarySpillEnv) ? "true" : "false")
<< "\n";
configOut << "boundary_spill_limit = "
<< parseSizeEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_BOUNDARY_SPILL_LIMIT", 1)
<< "\n";
configOut
<< "boundary_spill_distance_ratio = "
<< parseFloatEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_BOUNDARY_SPILL_DISTANCE_RATIO", 1.05F)
<< "\n";
configOut
<< "boundary_spill_residual_penalty = "
<< parseFloatEnvOrDefault(
"YAMS_BENCH_TOPOLOGY_BOUNDARY_SPILL_RESIDUAL_PENALTY", 1.0F)
<< "\n";
}
}
configOut.close();
harnessOptions.configPath = configPath;
spdlog::info("Using isolated benchmark config: {}", configPath.string());
}
}
} else {
spdlog::info("Using mock model provider (YAMS_DISABLE_VECTORS=1)");
}
harness = std::make_unique<DaemonHarness>(harnessOptions);
std::chrono::milliseconds daemonReadyTimeout{30000};
if (const char* env = std::getenv("YAMS_BENCH_DAEMON_READY_TIMEOUT_MS")) {
daemonReadyTimeout = std::chrono::milliseconds{
static_cast<std::chrono::milliseconds::rep>(std::stoll(env))};
}
if (!harness->start(daemonReadyTimeout))
throw std::runtime_error("Failed to start daemon");
ensureBenchmarkEmbeddingsReady(harness.get(), vectorsDisabled, forceMockEmbeddings,
embedBackendSimeon);
if (useBEIR) {
const BEIRDataset& dataset = preparedBeirDataset.value();
fs::path corpusDir = harness->rootDir() / "corpus";
beirCorpus = std::make_unique<BEIRCorpusLoader>(dataset, corpusDir);
beirCorpus->writeDocumentsAsFiles();
benchCorpusDir = corpusDir;
corpusSize = static_cast<int>(dataset.documents.size());
numQueries = static_cast<int>(dataset.queries.size());
expectedCacheMetadata.expectedDocs = corpusSize;
expectedCacheMetadata.expectedQueries = numQueries;
expectedCacheMetadata.corpusFingerprint = computePathFingerprint(benchCorpusDir);
} else {
benchCorpusDir = harness->rootDir() / "corpus";
corpus = std::make_unique<CorpusGenerator>(benchCorpusDir, syntheticCorpusMode);
corpus->generateDocuments(corpusSize);
corpusSize = static_cast<int>(corpus->createdFiles.size());
expectedCacheMetadata.expectedDocs = corpusSize;
expectedCacheMetadata.corpusFingerprint = computePathFingerprint(benchCorpusDir);
}
yams::daemon::ClientConfig clientCfg;
// Prefer proxy socket for long-running benchmark connections; it skips
// maxConnectionLifetime enforcement (main socket forces close at ~300s).
{
// Proxy socket is derived from daemon socket stem + ".proxy.sock".
fs::path daemonSock = harness->socketPath();
auto base = daemonSock.stem().string();
if (base.empty())
base = daemonSock.filename().string();
if (base.empty())
base = "yams-daemon";
fs::path proxySock = daemonSock.parent_path() / (base + ".proxy.sock");
if (fs::exists(proxySock)) {
clientCfg.socketPath = proxySock;
spdlog::info("Using proxy socket for benchmark client: {}", proxySock.string());
} else {
clientCfg.socketPath = harness->socketPath();
spdlog::info("Proxy socket not found; using main socket: {}",
clientCfg.socketPath.string());
}
}
clientCfg.connectTimeout = 5s;
const auto benchmarkRequestTimeout =
std::max(std::chrono::duration_cast<std::chrono::milliseconds>(300s),
configuredBenchQueryTimeout("hybrid"));
clientCfg.requestTimeout = benchmarkRequestTimeout;
clientCfg.autoStart = false;
auto isEofLikeError = [](const yams::Error& error) {
return error.message.find("[ipc:eof]") != std::string::npos ||
error.message.find("End of file") != std::string::npos ||
error.message.find("Connection closed") != std::string::npos;
};
auto connectClientWithProbe = [&](const char* reason) {
if (reason && *reason) {
spdlog::info("[Bench] Reconnecting benchmark client ({})", reason);
}
client = std::make_unique<yams::daemon::DaemonClient>(clientCfg);
// Override header/body timeouts to handle slow reranker queries.
// Default headerTimeout=30s is too short when cross-encoder reranking
// takes 20-60s before any response header is sent.
client->setHeaderTimeout(benchmarkRequestTimeout);
client->setBodyTimeout(benchmarkRequestTimeout);
auto connectResult = benchRunSync(client->connect(), 5s);
if (!connectResult) {
throw std::runtime_error("Failed to connect: " + connectResult.error().message);
}
// Probe the exact selected socket path before ingesting to catch startup races.
constexpr int kStatusProbeAttempts = 8;
for (int attempt = 1; attempt <= kStatusProbeAttempts; ++attempt) {
auto statusProbe = benchRunSync(client->status(true), 5s);
if (statusProbe) {
if (attempt > 1) {
spdlog::info("[Bench] Client status probe succeeded after {} attempts",
attempt);
}
return;
}
if (attempt == kStatusProbeAttempts) {
throw std::runtime_error("Client status probe failed: " +
statusProbe.error().message);
}
const bool eofLike = isEofLikeError(statusProbe.error());
const auto backoff = std::chrono::milliseconds(125 * attempt);
spdlog::warn("[Bench] Client status probe attempt {}/{} failed ({}{}), retrying in "
"{}ms",
attempt, kStatusProbeAttempts, statusProbe.error().message,
eofLike ? ", eof-like" : "", backoff.count());
if (eofLike) {
// Drop any potentially stale pooled transport before retrying.
client.reset();
yams::daemon::AsioConnectionPool::shutdown_all(std::chrono::milliseconds(250));
auto reconnectWait = std::chrono::milliseconds(80);
std::this_thread::sleep_for(reconnectWait);
client = std::make_unique<yams::daemon::DaemonClient>(clientCfg);
client->setHeaderTimeout(std::chrono::milliseconds(300000));
client->setBodyTimeout(std::chrono::milliseconds(300000));
auto reconnectResult = benchRunSync(client->connect(), 5s);
if (!reconnectResult) {
throw std::runtime_error("Failed to reconnect after probe EOF: " +
reconnectResult.error().message);
}
}
std::this_thread::sleep_for(backoff);
}
};
connectClientWithProbe("initial setup");
const bool usingExternalBenchDataDir = harnessOptions.dataDir.has_value();
const fs::path effectiveDataDir =
harnessOptions.dataDir.has_value() ? *harnessOptions.dataDir : harness->dataDir();
auto initialStatusResult = benchRunSync(client->status(true), 5s);
const auto getInitialCount = [&](const std::string& key) -> uint64_t {
if (!initialStatusResult) {
return 0;
}
auto it = initialStatusResult.value().requestCounts.find(key);
return (it == initialStatusResult.value().requestCounts.end()) ? 0ULL : it->second;
};
const bool warmCacheMetadataReusable =
warmCacheMetadata.has_value() &&
(warmCacheMetadata->status == "primed" || warmCacheMetadata->status == "partial");
const uint64_t initialIndexedDocCount = [&]() -> uint64_t {
if (warmCacheMetadataReusable) {
return static_cast<uint64_t>(warmCacheMetadata->expectedDocs);
}
if (!initialStatusResult) {
return 0;
}
auto itIndexed = initialStatusResult.value().requestCounts.find("documents_indexed");
if (itIndexed != initialStatusResult.value().requestCounts.end()) {
return itIndexed->second;
}
auto itTotal = initialStatusResult.value().requestCounts.find("documents_total");
return (itTotal == initialStatusResult.value().requestCounts.end()) ? 0ULL
: itTotal->second;
}();
const uint64_t initialDocsEmbedded =
warmCacheMetadataReusable ? static_cast<uint64_t>(warmCacheMetadata->embeddedDocs)
: getInitialCount("documents_embedded");
const uint64_t initialVectorCount =
warmCacheMetadataReusable ? static_cast<uint64_t>(warmCacheMetadata->vectorCount)
: getInitialCount("vector_count");
const uint64_t initialEmbedQueued =
warmCacheMetadataReusable ? 0 : getInitialCount("embed_svc_queued");
const uint64_t initialEmbedInFlight =
warmCacheMetadataReusable ? 0 : getInitialCount("embed_in_flight");
const PersistedHnswState initialPersistedHnsw = inspectPersistedHnswState(effectiveDataDir);
bool initialBackendIndexReusable = false;
if (auto* daemon = harness ? harness->daemon() : nullptr) {
if (auto* serviceManager = daemon->getServiceManager()) {
if (auto vectorDb = serviceManager->getVectorDatabase();
vectorDb && vectorDb->isInitialized()) {
const auto probeStart = std::chrono::steady_clock::now();
initialBackendIndexReusable = vectorDb->hasReusablePersistedSearchIndex();
setupTimings.vectorIndexReusableProbeMs +=
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - probeStart)
.count();
++setupTimings.vectorIndexReusableProbeCount;
}
}
}
const bool cacheHasIndexedCorpus =
usingExternalBenchDataDir &&
initialIndexedDocCount >= static_cast<uint64_t>(corpusSize);
const bool cacheHasFullEmbeddings =
usingExternalBenchDataDir && !vectorsDisabled &&
initialDocsEmbedded >= static_cast<uint64_t>(corpusSize) && initialEmbedQueued == 0 &&
initialEmbedInFlight == 0;
const bool cacheHasReusableVectorIndex =
vectorsDisabled || (usingExternalBenchDataDir &&
yams::bench::benchmarkSearchIndexReusable(
benchmarkVectorSearchEngine(), initialBackendIndexReusable));
if (usingExternalBenchDataDir) {
spdlog::info("[Bench] External data dir status: indexed_docs={} embedded_docs={} "
"vector_count={} embed_queue={} embed_in_flight={} vector_index_ready={} "
"persisted_hnsw_nodes={} persisted_hnsw_meta={}{}",
initialIndexedDocCount, initialDocsEmbedded, initialVectorCount,
initialEmbedQueued, initialEmbedInFlight,
(cacheHasReusableVectorIndex ? "true" : "false"),
initialPersistedHnsw.nodeRows, initialPersistedHnsw.metaRows,
warmCacheMetadataReusable ? " (trusted metadata)" : "");
}
if (!(cacheHasFullEmbeddings && cacheHasReusableVectorIndex)) {
if (cacheHasIndexedCorpus) {
spdlog::info("[Bench] Reusing indexed corpus from external data dir; skipping "
"directory ingest");
}
if (!cacheHasIndexedCorpus) {
// Use directory ingestion for faster bulk add via IndexingService
fs::path corpusDir = useBEIR ? beirCorpus->corpusDir : corpus->corpusDir;
spdlog::info("Ingesting {} documents from {}...", corpusSize, corpusDir.string());
setupTimings.coldIngestPerformed = true;
coldIngestStartedAt = SetupClock::now();
yams::daemon::AddDocumentRequest addReq;
addReq.path = corpusDir.string();
addReq.recursive = true;
addReq.noEmbeddings = false;
addReq.includePatterns = {"*.txt"};
Result<yams::daemon::AddDocumentResponse> addResult =
Error{ErrorCode::Unknown, "ingest not attempted"};
constexpr int kIngestAttempts = 2;
for (int ingestAttempt = 1; ingestAttempt <= kIngestAttempts; ++ingestAttempt) {
if (!client) {
connectClientWithProbe("directory ingest attempt");
if (!client) {
throw std::runtime_error(
"Benchmark client unavailable before directory ingest attempt");
}
}
addResult = benchRunSync(client->streamingAddDocument(addReq), 120s);
if (addResult) {
break;
}
const bool eofLike = isEofLikeError(addResult.error());
if (!(eofLike && ingestAttempt < kIngestAttempts)) {
break;
}
spdlog::warn(
"[Bench] Directory ingest attempt {}/{} failed with eof-like transport "
"error ({}). Retrying once with a fresh client.",
ingestAttempt, kIngestAttempts, addResult.error().message);
client.reset();
yams::daemon::AsioConnectionPool::shutdown_all(std::chrono::milliseconds(250));
std::this_thread::sleep_for(std::chrono::milliseconds(120));
connectClientWithProbe("ingest retry after eof");
}
if (!addResult) {
throw std::runtime_error("Failed to ingest directory: " +
addResult.error().message);
}
admissionCompletedAt = SetupClock::now();
setupTimings.admissionMs = elapsedMsSince(*coldIngestStartedAt);
spdlog::info("Directory ingestion request accepted: {}", addResult.value().message);
// Wait for directory ingestion to complete by monitoring document count
// Use progress-based timeout: only timeout if no progress for N seconds
// This is more robust than a hard deadline because slow ingestion won't fail,
// but true hangs will still be detected.
auto progressTimeoutSec = std::chrono::seconds(
300); // 5 minutes without progress = stalled (embedding/model load)
if (const char* env = std::getenv("YAMS_BENCH_PROGRESS_TIMEOUT")) {
progressTimeoutSec = std::chrono::seconds(std::stoi(env));
}
double ingestMinFraction = 1.0;
if (const char* env = std::getenv("YAMS_BENCH_INGEST_MIN_FRACTION")) {
ingestMinFraction = std::clamp(std::stod(env), 0.0, 1.0);
}
const auto ingestTargetDocs = static_cast<uint64_t>(
std::ceil(static_cast<double>(corpusSize) * ingestMinFraction));
spdlog::info("Waiting for ingestion to complete (expecting {} documents, "
"progress timeout {}s)...",
corpusSize, progressTimeoutSec.count());
auto lastProgressTime = std::chrono::steady_clock::now();
auto ingestHeartbeatAt = lastProgressTime + std::chrono::seconds(5);
uint64_t lastDocCount = 0;
uint64_t lastIndexedDocCount = 0;
uint64_t lastContentExtracted = 0;
uint64_t lastPostProcessed = 0;
uint64_t lastEmbedQueued = 0;
uint64_t lastEmbedInFlight = 0;
uint64_t lastVectorCount = 0;
uint32_t lastDepth = 0;
int stableChecks = 0;
// Helper to get metric from requestCounts with presence flag
auto getMetric = [](const auto& counts,
const std::string& key) -> std::pair<uint64_t, bool> {
auto it = counts.find(key);
if (it == counts.end())
return {0, false};
return {it->second, true};
};
uint64_t lastKgInFlight = 0, lastSymbolInFlight = 0, lastEntityInFlight = 0;
uint64_t lastExtractionInFlight = 0;
auto ingestionLooksCompleteFromLastObserved = [&]() -> bool {
const bool targetReached = (lastIndexedDocCount >= ingestTargetDocs) ||
(lastDocCount >= ingestTargetDocs);
const bool allQueuesDrained =
(lastDepth == 0 && lastExtractionInFlight == 0 && lastKgInFlight == 0 &&
lastSymbolInFlight == 0 && lastEntityInFlight == 0 &&
lastEmbedQueued == 0 && lastEmbedInFlight == 0);
const bool extractionOrPostDone = (lastContentExtracted >= ingestTargetDocs) ||
(lastPostProcessed >= ingestTargetDocs);
return targetReached && allQueuesDrained && extractionOrPostDone;
};
while (true) {
auto now = std::chrono::steady_clock::now();
auto timeSinceProgress =
std::chrono::duration_cast<std::chrono::seconds>(now - lastProgressTime);
if (now >= ingestHeartbeatAt) {
ingestHeartbeatAt = now + std::chrono::seconds(5);
spdlog::info("Ingest wait heartbeat: since_progress={}s last_total={} "
"last_indexed={} last_queue={} "
"last_processed={}",
timeSinceProgress.count(), lastDocCount, lastIndexedDocCount,
lastDepth, lastPostProcessed);
}
// Check for stall (no progress for progressTimeoutSec)
if (timeSinceProgress > progressTimeoutSec) {
if (ingestionLooksCompleteFromLastObserved()) {
spdlog::warn(
"Ingestion progress timeout reached, but last observed metrics "
"already indicate completion; accepting completion.");
break;
}
// Status polls may intermittently fail even when work is done; perform one
// final verification poll before classifying as stalled.
auto finalVerify = benchRunSync(client->status(true), 10s);
if (finalVerify) {
const auto& counts = finalVerify.value().requestCounts;
auto get = [&](const std::string& key) -> uint64_t {
auto it = counts.find(key);
return (it == counts.end()) ? 0ULL : it->second;
};
const uint64_t docsTotal = get("documents_total");
const uint64_t docsIndexed = get("documents_indexed");
const uint64_t extracted = get("documents_content_extracted");
const uint64_t postQueued = get("post_ingest_queued");
const uint64_t postInFlight = get("post_ingest_inflight");
const uint64_t postProcessed = get("post_ingest_processed");
const uint64_t extractionInFlight = get("extraction_inflight");
const uint64_t kgInFlight = get("kg_inflight");
const uint64_t symbolInFlight = get("symbol_inflight");
const uint64_t entityInFlight = get("entity_inflight");
const uint64_t embedQueued = get("embed_svc_queued");
const uint64_t embedInFlight = get("embed_in_flight");
const bool targetReached = (docsIndexed >= ingestTargetDocs) ||
(docsTotal >= ingestTargetDocs);
const bool allQueuesDrained =
(postQueued == 0 && postInFlight == 0 && extractionInFlight == 0 &&
kgInFlight == 0 && symbolInFlight == 0 && entityInFlight == 0 &&
embedQueued == 0 && embedInFlight == 0);
const bool extractionOrPostDone = (extracted >= ingestTargetDocs) ||
(postProcessed >= ingestTargetDocs);
if (targetReached && allQueuesDrained && extractionOrPostDone) {
spdlog::warn(
"Ingestion progress timeout reached but final verification "
"shows completion (docs_total={}, docs_indexed={}, "
"extracted={}, post_processed={}); accepting completion.",
docsTotal, docsIndexed, extracted, postProcessed);
break;
}
}
spdlog::error(
"Ingestion stalled - no progress for {}s (docs: total={} indexed={} "
"target={} "
"queue={}, inflight: extract={} kg={} symbol={} entity={} extracted={} "
"processed={} embed_queue={} embed_in_flight={} vectors={})",
timeSinceProgress.count(), lastDocCount, lastIndexedDocCount,
corpusSize, lastDepth, lastExtractionInFlight, lastKgInFlight,
lastSymbolInFlight, lastEntityInFlight, lastContentExtracted,
lastPostProcessed, lastEmbedQueued, lastEmbedInFlight, lastVectorCount);
throw std::runtime_error(
"Ingestion stalled - benchmark results would be invalid. "
"Set YAMS_BENCH_PROGRESS_TIMEOUT=300 for slower systems. "
"For faster ingestion: YAMS_POST_EMBED_CONCURRENT=12 "
"YAMS_POST_EXTRACTION_CONCURRENT=12 YAMS_DB_POOL_MAX=48");
}
auto statusResult = benchRunSync(client->status(true), 5s);
if (statusResult) {
const auto& counts = statusResult.value().requestCounts;
uint32_t depth = statusResult.value().postIngestQueueDepth;
auto [docCount, docCountPresent] = getMetric(counts, "documents_total");
auto [indexedCount, indexedPresent] =
getMetric(counts, "documents_indexed");
auto [contentExtracted, extractedPresent] =
getMetric(counts, "documents_content_extracted");
auto [postQueued, postQueuedPresent] =
getMetric(counts, "post_ingest_queued");
auto [postInflight, postInflightPresent] =
getMetric(counts, "post_ingest_inflight");
auto [postProcessed, postProcessedPresent] =
getMetric(counts, "post_ingest_processed");
auto [embedQueued, embedQueuedPresent] =
getMetric(counts, "embed_svc_queued");
auto [embedInFlight, embedInFlightPresent] =
getMetric(counts, "embed_in_flight");
auto [vectorCount, vectorPresent] = getMetric(counts, "vector_count");
uint64_t queuedTotal = std::max<uint64_t>(depth, postQueued);
// Check all processing stage in-flight counters
auto [extractionInFlight, extractionPresent] =
getMetric(counts, "extraction_inflight");
auto [kgInFlight, kgPresent] = getMetric(counts, "kg_inflight");
auto [symbolInFlight, symbolPresent] = getMetric(counts, "symbol_inflight");
auto [entityInFlight, entityPresent] = getMetric(counts, "entity_inflight");
// Total in-flight across all stages (matches
// PostIngestQueue::totalInFlight())
uint64_t totalInFlight =
extractionInFlight + kgInFlight + symbolInFlight + entityInFlight;
bool statusChanged =
(queuedTotal != lastDepth || docCount != lastDocCount ||
indexedCount != lastIndexedDocCount ||
contentExtracted != lastContentExtracted ||
postProcessed != lastPostProcessed || kgInFlight != lastKgInFlight ||
symbolInFlight != lastSymbolInFlight ||
entityInFlight != lastEntityInFlight ||
extractionInFlight != lastExtractionInFlight ||
embedQueued != lastEmbedQueued || embedInFlight != lastEmbedInFlight ||
vectorCount != lastVectorCount);
if (statusChanged) {
bool vectorDbReady = statusResult.value().vectorDbReady;
if (auto it = statusResult.value().readinessStates.find("vector_db");
it != statusResult.value().readinessStates.end()) {
vectorDbReady = it->second;
}
spdlog::debug(
"Documents: total={} indexed={} / {} | queue={} inflight={} | "
"extracted={} "
"processed={} | extract={} kg={} symbol={} entity={} (total={}) | "
"embed_queue={} embed_in_flight={} vectors={} (ready={})",
docCount, indexedCount, corpusSize, queuedTotal, postInflight,
contentExtracted, postProcessed, extractionInFlight, kgInFlight,
symbolInFlight, entityInFlight, totalInFlight, embedQueued,
embedInFlight, vectorCount, (vectorDbReady ? "true" : "false"));
lastDepth = static_cast<uint32_t>(queuedTotal);
lastDocCount = docCount;
lastIndexedDocCount = indexedCount;
lastContentExtracted = contentExtracted;
lastPostProcessed = postProcessed;
lastEmbedQueued = embedQueued;
lastEmbedInFlight = embedInFlight;
lastVectorCount = vectorCount;
lastKgInFlight = kgInFlight;
lastSymbolInFlight = symbolInFlight;
lastEntityInFlight = entityInFlight;
lastExtractionInFlight = extractionInFlight;
stableChecks = 0;
lastProgressTime = now; // Reset progress timer on any change
} else {
stableChecks++;
}
// Complete when ALL stages are idle:
// - post_ingest_queued == 0 AND post_ingest_inflight == 0 (queue empty)
// - totalInFlight == 0 (all stages: extraction, KG, symbol, entity)
// - indexedCount >= corpusSize (or stable for 5+ seconds after reaching
// target)
bool allStagesDrained =
(queuedTotal == 0 && postInflight == 0 && totalInFlight == 0);
bool extractedReady =
extractedPresent
? (contentExtracted >= static_cast<uint64_t>(corpusSize))
: true;
bool processedReady =
postProcessedPresent
? (postProcessed >= static_cast<uint64_t>(corpusSize))
: true;
// `documents_indexed` is a search/FTS visibility counter and can lag or
// remain stale under sanitizer/slow asynchronous rebuild runs even after
// the directory ingest and post-ingest pipeline have accepted and processed
// the whole corpus. For benchmark setup, do not block forever on that
// secondary visibility counter once the canonical document total reached
// the requested corpus and extraction/post-processing has completed.
const bool corpusStored = docCount >= static_cast<uint64_t>(corpusSize);
bool indexedReady =
indexedPresent ? (indexedCount >= static_cast<uint64_t>(corpusSize) ||
(corpusStored && (extractedReady || processedReady)))
: corpusStored;
bool stableAfterDrain = (stableChecks >= 6);
if (allStagesDrained && indexedReady &&
(extractedReady || processedReady || stableAfterDrain)) {
spdlog::info(
"Ingestion complete: total={} indexed={} (target={}), all stages "
"drained (extracted={}, processed={})",
docCount, indexedCount, corpusSize, contentExtracted,
postProcessed);
break;
}
} else {
if (now >= ingestHeartbeatAt - std::chrono::seconds(5)) {
spdlog::warn("Status polling failed during ingest wait: {}",
statusResult.error().message);
}
}
std::this_thread::sleep_for(500ms);
}
setupTimings.storageReadyMs = elapsedMsSince(*coldIngestStartedAt);
setupTimings.pipelineDrainMs =
admissionCompletedAt.has_value() ? elapsedMsSince(*admissionCompletedAt) : 0;
} else if (!vectorsDisabled) {
yams::daemon::RepairRequest repairReq;
repairReq.repairEmbeddings = true;
repairReq.embeddingModel = "embeddinggemma-300m";
repairReq.foreground = true;
repairReq.maxRetries = 3;
spdlog::info("[Bench] Requesting embedding repair to resume warm-cache priming...");
auto repairResult = benchRunSync(client->callRepair(repairReq), 300s);
if (!repairResult) {
spdlog::warn("[Bench] Embedding repair request failed: {}",
repairResult.error().message);
} else {
spdlog::info("[Bench] Embedding repair queued: success={} ops={} succeeded={} "
"failed={} skipped={}",
repairResult.value().success, repairResult.value().totalOperations,
repairResult.value().totalSucceeded,
repairResult.value().totalFailed,
repairResult.value().totalSkipped);
}
}
// Directory ingest carries no per-file metadata; copy the corpus metadata
// (event_time, session_id, round_index, ...) onto the stored documents so the
// topology extractor sees it. Idempotent, so warm caches repeat it.
if (useBEIR && beirCorpus) {
auto* daemon = harness ? harness->daemon() : nullptr;
auto* serviceManager = daemon ? daemon->getServiceManager() : nullptr;
auto metadataRepo = serviceManager ? serviceManager->getMetadataRepo() : nullptr;
if (metadataRepo) {
std::size_t missing = 0;
const auto applied = yams::bench::applyBenchmarkDocumentMetadata(
*metadataRepo, *beirCorpus, &missing);
if (applied > 0 || missing > 0) {
spdlog::info("[Bench] Applied corpus metadata to {} documents ({} not "
"found)",
applied, missing);
}
}
}
// Wait for embedding provider to become available
spdlog::info("Waiting for embedding provider to become available...");
auto deadline = std::chrono::steady_clock::now() + 30s;
bool embeddingReady = false;
while (std::chrono::steady_clock::now() < deadline) {
auto statusResult = benchRunSync(client->status(true), 5s);
if (statusResult && statusResult.value().embeddingAvailable) {
spdlog::info("Embedding provider is available (backend: {}, model: {})",
statusResult.value().embeddingBackend,
statusResult.value().embeddingModel);
embeddingReady = true;
break;
}
std::this_thread::sleep_for(500ms);
}
if (!embeddingReady) {
spdlog::warn("Embedding provider did not become available within 30s");
}
// Wait for embeddings to be generated by checking document-level embedding progress
// (documents_embedded) and queue drain. We still track vector_count for row-level
// visibility, but vector_count is not a document counter.
bool vectorsEnabled = !vectorsDisabled;
if (vectorsEnabled && !embeddingReady) {
vectorsEnabled = false;
spdlog::warn("Disabling vector wait for this benchmark run: embedding provider is "
"unavailable; proceeding with keyword-dominant evaluation");
if (summaryLog) {
summaryLog << "Embedding provider unavailable after 30s; skipping vector wait"
<< std::endl;
summaryLog.flush();
}
}
if (vectorsEnabled) {
spdlog::info("Waiting for embeddings to be generated (target: {} docs)...",
corpusSize);
auto embedHeartbeatAt = std::chrono::steady_clock::now() + std::chrono::seconds(5);
// Log to summary file
if (summaryLog) {
summaryLog << "=== Embedding Generation ===" << std::endl;
summaryLog << "Target: " << corpusSize << " documents" << std::endl;
}
auto embedProgressTimeoutSec = std::chrono::seconds(600);
if (const char* env = std::getenv("YAMS_BENCH_EMBED_PROGRESS_TIMEOUT")) {
embedProgressTimeoutSec = std::chrono::seconds(std::stoi(env));
}
auto embedMaxWaitSec = std::chrono::seconds(7200); // 2h hard cap (configurable)
if (const char* env = std::getenv("YAMS_BENCH_EMBED_MAX_WAIT")) {
embedMaxWaitSec = std::chrono::seconds(std::stoi(env));
}
struct EmbedWaitSnapshot {
uint64_t vectorCount = 0;
uint64_t docsEmbedded = 0;
uint64_t preparedDocs = 0;
uint64_t preparedChunks = 0;
uint64_t queueDepth = 0;
uint64_t inFlight = 0;
uint64_t dropped = 0;
uint64_t inferActive = 0;
uint64_t inferOldestMs = 0;
uint64_t inferStarted = 0;
uint64_t inferCompleted = 0;
uint64_t inferWarnCount = 0;
bool hasQueueMetrics = false;
bool vectorDbReady = false;
bool queueDrained() const { return queueDepth == 0 && inFlight == 0; }
bool inferLikelyActive(std::chrono::seconds timeout) const {
return inferActive > 0 &&
inferOldestMs < static_cast<uint64_t>(timeout.count()) * 1000ull;
}
double coverageFor(int totalDocs) const {
return totalDocs > 0 ? (docsEmbedded * 100.0 / totalDocs) : 0.0;
}
double avgChunksPerDoc() const {
return preparedDocs > 0 ? static_cast<double>(preparedChunks) /
static_cast<double>(preparedDocs)
: 0.0;
}
};
auto progressSignalChanged = [](const EmbedWaitSnapshot& lhs,
const EmbedWaitSnapshot& rhs) {
return lhs.vectorCount != rhs.vectorCount ||
lhs.docsEmbedded != rhs.docsEmbedded ||
lhs.preparedDocs != rhs.preparedDocs ||
lhs.preparedChunks != rhs.preparedChunks ||
lhs.queueDepth != rhs.queueDepth || lhs.inFlight != rhs.inFlight ||
lhs.dropped != rhs.dropped || lhs.inferActive != rhs.inferActive ||
lhs.inferStarted != rhs.inferStarted ||
lhs.inferCompleted != rhs.inferCompleted ||
lhs.inferWarnCount != rhs.inferWarnCount;
};
auto getRequestCount = [](const auto& status, std::string_view key) {
auto it = status.requestCounts.find(std::string(key));
return (it == status.requestCounts.end()) ? 0ULL : it->second;
};
auto collectEmbedSnapshot = [&](const auto& status) {
EmbedWaitSnapshot snapshot;
snapshot.vectorDbReady = status.vectorDbReady;
if (auto it = status.readinessStates.find("vector_db");
it != status.readinessStates.end()) {
snapshot.vectorDbReady = it->second;
}
snapshot.vectorCount = getRequestCount(status, "vector_count");
snapshot.docsEmbedded = getRequestCount(status, "documents_embedded");
snapshot.preparedDocs =
getRequestCount(status, "bus_embed_prepared_docs_queued");
snapshot.preparedChunks =
getRequestCount(status, "bus_embed_prepared_chunks_queued");
snapshot.queueDepth = getRequestCount(status, "embed_svc_queued");
snapshot.inFlight = getRequestCount(status, "embed_in_flight");
snapshot.dropped = getRequestCount(status, "bus_embed_dropped");
snapshot.inferActive = getRequestCount(status, "embed_infer_active");
snapshot.inferOldestMs = getRequestCount(status, "embed_infer_oldest_ms");
snapshot.inferStarted = getRequestCount(status, "embed_infer_started");
snapshot.inferCompleted = getRequestCount(status, "embed_infer_completed");
snapshot.inferWarnCount = getRequestCount(status, "embed_infer_warn_count");
snapshot.hasQueueMetrics = status.requestCounts.contains("embed_svc_queued") ||
status.requestCounts.contains("embed_in_flight");
return snapshot;
};
EmbedWaitSnapshot observedSnapshot;
EmbedWaitSnapshot lastProgressSnapshot;
bool haveObservedSnapshot = false;
bool haveProgressSnapshot = false;
int stableCount = 0;
bool embeddingDrainSatisfied = false;
auto embedStartTime = std::chrono::steady_clock::now();
auto lastEmbedProgressTime = embedStartTime;
// Phase 1: Wait for embedding queue to drain and embedded documents to reach
// target. Uses progress-based stall detection (like ingestion wait) to avoid
// failing while work is still actively progressing on large corpora/slow
// accelerators.
while (true) {
auto now = std::chrono::steady_clock::now();
auto elapsed =
std::chrono::duration_cast<std::chrono::seconds>(now - embedStartTime);
auto sinceProgress = std::chrono::duration_cast<std::chrono::seconds>(
now - lastEmbedProgressTime);
if (embedMaxWaitSec.count() > 0 && elapsed > embedMaxWaitSec) {
throw std::runtime_error(
"Embedding wait exceeded hard limit (" +
std::to_string(embedMaxWaitSec.count()) +
"s) before benchmark queries (embed_svc_queued=" +
std::to_string(observedSnapshot.queueDepth) +
", embed_in_flight=" + std::to_string(observedSnapshot.inFlight) +
", docs_embedded=" + std::to_string(observedSnapshot.docsEmbedded) +
", vectors=" + std::to_string(observedSnapshot.vectorCount) +
", dropped=" + std::to_string(observedSnapshot.dropped) +
"). Increase YAMS_BENCH_EMBED_MAX_WAIT or tune embedding concurrency.");
}
const bool inferLikelyActive =
observedSnapshot.inferLikelyActive(embedProgressTimeoutSec);
if (sinceProgress > embedProgressTimeoutSec && !inferLikelyActive) {
throw std::runtime_error(
"Embedding wait stalled for " +
std::to_string(embedProgressTimeoutSec.count()) +
"s before benchmark queries (embed_svc_queued=" +
std::to_string(observedSnapshot.queueDepth) +
", embed_in_flight=" + std::to_string(observedSnapshot.inFlight) +
", docs_embedded=" + std::to_string(observedSnapshot.docsEmbedded) +
", vectors=" + std::to_string(observedSnapshot.vectorCount) +
", dropped=" + std::to_string(observedSnapshot.dropped) +
"). Increase timeout/concurrency or investigate model/provider "
"stalls.");
}
if (now >= embedHeartbeatAt) {
embedHeartbeatAt = now + std::chrono::seconds(5);
spdlog::info("Embed wait heartbeat: elapsed={}s since_progress={}s "
"last_docs_embedded={} last_vectors={} queue={} in_flight={} "
"infer_active={} infer_oldest_ms={} infer_started={} "
"infer_completed={}",
elapsed.count(), sinceProgress.count(),
observedSnapshot.docsEmbedded, observedSnapshot.vectorCount,
observedSnapshot.queueDepth, observedSnapshot.inFlight,
observedSnapshot.inferActive, observedSnapshot.inferOldestMs,
observedSnapshot.inferStarted,
observedSnapshot.inferCompleted);
}
auto statusResult = benchRunSync(client->status(true), 5s);
if (statusResult) {
const auto snapshot = collectEmbedSnapshot(statusResult.value());
const bool metricsChanged =
!haveObservedSnapshot ||
progressSignalChanged(snapshot, observedSnapshot);
observedSnapshot = snapshot;
haveObservedSnapshot = true;
if (metricsChanged) {
lastEmbedProgressTime = now;
stableCount = 0;
} else {
stableCount++;
}
if (!haveProgressSnapshot ||
observedSnapshot.vectorCount != lastProgressSnapshot.vectorCount ||
observedSnapshot.docsEmbedded != lastProgressSnapshot.docsEmbedded) {
const double docCoverage = observedSnapshot.coverageFor(corpusSize);
const double preparedAvgChunksPerDoc =
observedSnapshot.avgChunksPerDoc();
spdlog::info(
"Embedding progress: docs={} / {} ({:.1f}%) vectors={} "
"prepared_docs={} prepared_chunks={} avg_chunks_per_doc={:.2f} "
"| queue={} in_flight={} dropped={}",
observedSnapshot.docsEmbedded, corpusSize, docCoverage,
observedSnapshot.vectorCount, observedSnapshot.preparedDocs,
observedSnapshot.preparedChunks, preparedAvgChunksPerDoc,
observedSnapshot.queueDepth, observedSnapshot.inFlight,
observedSnapshot.dropped);
// Log progress to summary file
if (summaryLog) {
auto elapsed =
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - embedStartTime)
.count();
summaryLog << "[" << elapsed
<< "ms] DocsEmbedded: " << observedSnapshot.docsEmbedded
<< " / " << corpusSize << " (" << std::fixed
<< std::setprecision(1) << docCoverage << "%)"
<< " | vectors=" << observedSnapshot.vectorCount
<< " | prepared_docs=" << observedSnapshot.preparedDocs
<< " prepared_chunks=" << observedSnapshot.preparedChunks
<< " avg_chunks_per_doc=" << std::fixed
<< std::setprecision(2) << preparedAvgChunksPerDoc
<< " | queue=" << observedSnapshot.queueDepth
<< " in_flight=" << observedSnapshot.inFlight
<< " dropped=" << observedSnapshot.dropped << std::endl;
summaryLog.flush();
}
lastProgressSnapshot = observedSnapshot;
haveProgressSnapshot = true;
}
// Success condition: embedded docs >= corpusSize AND queue drained.
// vector_count tracks rows (chunks + doc records), not documents.
if (observedSnapshot.hasQueueMetrics &&
observedSnapshot.docsEmbedded >= static_cast<uint64_t>(corpusSize) &&
observedSnapshot.queueDrained()) {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - embedStartTime)
.count();
spdlog::info(
"Full embedding coverage achieved: {} embedded docs for {} "
"target docs (queue drained, vector_db_ready={})",
observedSnapshot.docsEmbedded, corpusSize,
(observedSnapshot.vectorDbReady ? "true" : "false"));
if (summaryLog) {
summaryLog << "\n*** SUCCESS: Full coverage in " << elapsed
<< "ms ***" << std::endl;
summaryLog
<< "Final embedded docs: " << observedSnapshot.docsEmbedded
<< std::endl;
summaryLog << "Final vectors: " << observedSnapshot.vectorCount
<< std::endl;
summaryLog << "Total dropped: " << observedSnapshot.dropped
<< std::endl;
summaryLog.flush();
}
embeddingDrainSatisfied = true;
break;
}
// If very stable (20s) with queue drained, we're done even if < corpusSize
// This handles cases where some docs have no content to embed
// IMPORTANT: Require minimum 90% coverage to prevent premature exit from
// transient queue drain (e.g., between embedding batches)
const double coverage = observedSnapshot.coverageFor(corpusSize);
constexpr double MIN_COVERAGE_THRESHOLD = 90.0;
if (observedSnapshot.hasQueueMetrics && stableCount >= 40 &&
observedSnapshot.queueDrained() && observedSnapshot.docsEmbedded > 0 &&
coverage >= MIN_COVERAGE_THRESHOLD) {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - embedStartTime)
.count();
spdlog::info(
"Embedding generation complete (queue drained, stable): {} "
"embedded docs ({:.1f}% coverage, vector_db_ready={})",
observedSnapshot.docsEmbedded, coverage,
(observedSnapshot.vectorDbReady ? "true" : "false"));
if (summaryLog) {
summaryLog << "\n*** Complete (queue drained) in " << elapsed
<< "ms ***" << std::endl;
summaryLog
<< "Final embedded docs: " << observedSnapshot.docsEmbedded
<< " (" << coverage << "%)" << std::endl;
summaryLog << "Final vectors: " << observedSnapshot.vectorCount
<< std::endl;
summaryLog.flush();
}
embeddingDrainSatisfied = true;
break;
}
if (observedSnapshot.hasQueueMetrics && stableCount >= 40 &&
observedSnapshot.queueDrained() && observedSnapshot.docsEmbedded == 0) {
throw std::runtime_error(
"Embedding failed before benchmark queries (queue "
"drained, no documents "
"embedded, dropped=" +
std::to_string(observedSnapshot.dropped) +
"). Check embedding provider/model availability.");
}
if (observedSnapshot.hasQueueMetrics && observedSnapshot.queueDrained() &&
observedSnapshot.docsEmbedded == 0 &&
elapsed > std::chrono::seconds(60)) {
throw std::runtime_error(
"Embedding appears unavailable for this candidate (queue drained, "
"no "
"documents embedded after " +
std::to_string(elapsed.count()) +
"s, dropped=" + std::to_string(observedSnapshot.dropped) +
"). Aborting candidate to keep optimization loop progressing.");
}
// If stable but queue NOT drained, warn about potential issues
if (observedSnapshot.hasQueueMetrics && stableCount >= 20 &&
!observedSnapshot.queueDrained() &&
!observedSnapshot.inferLikelyActive(embedProgressTimeoutSec) &&
observedSnapshot.docsEmbedded > 0) {
spdlog::warn("Embedding stalled at {:.1f}% but queue not drained "
"(queue={}, in_flight={}). Continuing to wait...",
coverage, observedSnapshot.queueDepth,
observedSnapshot.inFlight);
// Don't break - keep waiting for queue to drain
}
}
std::this_thread::sleep_for(500ms);
}
if (auto* daemon = harness->daemon()) {
if (auto* serviceManager = daemon->getServiceManager()) {
auto vectorDb = serviceManager->getVectorDatabase();
auto metadataRepo = serviceManager->getMetadataRepo();
if (vectorDb && metadataRepo) {
auto embeddedHashes = vectorDb->getEmbeddedDocumentHashes();
if (embeddedHashes.size() > observedSnapshot.docsEmbedded) {
std::vector<std::string> reconciledHashes;
reconciledHashes.reserve(embeddedHashes.size());
for (const auto& hash : embeddedHashes) {
reconciledHashes.push_back(hash);
}
auto reconcileResult =
metadataRepo->batchUpdateDocumentEmbeddingStatusByHashes(
reconciledHashes, true,
serviceManager->getEmbeddingModelName());
if (!reconcileResult) {
spdlog::warn("Failed to reconcile embedded-doc status from "
"vector rows: {}",
reconcileResult.error().message);
} else {
spdlog::info("Reconciled embedded-doc status from vector rows: "
"{} -> {}",
observedSnapshot.docsEmbedded,
reconciledHashes.size());
observedSnapshot.docsEmbedded = reconciledHashes.size();
lastProgressSnapshot.docsEmbedded = reconciledHashes.size();
}
}
}
}
}
// Final status check
auto finalStatus = benchRunSync(client->status(true), 5s);
if (finalStatus) {
uint64_t finalVectorCount = 0;
auto it = finalStatus.value().requestCounts.find("vector_count");
if (it != finalStatus.value().requestCounts.end()) {
finalVectorCount = it->second;
}
uint64_t finalDocsEmbedded = 0;
auto itDocsEmbedded =
finalStatus.value().requestCounts.find("documents_embedded");
if (itDocsEmbedded != finalStatus.value().requestCounts.end()) {
finalDocsEmbedded = itDocsEmbedded->second;
}
uint64_t finalEmbedQueued = 0;
auto itQ = finalStatus.value().requestCounts.find("embed_svc_queued");
if (itQ != finalStatus.value().requestCounts.end()) {
finalEmbedQueued = itQ->second;
}
uint64_t finalEmbedInFlight = 0;
auto itInFlight = finalStatus.value().requestCounts.find("embed_in_flight");
if (itInFlight != finalStatus.value().requestCounts.end()) {
finalEmbedInFlight = itInFlight->second;
}
double finalCoverage =
corpusSize > 0 ? (finalDocsEmbedded * 100.0 / corpusSize) : 0;
bool vectorDbReady = finalStatus.value().vectorDbReady;
if (auto it = finalStatus.value().readinessStates.find("vector_db");
it != finalStatus.value().readinessStates.end()) {
vectorDbReady = it->second;
}
spdlog::info("Vector DB: ready={} dim={}, docs_embedded={} vectors={} "
"({:.1f}% doc coverage)",
(vectorDbReady ? "true" : "false"),
finalStatus.value().vectorDbDim, finalDocsEmbedded,
finalVectorCount, finalCoverage);
if (!embeddingDrainSatisfied) {
const bool queueDrained =
(finalEmbedQueued == 0 && finalEmbedInFlight == 0);
if (!queueDrained) {
throw std::runtime_error(
"Embedding drain not reached before benchmark queries "
"(embed_svc_queued=" +
std::to_string(finalEmbedQueued) +
", embed_in_flight=" + std::to_string(finalEmbedInFlight) +
", docs_embedded=" + std::to_string(finalDocsEmbedded) +
", vectors=" + std::to_string(finalVectorCount) +
"). Increase timeout/concurrency or reduce corpus size to obtain "
"valid "
"results.");
}
// Queue may only be fully drained on the final status check.
embeddingDrainSatisfied = true;
}
if (finalDocsEmbedded == 0) {
spdlog::warn("No embedded documents present after embedding wait");
} else if (finalCoverage < 90.0) {
spdlog::error("WARNING: Low document embedding coverage ({:.1f}%) will "
"degrade retrieval "
"quality!",
finalCoverage);
spdlog::error(
"Consider reducing corpus size or increasing embed channel capacity.");
}
if (!vectorDbReady) {
spdlog::warn("Vector DB not ready yet; waiting briefly before queries...");
const auto guardDeadline = std::chrono::steady_clock::now() + 30s;
while (std::chrono::steady_clock::now() < guardDeadline) {
auto st = benchRunSync(client->status(true), 5s);
if (st) {
bool ready = st.value().vectorDbReady;
if (auto it = st.value().readinessStates.find("vector_db");
it != st.value().readinessStates.end()) {
ready = it->second;
}
if (ready) {
spdlog::info("Vector DB is now ready after guard wait");
break;
}
}
std::this_thread::sleep_for(500ms);
}
}
} else {
spdlog::warn("Final status check failed after embedding wait: {}",
finalStatus.error().message);
if (!embeddingDrainSatisfied) {
throw std::runtime_error("Final status check failed after embedding wait; "
"cannot verify embedding drain. "
"Benchmark results would be unreliable.");
}
}
} else {
spdlog::info("Vectors disabled - skipping embedding generation wait");
}
} else {
spdlog::info("[Bench] Warm data dir: skipping ingestion + embedding wait (usable "
"vector index already present)");
} // end if (!warmDataDir) / else
bool benchmarkVectorIndexReusableAfter = vectorsDisabled;
auto ensureVectorIndexReady = [&]() -> bool {
if (vectorsDisabled) {
return true;
}
const fs::path vectorIndexDataDir =
harnessOptions.dataDir.has_value() ? *harnessOptions.dataDir : harness->dataDir();
auto* daemon = harness ? harness->daemon() : nullptr;
auto* serviceManager = daemon ? daemon->getServiceManager() : nullptr;
auto vectorDb = serviceManager ? serviceManager->getVectorDatabase() : nullptr;
if (!vectorDb || !vectorDb->isInitialized()) {
spdlog::warn("[Bench] Cannot ensure vector index readiness: vector DB unavailable");
return false;
}
const auto probeBackendReusable = [&]() {
const auto probeStart = std::chrono::steady_clock::now();
const bool reusable = vectorDb->hasReusablePersistedSearchIndex();
setupTimings.vectorIndexReusableProbeMs +=
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - probeStart)
.count();
++setupTimings.vectorIndexReusableProbeCount;
return reusable;
};
auto persisted = vectorIndexDataDir.empty()
? PersistedHnswState{}
: inspectPersistedHnswState(vectorIndexDataDir);
const auto benchmarkEngine = benchmarkVectorSearchEngine();
const bool backendReusableBefore = probeBackendReusable();
setupTimings.vectorIndexReusableBefore =
yams::bench::benchmarkSearchIndexReusable(benchmarkEngine, backendReusableBefore);
const auto updateVectorIndexReadiness = [&](bool ready) {
if (!daemon) {
return;
}
daemon->state_.readiness.vectorIndexReady.store(ready, std::memory_order_relaxed);
daemon->state_.readiness.vectorIndexProgress.store(
ready ? 100
: (daemon->state_.readiness.vectorDbReady.load(std::memory_order_relaxed)
? 50
: 0),
std::memory_order_relaxed);
};
const auto vectorRows = vectorDb->getVectorCount();
if (vectorRows == 0) {
spdlog::warn("[Bench] Cannot ensure vector index readiness: vector DB is empty");
return false;
}
const bool indexlessExact =
benchmarkEngine == yams::vector::VectorSearchEngine::ExactScan;
bool preparedThisRun = false;
if (!indexlessExact && !setupTimings.vectorIndexReusableBefore) {
const char* modeLabel = warmDataDirPath.empty() ? "cold-run" : "warm-cache";
spdlog::info("[Bench] Finalizing {} vector index (vectors={} persisted_nodes={} "
"persisted_meta={})",
modeLabel, vectorRows, persisted.nodeRows, persisted.metaRows);
const auto start = std::chrono::steady_clock::now();
const bool prepared = vectorDb->prepareSearchIndex();
const auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start)
.count();
setupTimings.vectorIndexPrepared = prepared;
setupTimings.vectorIndexPrepareMs = elapsedMs;
if (!prepared) {
spdlog::warn("[Bench] Vector index finalize failed after {} ms: {}", elapsedMs,
vectorDb->getLastError());
return false;
}
preparedThisRun = true;
const auto persistStart = std::chrono::steady_clock::now();
const bool saved = vectorDb->persistIndex();
setupTimings.vectorIndexPersistMs =
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - persistStart)
.count();
setupTimings.vectorIndexPersisted = saved;
if (!saved) {
spdlog::warn("[Bench] Vector index persist failed after {} ms: {}",
setupTimings.vectorIndexPersistMs, vectorDb->getLastError());
return false;
}
persisted = vectorIndexDataDir.empty()
? PersistedHnswState{}
: inspectPersistedHnswState(vectorIndexDataDir);
const bool backendReusableAfterPersist = probeBackendReusable();
if (yams::bench::benchmarkSearchIndexReusable(benchmarkEngine,
backendReusableAfterPersist)) {
spdlog::info("[Bench] Vector index finalized in {} ms "
"(persisted_nodes={} persisted_meta={})",
elapsedMs, persisted.nodeRows, persisted.metaRows);
} else if (benchmarkEngine == yams::vector::VectorSearchEngine::SimeonPqAdc) {
spdlog::info("[Bench] Vector index finalized in {} ms for {} "
"(persisted PQ rows not required for this cold-run process)",
elapsedMs, yams::vector::vectorSearchEngineName(benchmarkEngine));
} else if (benchmarkEngine == yams::vector::VectorSearchEngine::Vec0L2) {
spdlog::info("[Bench] Vec0 index finalized in {} ms and is query-ready for "
"this process (restart persistence unavailable)",
elapsedMs);
} else {
spdlog::info("[Bench] Exact-scan engine query-ready after {} ms; no persisted "
"search index is required",
elapsedMs);
}
}
bool reusable =
yams::bench::benchmarkSearchIndexReusable(benchmarkEngine, probeBackendReusable());
bool ready = yams::bench::benchmarkSearchIndexQueryReady(benchmarkEngine, reusable,
preparedThisRun);
benchmarkVectorIndexReusableAfter = reusable;
setupTimings.vectorIndexReusableAfter = reusable;
setupTimings.vectorQueryReadyAfter = ready;
updateVectorIndexReadiness(ready);
int readyTimeoutSec = 60;
if (const char* env = std::getenv("YAMS_BENCH_VECTOR_INDEX_READY_TIMEOUT")) {
readyTimeoutSec = std::max(0, std::stoi(env));
}
const auto readyDeadline =
std::chrono::steady_clock::now() + std::chrono::seconds(readyTimeoutSec);
while (!ready && std::chrono::steady_clock::now() < readyDeadline) {
if (!vectorIndexDataDir.empty()) {
persisted = inspectPersistedHnswState(vectorIndexDataDir);
reusable = yams::bench::benchmarkSearchIndexReusable(benchmarkEngine,
probeBackendReusable());
}
ready = yams::bench::benchmarkSearchIndexQueryReady(benchmarkEngine, reusable,
preparedThisRun);
if (!ready) {
std::this_thread::sleep_for(500ms);
}
}
benchmarkVectorIndexReusableAfter = reusable;
setupTimings.vectorIndexReusableAfter = reusable;
setupTimings.vectorQueryReadyAfter = ready;
updateVectorIndexReadiness(ready);
return ready;
};
if (!ensureVectorIndexReady()) {
throw std::runtime_error("Vector index readiness not reached before benchmark queries; "
"benchmark would mix brute-force fallback with ANN timing.");
}
if (!warmDataDirPath.empty()) {
auto finalCacheMetadata =
warmCacheMetadata.has_value()
? preferStrongerBenchCacheMetadata(expectedCacheMetadata, warmCacheMetadata)
: expectedCacheMetadata;
auto finalStatusForCache = benchRunSync(client->status(true), 5s);
if (finalStatusForCache) {
finalCacheMetadata =
currentBenchCacheMetadata(finalCacheMetadata, finalStatusForCache.value(),
warmDataDirPath, benchmarkVectorIndexReusableAfter);
}
finalCacheMetadata =
preferStrongerBenchCacheMetadata(finalCacheMetadata, warmCacheMetadata);
if (!expectedCacheMetadata.datasetPath.empty() &&
finalCacheMetadata.datasetPath.empty()) {
finalCacheMetadata.datasetPath = expectedCacheMetadata.datasetPath;
}
const auto metadataWrite = writeBenchCacheMetadata(warmDataDirPath, finalCacheMetadata);
if (!metadataWrite) {
spdlog::warn("[Bench] Failed to write warm cache metadata for {}: {}",
warmDataDirPath.string(), metadataWrite.error().message);
} else {
spdlog::info(
"[Bench] Warm cache metadata written to {} (status={}, embedded_docs={}, "
"vectors={})",
(warmDataDirPath / "retrieval_bench_cache.json").string(),
finalCacheMetadata.status, finalCacheMetadata.embeddedDocs,
finalCacheMetadata.vectorCount);
}
}
// Wait for search engine to be ready (critical for hybrid search!)
// Without this, all hybrid searches fall back to keyword-only
spdlog::info("Waiting for hybrid/vector search readiness...");
const auto searchEngineDeadline = std::chrono::steady_clock::now() + 60s;
bool searchEngineReady = false;
while (std::chrono::steady_clock::now() < searchEngineDeadline) {
auto statusCheck = benchRunSync(client->status(true), 5s);
if (statusCheck) {
const auto& readiness = statusCheck.value().readinessStates;
bool hybridReady = false;
if (auto it = readiness.find("search_engine_hybrid_usable");
it != readiness.end()) {
hybridReady = it->second;
} else {
const bool lexicalReady = [&]() {
if (auto it = readiness.find("search_engine_lexical");
it != readiness.end()) {
return it->second;
}
if (auto it = readiness.find("search_engine"); it != readiness.end()) {
return it->second;
}
return false;
}();
const bool vectorReady = [&]() {
if (auto it = readiness.find("search_engine_vector_usable");
it != readiness.end()) {
return it->second;
}
if (auto it = readiness.find("vector_db_ready"); it != readiness.end()) {
return it->second;
}
if (auto it = readiness.find("vector_db"); it != readiness.end()) {
return it->second;
}
return statusCheck.value().vectorDbReady;
}();
hybridReady = lexicalReady && vectorReady;
}
if (hybridReady) {
searchEngineReady = true;
spdlog::info("Hybrid search is ready (lexical + vector usable)");
break;
}
}
std::this_thread::sleep_for(500ms);
}
if (!searchEngineReady) {
spdlog::error(
"Hybrid/vector search not ready after 60s - hybrid searches will fall back to "
"keyword-only!");
spdlog::error("This is likely due to vector publication not completing.");
}
// Wait for Simeon lexical enhancement and concept mining to complete.
// The async build thread can still be running after the search engine reports
// ready, and concept mining can take additional wall-clock time.
if (!vectorsDisabled) {
const bool waitForConcepts =
std::getenv("YAMS_SEARCH_WAIT_FOR_CONCEPTS") &&
std::string(std::getenv("YAMS_SEARCH_WAIT_FOR_CONCEPTS")) != "0";
if (waitForConcepts) {
const auto lexicalReadyDeadline = std::chrono::steady_clock::now() + 120s;
bool lexicalReady = false;
spdlog::info("Waiting for Simeon lexical enhancement readiness...");
while (std::chrono::steady_clock::now() < lexicalReadyDeadline) {
auto statusCheck = benchRunSync(client->status(true), 5s);
if (statusCheck) {
const auto& rc = statusCheck.value().requestCounts;
const auto get = [&](const std::string& key) -> uint64_t {
auto it = rc.find(key);
return (it == rc.end()) ? 0ULL : it->second;
};
const bool configured =
get("status_search_engine_lexical_enhancement_configured") > 0;
const bool ready =
get("status_search_engine_lexical_enhancement_ready") > 0;
const bool building =
get("status_search_engine_lexical_enhancement_building") > 0;
const uint64_t docCount =
get("status_search_engine_lexical_enhancement_doc_count");
const uint64_t conceptCount =
get("status_search_engine_lexical_enhancement_concept_count");
const bool conceptMiningEnabled =
get("status_search_engine_lexical_enhancement_concept_mining_enabled") >
0;
if (!configured) {
spdlog::info("Simeon lexical enhancement not configured; skipping "
"lexical readiness wait");
lexicalReady = true;
break;
}
if (ready && !building && docCount >= static_cast<uint64_t>(corpusSize)) {
if (conceptMiningEnabled && corpusSize >= 100 && conceptCount == 0) {
spdlog::info("Simeon lexical ready (docs={}) but concept mining "
"still pending; waiting...",
docCount);
} else {
spdlog::info("Simeon lexical enhancement ready: configured={} "
"ready={} building={} doc_count={} concept_count={} "
"concept_mining_enabled={}",
configured, ready, building, docCount, conceptCount,
conceptMiningEnabled);
lexicalReady = true;
break;
}
} else {
spdlog::info("Simeon lexical enhancement not yet ready: "
"configured={} ready={} building={} doc_count={} "
"concept_count={}",
configured, ready, building, docCount, conceptCount);
}
}
std::this_thread::sleep_for(500ms);
}
if (!lexicalReady) {
spdlog::error("Simeon lexical enhancement not ready after 120s");
}
}
}
if (coldIngestStartedAt.has_value()) {
setupTimings.searchabilityReadyMs = elapsedMsSince(*coldIngestStartedAt);
}
auto effectiveKgReadinessPolicy = kgReadinessPolicy;
if (kgReadinessPolicy != yams::search::BenchmarkKgReadinessPolicy::Skip) {
auto statusForKgPolicy = benchRunSync(client->status(true), 5s);
if (statusForKgPolicy) {
bool tunedGraphRerank = graphRerankRequested;
if (auto it =
statusForKgPolicy.value().searchTuningParams.find("enable_graph_rerank");
it != statusForKgPolicy.value().searchTuningParams.end()) {
tunedGraphRerank = it->second > 0.5f;
}
effectiveKgReadinessPolicy = yams::search::refineBenchmarkKgReadinessPolicy(
kgReadinessPolicy, explicitRequireKgReady, tunedGraphRerank);
if (effectiveKgReadinessPolicy != kgReadinessPolicy) {
spdlog::info("[Bench] Refined KG readiness policy to {} after search tuning "
"(state='{}', enable_graph_rerank={})",
yams::search::benchmarkKgReadinessPolicyToString(
effectiveKgReadinessPolicy),
statusForKgPolicy.value().searchTuningState,
tunedGraphRerank ? "true" : "false");
}
} else {
spdlog::warn("[Bench] Unable to inspect tuned graph rerank before KG wait: {}",
statusForKgPolicy.error().message);
}
}
if (effectiveKgReadinessPolicy != yams::search::BenchmarkKgReadinessPolicy::Skip) {
int kgReadyTimeoutSec = 180;
if (const char* env = std::getenv("YAMS_BENCH_KG_READY_TIMEOUT")) {
kgReadyTimeoutSec = std::max(0, std::stoi(env));
}
spdlog::info("Waiting for KG readiness before query evaluation (timeout={}s)...",
kgReadyTimeoutSec);
const auto kgDeadline =
std::chrono::steady_clock::now() + std::chrono::seconds(kgReadyTimeoutSec);
int stickyInflightGraceSec = 20;
if (const char* env = std::getenv("YAMS_BENCH_KG_STICKY_INFLIGHT_GRACE_SEC")) {
stickyInflightGraceSec = std::max(0, std::stoi(env));
}
const auto stickyInflightGrace = std::chrono::seconds(stickyInflightGraceSec);
int stableReadyChecks = 0;
bool kgReady = false;
bool acceptedStickyInflight = false;
bool acceptedWarmReuseIdle = false;
bool lastHasKg = false;
uint32_t lastQueueDepth = 0;
uint64_t lastPostQueued = 0;
uint64_t lastPostInflight = 0;
uint64_t lastKgInflight = 0;
uint64_t lastKgConsumed = 0;
uint64_t lastSymbolInflight = 0;
uint64_t lastEntityInflight = 0;
uint64_t lastTitleInflight = 0;
uint64_t lastTitleQueueDepth = 0;
uint64_t lastExtractionInflight = 0;
bool stickyInflightActive = false;
uint64_t stickyInflightValue = 0;
auto stickyInflightSince = std::chrono::steady_clock::time_point{};
while (std::chrono::steady_clock::now() < kgDeadline) {
const auto now = std::chrono::steady_clock::now();
auto statusCheck = benchRunSync(client->status(true), 5s);
auto statsCheck =
benchRunSync(client->getStats(yams::daemon::GetStatsRequest{}), 10s);
bool queuesDrained = false;
if (statusCheck) {
const auto& st = statusCheck.value();
const auto getCount = [&st](const std::string& key) -> uint64_t {
auto it = st.requestCounts.find(key);
return (it == st.requestCounts.end()) ? 0ULL : it->second;
};
lastQueueDepth = st.postIngestQueueDepth;
lastPostQueued = getCount("post_ingest_queued");
lastPostInflight = getCount("post_ingest_inflight");
lastExtractionInflight = getCount("extraction_inflight");
lastKgInflight = getCount("kg_inflight");
lastKgConsumed = getCount("kg_consumed");
lastSymbolInflight = getCount("symbol_inflight");
lastEntityInflight = getCount("entity_inflight");
lastTitleInflight = getCount("title_inflight");
lastTitleQueueDepth = getCount("title_queue_depth");
queuesDrained =
(lastQueueDepth == 0 && lastPostQueued == 0 && lastPostInflight == 0 &&
lastExtractionInflight == 0 && lastKgInflight == 0 &&
lastSymbolInflight == 0 && lastEntityInflight == 0 &&
lastTitleInflight == 0 && lastTitleQueueDepth == 0);
}
bool hasKg = false;
uint64_t docEntitiesInserted = 0;
if (statsCheck) {
const auto& stats = statsCheck.value();
if (auto it = stats.additionalStats.find("corpus_stats");
it != stats.additionalStats.end() && !it->second.empty()) {
try {
auto corpusStats = json::parse(it->second);
if (corpusStats.contains("classification") &&
corpusStats["classification"].is_object()) {
hasKg = corpusStats["classification"].value("has_kg", false);
}
} catch (const json::exception&) {
}
}
if (auto it = stats.additionalStats.find("kg_write_doc_entities_inserted");
it != stats.additionalStats.end() && !it->second.empty()) {
docEntitiesInserted = static_cast<uint64_t>(std::stoull(it->second));
}
}
lastHasKg = hasKg;
const bool kgSignalReady =
hasKg || (lastKgConsumed > 0) || (docEntitiesInserted > 0);
const bool queueIdle = (lastQueueDepth == 0 && lastPostQueued == 0);
const bool stageInflightIdle =
(lastExtractionInflight == 0 && lastKgInflight == 0 &&
lastSymbolInflight == 0 && lastEntityInflight == 0 && lastTitleInflight == 0 &&
lastTitleQueueDepth == 0);
bool stickyInflightReady = false;
if (kgSignalReady && queueIdle && stageInflightIdle && lastPostInflight > 0) {
if (!stickyInflightActive || stickyInflightValue != lastPostInflight) {
stickyInflightActive = true;
stickyInflightValue = lastPostInflight;
stickyInflightSince = now;
} else if (now - stickyInflightSince >= stickyInflightGrace) {
stickyInflightReady = true;
}
} else {
stickyInflightActive = false;
stickyInflightValue = 0;
}
const bool reusedWarmCacheWithoutIngest =
usingExternalBenchDataDir && cacheHasIndexedCorpus && cacheHasFullEmbeddings &&
cacheHasReusableVectorIndex;
const bool warmReuseIdleReady =
reusedWarmCacheWithoutIngest && queuesDrained && lastPostInflight == 0;
if (yams::search::benchmarkKgReadinessSatisfied(effectiveKgReadinessPolicy,
queuesDrained, kgSignalReady)) {
stableReadyChecks++;
if (stableReadyChecks >= 4) {
kgReady = true;
break;
}
} else if (warmReuseIdleReady) {
kgReady = true;
acceptedWarmReuseIdle = true;
break;
} else if (stickyInflightReady) {
kgReady = true;
acceptedStickyInflight = true;
break;
} else {
stableReadyChecks = 0;
}
std::this_thread::sleep_for(500ms);
}
if (!kgReady) {
throw std::runtime_error(
"KG not ready before benchmark queries (has_kg=" +
std::string(lastHasKg ? "true" : "false") + ", policy=" +
std::string(yams::search::benchmarkKgReadinessPolicyToString(
effectiveKgReadinessPolicy)) +
", kg_consumed=" + std::to_string(lastKgConsumed) +
", post_ingest_queue_depth=" + std::to_string(lastQueueDepth) +
", post_ingest_queued=" + std::to_string(lastPostQueued) +
", post_ingest_inflight=" + std::to_string(lastPostInflight) +
", extraction_inflight=" + std::to_string(lastExtractionInflight) +
", kg_inflight=" + std::to_string(lastKgInflight) +
", symbol_inflight=" + std::to_string(lastSymbolInflight) +
", entity_inflight=" + std::to_string(lastEntityInflight) +
", title_inflight=" + std::to_string(lastTitleInflight) +
", title_queue_depth=" + std::to_string(lastTitleQueueDepth) +
"). Increase YAMS_BENCH_KG_READY_TIMEOUT or reduce ingestion load.");
}
if (effectiveKgReadinessPolicy ==
yams::search::BenchmarkKgReadinessPolicy::AcceptDrainedWithoutSignal &&
!lastHasKg && lastKgConsumed == 0) {
spdlog::warn(
"KG readiness satisfied with drained queues under relaxed synthetic policy; "
"no explicit KG signal was observed. Graph signals may be weak.");
} else if (!lastHasKg) {
if (acceptedWarmReuseIdle) {
spdlog::warn(
"KG readiness accepted for reused warm cache with drained queues and no "
"active KG work, but corpus_stats.has_kg=false and kg_consumed={} in this "
"process. Graph signals may still be weak.",
lastKgConsumed);
} else {
spdlog::warn(
"KG readiness satisfied via kg_consumed={}, but corpus_stats.has_kg=false "
"(low symbol_density). Graph signals may still be weak.",
lastKgConsumed);
}
} else if (acceptedStickyInflight) {
spdlog::warn(
"KG readiness accepted with sticky post_ingest_inflight={} after {}s grace "
"(queues drained + stage inflight idle + corpus_stats.has_kg=true).",
lastPostInflight, stickyInflightGraceSec);
} else if (acceptedWarmReuseIdle) {
spdlog::info(
"KG readiness accepted for reused warm cache with drained queues and no "
"active KG work");
} else {
spdlog::info("KG readiness satisfied (queues drained + corpus_stats.has_kg=true)");
}
} else if (requireKgReady) {
spdlog::info(
"Skipping KG readiness wait because the effective tuned search policy disables "
"graph rerank.");
}
yams::search::BenchmarkDaemonGraphState daemonGraphState;
bool daemonGraphReady = false;
bool daemonGraphCanonicalized = false;
std::size_t daemonGraphCanonicalEdgesGenerated = 0;
std::string daemonGraphCanonicalTopologySnapshot;
std::string daemonGraphCanonicalTopologyFingerprint;
if (g_requireDaemonNativeGraph) {
auto graphState =
waitForDaemonNativeGraph(*client, static_cast<std::uint64_t>(corpusSize));
if (!graphState) {
throw std::runtime_error(graphState.error().message);
}
daemonGraphState = std::move(graphState.value());
auto* daemon = harness ? harness->daemon() : nullptr;
auto* serviceManager = daemon ? daemon->getServiceManager() : nullptr;
if (!serviceManager) {
throw std::runtime_error(
"Daemon-native graph canonicalization requires ServiceManager");
}
auto semanticRebuild = serviceManager->rebuildSemanticNeighborGraph(
"benchmark_daemon_native_graph_canonicalization");
if (!semanticRebuild) {
throw std::runtime_error("Failed to canonicalize daemon semantic graph: " +
semanticRebuild.error().message);
}
daemonGraphCanonicalEdgesGenerated = semanticRebuild.value();
auto canonicalGraphState =
waitForDaemonNativeGraph(*client, static_cast<std::uint64_t>(corpusSize));
if (!canonicalGraphState) {
throw std::runtime_error(canonicalGraphState.error().message);
}
daemonGraphState = std::move(canonicalGraphState.value());
std::string topologyAlgorithm = "connected";
if (const char* raw = std::getenv("YAMS_BENCH_TOPOLOGY_ENGINE"); raw && *raw) {
topologyAlgorithm = raw;
}
auto topologyRebuild =
rebuildCanonicalDaemonTopology(*serviceManager, topologyAlgorithm);
if (!topologyRebuild) {
throw std::runtime_error("Failed to canonicalize daemon topology artifacts: " +
topologyRebuild.error().message);
}
daemonGraphCanonicalTopologySnapshot = topologyRebuild.value().snapshotId;
auto topologyFingerprint = loadTopologyRoutingConstructionFingerprint(
*serviceManager, daemonGraphCanonicalTopologySnapshot);
if (!topologyFingerprint) {
throw std::runtime_error(
"Failed to fingerprint canonical daemon topology artifacts: " +
topologyFingerprint.error().message);
}
daemonGraphCanonicalTopologyFingerprint = std::move(topologyFingerprint.value());
daemonGraphCanonicalized = true;
daemonGraphReady = true;
}
if (coldIngestStartedAt.has_value()) {
setupTimings.enrichmentReadyMs = elapsedMsSince(*coldIngestStartedAt);
}
// Verify document count using status metrics (avoids degraded search false negatives)
uint64_t indexedDocCount = 0;
auto statusResult = benchRunSync(client->status(true), 5s);
if (warmCacheMetadata && warmCacheMetadata->status == "primed") {
indexedDocCount = static_cast<uint64_t>(warmCacheMetadata->expectedDocs);
} else if (statusResult) {
if (auto it = statusResult.value().requestCounts.find("documents_indexed");
it != statusResult.value().requestCounts.end()) {
indexedDocCount = it->second;
} else if (auto it = statusResult.value().requestCounts.find("documents_total");
it != statusResult.value().requestCounts.end()) {
indexedDocCount = it->second;
}
}
if (indexedDocCount == 0) {
yams::daemon::SearchRequest testReq;
testReq.query = "test";
testReq.searchType = "hybrid";
testReq.limit = 1000;
const auto queryTimeout = configuredBenchQueryTimeout("hybrid");
testReq.timeout = queryTimeout;
auto waitBudget = queryTimeout + std::chrono::seconds(10);
if (waitBudget < std::chrono::seconds(10)) {
waitBudget = std::chrono::seconds(10);
}
auto testSearch =
[&]() -> boost::asio::awaitable<Result<yams::daemon::SearchResponse>> {
if (benchUseStreamingSearch()) {
co_return co_await client->search(testReq);
}
co_return co_await client->unarySearch(testReq);
};
auto testResult = benchRunSync(testSearch(), waitBudget);
indexedDocCount = testResult ? testResult.value().results.size() : 0;
}
spdlog::info("Verified indexed documents: {} (expected: {})", indexedDocCount, corpusSize);
if (indexedDocCount == 0) {
spdlog::error("NO DOCUMENTS IN INDEX! Ingestion failed completely.");
throw std::runtime_error("No documents indexed - benchmark cannot proceed");
}
seedSyntheticCommunityEdges();
const bool reuseSeededTopologyInputs =
warmDataDir && envTruthy(std::getenv("YAMS_BENCH_REUSE_SEEDED_TOPOLOGY_INPUTS"));
const bool seededBenchmarkTopology =
reuseSeededTopologyInputs || seedBenchmarkSemanticNeighbors();
if (reuseSeededTopologyInputs) {
spdlog::info("Reusing immutable semantic-neighbor topology inputs from warm cache");
}
if (benchmarkSemanticNeighborSeedingEnabled()) {
if (!seededBenchmarkTopology) {
throw std::runtime_error(
"Benchmark topology source seeding did not produce semantic_neighbor edges");
}
auto* daemon = harness ? harness->daemon() : nullptr;
auto* serviceManager = daemon ? daemon->getServiceManager() : nullptr;
if (!serviceManager) {
throw std::runtime_error("Topology source benchmark requires ServiceManager");
}
std::string topologyEngine = "connected";
if (const char* raw = std::getenv("YAMS_BENCH_TOPOLOGY_ENGINE"); raw && *raw) {
topologyEngine = raw;
}
auto& topologyManager = serviceManager->getTopologyManager();
const auto rebuildDeadline = std::chrono::steady_clock::now() + 60s;
std::optional<yams::daemon::TopologyManager::RebuildStats> rebuildStats;
do {
if (topologyManager.isRebuildInProgress()) {
std::this_thread::sleep_for(50ms);
continue;
}
auto attempt = topologyManager.rebuildArtifacts("benchmark_topology_source_seed",
false, {}, topologyEngine);
if (!attempt) {
throw std::runtime_error("Failed to rebuild topology after benchmark source "
"seeding: " +
attempt.error().message);
}
if (!attempt.value().skipped) {
rebuildStats = std::move(attempt.value());
break;
}
std::this_thread::sleep_for(50ms);
} while (std::chrono::steady_clock::now() < rebuildDeadline);
if (!rebuildStats.has_value()) {
throw std::runtime_error(
"Timed out waiting to publish benchmark topology snapshot");
}
if (!rebuildStats->stored) {
throw std::runtime_error(
"Topology source benchmark rebuild did not publish a stored snapshot");
}
spdlog::info("Rebuilt topology after source seeding: source={} clusters={} "
"memberships={} snapshot={}",
std::getenv("YAMS_BENCH_TOPOLOGY_SOURCE")
? std::getenv("YAMS_BENCH_TOPOLOGY_SOURCE")
: "vector",
rebuildStats->clustersBuilt, rebuildStats->membershipsBuilt,
rebuildStats->snapshotId);
auto exportResult = exportTopologyClusters(*serviceManager, rebuildStats->snapshotId);
if (!exportResult) {
throw std::runtime_error("Failed to export topology cluster composition: " +
exportResult.error().message);
}
}
// Post-ingest status/stats snapshot + sanity searches.
// Goal: distinguish "no docs" vs "docs but query returns empty".
{
// Log StatusResponse (it already powers our ingestion waits).
auto statusRes = benchRunSync(client->status(true), 5s);
if (statusRes) {
const auto& st = statusRes.value();
uint64_t documentsTotal = 0;
uint64_t documentsEmbedded = 0;
uint64_t vectorCount = 0;
uint64_t preparedDocsQueued = 0;
uint64_t preparedChunksQueued = 0;
uint64_t titleInflight = 0;
uint64_t titleQueueDepth = 0;
uint64_t titleQueued = 0;
uint64_t titleDropped = 0;
uint64_t titleConsumed = 0;
if (auto it = st.requestCounts.find("documents_total");
it != st.requestCounts.end()) {
documentsTotal = it->second;
}
if (auto it = st.requestCounts.find("documents_embedded");
it != st.requestCounts.end()) {
documentsEmbedded = it->second;
}
if (auto it = st.requestCounts.find("vector_count"); it != st.requestCounts.end()) {
vectorCount = it->second;
}
if (auto it = st.requestCounts.find("bus_embed_prepared_docs_queued");
it != st.requestCounts.end()) {
preparedDocsQueued = it->second;
}
if (auto it = st.requestCounts.find("bus_embed_prepared_chunks_queued");
it != st.requestCounts.end()) {
preparedChunksQueued = it->second;
}
if (auto it = st.requestCounts.find("title_inflight");
it != st.requestCounts.end()) {
titleInflight = it->second;
}
if (auto it = st.requestCounts.find("title_queue_depth");
it != st.requestCounts.end()) {
titleQueueDepth = it->second;
}
if (auto it = st.requestCounts.find("title_queued"); it != st.requestCounts.end()) {
titleQueued = it->second;
}
if (auto it = st.requestCounts.find("title_dropped");
it != st.requestCounts.end()) {
titleDropped = it->second;
}
if (auto it = st.requestCounts.find("title_consumed");
it != st.requestCounts.end()) {
titleConsumed = it->second;
}
// Log search tuning state (epic yams-7ez4)
if (!st.searchTuningState.empty()) {
spdlog::info("Search tuning state: {} ({})", st.searchTuningState,
st.searchTuningReason);
spdlog::info("Search tuning params:");
for (const auto& [k, v] : st.searchTuningParams) {
spdlog::info(" {}: {:.4f}", k, v);
}
}
DebugLogEntry statusEntry;
statusEntry.query = "__post_ingest_status__";
statusEntry.returnedPaths.push_back("documents_total=" +
std::to_string(documentsTotal));
statusEntry.returnedPaths.push_back("documents_embedded=" +
std::to_string(documentsEmbedded));
statusEntry.returnedPaths.push_back("vector_count=" + std::to_string(vectorCount));
statusEntry.returnedPaths.push_back("bus_embed_prepared_docs_queued=" +
std::to_string(preparedDocsQueued));
statusEntry.returnedPaths.push_back("bus_embed_prepared_chunks_queued=" +
std::to_string(preparedChunksQueued));
statusEntry.returnedPaths.push_back("title_inflight=" +
std::to_string(titleInflight));
statusEntry.returnedPaths.push_back("title_queue_depth=" +
std::to_string(titleQueueDepth));
statusEntry.returnedPaths.push_back("title_queued=" + std::to_string(titleQueued));
statusEntry.returnedPaths.push_back("title_dropped=" +
std::to_string(titleDropped));
statusEntry.returnedPaths.push_back("title_consumed=" +
std::to_string(titleConsumed));
statusEntry.returnedPaths.push_back("post_ingest_queue_depth=" +
std::to_string(st.postIngestQueueDepth));
statusEntry.returnedPaths.push_back(std::string("embedding_available=") +
(st.embeddingAvailable ? "true" : "false"));
bool vectorDbReady = st.vectorDbReady;
if (auto it = st.readinessStates.find("vector_db");
it != st.readinessStates.end()) {
vectorDbReady = it->second;
}
statusEntry.returnedPaths.push_back(std::string("vector_db_ready=") +
(vectorDbReady ? "true" : "false"));
statusEntry.returnedPaths.push_back("vector_db_dim=" +
std::to_string(st.vectorDbDim));
statusEntry.returnedPaths.push_back("embedding_backend=" + st.embeddingBackend);
statusEntry.returnedPaths.push_back("embedding_model=" + st.embeddingModel);
// Search tuning state (epic yams-7ez4)
statusEntry.returnedPaths.push_back("search_tuning_state=" + st.searchTuningState);
statusEntry.returnedPaths.push_back("search_tuning_reason=" +
st.searchTuningReason);
double tunedKgWeight = 0.0;
bool tunedGraphRerank = graphRerankRequested;
if (auto it = st.searchTuningParams.find("kg_weight");
it != st.searchTuningParams.end()) {
tunedKgWeight = static_cast<double>(it->second);
}
if (auto it = st.searchTuningParams.find("enable_graph_rerank");
it != st.searchTuningParams.end()) {
tunedGraphRerank = it->second > 0.5;
}
spdlog::info("[Bench] Effective graph rerank: env={} tuned={} kg_weight={:.4f}",
graphRerankRequested ? "on" : "off", tunedGraphRerank ? "on" : "off",
tunedKgWeight);
for (const auto& [k, v] : st.searchTuningParams) {
statusEntry.returnedPaths.push_back("tuning_" + k + "=" + std::to_string(v));
}
debugLogWriteJsonLine(statusEntry);
} else {
spdlog::warn("Status failed (post-ingest): {}", statusRes.error().message);
}
// Log GetStatsResponse (extra counters);
// we also emit structured JSON (additional_stats) for easier parsing.
yams::daemon::GetStatsRequest statsReq;
auto statsRes = benchRunSync(client->getStats(statsReq), 10s);
if (statsRes) {
const auto& st = statsRes.value();
spdlog::info("GetStats: totalDocuments={} indexedDocuments={} vectorIndexSize={} "
"additionalStats.size={}",
st.totalDocuments, st.indexedDocuments, st.vectorIndexSize,
st.additionalStats.size());
DebugLogEntry statsEntry;
statsEntry.query = "__post_ingest_stats__";
statsEntry.returnedPaths.push_back("totalDocuments=" +
std::to_string(st.totalDocuments));
statsEntry.returnedPaths.push_back("indexedDocuments=" +
std::to_string(st.indexedDocuments));
statsEntry.returnedPaths.push_back("vectorIndexSize=" +
std::to_string(st.vectorIndexSize));
const auto appendIfPresent = [&](std::string_view key) {
auto it = st.additionalStats.find(std::string(key));
if (it != st.additionalStats.end()) {
statsEntry.returnedPaths.push_back(std::string(key) + "=" + it->second);
}
};
appendIfPresent("kg_doc_entities_total");
appendIfPresent("post_title_nl_docs_processed");
appendIfPresent("post_title_nl_docs_with_entities");
appendIfPresent("post_title_nl_entities_extracted");
appendIfPresent("post_deferred_doc_entities_queued");
appendIfPresent("post_deferred_doc_entity_queue_failures");
appendIfPresent("kg_write_doc_entities_inserted");
appendIfPresent("kg_write_deferred_doc_entities_skipped");
statsEntry.extraFields = {
{"additional_stats", st.additionalStats},
{"total_documents", st.totalDocuments},
{"indexed_documents", st.indexedDocuments},
{"vector_index_size", st.vectorIndexSize},
};
for (const auto& [k, v] : st.additionalStats) {
statsEntry.returnedPaths.push_back(k + "=" + v);
}
debugLogWriteJsonLine(statsEntry);
} else {
spdlog::warn("GetStats failed: {}", statsRes.error().message);
}
auto doSanitySearch = [&](std::string label, std::string query,
std::string searchType) {
yams::cli::search_runner::DaemonSearchOptions opts;
opts.query = std::move(query);
opts.searchType = std::move(searchType);
opts.limit = 25;
const auto queryTimeout = configuredBenchQueryTimeout(opts.searchType);
opts.timeout = queryTimeout;
opts.allowFuzzyRetry = false;
opts.allowLiteralTextRetry = false;
DebugLogEntry sanityEntry;
sanityEntry.query = std::move(label);
auto waitBudget =
std::chrono::duration_cast<std::chrono::milliseconds>(opts.timeout) +
std::chrono::seconds(10);
if (waitBudget < std::chrono::seconds(10)) {
waitBudget = std::chrono::seconds(10);
}
auto run = benchRunSync(yams::cli::search_runner::daemon_search(
*client, opts, benchUseStreamingSearch()),
waitBudget);
if (!run) {
sanityEntry.returnedPaths.push_back("error=" + run.error().message);
debugLogWriteJsonLine(sanityEntry);
return;
}
sanityEntry.attempts = run.value().attempts;
sanityEntry.usedStreaming = run.value().usedStreaming;
sanityEntry.usedFuzzyRetry = run.value().usedFuzzyRetry;
sanityEntry.usedLiteralTextRetry = run.value().usedLiteralTextRetry;
const auto& results = run.value().response.results;
for (size_t i = 0; i < std::min<size_t>(results.size(), 10); ++i) {
sanityEntry.returnedPaths.push_back(results[i].path);
}
sanityEntry.returnedDocIds.push_back("result_count=" +
std::to_string(results.size()));
debugLogWriteJsonLine(sanityEntry);
};
// Keyword search should return something if FTS is populated.
doSanitySearch("__sanity_keyword_the__", "the", "keyword");
// Hybrid search without symbol rank to avoid symbol-only decisions.
doSanitySearch("__sanity_hybrid_the__", "the", "hybrid");
// If BEIR corpus is used, easily-searchable token is doc id.
if (useBEIR && beirCorpus && !beirCorpus->dataset.documents.empty()) {
const auto& firstDocId = beirCorpus->dataset.documents.begin()->first;
doSanitySearch("__sanity_keyword_docid__", firstDocId, "keyword");
}
}
if (useBEIR) {
queries = beirCorpus->generateTestQueries();
} else {
queries = corpus->generateQueries(numQueries);
}
spdlog::info("Generated {} test queries", queries.size());
setupTimings.setupTotalMs = elapsedMsSince(setupStartedAt);
if (g_debugOut) {
DebugLogEntry timingEntry;
timingEntry.searchType = "benchmark_setup";
timingEntry.extraFields = {
{"event", "benchmark_setup_metrics"},
{"ingestion",
{{"cold_performed", setupTimings.coldIngestPerformed},
{"admission_ms", setupTimings.admissionMs},
{"storage_ready_ms", setupTimings.storageReadyMs},
{"pipeline_drain_ms", setupTimings.pipelineDrainMs},
{"searchability_ready_ms", setupTimings.searchabilityReadyMs},
{"enrichment_ready_ms", setupTimings.enrichmentReadyMs},
{"measurement_n", setupTimings.coldIngestPerformed ? 1 : 0}}},
{"vector_index",
{{"prepared", setupTimings.vectorIndexPrepared},
{"persisted", setupTimings.vectorIndexPersisted},
{"reusable_before", setupTimings.vectorIndexReusableBefore},
{"reusable_after", setupTimings.vectorIndexReusableAfter},
{"query_ready_after", setupTimings.vectorQueryReadyAfter},
{"prepare_ms", setupTimings.vectorIndexPrepareMs},
{"persist_ms", setupTimings.vectorIndexPersistMs},
{"reusable_probe_ms", setupTimings.vectorIndexReusableProbeMs},
{"reusable_probe_count", setupTimings.vectorIndexReusableProbeCount}}},
{"setup_total_ms", setupTimings.setupTotalMs},
{"dataset", datasetName},
{"corpus_size", corpusSize},
{"daemon_graph",
{{"required", g_requireDaemonNativeGraph},
{"ready", daemonGraphReady},
{"source", "post_ingest_queue_embedding_service"},
{"canonicalized", daemonGraphCanonicalized},
{"canonical_edges_generated", daemonGraphCanonicalEdgesGenerated},
{"canonical_topology_snapshot", daemonGraphCanonicalTopologySnapshot},
{"canonical_topology_fingerprint", daemonGraphCanonicalTopologyFingerprint},
{"semantic_documents_processed", daemonGraphState.semanticDocumentsProcessed},
{"semantic_edges_created", daemonGraphState.semanticEdgesCreated},
{"semantic_update_errors", daemonGraphState.semanticUpdateErrors},
{"topology_document_nodes", daemonGraphState.topologyDocumentNodes},
{"topology_semantic_edges", daemonGraphState.topologySemanticEdges},
{"topology_documents_with_neighbors",
daemonGraphState.topologyDocumentsWithNeighbors},
{"topology_isolated_documents", daemonGraphState.topologyIsolatedDocuments},
{"post_ingest_queued", daemonGraphState.postIngestQueued},
{"post_ingest_in_flight", daemonGraphState.postIngestInFlight},
{"embedding_queued", daemonGraphState.embeddingQueued},
{"embedding_in_flight", daemonGraphState.embeddingInFlight},
{"write_queued", daemonGraphState.writeQueued},
{"write_in_flight", daemonGraphState.writeInFlight}}},
};
debugLogWriteJsonLine(timingEntry);
}
}
void teardown() {
client.reset();
harness.reset();
corpus.reset();
g_debugRunContext.reset();
g_debugOut = nullptr;
g_debugFile.reset();
}
};
static std::unique_ptr<BenchFixture> g_fixture;
void SetupFixture() {
if (!g_fixture) {
g_fixture = std::make_unique<BenchFixture>();
g_fixture->setup();
}
}
void CleanupFixture() {
if (g_fixture) {
g_fixture->teardown();
g_fixture.reset();
}
}
static OptimizationRunResult runOptimizationCandidate(const OptimizationCandidate& candidate,
const std::optional<fs::path>& outputFile) {
OptimizationRunResult result;
result.candidate = candidate;
result.runId = makeRunId(candidate.name);
std::vector<EnvSetting> candidateOverrides = candidate.envOverrides;
const char* debugBaseEnv = std::getenv("YAMS_BENCH_DEBUG_FILE");
if (debugBaseEnv && std::strlen(debugBaseEnv) > 0) {
const fs::path basePath(debugBaseEnv);
const fs::path candidateDebugPath =
makeCandidateDebugPath(basePath, candidate.name, result.runId);
result.debugFile = candidateDebugPath.string();
candidateOverrides.push_back({"YAMS_BENCH_DEBUG_FILE", result.debugFile});
const bool hasExplicitTrace = hasOverrideKey(candidateOverrides, "YAMS_SEARCH_STAGE_TRACE");
if (!hasExplicitTrace && std::getenv("YAMS_SEARCH_STAGE_TRACE") == nullptr) {
candidateOverrides.push_back({"YAMS_SEARCH_STAGE_TRACE", "1"});
result.stageTraceEnabled = true;
} else {
result.stageTraceEnabled = envTruthy(std::getenv("YAMS_SEARCH_STAGE_TRACE"));
}
const int traceTopNDefault = parseIntEnvOrDefault("YAMS_BENCH_TRACE_TOP_N", 50, 1, 10000);
const int traceComponentDefault = parseIntEnvOrDefault(
"YAMS_BENCH_TRACE_COMPONENT_TOP_N", std::min(traceTopNDefault, 50), 1, 10000);
const bool hasExplicitTraceTop =
hasOverrideKey(candidateOverrides, "YAMS_SEARCH_STAGE_TRACE_TOP_N");
if (!hasExplicitTraceTop && std::getenv("YAMS_SEARCH_STAGE_TRACE_TOP_N") == nullptr) {
candidateOverrides.push_back(
{"YAMS_SEARCH_STAGE_TRACE_TOP_N", std::to_string(traceTopNDefault)});
}
const bool hasExplicitComponentTop =
hasOverrideKey(candidateOverrides, "YAMS_SEARCH_STAGE_TRACE_COMPONENT_TOP_N");
if (!hasExplicitComponentTop &&
std::getenv("YAMS_SEARCH_STAGE_TRACE_COMPONENT_TOP_N") == nullptr) {
candidateOverrides.push_back(
{"YAMS_SEARCH_STAGE_TRACE_COMPONENT_TOP_N", std::to_string(traceComponentDefault)});
}
} else {
result.stageTraceEnabled = envTruthy(std::getenv("YAMS_SEARCH_STAGE_TRACE"));
}
ScopedEnvOverrides scopedEnv(candidateOverrides);
result.traceTopN = parseIntEnvOrDefault("YAMS_SEARCH_STAGE_TRACE_TOP_N", 0, 0, 10000);
result.traceComponentTopN =
parseIntEnvOrDefault("YAMS_SEARCH_STAGE_TRACE_COMPONENT_TOP_N", 0, 0, 10000);
g_debugRunContext = DebugRunContext{
.runId = result.runId,
.candidate = result.candidate.name,
.dataset = "",
.corpusSize = 0,
.numQueries = 0,
.topK = 0,
.traceTopN = result.traceTopN,
.traceComponentTopN = result.traceComponentTopN,
.stageTraceEnabled = envTruthy(std::getenv("YAMS_SEARCH_STAGE_TRACE")),
.debugFile = result.debugFile,
};
CleanupFixture();
try {
SetupFixture();
if (!g_fixture || !g_fixture->client) {
throw std::runtime_error("Benchmark fixture did not initialize a daemon client");
}
auto status = benchRunSync(g_fixture->client->status(true), 5s);
if (status) {
result.tuningState = status.value().searchTuningState;
result.tuningReason = status.value().searchTuningReason;
}
result.searchEngine =
std::string(yams::vector::vectorSearchEngineName(benchmarkVectorSearchEngine()));
if (g_fixture && g_fixture->harness) {
const auto persisted = inspectPersistedHnswState(g_fixture->harness->dataDir());
result.vectorsDbBytes = persisted.vectorsDbBytes;
result.vectorsWalBytes = persisted.vectorsWalBytes;
result.persistedHnswNodes = persisted.nodeRows;
result.persistedHnswMetaRows = persisted.metaRows;
result.persistedVec0Tables = persisted.vec0Tables;
result.persistedVec0Rows = persisted.vec0Rows;
result.persistedPqCodes = persisted.pqCodeRows;
result.persistedPqMeta = persisted.pqMetaRows;
}
g_debugRunContext->dataset =
g_fixture->useBEIR ? g_fixture->beirDatasetName : g_fixture->datasetName;
g_debugRunContext->corpusSize = g_fixture->corpusSize;
g_debugRunContext->numQueries = static_cast<int>(g_fixture->queries.size());
g_debugRunContext->topK = g_fixture->topK;
g_debugRunContext->traceTopN =
parseIntEnvOrDefault("YAMS_SEARCH_STAGE_TRACE_TOP_N", result.traceTopN, 0, 10000);
g_debugRunContext->traceComponentTopN = parseIntEnvOrDefault(
"YAMS_SEARCH_STAGE_TRACE_COMPONENT_TOP_N", result.traceComponentTopN, 0, 10000);
g_debugRunContext->stageTraceEnabled = envTruthy(std::getenv("YAMS_SEARCH_STAGE_TRACE"));
result.traceTopN = g_debugRunContext->traceTopN;
result.traceComponentTopN = g_debugRunContext->traceComponentTopN;
result.stageTraceEnabled = g_debugRunContext->stageTraceEnabled;
const auto hybridStart = std::chrono::steady_clock::now();
result.hybridMetrics =
evaluateQueries(*g_fixture->client, g_fixture->benchCorpusDir, g_fixture->queries,
g_fixture->topK, "hybrid", &result.hybridDiagnostics);
const auto hybridEnd = std::chrono::steady_clock::now();
result.hybridEvalMs =
std::chrono::duration<double, std::milli>(hybridEnd - hybridStart).count();
const auto keywordStart = std::chrono::steady_clock::now();
result.keywordMetrics =
evaluateQueries(*g_fixture->client, g_fixture->benchCorpusDir, g_fixture->queries,
g_fixture->topK, "keyword", &result.keywordDiagnostics);
const auto keywordEnd = std::chrono::steady_clock::now();
result.keywordEvalMs =
std::chrono::duration<double, std::milli>(keywordEnd - keywordStart).count();
if (result.hybridMetrics.numQueries <= 0) {
throw std::runtime_error("No benchmark queries generated for optimization candidate");
}
const double avgHybridQueryMs =
result.hybridEvalMs / std::max(1, result.hybridMetrics.numQueries);
result.objectiveScore =
computeOptimizationObjective(result.hybridMetrics, result.keywordMetrics,
result.hybridDiagnostics, avgHybridQueryMs);
result.success = true;
} catch (const std::exception& e) {
result.success = false;
result.errorMessage = e.what();
}
g_debugRunContext.reset();
CleanupFixture();
if (outputFile.has_value()) {
appendOptimizationResultJson(*outputFile, result);
}
return result;
}
static int runOptimizationLoop() {
std::vector<OptimizationCandidate> candidates = defaultOptimizationCandidates();
const auto optProfile = parseOptimizationProfile(std::getenv("YAMS_BENCH_OPT_PROFILE"));
const bool hasExplicitCandidateFilter =
std::getenv("YAMS_BENCH_OPT_CANDIDATE") &&
std::strlen(std::getenv("YAMS_BENCH_OPT_CANDIDATE")) > 0;
if (!hasExplicitCandidateFilter) {
std::vector<OptimizationCandidate> filteredByProfile;
filteredByProfile.reserve(candidates.size());
for (const auto& candidate : candidates) {
const bool keep =
optProfile == OptimizationProfile::All ||
(optProfile == OptimizationProfile::EnginePolicy &&
candidate.enginePolicyCandidate) ||
(optProfile == OptimizationProfile::Diagnostics && candidate.diagnosticCandidate);
if (keep) {
filteredByProfile.push_back(candidate);
}
}
if (!filteredByProfile.empty()) {
spdlog::info("[OptLoop] No candidate filter set; using optimization profile '{}' with "
"{} candidates",
optimizationProfileName(optProfile), filteredByProfile.size());
candidates = std::move(filteredByProfile);
}
}
const auto selectedCandidates = parseCandidateFilter(std::getenv("YAMS_BENCH_OPT_CANDIDATE"));
if (!selectedCandidates.empty()) {
std::vector<OptimizationCandidate> filtered;
filtered.reserve(candidates.size());
for (const auto& candidate : candidates) {
if (selectedCandidates.count(candidate.name) > 0) {
filtered.push_back(candidate);
}
}
candidates = std::move(filtered);
if (candidates.empty()) {
std::cerr << "No optimization candidates matched YAMS_BENCH_OPT_CANDIDATE='"
<< std::getenv("YAMS_BENCH_OPT_CANDIDATE") << "'\n";
return 2;
}
}
bool pruneKnownBadCandidates = true;
if (const char* pruneEnv = std::getenv("YAMS_BENCH_OPT_PRUNE_KNOWN_BAD"); pruneEnv) {
pruneKnownBadCandidates = envTruthy(pruneEnv);
}
if (pruneKnownBadCandidates && selectedCandidates.empty()) {
static const std::unordered_set<std::string> kKnownBadCandidates = {
"mixed_precision_rrf_neighbor_t92_p80_k14",
"mixed_precision_rrf_neighbor_t92_p78_k16",
"mixed_precision_rrf_neighbor_t90_p84_k18",
"mixed_ablation_disable_graph_rerank",
};
const auto beforeCount = candidates.size();
candidates.erase(std::remove_if(candidates.begin(), candidates.end(),
[](const OptimizationCandidate& candidate) {
return kKnownBadCandidates.count(candidate.name) > 0;
}),
candidates.end());
const auto removed = beforeCount - candidates.size();
if (removed > 0) {
spdlog::info("[OptLoop] Pruned {} known low-value candidates (set "
"YAMS_BENCH_OPT_PRUNE_KNOWN_BAD=0 to disable)",
removed);
}
}
if (const char* maxCandidatesEnv = std::getenv("YAMS_BENCH_OPT_MAX_CANDIDATES")) {
try {
const auto parsed = std::max(1, std::stoi(maxCandidatesEnv));
if (static_cast<std::size_t>(parsed) < candidates.size()) {
candidates.resize(static_cast<std::size_t>(parsed));
}
} catch (...) {
spdlog::warn("[OptLoop] Invalid YAMS_BENCH_OPT_MAX_CANDIDATES='{}', using default {}",
maxCandidatesEnv, candidates.size());
}
}
std::optional<fs::path> outputFile;
if (const char* outputEnv = std::getenv("YAMS_BENCH_OPT_RESULTS_FILE");
outputEnv && std::strlen(outputEnv) > 0) {
outputFile = fs::path(outputEnv);
const bool appendMode = envTruthy(std::getenv("YAMS_BENCH_OPT_APPEND_RESULTS"));
std::ofstream probe(*outputFile, appendMode ? (std::ios::out | std::ios::app)
: (std::ios::out | std::ios::trunc));
if (!probe.is_open()) {
spdlog::warn("[OptLoop] Could not create optimization results file: {}",
outputFile->string());
outputFile.reset();
} else {
spdlog::info("[OptLoop] Writing optimization results to {} ({})", outputFile->string(),
appendMode ? "append" : "truncate");
}
}
std::cout << "\n" << std::string(78, '=') << "\n";
std::cout << " RETRIEVAL OPTIMIZATION LOOP (code/mixed first)\n";
std::cout << std::string(78, '=') << "\n";
std::cout << "Profile: " << optimizationProfileName(optProfile)
<< (hasExplicitCandidateFilter ? " (explicit candidate filter)" : "") << "\n";
std::vector<OptimizationRunResult> allResults;
allResults.reserve(candidates.size());
for (std::size_t i = 0; i < candidates.size(); ++i) {
const auto& candidate = candidates[i];
spdlog::info("[OptLoop] Candidate {}/{}: {} - {}", i + 1, candidates.size(), candidate.name,
candidate.description);
auto result = runOptimizationCandidate(candidate, outputFile);
allResults.push_back(result);
if (!result.success) {
std::cout << "\n [" << (i + 1) << "/" << candidates.size() << "] " << candidate.name
<< " -> FAILED: " << result.errorMessage << "\n";
continue;
}
const double avgMs = result.hybridEvalMs / std::max(1, result.hybridMetrics.numQueries);
const double mrrDelta = result.hybridMetrics.mrr - result.keywordMetrics.mrr;
std::cout << "\n [" << (i + 1) << "/" << candidates.size() << "] " << candidate.name
<< "\n";
std::cout << " objective=" << std::fixed << std::setprecision(4) << result.objectiveScore
<< " hybrid_mrr=" << result.hybridMetrics.mrr
<< " keyword_mrr=" << result.keywordMetrics.mrr << " mrr_delta=" << std::showpos
<< std::setprecision(4) << mrrDelta << std::noshowpos
<< " dup@k=" << std::setprecision(4) << result.hybridMetrics.duplicateRateAtK
<< " avg_hybrid_ms=" << std::setprecision(1) << avgMs << "\n";
const auto hybridDiag = queryDiagnosticsToJson(result.hybridDiagnostics);
std::cout << " trace_cov=" << std::setprecision(3)
<< hybridDiag["trace_coverage"].get<double>() << " sem_rescue_mean="
<< hybridDiag["semantic_rescue_rate"]["mean"].get<double>()
<< " ca_apply_rate=" << hybridDiag["compressed_ann_apply_rate"].get<double>()
<< " vec_only_below_mean="
<< hybridDiag["vector_only_below_threshold"]["mean"].get<double>()
<< " no_relevant_hit_rate="
<< hybridDiag["query_without_relevant_hit_rate"].get<double>()
<< " graph_apply_rate=" << hybridDiag["graph_rerank_apply_rate"].get<double>()
<< " miss_pre_fusion_rate="
<< hybridDiag["miss_missing_pre_fusion_rate"].get<double>() << "\n";
std::cout << " timing_tradeoffs=" << summarizeTimingTradeoffs(hybridDiag) << "\n";
std::cout << " ca_skip_reasons=" << hybridDiag["compressed_ann_skip_reasons"].dump()
<< "\n";
std::cout << " stage_tradeoffs=" << summarizeStageTradeoffs(hybridDiag) << "\n";
std::cout << " stage_quality=" << summarizeStageMetricGaps(hybridDiag) << "\n";
if (result.traceTopN > 0 || result.traceComponentTopN > 0) {
std::cout << " trace_top_n=" << result.traceTopN
<< " trace_component_top_n=" << result.traceComponentTopN << "\n";
}
std::cout << " tuning_state="
<< (result.tuningState.empty() ? "<unknown>" : result.tuningState) << "\n";
if (!result.debugFile.empty()) {
std::cout << " debug_file=" << result.debugFile << "\n";
}
}
std::vector<OptimizationRunResult> successful;
for (const auto& result : allResults) {
if (result.success) {
successful.push_back(result);
}
}
if (successful.empty()) {
std::cout << "\nNo successful optimization candidates completed.\n";
return 2;
}
std::sort(successful.begin(), successful.end(),
[](const OptimizationRunResult& a, const OptimizationRunResult& b) {
return a.objectiveScore > b.objectiveScore;
});
const auto& best = successful.front();
g_final_metrics = best.hybridMetrics;
g_keyword_metrics = best.keywordMetrics;
std::cout << "\n" << std::string(78, '-') << "\n";
std::cout << "Top candidates by objective\n";
std::cout << std::string(78, '-') << "\n";
for (std::size_t i = 0; i < successful.size(); ++i) {
const auto& result = successful[i];
const double avgMs = result.hybridEvalMs / std::max(1, result.hybridMetrics.numQueries);
const double mrrDelta = result.hybridMetrics.mrr - result.keywordMetrics.mrr;
std::cout << " " << (i + 1) << ". " << result.candidate.name
<< " objective=" << std::fixed << std::setprecision(4) << result.objectiveScore
<< " mrr=" << result.hybridMetrics.mrr
<< " ndcg=" << result.hybridMetrics.ndcgAtK << " mrr_delta=" << std::showpos
<< std::setprecision(4) << mrrDelta << std::noshowpos
<< " dup@k=" << std::setprecision(4) << result.hybridMetrics.duplicateRateAtK
<< " avg_ms=" << std::setprecision(1) << avgMs << "\n";
}
std::cout << "\nBest candidate: " << best.candidate.name << "\n";
std::cout << " " << best.candidate.description << "\n";
std::ostringstream envLine;
for (const auto& setting : best.candidate.envOverrides) {
if (setting.value.has_value()) {
envLine << setting.key << "=" << *setting.value << " ";
}
}
std::cout << " Recommended env overrides: " << envLine.str() << "\n";
if (outputFile.has_value()) {
std::cout << " Saved per-run JSONL to: " << outputFile->string() << "\n";
}
const char* datasetEnv = std::getenv("YAMS_BENCH_DATASET");
if (!(datasetEnv && std::strlen(datasetEnv) > 0)) {
std::cout << " Next: validate this winner on a harder dataset, e.g. "
<< "YAMS_BENCH_DATASET=synthetic-hard or YAMS_BENCH_DATASET=scifact ...\n";
}
std::cout << std::string(78, '=') << "\n";
return 0;
}
void BM_RetrievalQuality(benchmark::State& state) {
SetupFixture();
auto& fixture = *g_fixture;
RetrievalMetrics metrics;
for (auto _ : state) {
std::vector<double> warmupLatencyMs;
std::vector<TestQuery> failedWarmupQueries;
evaluateQueries(*fixture.client, fixture.benchCorpusDir, fixture.queries, fixture.topK,
"hybrid", nullptr, false, &warmupLatencyMs, &failedWarmupQueries);
const auto retriedWarmupQueries = failedWarmupQueries.size();
if (!failedWarmupQueries.empty()) {
std::vector<TestQuery> retryFailures;
evaluateQueries(*fixture.client, fixture.benchCorpusDir, failedWarmupQueries,
fixture.topK, "hybrid", nullptr, false, &warmupLatencyMs,
&retryFailures);
failedWarmupQueries = std::move(retryFailures);
}
if (g_debugOut) {
DebugLogEntry warmupEntry;
warmupEntry.searchType = "benchmark_warmup";
warmupEntry.extraFields = {
{"event", "query_warmup_summary"},
{"query_count", fixture.queries.size()},
{"completed_queries", warmupLatencyMs.size()},
{"first_query_latency_ms", warmupLatencyMs.empty() ? 0.0 : warmupLatencyMs.front()},
{"retried_queries", retriedWarmupQueries},
{"retry_failed_queries", failedWarmupQueries.size()},
};
debugLogWriteJsonLine(warmupEntry);
}
g_final_hybrid_diagnostics = QueryDiagnosticsSummary{};
metrics = evaluateQueries(*fixture.client, fixture.benchCorpusDir, fixture.queries,
fixture.topK, "hybrid", &g_final_hybrid_diagnostics);
benchmark::DoNotOptimize(metrics);
}
// Store metrics globally for post-teardown summary
g_final_metrics = metrics;
g_benchmark_search_engine =
std::string(yams::vector::vectorSearchEngineName(benchmarkVectorSearchEngine()));
if (fixture.harness) {
g_final_vector_index_state = inspectPersistedHnswState(fixture.harness->dataDir());
}
state.counters["MRR"] = metrics.mrr;
state.counters["Recall@K"] = metrics.recallAtK;
state.counters["Precision@K"] = metrics.precisionAtK;
state.counters["nDCG@K"] = metrics.ndcgAtK;
state.counters["MAP"] = metrics.map;
state.counters["num_queries"] = metrics.numQueries;
// Also evaluate keyword-only (FTS5) for component isolation
// This helps diagnose whether low MRR is from FTS5 or vector search
spdlog::info("=== Evaluating KEYWORD-ONLY (FTS5) for component isolation ===");
g_final_keyword_diagnostics = QueryDiagnosticsSummary{};
g_keyword_metrics = evaluateQueries(*fixture.client, fixture.benchCorpusDir, fixture.queries,
fixture.topK, "keyword", &g_final_keyword_diagnostics);
if (g_debugOut) {
DebugLogEntry summaryEntry;
summaryEntry.searchType = "benchmark_summary";
summaryEntry.extraFields = {
{"event", "hybrid_summary"},
{"hybrid_metrics",
{{"mrr", metrics.mrr},
{"recall_at_k", metrics.recallAtK},
{"precision_at_k", metrics.precisionAtK},
{"ndcg_at_k", metrics.ndcgAtK},
{"map", metrics.map}}},
{"hybrid_diag_summary", queryDiagnosticsToJson(g_final_hybrid_diagnostics)},
{"keyword_metrics",
{{"mrr", g_keyword_metrics.mrr},
{"recall_at_k", g_keyword_metrics.recallAtK},
{"precision_at_k", g_keyword_metrics.precisionAtK},
{"ndcg_at_k", g_keyword_metrics.ndcgAtK},
{"map", g_keyword_metrics.map}}},
{"keyword_diag_summary", queryDiagnosticsToJson(g_final_keyword_diagnostics)},
};
debugLogWriteJsonLine(summaryEntry);
}
state.counters["MRR_keyword"] = g_keyword_metrics.mrr;
state.counters["Recall_keyword"] = g_keyword_metrics.recallAtK;
if (benchEvaluateGrepBaseline()) {
spdlog::info("=== Evaluating GREP literal baseline for exact-match comparison ===");
g_grep_metrics = evaluateGrepQueries(*fixture.client, fixture.queries, fixture.topK);
state.counters["MRR_grep"] = g_grep_metrics.mrr;
state.counters["Recall_grep"] = g_grep_metrics.recallAtK;
} else {
spdlog::info("=== Skipping GREP literal baseline for this benchmark plan ===");
}
if (g_debugOut) {
const auto peakRssMb = yams::bench::processPeakRssMb();
DebugLogEntry resourceEntry;
resourceEntry.searchType = "benchmark_summary";
resourceEntry.extraFields = {
{"event", "process_resource_metrics"},
{"process",
{{"peak_rss_observed", peakRssMb.has_value()},
{"peak_rss_mb", peakRssMb.value_or(0.0)}}},
};
debugLogWriteJsonLine(resourceEntry);
}
}
// Retrieval quality is a long-running, end-to-end evaluation.
// Force a single iteration to avoid repeated full-corpus evaluations.
BENCHMARK(BM_RetrievalQuality)->Iterations(1);
int main(int argc, char** argv) {
consumeXplanConfigArguments(argc, argv);
if (envTruthy(std::getenv("YAMS_BENCH_OPT_LOOP"))) {
return runOptimizationLoop();
}
benchmark::Initialize(&argc, argv);
if (benchmark::ReportUnrecognizedArguments(argc, argv))
return 1;
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
CleanupFixture();
// Re-print summary after all teardown logs
// This ensures results are visible even when daemon teardown is verbose
std::cout << "\n" << std::string(70, '=') << "\n";
std::cout << " RETRIEVAL QUALITY BENCHMARK RESULTS\n";
std::cout << std::string(70, '=') << "\n";
std::cout << std::fixed << std::setprecision(4);
// Hybrid search results
std::cout << "\n --- HYBRID SEARCH (text + vector) ---\n";
std::cout << " Search engine: " << std::setw(10)
<< g_benchmark_search_engine << "\n";
std::cout << " MRR (Mean Reciprocal Rank): " << std::setw(10) << g_final_metrics.mrr
<< "\n";
std::cout << " Recall@K: " << std::setw(10) << g_final_metrics.recallAtK
<< "\n";
std::cout << " Precision@K: " << std::setw(10)
<< g_final_metrics.precisionAtK << "\n";
std::cout << " nDCG@K (Normalized DCG): " << std::setw(10) << g_final_metrics.ndcgAtK
<< "\n";
std::cout << " MAP (Mean Average Precision): " << std::setw(10) << g_final_metrics.map
<< "\n";
const auto hybridDiag = queryDiagnosticsToJson(g_final_hybrid_diagnostics);
std::cout << " Stage quality gaps: " << summarizeStageMetricGaps(hybridDiag)
<< "\n";
std::cout << " vectors.db bytes: " << std::setw(10)
<< g_final_vector_index_state.vectorsDbBytes << "\n";
std::cout << " vectors.db-wal bytes: " << std::setw(10)
<< g_final_vector_index_state.vectorsWalBytes << "\n";
std::cout << " persisted_hnsw_nodes: " << std::setw(10)
<< g_final_vector_index_state.nodeRows << "\n";
std::cout << " persisted_hnsw_meta_rows: " << std::setw(10)
<< g_final_vector_index_state.metaRows << "\n";
std::cout << " persisted_vec0_tables: " << std::setw(10)
<< g_final_vector_index_state.vec0Tables << "\n";
std::cout << " persisted_vec0_rows: " << std::setw(10)
<< g_final_vector_index_state.vec0Rows << "\n";
std::cout << " persisted_simeon_pq_codes: " << std::setw(10)
<< g_final_vector_index_state.pqCodeRows << "\n";
std::cout << " persisted_simeon_pq_meta: " << std::setw(10)
<< g_final_vector_index_state.pqMetaRows << "\n";
// Keyword-only search results (FTS5 isolation)
std::cout << "\n --- KEYWORD SEARCH (FTS5 only) ---\n";
std::cout << " MRR (Mean Reciprocal Rank): " << std::setw(10) << g_keyword_metrics.mrr
<< "\n";
std::cout << " Recall@K: " << std::setw(10)
<< g_keyword_metrics.recallAtK << "\n";
std::cout << " Precision@K: " << std::setw(10)
<< g_keyword_metrics.precisionAtK << "\n";
std::cout << " nDCG@K (Normalized DCG): " << std::setw(10) << g_keyword_metrics.ndcgAtK
<< "\n";
std::cout << " MAP (Mean Average Precision): " << std::setw(10) << g_keyword_metrics.map
<< "\n";
// Grep baseline results
std::cout << "\n --- GREP BASELINE (literal exact match) ---\n";
std::cout << " MRR (Mean Reciprocal Rank): " << std::setw(10) << g_grep_metrics.mrr
<< "\n";
std::cout << " Recall@K: " << std::setw(10) << g_grep_metrics.recallAtK
<< "\n";
std::cout << " Precision@K: " << std::setw(10)
<< g_grep_metrics.precisionAtK << "\n";
std::cout << " nDCG@K (Normalized DCG): " << std::setw(10) << g_grep_metrics.ndcgAtK
<< "\n";
std::cout << " MAP (Mean Average Precision): " << std::setw(10) << g_grep_metrics.map
<< "\n";
// Component comparison summary
std::cout << "\n --- COMPONENT COMPARISON ---\n";
std::cout << " Number of queries evaluated: " << std::setw(10) << g_final_metrics.numQueries
<< "\n";
auto describeDelta = [](double delta, std::string_view positive, std::string_view negative) {
constexpr double kTieEpsilon = 1e-9;
if (std::abs(delta) <= kTieEpsilon) {
return std::string(" (tied)");
}
return std::string(delta > 0 ? positive : negative);
};
double keywordMrrDelta = g_final_metrics.mrr - g_keyword_metrics.mrr;
double grepMrrDelta = g_final_metrics.mrr - g_grep_metrics.mrr;
std::cout << " MRR delta (hybrid - keyword): " << std::setw(10) << keywordMrrDelta
<< describeDelta(keywordMrrDelta, " (hybrid better)", " (keyword better)") << "\n";
std::cout << " MRR delta (hybrid - grep): " << std::setw(10) << grepMrrDelta
<< describeDelta(grepMrrDelta, " (hybrid better)", " (grep better)") << "\n";
std::cout << std::string(70, '=') << "\n";
return 0;
}