// Package executor provides runtime execution capabilities for various AI service providers.// This file implements an xAI executor that uses the Responses API WebSocket transport.package executor import ( "bytes" "context" "encoding/json" "fmt" "net/http" "net/url" "strconv" "strings" "sync" "time" "github.com/gorilla/websocket" xaiauth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth/xai" "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" cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "github.com/tidwall/sjson") // XAIWebsocketsExecutor executes xAI Responses requests using a WebSocket transport.type XAIWebsocketsExecutor struct { *XAIExecutor store *codexWebsocketSessionStore idStore *xaiWebsocketIDStateStore} var globalXAIWebsocketSessionStore = &codexWebsocketSessionStore{ sessions: make(map[string]*codexWebsocketSession),} var globalXAIWebsocketIDStates = &xaiWebsocketIDStateStore{ sessions: make(map[string]*xaiWebsocketIDState),} type xaiWebsocketIDStateStore struct { mu sync.Mutex sessions map[string]*xaiWebsocketIDState} type xaiWebsocketIDState struct { requestMu sync.Mutex mu sync.Mutex downstreamToUpstream map[string]string sequence int transcriptInput []json.RawMessage replayCompactedTranscriptOnReset bool} type xaiWebsocketRequestIDMapper struct { state *xaiWebsocketIDState downstreamPreviousID string upstreamPreviousID string upstreamResponseID string downstreamResponseID string replayedCompactedTranscript bool} func NewXAIWebsocketsExecutor(cfg *config.Config) *XAIWebsocketsExecutor { return &XAIWebsocketsExecutor{ XAIExecutor: NewXAIExecutor(cfg), store: globalXAIWebsocketSessionStore, idStore: globalXAIWebsocketIDStates, }} func getXAIWebsocketIDState(store *xaiWebsocketIDStateStore, sessionID string) *xaiWebsocketIDState { sessionID = strings.TrimSpace(sessionID) if sessionID == "" || store == nil { return nil } store.mu.Lock() defer store.mu.Unlock() if store.sessions == nil { store.sessions = make(map[string]*xaiWebsocketIDState) } if state := store.sessions[sessionID]; state != nil { return state } state := &xaiWebsocketIDState{ downstreamToUpstream: make(map[string]string), } store.sessions[sessionID] = state return state} func deleteXAIWebsocketIDState(store *xaiWebsocketIDStateStore, sessionID string) { sessionID = strings.TrimSpace(sessionID) if sessionID == "" || store == nil { return } store.mu.Lock() delete(store.sessions, sessionID) store.mu.Unlock()} func newXAIWebsocketRequestIDMapper(store *xaiWebsocketIDStateStore, sessionID string, downstreamRequest []byte) *xaiWebsocketRequestIDMapper { state := getXAIWebsocketIDState(store, sessionID) if state == nil { return nil } downstreamPreviousID := strings.TrimSpace(gjson.GetBytes(downstreamRequest, "previous_response_id").String()) upstreamPreviousID := downstreamPreviousID if downstreamPreviousID != "" { upstreamPreviousID = state.upstreamIDForDownstream(downstreamPreviousID) } return &xaiWebsocketRequestIDMapper{ state: state, downstreamPreviousID: downstreamPreviousID, upstreamPreviousID: upstreamPreviousID, }} func (s *xaiWebsocketIDState) upstreamIDForDownstream(downstreamID string) string { downstreamID = strings.TrimSpace(downstreamID) if s == nil || downstreamID == "" { return downstreamID } s.mu.Lock() defer s.mu.Unlock() if upstreamID, ok := s.downstreamToUpstream[downstreamID]; ok { return strings.TrimSpace(upstreamID) } return downstreamID} func (s *xaiWebsocketIDState) mapDownstreamToUpstream(downstreamID string, upstreamID string) { downstreamID = strings.TrimSpace(downstreamID) if s == nil || downstreamID == "" { return } s.mu.Lock() if s.downstreamToUpstream == nil { s.downstreamToUpstream = make(map[string]string) } s.downstreamToUpstream[downstreamID] = strings.TrimSpace(upstreamID) s.mu.Unlock()} func (s *xaiWebsocketIDState) snapshotTranscriptInput() []byte { if s == nil { return nil } s.mu.Lock() defer s.mu.Unlock() if len(s.transcriptInput) == 0 { return nil } return xaiMarshalRawMessages(s.transcriptInput)} func (s *xaiWebsocketIDState) prependTranscriptInput(payload []byte) []byte { if s == nil || len(payload) == 0 { return payload } s.mu.Lock() prefix := make([]json.RawMessage, 0, len(s.transcriptInput)) for _, item := range s.transcriptInput { prefix = append(prefix, bytes.Clone(item)) } s.mu.Unlock() if len(prefix) == 0 { return payload } current := xaiJSONRawMessages(gjson.GetBytes(payload, "input")) merged := append(prefix, current...) out, errSet := sjson.SetRawBytes(payload, "input", xaiMarshalRawMessages(merged)) if errSet != nil { return payload } return out} func (s *xaiWebsocketIDState) recordTranscriptTurn(requestPayload []byte, completedPayload []byte, reset bool) { if s == nil || len(requestPayload) == 0 || len(completedPayload) == 0 { return } inputItems := xaiJSONRawMessages(gjson.GetBytes(requestPayload, "input")) outputItems := xaiJSONRawMessages(gjson.GetBytes(completedPayload, "response.output")) s.mu.Lock() defer s.mu.Unlock() if reset { s.transcriptInput = nil s.replayCompactedTranscriptOnReset = false } if len(inputItems) == 0 && len(outputItems) == 0 { return } s.transcriptInput = append(s.transcriptInput, inputItems...) s.transcriptInput = append(s.transcriptInput, outputItems...)} func (s *xaiWebsocketIDState) replaceTranscriptWithItems(items ...[]byte) { if s == nil { return } next := make([]json.RawMessage, 0, len(items)) for _, item := range items { item = bytes.TrimSpace(item) if len(item) == 0 || !json.Valid(item) { continue } next = append(next, bytes.Clone(item)) } s.mu.Lock() s.transcriptInput = next s.replayCompactedTranscriptOnReset = len(next) > 0 s.mu.Unlock()} func (s *xaiWebsocketIDState) prependCompactedTranscriptOnReset(payload []byte) ([]byte, bool) { if s == nil || len(payload) == 0 { return payload, false } s.mu.Lock() if !s.replayCompactedTranscriptOnReset || len(s.transcriptInput) == 0 { s.mu.Unlock() return payload, false } prefix := make([]json.RawMessage, 0, len(s.transcriptInput)) for _, item := range s.transcriptInput { prefix = append(prefix, bytes.Clone(item)) } s.mu.Unlock() current := xaiJSONRawMessages(gjson.GetBytes(payload, "input")) merged := append(prefix, current...) out, errSet := sjson.SetRawBytes(payload, "input", xaiMarshalRawMessages(merged)) if errSet != nil { return payload, false } return out, true} func xaiJSONRawMessages(result gjson.Result) []json.RawMessage { if !result.Exists() || !result.IsArray() { return nil } items := result.Array() out := make([]json.RawMessage, 0, len(items)) for _, item := range items { raw := bytes.TrimSpace([]byte(item.Raw)) if len(raw) == 0 || !json.Valid(raw) { continue } out = append(out, bytes.Clone(raw)) } return out} func xaiMarshalRawMessages(items []json.RawMessage) []byte { var buf bytes.Buffer buf.WriteByte('[') for i, item := range items { if i > 0 { buf.WriteByte(',') } buf.Write(bytes.TrimSpace(item)) } buf.WriteByte(']') return buf.Bytes()} func (m *xaiWebsocketRequestIDMapper) upstreamRequestPayload(payload []byte) []byte { if m == nil || len(payload) == 0 { return payload } if m.downstreamPreviousID == m.upstreamPreviousID { requestType := strings.TrimSpace(gjson.GetBytes(payload, "type").String()) if m.downstreamPreviousID == "" && requestType == "response.append" && m.state != nil { out, replayed := m.state.prependCompactedTranscriptOnReset(payload) m.replayedCompactedTranscript = replayed return out } return payload } if m.upstreamPreviousID == "" { out, errDelete := sjson.DeleteBytes(payload, "previous_response_id") if errDelete == nil { if m.downstreamPreviousID != "" && m.state != nil { out = m.state.prependTranscriptInput(out) m.replayedCompactedTranscript = true } return out } return payload } out, errSet := sjson.SetBytes(payload, "previous_response_id", m.upstreamPreviousID) if errSet != nil { return payload } return out} func (m *xaiWebsocketRequestIDMapper) downstreamResponsePayload(payload []byte) []byte { if m == nil || len(payload) == 0 { return payload } upstreamResponseID := strings.TrimSpace(gjson.GetBytes(payload, "response.id").String()) downstreamResponseID := m.downstreamIDForUpstreamResponse(upstreamResponseID) if downstreamResponseID == "" { return payload } return rewriteXAIWebsocketDownstreamIDs(payload, m.upstreamResponseID, downstreamResponseID, m.upstreamPreviousID, m.downstreamPreviousID)} func (m *xaiWebsocketRequestIDMapper) downstreamIDForUpstreamResponse(upstreamResponseID string) string { upstreamResponseID = strings.TrimSpace(upstreamResponseID) if m == nil || m.state == nil { return upstreamResponseID } if m.upstreamResponseID != "" { return m.downstreamResponseID } if upstreamResponseID == "" { return "" } m.state.mu.Lock() defer m.state.mu.Unlock() m.upstreamResponseID = upstreamResponseID m.downstreamResponseID = upstreamResponseID if m.state.downstreamToUpstream == nil { m.state.downstreamToUpstream = make(map[string]string) } _, upstreamResponseIDSeen := m.state.downstreamToUpstream[upstreamResponseID] if (m.downstreamPreviousID != "" && m.upstreamPreviousID != "" && upstreamResponseID == m.upstreamPreviousID) || upstreamResponseIDSeen { m.state.sequence++ m.downstreamResponseID = fmt.Sprintf("%s-xai-%d", upstreamResponseID, m.state.sequence) } m.state.downstreamToUpstream[upstreamResponseID] = upstreamResponseID m.state.downstreamToUpstream[m.downstreamResponseID] = upstreamResponseID return m.downstreamResponseID} func rewriteXAIWebsocketDownstreamIDs(payload []byte, upstreamResponseID string, downstreamResponseID string, upstreamPreviousID string, downstreamPreviousID string) []byte { upstreamResponseID = strings.TrimSpace(upstreamResponseID) downstreamResponseID = strings.TrimSpace(downstreamResponseID) upstreamPreviousID = strings.TrimSpace(upstreamPreviousID) downstreamPreviousID = strings.TrimSpace(downstreamPreviousID) if len(payload) == 0 || (upstreamResponseID == downstreamResponseID && upstreamPreviousID == downstreamPreviousID) { return payload } var value any decoder := json.NewDecoder(bytes.NewReader(payload)) decoder.UseNumber() if errDecode := decoder.Decode(&value); errDecode != nil { return payload } if !rewriteXAIWebsocketDownstreamIDValue(value, upstreamResponseID, downstreamResponseID, upstreamPreviousID, downstreamPreviousID, "") { return payload } out, errMarshal := json.Marshal(value) if errMarshal != nil { return payload } return out} func rewriteXAIWebsocketDownstreamIDValue(value any, upstreamResponseID string, downstreamResponseID string, upstreamPreviousID string, downstreamPreviousID string, key string) bool { switch typed := value.(type) { case map[string]any: changed := false for childKey, childValue := range typed { if childString, ok := childValue.(string); ok { replaced := rewriteXAIWebsocketDownstreamIDString(childString, childKey, upstreamResponseID, downstreamResponseID, upstreamPreviousID, downstreamPreviousID) if replaced != childString { typed[childKey] = replaced changed = true } continue } if rewriteXAIWebsocketDownstreamIDValue(childValue, upstreamResponseID, downstreamResponseID, upstreamPreviousID, downstreamPreviousID, childKey) { changed = true } } return changed case []any: changed := false for i := range typed { if rewriteXAIWebsocketDownstreamIDValue(typed[i], upstreamResponseID, downstreamResponseID, upstreamPreviousID, downstreamPreviousID, key) { changed = true } } return changed default: return false }} func rewriteXAIWebsocketDownstreamIDString(value string, key string, upstreamResponseID string, downstreamResponseID string, upstreamPreviousID string, downstreamPreviousID string) string { switch key { case "id", "item_id": if upstreamResponseID != "" && downstreamResponseID != "" && downstreamResponseID != upstreamResponseID && strings.Contains(value, upstreamResponseID) { return strings.ReplaceAll(value, upstreamResponseID, downstreamResponseID) } case "previous_response_id": if upstreamPreviousID != "" && downstreamPreviousID != "" && value == upstreamPreviousID { return downstreamPreviousID } } return value} func (e *XAIWebsocketsExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) { if e == nil || e.XAIExecutor == nil { return cliproxyexecutor.Response{}, fmt.Errorf("xai websockets executor: executor is nil") } return e.XAIExecutor.Execute(ctx, auth, req, opts)} func (e *XAIWebsocketsExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (_ *cliproxyexecutor.StreamResult, err error) { if e == nil || e.XAIExecutor == nil { return nil, fmt.Errorf("xai websockets executor: executor is nil") } if ctx == nil { ctx = context.Background() } if opts.Alt == "responses/compact" { return nil, statusErr{code: http.StatusBadRequest, msg: "streaming not supported for /responses/compact"} } executionSessionID := executionSessionIDFromOptions(opts) stateSessionID := xaiExecutionSessionID(req, opts) if stateSessionID == "" { stateSessionID = executionSessionID } state := getXAIWebsocketIDState(e.idStore, stateSessionID) stateRequestLocked := false stateRequestLockTransferred := false if executionSessionID == "" && state != nil { state.requestMu.Lock() stateRequestLocked = true } defer func() { if stateRequestLocked && !stateRequestLockTransferred { state.requestMu.Unlock() } }() if xaiInputHasItemType(req.Payload, "compaction_trigger") { if cliproxyexecutor.RequiredUpstreamWebsocket(ctx) { return nil, cliproxyexecutor.NewUpstreamWebsocketReplayRequiredError() } if executionSessionID != "" { sess := e.getOrCreateSession(executionSessionID) if sess != nil { sess.reqMu.Lock() defer sess.reqMu.Unlock() } } idMapper := newXAIWebsocketRequestIDMapper(e.idStore, stateSessionID, req.Payload) return e.executeCompactionTriggerFromWebsocketContext(ctx, auth, req, opts, idMapper) } // Keep websocket on the official API base URL (or an explicit non-default // base_url). Do not reuse xaiChatBaseURL: cli-chat-proxy only accepts HTTP // POST and returns 405 for websocket upgrades. token, baseURL := xaiCreds(auth) if baseURL == "" { baseURL = xaiauth.DefaultAPIBaseURL } prepared, err := e.prepareResponsesWebsocketRequest(ctx, req, opts) if err != nil { return nil, err } reporter := helps.NewExecutorUsageReporter(ctx, e, prepared.baseModel, auth) defer reporter.TrackFailure(ctx, &err) httpURL := strings.TrimSuffix(baseURL, "/") + "/responses" wsURL, err := buildXAIResponsesWebsocketURL(httpURL) if err != nil { return nil, err } var authID, authLabel, authType, authValue string if auth != nil { authID = auth.ID authLabel = auth.Label authType, authValue = auth.AccountInfo() } var sess *codexWebsocketSession isEphemeralSession := false if executionSessionID != "" { sess = e.getOrCreateSession(executionSessionID) if sess != nil { sess.reqMu.Lock() }Lines 1–500 of 1807. Load more
// Package executor provides runtime execution capabilities for various AI service providers. // This file implements an xAI executor that uses the Responses API WebSocket transport. package executor import ( "bytes" "context" "encoding/json" "fmt" "net/http" "net/url" "strconv" "strings" "sync" "time" "github.com/gorilla/websocket" xaiauth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth/xai" "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" cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) // XAIWebsocketsExecutor executes xAI Responses requests using a WebSocket transport. type XAIWebsocketsExecutor struct { *XAIExecutor store *codexWebsocketSessionStore idStore *xaiWebsocketIDStateStore } var globalXAIWebsocketSessionStore = &codexWebsocketSessionStore{ sessions: make(map[string]*codexWebsocketSession), } var globalXAIWebsocketIDStates = &xaiWebsocketIDStateStore{ sessions: make(map[string]*xaiWebsocketIDState), } type xaiWebsocketIDStateStore struct { mu sync.Mutex sessions map[string]*xaiWebsocketIDState } type xaiWebsocketIDState struct { requestMu sync.Mutex mu sync.Mutex downstreamToUpstream map[string]string sequence int transcriptInput []json.RawMessage replayCompactedTranscriptOnReset bool } type xaiWebsocketRequestIDMapper struct { state *xaiWebsocketIDState downstreamPreviousID string upstreamPreviousID string upstreamResponseID string downstreamResponseID string replayedCompactedTranscript bool } func NewXAIWebsocketsExecutor(cfg *config.Config) *XAIWebsocketsExecutor { return &XAIWebsocketsExecutor{ XAIExecutor: NewXAIExecutor(cfg), store: globalXAIWebsocketSessionStore, idStore: globalXAIWebsocketIDStates, } } func getXAIWebsocketIDState(store *xaiWebsocketIDStateStore, sessionID string) *xaiWebsocketIDState { sessionID = strings.TrimSpace(sessionID) if sessionID == "" || store == nil { return nil } store.mu.Lock() defer store.mu.Unlock() if store.sessions == nil { store.sessions = make(map[string]*xaiWebsocketIDState) } if state := store.sessions[sessionID]; state != nil { return state } state := &xaiWebsocketIDState{ downstreamToUpstream: make(map[string]string), } store.sessions[sessionID] = state return state } func deleteXAIWebsocketIDState(store *xaiWebsocketIDStateStore, sessionID string) { sessionID = strings.TrimSpace(sessionID) if sessionID == "" || store == nil { return } store.mu.Lock() delete(store.sessions, sessionID) store.mu.Unlock() } func newXAIWebsocketRequestIDMapper(store *xaiWebsocketIDStateStore, sessionID string, downstreamRequest []byte) *xaiWebsocketRequestIDMapper { state := getXAIWebsocketIDState(store, sessionID) if state == nil { return nil } downstreamPreviousID := strings.TrimSpace(gjson.GetBytes(downstreamRequest, "previous_response_id").String()) upstreamPreviousID := downstreamPreviousID if downstreamPreviousID != "" { upstreamPreviousID = state.upstreamIDForDownstream(downstreamPreviousID) } return &xaiWebsocketRequestIDMapper{ state: state, downstreamPreviousID: downstreamPreviousID, upstreamPreviousID: upstreamPreviousID, } } func (s *xaiWebsocketIDState) upstreamIDForDownstream(downstreamID string) string { downstreamID = strings.TrimSpace(downstreamID) if s == nil || downstreamID == "" { return downstreamID } s.mu.Lock() defer s.mu.Unlock() if upstreamID, ok := s.downstreamToUpstream[downstreamID]; ok { return strings.TrimSpace(upstreamID) } return downstreamID } func (s *xaiWebsocketIDState) mapDownstreamToUpstream(downstreamID string, upstreamID string) { downstreamID = strings.TrimSpace(downstreamID) if s == nil || downstreamID == "" { return } s.mu.Lock() if s.downstreamToUpstream == nil { s.downstreamToUpstream = make(map[string]string) } s.downstreamToUpstream[downstreamID] = strings.TrimSpace(upstreamID) s.mu.Unlock() } func (s *xaiWebsocketIDState) snapshotTranscriptInput() []byte { if s == nil { return nil } s.mu.Lock() defer s.mu.Unlock() if len(s.transcriptInput) == 0 { return nil } return xaiMarshalRawMessages(s.transcriptInput) } func (s *xaiWebsocketIDState) prependTranscriptInput(payload []byte) []byte { if s == nil || len(payload) == 0 { return payload } s.mu.Lock() prefix := make([]json.RawMessage, 0, len(s.transcriptInput)) for _, item := range s.transcriptInput { prefix = append(prefix, bytes.Clone(item)) } s.mu.Unlock() if len(prefix) == 0 { return payload } current := xaiJSONRawMessages(gjson.GetBytes(payload, "input")) merged := append(prefix, current...) out, errSet := sjson.SetRawBytes(payload, "input", xaiMarshalRawMessages(merged)) if errSet != nil { return payload } return out } func (s *xaiWebsocketIDState) recordTranscriptTurn(requestPayload []byte, completedPayload []byte, reset bool) { if s == nil || len(requestPayload) == 0 || len(completedPayload) == 0 { return } inputItems := xaiJSONRawMessages(gjson.GetBytes(requestPayload, "input")) outputItems := xaiJSONRawMessages(gjson.GetBytes(completedPayload, "response.output")) s.mu.Lock() defer s.mu.Unlock() if reset { s.transcriptInput = nil s.replayCompactedTranscriptOnReset = false } if len(inputItems) == 0 && len(outputItems) == 0 { return } s.transcriptInput = append(s.transcriptInput, inputItems...) s.transcriptInput = append(s.transcriptInput, outputItems...) } func (s *xaiWebsocketIDState) replaceTranscriptWithItems(items ...[]byte) { if s == nil { return } next := make([]json.RawMessage, 0, len(items)) for _, item := range items { item = bytes.TrimSpace(item) if len(item) == 0 || !json.Valid(item) { continue } next = append(next, bytes.Clone(item)) } s.mu.Lock() s.transcriptInput = next s.replayCompactedTranscriptOnReset = len(next) > 0 s.mu.Unlock() } func (s *xaiWebsocketIDState) prependCompactedTranscriptOnReset(payload []byte) ([]byte, bool) { if s == nil || len(payload) == 0 { return payload, false } s.mu.Lock() if !s.replayCompactedTranscriptOnReset || len(s.transcriptInput) == 0 { s.mu.Unlock() return payload, false } prefix := make([]json.RawMessage, 0, len(s.transcriptInput)) for _, item := range s.transcriptInput { prefix = append(prefix, bytes.Clone(item)) } s.mu.Unlock() current := xaiJSONRawMessages(gjson.GetBytes(payload, "input")) merged := append(prefix, current...) out, errSet := sjson.SetRawBytes(payload, "input", xaiMarshalRawMessages(merged)) if errSet != nil { return payload, false } return out, true } func xaiJSONRawMessages(result gjson.Result) []json.RawMessage { if !result.Exists() || !result.IsArray() { return nil } items := result.Array() out := make([]json.RawMessage, 0, len(items)) for _, item := range items { raw := bytes.TrimSpace([]byte(item.Raw)) if len(raw) == 0 || !json.Valid(raw) { continue } out = append(out, bytes.Clone(raw)) } return out } func xaiMarshalRawMessages(items []json.RawMessage) []byte { var buf bytes.Buffer buf.WriteByte('[') for i, item := range items { if i > 0 { buf.WriteByte(',') } buf.Write(bytes.TrimSpace(item)) } buf.WriteByte(']') return buf.Bytes() } func (m *xaiWebsocketRequestIDMapper) upstreamRequestPayload(payload []byte) []byte { if m == nil || len(payload) == 0 { return payload } if m.downstreamPreviousID == m.upstreamPreviousID { requestType := strings.TrimSpace(gjson.GetBytes(payload, "type").String()) if m.downstreamPreviousID == "" && requestType == "response.append" && m.state != nil { out, replayed := m.state.prependCompactedTranscriptOnReset(payload) m.replayedCompactedTranscript = replayed return out } return payload } if m.upstreamPreviousID == "" { out, errDelete := sjson.DeleteBytes(payload, "previous_response_id") if errDelete == nil { if m.downstreamPreviousID != "" && m.state != nil { out = m.state.prependTranscriptInput(out) m.replayedCompactedTranscript = true } return out } return payload } out, errSet := sjson.SetBytes(payload, "previous_response_id", m.upstreamPreviousID) if errSet != nil { return payload } return out } func (m *xaiWebsocketRequestIDMapper) downstreamResponsePayload(payload []byte) []byte { if m == nil || len(payload) == 0 { return payload } upstreamResponseID := strings.TrimSpace(gjson.GetBytes(payload, "response.id").String()) downstreamResponseID := m.downstreamIDForUpstreamResponse(upstreamResponseID) if downstreamResponseID == "" { return payload } return rewriteXAIWebsocketDownstreamIDs(payload, m.upstreamResponseID, downstreamResponseID, m.upstreamPreviousID, m.downstreamPreviousID) } func (m *xaiWebsocketRequestIDMapper) downstreamIDForUpstreamResponse(upstreamResponseID string) string { upstreamResponseID = strings.TrimSpace(upstreamResponseID) if m == nil || m.state == nil { return upstreamResponseID } if m.upstreamResponseID != "" { return m.downstreamResponseID } if upstreamResponseID == "" { return "" } m.state.mu.Lock() defer m.state.mu.Unlock() m.upstreamResponseID = upstreamResponseID m.downstreamResponseID = upstreamResponseID if m.state.downstreamToUpstream == nil { m.state.downstreamToUpstream = make(map[string]string) } _, upstreamResponseIDSeen := m.state.downstreamToUpstream[upstreamResponseID] if (m.downstreamPreviousID != "" && m.upstreamPreviousID != "" && upstreamResponseID == m.upstreamPreviousID) || upstreamResponseIDSeen { m.state.sequence++ m.downstreamResponseID = fmt.Sprintf("%s-xai-%d", upstreamResponseID, m.state.sequence) } m.state.downstreamToUpstream[upstreamResponseID] = upstreamResponseID m.state.downstreamToUpstream[m.downstreamResponseID] = upstreamResponseID return m.downstreamResponseID } func rewriteXAIWebsocketDownstreamIDs(payload []byte, upstreamResponseID string, downstreamResponseID string, upstreamPreviousID string, downstreamPreviousID string) []byte { upstreamResponseID = strings.TrimSpace(upstreamResponseID) downstreamResponseID = strings.TrimSpace(downstreamResponseID) upstreamPreviousID = strings.TrimSpace(upstreamPreviousID) downstreamPreviousID = strings.TrimSpace(downstreamPreviousID) if len(payload) == 0 || (upstreamResponseID == downstreamResponseID && upstreamPreviousID == downstreamPreviousID) { return payload } var value any decoder := json.NewDecoder(bytes.NewReader(payload)) decoder.UseNumber() if errDecode := decoder.Decode(&value); errDecode != nil { return payload } if !rewriteXAIWebsocketDownstreamIDValue(value, upstreamResponseID, downstreamResponseID, upstreamPreviousID, downstreamPreviousID, "") { return payload } out, errMarshal := json.Marshal(value) if errMarshal != nil { return payload } return out } func rewriteXAIWebsocketDownstreamIDValue(value any, upstreamResponseID string, downstreamResponseID string, upstreamPreviousID string, downstreamPreviousID string, key string) bool { switch typed := value.(type) { case map[string]any: changed := false for childKey, childValue := range typed { if childString, ok := childValue.(string); ok { replaced := rewriteXAIWebsocketDownstreamIDString(childString, childKey, upstreamResponseID, downstreamResponseID, upstreamPreviousID, downstreamPreviousID) if replaced != childString { typed[childKey] = replaced changed = true } continue } if rewriteXAIWebsocketDownstreamIDValue(childValue, upstreamResponseID, downstreamResponseID, upstreamPreviousID, downstreamPreviousID, childKey) { changed = true } } return changed case []any: changed := false for i := range typed { if rewriteXAIWebsocketDownstreamIDValue(typed[i], upstreamResponseID, downstreamResponseID, upstreamPreviousID, downstreamPreviousID, key) { changed = true } } return changed default: return false } } func rewriteXAIWebsocketDownstreamIDString(value string, key string, upstreamResponseID string, downstreamResponseID string, upstreamPreviousID string, downstreamPreviousID string) string { switch key { case "id", "item_id": if upstreamResponseID != "" && downstreamResponseID != "" && downstreamResponseID != upstreamResponseID && strings.Contains(value, upstreamResponseID) { return strings.ReplaceAll(value, upstreamResponseID, downstreamResponseID) } case "previous_response_id": if upstreamPreviousID != "" && downstreamPreviousID != "" && value == upstreamPreviousID { return downstreamPreviousID } } return value } func (e *XAIWebsocketsExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) { if e == nil || e.XAIExecutor == nil { return cliproxyexecutor.Response{}, fmt.Errorf("xai websockets executor: executor is nil") } return e.XAIExecutor.Execute(ctx, auth, req, opts) } func (e *XAIWebsocketsExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (_ *cliproxyexecutor.StreamResult, err error) { if e == nil || e.XAIExecutor == nil { return nil, fmt.Errorf("xai websockets executor: executor is nil") } if ctx == nil { ctx = context.Background() } if opts.Alt == "responses/compact" { return nil, statusErr{code: http.StatusBadRequest, msg: "streaming not supported for /responses/compact"} } executionSessionID := executionSessionIDFromOptions(opts) stateSessionID := xaiExecutionSessionID(req, opts) if stateSessionID == "" { stateSessionID = executionSessionID } state := getXAIWebsocketIDState(e.idStore, stateSessionID) stateRequestLocked := false stateRequestLockTransferred := false if executionSessionID == "" && state != nil { state.requestMu.Lock() stateRequestLocked = true } defer func() { if stateRequestLocked && !stateRequestLockTransferred { state.requestMu.Unlock() } }() if xaiInputHasItemType(req.Payload, "compaction_trigger") { if cliproxyexecutor.RequiredUpstreamWebsocket(ctx) { return nil, cliproxyexecutor.NewUpstreamWebsocketReplayRequiredError() } if executionSessionID != "" { sess := e.getOrCreateSession(executionSessionID) if sess != nil { sess.reqMu.Lock() defer sess.reqMu.Unlock() } } idMapper := newXAIWebsocketRequestIDMapper(e.idStore, stateSessionID, req.Payload) return e.executeCompactionTriggerFromWebsocketContext(ctx, auth, req, opts, idMapper) } // Keep websocket on the official API base URL (or an explicit non-default // base_url). Do not reuse xaiChatBaseURL: cli-chat-proxy only accepts HTTP // POST and returns 405 for websocket upgrades. token, baseURL := xaiCreds(auth) if baseURL == "" { baseURL = xaiauth.DefaultAPIBaseURL } prepared, err := e.prepareResponsesWebsocketRequest(ctx, req, opts) if err != nil { return nil, err } reporter := helps.NewExecutorUsageReporter(ctx, e, prepared.baseModel, auth) defer reporter.TrackFailure(ctx, &err) httpURL := strings.TrimSuffix(baseURL, "/") + "/responses" wsURL, err := buildXAIResponsesWebsocketURL(httpURL) if err != nil { return nil, err } var authID, authLabel, authType, authValue string if auth != nil { authID = auth.ID authLabel = auth.Label authType, authValue = auth.AccountInfo() } var sess *codexWebsocketSession isEphemeralSession := false if executionSessionID != "" { sess = e.getOrCreateSession(executionSessionID) if sess != nil { sess.reqMu.Lock() }