064747f192
Introduce the typed boundary between routing and action resolution: - ActionCandidate: Fn, Args, Source (route|matcher), Producer, Confidence - ResolveActionCandidate(dec, m): standalone function usable by both the daemon and the eval harness - Update eval harness Reach() to use ResolveActionCandidate instead of duplicating the matcher fallback logic This is the routing-side half of the action-resolution boundary. The daemon integration follows in the next commit.
96 lines
3.4 KiB
Go
96 lines
3.4 KiB
Go
package router
|
|
|
|
// ActionCandidate — the result of action resolution, produced before execution.
|
|
// It replaces the implicit ownership split where the router filled Slots.Fn/Args
|
|
// and actionAct re-matched when they were absent. One candidate is produced per
|
|
// IntentAct decision, carrying the resolved function, its arguments, and where
|
|
// the resolution came from.
|
|
type ActionCandidate struct {
|
|
// Fn — the resolved function/tool identity. Empty when no match was found.
|
|
Fn string
|
|
|
|
// Args — positional arguments passed to the tool. May be nil when Fn is
|
|
// empty or when the match produced no arguments.
|
|
Args []string
|
|
|
|
// Source — where the resolution came from. Typed enum, not free-form.
|
|
Source ActionSource
|
|
|
|
// Producer — which cascade stage produced the routing decision that led
|
|
// here. Carried for observability; not used for dispatch.
|
|
Producer RouteProducer
|
|
|
|
// Confidence — the routing confidence from the decision. Carried for
|
|
// observability; not used for dispatch.
|
|
Confidence float64
|
|
}
|
|
|
|
// ActionSource — where action resolution came from. Two values: the router
|
|
// resolved the function upstream (stage-0 grammar or stage-2 extraction), or
|
|
// the fallback matcher ran because the router did not fill Fn.
|
|
type ActionSource string
|
|
|
|
const (
|
|
// ActionSourceRoute — Fn/Args were already resolved in the routing
|
|
// cascade (stage-0 grammar match, stage-2 extractor, or LLM slot
|
|
// backfill). The matcher was not invoked.
|
|
ActionSourceRoute ActionSource = "route"
|
|
|
|
// ActionSourceMatcher — the router left Fn empty, so the fallback
|
|
// matcher ran against the text slot and produced the match.
|
|
ActionSourceMatcher ActionSource = "matcher"
|
|
)
|
|
|
|
// ActionResolved reports whether the candidate resolved to a function.
|
|
func (c ActionCandidate) ActionResolved() bool { return c.Fn != "" }
|
|
|
|
// ResolveActionCandidate produces an ActionCandidate from a routing decision.
|
|
// It is the single boundary between routing and action resolution: everything
|
|
// downstream consumes the candidate rather than re-resolving the function.
|
|
//
|
|
// Resolution rules:
|
|
// - Non-act intents: candidate is not applicable (Fn empty, source empty).
|
|
// - Act with Slots.HasFn: the router already resolved the function upstream
|
|
// (stage-0 grammar, stage-2 extractor, or LLM slot backfill). Candidate
|
|
// source is ActionSourceRoute.
|
|
// - Act without Fn: the fallback matcher runs against the text slot.
|
|
// Candidate source is ActionSourceMatcher on match, or Fn stays empty.
|
|
//
|
|
// The matcher algorithm, enabled-tool set, alias behavior, fuzzy-prefix
|
|
// behavior, and ordering are unchanged — this is a mechanical extraction of
|
|
// the same matching call that actionAct previously owned.
|
|
func ResolveActionCandidate(dec Decision, m ActMatcher) ActionCandidate {
|
|
if dec.Intent != IntentAct {
|
|
return ActionCandidate{}
|
|
}
|
|
|
|
// Router resolved the function upstream.
|
|
if dec.Slots.HasFn {
|
|
return ActionCandidate{
|
|
Fn: dec.Slots.Fn,
|
|
Args: dec.Slots.Args,
|
|
Source: ActionSourceRoute,
|
|
Producer: dec.Producer,
|
|
Confidence: dec.Confidence,
|
|
}
|
|
}
|
|
|
|
// Fallback: invoke the matcher against the text slot.
|
|
if dec.Slots.Text != "" && m != nil {
|
|
if fn, args, ok := m.Match(dec.Slots.Text); ok {
|
|
return ActionCandidate{
|
|
Fn: fn,
|
|
Args: args,
|
|
Source: ActionSourceMatcher,
|
|
Producer: dec.Producer,
|
|
Confidence: dec.Confidence,
|
|
}
|
|
}
|
|
}
|
|
|
|
return ActionCandidate{
|
|
Producer: dec.Producer,
|
|
Confidence: dec.Confidence,
|
|
}
|
|
}
|