query

package
v1.0.0-beta.160 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package query provides a clean interface for reading graph data from NATS KV buckets.

Index

Constants

View Source
const (
	AggregationCount = "count"
	AggregationAvg   = "avg"
	AggregationSum   = "sum"
	AggregationMin   = "min"
	AggregationMax   = "max"
)

Aggregation type constants for aggregation queries.

View Source
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.

View Source
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

func (c *EmbeddingClassifier) FindBestMatch(ctx context.Context, query string) (*Example, float64)

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

func (a *LLMClientAdapter) ClassifyQuery(ctx context.Context, prompt string) (string, error)

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.

type SpatialBounds

type SpatialBounds struct {
	North float64 `json:"north"`
	South float64 `json:"south"`
	East  float64 `json:"east"`
	West  float64 `json:"west"`
}

SpatialBounds represents geographic bounding box.

type TimeRange

type TimeRange struct {
	Start time.Time `json:"start"`
	End   time.Time `json:"end"`
}

TimeRange represents temporal query bounds.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL