router: Router.Route consumes NormalizedInput directly (slice 11)
Change Route from (ctx, utterance string, now) to (ctx, input NormalizedInput, now). The ingress-constructed NormalizedInput now reaches the cascade intact — no reconstruction downstream. TryFastPath receives the same input, not a rebuilt one. All callers (production, eval framework, tests) updated to construct NormalizedInput.
This commit is contained in:
@@ -79,7 +79,7 @@ func (r *turnRoute) resolve(ctx context.Context) (router.Decision, bool, *dialog
|
||||
r.err = router.ErrNoIntents
|
||||
return
|
||||
}
|
||||
r.dec, r.err = r.h.router.Route(ctx, r.input.Text, r.now)
|
||||
r.dec, r.err = r.h.router.Route(ctx, r.input, r.now)
|
||||
})
|
||||
return r.dec, r.cont, r.prev, r.err
|
||||
}
|
||||
|
||||
@@ -103,17 +103,17 @@ func (f Fixture) Now() (time.Time, error) {
|
||||
// Router — the one thing a route decider must do to be scorable. *router.Router
|
||||
// satisfies it directly; an LLM-only path wraps its Route in RouterFunc.
|
||||
type Router interface {
|
||||
Route(ctx context.Context, utterance string, now time.Time) (router.Decision, error)
|
||||
Route(ctx context.Context, input router.NormalizedInput, now time.Time) (router.Decision, error)
|
||||
}
|
||||
|
||||
// RouterFunc adapts a bare function to Router — for scoring a single stage
|
||||
// (e.g. *router.LLMRouter, whose Route returns an extra ok bool) without
|
||||
// standing up the whole cascade.
|
||||
type RouterFunc func(ctx context.Context, utterance string, now time.Time) (router.Decision, error)
|
||||
type RouterFunc func(ctx context.Context, input router.NormalizedInput, now time.Time) (router.Decision, error)
|
||||
|
||||
// Route implements Router.
|
||||
func (f RouterFunc) Route(ctx context.Context, utterance string, now time.Time) (router.Decision, error) {
|
||||
return f(ctx, utterance, now)
|
||||
func (f RouterFunc) Route(ctx context.Context, input router.NormalizedInput, now time.Time) (router.Decision, error) {
|
||||
return f(ctx, input, now)
|
||||
}
|
||||
|
||||
// Outcome — one scored case. Reasons is empty exactly when Pass is true.
|
||||
@@ -231,7 +231,7 @@ func Score(ctx context.Context, name string, r Router, f Fixture) (Report, error
|
||||
|
||||
for _, c := range f.Cases {
|
||||
start := time.Now()
|
||||
d, err := r.Route(ctx, c.Utterance, now)
|
||||
d, err := r.Route(ctx, router.NormalizedInput{Text: c.Utterance}, now)
|
||||
o := Outcome{Case: c, Decision: d, Err: err, Latency: time.Since(start)}
|
||||
lat = append(lat, o.Latency)
|
||||
|
||||
|
||||
@@ -74,8 +74,8 @@ func TestLLMRouterBaseline(t *testing.T) {
|
||||
// llm-only: the LLM stage in isolation. Route returns (Decision, ok, err);
|
||||
// !ok without an error would be a contract violation, so it is surfaced as
|
||||
// one rather than silently scored as a miss.
|
||||
llmOnly := RouterFunc(func(ctx context.Context, u string, now time.Time) (router.Decision, error) {
|
||||
d, ok, err := lr.Route(ctx, u, now)
|
||||
llmOnly := RouterFunc(func(ctx context.Context, input router.NormalizedInput, now time.Time) (router.Decision, error) {
|
||||
d, ok, err := lr.Route(ctx, input.Text, now)
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
@@ -152,8 +152,8 @@ func TestReachWithLLMRouter(t *testing.T) {
|
||||
lr := router.NewLLMRouter(client)
|
||||
m := router.DefaultActMatcher{Fns: actFns}
|
||||
|
||||
llmOnly := RouterFunc(func(ctx context.Context, u string, now time.Time) (router.Decision, error) {
|
||||
d, ok, err := lr.Route(ctx, u, now)
|
||||
llmOnly := RouterFunc(func(ctx context.Context, input router.NormalizedInput, now time.Time) (router.Decision, error) {
|
||||
d, ok, err := lr.Route(ctx, input.Text, now)
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func ScoreReach(ctx context.Context, name string, r Router, m router.ActMatcher,
|
||||
|
||||
for _, c := range f.Cases {
|
||||
start := time.Now()
|
||||
d, err := r.Route(ctx, c.Utterance, now)
|
||||
d, err := r.Route(ctx, router.NormalizedInput{Text: c.Utterance}, now)
|
||||
o := ReachOutcome{Case: c, Decision: d, Err: err, Latency: time.Since(start)}
|
||||
lat = append(lat, o.Latency)
|
||||
|
||||
|
||||
@@ -71,9 +71,11 @@ func New(cfg Config) *Router {
|
||||
// is flagged Clarify (the daemon asks rather than guesses — same shape as
|
||||
// since(key)==null → don't fire: a misrouted fact is a confident wrong write,
|
||||
// worse than a gap).
|
||||
func (r *Router) Route(ctx context.Context, utterance string, now time.Time) (Decision, error) {
|
||||
func (r *Router) Route(ctx context.Context, input NormalizedInput, now time.Time) (Decision, error) {
|
||||
utterance := input.Text
|
||||
|
||||
// stage 0 — deterministic fast path. First match wins; grammars are ordered.
|
||||
fast, err := r.TryFastPath(ctx, NormalizedInput{Text: utterance}, now)
|
||||
fast, err := r.TryFastPath(ctx, input, now)
|
||||
if err != nil {
|
||||
return Decision{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user