Documentation
¶
Index ¶
- Constants
- func BuildAgentMainSessionKey(agentID string) string
- func BuildAgentPeerSessionKey(params SessionKeyParams) string
- func IsSubagentSessionKey(sessionKey string) bool
- func NormalizeAccountID(id string) string
- func NormalizeAgentID(id string) string
- type Classifier
- type DMScope
- type Features
- type ParsedSessionKey
- type ResolvedRoute
- type RouteInput
- type RoutePeer
- type RouteResolver
- type Router
- type RouterConfig
- type RuleClassifier
- type SessionKeyParams
Constants ¶
const ( DefaultAgentID = "main" DefaultMainKey = "main" DefaultAccountID = "default" MaxAgentIDLength = 64 )
Variables ¶
This section is empty.
Functions ¶
func BuildAgentMainSessionKey ¶
BuildAgentMainSessionKey returns "agent:<agentId>:main".
func BuildAgentPeerSessionKey ¶
func BuildAgentPeerSessionKey(params SessionKeyParams) string
BuildAgentPeerSessionKey constructs a session key based on agent, channel, peer, and DM scope.
func IsSubagentSessionKey ¶
IsSubagentSessionKey returns true if the session key represents a subagent.
func NormalizeAccountID ¶
NormalizeAccountID sanitizes an account ID. Empty returns DefaultAccountID.
func NormalizeAgentID ¶
NormalizeAgentID sanitizes an agent ID to [a-z0-9][a-z0-9_-]{0,63}. Invalid characters are collapsed to "-". Leading/trailing dashes stripped. Empty input returns DefaultAgentID ("main").
Types ¶
type Classifier ¶ added in v0.2.1
Classifier evaluates a feature set and returns a complexity score in [0, 1]. A higher score indicates a more complex task that benefits from a heavy model. The score is compared against the configured threshold: score >= threshold selects the primary (heavy) model; score < threshold selects the light model.
Classifier is an interface so that future implementations (ML-based, embedding-based, or any other approach) can be swapped in without changing routing infrastructure.
type Features ¶ added in v0.2.1
type Features struct {
// TokenEstimate is a proxy for token count.
// CJK runes count as 1 token each; non-CJK runes as 0.25 tokens each.
// This avoids API calls while giving accurate estimates for all scripts.
TokenEstimate int
// CodeBlockCount is the number of fenced code blocks (“` pairs) in the message.
// Coding tasks almost always require the heavy model.
CodeBlockCount int
// RecentToolCalls is the count of tool_call messages in the last lookbackWindow
// history entries. A high density indicates an active agentic workflow.
RecentToolCalls int
// ConversationDepth is the total number of messages in the session history.
// Deep sessions tend to carry implicit complexity built up over many turns.
ConversationDepth int
// HasAttachments is true when the message appears to contain media (images,
// audio, video). Multi-modal inputs require vision-capable heavy models.
HasAttachments bool
}
Features holds the structural signals extracted from a message and its session context. Every dimension is language-agnostic by construction — no keyword or pattern matching against natural-language content. This ensures consistent routing for all locales.
type ParsedSessionKey ¶
ParsedSessionKey is the result of parsing an agent-scoped session key.
func ParseAgentSessionKey ¶
func ParseAgentSessionKey(sessionKey string) *ParsedSessionKey
ParseAgentSessionKey extracts agentId and rest from "agent:<agentId>:<rest>".
type ResolvedRoute ¶
type ResolvedRoute struct {
AgentID string
Channel string
AccountID string
SessionKey string
MainSessionKey string
MatchedBy string // "binding.peer", "binding.peer.parent", "binding.guild", "binding.team", "binding.account", "binding.channel", "default"
}
ResolvedRoute is the result of agent routing.
type RouteInput ¶
type RouteInput struct {
Channel string
AccountID string
Peer *RoutePeer
ParentPeer *RoutePeer
GuildID string
TeamID string
}
RouteInput contains the routing context from an inbound message.
type RouteResolver ¶
type RouteResolver struct {
// contains filtered or unexported fields
}
RouteResolver determines which agent handles a message based on config bindings.
func NewRouteResolver ¶
func NewRouteResolver(cfg *config.Config) *RouteResolver
NewRouteResolver creates a new route resolver.
func (*RouteResolver) ResolveRoute ¶
func (r *RouteResolver) ResolveRoute(input RouteInput) ResolvedRoute
ResolveRoute determines which agent handles the message and constructs session keys. Implements the 7-level priority cascade: peer > parent_peer > guild > team > account > channel_wildcard > default
type Router ¶ added in v0.2.1
type Router struct {
// contains filtered or unexported fields
}
Router selects the appropriate model tier for each incoming message. It is safe for concurrent use from multiple goroutines.
func New ¶ added in v0.2.1
func New(cfg RouterConfig) *Router
New creates a Router with the given config and the default RuleClassifier. If cfg.Threshold is zero or negative, defaultThreshold (0.35) is used.
func (*Router) LightModel ¶ added in v0.2.1
LightModel returns the configured light model name.
func (*Router) SelectModel ¶ added in v0.2.1
func (r *Router) SelectModel( msg string, history []providers.Message, primaryModel string, ) (model string, usedLight bool, score float64)
SelectModel returns the model to use for this conversation turn along with the computed complexity score (for logging and debugging).
- If score < cfg.Threshold: returns (cfg.LightModel, true, score)
- Otherwise: returns (primaryModel, false, score)
The caller is responsible for resolving the returned model name into provider candidates (see AgentInstance.LightCandidates).
type RouterConfig ¶ added in v0.2.1
type RouterConfig struct {
// LightModel is the model_name (from model_list) used for simple tasks.
LightModel string
// Threshold is the complexity score cutoff in [0, 1].
// score >= Threshold → primary (heavy) model.
// score < Threshold → light model.
Threshold float64
}
RouterConfig holds the validated model routing settings. It mirrors config.RoutingConfig but lives in pkg/routing to keep the dependency graph simple: pkg/agent resolves config → routing, not the reverse.
type RuleClassifier ¶ added in v0.2.1
type RuleClassifier struct{}
RuleClassifier is the v1 implementation. It uses a weighted sum of structural signals with no external dependencies, no API calls, and sub-microsecond latency. The raw sum is capped at 1.0 so that the returned score always falls within the [0, 1] contract.
Individual weights (multiple signals can fire simultaneously):
token > 200 (≈600 chars): 0.35 — very long prompts are almost always complex token 50-200: 0.15 — medium length; may or may not be complex code block present: 0.40 — coding tasks need the heavy model tool calls > 3 (recent): 0.25 — dense tool usage signals an agentic workflow tool calls 1-3 (recent): 0.10 — some tool activity conversation depth > 10: 0.10 — long sessions carry implicit complexity attachments present: 1.00 — hard gate; multi-modal always needs heavy model
Default threshold is 0.35, so:
- Pure greetings / trivial Q&A: 0.00 → light ✓
- Medium prose message (50–200 tokens): 0.15 → light ✓
- Message with code block: 0.40 → heavy ✓
- Long message (>200 tokens): 0.35 → heavy ✓
- Active tool session + medium message: 0.25 → light (acceptable)
- Any message with an image/audio attachment: 1.00 → heavy ✓
func (*RuleClassifier) Score ¶ added in v0.2.1
func (c *RuleClassifier) Score(f Features) float64
Score computes the complexity score for the given feature set. The returned value is in [0, 1]. Attachments short-circuit to 1.0.