import { EmbedderService } from "../../../core/llm/services/embedder.service"; import { Neo4jService } from "../../../core/neo4j/services/neo4j.service"; import { CatalogEntity } from "../interfaces/graph.catalog.interface"; import { GraphIndexManager } from "./graph.index.manager"; import { GraphCatalogService } from "./graph.catalog.service"; import { ScopeGuard } from "./scope.guard"; export declare const GRAPH_EXACT_MAX_RESULTS = 10; export declare const GRAPH_FUZZY_MAX_RESULTS = 10; export declare const GRAPH_SEMANTIC_MAX_RESULTS = 5; export declare const GRAPH_RESOLVE_MAX_RESULTS = 10; export declare const GRAPH_SEMANTIC_MIN_SCORE = 0.6; export type MatchMode = "exact" | "fuzzy" | "semantic" | "none"; export interface RunSearchParams { entity: CatalogEntity; text: string; companyId: string; limit: number; /** Id of the scope-root node the run is confined to. Absent = unscoped. */ scopeId?: string; /** JSON:API type of the scope root, e.g. "campaigns". Present iff scopeId is. */ scopeType?: string; } export interface RankedCandidate { type: string; id: string; summary: string; score: number; } export interface ResolveEntityParams { text: string; companyId: string; userModuleIds: string[]; /** Id of the scope-root node the run is confined to. Absent = unscoped. */ scopeId?: string; /** JSON:API type of the scope root, e.g. "campaigns". Present iff scopeId is. */ scopeType?: string; } export interface ResolveEntityResult { matchMode: MatchMode; items: RankedCandidate[]; /** * When the merged candidate list satisfies a deterministic disambiguation * rule (literal-summary match, or score-margin dominance), surface a short * actionable hint here. The graph node prompt instructs the LLM to follow * this when present, since LLMs apply rules in the tool result more * reliably than rules they have to re-derive from the system prompt. */ recommendation?: string; } /** * Decide whether the merged candidate list satisfies a deterministic * disambiguation rule worth surfacing to the LLM. Returns the recommendation * text or `undefined` when the candidates are genuinely ambiguous. * * Two rules, in priority order: * 1. Literal-summary match: items[0].summary equals the user's literal * phrase (case-insensitive). Holds even with a smaller margin because * the name match is unambiguous on its own. * 2. Score-margin dominance: items[0] beats items[1] by ≥ 0.15 on * exact/fuzzy tiers, ≥ 0.08 on semantic. */ export declare function buildResolveRecommendation(items: RankedCandidate[], userText: string, matchMode: MatchMode): string | undefined; export declare class GraphSearchService { private readonly neo4j; private readonly embedder; private readonly indexNames; private readonly catalog; private readonly scopeGuard; private readonly logger; private existingIndexesPromise; constructor(neo4j: Neo4jService, embedder: EmbedderService, indexNames: GraphIndexManager, catalog: GraphCatalogService, scopeGuard: ScopeGuard); /** * Scope predicate for a tier query, or null when the run is unscoped. * The clause NARROWS the existing company predicate; it never replaces it. */ private scopeClauseFor; private getExistingIndexes; resolveEntity(params: ResolveEntityParams): Promise; private runTierForEntitySafe; private projectSummary; private tierFulltext; private tierSemantic; } //# sourceMappingURL=graph.search.service.d.ts.map