/* 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= - 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 #include #include #include #include "retrieval_benchmark_support.h" #include "tests/common/test_helpers_catch2.h" #include "tests/integration/daemon/test_daemon_harness.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 cb) override { progress_ = std::move(cb); } Result> generateEmbedding(const std::string& text) override { return generateEmbeddingFor(defaultModelName_, text); } Result>> generateBatchEmbeddings(const std::vector& texts) override { return generateBatchEmbeddingsFor(defaultModelName_, texts); } Result> generateEmbeddingFor(const std::string& modelName, const std::string& text) override { if (!isModelLoaded(modelName)) { return Error{ErrorCode::NotFound, "model not loaded"}; } std::vector embedding(dim_, 0.0f); std::hash hasher; const auto seed = hasher(text); for (std::size_t i = 0; i < embedding.size(); ++i) { embedding[i] = static_cast((seed ^ (i * 131)) % 1000) / 1000.0f; } return embedding; } Result>> generateBatchEmbeddingsFor(const std::string& modelName, const std::vector& texts) override { if (!isModelLoaded(modelName)) { return Error{ErrorCode::NotFound, "model not loaded"}; } std::vector> 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 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(); } Result unloadModel(const std::string& modelName) override { loadedModels_.erase(modelName); return Result(); } bool isModelLoaded(const std::string& modelName) const override { return loadedModels_.contains(modelName); } std::vector getLoadedModels() const override { return std::vector(loadedModels_.begin(), loadedModels_.end()); } size_t getLoadedModelCount() const override { return loadedModels_.size(); } Result 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 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 loadedModels_; std::function progress_; }; template static Result benchRunSync(boost::asio::awaitable> aw, const std::chrono::duration& timeout) { return yams::cli::run_sync(std::move(aw), timeout); } static std::uint64_t parseAdditionalStat(const std::map& 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 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 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 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(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 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 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 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 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 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(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 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 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 selectBenchmarkBEIRDataset(const std::string& datasetName, const fs::path& cacheDir, const std::optional& maxDocsOpt, const std::optional& maxQueriesOpt) { Result 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(fullDataset.documents.size()); const int maxQueries = maxQueriesOpt.has_value() ? *maxQueriesOpt : static_cast(fullDataset.queries.size()); std::set 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 includedDocIds; for (const auto& docId : qrelDocIds) { if (static_cast(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(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 semanticRescueRateSamples; std::vector semanticRescueFinalCountSamples; std::vector semanticRescueTargetSamples; std::vector compressedAnnResultCountSamples; std::unordered_map compressedAnnSkipReasonCounts; std::vector fusionPreFusionUniqueCountSamples; std::vector fusionPostFusionCountSamples; std::vector fusionDroppedCountSamples; std::vector fusionDroppedRateSamples; std::vector anchoredPreFusionCountSamples; std::vector anchoredFusionDroppedCountSamples; std::vector anchoredFusionDroppedRateSamples; std::vector topTextPreFusionCountSamples; std::vector topTextFusionDroppedCountSamples; std::vector topTextFusionDroppedRateSamples; std::vector vectorOnlyDocsSamples; std::vector vectorOnlyBelowThresholdSamples; std::vector vectorOnlyAboveThresholdSamples; std::vector vectorOnlyNearMissEligibleSamples; std::uint64_t adaptiveFusionEnabledQueryCount = 0; std::vector strongVectorOnlyDocsSamples; std::vector strongVectorOnlyScoreEligibleDocsSamples; std::vector strongVectorOnlyRankEligibleDocsSamples; std::vector graphAddedPostFusionSamples; std::vector graphDisplacedPostFusionSamples; std::vector topologyRoutedDocsSamples; std::vector topologyRouteBestScoreSamples; std::vector topologyRouteMeanAcceptedScoreSamples; std::vector topologyRouteAcceptedCountSamples; std::vector topologySeedCountSamples; std::vector topologySeedCoverageCountSamples; std::vector topologyCoreCandidateCoverageSamples; std::vector topologyAddedCandidatesSamples; std::vector topologyDuplicateCandidatesSamples; std::vector topologyStaleCandidatesSamples; std::vector graphCommunitySupportedDocsSamples; std::vector graphCommunityEdgeCountSamples; std::vector graphCommunityLargestSizeSamples; std::vector graphCommunitySignalMassSamples; std::vector graphCommunityBoostedDocsSamples; std::vector graphCommunityWeightSamples; std::vector multiVectorGeneratedPhraseSamples; std::vector multiVectorRawHitSamples; std::vector multiVectorAddedNewSamples; std::vector multiVectorReplacedBaseSamples; std::vector subPhraseGeneratedSamples; std::vector subPhraseClauseSamples; std::vector subPhraseFtsHitSamples; std::vector subPhraseFtsAddedSamples; std::vector aggressiveFtsClauseSamples; std::vector aggressiveFtsHitSamples; std::vector aggressiveFtsAddedSamples; std::unordered_map> timingSamplesMs; std::unordered_map stageEnabledCount; std::unordered_map stageAttemptedCount; std::unordered_map stageContributedCount; std::unordered_map stageSkippedCount; std::unordered_map stageRelevantPresenceCount; std::unordered_map> stageSkipReasons; std::unordered_map> fusionSourceScoreMassSamples; std::unordered_map> fusionSourceFinalDocSamples; std::unordered_map> stageUniqueDocCountSamples; std::unordered_map> stageRelevantDocCountSamples; std::map stageRetrievalMetrics; std::unordered_map> tunerSignalSamples; std::unordered_map tunerDecisionCounts; std::unordered_map zoomLevelCounts; }; static const std::vector& trackedTimingKeys() { static const std::vector 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 splitTabSet(const std::string& value) { std::unordered_set 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 splitTabList(const std::string& value, size_t limit) { std::vector 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 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& relevantDocIds, const std::vector& returnedDocIds, const std::map& 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 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()) != 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() == 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& 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 relevantDocIds; std::vector relevantFiles; std::vector returnedPaths; std::vector returnedDocIds; std::vector returnedScores; std::vector returnedGrades; std::vector diagnostics; std::uint64_t attempts = 0; bool usedStreaming = false; bool usedFuzzyFlag = false; bool usedLiteralFlag = false; bool usedFuzzyRetry = false; bool usedLiteralTextRetry = false; std::map 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 g_debugFile; static std::mutex g_debugMutex; static std::optional g_debugRunContext; static std::optional parseDoubleLoose(const std::string& value) { try { return std::stod(value); } catch (...) { return std::nullopt; } } static std::optional parseDoubleStat(const std::map& stats, const std::string& key) { if (auto it = stats.find(key); it != stats.end()) { return parseDoubleLoose(it->second); } return std::nullopt; } static std::optional parseBoolStat(const std::map& 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(std::tolower(static_cast(c))); }); return normalized == "1" || normalized == "true" || normalized == "yes" || normalized == "on"; } return std::nullopt; } static std::optional parseJsonStat(const std::map& 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 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(samples.size() - 1); const auto lo = static_cast(std::floor(idx)); const auto hi = static_cast(std::ceil(idx)); if (lo == hi) { return samples[lo]; } const double t = idx - static_cast(lo); return samples[lo] * (1.0 - t) + samples[hi] * t; } static json summarizeSamples(const std::vector& 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(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& 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 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()); } } if (auto it = parsed.find("vector_only_below_threshold"); it != parsed.end()) { if (it->is_number()) { summary.vectorOnlyBelowThresholdSamples.push_back(it->get()); } } if (auto it = parsed.find("vector_only_above_threshold"); it != parsed.end()) { if (it->is_number()) { summary.vectorOnlyAboveThresholdSamples.push_back(it->get()); } } if (auto it = parsed.find("vector_only_near_miss_eligible"); it != parsed.end()) { if (it->is_number()) { summary.vectorOnlyNearMissEligibleSamples.push_back(it->get()); } } } if (auto it = parsed.find("strong_vector_only_docs"); it != parsed.end()) { if (it->is_number()) { summary.strongVectorOnlyDocsSamples.push_back(it->get()); } } if (auto it = parsed.find("strong_vector_only_score_eligible_docs"); it != parsed.end()) { if (it->is_number()) { summary.strongVectorOnlyScoreEligibleDocsSamples.push_back( it->get()); } } if (auto it = parsed.find("strong_vector_only_rank_eligible_docs"); it != parsed.end()) { if (it->is_number()) { summary.strongVectorOnlyRankEligibleDocsSamples.push_back( it->get()); } } } } 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()); } if (auto it = parsed.find("graph_displaced_post_fusion_count"); it != parsed.end() && it->is_number()) { summary.graphDisplacedPostFusionSamples.push_back(it->get()); } } } 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()); } } } } 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()); } } } 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()); } if (auto it = sourceData.find("final_top_doc_count"); it != sourceData.end() && it->is_number()) { summary.fusionSourceFinalDocSamples[sourceName].push_back( it->get()); } } } } 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()]++; } static const std::vector 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()); } } } } 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 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()); } } } } catch (...) { } } } static json queryDiagnosticsToJson(const QueryDiagnosticsSummary& summary) { const double queryCount = static_cast(std::max(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(summary.queryWithResultsCount) / queryCount}, {"query_without_relevant_hit_rate", static_cast(summary.queryWithoutRelevantHitCount) / queryCount}, {"query_with_relevant_in_pre_fusion_rate", static_cast(summary.queryWithRelevantInPreFusionCount) / queryCount}, {"query_with_relevant_in_post_fusion_rate", static_cast(summary.queryWithRelevantInPostFusionCount) / queryCount}, {"query_with_relevant_in_graphless_post_fusion_rate", static_cast(summary.queryWithRelevantInGraphlessPostFusionCount) / queryCount}, {"query_with_relevant_in_final_window_rate", static_cast(summary.queryWithRelevantInFinalWindowCount) / queryCount}, {"query_with_graph_added_relevant_post_fusion_rate", static_cast(summary.queryWithGraphAddedRelevantPostFusionCount) / queryCount}, {"query_with_graph_displaced_relevant_post_fusion_rate", static_cast(summary.queryWithGraphDisplacedRelevantPostFusionCount) / queryCount}, {"query_with_topology_added_relevant_post_fusion_rate", static_cast(summary.queryWithTopologyAddedRelevantPostFusionCount) / queryCount}, {"query_with_topology_added_relevant_fusion_dropped_rate", static_cast(summary.queryWithTopologyAddedRelevantFusionDroppedCount) / queryCount}, {"query_with_topk_duplicate_rate", static_cast(summary.queryWithTopKDuplicateCount) / queryCount}, {"degraded_query_rate", static_cast(summary.degradedQueryCount) / queryCount}, {"trace_coverage", static_cast(summary.traceEnabledQueryCount) / queryCount}, {"graph_rerank_apply_rate", static_cast(summary.graphRerankAppliedQueryCount) / queryCount}, {"compressed_ann_enabled_rate", static_cast(summary.compressedAnnEnabledQueryCount) / queryCount}, {"compressed_ann_apply_rate", static_cast(summary.compressedAnnAppliedQueryCount) / queryCount}, {"adaptive_fusion_enabled_query_count", summary.adaptiveFusionEnabledQueryCount}, {"adaptive_fusion_enabled_rate", static_cast(summary.adaptiveFusionEnabledQueryCount) / queryCount}, {"semantic_rescue_nonzero_rate", static_cast(summary.semanticRescueNonZeroQueryCount) / queryCount}, {"miss_missing_pre_fusion_rate", static_cast(summary.missMissingPreFusionCount) / queryCount}, {"miss_fusion_cutoff_rate", static_cast(summary.missFusionCutoffCount) / queryCount}, {"miss_rerank_window_drop_rate", static_cast(summary.missRerankWindowDropCount) / queryCount}, {"miss_topk_drop_rate", static_cast(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(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(attemptedIt->second) : 0.0; const double contributed = contributedIt != summary.stageContributedCount.end() ? static_cast(contributedIt->second) : 0.0; const double skipped = skippedIt != summary.stageSkippedCount.end() ? static_cast(skippedIt->second) : 0.0; const auto relevantIt = summary.stageRelevantPresenceCount.find(stageName); const double relevantPresence = relevantIt != summary.stageRelevantPresenceCount.end() ? static_cast(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 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 value; }; class ScopedEnvOverrides { public: explicit ScopedEnvOverrides(const std::vector& overrides) { environment_.reserve(overrides.size()); for (const auto& setting : overrides) { environment_.emplace_back(setting.key, setting.value); } } private: std::vector environment_; }; struct OptimizationCandidate { std::string name; std::string description; std::vector 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 g_simeonFragmentGeometryEnabled; static std::optional g_simeonBm25Enabled; static std::optional 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 { 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(std::tolower(static_cast(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(std::tolower(static_cast(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::max(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(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(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(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 tables; while (sqlite3_step(stmt) == SQLITE_ROW) { const auto* text = reinterpret_cast(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(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(); } if (it->is_number_integer()) { const auto value = it->get(); return value >= 0 ? static_cast(value) : fallback; } if (it->is_string()) { try { return static_cast(std::stoull(it->get())); } catch (...) { return fallback; } } return fallback; } static std::optional 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 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 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{}; } 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& 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(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& settings, const std::string& key) { return std::any_of(settings.begin(), settings.end(), [&](const EnvSetting& setting) { return setting.key == key; }); } static std::vector 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 parseCandidateFilter(const char* raw) { std::set 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(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(std::max(summary.queryCount, 1)); const double noRelevantHitRate = static_cast(summary.queryWithoutRelevantHitCount) / queryCount; const double missPreFusionRate = static_cast(summary.missMissingPreFusionCount) / queryCount; const double fusionCutoffRate = static_cast(summary.missFusionCutoffCount) / queryCount; const double rerankDropRate = static_cast(summary.missRerankWindowDropCount) / queryCount; const double topKDropRate = static_cast(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> 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()); } 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(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 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 kStageOrder = { "pre_fusion", "graphless_post_fusion", "post_fusion", "post_graph", "final", }; std::vector 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& v) -> double { if (v.empty()) return 0.0; double sum = 0.0; for (double x : v) sum += x; return sum / static_cast(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::stoll(env))}; } return defaultBenchQueryTimeout(searchType); } static void ingestStageRetrievalMetrics(QueryDiagnosticsSummary& summary, const std::map& searchStats, const TestQuery& tq, int k) { static const std::vector> 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(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& queries, int k, const std::string& searchType = "hybrid", QueryDiagnosticsSummary* diagnostics = nullptr, bool emitDebug = true, std::vector* latencySamplesMs = nullptr, std::vector* failedQueries = nullptr) { RetrievalMetrics metrics; metrics.numQueries = static_cast(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(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(std::tolower(static_cast(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(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(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(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(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(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(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(snippet.size())); snippet.resize(static_cast(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 picked; picked.reserve(3); while (iss >> token && picked.size() < 3) { // Strip trailing punctuation for the diagnostic query. while (!token.empty() && std::ispunct(static_cast(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(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(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(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(duplicateTopKCount) / static_cast(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(totalAttempts) / metrics.numQueries, streamingCount, fuzzyRetryCount, literalRetryCount, metrics.duplicateRateAtK); } return metrics; } RetrievalMetrics evaluateGrepQueries(yams::daemon::DaemonClient& client, const std::vector& queries, int k) { RetrievalMetrics metrics; metrics.numQueries = static_cast(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 rankedDocIds; rankedDocIds.reserve(std::min(static_cast(std::max(0, k)), run.value().pathsOnly.size())); for (size_t i = 0; i < std::min(static_cast(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(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(duplicateTopKCount) / static_cast(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 harness; std::unique_ptr scopedEnv; std::unique_ptr pluginScopedEnv; std::unique_ptr client; std::unique_ptr corpus; std::unique_ptr beirCorpus; fs::path benchCorpusDir; fs::path warmDataDirPath; std::optional warmCacheMetadata; std::vector 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(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 docNames = { "community_target.txt", "community_partner.txt", "community_rival.txt"}; std::vector 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 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::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(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(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(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 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 embedding; std::int64_t documentId = 0; std::int64_t nodeId = 0; }; std::vector 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 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 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(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 docByBenchmarkId; docByBenchmarkId.reserve(docs.size()); for (const auto& doc : docs) { if (!doc.benchmarkDocId.empty()) { docByBenchmarkId.emplace(doc.benchmarkDocId, &doc); } } std::vector docEntities; std::vector 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> 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 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 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 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 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 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 nodeIdByHash; nodeIdByHash.reserve(docs.size()); std::unordered_map 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 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> overlapCounts; for (auto it = beirCorpus->dataset.qrels.begin(); it != beirCorpus->dataset.qrels.end();) { const auto queryId = it->first; std::vector 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> 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> 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(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 coldIngestStartedAt; std::optional admissionCompletedAt; const auto elapsedMsSince = [](SetupClock::time_point startedAt) { return std::chrono::duration_cast(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(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 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 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 { if (!(raw && *raw)) { return std::nullopt; } const int parsed = std::stoi(raw); if (parsed <= 0) { return std::nullopt; } return parsed; }; const std::optional corpusLimit = optionalPositiveInt(env_size); const std::optional 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(std::tolower(static_cast(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 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(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 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(preparedBeirDataset.value().documents.size()); numQueries = static_cast(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 : ""); 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") : "", std::getenv("YAMS_SEARCH_WAIT_FOR_CONCEPTS") ? std::getenv("YAMS_SEARCH_WAIT_FOR_CONCEPTS") : "", std::getenv("YAMS_HNSW_RANDOM_SEED") ? std::getenv("YAMS_HNSW_RANDOM_SEED") : "", std::getenv("YAMS_HNSW_PARALLEL_BUILD_THRESHOLD") ? std::getenv("YAMS_HNSW_PARALLEL_BUILD_THRESHOLD") : "", std::getenv("YAMS_POST_EMBED_CONCURRENT") ? std::getenv("YAMS_POST_EMBED_CONCURRENT") : "", std::getenv("YAMS_POST_EXTRACTION_CONCURRENT") ? std::getenv("YAMS_POST_EXTRACTION_CONCURRENT") : "", std::getenv("YAMS_POST_KG_CONCURRENT") ? std::getenv("YAMS_POST_KG_CONCURRENT") : ""); 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& 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 { struct Candidate { fs::path dir; fs::path onnxPlugin; fs::path glintPlugin; }; const std::array 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 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(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 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( 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(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(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::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(dataset, corpusDir); beirCorpus->writeDocumentsAsFiles(); benchCorpusDir = corpusDir; corpusSize = static_cast(dataset.documents.size()); numQueries = static_cast(dataset.queries.size()); expectedCacheMetadata.expectedDocs = corpusSize; expectedCacheMetadata.expectedQueries = numQueries; expectedCacheMetadata.corpusFingerprint = computePathFingerprint(benchCorpusDir); } else { benchCorpusDir = harness->rootDir() / "corpus"; corpus = std::make_unique(benchCorpusDir, syntheticCorpusMode); corpus->generateDocuments(corpusSize); corpusSize = static_cast(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(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(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(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(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(warmCacheMetadata->embeddedDocs) : getInitialCount("documents_embedded"); const uint64_t initialVectorCount = warmCacheMetadataReusable ? static_cast(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::steady_clock::now() - probeStart) .count(); ++setupTimings.vectorIndexReusableProbeCount; } } } const bool cacheHasIndexedCorpus = usingExternalBenchDataDir && initialIndexedDocCount >= static_cast(corpusSize); const bool cacheHasFullEmbeddings = usingExternalBenchDataDir && !vectorsDisabled && initialDocsEmbedded >= static_cast(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 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( std::ceil(static_cast(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 { 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(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(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(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(corpusSize)) : true; bool processedReady = postProcessedPresent ? (postProcessed >= static_cast(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(corpusSize); bool indexedReady = indexedPresent ? (indexedCount >= static_cast(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(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(preparedChunks) / static_cast(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(now - embedStartTime); auto sinceProgress = std::chrono::duration_cast( 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::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(corpusSize) && observedSnapshot.queueDrained()) { auto elapsed = std::chrono::duration_cast( 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::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 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::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::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::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(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(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(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(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(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> { 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 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(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(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(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 g_fixture; void SetupFixture() { if (!g_fixture) { g_fixture = std::make_unique(); g_fixture->setup(); } } void CleanupFixture() { if (g_fixture) { g_fixture->teardown(); g_fixture.reset(); } } static OptimizationRunResult runOptimizationCandidate(const OptimizationCandidate& candidate, const std::optional& outputFile) { OptimizationRunResult result; result.candidate = candidate; result.runId = makeRunId(candidate.name); std::vector 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(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(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(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 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 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 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 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(parsed) < candidates.size()) { candidates.resize(static_cast(parsed)); } } catch (...) { spdlog::warn("[OptLoop] Invalid YAMS_BENCH_OPT_MAX_CANDIDATES='{}', using default {}", maxCandidatesEnv, candidates.size()); } } std::optional 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 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() << " sem_rescue_mean=" << hybridDiag["semantic_rescue_rate"]["mean"].get() << " ca_apply_rate=" << hybridDiag["compressed_ann_apply_rate"].get() << " vec_only_below_mean=" << hybridDiag["vector_only_below_threshold"]["mean"].get() << " no_relevant_hit_rate=" << hybridDiag["query_without_relevant_hit_rate"].get() << " graph_apply_rate=" << hybridDiag["graph_rerank_apply_rate"].get() << " miss_pre_fusion_rate=" << hybridDiag["miss_missing_pre_fusion_rate"].get() << "\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() ? "" : result.tuningState) << "\n"; if (!result.debugFile.empty()) { std::cout << " debug_file=" << result.debugFile << "\n"; } } std::vector 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 warmupLatencyMs; std::vector 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 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; }