Skip to content

Commit 7eeffee

Browse files
committed
Add multimodal content and tool calls support
1 parent 14ace71 commit 7eeffee

3 files changed

Lines changed: 78 additions & 9 deletions

File tree

internal/api/openai.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,42 @@ package api
33
// OpenAI-compatible types (client-facing)
44

55
type OpenAIMessage struct {
6-
Role string `json:"role"`
7-
Content string `json:"content"`
8-
Name string `json:"name,omitempty"`
6+
Role string `json:"role"`
7+
Content interface{} `json:"content,omitempty"`
8+
Name string `json:"name,omitempty"`
9+
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
10+
ToolCallID string `json:"tool_call_id,omitempty"`
11+
Refusal string `json:"refusal,omitempty"`
12+
Audio *MessageAudio `json:"audio,omitempty"`
13+
}
14+
15+
type ContentPart struct {
16+
Type string `json:"type"`
17+
Text string `json:"text,omitempty"`
18+
ImageURL *ImageURL `json:"image_url,omitempty"`
19+
}
20+
21+
type ImageURL struct {
22+
URL string `json:"url"`
23+
Detail string `json:"detail,omitempty"`
24+
Modalities string `json:"modalities,omitempty"`
25+
}
26+
27+
type ToolCall struct {
28+
ID string `json:"id"`
29+
Type string `json:"type"`
30+
Function FunctionCall `json:"function"`
31+
}
32+
33+
type FunctionCall struct {
34+
Name string `json:"name"`
35+
Arguments string `json:"arguments"`
36+
}
37+
38+
type MessageAudio struct {
39+
ID string `json:"id"`
40+
Data string `json:"data"`
41+
Duration float64 `json:"duration"`
942
}
1043

1144
type OpenAIChatRequest struct {

internal/proxy/convert.go

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,50 @@ import (
1010
func ConvertMessages(openAIMsgs []api.OpenAIMessage) []api.CCMessage {
1111
var ccMsgs []api.CCMessage
1212
for _, m := range openAIMsgs {
13+
contentParts := parseContent(m.Content)
1314
ccMsgs = append(ccMsgs, api.CCMessage{
14-
Role: m.Role,
15-
Content: []api.CCContentPart{
16-
{Type: "text", Text: m.Content},
17-
},
15+
Role: m.Role,
16+
Content: contentParts,
1817
})
1918
}
2019
return ccMsgs
2120
}
2221

22+
func parseContent(content interface{}) []api.CCContentPart {
23+
switch v := content.(type) {
24+
case string:
25+
if v == "" {
26+
return nil
27+
}
28+
return []api.CCContentPart{{Type: "text", Text: v}}
29+
case []any:
30+
var parts []api.CCContentPart
31+
for _, part := range v {
32+
if partMap, ok := part.(map[string]any); ok {
33+
if typ, ok := partMap["type"].(string); ok {
34+
p := api.CCContentPart{Type: typ}
35+
if text, ok := partMap["text"].(string); ok {
36+
p.Text = text
37+
}
38+
// Note: CommandCode API may not support image_url
39+
// For now, we skip image parts
40+
if imgURL, ok := partMap["image_url"].(map[string]any); ok {
41+
if url, ok := imgURL["url"].(string); ok {
42+
// Try to extract text from image (e.g., base64 data)
43+
// or just include the URL as text
44+
p.Text = p.Text + "\n[Image URL: " + url + "]"
45+
}
46+
}
47+
parts = append(parts, p)
48+
}
49+
}
50+
}
51+
return parts
52+
default:
53+
return nil
54+
}
55+
}
56+
2357
// Extract system message and remaining messages
2458
func ExtractSystem(msgs []api.OpenAIMessage) (string, []api.OpenAIMessage) {
2559
var system strings.Builder
@@ -29,7 +63,9 @@ func ExtractSystem(msgs []api.OpenAIMessage) (string, []api.OpenAIMessage) {
2963
if system.Len() > 0 {
3064
system.WriteString("\n")
3165
}
32-
system.WriteString(m.Content)
66+
if contentStr, ok := m.Content.(string); ok {
67+
system.WriteString(contentStr)
68+
}
3369
} else {
3470
rest = append(rest, m)
3571
}

internal/proxy/proxy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func (p *Proxy) BuildRequest(openAIReq api.OpenAIChatRequest) (api.CCRequestBody
7373
System: system,
7474
MaxTokens: maxTokens,
7575
Temperature: temperature,
76-
Stream: true,
76+
Stream: openAIReq.Stream,
7777
},
7878
ThreadID: uuid.New().String(),
7979
}

0 commit comments

Comments
 (0)