Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
151
backend/internal/runtime/executor/codex_websockets_executor.go
Normal file
151
backend/internal/runtime/executor/codex_websockets_executor.go
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
// Package executor provides runtime execution capabilities for various AI service providers.
|
||||
// This file implements a Codex executor that uses the Responses API WebSocket transport.
|
||||
package executor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
// CodexWebsocketsExecutor executes Codex Responses requests using a WebSocket transport.
|
||||
//
|
||||
// It preserves the existing CodexExecutor HTTP implementation as a fallback for endpoints
|
||||
// not available over WebSocket (e.g. /responses/compact) and for websocket upgrade failures.
|
||||
type CodexWebsocketsExecutor struct {
|
||||
*CodexExecutor
|
||||
|
||||
store *codexWebsocketSessionStore
|
||||
}
|
||||
|
||||
func NewCodexWebsocketsExecutor(cfg *config.Config) *CodexWebsocketsExecutor {
|
||||
return &CodexWebsocketsExecutor{
|
||||
CodexExecutor: NewCodexExecutor(cfg),
|
||||
store: globalCodexWebsocketSessionStore,
|
||||
}
|
||||
}
|
||||
|
||||
// CodexAutoExecutor routes Codex requests to the websocket transport only when:
|
||||
// 1. The downstream transport is websocket, and
|
||||
// 2. The selected auth enables websockets.
|
||||
//
|
||||
// For non-websocket downstream requests, it always uses the legacy HTTP implementation.
|
||||
type CodexAutoExecutor struct {
|
||||
httpExec *CodexExecutor
|
||||
wsExec *CodexWebsocketsExecutor
|
||||
}
|
||||
|
||||
func NewCodexAutoExecutor(cfg *config.Config) *CodexAutoExecutor {
|
||||
return &CodexAutoExecutor{
|
||||
httpExec: NewCodexExecutor(cfg),
|
||||
wsExec: NewCodexWebsocketsExecutor(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *CodexAutoExecutor) Identifier() string { return "codex" }
|
||||
|
||||
func (e *CodexAutoExecutor) PrepareRequest(req *http.Request, auth *cliproxyauth.Auth) error {
|
||||
if e == nil || e.httpExec == nil {
|
||||
return nil
|
||||
}
|
||||
return e.httpExec.PrepareRequest(req, auth)
|
||||
}
|
||||
|
||||
func (e *CodexAutoExecutor) HttpRequest(ctx context.Context, auth *cliproxyauth.Auth, req *http.Request) (*http.Response, error) {
|
||||
if e == nil || e.httpExec == nil {
|
||||
return nil, fmt.Errorf("codex auto executor: http executor is nil")
|
||||
}
|
||||
return e.httpExec.HttpRequest(ctx, auth, req)
|
||||
}
|
||||
|
||||
func (e *CodexAutoExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) {
|
||||
if e == nil || e.httpExec == nil || e.wsExec == nil {
|
||||
return cliproxyexecutor.Response{}, fmt.Errorf("codex auto executor: executor is nil")
|
||||
}
|
||||
if cliproxyexecutor.DownstreamWebsocket(ctx) && codexWebsocketsEnabled(auth) {
|
||||
return e.wsExec.Execute(ctx, auth, req, opts)
|
||||
}
|
||||
if cliproxyexecutor.RequiredUpstreamWebsocket(ctx) {
|
||||
return cliproxyexecutor.Response{}, cliproxyexecutor.NewUpstreamWebsocketReplayRequiredError()
|
||||
}
|
||||
return e.httpExec.Execute(ctx, auth, req, opts)
|
||||
}
|
||||
|
||||
func (e *CodexAutoExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (*cliproxyexecutor.StreamResult, error) {
|
||||
if e == nil || e.httpExec == nil || e.wsExec == nil {
|
||||
return nil, fmt.Errorf("codex auto executor: executor is nil")
|
||||
}
|
||||
if cliproxyexecutor.DownstreamWebsocket(ctx) && codexWebsocketsEnabled(auth) {
|
||||
return e.wsExec.ExecuteStream(ctx, auth, req, opts)
|
||||
}
|
||||
if cliproxyexecutor.RequiredUpstreamWebsocket(ctx) {
|
||||
return nil, cliproxyexecutor.NewUpstreamWebsocketReplayRequiredError()
|
||||
}
|
||||
return e.httpExec.ExecuteStream(ctx, auth, req, opts)
|
||||
}
|
||||
|
||||
func (e *CodexAutoExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) (*cliproxyauth.Auth, error) {
|
||||
if e == nil || e.httpExec == nil {
|
||||
return nil, fmt.Errorf("codex auto executor: http executor is nil")
|
||||
}
|
||||
return e.httpExec.Refresh(ctx, auth)
|
||||
}
|
||||
|
||||
func (e *CodexAutoExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) {
|
||||
if e == nil || e.httpExec == nil {
|
||||
return cliproxyexecutor.Response{}, fmt.Errorf("codex auto executor: http executor is nil")
|
||||
}
|
||||
return e.httpExec.CountTokens(ctx, auth, req, opts)
|
||||
}
|
||||
|
||||
func (e *CodexAutoExecutor) CloseExecutionSession(sessionID string) {
|
||||
if e == nil || e.wsExec == nil {
|
||||
return
|
||||
}
|
||||
e.wsExec.CloseExecutionSession(sessionID)
|
||||
}
|
||||
|
||||
func (e *CodexAutoExecutor) UpstreamDisconnectChan(sessionID string) <-chan error {
|
||||
if e == nil || e.wsExec == nil {
|
||||
return nil
|
||||
}
|
||||
return e.wsExec.UpstreamDisconnectChan(sessionID)
|
||||
}
|
||||
|
||||
func codexWebsocketsEnabled(auth *cliproxyauth.Auth) bool {
|
||||
if auth == nil {
|
||||
return false
|
||||
}
|
||||
if len(auth.Attributes) > 0 {
|
||||
if raw := strings.TrimSpace(auth.Attributes["websockets"]); raw != "" {
|
||||
parsed, errParse := strconv.ParseBool(raw)
|
||||
if errParse == nil {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(auth.Metadata) == 0 {
|
||||
return false
|
||||
}
|
||||
raw, ok := auth.Metadata["websockets"]
|
||||
if !ok || raw == nil {
|
||||
return false
|
||||
}
|
||||
switch v := raw.(type) {
|
||||
case bool:
|
||||
return v
|
||||
case string:
|
||||
parsed, errParse := strconv.ParseBool(strings.TrimSpace(v))
|
||||
if errParse == nil {
|
||||
return parsed
|
||||
}
|
||||
default:
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Reference in a new issue