diff --git a/backend/internal/pkg/apicompat/chatcompletions_responses_test.go b/backend/internal/pkg/apicompat/chatcompletions_responses_test.go
index 1731892735..1a27ef2374 100644
--- a/backend/internal/pkg/apicompat/chatcompletions_responses_test.go
+++ b/backend/internal/pkg/apicompat/chatcompletions_responses_test.go
@@ -92,6 +92,56 @@ func TestChatCompletionsToResponses_SystemMessage(t *testing.T) {
assert.Equal(t, "user", items[1].Role)
}
+func TestChatCompletionsToResponses_MessageTypesWithReasoningAndTools(t *testing.T) {
+ req := &ChatCompletionsRequest{
+ Model: "step-5-preview",
+ Messages: []ChatMessage{
+ {Role: "system", Content: json.RawMessage(`"You are helpful."`)},
+ {Role: "user", Content: json.RawMessage(`"Check the directory"`)},
+ {
+ Role: "assistant",
+ Content: json.RawMessage(`"I will check."`),
+ ReasoningContent: "Need to inspect the directory.",
+ ToolCalls: []ChatToolCall{{
+ ID: "call_1", Type: "function",
+ Function: ChatFunctionCall{Name: "bash", Arguments: `{"cmd":"pwd"}`},
+ }},
+ },
+ {Role: "tool", ToolCallID: "call_1", Content: json.RawMessage(`"/tmp"`)},
+ {Role: "assistant", ReasoningContent: "The tool returned /tmp."},
+ {Role: "user", Content: json.RawMessage(`"Continue"`)},
+ },
+ }
+
+ resp, err := ChatCompletionsToResponses(req)
+ require.NoError(t, err)
+
+ var items []ResponsesInputItem
+ require.NoError(t, json.Unmarshal(resp.Input, &items))
+ require.Len(t, items, 7)
+ for i, want := range []struct{ typ, role string }{
+ {"message", "system"},
+ {"message", "user"},
+ {"message", "assistant"},
+ {"function_call", ""},
+ {"function_call_output", ""},
+ {"message", "assistant"},
+ {"message", "user"},
+ } {
+ assert.Equal(t, want.typ, items[i].Type, "input item %d type", i)
+ assert.Equal(t, want.role, items[i].Role, "input item %d role", i)
+ }
+ var assistantContent, reasoningOnlyContent []ResponsesContentPart
+ require.NoError(t, json.Unmarshal(items[2].Content, &assistantContent))
+ require.NoError(t, json.Unmarshal(items[5].Content, &reasoningOnlyContent))
+ require.Len(t, assistantContent, 1)
+ require.Len(t, reasoningOnlyContent, 1)
+ assert.Equal(t, "Need to inspect the directory.\nI will check.", assistantContent[0].Text)
+ assert.Equal(t, "The tool returned /tmp.", reasoningOnlyContent[0].Text)
+ assert.Equal(t, "call_1", items[3].CallID)
+ assert.Equal(t, "call_1", items[4].CallID)
+}
+
func TestChatCompletionsToResponses_ToolCalls(t *testing.T) {
req := &ChatCompletionsRequest{
Model: "gpt-4o",
diff --git a/backend/internal/pkg/apicompat/chatcompletions_to_responses.go b/backend/internal/pkg/apicompat/chatcompletions_to_responses.go
index 773b12b69a..c6fdcb7352 100644
--- a/backend/internal/pkg/apicompat/chatcompletions_to_responses.go
+++ b/backend/internal/pkg/apicompat/chatcompletions_to_responses.go
@@ -143,7 +143,7 @@ func chatSystemToResponses(m ChatMessage) ([]ResponsesInputItem, error) {
if err != nil {
return nil, err
}
- return []ResponsesInputItem{{Role: "system", Content: content}}, nil
+ return []ResponsesInputItem{{Type: "message", Role: "system", Content: content}}, nil
}
// chatUserToResponses converts a user message, handling both plain strings and
@@ -157,7 +157,7 @@ func chatUserToResponses(m ChatMessage) ([]ResponsesInputItem, error) {
if err != nil {
return nil, err
}
- return []ResponsesInputItem{{Role: "user", Content: content}}, nil
+ return []ResponsesInputItem{{Type: "message", Role: "user", Content: content}}, nil
}
// chatAssistantToResponses converts an assistant message. If there is both
@@ -192,7 +192,7 @@ func chatAssistantToResponses(m ChatMessage) ([]ResponsesInputItem, error) {
if err != nil {
return nil, err
}
- items = append(items, ResponsesInputItem{Role: "assistant", Content: partsJSON})
+ items = append(items, ResponsesInputItem{Type: "message", Role: "assistant", Content: partsJSON})
}
// Emit one function_call item per tool_call.
diff --git a/backend/internal/pkg/apicompat/types.go b/backend/internal/pkg/apicompat/types.go
index 44a5b4c79c..00b6e85c4d 100644
--- a/backend/internal/pkg/apicompat/types.go
+++ b/backend/internal/pkg/apicompat/types.go
@@ -263,7 +263,7 @@ type ResponsesText struct {
// The Type field determines which other fields are populated.
type ResponsesInputItem struct {
// Common
- Type string `json:"type,omitempty"` // "" for role-based messages
+ Type string `json:"type,omitempty"` // "message" for role-based messages
// Role-based messages (developer/system/user/assistant)
Role string `json:"role,omitempty"`