This commit is contained in:
2026-08-16 16:57:36 +02:00
parent 96f8c1115c
commit 532d439c41
106 changed files with 10147 additions and 494 deletions
@@ -312,11 +312,13 @@ func (s *Service) ResolveAIConfig(ctx context.Context, role string) (ResolvedAIC
Source: SourceDB,
}
if role == AIRoleProcessing {
// Prefer legacy openai JSON fields before OPENAI_* env so a mock .env
// cannot override a configured platform DB endpoint.
if out.BaseURL == "" {
out.BaseURL = strings.TrimSpace(s.Env.OpenAIBaseURL)
out.BaseURL = firstNonEmpty(strings.TrimSpace(doc.OpenAI.BaseURL), strings.TrimSpace(s.Env.OpenAIBaseURL))
}
if out.Model == "" {
out.Model = strings.TrimSpace(s.Env.OpenAIModel)
out.Model = firstNonEmpty(strings.TrimSpace(doc.OpenAI.Model), strings.TrimSpace(s.Env.OpenAIModel))
}
}
if role == AIRoleVectorization {
@@ -330,20 +332,36 @@ func (s *Service) ResolveAIConfig(ctx context.Context, role string) (ResolvedAIC
return out, nil
}
if role == AIRoleProcessing && !hasRoleMeta {
// processing: prefer legacy openai (DB) then env when the role slot has no key,
// even if provider/model/base meta was partially written.
if role == AIRoleProcessing {
legacy, err := s.resolveLegacyOpenAI(doc)
if err != nil {
return ResolvedAIConfig{}, err
}
return ResolvedAIConfig{
Role: role,
Provider: defaultAIProvider,
APIKey: legacy.APIKey,
BaseURL: legacy.BaseURL,
Model: legacy.Model,
Enabled: legacy.APIKey != "",
Source: legacy.Source,
}, nil
if strings.TrimSpace(legacy.APIKey) != "" {
return ResolvedAIConfig{
Role: role,
Provider: firstNonEmpty(strings.TrimSpace(st.Provider), defaultAIProvider),
APIKey: legacy.APIKey,
BaseURL: firstNonEmpty(strings.TrimSpace(st.BaseURL), legacy.BaseURL),
Model: firstNonEmpty(strings.TrimSpace(st.Model), legacy.Model),
Enabled: true,
Extras: copyStringMap(st.Extras),
Source: legacy.Source,
}, nil
}
if !hasRoleMeta {
return ResolvedAIConfig{
Role: role,
Provider: defaultAIProvider,
APIKey: legacy.APIKey,
BaseURL: legacy.BaseURL,
Model: legacy.Model,
Enabled: legacy.APIKey != "",
Source: legacy.Source,
}, nil
}
}
if role == AIRoleVectorization && !hasRoleMeta {
@@ -115,6 +115,38 @@ func TestResolveAIConfig_envFallback(t *testing.T) {
}
}
func TestResolveAIConfig_dbPreferredOverMockEnv(t *testing.T) {
key := DeriveKey("test-ai-config-secret-material", "fallback")
plain := "sk-db-overloaded-key"
enc, err := EncryptSecret(key, plain)
if err != nil {
t.Fatal(err)
}
svc := &Service{
Key: key,
Env: EnvConfig{
OpenAIAPIKey: "sk-mock-env",
OpenAIBaseURL: "http://127.0.0.1:18767/v1",
OpenAIModel: "mock-llm",
},
}
doc := storedDoc{
OpenAI: openaiStored{
BaseURL: "https://llm.overloadedbot.org/v1",
Model: "code-fast",
APIKeyEnc: enc,
APIKeyLast4: last4(plain),
},
}
got, err := svc.resolveLegacyOpenAI(doc)
if err != nil {
t.Fatal(err)
}
if got.Source != SourceDB || got.APIKey != plain || got.BaseURL != "https://llm.overloadedbot.org/v1" || got.Model != "code-fast" {
t.Fatalf("legacy resolve should prefer DB over mock env: %+v", got)
}
}
func TestPatchAIExtras(t *testing.T) {
var extras map[string]string
val := "1536"