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 | 747 | export function cancelStatusCheck(reason = 'Manually cancelled status check') { |
| 748 | 748 | abortStatusCheck?.abort(new AbortReason(reason)); |
| 749 | 749 | abortStatusCheck = new AbortController(); |
| 750 | 750 | setOnlineStatus('no_connection'); |
| @@ -7,6 +7,7 @@ import { Fuse, DOMPurify } from '../lib.js'; | ||
| 7 | 7 | |
| 8 | 8 | import { |
| 9 | 9 | abortStatusCheck, |
| 10 | + cancelStatusCheck, | |
| 10 | 11 | characters, |
| 11 | 12 | event_types, |
| 12 | 13 | eventSource, |
| @@ -6349,6 +6350,7 @@ export function initOpenAI() { | ||
| 6349 | 6350 | }); |
| 6350 | 6351 | |
| 6351 | 6352 | $('#chat_completion_source').on('change', function () { |
| 6353 | + cancelStatusCheck('Chat Completion source changed'); | |
| 6352 | 6354 | model_list = []; |
| 6353 | 6355 | oai_settings.chat_completion_source = String($(this).find(':selected').val()); |
| 6354 | 6356 | toggleChatCompletionForms(); |
| @@ -1560,6 +1560,7 @@ async function sendAzureOpenAIRequest(request, response) { | ||
| 1560 | 1560 | export const router = express.Router(); |
| 1561 | 1561 | |
| 1562 | 1562 | router.post('/status', async function (request, statusResponse) { |
| 1563 | + try { | |
| 1563 | 1564 | if (!request.body) return statusResponse.sendStatus(400); |
| 1564 | 1565 | |
| 1565 | 1566 | let apiUrl = ''; |
| @@ -1763,7 +1764,6 @@ router.post('/status', async function (request, statusResponse) { | ||
| 1763 | 1764 | return statusResponse.status(400).send({ error: true }); |
| 1764 | 1765 | } |
| 1765 | 1766 | |
| 1766 | - try { | |
| 1767 | 1767 | const modelsUrl = new URL(urlJoin(apiUrl, '/models')); |
| 1768 | 1768 | Object.keys(queryParams).forEach(key => { |
| 1769 | 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 | 1938 | if (!request.body) return response.status(400).send({ error: true }); |
| 1939 | 1939 | |
| 1940 | 1940 | const postProcessingType = request.body.custom_prompt_post_processing; |
| @@ -1951,18 +1951,18 @@ router.post('/generate', function (request, response) { | ||
| 1951 | 1951 | } |
| 1952 | 1952 | |
| 1953 | 1953 | switch (request.body.chat_completion_source) { |
| 1954 | 1954 | case CHAT_COMPLETION_SOURCES.CLAUDE: return await sendClaudeRequest(request, response); |
| 1955 | 1955 | case CHAT_COMPLETION_SOURCES.AI21: return await sendAI21Request(request, response); |
| 1956 | 1956 | case CHAT_COMPLETION_SOURCES.MAKERSUITE: return await sendMakerSuiteRequest(request, response); |
| 1957 | 1957 | case CHAT_COMPLETION_SOURCES.VERTEXAI: return await sendMakerSuiteRequest(request, response); |
| 1958 | 1958 | case CHAT_COMPLETION_SOURCES.MISTRALAI: return await sendMistralAIRequest(request, response); |
| 1959 | 1959 | case CHAT_COMPLETION_SOURCES.COHERE: return await sendCohereRequest(request, response); |
| 1960 | 1960 | case CHAT_COMPLETION_SOURCES.DEEPSEEK: return await sendDeepSeekRequest(request, response); |
| 1961 | 1961 | case CHAT_COMPLETION_SOURCES.AIMLAPI: return await sendAimlapiRequest(request, response); |
| 1962 | 1962 | case CHAT_COMPLETION_SOURCES.XAI: return await sendXaiRequest(request, response); |
| 1963 | 1963 | case CHAT_COMPLETION_SOURCES.CHUTES: return await sendChutesRequest(request, response); |
| 1964 | 1964 | case CHAT_COMPLETION_SOURCES.ELECTRONHUB: return await sendElectronHubRequest(request, response); |
| 1965 | 1965 | case CHAT_COMPLETION_SOURCES.AZURE_OPENAI: return await sendAzureOpenAIRequest(request, response); |
| 1966 | 1966 | } |
| 1967 | 1967 | |
| 1968 | 1968 | let apiUrl; |
| @@ -2300,32 +2300,33 @@ router.post('/generate', function (request, response) { | ||
| 2300 | 2300 | |
| 2301 | 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 | 2303 | const fetchResponse = await fetch(endpointUrl, config); |
| 2315 | 2304 | |
| 2316 | 2305 | if (request.body.stream) { |
| 2317 | 2306 | console.info('Streaming request in progress'); |
| 2318 | 2307 | return forwardFetchResponse(fetchResponse, response); |
| 2319 | - return; | |
| 2320 | 2308 | } |
| 2321 | 2309 | |
| 2322 | 2310 | if (fetchResponse.ok) { |
| 2323 | 2311 | /** @type {any} */ |
| 2324 | 2312 | let const json = await fetchResponse.json(); |
| 2325 | - response.send(json); | |
| 2326 | 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 | 2327 | } else { |
| 2328 | 2328 | await handleErrorResponseresponse.end(fetchResponse); |
| 2329 | + } | |
| 2329 | 2330 | } |
| 2330 | 2331 | } catch (error) { |
| 2331 | 2332 | console.error('Generation failed', error); |
| @@ -2339,27 +2340,6 @@ router.post('/generate', function (request, response) { | ||
| 2339 | 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 | 2345 | const multimodalModels = express.Router(); |