Refactor CC API async route handlers (#4885) * Improve error handling in CC /status and /generate endpoints * Cancel pending status check on switching CC source
Signed| @@ -744,7 +744,7 @@ function initStandaloneMode() { | |||
| 744 | } | 744 | } |
| 745 | } | 745 | } |
| 746 | 746 | ||
| 747 | function cancelStatusCheck(reason = 'Manually cancelled status check') { | 747 | export 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'); |
| @@ -7,6 +7,7 @@ import { Fuse, DOMPurify } from '../lib.js'; | |||
| 7 | 7 | ||
| 8 | import { | 8 | import { |
| 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 | }); |
| 6350 | 6351 | ||
| 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(); |
| @@ -1560,6 +1560,7 @@ async function sendAzureOpenAIRequest(request, response) { | |||
| 1560 | export const router = express.Router(); | 1560 | export const router = express.Router(); |
| 1561 | 1561 | ||
| 1562 | router.post('/status', async function (request, statusResponse) { | 1562 | router.post('/status', async function (request, statusResponse) { |
| 1563 | try { | ||
| 1563 | if (!request.body) return statusResponse.sendStatus(400); | 1564 | if (!request.body) return statusResponse.sendStatus(400); |
| 1564 | 1565 | ||
| 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 | } |
| 1765 | 1766 | ||
| 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 | }); |
| 1935 | 1935 | ||
| 1936 | 1936 | router.post('/generate', async function (request, response) { | |
| 1937 | router.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 }); |
| 1939 | 1939 | ||
| 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 | } |
| 1952 | 1952 | ||
| 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 | } |
| 1967 | 1967 | ||
| 1968 | let apiUrl; | 1968 | let apiUrl; |
| @@ -2300,32 +2300,33 @@ router.post('/generate', function (request, response) { | |||
| 2300 | 2300 | ||
| 2301 | console.debug('Chat Completion request:', requestBody); | 2301 | console.debug('Chat Completion request:', requestBody); |
| 2302 | 2302 | ||
| 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); |
| 2315 | 2304 | ||
| 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 | } |
| 2321 | 2309 | ||
| 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 | }); |
| 2364 | 2344 | ||
| 2365 | const multimodalModels = express.Router(); | 2345 | const multimodalModels = express.Router(); |