111 lines
3.9 KiB
Go
111 lines
3.9 KiB
Go
package executor
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
|
"github.com/router-for-me/CLIProxyAPI/v7/internal/runtime/executor/helps"
|
|
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
|
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
|
)
|
|
|
|
var (
|
|
xaiDataTag = []byte("data:")
|
|
xaiEventTag = []byte("event:")
|
|
)
|
|
|
|
const (
|
|
xaiImageHandlerType = "openai-image"
|
|
xaiVideoHandlerType = "openai-video"
|
|
xaiCustomToolType = "custom"
|
|
xaiFunctionToolType = "function"
|
|
xaiImageGenerationToolType = "image_generation"
|
|
xaiNamespaceToolType = "namespace"
|
|
xaiToolSearchType = "tool_search"
|
|
xaiWebSearchToolType = "web_search"
|
|
xaiXSearchToolType = "x_search"
|
|
// Codex Desktop injects codex_app.automation_update with a large oneOf+$ref
|
|
// schema. xAI's free/build Responses path accepts the HTTP request but never
|
|
// emits SSE when that schema is present, so Desktop hangs on "thinking".
|
|
xaiCodexAppNamespaceName = "codex_app"
|
|
xaiAutomationUpdateToolName = "automation_update"
|
|
// Permissive placeholder schema: keeps the tool callable without the hang.
|
|
xaiSafeFunctionParameters = `{"type":"object","properties":{},"additionalProperties":true}`
|
|
xaiImagesGenerationsPath = "/images/generations"
|
|
xaiImagesEditsPath = "/images/edits"
|
|
xaiDefaultImageEndpointPath = xaiImagesGenerationsPath
|
|
xaiVideosGenerationsPath = "/videos/generations"
|
|
xaiVideosEditsPath = "/videos/edits"
|
|
xaiVideosExtensionsPath = "/videos/extensions"
|
|
xaiVideosPath = "/videos"
|
|
xaiIdempotencyKeyMetaKey = "idempotency_key"
|
|
xaiComposerModelPrefix = "grok-composer-"
|
|
xaiTokenAuthHeader = "X-XAI-Token-Auth"
|
|
xaiTokenAuthValue = "xai-grok-cli"
|
|
xaiClientVersionHeader = "x-grok-client-version"
|
|
// Keep in sync with the current Grok CLI client version that chat-proxy expects.
|
|
xaiClientVersionValue = "0.2.120"
|
|
xaiClientIdentifierHeader = "x-grok-client-identifier"
|
|
xaiClientIdentifierValue = "grok-shell"
|
|
xaiAuthenticateResponseHeader = "x-authenticateresponse"
|
|
xaiAuthenticateResponseValue = "authenticate-response"
|
|
// xaiUsingAPIAttr enables the official API path for non-media HTTP chat.
|
|
xaiUsingAPIAttr = "using_api"
|
|
)
|
|
|
|
// xaiXSearchToolJSON is the native X Search tool injected when enabled by config.
|
|
// Internal subtool traces are still filtered downstream when this tool is present.
|
|
var xaiXSearchToolJSON = []byte(`{"type":"x_search"}`)
|
|
|
|
// XAIExecutor is a stateless executor for xAI Grok's Responses API.
|
|
type XAIExecutor struct {
|
|
cfg *config.Config
|
|
}
|
|
|
|
// NewXAIExecutor creates a new xAI executor.
|
|
func NewXAIExecutor(cfg *config.Config) *XAIExecutor {
|
|
return &XAIExecutor{cfg: cfg}
|
|
}
|
|
|
|
// Identifier returns the provider identifier.
|
|
func (e *XAIExecutor) Identifier() string {
|
|
return "xai"
|
|
}
|
|
|
|
// PrepareRequest injects xAI credentials into the outgoing HTTP request.
|
|
func (e *XAIExecutor) PrepareRequest(req *http.Request, auth *cliproxyauth.Auth) error {
|
|
if req == nil {
|
|
return nil
|
|
}
|
|
token, _ := xaiCreds(auth)
|
|
if strings.TrimSpace(token) != "" {
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
} else {
|
|
req.Header.Del("Authorization")
|
|
}
|
|
var attrs map[string]string
|
|
if auth != nil {
|
|
attrs = auth.Attributes
|
|
}
|
|
util.ApplyCustomHeadersFromAttrs(req, attrs)
|
|
return nil
|
|
}
|
|
|
|
// HttpRequest injects xAI credentials into the request and executes it.
|
|
func (e *XAIExecutor) HttpRequest(ctx context.Context, auth *cliproxyauth.Auth, req *http.Request) (*http.Response, error) {
|
|
if req == nil {
|
|
return nil, fmt.Errorf("xai executor: request is nil")
|
|
}
|
|
if ctx == nil {
|
|
ctx = req.Context()
|
|
}
|
|
httpReq := req.WithContext(ctx)
|
|
if errPrepare := e.PrepareRequest(httpReq, auth); errPrepare != nil {
|
|
return nil, errPrepare
|
|
}
|
|
httpClient := helps.NewProxyAwareHTTPClient(ctx, e.cfg, auth, 0)
|
|
return httpClient.Do(httpReq)
|
|
}
|