From 2092c50a04297fb128f78ea32adc544afa3dfd58 Mon Sep 17 00:00:00 2001 From: viettranx Date: Tue, 31 Mar 2026 23:38:48 +0700 Subject: [PATCH] fix(agent): use DB provider_type for SOUL echo and strict mode detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add providerTypeOf() to extract provider_type via type assertion (e.g. "chatgpt_oauth") instead of config name (user-set "openai") - Exclude "compat" providers (openai_compat → OpenRouter/DeepSeek/Groq) from strict mode and SOUL echo — they proxy to non-OpenAI models - Fix isOpenAIStrict matching openai_compat incorrectly --- internal/agent/loop_history.go | 2 +- internal/agent/memoryflush.go | 2 +- internal/agent/systemprompt.go | 15 +++++++++++++++ internal/agent/systemprompt_sections.go | 11 +++++++++-- internal/providers/schema_profile.go | 16 +++++++++++++--- 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/internal/agent/loop_history.go b/internal/agent/loop_history.go index 2cace9ff..dc411b78 100644 --- a/internal/agent/loop_history.go +++ b/internal/agent/loop_history.go @@ -252,7 +252,7 @@ func (l *Loop) buildMessages(ctx context.Context, history []providers.Message, s SandboxWorkspaceAccess: l.sandboxWorkspaceAccess, ShellDenyGroups: l.shellDenyGroups, SelfEvolve: l.selfEvolve, - ProviderType: l.provider.Name(), + ProviderType: providerTypeOf(l.provider), CredentialCLIContext: l.buildCredentialCLIContext(ctx), IsBootstrap: hadBootstrap && l.agentType != store.AgentTypePredefined, }) diff --git a/internal/agent/memoryflush.go b/internal/agent/memoryflush.go index ed84c666..28197087 100644 --- a/internal/agent/memoryflush.go +++ b/internal/agent/memoryflush.go @@ -108,7 +108,7 @@ func (l *Loop) runMemoryFlush(ctx context.Context, sessionKey string, settings * Mode: PromptMinimal, ToolNames: l.filteredToolNames(), HasMemory: l.hasMemory, - ProviderType: l.provider.Name(), + ProviderType: providerTypeOf(l.provider), }) systemPrompt += "\n\n" + settings.SystemPrompt diff --git a/internal/agent/systemprompt.go b/internal/agent/systemprompt.go index ec130dc9..7da0676e 100644 --- a/internal/agent/systemprompt.go +++ b/internal/agent/systemprompt.go @@ -6,10 +6,25 @@ import ( "strings" "github.com/nextlevelbuilder/goclaw/internal/bootstrap" + "github.com/nextlevelbuilder/goclaw/internal/providers" "github.com/nextlevelbuilder/goclaw/internal/store" "github.com/nextlevelbuilder/goclaw/internal/tools" ) +// providerTypeOf extracts the DB provider_type (e.g. "chatgpt_oauth", "codex") +// from a Provider. Falls back to Name() if the provider doesn't expose ProviderType(). +func providerTypeOf(p providers.Provider) string { + type providerTyper interface { + ProviderType() string + } + if pt, ok := p.(providerTyper); ok { + if t := pt.ProviderType(); t != "" { + return t + } + } + return p.Name() +} + // PromptMode controls which system prompt sections are included. // Matches TS PromptMode type in system-prompt.ts. type PromptMode string diff --git a/internal/agent/systemprompt_sections.go b/internal/agent/systemprompt_sections.go index 1576fc97..77f837d7 100644 --- a/internal/agent/systemprompt_sections.go +++ b/internal/agent/systemprompt_sections.go @@ -374,10 +374,17 @@ func buildPersonaReminder(files []bootstrap.ContextFile, agentType, providerType // needsSOULEcho returns true for providers that benefit from recency-zone personality echo. // GPT models have strong recency bias and tend to lose persona in long prompts. +// Matches first-party OpenAI (chatgpt_oauth) and Codex only — not compat proxies. func needsSOULEcho(providerType string) bool { - switch strings.ToLower(providerType) { - case "openai", "codex": + lower := strings.ToLower(providerType) + if strings.Contains(lower, "compat") { + return false // openai_compat routes to non-OpenAI models + } + switch { + case lower == "openai" || lower == "codex": return true + case strings.Contains(lower, "chatgpt"): + return true // chatgpt_oauth, chatgpt_plus, etc. } return false } diff --git a/internal/providers/schema_profile.go b/internal/providers/schema_profile.go index 7944638e..1c156dc7 100644 --- a/internal/providers/schema_profile.go +++ b/internal/providers/schema_profile.go @@ -79,11 +79,21 @@ func profileForProvider(name string) SchemaProfile { } // isOpenAIStrict returns true for providers known to support strict tool mode. -// Only first-party OpenAI and Codex are safe — third-party proxies (OpenRouter, -// DeepSeek, Groq, DashScope, etc.) may reject nullable unions or the strict flag. +// Matches first-party OpenAI (including chatgpt_oauth) and Codex. +// Explicitly excludes openai_compat (proxy for OpenRouter, DeepSeek, Groq, etc.). func isOpenAIStrict(name string) bool { lower := strings.ToLower(name) - return lower == "openai" || lower == "codex" + // Exclude compat/proxy providers first — they route to non-OpenAI models. + if strings.Contains(lower, "compat") { + return false + } + switch { + case lower == "openai" || lower == "codex": + return true + case strings.Contains(lower, "chatgpt"): + return true // chatgpt_oauth, chatgpt_plus, etc. + } + return false } // isGeminiName matches config names ("gemini", "gemini-flash") and