refactor: make routing system extensible with provider/builder interfaces
ci / test (push) Failing after 3m24s

BREAKING: LLMRouter now requires explicit LLMProvider

New Abstractions:
- LLMProvider interface: swap providers (OpenAI, Claude, local, etc)
- SpecBuilder interface: custom spec generation strategies
- ParameterBinder interface: flexible parameter resolution
- ActivityExecutor interface: pluggable activity execution
- WorkflowValidator interface: composable validation

Provider System:
- ProviderRegistry: manage multiple LLM providers
- RoutingProviderLLM: fallback across providers
- CachingLLMProvider: caching wrapper
- RetryingLLMProvider: retry wrapper

Spec Building:
- DefaultSpecBuilder: basic spec generation
- CronSpecBuilder: cron workflow specialization
- SpecBuilderFactory: builder selection
- CompositeSpecBuilder: multi-strategy fallback
- BuildMetadata: context for builders

Validators:
- StateGraphValidator: DAG structure
- ActivityAvailabilityValidator: activity existence
- TimeoutValidator: timeout format
- CompositeValidator: multiple validators
- TransitionValidator: state transitions

Refactored Components:
- LLMRouter: config-driven, provider-agnostic
- LLMClient: now implements LLMProvider
- llm_router.go: 97 fewer lines (delegated to builders)

Migration Path:
OLD: NewLLMRouter(kb)
NEW: NewLLMRouter(LLMRouterConfig{Provider: ..., KB: ...})
This commit is contained in:
Test
2026-09-03 09:17:38 -07:00
parent fd2ebce8e1
commit 0d70da4f31
7 changed files with 856 additions and 27 deletions
+73 -25
View File
@@ -53,46 +53,94 @@ type LLMRouterOutput struct {
// LLMRouter orchestrates intent analysis and spec generation
type LLMRouter struct {
client *LLMClient
knowledgeBase *KnowledgeBase
provider LLMProvider
knowledgeBase *KnowledgeBase
specBuilder SpecBuilder
validators []WorkflowValidator
paramBinder ParameterBinder
promptTemplate PromptTemplate
}
// NewLLMRouter creates a new LLM router
func NewLLMRouter(kb *KnowledgeBase) (*LLMRouter, error) {
return &LLMRouter{
client: NewLLMClient(),
knowledgeBase: kb,
}, nil
// LLMRouterConfig configures the router
type LLMRouterConfig struct {
Provider LLMProvider
KnowledgeBase *KnowledgeBase
SpecBuilder SpecBuilder
Validators []WorkflowValidator
ParamBinder ParameterBinder
}
// NewLLMRouter creates a new LLM router with custom config
func NewLLMRouter(config LLMRouterConfig) (*LLMRouter, error) {
if config.Provider == nil {
return nil, fmt.Errorf("provider is required")
}
if config.KnowledgeBase == nil {
return nil, fmt.Errorf("knowledge base is required")
}
router := &LLMRouter{
provider: config.Provider,
knowledgeBase: config.KnowledgeBase,
}
// Set defaults
if config.SpecBuilder == nil {
router.specBuilder = NewDefaultSpecBuilder(config.KnowledgeBase)
} else {
router.specBuilder = config.SpecBuilder
}
if config.ParamBinder == nil {
router.paramBinder = NewDefaultParameterBinder()
} else {
router.paramBinder = config.ParamBinder
}
router.validators = config.Validators
if len(router.validators) == 0 {
router.validators = []WorkflowValidator{
&StateGraphValidator{},
NewActivityAvailabilityValidator(config.KnowledgeBase),
&TimeoutValidator{},
}
}
return router, nil
}
// NewLLMRouterDefault creates router with default HTTP provider
func NewLLMRouterDefault(kb *KnowledgeBase) (*LLMRouter, error) {
client := NewLLMClient()
return NewLLMRouter(LLMRouterConfig{
Provider: client,
KnowledgeBase: kb,
})
}
// Route analyzes user message and generates appropriate workflow spec
func (r *LLMRouter) Route(ctx context.Context, input LLMRouterInput) (*LLMRouterOutput, error) {
// 1. Analyze intent using LLM
// 1. Analyze intent using LLM provider
intent, err := r.analyzeIntent(ctx, input)
if err != nil {
return nil, fmt.Errorf("intent analysis failed: %w", err)
}
// 2. Build workflow spec based on intent
if intent.IsCron {
cronSpec, err := r.buildCronSpec(intent, input)
if err != nil {
return nil, fmt.Errorf("cron spec build failed: %w", err)
}
return &LLMRouterOutput{
CronSpec: cronSpec,
IsCron: true,
}, nil
metadata := &BuildMetadata{
KnowledgeBase: r.knowledgeBase,
Context: input.Context,
Validators: r.validators,
}
spec, err := r.buildSpec(intent, input)
spec, err := r.specBuilder.FromIntent(intent, metadata)
if err != nil {
return nil, fmt.Errorf("spec build failed: %w", err)
}
return &LLMRouterOutput{
Spec: spec,
IsCron: false,
IsCron: intent.IsCron,
}, nil
}
@@ -107,15 +155,15 @@ type Intent struct {
ErrorHandling string `json:"errorHandling"` // "retry", "fail-fast", "continue"
}
// analyzeIntent uses LLM to understand user request
// analyzeIntent uses LLM provider to understand user request
func (r *LLMRouter) analyzeIntent(ctx context.Context, input LLMRouterInput) (*Intent, error) {
// Build prompt with knowledge base context
prompt := r.buildIntentPrompt(input)
userPrompt := r.buildIntentPrompt(input)
// Call LLM
response, err := r.client.Chat(ctx, intentSystemPrompt, prompt)
// Call LLM provider
response, err := r.provider.Chat(ctx, intentSystemPrompt, userPrompt)
if err != nil {
return nil, fmt.Errorf("LLM call failed: %w", err)
return nil, fmt.Errorf("LLM provider failed: %w", err)
}
// Parse LLM response