routing

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultAgentID   = "main"
	DefaultMainKey   = "main"
	DefaultAccountID = "default"
	MaxAgentIDLength = 64
)

Variables

This section is empty.

Functions

func BuildAgentMainSessionKey

func BuildAgentMainSessionKey(agentID string) string

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

func IsSubagentSessionKey(sessionKey string) bool

IsSubagentSessionKey returns true if the session key represents a subagent.

func NormalizeAccountID

func NormalizeAccountID(id string) string

NormalizeAccountID sanitizes an account ID. Empty returns DefaultAccountID.

func NormalizeAgentID

func NormalizeAgentID(id string) string

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

type Classifier interface {
	Score(f Features) float64
}

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 DMScope

type DMScope string

DMScope controls DM session isolation granularity.

const (
	DMScopeMain                  DMScope = "main"
	DMScopePerPeer               DMScope = "per-peer"
	DMScopePerChannelPeer        DMScope = "per-channel-peer"
	DMScopePerAccountChannelPeer DMScope = "per-account-channel-peer"
)

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.

func ExtractFeatures added in v0.2.1

func ExtractFeatures(msg string, history []providers.Message) Features

ExtractFeatures computes the structural feature vector for a message. It is a pure function with no side effects and zero allocations beyond the returned struct.

type ParsedSessionKey

type ParsedSessionKey struct {
	AgentID string
	Rest    string
}

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 RoutePeer

type RoutePeer struct {
	Kind string // "direct", "group", "channel"
	ID   string
}

RoutePeer represents a chat peer with kind and ID.

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

func (r *Router) LightModel() string

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).

func (*Router) Threshold added in v0.2.1

func (r *Router) Threshold() float64

Threshold returns the complexity threshold in use.

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.

type SessionKeyParams

type SessionKeyParams struct {
	AgentID       string
	Channel       string
	AccountID     string
	Peer          *RoutePeer
	DMScope       DMScope
	IdentityLinks map[string][]string
}

SessionKeyParams holds all inputs for session key construction.

Jump to

Keyboard shortcuts

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