mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-10-18 05:29:00 +08:00
- Add `InteractionsSessionCache` to preserve Antigravity interaction continuations across tool call rounds - Automatically restore and inject `previous_interaction_id` and `environment_id` in Gemini interactions executor - Normalize Antigravity tool identity mappings in OpenAI responses translator - Add `/v1/models/*model` endpoint to support retrieving individual model details Closes: #5190
69 lines
3.2 KiB
Go
69 lines
3.2 KiB
Go
package executor
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
sdktranslator "github.com/router-for-me/CLIProxyAPI/v8/sdk/translator"
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
func TestIssue5190ToolNamesAcrossChatAndResponses(t *testing.T) {
|
|
const model = "antigravity-preview-05-2026"
|
|
for _, format := range []sdktranslator.Format{sdktranslator.FormatOpenAI, sdktranslator.FormatOpenAIResponse} {
|
|
t.Run(format.String(), func(t *testing.T) {
|
|
original := []byte(`{"messages":[{"role":"user","content":"hi"}],"tools":[{"type":"function","function":{"name":"read_file","parameters":{"type":"object"}}},{"type":"function","function":{"name":"external_read_file","parameters":{"type":"object"}}}]}`)
|
|
if format == sdktranslator.FormatOpenAIResponse {
|
|
original = []byte(`{"input":"hi","tools":[{"type":"function","name":"read_file","parameters":{"type":"object"}},{"type":"function","name":"external_read_file","parameters":{"type":"object"}}]}`)
|
|
}
|
|
request := sdktranslator.TranslateRequest(format, sdktranslator.FormatInteractions, model, original, false)
|
|
for i, upstream := range []string{"external_read_file", "external_external_read_file"} {
|
|
if got := gjson.GetBytes(request, fmt.Sprintf("tools.%d.name", i)).String(); got != upstream {
|
|
t.Fatalf("upstream tool=%q want=%q: %s", got, upstream, request)
|
|
}
|
|
name := []string{"read_file", "external_read_file"}[i]
|
|
raw := []byte(fmt.Sprintf(`{"id":"interaction_1","model":%q,"status":"requires_action","steps":[{"type":"function_call","id":"call_1","name":%q,"arguments":{}}]}`, model, upstream))
|
|
var param any
|
|
out := sdktranslator.TranslateNonStream(context.Background(), sdktranslator.FormatInteractions, format, model, original, request, raw, ¶m)
|
|
path := "choices.0.message.tool_calls.0.function.name"
|
|
if format == sdktranslator.FormatOpenAIResponse {
|
|
path = "output.0.name"
|
|
}
|
|
if got := gjson.GetBytes(out, path).String(); got != name {
|
|
t.Errorf("nonstream tool=%q want=%q: %s", got, name, out)
|
|
}
|
|
param = nil
|
|
events := []string{
|
|
fmt.Sprintf(`{"event_type":"interaction.created","interaction":{"id":"interaction_1","model":%q}}`, model),
|
|
fmt.Sprintf(`{"event_type":"step.start","index":0,"step":{"type":"function_call","id":"call_1","name":%q,"arguments":{}}}`, upstream),
|
|
`{"event_type":"step.stop","index":0}`,
|
|
`{"event_type":"interaction.completed","interaction":{"id":"interaction_1","status":"requires_action"}}`,
|
|
}
|
|
found := false
|
|
for _, event := range events {
|
|
for _, chunk := range sdktranslator.TranslateStream(context.Background(), sdktranslator.FormatInteractions, format, model, original, request, []byte("data: "+event), ¶m) {
|
|
for _, line := range strings.Split(string(chunk), "\n") {
|
|
line = strings.TrimSpace(strings.TrimPrefix(line, "data:"))
|
|
path := "choices.0.delta.tool_calls.0.function.name"
|
|
if format == sdktranslator.FormatOpenAIResponse {
|
|
path = "item.name"
|
|
}
|
|
if got := gjson.Get(line, path); got.Exists() {
|
|
found = true
|
|
if got.String() != name {
|
|
t.Errorf("stream tool=%q want=%q: %s", got.String(), name, line)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("missing streamed tool name")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|