Documentation
¶
Overview ¶
Package query provides a clean interface for reading graph data from NATS KV buckets.
Index ¶
Constants ¶
const ( AggregationCount = "count" AggregationAvg = "avg" AggregationSum = "sum" AggregationMin = "min" AggregationMax = "max" )
Aggregation type constants for aggregation queries.
const DefaultClassificationTimeout = 30 * time.Second
DefaultClassificationTimeout caps a single query-classification LLM call when no operator-configured timeout is supplied. Classification produces a tiny JSON envelope, but the wall-clock budget must absorb model cold-start on modest hardware (Ollama loading a 1-2B model from disk can take 10-20s on first inference). Healthy classifiers respond in well under a second once warm; the 30s ceiling exists to bound a stuck call without rejecting cold-start on hardware where production users actually run. The bound still guards the HTTP gateway budget — 30s is well under the gateway's request timeout — so a slow LLM can't eat the full window and force handleGlobalSearch's keyword-fallback path to land after the gateway has already returned an error.
Operators running reasoning models that legitimately think (qwen3, deepseek-r1) can raise the bound via capability.timeout in the model registry. Operators on always-warm production hardware can lower it to recover faster fallback to keyword classification when the model is genuinely stuck.
History: was 5s pre-2026-05-17. The 5s default caused integration tests to fail on developer hardware where the model wasn't warm, and the same pattern would hit any production operator without a warm-up shim. Bumped to 30s after acknowledging that "clean except the LLM flake" was hiding a real production gotcha.
const DefaultMaxCommunities = 5
DefaultMaxCommunities is the default number of communities to search.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ClassificationResult ¶
type ClassificationResult struct {
Tier int // Which tier produced the result (0=keyword, 1=BM25, 2=neural)
Intent string // Classified intent (from embedding match, empty for keyword)
Options map[string]any // SearchOptions hints (from keyword or embedding example)
Confidence float64 // Confidence score (1.0 for keywords, similarity for embedding)
}
ClassificationResult contains the classification output from the tiered classifier chain.
type Classifier ¶
type Classifier interface {
// ClassifyQuery analyzes a query string and returns SearchOptions.
ClassifyQuery(ctx context.Context, query string) *SearchOptions
}
Classifier analyzes natural language queries to extract search intent.
type ClassifierChain ¶
type ClassifierChain struct {
// contains filtered or unexported fields
}
ClassifierChain orchestrates tiered query classification.
Routing logic:
- T0 (keyword): Always try first - keyword patterns bypass embedding
- T1/T2 (embedding): Only if no keyword match AND embedding != nil
- Default: Return T0 result with empty options if no tier matches
Thread-safe for concurrent ClassifyQuery calls.
func NewClassifierChain ¶
func NewClassifierChain(keyword *KeywordClassifier, embedding *EmbeddingClassifier, llmClassifiers ...*LLMClassifier) *ClassifierChain
NewClassifierChain creates a classifier chain with keyword and optional embedding/LLM classifiers.
All parameters may be nil. Chain will route queries through available tiers.
func (*ClassifierChain) ClassifyQuery ¶
func (c *ClassifierChain) ClassifyQuery(ctx context.Context, query string) *ClassificationResult
ClassifyQuery classifies a query through the tier chain.
Returns result from first tier that produces a match:
- T0: Keyword patterns (temporal, spatial, similarity, path, zone)
- T1/T2: Embedding similarity (if available and no keyword match)
- Default: T0 result with no filters if no tier matches
Returns nil if chain is nil or context cancelled.
type DomainExamples ¶
type DomainExamples struct {
Domain string `json:"domain"`
Version string `json:"version"`
Examples []Example `json:"examples"`
}
DomainExamples represents a collection of examples for a domain.
func LoadAllDomainExamples ¶
func LoadAllDomainExamples(filePaths []string) ([]*DomainExamples, error)
LoadAllDomainExamples loads and aggregates multiple domain example files.
func LoadDomainExamples ¶
func LoadDomainExamples(filePath string) (*DomainExamples, error)
LoadDomainExamples loads query examples from a JSON file.
type Embedder ¶
type Embedder interface {
// Embed generates a vector for a single text.
Embed(text string) ([]float32, error)
// EmbedBatch generates vectors for multiple texts.
EmbedBatch(texts []string) ([][]float32, error)
}
Embedder interface for vector generation.
This is a simplified interface for the query classifier that supports both single and batch embedding operations.
type EmbeddingClassifier ¶
type EmbeddingClassifier struct {
// contains filtered or unexported fields
}
EmbeddingClassifier classifies queries by finding similar domain examples.
The classifier starts with BM25 vectors (warm cache - no external service needed) and can be upgraded to neural vectors later when embeddings are available.
Thread-safe for concurrent FindBestMatch calls during vector upgrades.
func NewEmbeddingClassifier ¶
func NewEmbeddingClassifier(domains []*DomainExamples, threshold float64) *EmbeddingClassifier
NewEmbeddingClassifier creates classifier with BM25 warm cache.
Generates BM25 vectors for all examples immediately (no external service needed). Returns a classifier ready for statistical similarity matching.
func (*EmbeddingClassifier) FindBestMatch ¶
FindBestMatch finds the most similar example to the query.
Returns nil if no match above threshold or context cancelled. Thread-safe - uses read lock to allow concurrent calls during vector upgrades.
func (*EmbeddingClassifier) Threshold ¶
func (c *EmbeddingClassifier) Threshold() float64
Threshold returns the similarity threshold.
func (*EmbeddingClassifier) UpgradeVectors ¶
func (c *EmbeddingClassifier) UpgradeVectors(embedder Embedder) error
UpgradeVectors replaces current vectors with neural vectors from new embedder.
Thread-safe - uses write lock to prevent concurrent reads during upgrade. On error, preserves old vectors (rollback).
type Example ¶
type Example struct {
Query string `json:"query"` // Natural language query
Intent string `json:"intent"` // Intent category
Options map[string]any `json:"options"` // SearchOptions hints (optional)
Vector []float32 `json:"-"` // Runtime field - embedding vector (not serialized)
}
Example represents a single domain query example for intent classification.
type KeywordClassifier ¶
type KeywordClassifier struct{}
KeywordClassifier implements regex-based natural language query classification. Uses pattern matching to extract temporal, spatial, and intent information.
func NewKeywordClassifier ¶
func NewKeywordClassifier() *KeywordClassifier
NewKeywordClassifier creates a new keyword-based classifier.
func (*KeywordClassifier) ClassifyQuery ¶
func (k *KeywordClassifier) ClassifyQuery(_ context.Context, query string) *SearchOptions
ClassifyQuery analyzes a natural language query and populates SearchOptions. Detects temporal, spatial, similarity, path, and aggregation intents. Always returns non-nil SearchOptions with the original query preserved.
type LLMClassifier ¶
type LLMClassifier struct {
// contains filtered or unexported fields
}
LLMClassifier classifies queries by asking an LLM to return structured SearchOptions.
It is the T3 tier in the ClassifierChain — called only when T0 (keyword) and T1/T2 (embedding) tiers produce no confident match. When the LLM call fails or returns unparseable JSON, LLMClassifier returns an error so the chain can fall back gracefully rather than silently returning a wrong classification.
func NewLLMClassifier ¶
func NewLLMClassifier(client LLMClient, domains []*DomainExamples) *LLMClassifier
NewLLMClassifier creates an LLMClassifier with an LLM backend and optional domain examples.
domains may be nil or empty; when provided, a few representative examples are included in the prompt as few-shot context to improve classification accuracy.
func (*LLMClassifier) ClassifyQuery ¶
func (c *LLMClassifier) ClassifyQuery(ctx context.Context, query string) (*ClassificationResult, error)
ClassifyQuery classifies a natural language query using an LLM and returns a ClassificationResult at Tier 3.
Returns an error if:
- The LLM call fails (network, auth, quota)
- The LLM response is not valid JSON
- Context is cancelled before the call completes
type LLMClient ¶
type LLMClient interface {
// ClassifyQuery sends a structured classification prompt to the LLM and returns
// the raw JSON response string.
ClassifyQuery(ctx context.Context, prompt string) (string, error)
}
LLMClient is the minimal interface for sending a classification prompt to an LLM backend.
The returned string must be valid JSON matching llmResponse. Implementations are responsible for retries, timeouts, and backend-specific authentication.
type LLMClientAdapter ¶
type LLMClientAdapter struct {
// contains filtered or unexported fields
}
LLMClientAdapter adapts a graph/llm.Client to the query.LLMClient interface.
This allows the existing OpenAI-compatible LLM infrastructure (ollama, seminstruct, vLLM, OpenAI) to be used as the T3 classifier backend.
Each ClassifyQuery call is bounded by an internal timeout (see timeout field) so a slow upstream model cannot consume the parent ctx's full budget. ClassifierChain treats any LLM error as "fall through to default T0 classification" (see classifier_chain.go), so a timeout-induced error degrades gracefully instead of dropping the gateway's response.
func NewLLMClientAdapter ¶
func NewLLMClientAdapter(client llm.Client, timeout time.Duration) *LLMClientAdapter
NewLLMClientAdapter wraps a graph/llm.Client for use as a query.LLMClient with a bounded per-call LLM timeout. timeout=0 selects DefaultClassificationTimeout. Operators configure the value via capability.timeout (preferred) or endpoint.request_timeout in the model registry; graph-query's component reads those at construction time.
func (*LLMClientAdapter) ClassifyQuery ¶
ClassifyQuery sends the classification prompt to the LLM and returns the raw response.
The LLM call runs under a sub-context bounded by a.timeout, NOT under the parent ctx directly. This is the load-bearing detail: a slow upstream model can otherwise consume the entire gateway HTTP request budget; ClassifierChain's keyword-fallback then runs after the gateway has already returned an error to the client. Bounding here leaves margin for the rest of the response path so the chain's fallback actually reaches the HTTP layer.
If the parent ctx has less budget than a.timeout, the parent's deadline wins (context.WithTimeout uses the earlier of the two). If the parent ctx is already cancelled, the LLM call returns immediately with the inherited error and ClassifierChain falls through to the next tier.
type SearchOptions ¶
type SearchOptions struct {
Query string `json:"query,omitempty"`
GeoBounds *SpatialBounds `json:"geo_bounds,omitempty"`
TimeRange *TimeRange `json:"time_range,omitempty"`
Predicates []string `json:"predicates,omitempty"`
Types []string `json:"types,omitempty"`
KeyTerms []string `json:"key_terms,omitempty"`
RequireAllFilters bool `json:"require_all_filters,omitempty"`
UseEmbeddings bool `json:"use_embeddings,omitempty"`
Strategy SearchStrategy `json:"strategy,omitempty"`
Limit int `json:"limit,omitempty"`
Level int `json:"level,omitempty"`
MaxCommunities int `json:"max_communities,omitempty"`
PathIntent bool `json:"path_intent,omitempty"`
PathStartNode string `json:"path_start_node,omitempty"`
PathPredicates []string `json:"path_predicates,omitempty"`
// AggregationType specifies the type of aggregation (count, avg, sum, min, max).
// Use the AggregationCount / AggregationAvg / … constants.
AggregationType string `json:"aggregation_type,omitempty"`
// AggregationField is the property or attribute to aggregate on (e.g. "temperature").
// Empty means aggregate over entity count.
AggregationField string `json:"aggregation_field,omitempty"`
// RankingIntent is true when the query requests a ranked/top-N result set.
RankingIntent bool `json:"ranking_intent,omitempty"`
}
SearchOptions provides declarative search configuration.
func (*SearchOptions) HasIndexFilters ¶
func (o *SearchOptions) HasIndexFilters() bool
HasIndexFilters returns true if any index filters are specified.
func (*SearchOptions) InferStrategy ¶
func (o *SearchOptions) InferStrategy() SearchStrategy
InferStrategy determines the best search strategy based on options.
func (*SearchOptions) SetDefaults ¶
func (o *SearchOptions) SetDefaults()
SetDefaults applies default values for unset options.
type SearchStrategy ¶
type SearchStrategy string
SearchStrategy represents the type of search to perform.
const ( StrategyGraphRAG SearchStrategy = "graphrag" StrategyGeoGraphRAG SearchStrategy = "geo_graphrag" StrategyTemporalGraphRAG SearchStrategy = "temporal_graphrag" StrategyHybridGraphRAG SearchStrategy = "hybrid_graphrag" StrategyPathRAG SearchStrategy = "pathrag" StrategySemantic SearchStrategy = "semantic" StrategyExact SearchStrategy = "exact" StrategyAggregation SearchStrategy = "aggregation" )
Search strategy constants for query routing.