Refactor CC API async route handlers (#4885) * Improve error handling in CC /status and /generate endpoints * Cancel pending status check on switching CC source

9046fe8d2de22b166e588839c8579c9b1da754c0

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
3 files changed, +35 -53Showing whitespace changes
public/script.js+1 -1
@@ -744,7 +744,7 @@ function initStandaloneMode() {
744 }744 }
745}745}
746746
747function cancelStatusCheck(reason = 'Manually cancelled status check') {747export function cancelStatusCheck(reason = 'Manually cancelled status check') {
748 abortStatusCheck?.abort(new AbortReason(reason));748 abortStatusCheck?.abort(new AbortReason(reason));
749 abortStatusCheck = new AbortController();749 abortStatusCheck = new AbortController();
750 setOnlineStatus('no_connection');750 setOnlineStatus('no_connection');
public/scripts/openai.js+2 -0
@@ -7,6 +7,7 @@ import { Fuse, DOMPurify } from '../lib.js';
77
8import {8import {
9 abortStatusCheck,9 abortStatusCheck,
10 cancelStatusCheck,
10 characters,11 characters,
11 event_types,12 event_types,
12 eventSource,13 eventSource,
@@ -6349,6 +6350,7 @@ export function initOpenAI() {
6349 });6350 });
63506351
6351 $('#chat_completion_source').on('change', function () {6352 $('#chat_completion_source').on('change', function () {
6353 cancelStatusCheck('Chat Completion source changed');
6352 model_list = [];6354 model_list = [];
6353 oai_settings.chat_completion_source = String($(this).find(':selected').val());6355 oai_settings.chat_completion_source = String($(this).find(':selected').val());
6354 toggleChatCompletionForms();6356 toggleChatCompletionForms();
src/endpoints/backends/chat-completions.js+32 -52
@@ -1560,6 +1560,7 @@ async function sendAzureOpenAIRequest(request, response) {
1560export const router = express.Router();1560export const router = express.Router();
15611561
1562router.post('/status', async function (request, statusResponse) {1562router.post('/status', async function (request, statusResponse) {
1563 try {
1563 if (!request.body) return statusResponse.sendStatus(400);1564 if (!request.body) return statusResponse.sendStatus(400);
15641565
1565 let apiUrl = '';1566 let apiUrl = '';
@@ -1763,7 +1764,6 @@ router.post('/status', async function (request, statusResponse) {
1763 return statusResponse.status(400).send({ error: true });1764 return statusResponse.status(400).send({ error: true });
1764 }1765 }
17651766
1766 try {
1767 const modelsUrl = new URL(urlJoin(apiUrl, '/models'));1767 const modelsUrl = new URL(urlJoin(apiUrl, '/models'));
1768 Object.keys(queryParams).forEach(key => {1768 Object.keys(queryParams).forEach(key => {
1769 modelsUrl.searchParams.append(key, queryParams[key]);1769 modelsUrl.searchParams.append(key, queryParams[key]);
@@ -1933,8 +1933,8 @@ router.post('/bias', async function (request, response) {
1933 }1933 }
1934});1934});
19351935
19361936router.post('/generate', async function (request, response) {
1937router.post('/generate', function (request, response) {1937 try {
1938 if (!request.body) return response.status(400).send({ error: true });1938 if (!request.body) return response.status(400).send({ error: true });
19391939
1940 const postProcessingType = request.body.custom_prompt_post_processing;1940 const postProcessingType = request.body.custom_prompt_post_processing;
@@ -1951,18 +1951,18 @@ router.post('/generate', function (request, response) {
1951 }1951 }
19521952
1953 switch (request.body.chat_completion_source) {1953 switch (request.body.chat_completion_source) {
1954 case CHAT_COMPLETION_SOURCES.CLAUDE: return sendClaudeRequest(request, response);1954 case CHAT_COMPLETION_SOURCES.CLAUDE: return await sendClaudeRequest(request, response);
1955 case CHAT_COMPLETION_SOURCES.AI21: return sendAI21Request(request, response);1955 case CHAT_COMPLETION_SOURCES.AI21: return await sendAI21Request(request, response);
1956 case CHAT_COMPLETION_SOURCES.MAKERSUITE: return sendMakerSuiteRequest(request, response);1956 case CHAT_COMPLETION_SOURCES.MAKERSUITE: return await sendMakerSuiteRequest(request, response);
1957 case CHAT_COMPLETION_SOURCES.VERTEXAI: return sendMakerSuiteRequest(request, response);1957 case CHAT_COMPLETION_SOURCES.VERTEXAI: return await sendMakerSuiteRequest(request, response);
1958 case CHAT_COMPLETION_SOURCES.MISTRALAI: return sendMistralAIRequest(request, response);1958 case CHAT_COMPLETION_SOURCES.MISTRALAI: return await sendMistralAIRequest(request, response);
1959 case CHAT_COMPLETION_SOURCES.COHERE: return sendCohereRequest(request, response);1959 case CHAT_COMPLETION_SOURCES.COHERE: return await sendCohereRequest(request, response);
1960 case CHAT_COMPLETION_SOURCES.DEEPSEEK: return sendDeepSeekRequest(request, response);1960 case CHAT_COMPLETION_SOURCES.DEEPSEEK: return await sendDeepSeekRequest(request, response);
1961 case CHAT_COMPLETION_SOURCES.AIMLAPI: return sendAimlapiRequest(request, response);1961 case CHAT_COMPLETION_SOURCES.AIMLAPI: return await sendAimlapiRequest(request, response);
1962 case CHAT_COMPLETION_SOURCES.XAI: return sendXaiRequest(request, response);1962 case CHAT_COMPLETION_SOURCES.XAI: return await sendXaiRequest(request, response);
1963 case CHAT_COMPLETION_SOURCES.CHUTES: return sendChutesRequest(request, response);1963 case CHAT_COMPLETION_SOURCES.CHUTES: return await sendChutesRequest(request, response);
1964 case CHAT_COMPLETION_SOURCES.ELECTRONHUB: return sendElectronHubRequest(request, response);1964 case CHAT_COMPLETION_SOURCES.ELECTRONHUB: return await sendElectronHubRequest(request, response);
1965 case CHAT_COMPLETION_SOURCES.AZURE_OPENAI: return sendAzureOpenAIRequest(request, response);1965 case CHAT_COMPLETION_SOURCES.AZURE_OPENAI: return await sendAzureOpenAIRequest(request, response);
1966 }1966 }
19671967
1968 let apiUrl;1968 let apiUrl;
@@ -2300,32 +2300,33 @@ router.post('/generate', function (request, response) {
23002300
2301 console.debug('Chat Completion request:', requestBody);2301 console.debug('Chat Completion request:', requestBody);
23022302
2303 makeRequest(config, response, request);
2304
2305 /**
2306 * Makes a fetch request to the OpenAI API endpoint.
2307 * @param {import('node-fetch').RequestInit} config Fetch config
2308 * @param {express.Response} response Express response
2309 * @param {express.Request} request Express request
2310 */
2311 async function makeRequest(config, response, request) {
2312 try {
2313 controller.signal.throwIfAborted();
2314 const fetchResponse = await fetch(endpointUrl, config);2303 const fetchResponse = await fetch(endpointUrl, config);
23152304
2316 if (request.body.stream) {2305 if (request.body.stream) {
2317 console.info('Streaming request in progress');2306 console.info('Streaming request in progress');
2318 forwardFetchResponse(fetchResponse, response);2307 return forwardFetchResponse(fetchResponse, response);
2319 return;
2320 }2308 }
23212309
2322 if (fetchResponse.ok) {2310 if (fetchResponse.ok) {
2323 /** @type {any} */2311 /** @type {any} */
2324 let json = await fetchResponse.json();2312 const json = await fetchResponse.json();
2325 response.send(json);
2326 console.debug('Chat Completion response:', json);2313 console.debug('Chat Completion response:', json);
2314 return response.send(json);
2315 } else {
2316 const responseText = await fetchResponse.text();
2317 const errorData = tryParse(responseText);
2318
2319 const message = fetchResponse.statusText || 'Unknown error occurred';
2320 const quota_error = fetchResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';
2321 console.error('Chat completion request error: ', message, responseText);
2322
2323 if (!response.headersSent) {
2324 response.send({ error: { message }, quota_error: quota_error });
2325 } else if (!response.writableEnded) {
2326 response.write(responseText);
2327 } else {2327 } else {
2328 await handleErrorResponse(fetchResponse);2328 response.end();
2329 }
2329 }2330 }
2330 } catch (error) {2331 } catch (error) {
2331 console.error('Generation failed', error);2332 console.error('Generation failed', error);
@@ -2339,27 +2340,6 @@ router.post('/generate', function (request, response) {
2339 response.end();2340 response.end();
2340 }2341 }
2341 }2342 }
2342 }
2343
2344 /**
2345 * @param {import("node-fetch").Response} errorResponse
2346 */
2347 async function handleErrorResponse(errorResponse) {
2348 const responseText = await errorResponse.text();
2349 const errorData = tryParse(responseText);
2350
2351 const message = errorResponse.statusText || 'Unknown error occurred';
2352 const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';
2353 console.error('Chat completion request error: ', message, responseText);
2354
2355 if (!response.headersSent) {
2356 response.send({ error: { message }, quota_error: quota_error });
2357 } else if (!response.writableEnded) {
2358 response.write(responseText);
2359 } else {
2360 response.end();
2361 }
2362 }
2363});2343});
23642344
2365const multimodalModels = express.Router();2345const multimodalModels = express.Router();