fix(api): map upstream timeouts to 504 responses

This commit is contained in:
2026-04-21 13:32:12 +00:00
parent d6b4c0def1
commit fe4fdb32d7
4 changed files with 154 additions and 0 deletions
+11
View File
@@ -6,6 +6,7 @@ import * as http from 'node:http';
import type { IApiError, IChatCompletionRequest } from '../../interfaces/api.ts';
import { ClusterCoordinator } from '../../cluster/coordinator.ts';
import { ContainerManager } from '../../containers/container-manager.ts';
import { UpstreamTimeoutError } from '../../containers/base-container.ts';
import { API_SERVER } from '../../constants.ts';
import { logger } from '../../logger.ts';
import { ModelRegistry } from '../../models/registry.ts';
@@ -86,6 +87,11 @@ export class ChatHandler {
await this.proxyChatRequest(req, res, ensured.location.endpoint, requestBody);
} catch (error) {
if (error instanceof UpstreamTimeoutError) {
this.sendError(res, 504, error.message, 'upstream_timeout');
return;
}
const message = error instanceof Error ? error.message : String(error);
logger.error(`Chat completion error: ${message}`);
this.sendError(res, 500, `Chat completion failed: ${message}`, 'server_error');
@@ -166,6 +172,11 @@ export class ChatHandler {
headers: this.buildForwardHeaders(req),
body: JSON.stringify(body),
signal: controller.signal,
}).catch((error) => {
if (error instanceof Error && error.name === 'AbortError') {
throw new UpstreamTimeoutError();
}
throw error;
}).finally(() => clearTimeout(timeout));
if (body.stream) {
+11
View File
@@ -11,6 +11,7 @@ import type {
} from '../../interfaces/api.ts';
import { ClusterCoordinator } from '../../cluster/coordinator.ts';
import { ContainerManager } from '../../containers/container-manager.ts';
import { UpstreamTimeoutError } from '../../containers/base-container.ts';
import { API_SERVER } from '../../constants.ts';
import { logger } from '../../logger.ts';
import { ModelRegistry } from '../../models/registry.ts';
@@ -93,6 +94,11 @@ export class EmbeddingsHandler {
});
res.end(text);
} catch (error) {
if (error instanceof UpstreamTimeoutError) {
this.sendError(res, 504, error.message, 'upstream_timeout');
return;
}
const message = error instanceof Error ? error.message : String(error);
logger.error(`Embeddings error: ${message}`);
this.sendError(res, 500, `Embeddings generation failed: ${message}`, 'server_error');
@@ -224,6 +230,11 @@ export class EmbeddingsHandler {
...init,
signal: controller.signal,
});
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
throw new UpstreamTimeoutError();
}
throw error;
} finally {
clearTimeout(timeout);
}
+12
View File
@@ -24,6 +24,13 @@ export type TModelPullProgress = (progress: {
percent?: number;
}) => void;
export class UpstreamTimeoutError extends Error {
constructor(message: string = 'Upstream request timed out') {
super(message);
this.name = 'UpstreamTimeoutError';
}
}
/**
* Abstract base class for AI model containers
*/
@@ -181,6 +188,11 @@ export abstract class BaseContainer {
});
return response;
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
throw new UpstreamTimeoutError();
}
throw error;
} finally {
clearTimeout(timeoutId);
}