Skip to content

Commit 6f31578

Browse files
committed
tools support
Signed-off-by: JaredforReal <w13431838023@gmail.com>
1 parent 4635a96 commit 6f31578

File tree

1 file changed

+109
-14
lines changed

1 file changed

+109
-14
lines changed

src/semantic-router/pkg/extproc/mapping_responses.go

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ func mapResponsesRequestToChatCompletions(original []byte) ([]byte, error) {
8888
mapped["max_tokens"] = v
8989
}
9090

91+
// Map tools and tool_choice if present
92+
if v, ok := req["tools"]; ok {
93+
mapped["tools"] = v
94+
}
95+
if v, ok := req["tool_choice"]; ok {
96+
mapped["tool_choice"] = v
97+
}
98+
9199
return json.Marshal(mapped)
92100
}
93101

@@ -117,23 +125,75 @@ func mapChatCompletionToResponses(chatCompletionJSON []byte) ([]byte, error) {
117125
return nil, err
118126
}
119127

120-
content := ""
121-
stopReason := "stop"
122-
if len(parsed.Choices) > 0 {
123-
content = parsed.Choices[0].Message.Content
124-
if parsed.Choices[0].FinishReason != "" {
125-
stopReason = parsed.Choices[0].FinishReason
128+
// Also parse generically to inspect tool calls
129+
var generic map[string]interface{}
130+
_ = json.Unmarshal(chatCompletionJSON, &generic)
131+
132+
var output []map[string]interface{}
133+
if len(parsed.Choices) > 0 && parsed.Choices[0].Message.Content != "" {
134+
output = append(output, map[string]interface{}{
135+
"type": "message",
136+
"role": "assistant",
137+
"content": parsed.Choices[0].Message.Content,
138+
})
139+
}
140+
141+
// Modern tool_calls
142+
if chs, ok := generic["choices"].([]interface{}); ok && len(chs) > 0 {
143+
if ch, ok := chs[0].(map[string]interface{}); ok {
144+
if msg, ok := ch["message"].(map[string]interface{}); ok {
145+
if tcs, ok := msg["tool_calls"].([]interface{}); ok {
146+
for _, tci := range tcs {
147+
if tc, ok := tci.(map[string]interface{}); ok {
148+
name := ""
149+
args := ""
150+
if fn, ok := tc["function"].(map[string]interface{}); ok {
151+
if n, ok := fn["name"].(string); ok {
152+
name = n
153+
}
154+
if a, ok := fn["arguments"].(string); ok {
155+
args = a
156+
}
157+
}
158+
output = append(output, map[string]interface{}{
159+
"type": "tool_call",
160+
"tool_name": name,
161+
"arguments": args,
162+
})
163+
}
164+
}
165+
}
166+
// Legacy function_call
167+
if fc, ok := msg["function_call"].(map[string]interface{}); ok {
168+
name := ""
169+
args := ""
170+
if n, ok := fc["name"].(string); ok {
171+
name = n
172+
}
173+
if a, ok := fc["arguments"].(string); ok {
174+
args = a
175+
}
176+
output = append(output, map[string]interface{}{
177+
"type": "tool_call",
178+
"tool_name": name,
179+
"arguments": args,
180+
})
181+
}
182+
}
126183
}
127184
}
128185

186+
stopReason := "stop"
187+
if len(parsed.Choices) > 0 && parsed.Choices[0].FinishReason != "" {
188+
stopReason = parsed.Choices[0].FinishReason
189+
}
190+
129191
out := map[string]interface{}{
130-
"id": parsed.ID,
131-
"object": "response",
132-
"created": parsed.Created,
133-
"model": parsed.Model,
134-
"output": []map[string]interface{}{
135-
{"type": "message", "role": "assistant", "content": content},
136-
},
192+
"id": parsed.ID,
193+
"object": "response",
194+
"created": parsed.Created,
195+
"model": parsed.Model,
196+
"output": output,
137197
"stop_reason": stopReason,
138198
"usage": map[string]int{
139199
"input_tokens": parsed.Usage.PromptTokens,
@@ -160,9 +220,10 @@ func translateSSEChunkToResponses(chunk []byte) ([][]byte, bool) {
160220
created, _ := parsed["created"].(float64)
161221
// Emit a created event only once per stream (handled by caller)
162222

163-
// Extract content delta and finish_reason
223+
// Extract content delta, tool call deltas, and finish_reason
164224
var deltaText string
165225
var finish string
226+
var toolEvents [][]byte
166227
if arr, ok := parsed["choices"].([]interface{}); ok && len(arr) > 0 {
167228
if ch, ok := arr[0].(map[string]interface{}); ok {
168229
if fr, ok := ch["finish_reason"].(string); ok && fr != "" {
@@ -172,11 +233,45 @@ func translateSSEChunkToResponses(chunk []byte) ([][]byte, bool) {
172233
if c, ok := d["content"].(string); ok {
173234
deltaText = c
174235
}
236+
if tcs, ok := d["tool_calls"].([]interface{}); ok {
237+
for _, tci := range tcs {
238+
if tc, ok := tci.(map[string]interface{}); ok {
239+
ev := map[string]interface{}{"type": "response.tool_calls.delta"}
240+
if idx, ok := tc["index"].(float64); ok {
241+
ev["index"] = int(idx)
242+
}
243+
if fn, ok := tc["function"].(map[string]interface{}); ok {
244+
if n, ok := fn["name"].(string); ok && n != "" {
245+
ev["name"] = n
246+
}
247+
if a, ok := fn["arguments"].(string); ok && a != "" {
248+
ev["arguments_delta"] = a
249+
}
250+
}
251+
b, _ := json.Marshal(ev)
252+
toolEvents = append(toolEvents, b)
253+
}
254+
}
255+
}
256+
if fc, ok := d["function_call"].(map[string]interface{}); ok {
257+
ev := map[string]interface{}{"type": "response.tool_calls.delta"}
258+
if n, ok := fc["name"].(string); ok && n != "" {
259+
ev["name"] = n
260+
}
261+
if a, ok := fc["arguments"].(string); ok && a != "" {
262+
ev["arguments_delta"] = a
263+
}
264+
b, _ := json.Marshal(ev)
265+
toolEvents = append(toolEvents, b)
266+
}
175267
}
176268
}
177269
}
178270

179271
var events [][]byte
272+
if len(toolEvents) > 0 {
273+
events = append(events, toolEvents...)
274+
}
180275
if deltaText != "" {
181276
ev := map[string]interface{}{
182277
"type": "response.output_text.delta",

0 commit comments

Comments
 (0)