feat(thinking): add xAI provider support with reasoning.effort implementation

- Implemented `xAI` provider for thinking configurations with support for reasoning.effort levels.
- Registered `xAI` in available providers and updated relevant APIs for compatibility.
- Added unit tests for `xAI` provider functionality, including fallback logic for unsupported levels.
- Integrated `xAI` with executor handling and ensured conformance with OpenAI-compatible standards.
This commit is contained in:
Luis Pater
2026-05-19 03:09:53 +08:00
parent ad98c9549a
commit bac006e72b
9 changed files with 127 additions and 6 deletions

View File

@@ -8,4 +8,5 @@ import (
_ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/geminicli"
_ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/kimi"
_ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/openai"
_ "github.com/router-for-me/CLIProxyAPI/v7/internal/thinking/provider/xai"
)

View File

@@ -487,7 +487,7 @@ func (e *XAIExecutor) prepareResponsesRequest(ctx context.Context, req cliproxye
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), stream)
var err error
body, err = thinking.ApplyThinking(body, req.Model, from.String(), to.String(), e.Identifier())
body, err = thinking.ApplyThinking(body, req.Model, from.String(), e.Identifier(), e.Identifier())
if err != nil {
return nil, err
}

View File

@@ -196,6 +196,48 @@ func TestXAIExecutorOmitsUnsupportedReasoningEffort(t *testing.T) {
}
}
func TestXAIExecutorAppliesThinkingSuffix(t *testing.T) {
var gotBody []byte
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var errRead error
gotBody, errRead = io.ReadAll(r.Body)
if errRead != nil {
t.Fatalf("read body: %v", errRead)
}
w.Header().Set("Content-Type", "text/event-stream")
_, _ = w.Write([]byte("data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_1\",\"object\":\"response\",\"created_at\":0,\"status\":\"completed\",\"model\":\"grok-4.3\",\"output\":[{\"type\":\"message\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"ok\"}]}]}}\n\n"))
}))
defer server.Close()
exec := NewXAIExecutor(&config.Config{})
auth := &cliproxyauth.Auth{
Provider: "xai",
Attributes: map[string]string{
"base_url": server.URL,
"auth_kind": "oauth",
},
Metadata: map[string]any{"access_token": "xai-token"},
}
_, err := exec.Execute(context.Background(), auth, cliproxyexecutor.Request{
Model: "grok-4.3(low)",
Payload: []byte(`{"model":"grok-4.3","input":"hello"}`),
}, cliproxyexecutor.Options{
SourceFormat: sdktranslator.FormatOpenAIResponse,
Stream: false,
})
if err != nil {
t.Fatalf("Execute() error = %v", err)
}
if got := gjson.GetBytes(gotBody, "model").String(); got != "grok-4.3" {
t.Fatalf("model = %q, want grok-4.3; body=%s", got, string(gotBody))
}
if got := gjson.GetBytes(gotBody, "reasoning.effort").String(); got != "low" {
t.Fatalf("reasoning.effort = %q, want low; body=%s", got, string(gotBody))
}
}
func TestXAIExecutorExecuteStreamFiltersToolSearchTool(t *testing.T) {
var gotBody []byte
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {