/** * Semantic similarity scoring for catalog selection (#294). * * Provides: * - Pure math helpers: cosineSimilarity, scoreToFitLevel * - Entry/query text builders for embedding input * - Optional embedding pipeline via @xenova/transformers (graceful fallback) * - SemanticScorer class that wraps the optional dependency * * Design: this module is import-safe with no hard dependency on * @xenova/transformers. When the package is not installed, SemanticScorer * falls back to a no-op state and callers continue with keyword-overlap gating. */ import type { AssetCatalogEntry, DemandProfile } from "../../types.js"; /** Valid fit-level values derived from cosine similarity scores. */ export type FitLevel = "strong" | "moderate" | "weak" | "none"; /** Cosine similarity thresholds that determine fit levels. */ export declare const FIT_LEVEL_THRESHOLDS: { /** Cosine score ≥ this → "strong" */ readonly strong: 0.75; /** Cosine score ≥ this → "moderate" */ readonly moderate: 0.55; /** Cosine score ≥ this → "weak" (kept but deprioritised) */ readonly weak: 0.35; }; /** * Weight multiplier for entries carrying ARD representativeQueries (#327). * When an entry has ARD-generated natural-language query text, its semantic * similarity score is multiplied by this factor to reflect the higher-quality * embedding signal compared to keyword-derived capability text. */ export declare const ARD_REPRESENTATIVE_QUERY_WEIGHT = 1.2; /** * Computes the cosine similarity between two equal-length float32 vectors. * Both vectors should be L2-normalised for meaningful results. * Returns a value in `[-1, 1]`; returns `0` for zero-magnitude inputs. */ export declare function cosineSimilarity(a: Float32Array, b: Float32Array): number; /** * Maps a cosine similarity score to a human-readable fit level. * * - ≥ 0.75 → `"strong"` * - ≥ 0.55 → `"moderate"` * - ≥ 0.35 → `"weak"` * - < 0.35 → `"none"` (should be filtered out) */ export declare function scoreToFitLevel(score: number): FitLevel; /** * Builds the embedding input text for a catalog entry. * Concatenates display name and capability terms for best semantic coverage. */ export declare function buildEntryEmbeddingText(entry: AssetCatalogEntry): string; /** * Builds the embedding input text for a workspace demand profile. * Concatenates all signal categories into a natural-language query string. */ export declare function buildDemandQueryText(demandProfile: DemandProfile | null): string; /** * Optional embedding-based scorer. * * When `@xenova/transformers` is installed and semantic scoring is enabled, * embeds the demand query and catalog entries then ranks by cosine similarity. * * When the package is absent or semantic scoring is disabled, all methods * return `null`/`undefined` and the caller falls back to keyword-overlap gating. */ export declare class SemanticScorer { /** * Whether the optional embedding pipeline is available. * Set to `true` by the constructor (embedder override path) or by a * successful `tryInit()` call. Set to `false` when initialisation fails. */ get available(): boolean; private _available; private readonly minSimilarity; private embedder; constructor(options: { /** Minimum cosine similarity to accept an entry. Default: 0.35 */ minSimilarity?: number; /** * Optional embedder override for tests — skip the @xenova/transformers * dependency entirely. */ embedderOverride?: (text: string) => Promise; }); /** * Attempts to load the @xenova/transformers pipeline. * No-op if the package is absent — sets `available` to false. * Call this once before any embed operations when not using an override. */ tryInit(modelName?: string): Promise; /** * Embeds a single text string. * Returns `null` when the scorer is not available. */ embed(text: string): Promise; /** * Scores a catalog entry against a pre-embedded query vector. * Returns `null` when the scorer is unavailable. * Returns a `{ score, fitLevel }` object otherwise. */ scoreEntry(entry: AssetCatalogEntry, queryVec: Float32Array): Promise<{ score: number; fitLevel: FitLevel; } | null>; /** * Filters and ranks catalog entries by semantic similarity to the demand query. * * Returns `null` when the scorer is unavailable (caller should fall back to * keyword-overlap gating). * * Returns `{ selected, rejected }` where: * - `selected` entries have `score >= minSimilarity` and include an updated * `fit.fitLevel` field. * - `rejected` entries have `score < minSimilarity`. */ filterAndRank(catalogEntries: AssetCatalogEntry[], demandProfile: DemandProfile | null): Promise<{ selected: AssetCatalogEntry[]; rejected: AssetCatalogEntry[]; } | null>; /** Minimum similarity threshold used by this scorer. */ get threshold(): number; } /** Exposes narrow semantic-scoring internals for focused tests. */ export declare const semanticScoringInternals: { FIT_LEVEL_THRESHOLDS: { /** Cosine score ≥ this → "strong" */ readonly strong: 0.75; /** Cosine score ≥ this → "moderate" */ readonly moderate: 0.55; /** Cosine score ≥ this → "weak" (kept but deprioritised) */ readonly weak: 0.35; }; scoreToFitLevel: typeof scoreToFitLevel; cosineSimilarity: typeof cosineSimilarity; buildEntryEmbeddingText: typeof buildEntryEmbeddingText; buildDemandQueryText: typeof buildDemandQueryText; };