package routing import ( "fmt" ) // SpecBuilder defines interface for building workflow specs type SpecBuilder interface { // FromIntent builds spec from an analyzed intent FromIntent(intent *Intent, metadata *BuildMetadata) (*WorkflowSpec, error) // Validate checks if builder can build this intent Validate(intent *Intent) error } // BuildMetadata contains metadata for spec building type BuildMetadata struct { KnowledgeBase *KnowledgeBase Context map[string]interface{} Validators []WorkflowValidator } // DefaultSpecBuilder implements basic spec building type DefaultSpecBuilder struct { kb *KnowledgeBase } // NewDefaultSpecBuilder creates a builder func NewDefaultSpecBuilder(kb *KnowledgeBase) *DefaultSpecBuilder { return &DefaultSpecBuilder{kb: kb} } // Validate checks if intent is buildable func (dsb *DefaultSpecBuilder) Validate(intent *Intent) error { if len(intent.Activities) == 0 { return fmt.Errorf("intent has no activities") } for _, actName := range intent.Activities { if !dsb.kb.HasActivity(actName) { return fmt.Errorf("activity %s not found", actName) } } return nil } // FromIntent builds spec from intent func (dsb *DefaultSpecBuilder) FromIntent(intent *Intent, metadata *BuildMetadata) (*WorkflowSpec, error) { if err := dsb.Validate(intent); err != nil { return nil, err } states := make([]State, 0, len(intent.Activities)+1) // Build states for each activity for i, actName := range intent.Activities { act := dsb.kb.GetActivity(actName) state := State{ Name: actName, Type: StateTypeTask, Resource: actName, Parameters: dsb.buildParameters(act, intent, i), Timeout: act.Constraints.DefaultTimeout, Retry: dsb.buildRetryPolicy(act, intent), } // Set next state if i < len(intent.Activities)-1 { state.Next = intent.Activities[i+1] } else { state.End = true } // Add catch for flaky activities if act.Constraints.IsFlaky && intent.ErrorHandling != "fail-fast" { state.Catch = []CatchClause{ { ErrorEquals: []string{"ActivityError", "TimeoutError"}, Next: "HandleError", }, } } states = append(states, state) } // Add error handler if needed if dsb.hasFlaky(intent) && intent.ErrorHandling != "fail-fast" { states = append(states, State{ Name: "HandleError", Type: StateTypeFail, Error: "ActivityFailed", Cause: "One or more activities failed", }) } // Build input map inputMap := make(map[string]interface{}) for k, v := range intent.Parameters { inputMap[k] = v } if metadata != nil && metadata.Context != nil { for k, v := range metadata.Context { if _, exists := inputMap[k]; !exists { inputMap[k] = v } } } spec := &WorkflowSpec{ Name: intent.WorkflowName, Input: inputMap, States: states, } // Validate if validators provided if metadata != nil && len(metadata.Validators) > 0 { for _, v := range metadata.Validators { if err := v.Validate(spec); err != nil { return nil, err } } } return spec, nil } // buildParameters creates parameters for activity func (dsb *DefaultSpecBuilder) buildParameters(act *ActivityMetadata, intent *Intent, stateIndex int) map[string]interface{} { params := make(map[string]interface{}) resolver := ¶mResolver{ intent: intent, kb: dsb.kb, } if stateIndex > 0 { resolver.prevState = intent.Activities[stateIndex-1] } for name, def := range act.Inputs { if val := resolver.resolve(name, def); val != nil { params[name] = val } } return params } // buildRetryPolicy creates retry policy func (dsb *DefaultSpecBuilder) buildRetryPolicy(act *ActivityMetadata, intent *Intent) *RetryPolicy { if intent.ErrorHandling == "fail-fast" { return &RetryPolicy{ MaxAttempts: 1, BackoffRate: 1.0, InitialInterval: "1s", } } return &RetryPolicy{ MaxAttempts: int32(act.Constraints.RecommendedRetries), BackoffRate: act.Constraints.RetryBackoff, InitialInterval: "1s", MaxInterval: "30s", } } // hasFlaky checks if any activity is flaky func (dsb *DefaultSpecBuilder) hasFlaky(intent *Intent) bool { for _, actName := range intent.Activities { act := dsb.kb.GetActivity(actName) if act != nil && act.Constraints.IsFlaky { return true } } return false } // CronSpecBuilder builds cron workflow specs type CronSpecBuilder struct { regularBuilder SpecBuilder } // NewCronSpecBuilder creates a cron builder func NewCronSpecBuilder(regularBuilder SpecBuilder) *CronSpecBuilder { return &CronSpecBuilder{ regularBuilder: regularBuilder, } } // Validate checks if intent is valid for cron func (csb *CronSpecBuilder) Validate(intent *Intent) error { if !intent.IsCron { return fmt.Errorf("intent is not marked as cron") } if intent.CronSchedule == "" { return fmt.Errorf("cron schedule is empty") } return csb.regularBuilder.Validate(intent) } // FromIntent builds cron spec func (csb *CronSpecBuilder) FromIntent(intent *Intent, metadata *BuildMetadata) (*WorkflowSpec, error) { spec, err := csb.regularBuilder.FromIntent(intent, metadata) if err != nil { return nil, err } // In production, wrap with cron metadata // For now, just return the regular spec return spec, nil } // SpecBuilderFactory creates appropriate spec builders type SpecBuilderFactory struct { kb *KnowledgeBase } // NewSpecBuilderFactory creates a factory func NewSpecBuilderFactory(kb *KnowledgeBase) *SpecBuilderFactory { return &SpecBuilderFactory{kb: kb} } // CreateBuilder creates appropriate builder for intent func (sbf *SpecBuilderFactory) CreateBuilder(intent *Intent) (SpecBuilder, error) { if intent.IsCron { return NewCronSpecBuilder(NewDefaultSpecBuilder(sbf.kb)), nil } return NewDefaultSpecBuilder(sbf.kb), nil } // CompositeSpecBuilder combines multiple builders with fallback type CompositeSpecBuilder struct { builders []SpecBuilder } // NewCompositeSpecBuilder creates a composite builder func NewCompositeSpecBuilder(builders ...SpecBuilder) *CompositeSpecBuilder { return &CompositeSpecBuilder{builders: builders} } // Validate tries each builder func (csb *CompositeSpecBuilder) Validate(intent *Intent) error { var lastErr error for _, builder := range csb.builders { if err := builder.Validate(intent); err == nil { return nil } else { lastErr = err } } if lastErr != nil { return lastErr } return fmt.Errorf("no builder could validate intent") } // FromIntent tries each builder func (csb *CompositeSpecBuilder) FromIntent(intent *Intent, metadata *BuildMetadata) (*WorkflowSpec, error) { var lastErr error for _, builder := range csb.builders { if spec, err := builder.FromIntent(intent, metadata); err == nil { return spec, nil } else { lastErr = err } } if lastErr != nil { return nil, lastErr } return nil, fmt.Errorf("no builder could create spec") }