mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 22:15:47 +00:00
fix(providers): use developer role for native OpenAI endpoints (GPT-4o+)
GPT-4o+ models prioritize "developer" messages over "system" for instruction adherence. GoClaw was sending "system" for all providers, causing GPT models to poorly follow SOUL/system prompts. Map "system" → "developer" only for native OpenAI endpoints (api.openai.com). Non-OpenAI backends keep "system" role unchanged. Ported from OpenClaw TS: model-compat.ts → isOpenAINativeEndpoint()
This commit is contained in:
1 parent
7fd31c34aa
commit
a47d7f9f4f
1 file changed
+23
-1
@@ -28,6 +28,16 @@ type OpenAIProvider struct {
|
||||
retryConfig RetryConfig
|
||||
}
|
||||
|
||||
// isOpenAINativeEndpoint returns true for endpoints confirmed to be native OpenAI
|
||||
// infrastructure that accepts the "developer" message role.
|
||||
// Azure OpenAI, proxies, and other OpenAI-compatible backends only support "system".
|
||||
// Matching OpenClaw TS: model-compat.ts → isOpenAINativeEndpoint().
|
||||
func isOpenAINativeEndpoint(apiBase string) bool {
|
||||
// Extract hostname from the API base URL.
|
||||
lower := strings.ToLower(apiBase)
|
||||
return strings.Contains(lower, "api.openai.com")
|
||||
}
|
||||
|
||||
func NewOpenAIProvider(name, apiKey, apiBase, defaultModel string) *OpenAIProvider {
|
||||
if apiBase == "" {
|
||||
apiBase = "https://api.openai.com/v1"
|
||||
@@ -269,14 +279,26 @@ func (p *OpenAIProvider) buildRequestBody(model string, req ChatRequest, stream
|
||||
inputMessages = collapseToolCallsWithoutSig(inputMessages)
|
||||
}
|
||||
|
||||
// Detect native OpenAI endpoint to enable developer role.
|
||||
// GPT-4o+ models prioritize "developer" messages over "system" for instruction
|
||||
// adherence. Non-OpenAI backends (proxies, Qwen, DeepSeek, etc.) reject "developer".
|
||||
// Matching OpenClaw TS: model-compat.ts → isOpenAINativeEndpoint().
|
||||
useDevRole := isOpenAINativeEndpoint(p.apiBase)
|
||||
|
||||
// Convert messages to proper OpenAI wire format.
|
||||
// This is necessary because our internal Message/ToolCall structs don't match
|
||||
// the OpenAI API format (tool_calls need type+function wrapper, arguments as JSON string).
|
||||
// Also omits empty content on assistant messages with tool_calls (Gemini compatibility).
|
||||
msgs := make([]map[string]any, 0, len(inputMessages))
|
||||
for _, m := range inputMessages {
|
||||
role := m.Role
|
||||
// Map "system" → "developer" for native OpenAI endpoints (GPT-4o+).
|
||||
// The developer role has higher instruction priority than system role.
|
||||
if useDevRole && role == "system" {
|
||||
role = "developer"
|
||||
}
|
||||
msg := map[string]any{
|
||||
"role": m.Role,
|
||||
"role": role,
|
||||
}
|
||||
|
||||
// Echo reasoning_content for assistant messages (required by Kimi, DeepSeek when thinking is enabled)
|
||||
|
||||
Reference in new issue
Block a user