@@ -55997,19 +55997,30 @@ var CopilotApiGateway = class {
5599755997 if (this.config.enableWebSocket) {
5599855998 const { WebSocketServer: WSServer } = await Promise.resolve().then(() => (init_wrapper(), wrapper_exports));
5599955999 this.wsServer = new WSServer({ noServer: true });
56000- this.wsServer.on("connection", (socket) => this.handleWebSocketConnection(socket));
5600156000 this.wsServer.on("error", (error2) => {
5600256001 this.logError("WebSocket server error", error2);
5600356002 });
56003+ const wsEndpoints = [
56004+ "/v1/realtime",
56005+ // OpenAI format
56006+ "/anthropic/v1/realtime",
56007+ // Anthropic format
56008+ "/google/v1/realtime",
56009+ // Google format
56010+ "/llama/v1/realtime"
56011+ // Llama format
56012+ ];
5600456013 this.httpServer.on("upgrade", (request, socket, head) => {
5600556014 if (!request.url) {
5600656015 socket.destroy();
5600756016 return;
5600856017 }
5600956018 const url2 = this.buildUrl(request.url);
56010- if (url2.pathname === "/v1/realtime") {
56019+ const endpoint = wsEndpoints.find((ep) => url2.pathname === ep);
56020+ if (endpoint) {
5601156021 this.wsServer?.handleUpgrade(request, socket, head, (ws) => {
56012- this.wsServer?.emit("connection", ws, request);
56022+ ws._endpoint = endpoint;
56023+ this.handleWebSocketConnection(ws, endpoint);
5601356024 });
5601456025 } else {
5601556026 socket.destroy();
@@ -56462,6 +56473,18 @@ var CopilotApiGateway = class {
5646256473 if (url2.pathname.startsWith("/v1/audio")) {
5646356474 throw new ApiError(501, "Audio processing is not supported by Copilot.", "not_implemented", "audio_not_supported");
5646456475 }
56476+ const wsEndpoints = ["/v1/realtime", "/anthropic/v1/realtime", "/google/v1/realtime", "/llama/v1/realtime"];
56477+ if (req.method === "GET" && wsEndpoints.includes(url2.pathname)) {
56478+ res.writeHead(426, { "Content-Type": "application/json", "Upgrade": "websocket" });
56479+ res.end(JSON.stringify({
56480+ error: {
56481+ message: "This is a WebSocket endpoint. Please use a WebSocket client and Upgrade header.",
56482+ type: "upgrade_required",
56483+ code: "websocket_only"
56484+ }
56485+ }));
56486+ return;
56487+ }
5646556488 throw new ApiError(404, `No route for ${req.method ?? "UNKNOWN"} ${url2.pathname}`, "not_found");
5646656489 }
5646756490 async processStreamingGoogleGenerateContent(modelId, payload, req, res, logRequestId, logRequestStart) {
@@ -57428,7 +57451,7 @@ IMPORTANT: You MUST respond with valid JSON only.No markdown, no explanation, ju
5742857451 }
5742957452 };
5743057453 }
57431- async handleWebSocketMessage(socket, raw) {
57454+ async handleWebSocketMessage(socket, raw, endpoint ) {
5743257455 const text = typeof raw === "string" ? raw : raw.toString("utf8");
5743357456 let payload;
5743457457 try {
@@ -57441,9 +57464,33 @@ IMPORTANT: You MUST respond with valid JSON only.No markdown, no explanation, ju
5744157464 socket.send(JSON.stringify({ type: "pong", timestamp: Date.now() }));
5744257465 return;
5744357466 }
57467+ if (type === "models.list") {
57468+ const models = await this.getAvailableModels();
57469+ socket.send(JSON.stringify({ type: "models.result", data: { object: "list", data: models } }));
57470+ return;
57471+ }
57472+ switch (endpoint) {
57473+ case "/v1/realtime":
57474+ case "/llama/v1/realtime":
57475+ await this.handleOpenAIWebSocketMessage(socket, payload, type, endpoint);
57476+ return;
57477+ case "/anthropic/v1/realtime":
57478+ await this.handleAnthropicWebSocketMessage(socket, payload, type);
57479+ return;
57480+ case "/google/v1/realtime":
57481+ await this.handleGoogleWebSocketMessage(socket, payload, type);
57482+ return;
57483+ default:
57484+ throw new ApiError(400, `Unknown WebSocket endpoint: ${endpoint}`, "invalid_request_error", "unknown_endpoint");
57485+ }
57486+ }
57487+ async handleOpenAIWebSocketMessage(socket, payload, type, endpoint) {
5744457488 if (!type || type === "chat.completions.create") {
5744557489 const body = payload?.data ?? payload?.request ?? payload;
57446- const response = await this.processChatCompletion(body, { source: "websocket", endpoint: "/v1/chat/completions" });
57490+ if (endpoint === "/llama/v1/realtime" && body?.max_completion_tokens && !body?.max_tokens) {
57491+ body.max_tokens = body.max_completion_tokens;
57492+ }
57493+ const response = await this.processChatCompletion(body, { source: "websocket", endpoint });
5744757494 socket.send(JSON.stringify({ type: "chat.completion.result", data: response }));
5744857495 return;
5744957496 }
@@ -57453,19 +57500,39 @@ IMPORTANT: You MUST respond with valid JSON only.No markdown, no explanation, ju
5745357500 socket.send(JSON.stringify({ type: "completion.result", data: response }));
5745457501 return;
5745557502 }
57456- throw new ApiError(400, `Unsupported message type: ${type} `, "invalid_request_error", "unsupported_ws_message");
57503+ throw new ApiError(400, `Unsupported OpenAI WebSocket message type: ${type}`, "invalid_request_error", "unsupported_ws_message");
5745757504 }
57458- handleWebSocketConnection(socket) {
57505+ async handleAnthropicWebSocketMessage(socket, payload, type) {
57506+ if (!type || type === "messages.create") {
57507+ const body = payload?.data ?? payload?.request ?? payload;
57508+ const response = await this.processAnthropicMessages(body);
57509+ socket.send(JSON.stringify({ type: "message.result", data: response }));
57510+ return;
57511+ }
57512+ throw new ApiError(400, `Unsupported Anthropic WebSocket message type: ${type}`, "invalid_request_error", "unsupported_ws_message");
57513+ }
57514+ async handleGoogleWebSocketMessage(socket, payload, type) {
57515+ if (!type || type === "generateContent") {
57516+ const body = payload?.data ?? payload?.request ?? payload;
57517+ const modelId = payload?.model ?? "gemini-pro";
57518+ const response = await this.processGoogleGenerateContent(modelId, body);
57519+ socket.send(JSON.stringify({ type: "content.result", data: response }));
57520+ return;
57521+ }
57522+ throw new ApiError(400, `Unsupported Google WebSocket message type: ${type}`, "invalid_request_error", "unsupported_ws_message");
57523+ }
57524+ handleWebSocketConnection(socket, endpoint) {
5745957525 socket.send(JSON.stringify({
5746057526 type: "session.created",
5746157527 session: {
5746257528 id: (0, import_crypto.randomUUID)(),
5746357529 model: this.config.defaultModel,
57464- created: Math.floor(Date.now() / 1e3)
57530+ created: Math.floor(Date.now() / 1e3),
57531+ endpoint
5746557532 }
5746657533 }));
5746757534 socket.on("message", (data) => {
57468- void this.handleWebSocketMessage(socket, data).catch((error2) => {
57535+ void this.handleWebSocketMessage(socket, data, endpoint ).catch((error2) => {
5746957536 if (error2 instanceof ApiError) {
5747057537 this.sendWsError(socket, error2);
5747157538 } else {
@@ -57842,7 +57909,8 @@ ${text} `;
5784257909 { name: "Google", description: "Google Generative AI API compatible endpoints" },
5784357910 { name: "Llama", description: "Meta Llama API compatible endpoints" },
5784457911 { name: "Models", description: "Model information" },
57845- { name: "Utilities", description: "Utility endpoints" }
57912+ { name: "Utilities", description: "Utility endpoints" },
57913+ { name: "WebSocket", description: "Real-time WebSocket endpoints for all providers" }
5784657914 ],
5784757915 paths: {
5784857916 "/v1/chat/completions": {
@@ -58190,6 +58258,54 @@ ${text} `;
5819058258 }
5819158259 }
5819258260 }
58261+ },
58262+ "/v1/realtime": {
58263+ get: {
58264+ tags: ["WebSocket"],
58265+ summary: "OpenAI Realtime WebSocket",
58266+ description: "Initiate a WebSocket connection for real-time chat completions using the OpenAI format. Requires `Upgrade: websocket` header.",
58267+ operationId: "connectOpenAIRealtime",
58268+ responses: {
58269+ "101": { description: "Switching Protocols to WebSocket" },
58270+ "426": { description: "Upgrade Required - Use WebSocket client" }
58271+ }
58272+ }
58273+ },
58274+ "/anthropic/v1/realtime": {
58275+ get: {
58276+ tags: ["WebSocket", "Anthropic"],
58277+ summary: "Anthropic Realtime WebSocket",
58278+ description: "Initiate a WebSocket connection using the Anthropic Messages API format.",
58279+ operationId: "connectAnthropicRealtime",
58280+ responses: {
58281+ "101": { description: "Switching Protocols to WebSocket" },
58282+ "426": { description: "Upgrade Required - Use WebSocket client" }
58283+ }
58284+ }
58285+ },
58286+ "/google/v1/realtime": {
58287+ get: {
58288+ tags: ["WebSocket", "Google"],
58289+ summary: "Google Realtime WebSocket",
58290+ description: "Initiate a WebSocket connection using the Google Gemini format.",
58291+ operationId: "connectGoogleRealtime",
58292+ responses: {
58293+ "101": { description: "Switching Protocols to WebSocket" },
58294+ "426": { description: "Upgrade Required - Use WebSocket client" }
58295+ }
58296+ }
58297+ },
58298+ "/llama/v1/realtime": {
58299+ get: {
58300+ tags: ["WebSocket", "Llama"],
58301+ summary: "Llama Realtime WebSocket",
58302+ description: "Initiate a WebSocket connection using the Llama/OpenAI format.",
58303+ operationId: "connectLlamaRealtime",
58304+ responses: {
58305+ "101": { description: "Switching Protocols to WebSocket" },
58306+ "426": { description: "Upgrade Required - Use WebSocket client" }
58307+ }
58308+ }
5819358309 }
5819458310 },
5819558311 components: {
0 commit comments