{"record":{"id":"2d5edf7a6f2e2def","repo":"micro/go-micro","slug":"flow-llmoptimizer-requires-a-model","errorCode":null,"errorMessage":"flow: LLMOptimizer requires a model","messagePattern":"flow: LLMOptimizer requires a model","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"flow/analyze.go","lineNumber":159,"sourceCode":"\truns, graded, gradeFailures, errors, retries int\n\tfeedback, runIDs                             []string\n\tlatencies                                    []time.Duration\n}\n\n// PromptOptimizer proposes prompt improvements for a candidate without mutating\n// the source flow. Applying the returned prompt stays explicitly gated by the caller.\ntype PromptOptimizer struct{ model ai.Model }\n\n// LLMOptimizer returns an optimizer that asks model to revise prompts for\n// Analyze candidates. The model is injected so tests and callers can use mocks.\nfunc LLMOptimizer(model ai.Model) *PromptOptimizer { return &PromptOptimizer{model: model} }\n\n// OptimizePrompt asks the model for a revised prompt for candidate using the\n// current prompt and trace feedback. It returns only the proposal; it never\n// modifies a Flow, Step, or Checkpoint.\nfunc (o *PromptOptimizer) OptimizePrompt(ctx context.Context, candidate Candidate, currentPrompt string) (string, error) {\n\tif o == nil || o.model == nil {\n\t\treturn \"\", fmt.Errorf(\"flow: LLMOptimizer requires a model\")\n\t}\n\tprompt := fmt.Sprintf(\"Revise this workflow step prompt to improve the failing step.\\nStep: %s\\nMetric: %s\\nScore: %.2f\\nFeedback:\\n- %s\\n\\nCurrent prompt:\\n%s\\n\\nReturn only the revised prompt.\", candidate.Step, candidate.Metric, candidate.Score, strings.Join(candidate.SampleFeedback, \"\\n- \"), currentPrompt)\n\tresp, err := o.model.Generate(ctx, &ai.Request{Prompt: prompt})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tproposal := strings.TrimSpace(resp.Answer)\n\tif proposal == \"\" {\n\t\tproposal = strings.TrimSpace(resp.Reply)\n\t}\n\tif proposal == \"\" {\n\t\treturn \"\", fmt.Errorf(\"flow: LLMOptimizer returned an empty prompt\")\n\t}\n\treturn proposal, nil\n}\n\nfunc verificationFields(result string) (bool, string, bool) {\n\tif result == \"\" {","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/flow/analyze.go#L141-L177","documentation":"The PromptOptimizer was constructed without a backing AI model, so OptimizePrompt cannot generate a revised prompt. The check also covers calling the method on a nil receiver. The library refuses to proceed rather than panicking on a nil model.","triggerScenarios":"Calling OptimizePrompt on a PromptOptimizer built without NewLLMOptimizer(model) — e.g. constructed with a nil model, or when model initialization was skipped because ai.New returned nil for an unknown provider.","commonSituations":"Configuring a flow without Provider/APIKey so no model is created; passing a nil model deliberately for testing; using a pointer to PromptOptimizer that was never initialized.","solutions":["Create the optimizer with a valid model: NewLLMOptimizer(ai.New(\"openai\", ai.WithAPIKey(key))).","Set Provider and APIKey (and optionally BaseURL) in flow options so the model is initialized.","Check the ai.New return value for nil before wiring it into the optimizer.","Skip optimization paths when no model is configured instead of calling OptimizePrompt."],"exampleFix":"// before\nopt := &flow.PromptOptimizer{}\nnewPrompt, err := opt.OptimizePrompt(ctx, cand, prompt)\n// after\nopt := flow.NewLLMOptimizer(ai.New(\"openai\", ai.WithAPIKey(os.Getenv(\"OPENAI_API_KEY\"))))\nnewPrompt, err := opt.OptimizePrompt(ctx, cand, prompt)","handlingStrategy":"validation","validationCode":"if optimizer == nil || reflect.ValueOf(optimizer).IsZero() {\n\treturn errors.New(\"prompt optimizer not configured with a model\")\n}","typeGuard":"func (o *PromptOptimizer) Ready() bool { return o != nil && o.model != nil }","tryCatchPattern":"prompt, err := opt.OptimizePrompt(ctx, cand, current)\nif err != nil && strings.Contains(err.Error(), \"requires a model\") {\n\treturn current, nil // fall back to the unchanged prompt\n}","preventionTips":["Always construct the optimizer via NewLLMOptimizer with a non-nil model.","Validate Provider/APIKey config at service startup, before any optimization runs.","Gate optimization behind a config flag so it's skipped when no model is configured."],"tags":["flow","ai","configuration"],"backgroundTag":"missing-model-configuration","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}