Skip to content

Commit 776a9c0

Browse files
zzmczzmc
authored andcommitted
fix(translator/gemini): support developer role in OpenAI Responses requests
1 parent 167edfe commit 776a9c0

2 files changed

Lines changed: 110 additions & 1 deletion

File tree

internal/translator/gemini/openai/responses/gemini_openai-responses_request.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte
118118

119119
switch itemType {
120120
case "message":
121-
if strings.EqualFold(itemRole, "system") {
121+
if strings.EqualFold(itemRole, "system") || strings.EqualFold(itemRole, "developer") {
122122
if contentArray := item.Get("content"); contentArray.Exists() {
123123
systemInstr := []byte(`{"parts":[]}`)
124124
if systemInstructionResult := gjson.GetBytes(out, "systemInstruction"); systemInstructionResult.Exists() {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package responses
2+
3+
import (
4+
"testing"
5+
6+
"github.com/tidwall/gjson"
7+
)
8+
9+
func TestConvertOpenAIResponsesRequestToGemini_SystemAndDeveloperRoles(t *testing.T) {
10+
// Test system role conversion
11+
systemInput := []byte(`{
12+
"instructions": "Be a helpful assistant",
13+
"input": [
14+
{
15+
"type": "message",
16+
"role": "system",
17+
"content": [
18+
{
19+
"type": "input_text",
20+
"text": "System message text"
21+
}
22+
]
23+
},
24+
{
25+
"type": "message",
26+
"role": "user",
27+
"content": [
28+
{
29+
"type": "input_text",
30+
"text": "Hello"
31+
}
32+
]
33+
}
34+
]
35+
}`)
36+
37+
outSystem := ConvertOpenAIResponsesRequestToGemini("gemini-3.5-flash", systemInput, false)
38+
resSystem := gjson.ParseBytes(outSystem)
39+
40+
systemInstruction := resSystem.Get("systemInstruction")
41+
if !systemInstruction.Exists() {
42+
t.Errorf("Expected systemInstruction field to exist")
43+
}
44+
parts := systemInstruction.Get("parts")
45+
if parts.Get("#").Int() != 2 {
46+
t.Errorf("Expected 2 parts in systemInstruction, got %d", parts.Get("#").Int())
47+
}
48+
if parts.Get("0.text").String() != "Be a helpful assistant" {
49+
t.Errorf("Expected first part to be 'Be a helpful assistant', got '%s'", parts.Get("0.text").String())
50+
}
51+
if parts.Get("1.text").String() != "System message text" {
52+
t.Errorf("Expected second part to be 'System message text', got '%s'", parts.Get("1.text").String())
53+
}
54+
55+
// Test developer role conversion (which is the main bug we're addressing)
56+
developerInput := []byte(`{
57+
"instructions": "Be a helpful assistant",
58+
"input": [
59+
{
60+
"type": "message",
61+
"role": "developer",
62+
"content": [
63+
{
64+
"type": "input_text",
65+
"text": "Developer message text"
66+
}
67+
]
68+
},
69+
{
70+
"type": "message",
71+
"role": "user",
72+
"content": [
73+
{
74+
"type": "input_text",
75+
"text": "Hello"
76+
}
77+
]
78+
}
79+
]
80+
}`)
81+
82+
outDev := ConvertOpenAIResponsesRequestToGemini("gemini-3.5-flash", developerInput, false)
83+
resDev := gjson.ParseBytes(outDev)
84+
85+
systemInstructionDev := resDev.Get("systemInstruction")
86+
if !systemInstructionDev.Exists() {
87+
t.Errorf("Expected systemInstruction field to exist for developer role")
88+
}
89+
partsDev := systemInstructionDev.Get("parts")
90+
if partsDev.Get("#").Int() != 2 {
91+
t.Errorf("Expected 2 parts in systemInstruction for developer role, got %d", partsDev.Get("#").Int())
92+
}
93+
if partsDev.Get("0.text").String() != "Be a helpful assistant" {
94+
t.Errorf("Expected first part to be 'Be a helpful assistant', got '%s'", partsDev.Get("0.text").String())
95+
}
96+
if partsDev.Get("1.text").String() != "Developer message text" {
97+
t.Errorf("Expected second part to be 'Developer message text', got '%s'", partsDev.Get("1.text").String())
98+
}
99+
100+
// Ensure role 'developer' is not sent inside contents array as a regular message
101+
contents := resDev.Get("contents")
102+
contents.ForEach(func(_, value gjson.Result) bool {
103+
role := value.Get("role").String()
104+
if role == "developer" {
105+
t.Errorf("Role 'developer' leaked into contents array")
106+
}
107+
return true
108+
})
109+
}

0 commit comments

Comments
 (0)