Files
CLIProxyAPI/internal/runtime/executor/xai_client_version_test.go
ggbdpq b467a83c0f fix(xai): bump pinned grok client version to 1.0.44 for chat-proxy
xAI raised the minimum accepted x-grok-client-version on
cli-chat-proxy.grok.com, so every xAI OAuth chat request through the CLI
chat-proxy failed with HTTP 426 ("Your Grok CLI version (0.2.120) is
outdated"). /v1/models is not gated, so discovery looked healthy while
inference was dead.

Bump the pin to the current stable Grok CLI version (1.0.44) and record
the observed server floor in the constant's comment. Both
x-grok-client-version and the derived User-Agent ship from this one
constant.

Regression tests lock three things: the pinned version meets the known
server floor (red against 0.2.120), the two identity headers stay
derived from the same constant, and the per-auth `header:*` custom
header override still wins over the pin - the documented operator
escape hatch when xAI ratchets the floor again.

Closes #6249

Generated-by: GLM-5.3-Flash (ZCode)
2026-10-01 06:14:37 +08:00

77 lines
2.3 KiB
Go

package executor
import (
"net/http"
"strconv"
"strings"
"testing"
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v8/sdk/cliproxy/auth"
)
// xaiVersionAtLeast compares dotted numeric version strings.
func xaiVersionAtLeast(got, floor string) bool {
gotParts := strings.Split(got, ".")
floorParts := strings.Split(floor, ".")
for i := 0; i < len(gotParts) || i < len(floorParts); i++ {
var g, f int
if i < len(gotParts) {
g, _ = strconv.Atoi(gotParts[i])
}
if i < len(floorParts) {
f, _ = strconv.Atoi(floorParts[i])
}
if g != f {
return g > f
}
}
return true
}
func TestXAIChatProxyClientVersionMeetsServerFloor(t *testing.T) {
// cli-chat-proxy.grok.com started rejecting client versions older than
// 1.0.13 with HTTP 426 ("Your Grok CLI version is outdated"); see #6249.
const serverFloor = "1.0.13"
if !xaiVersionAtLeast(xaiClientVersionValue, serverFloor) {
t.Fatalf("xaiClientVersionValue = %q, cli-chat-proxy rejects clients older than %s with 426", xaiClientVersionValue, serverFloor)
}
}
func TestXAIChatProxyIdentityHeadersDeriveFromOneConstant(t *testing.T) {
auth := &cliproxyauth.Auth{
Provider: "xai",
Attributes: map[string]string{"auth_kind": "oauth"},
}
req, err := http.NewRequest(http.MethodPost, xaiChatBaseURL(auth)+"/chat/completions", nil)
if err != nil {
t.Fatal(err)
}
applyXAIChatHeaders(req, auth, "token", false, "")
if got := req.Header.Get(xaiClientVersionHeader); got != xaiClientVersionValue {
t.Fatalf("%s = %q, want %q", xaiClientVersionHeader, got, xaiClientVersionValue)
}
if got, want := req.Header.Get("User-Agent"), "xai-grok-workspace/"+xaiClientVersionValue; got != want {
t.Fatalf("User-Agent = %q, want %q (must derive from xaiClientVersionValue)", got, want)
}
}
func TestXAIChatProxyCustomHeadersOverridePinnedVersion(t *testing.T) {
auth := &cliproxyauth.Auth{
Provider: "xai",
Attributes: map[string]string{
"auth_kind": "oauth",
"header:x-grok-client-version": "9.9.9",
},
}
req, err := http.NewRequest(http.MethodPost, xaiChatBaseURL(auth)+"/chat/completions", nil)
if err != nil {
t.Fatal(err)
}
applyXAIChatHeaders(req, auth, "token", false, "")
if got := req.Header.Get(xaiClientVersionHeader); got != "9.9.9" {
t.Fatalf("%s = %q, want the per-auth custom header to override the pin", xaiClientVersionHeader, got)
}
}