Documentation
¶
Overview ¶
Package routing provides tool selection using a fast micro LLM.
Description ¶
The routing package implements a ToolRouter that uses a dedicated micro LLM (like granite4:micro-h) to quickly classify queries and select the appropriate tool before the main reasoning LLM processes the request.
This separation of concerns allows:
- Fast tool selection (~50-100ms) vs full LLM reasoning (~10-30s)
- Better resource utilization (small model for routing, large for reasoning)
- Parallel model execution on GPUs with sufficient VRAM
Thread Safety ¶
All types in this package are designed for concurrent use.
Index ¶
- Constants
- func BuildAssignmentFromSteps(steps []crs.StepRecord) map[string]bool
- func ExtractQueryFromMetadata(metadata map[string]string) string
- func ExtractQueryFromParams(params map[string]any) string
- func ExtractQueryTerms(query string) map[string]bool
- func JaccardSimilarity(a, b map[string]bool) float64
- func NewRouterAdapter(router ToolRouter) agent.ToolRouter
- func RecordModelWarmup(model string, durationSec float64, success bool)
- func RecordParamExtractionLatency(model, status string, durationSec float64)
- func RecordParamExtractionTotal(model, status string)
- func RecordRouterInit(model string, success bool, reason string)
- func RecordRoutingConfidence(model string, confidence float64)
- func RecordRoutingError(model, errorType string)
- func RecordRoutingFallback(model, reason string)
- func RecordRoutingLatency(model, status string, durationSec float64)
- func RecordRoutingSelection(model, tool string)
- func RecordSpeculativeExtraction(outcome string, durationSec float64)
- func RecordUCB1AllBlocked()
- func RecordUCB1BlockedSelection(tool, reasonType string)
- func RecordUCB1CacheHit()
- func RecordUCB1CacheInvalidation(reason string)
- func RecordUCB1CacheMiss()
- func RecordUCB1ForcedMove(tool string)
- func RecordUCB1ScoringLatency(durationSec float64)
- func RecordUCB1Selection(tool string, score, proofPenalty, explorationBonus float64)
- type AgentPreFilterResult
- type AllBlockedResult
- type BM25Index
- type BadgerRouterCacheStore
- type CacheMetrics
- type ClauseChecker
- type CodeContext
- type EscalatingRouter
- type ForcedMoveChecker
- func (c *ForcedMoveChecker) CheckAllBlocked(availableTools []string, clauseChecker ClauseChecker, ...) AllBlockedResult
- func (c *ForcedMoveChecker) CheckForcedMove(availableTools []string, clauseChecker ClauseChecker, ...) ForcedMoveResult
- func (c *ForcedMoveChecker) CheckForcedMoveFromSteps(availableTools []string, steps []crs.StepRecord, clauseChecker ClauseChecker) ForcedMoveResult
- type ForcedMoveCheckerConfig
- type ForcedMoveResult
- type Granite4Router
- func (r *Granite4Router) Close() error
- func (r *Granite4Router) FilterBatch(ctx context.Context, prompt string) (string, error)
- func (r *Granite4Router) Model() string
- func (r *Granite4Router) SelectTool(ctx context.Context, query string, availableTools []ToolSpec, ...) (*ToolSelection, error)
- func (r *Granite4Router) WarmRouter(ctx context.Context, lifecycle providers.ModelLifecycleManager) error
- type ParamExtractor
- func (e *ParamExtractor) ExtractParams(ctx context.Context, query string, toolName string, paramSchemas []ParamSchema, ...) (map[string]any, error)
- func (e *ParamExtractor) IsEnabled() bool
- func (e *ParamExtractor) ResolveConceptualSymbol(ctx context.Context, query string, candidates []agent.SymbolCandidate, ...) (string, error)
- type ParamExtractorConfig
- type ParamSchema
- type PreFilter
- type PreFilterResult
- type PromptBuilder
- type PromptData
- type RouterAdapter
- func (a *RouterAdapter) Close() error
- func (a *RouterAdapter) Model() string
- func (a *RouterAdapter) SelectTool(ctx context.Context, query string, availableTools []agent.ToolRouterSpec, ...) (*agent.ToolRouterSelection, error)
- func (a *RouterAdapter) WarmRouter(ctx context.Context, lifecycle providers.ModelLifecycleManager) error
- type RouterCacheStore
- type RouterConfig
- type RouterError
- type RouterResult
- type SelectionCounts
- type SemanticConfig
- type StateKeyBuilder
- type ToolCallHistory
- func (h *ToolCallHistory) Add(sig ToolCallSignature)
- func (h *ToolCallHistory) Clear()
- func (h *ToolCallHistory) Config() SemanticConfig
- func (h *ToolCallHistory) GetCallsForTool(tool string) []ToolCallSignature
- func (h *ToolCallHistory) GetSimilarity(tool string, queryTerms map[string]bool) (float64, *ToolCallSignature)
- func (h *ToolCallHistory) IsExactDuplicate(tool, rawQuery string) (bool, *ToolCallSignature)
- func (h *ToolCallHistory) Len() int
- func (h *ToolCallHistory) MaxSize() int
- func (h *ToolCallHistory) Trim(size int)
- type ToolCallSignature
- type ToolEmbeddingCache
- type ToolError
- type ToolHistoryEntry
- type ToolRouter
- type ToolScore
- type ToolSelection
- type ToolSelectionCache
- func (c *ToolSelectionCache) Clear()
- func (c *ToolSelectionCache) Get(key string, currentGen int64) (string, float64, bool)
- func (c *ToolSelectionCache) InvalidateByGeneration(oldGeneration int64) int
- func (c *ToolSelectionCache) Metrics() CacheMetrics
- func (c *ToolSelectionCache) Put(key, tool string, score float64, generation int64)
- func (c *ToolSelectionCache) Size() int
- type ToolSelectionCacheConfig
- type ToolSpec
- type ToolSubstitution
- type UCB1Scorer
- func (s *UCB1Scorer) ScoreTools(routerResults []RouterResult, proofIndex crs.ProofIndexView, ...) []ToolScore
- func (s *UCB1Scorer) ScoreToolsWithSemantic(routerResults []RouterResult, proofIndex crs.ProofIndexView, ...) []ToolScore
- func (s *UCB1Scorer) SelectBest(scores []ToolScore) (string, ToolScore)
- type UCB1ScorerConfig
Constants ¶
const ( // SemanticDuplicateThreshold is the similarity threshold for blocking. // Calls with similarity >= this are considered semantic duplicates. SemanticDuplicateThreshold = 0.8 // SemanticPenaltyThreshold is the similarity threshold for penalizing. // Calls with similarity >= this (but < duplicate threshold) get penalized. SemanticPenaltyThreshold = 0.3 // DefaultSemanticPenaltyWeight is the default UCB1 penalty weight. // SemanticPenalty = similarity * weight. DefaultSemanticPenaltyWeight = 0.5 )
const ( ErrCodeTimeout = "ROUTER_TIMEOUT" ErrCodeLowConfidence = "ROUTER_LOW_CONFIDENCE" ErrCodeParseError = "ROUTER_PARSE_ERROR" ErrCodeNoTools = "ROUTER_NO_TOOLS" ErrCodeEmptyResponse = "ROUTER_EMPTY_RESPONSE" // GR-Phase1: Model returned empty response )
Common router error codes.
const DefaultMaxProofNumber = 100.0
DefaultMaxProofNumber is the default maximum proof number for normalization. Proof numbers in PN-MCTS typically range from 1-100 for most tool selections.
const MaxToolCallHistorySize = 100
MaxToolCallHistorySize is the maximum number of calls to retain in history. Older calls are evicted when this limit is reached. R1.1 Fix: Prevent unbounded memory growth. Deprecated: Use SemanticConfig.MaxHistorySize instead.
Variables ¶
This section is empty.
Functions ¶
func BuildAssignmentFromSteps ¶
func BuildAssignmentFromSteps(steps []crs.StepRecord) map[string]bool
BuildAssignmentFromSteps builds a variable assignment from step history.
Description:
Creates the assignment map used for clause checking. Includes: - prev_tool:<name> for the previous tool - outcome:<result> for the previous outcome - error:<category> for the previous error category - prev_prev_tool:<name> for the tool before previous
Inputs:
steps - Step history from CRS.
Outputs:
map[string]bool - The variable assignment.
Thread Safety: Safe for concurrent use.
func ExtractQueryFromMetadata ¶
ExtractQueryFromMetadata extracts query from trace step metadata.
Description:
Trace steps store parameters in Metadata as string values. This function extracts the query for semantic comparison.
Inputs:
metadata - Map of parameter name to string value.
Outputs:
string - The query string, or empty if not found.
func ExtractQueryFromParams ¶
ExtractQueryFromParams extracts the query/pattern parameter from tool params.
Description:
Different tools use different parameter names for their "query" concept. This function extracts the relevant parameter for semantic comparison.
Inputs:
params - Map of parameter name to value.
Outputs:
string - The query/pattern string, or empty if not found.
func ExtractQueryTerms ¶
func JaccardSimilarity ¶
JaccardSimilarity calculates the Jaccard similarity between two term sets.
Description:
Jaccard = |intersection| / |union| Returns 0.0 if either set is empty, 1.0 if identical.
Inputs:
a, b - Term sets to compare. Nil maps are treated as empty.
Outputs:
float64 - Similarity score in range [0.0, 1.0].
Thread Safety: Safe for concurrent use (read-only on inputs).
func NewRouterAdapter ¶
func NewRouterAdapter(router ToolRouter) agent.ToolRouter
NewRouterAdapter creates an adapter that implements agent.ToolRouter.
Inputs ¶
- router: The underlying ToolRouter (Granite4Router, EscalatingRouter, etc).
Outputs ¶
- agent.ToolRouter: The adapted router.
func RecordModelWarmup ¶
RecordModelWarmup records model warmup metrics.
Inputs:
model - The model name. durationSec - Warmup duration in seconds. success - Whether warmup succeeded.
func RecordParamExtractionLatency ¶
RecordParamExtractionLatency records the latency of a parameter extraction call.
Inputs:
model - The extractor model name. status - "success", "timeout", "error", or "parse_error". durationSec - Duration in seconds.
func RecordParamExtractionTotal ¶
func RecordParamExtractionTotal(model, status string)
RecordParamExtractionTotal records a parameter extraction attempt.
Inputs:
model - The extractor model name. status - "success", "timeout", "error", "parse_error", or "fallback".
func RecordRouterInit ¶
RecordRouterInit records router initialization metrics.
Description ¶
Tracks router initialization attempts, successes, and failures. Use this to monitor router health and identify initialization issues.
Inputs ¶
model - The router model name.
success - Whether initialization succeeded.
reason - Failure reason if !success (e.g., "model_manager_nil", "warmup_failed").
Empty string if success=true.
Thread Safety ¶
Safe for concurrent use.
func RecordRoutingConfidence ¶
RecordRoutingConfidence records a confidence score.
Inputs:
model - The router model name. confidence - The confidence score (0.0-1.0).
func RecordRoutingError ¶
func RecordRoutingError(model, errorType string)
RecordRoutingError records a routing error.
Inputs:
model - The router model name. errorType - Error type (e.g., "timeout", "parse_error").
func RecordRoutingFallback ¶
func RecordRoutingFallback(model, reason string)
RecordRoutingFallback records a fallback to main LLM.
Inputs:
model - The router model name (empty if router disabled). reason - "error", "low_confidence", or "disabled".
func RecordRoutingLatency ¶
RecordRoutingLatency records the latency of a routing decision.
Inputs:
model - The router model name. status - "success", "error", or "low_confidence". durationSec - Duration in seconds.
func RecordRoutingSelection ¶
func RecordRoutingSelection(model, tool string)
RecordRoutingSelection records a successful tool selection.
Inputs:
model - The router model name. tool - The selected tool name.
func RecordSpeculativeExtraction ¶
RecordSpeculativeExtraction records the outcome and duration of a speculative extraction.
Description:
CRS-14: Tracks speculative extraction outcomes to determine hit/mispredict rates and latency impact for ROI analysis.
Inputs:
outcome - "hit", "hit_conversion_failed", "mispredict_reextracted",
"mispredict_regex_fallback", or "error".
durationSec - Wall-clock time from speculative launch to outcome.
Thread Safety: Safe for concurrent use (Prometheus metrics are thread-safe).
func RecordUCB1AllBlocked ¶
func RecordUCB1AllBlocked()
RecordUCB1AllBlocked records when all tools are blocked.
func RecordUCB1BlockedSelection ¶
func RecordUCB1BlockedSelection(tool, reasonType string)
RecordUCB1BlockedSelection records a tool blocked by clause.
Inputs:
tool - The blocked tool name. reasonType - Category of the blocking reason.
func RecordUCB1CacheInvalidation ¶
func RecordUCB1CacheInvalidation(reason string)
RecordUCB1CacheInvalidation records a cache invalidation.
Inputs:
reason - "ttl_expired", "generation_changed", or "evicted".
func RecordUCB1ForcedMove ¶
func RecordUCB1ForcedMove(tool string)
RecordUCB1ForcedMove records a forced move.
Inputs:
tool - The forced tool name.
func RecordUCB1ScoringLatency ¶
func RecordUCB1ScoringLatency(durationSec float64)
RecordUCB1ScoringLatency records UCB1 scoring duration.
Inputs:
durationSec - Duration in seconds.
func RecordUCB1Selection ¶
RecordUCB1Selection records a UCB1 tool selection.
Inputs:
tool - The selected tool name. score - The UCB1 final score. proofPenalty - The proof penalty applied. explorationBonus - The exploration bonus.
Types ¶
type AgentPreFilterResult ¶
type AgentPreFilterResult struct {
// NarrowedSpecs is the filtered set of tool specs for the router.
NarrowedSpecs []agent.ToolRouterSpec
// ForcedTool is set when the pre-filter deterministically selects a tool.
ForcedTool string
// ForcedReason explains why the tool was forced.
ForcedReason string
// Duration is how long the pre-filter took.
Duration time.Duration
// AppliedRules lists the rules that fired during filtering.
AppliedRules []string
// OriginalCount is the number of tools before filtering.
OriginalCount int
// NarrowedCount is the number of tools after filtering.
NarrowedCount int
}
AgentPreFilterResult contains the pre-filter output using agent package types.
Description:
Same as PreFilterResult but uses agent.ToolRouterSpec instead of routing.ToolSpec, for direct use in the execute phase.
type AllBlockedResult ¶
type AllBlockedResult struct {
// AllBlocked is true if every tool is blocked by clauses.
AllBlocked bool `json:"all_blocked"`
// BlockedCount is the number of blocked tools.
BlockedCount int `json:"blocked_count"`
// TotalTools is the total number of tools checked.
TotalTools int `json:"total_tools"`
// Reasons maps tool names to block reasons.
Reasons map[string]string `json:"reasons,omitempty"`
}
AllBlockedResult contains the result when all tools are blocked.
type BM25Index ¶
type BM25Index struct {
// contains filtered or unexported fields
}
BM25Index is a pre-built inverted index over tool descriptions.
Description ¶
Implements Okapi BM25 ranking over a corpus of tool "documents" where each document is the concatenation of a tool's keywords and use_when text. At query time, BM25 produces a ranked score for each tool that is proportional to how well its document matches the query terms, weighted by term rarity across all tools (IDF).
This replaces plain substring keyword counting, which has no IDF weighting and no morphological flexibility.
Thread Safety ¶
BM25Index is immutable after construction via BuildBM25Index. Safe for concurrent use without additional synchronization.
func BuildBM25Index ¶
BuildBM25Index constructs a BM25Index from a slice of ToolSpecs.
Description ¶
Each tool's "document" is built from its keywords and use_when text. Tokenization reuses ExtractQueryTerms from semantic.go (same package), which handles camelCase splitting and noise-word removal. IDF is computed with Lucene-style add-one smoothing to avoid zero division.
Inputs ¶
- specs: Tool specifications to index. Empty slice returns a valid but empty index that will produce zero scores for all queries.
Outputs ¶
- *BM25Index: The constructed index. Never nil.
Thread Safety ¶
The returned index is immutable and safe for concurrent use.
func (*BM25Index) IsEmpty ¶
IsEmpty reports whether the index contains no tool documents.
Description ¶
Returns true for an index built from nil or empty specs (the initial state of a lazily-initialized PreFilter). Used by scoreHybrid to trigger the one-time corpus build when allSpecs first becomes available.
Thread Safety ¶
The BM25Index is immutable after construction. IsEmpty() is safe to call concurrently, but the caller must hold at least a read lock on the enclosing PreFilter.bm25mu when reading the *BM25Index pointer itself.
func (*BM25Index) Score ¶
Score computes the BM25 score for each tool given a query string.
Description ¶
Tokenizes the query, then for each tool computes:
score(tool, query) = Σ_t [ idf(t) × (tf(t,doc) × (k1+1)) / (tf(t,doc) + k1 × (1 - b + b × dl/avgdl)) ]
where t ranges over unique query terms present in the tool's document. Scores are normalized to [0.0, 1.0] by dividing by the maximum score.
Inputs ¶
- query: The raw query string. Empty query returns empty scores map.
Outputs ¶
- map[string]float64: Tool name → normalized BM25 score in [0.0, 1.0]. Tools with zero BM25 score are omitted from the result.
Thread Safety ¶
Safe for concurrent use. Does not modify the index.
type BadgerRouterCacheStore ¶
type BadgerRouterCacheStore struct {
// contains filtered or unexported fields
}
BadgerRouterCacheStore implements RouterCacheStore backed by a BadgerDB instance. The DB is expected to be a service-global singleton opened at startup with its own path, separate from per-project CRS journals.
Description ¶
Vectors are gob-encoded as map[string][]float32. Encoding is compact (~4 bytes/float32; 30 tools × 768 dims ≈ 90KB) and fast (~5µs encode/decode). The key is the corpus hash prefixed with the storage layout version string.
TTL is enforced by BadgerDB's native GC — no application-level expiry check is needed. Expired keys return ErrKeyNotFound, which this store treats as a cache miss.
Thread Safety ¶
Safe for concurrent use. BadgerDB transactions are per-goroutine.
func NewBadgerRouterCacheStore ¶
func NewBadgerRouterCacheStore(db *badgerstore.DB, ttl time.Duration, logger *slog.Logger) *BadgerRouterCacheStore
NewBadgerRouterCacheStore creates a BadgerRouterCacheStore backed by the given DB instance.
Description ¶
The DB must be opened by the caller (typically in main) and must not be closed before the store is done being used. The caller is responsible for the DB lifecycle — this store does not own the DB.
Inputs ¶
- db: Opened BadgerDB wrapper. Must not be nil.
- ttl: Lifetime for each cached entry. Pass 0 to use the default (7 days).
- logger: Logger for cache hit/miss diagnostics. May be nil.
Outputs ¶
- *BadgerRouterCacheStore: Ready-to-use store. Never nil.
Thread Safety ¶
The returned store is safe for concurrent use.
func (*BadgerRouterCacheStore) LoadEmbeddings ¶
func (s *BadgerRouterCacheStore) LoadEmbeddings(ctx context.Context, corpusHash string) (map[string][]float32, error)
LoadEmbeddings retrieves cached unit-normalized tool embedding vectors.
Description ¶
Looks up the key routing/emb/v1/{corpusHash}. Returns (nil, nil) on miss (key not found or TTL expired). Returns (nil, error) on storage or decode failure. Returns (vectors, nil) on success.
Inputs ¶
- ctx: Context for cancellation.
- corpusHash: Hex SHA256 of the tool corpus + model name (from computeCorpusHash).
Outputs ¶
- map[string][]float32: Tool name → unit-normalized vector. Nil on miss or error.
- error: Non-nil on storage or decode failure. Nil on miss and on success.
Thread Safety ¶
Safe for concurrent use.
func (*BadgerRouterCacheStore) SaveEmbeddings ¶
func (s *BadgerRouterCacheStore) SaveEmbeddings(ctx context.Context, corpusHash string, vectors map[string][]float32) error
SaveEmbeddings persists unit-normalized tool embedding vectors with a 7-day TTL.
Description ¶
Encodes vectors as gob-encoded map[string][]float32 and writes to BadgerDB under the key routing/emb/v1/{corpusHash} with the configured TTL. After TTL expires, the key is invisible to LoadEmbeddings (returns cache miss).
Inputs ¶
- ctx: Context for cancellation.
- corpusHash: Hex SHA256 of the tool corpus + model name.
- vectors: Unit-normalized embedding vectors, keyed by tool name. Must not be empty.
Outputs ¶
- error: Non-nil on encode or storage failure.
Thread Safety ¶
Safe for concurrent use.
type CacheMetrics ¶
type CacheMetrics struct {
// Hits is the number of cache hits.
Hits int64 `json:"hits"`
// Misses is the number of cache misses.
Misses int64 `json:"misses"`
// Invalidations is the number of invalidated entries.
Invalidations int64 `json:"invalidations"`
// HitRate is hits / (hits + misses).
HitRate float64 `json:"hit_rate"`
// Size is the current number of entries.
Size int `json:"size"`
}
CacheMetrics contains cache performance statistics.
type ClauseChecker ¶
type ClauseChecker interface {
// IsBlocked checks if the assignment violates any clause.
//
// Inputs:
// - assignment: The variable assignment to check.
//
// Outputs:
// - bool: True if blocked by a clause.
// - string: Reason for blocking.
IsBlocked(assignment map[string]bool) (bool, string)
}
ClauseChecker checks if an assignment violates learned clauses.
Description:
Interface for checking clause violations without tight coupling to CRS.
func NewClauseCheckerFromConstraintIndex ¶
func NewClauseCheckerFromConstraintIndex(view crs.ConstraintIndexView) ClauseChecker
NewClauseCheckerFromConstraintIndex creates a ClauseChecker from a ConstraintIndexView.
Inputs:
view - The constraint index view.
Outputs:
ClauseChecker - The adapter.
type CodeContext ¶
type CodeContext struct {
// Language is the primary programming language (e.g., "go", "python").
Language string `json:"language,omitempty"`
// Files is the number of files currently loaded/indexed.
Files int `json:"files,omitempty"`
// Symbols is the number of symbols available for search.
Symbols int `json:"symbols,omitempty"`
// CurrentFile is the file currently being viewed/edited.
CurrentFile string `json:"current_file,omitempty"`
// RecentTools lists recently used tools (for context awareness).
// DEPRECATED: Use ToolHistory instead for richer context.
RecentTools []string `json:"recent_tools,omitempty"`
// PreviousErrors contains tools that failed in this session.
// The router should avoid suggesting these unless it can fix the issue.
PreviousErrors []ToolError `json:"previous_errors,omitempty"`
// ToolHistory contains the sequence of tools used with their results.
// This enables history-aware routing where the router can see what
// was already tried and what was learned from each tool.
ToolHistory []ToolHistoryEntry `json:"tool_history,omitempty"`
// Progress describes the current state of information gathering.
// Example: "Found 3 entry points, read 2 files, identified main handler"
Progress string `json:"progress,omitempty"`
// StepNumber is the current execution step (1-indexed).
StepNumber int `json:"step_number,omitempty"`
}
CodeContext provides optional context about the codebase.
Description ¶
Gives the router additional context to make better decisions. For example, knowing the primary language helps select language-specific tools.
History-Aware Routing ¶
This struct is designed to leverage Mamba2's O(n) linear complexity and 1M token context window. By including tool history with summaries, the router can make informed decisions about what information is still needed rather than suggesting the same tools repeatedly.
type EscalatingRouter ¶
type EscalatingRouter struct {
// contains filtered or unexported fields
}
EscalatingRouter wraps a primary ToolRouter and escalates to a larger model when the primary router's confidence is below a threshold.
Description:
Delegates to the primary router first. If the primary returns a selection with confidence below the threshold AND an escalation router is configured, re-routes the query through the escalation router with the full tool set (bypassing the prefilter). If escalation fails, falls back to the primary result (best effort).
Inputs:
primary - The fast primary router (e.g., granite4:micro-h). Must not be nil. escalation - The larger escalation router. Nil disables escalation. threshold - Minimum primary confidence to skip escalation. Default: 0.7. allSpecs - Full tool set for escalation (bypasses prefilter). timeout - Maximum time for escalation call. Default: 3s. logger - Logger instance. Must not be nil.
Thread Safety: Safe for concurrent use (delegates to thread-safe routers).
func NewEscalatingRouter ¶
func NewEscalatingRouter(primary ToolRouter, escalation ToolRouter, threshold float64, allSpecs []ToolSpec, timeout time.Duration, logger *slog.Logger) *EscalatingRouter
NewEscalatingRouter creates a new EscalatingRouter.
Description:
Creates a router that delegates to primary first, then escalates to a larger model when confidence is low. If escalation is nil, behaves identically to the primary router (zero overhead).
Inputs:
primary - The fast primary router. Must not be nil. escalation - The larger escalation router. Nil disables escalation. threshold - Minimum primary confidence to skip escalation. allSpecs - Full tool set for escalation calls. timeout - Maximum time for escalation. Zero uses default (3s). logger - Logger instance. Must not be nil.
Outputs:
*EscalatingRouter - The constructed router. Never nil.
Limitations:
allSpecs must be set at construction time. If the tool set changes, a new EscalatingRouter must be created.
Assumptions:
Both primary and escalation routers are already warmed.
func (*EscalatingRouter) Close ¶
func (r *EscalatingRouter) Close() error
Close releases resources held by both routers.
Outputs:
error - Non-nil if either router's Close fails.
func (*EscalatingRouter) Model ¶
func (r *EscalatingRouter) Model() string
Model returns the primary router's model name.
Outputs:
string - The model name of the primary router.
func (*EscalatingRouter) SelectTool ¶
func (r *EscalatingRouter) SelectTool(ctx context.Context, query string, availableTools []ToolSpec, codeCtx *CodeContext) (*ToolSelection, error)
SelectTool chooses the best tool, escalating to a larger model if needed.
Description:
- Calls the primary router with the prefiltered candidate set.
- If the primary's confidence >= threshold, returns immediately.
- If confidence is low and escalation is configured, calls the escalation router with the FULL tool set (all 55 tools).
- If escalation fails or times out, returns the primary result (best effort).
Inputs:
ctx - Context for cancellation/timeout. Must not be nil. query - The user's question or request. availableTools - Prefiltered tools for the primary router. codeCtx - Optional code context (symbols, files loaded, etc.)
Outputs:
*ToolSelection - The selected tool and confidence. error - Non-nil only if the primary router fails.
Thread Safety: Safe for concurrent use.
func (*EscalatingRouter) SetAllSpecs ¶
func (r *EscalatingRouter) SetAllSpecs(specs []ToolSpec)
SetAllSpecs sets the full tool set used for escalation calls.
Description:
This is a post-construction setter for cases where the full tool set is not available at EscalatingRouter creation time (e.g., when tool registration happens after router wiring). Must be called before the first query if escalation is enabled.
Inputs:
specs - The full tool spec set. Nil disables escalation passthrough.
Thread Safety: NOT safe for concurrent use with SelectTool. Call during init only.
type ForcedMoveChecker ¶
type ForcedMoveChecker struct {
// contains filtered or unexported fields
}
ForcedMoveChecker detects when only one tool is viable.
Description:
Uses unit propagation logic from CDCL to detect forced moves. A move is forced when all but one tool are blocked by learned clauses. This is analogous to SAT unit propagation: - Each tool is a "variable" - Clauses constrain which tools can be used - When only one tool satisfies all clauses, it's forced
Thread Safety: Safe for concurrent use (stateless).
func NewForcedMoveChecker ¶
func NewForcedMoveChecker() *ForcedMoveChecker
NewForcedMoveChecker creates a new forced move checker.
Outputs:
*ForcedMoveChecker - The checker instance.
func NewForcedMoveCheckerWithConfig ¶
func NewForcedMoveCheckerWithConfig(config *ForcedMoveCheckerConfig) *ForcedMoveChecker
NewForcedMoveCheckerWithConfig creates a checker with config.
Inputs:
config - The configuration. If nil, uses defaults.
Outputs:
*ForcedMoveChecker - The checker instance.
func (*ForcedMoveChecker) CheckAllBlocked ¶
func (c *ForcedMoveChecker) CheckAllBlocked( availableTools []string, clauseChecker ClauseChecker, currentAssignment map[string]bool, ) AllBlockedResult
CheckAllBlocked checks if all available tools are blocked.
Description:
When all tools are blocked, the agent must synthesize an answer from gathered information. This detects that situation.
Inputs:
availableTools - List of tool names. clauseChecker - The clause checker. currentAssignment - Current variable assignment.
Outputs:
AllBlockedResult - Result indicating if all blocked.
Thread Safety: Safe for concurrent use.
func (*ForcedMoveChecker) CheckForcedMove ¶
func (c *ForcedMoveChecker) CheckForcedMove( availableTools []string, clauseChecker ClauseChecker, currentAssignment map[string]bool, ) ForcedMoveResult
CheckForcedMove uses unit propagation to detect if only one tool is viable.
Description:
For each available tool, checks if selecting it would violate any learned clause. If only one tool passes all clause checks, it's forced.
Inputs:
availableTools - List of tool names that could be selected. clauseChecker - The clause checker for violation detection. currentAssignment - Current variable assignment (step history context).
Outputs:
ForcedMoveResult - Result containing forced tool or viable count.
Thread Safety: Safe for concurrent use.
func (*ForcedMoveChecker) CheckForcedMoveFromSteps ¶
func (c *ForcedMoveChecker) CheckForcedMoveFromSteps( availableTools []string, steps []crs.StepRecord, clauseChecker ClauseChecker, ) ForcedMoveResult
CheckForcedMoveFromSteps is a convenience method using CRS step history.
Description:
Builds the current assignment from step history and checks for forced moves.
Inputs:
availableTools - List of tool names. steps - Step history from CRS. clauseChecker - The clause checker.
Outputs:
ForcedMoveResult - Result containing forced tool or viable count.
Thread Safety: Safe for concurrent use.
type ForcedMoveCheckerConfig ¶
type ForcedMoveCheckerConfig struct {
// Logger for debug output. If nil, uses default.
Logger *slog.Logger
}
ForcedMoveCheckerConfig configures the checker.
type ForcedMoveResult ¶
type ForcedMoveResult struct {
// IsForced is true if exactly one tool is viable.
IsForced bool `json:"is_forced"`
// ForcedTool is the tool name if IsForced is true.
ForcedTool string `json:"forced_tool,omitempty"`
// ViableCount is the number of viable (non-blocked) tools.
ViableCount int `json:"viable_count"`
// BlockedTools lists tools blocked by clauses.
BlockedTools []string `json:"blocked_tools,omitempty"`
// BlockReasons maps blocked tool to the reason.
BlockReasons map[string]string `json:"block_reasons,omitempty"`
}
ForcedMoveResult contains the result of forced move detection.
Description:
When unit propagation determines that only one tool is viable (all others are blocked by learned clauses), that tool is "forced". This allows the agent to skip the router entirely in deterministic situations.
type Granite4Router ¶
type Granite4Router struct {
// contains filtered or unexported fields
}
Granite4Router implements ToolRouter using a fast model for tool routing.
Description ¶
Uses a fast model (e.g., IBM Granite4 with hybrid Mamba-2 architecture) for ultra-fast tool routing. The ChatClient interface allows any provider (Ollama, Anthropic, OpenAI, Gemini) to be used as the routing backend.
CB-60: Refactored to use providers.ChatClient instead of direct MultiModelManager dependency, enabling any LLM provider for tool routing.
Thread Safety ¶
Granite4Router is safe for concurrent use.
func NewGranite4Router ¶
func NewGranite4Router(chatClient providers.ChatClient, config RouterConfig) (*Granite4Router, error)
NewGranite4Router creates a new router with the specified configuration.
Description ¶
Creates a router that uses a fast model for tool selection. The ChatClient interface allows any provider (Ollama, Anthropic, OpenAI, Gemini) to be used.
CB-60: Accepts providers.ChatClient instead of *llm.MultiModelManager.
Inputs ¶
- chatClient: ChatClient for sending routing queries. Must not be nil.
- config: Router configuration.
Outputs ¶
- *Granite4Router: Configured router.
- error: Non-nil if initialization fails.
Example ¶
factory := providers.NewProviderFactory(modelManager) client, _ := factory.CreateChatClient(cfg) config := routing.DefaultRouterConfig() router, err := routing.NewGranite4Router(client, config)
func NewGranite4RouterWithDefaults ¶
func NewGranite4RouterWithDefaults(ollamaEndpoint string) (*Granite4Router, error)
NewGranite4RouterWithDefaults creates a router with default configuration.
Description ¶
Convenience constructor that creates a MultiModelManager wrapped in an OllamaChatAdapter and uses default configuration. For more control or non-Ollama providers, use NewGranite4Router with a custom ChatClient.
Inputs ¶
- ollamaEndpoint: Ollama server URL.
Outputs ¶
- *Granite4Router: Configured router.
- error: Non-nil if initialization fails.
func (*Granite4Router) Close ¶
func (r *Granite4Router) Close() error
Close releases any resources held by the router.
Description ¶
Currently a no-op since the MultiModelManager is shared. Future implementations might unload the router model here.
func (*Granite4Router) FilterBatch ¶
FilterBatch evaluates a batch of tool calls and returns filter decisions.
Description ¶
Uses the router model to decide which tool calls in a batch should be executed (KEEP) vs skipped (SKIP). This helps reduce redundant tool calls when the main LLM requests multiple similar operations.
GR-39a: Implements the BatchFilterer interface for semantic deduplication.
Inputs ¶
- ctx: Context for cancellation/timeout.
- prompt: Filter prompt containing tool calls with similarity scores.
Outputs ¶
- string: Response with KEEP/SKIP decisions (e.g., "1:KEEP 2:SKIP 3:KEEP").
- error: Non-nil if the filter call fails.
Thread Safety ¶
This method is safe for concurrent use.
func (*Granite4Router) Model ¶
func (r *Granite4Router) Model() string
Model returns the model being used for routing.
func (*Granite4Router) SelectTool ¶
func (r *Granite4Router) SelectTool(ctx context.Context, query string, availableTools []ToolSpec, codeContext *CodeContext) (*ToolSelection, error)
SelectTool chooses the best tool for the given query.
Description ¶
Uses the micro LLM to analyze the query and select the most appropriate tool. Returns an error if the selection fails or confidence is too low.
Inputs ¶
- ctx: Context for cancellation/timeout.
- query: The user's question or request.
- availableTools: Tools currently available.
- codeContext: Optional context about the codebase.
Outputs ¶
- *ToolSelection: The selected tool with confidence.
- error: Non-nil if routing fails.
func (*Granite4Router) WarmRouter ¶
func (r *Granite4Router) WarmRouter(ctx context.Context, lifecycle providers.ModelLifecycleManager) error
WarmRouter pre-loads the routing model using the provided lifecycle manager.
Description ¶
Warms up the router model so the first routing request doesn't incur model loading latency. Should be called during initialization.
CB-60: Refactored to accept a ModelLifecycleManager instead of using the internal model manager. For cloud providers, warmup is a no-op.
Inputs ¶
- ctx: Context for cancellation.
- lifecycle: Lifecycle manager for model warmup. If nil, warmup is skipped.
Outputs ¶
- error: Non-nil if warmup fails.
type ParamExtractor ¶
type ParamExtractor struct {
// contains filtered or unexported fields
}
ParamExtractor uses a fast LLM to extract tool parameters from natural language queries. It corrects errors using semantic understanding of hierarchical scoping (e.g., project vs. module names).
Description ¶
The regex-based parameter extraction in extractToolParameters() fails on queries with hierarchical scope references (e.g., "in the Flask helpers module" extracts "flask" instead of "helpers"). ParamExtractor uses a dedicated small model (ministral-3:3b by default) that runs in parallel with the tool router (granite4:micro-h) on a separate Ollama instance.
IT-08e: The extractor runs speculatively on the pre-filter's top candidate BEFORE routing completes. If the router confirms the prediction, LLM params are used; otherwise regex fallback kicks in.
CB-60: Refactored to use providers.ChatClient instead of direct MultiModelManager dependency, enabling any LLM provider.
Thread Safety ¶
ParamExtractor is safe for concurrent use.
func NewParamExtractor ¶
func NewParamExtractor(chatClient providers.ChatClient, config ParamExtractorConfig) (*ParamExtractor, error)
NewParamExtractor creates a new LLM-based parameter extractor.
Description ¶
Creates a ParamExtractor that uses the specified model for semantic parameter extraction. The ChatClient interface allows any provider (Ollama, Anthropic, OpenAI, Gemini) to be used.
CB-60: Accepts providers.ChatClient instead of *llm.MultiModelManager.
Inputs ¶
- chatClient: ChatClient for sending extraction queries. Must not be nil.
- config: Extractor configuration.
Outputs ¶
- *ParamExtractor: Configured extractor.
- error: Non-nil if chatClient is nil.
func (*ParamExtractor) ExtractParams ¶
func (e *ParamExtractor) ExtractParams( ctx context.Context, query string, toolName string, paramSchemas []ParamSchema, regexHint map[string]any, ) (map[string]any, error)
ExtractParams uses the LLM to extract or correct tool parameters.
Description ¶
Takes the user query, tool name, parameter schema, and the regex-based extraction result. Sends these to the LLM which acts as a semantic parser to correct hierarchical scoping errors. Returns the corrected parameters or an error (caller should fall back to regex result).
Inputs ¶
- ctx: Context for cancellation/timeout.
- query: The user's natural language query.
- toolName: The name of the selected tool.
- paramSchemas: Parameter definitions for the tool.
- regexHint: The regex extractor's output (used as a hint).
Outputs ¶
- map[string]any: Corrected parameter values.
- error: Non-nil if extraction fails (caller should use regexHint).
Thread Safety ¶
Safe for concurrent use.
func (*ParamExtractor) IsEnabled ¶
func (e *ParamExtractor) IsEnabled() bool
IsEnabled returns true if the extractor is enabled.
Outputs ¶
- bool: True if the feature flag is set.
Thread Safety ¶
Safe for concurrent use (reads immutable config).
func (*ParamExtractor) ResolveConceptualSymbol ¶
func (e *ParamExtractor) ResolveConceptualSymbol( ctx context.Context, query string, candidates []agent.SymbolCandidate, tier0Count, tier1Count int, sourceContext string, ) (string, error)
ResolveConceptualSymbol uses the LLM to pick the best symbol from candidates when the query uses conceptual descriptions instead of function names.
Description ¶
IT-12: When regex-based extraction produces words like "assigning" that don't match any symbol in the index, this method searches the index for keywords from the query, collects candidate symbols, and asks the LLM to pick the best starting point. This gives the LLM actual codebase knowledge it otherwise lacks.
Inputs ¶
- ctx: Context for cancellation/timeout. Must not be nil.
- query: The user's natural language query.
- candidates: Symbol candidates found by keyword search of the index.
- tier0Count: Number of tier0 (best match) candidates at the start of the list.
- tier1Count: Number of tier1 (partial match) candidates after tier0.
Outputs ¶
- string: The best symbol name from the candidates.
- error: Non-nil if resolution fails (extractor disabled, no candidates, LLM returns invalid name).
Thread Safety ¶
Safe for concurrent use.
type ParamExtractorConfig ¶
type ParamExtractorConfig struct {
// Model is the Ollama model to use for parameter extraction.
// IT-08e: Dedicated small model, separate from the router.
// Default: "ministral-3:3b"
Model string `json:"model"`
// Timeout is the maximum time for a parameter extraction call.
// IT-08e: Increased from 500ms to 2s (dedicated model, no serialization).
// Default: 2s
Timeout time.Duration `json:"timeout"`
// Temperature controls randomness. Lower = more deterministic.
// Default: 0.1
Temperature float64 `json:"temperature"`
// MaxTokens limits the response length.
// Default: 512
MaxTokens int `json:"max_tokens"`
// NumCtx is the context window size.
// IT-08e: Reduced from 8192 to 4096 (12x headroom for ~300 token budget).
// Default: 4096
NumCtx int `json:"num_ctx"`
// KeepAlive controls how long the model stays in VRAM.
// Default: "24h"
KeepAlive string `json:"keep_alive"`
// Enabled is the feature flag. When false, ExtractParams is a no-op.
// Default: true
Enabled bool `json:"enabled"`
}
ParamExtractorConfig configures the parameter extractor.
Description ¶
Controls the model, timeout, and feature flag for LLM parameter extraction. IT-08e: Uses a dedicated small model (ministral-3:3b) separate from the tool router to enable parallel execution without Ollama serialization.
func DefaultParamExtractorConfig ¶
func DefaultParamExtractorConfig() ParamExtractorConfig
DefaultParamExtractorConfig returns sensible defaults.
Outputs ¶
- ParamExtractorConfig: Default configuration.
type ParamSchema ¶
type ParamSchema = agent.ParamExtractorSchema
ParamSchema describes a single parameter for the LLM prompt. This is an alias for the agent-level interface type.
type PreFilter ¶
type PreFilter struct {
// contains filtered or unexported fields
}
PreFilter narrows tool candidates before the LLM router classifies.
Description:
Implements a 5-phase deterministic pipeline: 1. Forced mapping check (exact phrases → deterministic tool) 2. Negation detection (negation word + keyword proximity → redirect) 3. Keyword matching (registry keyword index → scored candidates) 4. Confusion pair resolution (pattern boost for ambiguous pairs) 5. Candidate selection (top N by score, floor at min)
Inputs:
registry - The tool routing registry for keyword lookup. May be nil (passthrough). cfg - Pre-filter configuration with rules. Must not be nil. logger - Logger for structured output. Must not be nil.
Thread Safety: Safe for concurrent use (all state is read-only after construction).
func NewPreFilter ¶
func NewPreFilter(registry *config.ToolRoutingRegistry, cfg *config.PreFilterConfig, logger *slog.Logger, store RouterCacheStore) *PreFilter
NewPreFilter creates a new PreFilter.
Description:
Creates a pre-filter with the given registry and configuration. If registry is nil, keyword matching (Phase 3) falls back to legacy BestFor substring matching. IT-06c: BM25 and embedding components are lazily initialized on the first Filter/FilterAgentSpecs call that provides non-empty tool specs. The embedding warm-up runs once in a background goroutine; Phase 3 degrades gracefully to BM25-only while warm-up is in progress. GR-61: If store is non-nil, the embedding cache will load pre-computed vectors from BadgerDB on warm-up (skipping Ollama) and persist newly computed vectors for future service restarts. Pass nil for tests and for deployments without a routing cache directory.
Inputs:
registry - Tool routing registry for keyword lookup. May be nil. cfg - Pre-filter configuration. Must not be nil. logger - Logger instance. Must not be nil. store - Optional BadgerDB embedding cache store. Nil disables persistence.
Outputs:
*PreFilter - The constructed pre-filter.
Thread Safety: The returned PreFilter is safe for concurrent use.
func (*PreFilter) Filter ¶
func (pf *PreFilter) Filter(ctx context.Context, query string, allSpecs []ToolSpec, sessionCounts map[string]int) *PreFilterResult
Filter narrows the tool candidate set based on query analysis.
Description:
Runs the 5-phase pipeline to either force a tool selection or narrow the candidate set for the LLM router. Returns all specs unchanged (passthrough) when disabled, query is empty, or no rules match. IT-06c: Phase 3 now uses hybrid BM25 + embedding scoring instead of plain keyword substring counting. sessionCounts provides per-tool selection counts for the current session; tools already selected receive a UCB1 exploration penalty. Pass nil to disable the penalty.
Inputs:
ctx - Context for tracing and cancellation. Must not be nil. query - The user's query string. allSpecs - All available tool specs. sessionCounts - Current session tool selection counts (tool → count). May be nil.
Outputs:
*PreFilterResult - The filtering result with narrowed specs or forced tool.
Thread Safety: Safe for concurrent use.
func (*PreFilter) FilterAgentSpecs ¶
func (pf *PreFilter) FilterAgentSpecs(ctx context.Context, query string, allSpecs []agent.ToolRouterSpec, sessionCounts map[string]int) *AgentPreFilterResult
FilterAgentSpecs narrows agent.ToolRouterSpec candidates.
Description:
Converts agent types to routing types, runs the pre-filter pipeline, and converts results back. This is the primary integration point for the execute phase. IT-06c: sessionCounts provides per-tool selection counts for the current session, used by Phase 3 UCB1 exploration penalty. Pass nil to disable.
Inputs:
ctx - Context for tracing. query - The user's query string. allSpecs - All available tool specs in agent format. sessionCounts - Per-tool selection counts for this session. May be nil.
Outputs:
*AgentPreFilterResult - The filtering result.
Thread Safety: Safe for concurrent use.
type PreFilterResult ¶
type PreFilterResult struct {
// NarrowedSpecs is the filtered set of tool specs for the router.
NarrowedSpecs []ToolSpec
// ForcedTool is set when the pre-filter deterministically selects a tool.
// When non-empty, the router should be skipped entirely.
ForcedTool string
// ForcedReason explains why the tool was forced.
ForcedReason string
// Scores maps tool name to its pre-filter score.
Scores map[string]float64
// AppliedRules lists the rules that fired during filtering.
AppliedRules []string
// OriginalCount is the number of tools before filtering.
OriginalCount int
// NarrowedCount is the number of tools after filtering.
NarrowedCount int
// Duration is how long the pre-filter took.
Duration time.Duration
}
PreFilterResult contains the output of a pre-filter operation.
Description:
Holds either a forced tool selection (skip router) or a narrowed set of candidates for the router to classify.
type PromptBuilder ¶
type PromptBuilder struct {
// contains filtered or unexported fields
}
PromptBuilder constructs dynamic system prompts for tool routing.
Description ¶
Builds prompts that include available tools, their descriptions, and context about the codebase. The prompt instructs the router to output a JSON object with the selected tool and confidence score.
Thread Safety ¶
PromptBuilder is safe for concurrent use.
func NewPromptBuilder ¶
func NewPromptBuilder() (*PromptBuilder, error)
NewPromptBuilder creates a new PromptBuilder.
Outputs ¶
- *PromptBuilder: Configured builder.
- error: Non-nil if template parsing fails.
func (*PromptBuilder) BuildSystemPrompt ¶
func (p *PromptBuilder) BuildSystemPrompt(tools []ToolSpec, context *CodeContext) (string, error)
BuildSystemPrompt generates the system prompt for the router.
Description ¶
Creates a system prompt that includes all available tools, their descriptions, and instructions for JSON output format.
Inputs ¶
- tools: Available tools for selection.
- context: Optional codebase context.
Outputs ¶
- string: The rendered system prompt.
- error: Non-nil if template rendering fails.
func (*PromptBuilder) BuildUserPrompt ¶
func (p *PromptBuilder) BuildUserPrompt(query string) string
BuildUserPrompt generates the user message containing the query.
Inputs ¶
- query: The user's question/request.
Outputs ¶
- string: The rendered user prompt.
type PromptData ¶
type PromptData struct {
// Tools is the list of available tools.
Tools []ToolSpec
// Context contains optional codebase context.
Context *CodeContext
// Query is the user's question/request.
Query string
}
PromptData contains the data for prompt template rendering.
type RouterAdapter ¶
type RouterAdapter struct {
// contains filtered or unexported fields
}
RouterAdapter adapts a routing.ToolRouter to the agent.ToolRouter interface.
Description ¶
The routing package defines its own types (ToolSpec, CodeContext, ToolSelection) for internal use, but the agent package expects its own types (ToolRouterSpec, ToolRouterCodeContext, ToolRouterSelection). This adapter bridges the two.
Thread Safety ¶
RouterAdapter is safe for concurrent use if the underlying router is.
func (*RouterAdapter) Close ¶
func (a *RouterAdapter) Close() error
Close implements agent.ToolRouter.
func (*RouterAdapter) Model ¶
func (a *RouterAdapter) Model() string
Model implements agent.ToolRouter.
func (*RouterAdapter) SelectTool ¶
func (a *RouterAdapter) SelectTool(ctx context.Context, query string, availableTools []agent.ToolRouterSpec, codeContext *agent.ToolRouterCodeContext) (*agent.ToolRouterSelection, error)
SelectTool implements agent.ToolRouter.
Converts agent types to routing types, calls the underlying router, and converts the result back to agent types.
func (*RouterAdapter) WarmRouter ¶
func (a *RouterAdapter) WarmRouter(ctx context.Context, lifecycle providers.ModelLifecycleManager) error
WarmRouter exposes the underlying router's WarmRouter method.
Description ¶
Allows callers to warm the router model. This is not part of the agent.ToolRouter interface but is useful during initialization. If the underlying router does not support WarmRouter (e.g., EscalatingRouter), this is a no-op.
CB-60: Now accepts a ModelLifecycleManager for provider-agnostic warmup.
type RouterCacheStore ¶
type RouterCacheStore interface {
// LoadEmbeddings retrieves cached unit-normalized tool embedding vectors
// for the given corpus hash.
//
// Returns (nil, nil) on cache miss (key absent or TTL expired).
// Returns (nil, error) on storage failure.
// Returns (vectors, nil) on cache hit; vectors is never empty on success.
LoadEmbeddings(ctx context.Context, corpusHash string) (map[string][]float32, error)
// SaveEmbeddings persists unit-normalized tool embedding vectors for the
// given corpus hash. The store applies a 7-day TTL automatically.
//
// Returns non-nil error only on storage failure. The caller logs the error
// as a warning and continues — persistence failure is non-fatal; vectors
// will be recomputed on the next service restart.
SaveEmbeddings(ctx context.Context, corpusHash string, vectors map[string][]float32) error
}
RouterCacheStore persists tool embedding vectors across service restarts.
Description ¶
The store is keyed by corpus hash — a SHA256 digest of all tool names, keywords, and use_when text plus the embedding model name. Any change to the tool registry or model automatically produces a different hash, so the previous entry becomes unreachable (expires via TTL) without explicit invalidation.
Both methods are nil-safe: the PreFilter and ToolEmbeddingCache check for a nil RouterCacheStore and skip persistence, operating in in-memory-only mode. This is the correct behavior for tests and for deployments that do not configure a routing cache directory.
Thread Safety ¶
Implementations must be safe for concurrent use.
type RouterConfig ¶
type RouterConfig struct {
// Model is the Ollama model to use for routing (e.g., "granite4:micro-h").
Model string `json:"model"`
// OllamaEndpoint is the Ollama server URL (e.g., "http://localhost:11434").
OllamaEndpoint string `json:"ollama_endpoint"`
// Timeout is the maximum time for a routing decision.
// Recommended: 500ms. If exceeded, returns error for fallback.
Timeout time.Duration `json:"timeout"`
// Temperature controls randomness. Lower = more deterministic.
// Recommended: 0.1 for routing (we want consistency).
Temperature float64 `json:"temperature"`
// ConfidenceThreshold is the minimum confidence for a selection.
// Below this, the router returns an error for fallback to main LLM.
ConfidenceThreshold float64 `json:"confidence_threshold"`
// KeepAlive controls how long the router model stays in VRAM.
// "-1" = infinite (recommended when using with main LLM).
KeepAlive string `json:"keep_alive"`
// MaxTokens limits the router's response length.
// Should be small since we only need JSON output.
// Recommended: 256.
MaxTokens int `json:"max_tokens"`
// NumCtx sets the context window size for the router model.
// Router only needs to see current query, available tools, and recent history.
// Recommended: 16384 (16K tokens) to minimize VRAM usage and allow main agent larger context.
// Note: On systems with limited VRAM (32GB), keeping router context low allows main agent 64K+ context.
NumCtx int `json:"num_ctx"`
// EscalationModel is the Ollama model used for escalation when the primary
// router's confidence is below EscalationThreshold. Empty string disables
// escalation. Example: "granite3.3:8b".
EscalationModel string `json:"escalation_model,omitempty"`
// EscalationThreshold is the minimum confidence from the primary router
// below which escalation to the larger model is triggered. Default: 0.7.
EscalationThreshold float64 `json:"escalation_threshold,omitempty"`
// EscalationTimeout is the maximum time for an escalation routing decision.
// Default: 3s.
EscalationTimeout time.Duration `json:"escalation_timeout,omitempty"`
}
RouterConfig configures the tool router behavior.
Description ¶
Controls the router's model selection, timeout, and fallback behavior.
func DefaultRouterConfig ¶
func DefaultRouterConfig() RouterConfig
DefaultRouterConfig returns sensible defaults for the router.
Outputs ¶
- RouterConfig: Default configuration.
type RouterError ¶
type RouterError struct {
// Code is a machine-readable error code.
Code string `json:"code"`
// Message is a human-readable error message.
Message string `json:"message"`
// Retryable indicates if the error might resolve on retry.
Retryable bool `json:"retryable"`
}
RouterError represents an error from the tool router.
func NewRouterError ¶
func NewRouterError(code, message string, retryable bool) *RouterError
NewRouterError creates a new RouterError.
func (*RouterError) Error ¶
func (e *RouterError) Error() string
Error implements the error interface.
type RouterResult ¶
type RouterResult struct {
// Tool is the tool name.
Tool string
// Confidence is the router's confidence (0.0-1.0).
Confidence float64
}
RouterResult represents a tool selection from the router.
Description:
Used as input to ScoreTools to provide the router's initial ranking.
type SelectionCounts ¶
type SelectionCounts struct {
// contains filtered or unexported fields
}
SelectionCounts tracks tool selection counts for UCB1 exploration term.
Description:
Maintains per-session tool selection counts for the UCB1 exploration term. Selection counts are used to calculate the exploration bonus: exploration = C * sqrt(ln(total) / count)
Thread Safety: Safe for concurrent use.
func NewSelectionCounts ¶
func NewSelectionCounts() *SelectionCounts
NewSelectionCounts creates a new selection counter.
Outputs:
*SelectionCounts - The counter instance.
func (*SelectionCounts) AsMap ¶
func (c *SelectionCounts) AsMap() map[string]int
AsMap returns the counts as a map.
Outputs:
map[string]int - Copy of the counts map.
Thread Safety: Safe for concurrent use.
func (*SelectionCounts) Get ¶
func (c *SelectionCounts) Get(tool string) int
Get returns the count for a tool.
Inputs:
tool - The tool name.
Outputs:
int - The selection count (0 if never selected).
Thread Safety: Safe for concurrent use.
func (*SelectionCounts) Increment ¶
func (c *SelectionCounts) Increment(tool string)
Increment increases the count for a tool.
Inputs:
tool - The tool name.
Thread Safety: Safe for concurrent use.
func (*SelectionCounts) Reset ¶
func (c *SelectionCounts) Reset()
Reset clears all counts.
Thread Safety: Safe for concurrent use.
func (*SelectionCounts) Total ¶
func (c *SelectionCounts) Total() int
Total returns the total selections across all tools.
Outputs:
int - Total selection count.
Thread Safety: Safe for concurrent use.
type SemanticConfig ¶
type SemanticConfig struct {
// DuplicateThreshold is the similarity threshold for blocking (default 0.8).
// Calls with similarity >= this are considered semantic duplicates.
DuplicateThreshold float64
// PenaltyThreshold is the similarity threshold for penalizing (default 0.3).
// Calls with similarity >= this (but < duplicate) get UCB1 penalty.
PenaltyThreshold float64
// PenaltyWeight is the UCB1 penalty multiplier (default 0.5).
// SemanticPenalty = similarity * PenaltyWeight.
PenaltyWeight float64
// MaxHistorySize is the maximum tool calls to retain (default 100).
MaxHistorySize int
}
SemanticConfig holds configurable thresholds for semantic routing.
Description:
Allows customization of semantic duplicate detection behavior. Use DefaultSemanticConfig() for production defaults.
Thread Safety: Immutable after creation. Safe to share across goroutines.
func DefaultSemanticConfig ¶
func DefaultSemanticConfig() SemanticConfig
DefaultSemanticConfig returns production-ready default configuration.
Outputs:
SemanticConfig - Configuration with default values.
Thread Safety: Safe for concurrent use (returns value type).
func (SemanticConfig) Validate ¶
func (c SemanticConfig) Validate() bool
Validate checks that configuration values are sensible.
Outputs:
bool - True if configuration is valid.
Thread Safety: Safe for concurrent use (read-only).
type StateKeyBuilder ¶
type StateKeyBuilder struct{}
StateKeyBuilder builds cache keys from tool execution history.
Description:
Generates deterministic, order-preserving keys from the tool execution sequence. Unlike transposition keys (which are order-independent), these keys preserve the sequence because tool order matters. Key format: "gen:<generation>|<tool1>:<outcome1>→<tool2>:<outcome2>→..."
Thread Safety: StateKeyBuilder is safe for concurrent use (stateless).
func NewStateKeyBuilder ¶
func NewStateKeyBuilder() *StateKeyBuilder
NewStateKeyBuilder creates a new state key builder.
Outputs:
*StateKeyBuilder - The builder instance.
func (*StateKeyBuilder) BuildKey ¶
func (b *StateKeyBuilder) BuildKey(steps []crs.StepRecord, generation int64) string
BuildKey generates a cache key from step history and CRS generation.
Description:
Creates an order-preserving key that captures the tool execution sequence. The key includes the CRS generation for automatic invalidation.
Inputs:
steps - The step history from CRS. generation - The current CRS generation.
Outputs:
string - The cache key.
func (*StateKeyBuilder) BuildKeyFromHistory ¶
func (b *StateKeyBuilder) BuildKeyFromHistory(history []ToolHistoryEntry, generation int64) string
BuildKeyFromHistory generates a cache key from tool history entries.
Description:
Alternative method for building keys from ToolHistoryEntry slice.
Inputs:
history - The tool history entries. generation - The current CRS generation.
Outputs:
string - The cache key.
type ToolCallHistory ¶
type ToolCallHistory struct {
// contains filtered or unexported fields
}
ToolCallHistory tracks tool calls with semantic signatures.
Description:
Maintains a history of tool calls for semantic duplicate detection. Provides methods to check if a proposed call is similar to prior calls. R1.1 Fix: History is limited to MaxToolCallHistorySize entries. P1.1 Fix: Tool-indexed map for O(1) tool lookup.
Thread Safety: Safe for concurrent use. All public methods acquire locks.
func NewToolCallHistory ¶
func NewToolCallHistory() *ToolCallHistory
NewToolCallHistory creates a new empty history with default configuration.
Outputs:
*ToolCallHistory - The history instance.
Thread Safety: The returned instance is safe for concurrent use.
func NewToolCallHistoryWithConfig ¶
func NewToolCallHistoryWithConfig(config SemanticConfig) *ToolCallHistory
NewToolCallHistoryWithConfig creates a new empty history with full configuration.
Inputs:
config - Semantic configuration. If invalid, uses defaults.
Outputs:
*ToolCallHistory - The history instance.
Thread Safety: The returned instance is safe for concurrent use.
func NewToolCallHistoryWithSize
deprecated
func NewToolCallHistoryWithSize(maxSize int) *ToolCallHistory
NewToolCallHistoryWithSize creates a new empty history with specified max size.
Inputs:
maxSize - Maximum number of entries to retain. If <= 0, uses default.
Outputs:
*ToolCallHistory - The history instance.
Thread Safety: The returned instance is safe for concurrent use.
Deprecated: Use NewToolCallHistoryWithConfig for full configuration.
func (*ToolCallHistory) Add ¶
func (h *ToolCallHistory) Add(sig ToolCallSignature)
Add records a tool call signature in the history.
Description:
Appends the signature to history. If history exceeds maxSize, older entries are evicted (sliding window). Updates the tool index.
Inputs:
sig - The tool call signature to record.
Thread Safety: Safe for concurrent use. Acquires write lock.
func (*ToolCallHistory) Clear ¶
func (h *ToolCallHistory) Clear()
Clear removes all recorded calls and resets the tool index.
Thread Safety: Safe for concurrent use. Acquires write lock.
func (*ToolCallHistory) Config ¶
func (h *ToolCallHistory) Config() SemanticConfig
Config returns the semantic configuration.
Thread Safety: Safe for concurrent use (returns copy of immutable config).
func (*ToolCallHistory) GetCallsForTool ¶
func (h *ToolCallHistory) GetCallsForTool(tool string) []ToolCallSignature
GetCallsForTool returns all calls for a specific tool.
Description:
P1.1 Fix: Uses tool index for O(1) lookup instead of O(n) scan.
Inputs:
tool - The tool name to filter by.
Outputs:
[]ToolCallSignature - Copies of calls matching the tool name.
Thread Safety: Safe for concurrent use. Acquires read lock.
func (*ToolCallHistory) GetSimilarity ¶
func (h *ToolCallHistory) GetSimilarity(tool string, queryTerms map[string]bool) (float64, *ToolCallSignature)
GetSimilarity finds the most similar prior call for a tool+query combination.
Description:
Compares the proposed query terms against all prior calls of the same tool. Returns the maximum similarity found and a copy of the most similar call. I1.1 Fix: Returns copy instead of pointer to avoid data races. P1.1 Fix: Uses tool index to only check relevant calls.
Inputs:
tool - The tool name to check. queryTerms - Extracted terms from the proposed call's parameters.
Outputs:
maxSimilarity - Highest Jaccard similarity found (0.0-1.0). mostSimilar - Copy of the most similar prior call, or nil if none found.
Thread Safety: Safe for concurrent use. Acquires read lock.
func (*ToolCallHistory) IsExactDuplicate ¶
func (h *ToolCallHistory) IsExactDuplicate(tool, rawQuery string) (bool, *ToolCallSignature)
IsExactDuplicate checks if a call is an exact duplicate of a prior call.
Description:
An exact duplicate is same tool + same raw query (case-insensitive). P1.3 Fix: Uses EqualFold directly without redundant ToLower. I1.1 Fix: Returns copy instead of pointer to avoid data races. P1.1 Fix: Uses tool index to only check relevant calls.
Inputs:
tool - The tool name. rawQuery - The raw query string.
Outputs:
bool - True if an exact duplicate exists. *ToolCallSignature - Copy of the duplicate call, or nil if none.
Thread Safety: Safe for concurrent use. Acquires read lock.
func (*ToolCallHistory) Len ¶
func (h *ToolCallHistory) Len() int
Len returns the number of recorded calls.
Thread Safety: Safe for concurrent use.
func (*ToolCallHistory) MaxSize ¶
func (h *ToolCallHistory) MaxSize() int
MaxSize returns the maximum history size.
Thread Safety: Safe for concurrent use (reads immutable field).
func (*ToolCallHistory) Trim ¶
func (h *ToolCallHistory) Trim(size int)
Trim reduces the history to the specified size, keeping the most recent entries.
Description:
R1.2 Fix: Allows external callers to trim history if needed. Rebuilds the tool index after trimming.
Inputs:
size - Maximum number of entries to keep. If <= 0, clears all entries.
Thread Safety: Safe for concurrent use. Acquires write lock.
type ToolCallSignature ¶
type ToolCallSignature struct {
// Tool is the tool name (e.g., "Grep", "find_callers").
Tool string
// QueryTerms are extracted terms from the query parameters.
// Used for Jaccard similarity comparison.
QueryTerms map[string]bool
// RawQuery is the original query string before term extraction.
// Stored for exact duplicate detection and debugging.
RawQuery string
// StepNumber is when this call was made (1-indexed).
StepNumber int
// Success indicates whether the tool call succeeded.
Success bool
}
ToolCallSignature represents a unique tool call with semantic signature.
Description:
Captures the tool name and extracted query terms for semantic comparison. Two calls are considered semantically equivalent if they have the same tool and high Jaccard similarity between their query terms.
Thread Safety: Immutable after creation.
func CheckSemanticStatus ¶
func CheckSemanticStatus(history *ToolCallHistory, tool, rawQuery string) (status string, similarity float64, similarCall *ToolCallSignature)
CheckSemanticStatus evaluates a proposed call against history.
Description:
Returns the semantic status of a proposed tool call: - Blocked: Similarity >= DuplicateThreshold (semantic duplicate) - Penalized: Similarity >= PenaltyThreshold (similar but not duplicate) - Allowed: Similarity < PenaltyThreshold (sufficiently different) S1.3 Fix: Uses configurable thresholds from history's config. O1.1 Fix: Records Prometheus metrics.
Inputs:
history - The call history to check against. If nil, returns "allowed". tool - The proposed tool name. If empty, returns "allowed". rawQuery - The raw query string.
Outputs:
status - "blocked", "penalized", or "allowed". similarity - The maximum similarity found. similarCall - Copy of the most similar prior call, or nil.
Thread Safety: Safe for concurrent use.
type ToolEmbeddingCache ¶
type ToolEmbeddingCache struct {
// contains filtered or unexported fields
}
ToolEmbeddingCache pre-computes and caches embedding vectors for every tool at service startup, then uses cosine similarity at query time to score how well a user's query matches each tool.
Description ¶
Embedding-based scoring is semantically robust: "Where is X referenced?" and "Where is X used?" produce nearly identical query vectors, both close to the find_references tool vector — regardless of exact word form.
The cache calls Ollama's /api/embed endpoint (nomic-embed-text-v2-moe by default) in parallel during Warm(). If Ollama is unavailable, the cache degrades gracefully: Score() returns (nil, nil) and the hybrid scorer falls back to BM25-only mode.
GR-61: Vectors are persisted in BadgerDB (via RouterCacheStore) between service restarts. The corpus hash (SHA256 of tool specs + model name) serves as the cache key, providing automatic invalidation when the tool registry or model changes. If the store is nil, the cache operates in in-memory-only mode (no persistence).
Thread Safety ¶
Safe for concurrent use after Warm() completes.
func NewToolEmbeddingCache ¶
func NewToolEmbeddingCache(logger *slog.Logger, store RouterCacheStore) *ToolEmbeddingCache
NewToolEmbeddingCache creates an unwarmed embedding cache.
Description ¶
Reads EMBEDDING_SERVICE_URL and EMBEDDING_MODEL from the environment. Call Warm() to pre-compute tool embeddings before the cache can score queries.
GR-61: If store is non-nil, Warm() will check the BadgerDB cache before calling Ollama and persist newly computed vectors after warm-up. If store is nil, the cache operates in in-memory-only mode — correct for tests and for deployments without a routing cache directory configured.
Inputs ¶
- logger: Logger for warnings and debug output. Must not be nil.
- store: Optional BadgerDB persistence store. Nil disables persistence.
Outputs ¶
- *ToolEmbeddingCache: Unwarmed cache. Never nil.
Thread Safety ¶
The returned cache is safe for concurrent use after Warm() completes.
func (*ToolEmbeddingCache) IsWarmed ¶
func (c *ToolEmbeddingCache) IsWarmed() bool
IsWarmed reports whether the cache has been successfully warmed.
Thread Safety ¶
Safe for concurrent use.
func (*ToolEmbeddingCache) Score ¶
Score embeds the query and returns cosine similarity vs each cached tool vector.
Description ¶
Returns (nil, nil) in two cases where the caller should fall back to BM25:
- The cache was never warmed (Ollama was unavailable at startup).
- The Ollama call for the query embedding fails or times out.
Returns a non-nil map on success. Scores are in [0.0, 1.0]; tools absent from the cache have implicit score 0 and are omitted from the map.
Inputs ¶
- ctx: Context for cancellation. A per-call timeout is applied internally.
- query: The user's raw query string.
Outputs ¶
- map[string]float64: Tool name → cosine similarity in [0.0, 1.0]. Nil signals graceful degradation — caller should use BM25 only.
- error: Always nil. Errors are absorbed and signaled via nil map.
Thread Safety ¶
Safe for concurrent use after Warm() completes.
func (*ToolEmbeddingCache) Warm ¶
func (c *ToolEmbeddingCache) Warm(ctx context.Context, specs []ToolSpec) error
Warm pre-computes and caches an embedding vector for every tool spec.
Description ¶
Builds an embedding document for each tool (name + keywords + use_when), then calls Ollama in parallel (up to toolEmbeddingWarmConcurrency concurrent requests). Vectors are stored unit-normalized for efficient cosine similarity.
If any single tool fails to embed, a warning is logged and that tool is skipped — it will receive score 0 from Score(). If all tools fail, warmed remains false and Score() degrades gracefully.
Inputs ¶
- ctx: Context for the warm-up HTTP calls. Cancellation aborts all pending embeds.
- specs: Tool specifications to embed. Empty slice is a no-op.
Outputs ¶
- error: Non-nil if the Ollama endpoint is completely unreachable. Partial failures (individual tools) are logged as warnings, not returned as errors.
Thread Safety ¶
Not safe to call concurrently. Call once at service startup.
type ToolError ¶
type ToolError struct {
// Tool is the tool name that failed.
Tool string `json:"tool"`
// Error is the error message from the failure.
Error string `json:"error"`
// Timestamp when the error occurred.
Timestamp string `json:"timestamp,omitempty"`
}
ToolError captures a failed tool attempt for router feedback.
Description ¶
When a tool fails (e.g., missing required parameter), the error is recorded and fed back to the router. This helps the router avoid suggesting the same tool repeatedly when it cannot be used successfully.
type ToolHistoryEntry ¶
type ToolHistoryEntry struct {
// Tool is the tool name that was called.
Tool string `json:"tool"`
// Summary is a brief description of what was found/returned.
// Example: "Found 5 callers of parseConfig in pkg/config/"
Summary string `json:"summary"`
// Success indicates whether the tool call succeeded.
Success bool `json:"success"`
// StepNumber when this tool was called.
StepNumber int `json:"step_number,omitempty"`
}
ToolHistoryEntry captures a tool execution with its outcome.
Description ¶
Records what tool was called, what it found, and whether it succeeded. This gives the router context about what information is already available, enabling it to suggest the NEXT logical tool rather than repeating.
type ToolRouter ¶
type ToolRouter interface {
// SelectTool chooses the best tool for the given query and context.
//
// Inputs:
// - ctx: Context for cancellation/timeout. Must not be nil.
// - query: The user's question or request.
// - availableTools: Tools currently available for selection.
// - context: Optional code context (symbols, files loaded, etc.)
//
// Outputs:
// - *ToolSelection: The selected tool and optional parameter hints.
// - error: Non-nil if routing fails (caller should fall back to main LLM).
SelectTool(ctx context.Context, query string, availableTools []ToolSpec, context *CodeContext) (*ToolSelection, error)
// Model returns the model being used for routing.
Model() string
// Close releases any resources held by the router.
Close() error
}
ToolRouter selects the appropriate tool for a user query.
Description ¶
ToolRouter uses a fast micro LLM to classify queries into tool categories before the main reasoning LLM processes the request. This allows tool selection to complete in ~50-100ms instead of the full LLM inference time.
Thread Safety ¶
Implementations must be safe for concurrent use.
Example ¶
router := NewGranite4Router(ollamaEndpoint, "granite4:micro-h")
selection, err := router.SelectTool(ctx, "What functions call parseConfig?", tools, codeCtx)
if err != nil {
// Fall back to letting main LLM choose
}
// Use selection.Tool to constrain main LLM
type ToolScore ¶
type ToolScore struct {
// Tool is the tool name.
Tool string `json:"tool"`
// RouterConfidence is the router's confidence for this tool (0.0-1.0).
RouterConfidence float64 `json:"router_confidence"`
// ProofPenalty is the penalty based on proof number (0.0-1.0).
// Higher proof number = higher penalty = harder to prove = less preferred.
ProofPenalty float64 `json:"proof_penalty"`
// SemanticPenalty is the penalty for similar prior calls (0.0-1.0).
// Based on Jaccard similarity with previous calls of the same tool.
// GR-38 Issue 17: Prevents redundant tool calls with similar params.
SemanticPenalty float64 `json:"semantic_penalty"`
// ExplorationBonus is the UCB1 exploration term.
// Encourages trying less-used tools.
ExplorationBonus float64 `json:"exploration_bonus"`
// FinalScore is the combined UCB1 score.
// Higher score = more preferred tool.
FinalScore float64 `json:"final_score"`
// Blocked indicates if the tool is blocked by a learned clause.
Blocked bool `json:"blocked"`
// BlockReason explains why the tool is blocked.
BlockReason string `json:"block_reason,omitempty"`
// ProofStatus contains the proof index status if available.
ProofStatus *crs.ProofNumber `json:"proof_status,omitempty"`
// SimilarCall is the most similar prior call, if any.
// GR-38 Issue 17: For debugging and tracing.
SimilarCall *ToolCallSignature `json:"similar_call,omitempty"`
}
ToolScore represents a tool's UCB1-based selection score.
Description:
Contains the breakdown of the UCB1 scoring components for transparency and debugging. The final score is computed as: score = exploitation + exploration exploitation = router_confidence - proof_penalty exploration = C * sqrt(ln(total_selections) / tool_selections)
Thread Safety: ToolScore is immutable after creation.
type ToolSelection ¶
type ToolSelection struct {
// Tool is the selected tool name (e.g., "find_symbol_usages").
Tool string `json:"tool"`
// Confidence is the router's confidence in this selection (0.0-1.0).
// Use this to decide whether to fall back to the main LLM.
// Recommended threshold: 0.7
Confidence float64 `json:"confidence"`
// ParamsHint contains optional parameter suggestions.
// Keys are parameter names, values are suggested values.
// The main LLM can use these as hints but isn't bound by them.
ParamsHint map[string]string `json:"params_hint,omitempty"`
// Reasoning is a brief explanation of why this tool was selected.
// Used for debugging and tracing.
Reasoning string `json:"reasoning,omitempty"`
// Duration is how long the routing decision took.
Duration time.Duration `json:"duration,omitempty"`
// PrefilterMiss is true when the router selected a tool that was NOT in the
// prefiltered candidate set. This means the prefilter incorrectly excluded
// the correct tool. The EscalatingRouter uses this flag to trigger recovery.
// CB-62 Rev 2.
PrefilterMiss bool `json:"prefilter_miss,omitempty"`
// RawModelPick is the tool name the router model originally returned,
// before any findClosestTool correction. Empty when the model's pick
// was in the candidate set. Used for observability and recovery.
// CB-62 Rev 2.
RawModelPick string `json:"raw_model_pick,omitempty"`
}
ToolSelection represents the router's decision.
Description ¶
Contains the selected tool name, confidence score, and optional hints for parameter values. The confidence score can be used to decide whether to trust the selection or fall back to the main LLM.
func (*ToolSelection) IsConfident ¶
func (s *ToolSelection) IsConfident(threshold float64) bool
IsConfident returns true if the selection confidence is above the threshold.
Inputs ¶
- threshold: Minimum confidence to consider (e.g., 0.7).
Outputs ¶
- bool: True if Confidence >= threshold.
type ToolSelectionCache ¶
type ToolSelectionCache struct {
// contains filtered or unexported fields
}
ToolSelectionCache caches tool selections for identical states.
Description:
Provides lightweight state caching for tool selection decisions. Cache entries are invalidated when: - TTL expires - CRS generation changes (clauses were added/removed) - Cache is explicitly cleared This is a simplified transposition table optimized for tool selection. Unlike the full MCTS transposition table, this cache: - Uses order-preserving state keys (tool sequence matters) - Invalidates on CRS changes (clause learning) - Has shorter TTL (session-scoped)
Thread Safety: Safe for concurrent use.
func NewToolSelectionCache ¶
func NewToolSelectionCache() *ToolSelectionCache
NewToolSelectionCache creates a new tool selection cache with default config.
Outputs:
*ToolSelectionCache - The cache instance.
func NewToolSelectionCacheWithConfig ¶
func NewToolSelectionCacheWithConfig(config *ToolSelectionCacheConfig) *ToolSelectionCache
NewToolSelectionCacheWithConfig creates a new cache with the given config.
Inputs:
config - The configuration. If nil, uses default.
Outputs:
*ToolSelectionCache - The cache instance.
func (*ToolSelectionCache) Clear ¶
func (c *ToolSelectionCache) Clear()
Clear removes all cache entries.
Thread Safety: Safe for concurrent use.
func (*ToolSelectionCache) Get ¶
Get retrieves a cached selection if valid.
Description:
Returns the cached tool selection if: - Key exists in cache - Entry hasn't expired (TTL) - CRS generation matches (clauses haven't changed)
Inputs:
key - The state key (from StateKeyBuilder). currentGen - The current CRS generation.
Outputs:
string - The cached tool name. float64 - The cached score. bool - True if cache hit, false otherwise.
Thread Safety: Safe for concurrent use.
func (*ToolSelectionCache) InvalidateByGeneration ¶
func (c *ToolSelectionCache) InvalidateByGeneration(oldGeneration int64) int
InvalidateByGeneration removes all entries with an older generation.
Description:
Called when CRS changes (clause added/removed) to invalidate stale entries.
Inputs:
oldGeneration - Generation to invalidate.
Outputs:
int - Number of entries invalidated.
Thread Safety: Safe for concurrent use.
func (*ToolSelectionCache) Metrics ¶
func (c *ToolSelectionCache) Metrics() CacheMetrics
Metrics returns cache performance metrics.
Outputs:
CacheMetrics - Hit/miss statistics.
Thread Safety: Safe for concurrent use.
func (*ToolSelectionCache) Put ¶
func (c *ToolSelectionCache) Put(key, tool string, score float64, generation int64)
Put stores a tool selection in the cache.
Description:
Stores the selection keyed by state. If the cache is full, evicts the oldest entry.
Inputs:
key - The state key. tool - The selected tool. score - The selection score. generation - The CRS generation.
Thread Safety: Safe for concurrent use.
func (*ToolSelectionCache) Size ¶
func (c *ToolSelectionCache) Size() int
Size returns the current number of cache entries.
Outputs:
int - Number of entries.
Thread Safety: Safe for concurrent use.
type ToolSelectionCacheConfig ¶
type ToolSelectionCacheConfig struct {
// TTL is the time-to-live for cache entries.
// Default: 5 minutes.
TTL time.Duration
// MaxLen is the maximum number of entries.
// Default: 1000.
MaxLen int
// Logger for debug output. If nil, uses default logger.
Logger *slog.Logger
}
ToolSelectionCacheConfig configures the cache.
func DefaultToolSelectionCacheConfig ¶
func DefaultToolSelectionCacheConfig() *ToolSelectionCacheConfig
DefaultToolSelectionCacheConfig returns the default cache configuration.
Outputs:
*ToolSelectionCacheConfig - Default configuration.
type ToolSpec ¶
type ToolSpec struct {
// Name is the tool's unique identifier (e.g., "find_symbol_usages").
Name string `json:"name"`
// Description explains what the tool does.
Description string `json:"description"`
// BestFor lists keywords that should trigger this tool.
// CB-31e: Now populated from WhenToUse.Keywords.
BestFor []string `json:"best_for,omitempty"`
// UseWhen describes when to use this tool.
// CB-31e: New field for structured routing guidance.
UseWhen string `json:"use_when,omitempty"`
// AvoidWhen describes when NOT to use this tool.
// CB-31e: New field for structured routing guidance.
AvoidWhen string `json:"avoid_when,omitempty"`
// InsteadOf lists tools this should replace in specific scenarios.
// CB-31e: New field for tool substitution patterns.
InsteadOf []ToolSubstitution `json:"instead_of,omitempty"`
// Params lists the parameter names (without full schema).
// Used to generate parameter hints.
Params []string `json:"params,omitempty"`
// Category groups similar tools (e.g., "search", "read", "analyze").
// Helps the router with coarse-grained classification.
Category string `json:"category,omitempty"`
}
ToolSpec describes a tool for the router's system prompt.
Description ¶
Provides enough information about a tool for the router to make a selection decision. This is a simplified view of the full tool definition optimized for fast classification.
func DefaultToolSpecs ¶
func DefaultToolSpecs() []ToolSpec
DefaultToolSpecs returns the standard Trace tool specifications.
Description ¶
Returns ToolSpec entries for the core Trace tools. These can be filtered based on what's actually available in the current session.
Outputs ¶
- []ToolSpec: Standard tool specifications.
type ToolSubstitution ¶
type ToolSubstitution struct {
// Tool is the name of the tool to replace.
Tool string `json:"tool"`
// When describes when substitution applies.
When string `json:"when"`
}
ToolSubstitution describes when one tool should be used instead of another. CB-31e: Captures tool substitution patterns for the router.
type UCB1Scorer ¶
type UCB1Scorer struct {
// contains filtered or unexported fields
}
UCB1Scorer scores tools using UCB1 formula with proof integration.
Description:
UCB1 (Upper Confidence Bound) is the right algorithm for tool selection: - Exploits tools with high router confidence and low proof penalty - Explores less-used tools to discover alternatives - Respects learned clauses that block certain tool sequences UCB1 Formula: score = exploitation + exploration exploitation = router_confidence - proof_penalty exploration = C * sqrt(ln(total_selections) / tool_selections) Where: - router_confidence: From granite4:micro-h (0.0-1.0) - proof_penalty: Based on proof number (higher PN = more penalty) - C: Exploration constant (default sqrt(2) ≈ 1.41) - N: Total tool selections across all tools - n_i: Times this specific tool was selected
Thread Safety: UCB1Scorer is safe for concurrent use.
func NewUCB1Scorer ¶
func NewUCB1Scorer() *UCB1Scorer
NewUCB1Scorer creates a new UCB1 scorer with default configuration.
Outputs:
*UCB1Scorer - The scorer instance.
func NewUCB1ScorerWithConfig ¶
func NewUCB1ScorerWithConfig(config *UCB1ScorerConfig) *UCB1Scorer
NewUCB1ScorerWithConfig creates a new UCB1 scorer with the given configuration.
Inputs:
config - The configuration. If nil, uses default.
Outputs:
*UCB1Scorer - The scorer instance.
func (*UCB1Scorer) ScoreTools ¶
func (s *UCB1Scorer) ScoreTools( routerResults []RouterResult, proofIndex crs.ProofIndexView, selectionCounts map[string]int, clauseChecker ClauseChecker, currentAssignment map[string]bool, ) []ToolScore
ScoreTools scores all tools using UCB1 and returns sorted by score.
Description:
Applies the UCB1 formula to each tool, combining router confidence with proof penalty and exploration bonus. Tools blocked by clauses get a negative score and are sorted to the end.
Inputs:
routerResults - Tool selections from router with confidence. proofIndex - Proof numbers for each tool (can be nil). selectionCounts - How many times each tool was selected. clauseChecker - Learned clauses for blocking (can be nil). currentAssignment - Current variable assignment for clause checking.
Outputs:
[]ToolScore - Tools sorted by score (highest first, blocked last).
Thread Safety: Safe for concurrent use.
func (*UCB1Scorer) ScoreToolsWithSemantic ¶
func (s *UCB1Scorer) ScoreToolsWithSemantic( routerResults []RouterResult, proofIndex crs.ProofIndexView, selectionCounts map[string]int, clauseChecker ClauseChecker, currentAssignment map[string]bool, toolHistory *ToolCallHistory, proposedTool string, proposedQuery string, ) []ToolScore
ScoreToolsWithSemantic scores tools with semantic similarity awareness.
Description:
Extends ScoreTools with semantic duplicate detection. If the proposed tool has similar prior calls in history, it gets penalized or blocked.
GR-38 Issue 17: Semantic-aware tool routing.
Inputs:
routerResults - Tool selections from router with confidence. proofIndex - Proof numbers for each tool (can be nil). selectionCounts - How many times each tool was selected. clauseChecker - Learned clauses for blocking (can be nil). currentAssignment - Current variable assignment for clause checking. toolHistory - History of prior tool calls with semantic signatures. proposedTool - The tool the router is suggesting. proposedQuery - The query/params for the proposed tool.
Outputs:
[]ToolScore - Tools sorted by score (highest first, blocked last).
Thread Safety: Safe for concurrent use.
func (*UCB1Scorer) SelectBest ¶
func (s *UCB1Scorer) SelectBest(scores []ToolScore) (string, ToolScore)
SelectBest selects the best non-blocked tool from scored results.
Description:
Returns the highest-scoring tool that is not blocked. If all tools are blocked, returns empty string.
Inputs:
scores - Sorted tool scores from ScoreTools.
Outputs:
string - Best tool name, or empty if none available. ToolScore - The selected tool's score, or zero value if none.
type UCB1ScorerConfig ¶
type UCB1ScorerConfig struct {
// ExplorationConst is the exploration constant C.
// Default: sqrt(2) ≈ 1.41.
ExplorationConst float64
// ProofWeight is the weight for proof penalty.
// Default: 0.5.
ProofWeight float64
// SemanticWeight is the weight for semantic similarity penalty.
// Default: 0.5. GR-38 Issue 17.
SemanticWeight float64
// SemanticThreshold is the similarity threshold for blocking.
// Calls with similarity >= threshold are blocked.
// Default: 0.8. GR-38 Issue 17.
SemanticThreshold float64
// MaxUnexploredBonus is the bonus for never-selected tools.
// Default: 2.0 * ExplorationConst.
MaxUnexploredBonus float64
// MaxProofNumber is the maximum expected proof number for normalization.
// Proof numbers are normalized to [0,1] by dividing by this value.
// Default: 100.
MaxProofNumber float64
// Logger for debug output. If nil, uses default logger.
Logger *slog.Logger
}
UCB1ScorerConfig configures the UCB1 scorer.
func DefaultUCB1ScorerConfig ¶
func DefaultUCB1ScorerConfig() *UCB1ScorerConfig
DefaultUCB1ScorerConfig returns the default UCB1 scorer configuration.
Description:
Uses standard UCB1 exploration constant sqrt(2) and moderate proof weight.
Outputs:
*UCB1ScorerConfig - Default configuration.