From 532e8ce28e5ebf7a4726fedf7c4c6ac1b837b846 Mon Sep 17 00:00:00 2001 From: ramnnn2006 Date: Wed, 3 Jun 2026 19:26:32 +0530 Subject: [PATCH] fix(ai): wire configured max_tokens and temperature onto requests DefaultAnalyzer.Analyze built the CompletionRequest without MaxTokens or Temperature, so the documented request-level fields never carried through the only real caller. It happened to work because each provider falls back to its own default, but anyone relying on the per-request values had them silently dropped. Thread the configured values through AnalyzerConfig onto the request so the request-level fields are honored, with consistent precedence: configured values ride the request, and a zero value leaves the provider default intact. Fixes #120 Signed-off-by: ramnnn2006 --- internal/ai/analyzer.go | 18 ++++++++++++++---- internal/ai/analyzer_test.go | 36 +++++++++++++++++++++++++++++++++++- internal/cli/doctor.go | 10 ++++++---- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/internal/ai/analyzer.go b/internal/ai/analyzer.go index 6d520c2..9c13eb4 100644 --- a/internal/ai/analyzer.go +++ b/internal/ai/analyzer.go @@ -20,6 +20,9 @@ type DefaultAnalyzer struct { cache *Cache privacy PrivacyMode logger *slog.Logger + + maxTokens int + temperature float64 } // AnalyzerConfig holds configuration for constructing a DefaultAnalyzer. @@ -28,6 +31,9 @@ type AnalyzerConfig struct { Cache *Cache Privacy PrivacyMode Logger *slog.Logger + + MaxTokens int + Temperature float64 } // NewAnalyzer creates a DefaultAnalyzer. @@ -41,10 +47,12 @@ func NewAnalyzer(cfg AnalyzerConfig) *DefaultAnalyzer { logger = slog.New(slog.NewTextHandler(io.Discard, nil)) } return &DefaultAnalyzer{ - provider: cfg.Provider, - cache: cfg.Cache, - privacy: privacy, - logger: logger, + provider: cfg.Provider, + cache: cfg.Cache, + privacy: privacy, + logger: logger, + maxTokens: cfg.MaxTokens, + temperature: cfg.Temperature, } } @@ -73,6 +81,8 @@ func (a *DefaultAnalyzer) Analyze(ctx context.Context, req doctor.AnalysisReques completion, err := a.provider.Complete(ctx, CompletionRequest{ SystemPrompt: SystemPrompt, UserPrompt: userPrompt, + MaxTokens: a.maxTokens, + Temperature: a.temperature, }) if err != nil { return nil, fmt.Errorf("AI provider %s: %w", a.provider.Name(), err) diff --git a/internal/ai/analyzer_test.go b/internal/ai/analyzer_test.go index 152ec7d..231c8b2 100644 --- a/internal/ai/analyzer_test.go +++ b/internal/ai/analyzer_test.go @@ -36,12 +36,15 @@ type scriptedProvider struct { model string err error calls atomic.Uint64 + + lastReq CompletionRequest } func (p *scriptedProvider) Name() string { return "scripted" } -func (p *scriptedProvider) Complete(_ context.Context, _ CompletionRequest) (*CompletionResponse, error) { +func (p *scriptedProvider) Complete(_ context.Context, req CompletionRequest) (*CompletionResponse, error) { p.calls.Add(1) + p.lastReq = req if p.err != nil { return nil, p.err } @@ -92,6 +95,37 @@ func TestAnalyzerUsesDiscardLoggerWhenNil(t *testing.T) { } } +func TestAnalyzerWiresConfiguredMaxTokensAndTemperature(t *testing.T) { + cases := []struct { + name string + maxTokens int + temperature float64 + }{ + {"configured values ride the request", 2048, 0.7}, + {"zero stays zero so provider keeps its default", 0, 0}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + prov := &scriptedProvider{text: `{"summary":"ok"}`} + a := NewAnalyzer(AnalyzerConfig{ + Provider: prov, + Logger: newSilentAILogger(), + MaxTokens: c.maxTokens, + Temperature: c.temperature, + }) + if _, err := a.Analyze(context.Background(), doctor.AnalysisRequest{Signals: emptySignals()}); err != nil { + t.Fatalf("Analyze error: %v", err) + } + if prov.lastReq.MaxTokens != c.maxTokens { + t.Errorf("request MaxTokens = %d, want %d", prov.lastReq.MaxTokens, c.maxTokens) + } + if prov.lastReq.Temperature != c.temperature { + t.Errorf("request Temperature = %v, want %v", prov.lastReq.Temperature, c.temperature) + } + }) + } +} + func TestAnalyzerMarkdownFencedJSON(t *testing.T) { prov := &scriptedProvider{text: "Here you go:\n```json\n{\"summary\":\"ok\"}\n```\n"} diff --git a/internal/cli/doctor.go b/internal/cli/doctor.go index 5076c11..7965c3b 100644 --- a/internal/cli/doctor.go +++ b/internal/cli/doctor.go @@ -453,10 +453,12 @@ func buildAnalyzer(c *config.Config, logger *slog.Logger) (doctor.Analyzer, erro } return ai.NewAnalyzer(ai.AnalyzerConfig{ - Provider: provider, - Cache: cache, - Privacy: privacy, - Logger: logger, + Provider: provider, + Cache: cache, + Privacy: privacy, + Logger: logger, + MaxTokens: aiCfg.MaxTokens, + Temperature: aiCfg.Temperature, }), nil }