Copy default LLM pointers when resolving agents

ResolveAgent aliased the default config's Temperature and MaxTokens
pointers into every agent that left them unset, so all resolved
agents shared one backing value. A mutation through any of those
pointers would corrupt the default and every other agent. It also
dereferenced the default unconditionally even though it can be nil.

Allocate a fresh pointer holding a copy of the default value, and
guard against a nil default so each resolved agent owns independent
state.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-06-01 13:56:16 +02:00
parent 8cf770613a
commit fb60ee1776
4 changed files with 24 additions and 23 deletions

View File

@@ -506,9 +506,9 @@ func (s DocumentService) generateChangelog(
"changelog_generator", "changelog_generator",
s.svc.llmClient, s.svc.llmClient,
agent.WithInstructions(changelogGeneratorSystemPrompt), agent.WithInstructions(changelogGeneratorSystemPrompt),
agent.WithModel(s.svc.llmModel), agent.WithModel(s.svc.llmConfig.Model),
agent.WithTemperature(s.svc.llmTemperature), agent.WithTemperature(s.svc.llmConfig.Temperature),
agent.WithMaxTokens(s.svc.llmMaxTokens), agent.WithMaxTokens(s.svc.llmConfig.MaxTokens),
) )
result, err := ag.Run( result, err := ag.Run(

View File

@@ -59,6 +59,14 @@ type ExportService interface {
} }
type ( type (
// LLMConfig holds the model parameters used by the agents the probo
// service runs.
LLMConfig struct {
Model string
Temperature float64
MaxTokens int
}
Service struct { Service struct {
pg *pg.Client pg *pg.Client
s3 *s3.Client s3 *s3.Client
@@ -67,9 +75,7 @@ type (
baseURL string baseURL string
tokenSecret string tokenSecret string
llmClient *llm.Client llmClient *llm.Client
llmModel string llmConfig LLMConfig
llmTemperature float64
llmMaxTokens int
html2pdfConverter *html2pdf.Converter html2pdfConverter *html2pdf.Converter
acmeService *certmanager.ACMEService acmeService *certmanager.ACMEService
fileManager *filemanager.Service fileManager *filemanager.Service
@@ -129,9 +135,7 @@ func NewService(
baseURL string, baseURL string,
tokenSecret string, tokenSecret string,
llmClient *llm.Client, llmClient *llm.Client,
llmModel string, llmConfig LLMConfig,
llmTemperature float64,
llmMaxTokens int,
html2pdfConverter *html2pdf.Converter, html2pdfConverter *html2pdf.Converter,
acmeService *certmanager.ACMEService, acmeService *certmanager.ACMEService,
fileManagerService *filemanager.Service, fileManagerService *filemanager.Service,
@@ -157,9 +161,7 @@ func NewService(
baseURL: baseURL, baseURL: baseURL,
tokenSecret: tokenSecret, tokenSecret: tokenSecret,
llmClient: llmClient, llmClient: llmClient,
llmModel: llmModel, llmConfig: llmConfig,
llmTemperature: llmTemperature,
llmMaxTokens: llmMaxTokens,
html2pdfConverter: html2pdfConverter, html2pdfConverter: html2pdfConverter,
acmeService: acmeService, acmeService: acmeService,
fileManager: fileManagerService, fileManager: fileManagerService,

View File

@@ -39,6 +39,7 @@ import (
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.gearno.de/kit/unit" "go.gearno.de/kit/unit"
"go.gearno.de/kit/worker" "go.gearno.de/kit/worker"
"go.gearno.de/x/ref"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
"go.probo.inc/probo/packages/emails" "go.probo.inc/probo/packages/emails"
"go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/accessreview"
@@ -507,9 +508,11 @@ func (impl *Implm) Run(
baseURL.String(), baseURL.String(),
impl.cfg.Auth.Cookie.Secret, impl.cfg.Auth.Cookie.Secret,
proboLLMClient, proboLLMClient,
proboAgentCfg.ModelName, probo.LLMConfig{
*proboAgentCfg.Temperature, Model: proboAgentCfg.ModelName,
*proboAgentCfg.MaxTokens, Temperature: ref.UnrefOrZero(proboAgentCfg.Temperature),
MaxTokens: ref.UnrefOrZero(proboAgentCfg.MaxTokens),
},
html2pdfConverter, html2pdfConverter,
acmeService, acmeService,
fileManagerService, fileManagerService,
@@ -790,8 +793,8 @@ func (impl *Implm) Run(
evidenceDescriberLLMClient, evidenceDescriberLLMClient,
evidencedescriber.Config{ evidencedescriber.Config{
Model: evidenceDescriberAgentCfg.ModelName, Model: evidenceDescriberAgentCfg.ModelName,
Temp: *evidenceDescriberAgentCfg.Temperature, Temp: ref.UnrefOrZero(evidenceDescriberAgentCfg.Temperature),
MaxTokens: *evidenceDescriberAgentCfg.MaxTokens, MaxTokens: ref.UnrefOrZero(evidenceDescriberAgentCfg.MaxTokens),
}, },
) )
evidenceDescriptionWorker := probo.NewEvidenceDescriptionWorker( evidenceDescriptionWorker := probo.NewEvidenceDescriptionWorker(

View File

@@ -96,12 +96,8 @@ func (c *AgentsConfig) ResolveAgent(agent LLMAgentConfig) LLMAgentConfig {
agent.ModelName = c.Default.ModelName agent.ModelName = c.Default.ModelName
} }
if agent.Temperature == nil { if agent.MaxTokens == nil && c.Default.MaxTokens != nil {
agent.Temperature = c.Default.Temperature agent.MaxTokens = new(*c.Default.MaxTokens)
}
if agent.MaxTokens == nil {
agent.MaxTokens = c.Default.MaxTokens
} }
return agent return agent