feat(core): add command, intent, and routing primitives
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export { RoutingPolicy } from './policy.js';
|
||||
export type { RoutingPath, RoutingPolicyConfig, RoutingDecisionInput, RoutingDecision } from './policy.js';
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { RoutingPolicy } from './policy.js';
|
||||
|
||||
describe('RoutingPolicy', () => {
|
||||
it('uses default path when disabled', () => {
|
||||
const policy = new RoutingPolicy({
|
||||
enabled: false,
|
||||
fastPathThreshold: 0.8,
|
||||
llmThreshold: 0.4,
|
||||
defaultPath: 'llm',
|
||||
});
|
||||
|
||||
expect(policy.decide({ confidence: 0.99 })).toEqual({ path: 'llm', reason: 'disabled' });
|
||||
});
|
||||
|
||||
it('routes to fast at or above fast threshold', () => {
|
||||
const policy = new RoutingPolicy({
|
||||
enabled: true,
|
||||
fastPathThreshold: 0.8,
|
||||
llmThreshold: 0.4,
|
||||
defaultPath: 'llm',
|
||||
});
|
||||
|
||||
expect(policy.decide({ confidence: 0.8 })).toEqual({ path: 'fast', reason: 'high_confidence' });
|
||||
expect(policy.decide({ confidence: 0.95 })).toEqual({ path: 'fast', reason: 'high_confidence' });
|
||||
});
|
||||
|
||||
it('routes to llm at or below llm threshold', () => {
|
||||
const policy = new RoutingPolicy({
|
||||
enabled: true,
|
||||
fastPathThreshold: 0.8,
|
||||
llmThreshold: 0.4,
|
||||
defaultPath: 'fast',
|
||||
});
|
||||
|
||||
expect(policy.decide({ confidence: 0.4 })).toEqual({ path: 'llm', reason: 'low_confidence' });
|
||||
expect(policy.decide({ confidence: 0.1 })).toEqual({ path: 'llm', reason: 'low_confidence' });
|
||||
});
|
||||
|
||||
it('uses default path between thresholds', () => {
|
||||
const policy = new RoutingPolicy({
|
||||
enabled: true,
|
||||
fastPathThreshold: 0.8,
|
||||
llmThreshold: 0.4,
|
||||
defaultPath: 'llm',
|
||||
});
|
||||
|
||||
expect(policy.decide({ confidence: 0.6 })).toEqual({ path: 'llm', reason: 'mid_confidence' });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
export type RoutingPath = 'fast' | 'llm';
|
||||
|
||||
export interface RoutingPolicyConfig {
|
||||
enabled: boolean;
|
||||
fastPathThreshold: number;
|
||||
llmThreshold: number;
|
||||
defaultPath: RoutingPath;
|
||||
}
|
||||
|
||||
export interface RoutingDecisionInput {
|
||||
confidence: number | null;
|
||||
}
|
||||
|
||||
export interface RoutingDecision {
|
||||
path: RoutingPath;
|
||||
reason: 'disabled' | 'no_match' | 'high_confidence' | 'low_confidence' | 'mid_confidence';
|
||||
}
|
||||
|
||||
export class RoutingPolicy {
|
||||
private readonly config: RoutingPolicyConfig;
|
||||
|
||||
constructor(config: RoutingPolicyConfig) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
isEnabled(): boolean {
|
||||
return this.config.enabled;
|
||||
}
|
||||
|
||||
decide(input: RoutingDecisionInput): RoutingDecision {
|
||||
if (!this.config.enabled) {
|
||||
return {
|
||||
path: this.config.defaultPath,
|
||||
reason: 'disabled',
|
||||
};
|
||||
}
|
||||
|
||||
if (input.confidence === null) {
|
||||
return {
|
||||
path: this.config.defaultPath,
|
||||
reason: 'no_match',
|
||||
};
|
||||
}
|
||||
|
||||
if (input.confidence >= this.config.fastPathThreshold) {
|
||||
return {
|
||||
path: 'fast',
|
||||
reason: 'high_confidence',
|
||||
};
|
||||
}
|
||||
|
||||
if (input.confidence <= this.config.llmThreshold) {
|
||||
return {
|
||||
path: 'llm',
|
||||
reason: 'low_confidence',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
path: this.config.defaultPath,
|
||||
reason: 'mid_confidence',
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user