diff --git a/internal/runtime/executor/xai_executor.go b/internal/runtime/executor/xai_executor.go index db3e06f6..c7b9e502 100644 --- a/internal/runtime/executor/xai_executor.go +++ b/internal/runtime/executor/xai_executor.go @@ -131,6 +131,7 @@ func (e *XAIExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req token, _ := xaiCreds(auth) baseURL := xaiChatBaseURL(auth) + logXAIResolvedBaseURL(ctx, baseURL) prepared, err := e.prepareResponsesRequest(ctx, req, opts, true) if err != nil { @@ -220,6 +221,7 @@ func (e *XAIExecutor) executeCompact(ctx context.Context, auth *cliproxyauth.Aut func (e *XAIExecutor) executeCompactRequest(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (*xaiPreparedRequest, []byte, http.Header, error) { token, _ := xaiCreds(auth) baseURL := xaiChatBaseURL(auth) + logXAIResolvedBaseURL(ctx, baseURL) prepared, err := e.prepareResponsesRequestTo(ctx, req, opts, false, sdktranslator.FormatOpenAIResponse) if err != nil { @@ -470,6 +472,7 @@ func (e *XAIExecutor) executeImages(ctx context.Context, auth *cliproxyauth.Auth if baseURL == "" { baseURL = xaiauth.DefaultAPIBaseURL } + logXAIResolvedBaseURL(ctx, baseURL) if endpointPath == "" { endpointPath = xaiDefaultImageEndpointPath } @@ -515,6 +518,7 @@ func (e *XAIExecutor) executeVideos(ctx context.Context, auth *cliproxyauth.Auth if baseURL == "" { baseURL = xaiauth.DefaultAPIBaseURL } + logXAIResolvedBaseURL(ctx, baseURL) method := http.MethodPost endpointPath := xaiVideosGenerationsPath @@ -585,6 +589,7 @@ func (e *XAIExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Auth token, _ := xaiCreds(auth) baseURL := xaiChatBaseURL(auth) + logXAIResolvedBaseURL(ctx, baseURL) prepared, err := e.prepareResponsesRequest(ctx, req, opts, true) if err != nil { @@ -985,6 +990,23 @@ func xaiIsCLIChatProxyBaseURL(baseURL string) bool { return xaiNormalizeBaseURL(baseURL) == xaiNormalizeBaseURL(xaiauth.CLIChatProxyBaseURL) } +// xaiBaseURLSource classifies a resolved xAI base URL for logging. +func xaiBaseURLSource(baseURL string) string { + switch { + case xaiIsDefaultAPIBaseURL(baseURL): + return "DefaultAPIBaseURL" + case xaiIsCLIChatProxyBaseURL(baseURL): + return "CLIChatProxyBaseURL" + default: + return "custom" + } +} + +// logXAIResolvedBaseURL emits a console log for the resolved upstream base URL. +func logXAIResolvedBaseURL(ctx context.Context, baseURL string) { + helps.LogWithRequestID(ctx).Infof("xai: using base_url=%s source=%s", baseURL, xaiBaseURLSource(baseURL)) +} + func applyXAIHeaders(r *http.Request, auth *cliproxyauth.Auth, token string, stream bool, sessionID string) { applyXAIDefaultHeaders(r, token, stream, sessionID) applyXAICustomHeaders(r, auth) diff --git a/internal/runtime/executor/xai_executor_test.go b/internal/runtime/executor/xai_executor_test.go index c43e8b5d..2c84043d 100644 --- a/internal/runtime/executor/xai_executor_test.go +++ b/internal/runtime/executor/xai_executor_test.go @@ -1573,6 +1573,28 @@ func TestXAIExecutorReasoningReplayCacheReplaysFunctionCallForClaudeToolResult(t } } +func TestXAIBaseURLSource(t *testing.T) { + tests := []struct { + name string + baseURL string + want string + }{ + {name: "default api", baseURL: xaiauth.DefaultAPIBaseURL, want: "DefaultAPIBaseURL"}, + {name: "default api trailing slash", baseURL: xaiauth.DefaultAPIBaseURL + "/", want: "DefaultAPIBaseURL"}, + {name: "cli chat proxy", baseURL: xaiauth.CLIChatProxyBaseURL, want: "CLIChatProxyBaseURL"}, + {name: "cli chat proxy trailing slash", baseURL: xaiauth.CLIChatProxyBaseURL + "/", want: "CLIChatProxyBaseURL"}, + {name: "custom", baseURL: "https://gateway.example.com/v1", want: "custom"}, + {name: "empty treated as custom", baseURL: "", want: "custom"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xaiBaseURLSource(tt.baseURL); got != tt.want { + t.Fatalf("xaiBaseURLSource(%q) = %q, want %q", tt.baseURL, got, tt.want) + } + }) + } +} + func TestXAIChatBaseURL(t *testing.T) { tests := []struct { name string