ai-chatbot/lib/canvas/image-task-config.ts
Your Name 94a22f0cc6
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: add image batch dispatch pacing
2026-05-01 01:14:07 +08:00

131 lines
3.6 KiB
TypeScript

export const CANVAS_IMAGE_TASK_QUEUE_CONFIG_KEY = "canvas_image_task_queue_config";
export const MIN_CANVAS_IMAGE_TASK_CONCURRENCY = 1;
export const DEFAULT_CANVAS_IMAGE_TASK_CONCURRENCY = 8;
export const MAX_CANVAS_IMAGE_TASK_CONCURRENCY = 100;
export const MIN_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS = 0;
export const DEFAULT_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS = 0;
export const MAX_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS = 60 * 1000;
export interface CanvasImageTaskQueueConfig {
concurrency: number;
batchDispatchIntervalMs: number;
}
function clampConcurrency(value: number) {
return Math.min(
MAX_CANVAS_IMAGE_TASK_CONCURRENCY,
Math.max(MIN_CANVAS_IMAGE_TASK_CONCURRENCY, Math.floor(value))
);
}
function clampBatchDispatchIntervalMs(value: number) {
return Math.min(
MAX_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS,
Math.max(MIN_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS, Math.floor(value))
);
}
function parseConcurrencyCandidate(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) {
return clampConcurrency(value);
}
if (typeof value === "string" && value.trim()) {
const parsed = Number.parseInt(value.trim(), 10);
if (Number.isFinite(parsed)) {
return clampConcurrency(parsed);
}
}
return null;
}
function parseBatchDispatchIntervalCandidate(
value: unknown,
options?: { treatAsSeconds?: boolean }
): number | null {
const normalize = (candidate: number) =>
clampBatchDispatchIntervalMs(
options?.treatAsSeconds ? candidate * 1000 : candidate
);
if (typeof value === "number" && Number.isFinite(value)) {
return normalize(value);
}
if (typeof value === "string" && value.trim()) {
const parsed = Number.parseFloat(value.trim());
if (Number.isFinite(parsed)) {
return normalize(parsed);
}
}
return null;
}
export function normalizeCanvasImageTaskQueueConfig(
value: unknown,
options?: { envConcurrency?: unknown; envBatchDispatchIntervalMs?: unknown }
): CanvasImageTaskQueueConfig {
const raw = value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: {};
const concurrencyCandidates = [
raw.concurrency,
raw.maxConcurrency,
raw.maxConcurrent,
typeof value === "number" || typeof value === "string" ? value : undefined,
options?.envConcurrency,
];
let concurrency = DEFAULT_CANVAS_IMAGE_TASK_CONCURRENCY;
for (const candidate of concurrencyCandidates) {
const parsed = parseConcurrencyCandidate(candidate);
if (parsed !== null) {
concurrency = parsed;
break;
}
}
const intervalCandidates = [
raw.batchDispatchIntervalMs,
raw.dispatchIntervalMs,
raw.batchDispatchDelayMs,
raw.batchDispatchInterval,
raw.dispatchInterval,
options?.envBatchDispatchIntervalMs,
];
const intervalSecondsCandidates = [
raw.batchDispatchIntervalSeconds,
raw.dispatchIntervalSeconds,
];
let batchDispatchIntervalMs = DEFAULT_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS;
for (const candidate of intervalCandidates) {
const parsed = parseBatchDispatchIntervalCandidate(candidate);
if (parsed !== null) {
batchDispatchIntervalMs = parsed;
break;
}
}
if (batchDispatchIntervalMs === DEFAULT_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS) {
for (const candidate of intervalSecondsCandidates) {
const parsed = parseBatchDispatchIntervalCandidate(candidate, {
treatAsSeconds: true,
});
if (parsed !== null) {
batchDispatchIntervalMs = parsed;
break;
}
}
}
return {
concurrency,
batchDispatchIntervalMs,
};
}