Increase AI rate limit window for batch requests

This commit is contained in:
Sam Becker 2025-04-20 11:48:50 -05:00
parent 3256774d48
commit b3dba5f676
3 changed files with 24 additions and 11 deletions

View File

@ -374,7 +374,7 @@ export const getExifDataAction = async (
// - strip GPS data if necessary
// - update blur data (or destroy if blur is disabled)
// - generate AI text data, if enabled, and auto-generated fields are empty
export const syncPhotoAction = async (photoId: string) =>
export const syncPhotoAction = async (photoId: string, isBatch?: boolean) =>
runAuthenticatedAdminServerAction(async () => {
const photo = await getPhoto(photoId ?? '', true);
@ -415,6 +415,7 @@ export const syncPhotoAction = async (photoId: string) =>
} = await generateAiImageQueries(
imageResizedBase64,
photo.syncStatus.missingAiTextFields,
isBatch,
);
const formDataFromPhoto = convertPhotoToFormData(photo);
@ -451,7 +452,7 @@ export const syncPhotoAction = async (photoId: string) =>
export const syncPhotosAction = async (photoIds: string[]) =>
runAuthenticatedAdminServerAction(async () => {
for (const photoId of photoIds) {
await syncPhotoAction(photoId);
await syncPhotoAction(photoId, true);
}
revalidateAllKeysAndPaths();
});

View File

@ -9,6 +9,7 @@ import { getUniqueTags } from '../db/query';
export const generateAiImageQueries = async (
imageBase64?: string,
textFieldsToGenerate: AiAutoGeneratedField[] = [],
isBatch?: boolean,
): Promise<{
title?: string
caption?: string
@ -31,6 +32,7 @@ export const generateAiImageQueries = async (
const titleAndCaption = await generateOpenAiImageQuery(
imageBase64,
getAiImageQuery('title-and-caption'),
isBatch,
);
if (titleAndCaption) {
const titleAndCaptionParsed = parseTitleAndCaption(titleAndCaption);
@ -42,12 +44,14 @@ export const generateAiImageQueries = async (
title = await generateOpenAiImageQuery(
imageBase64,
getAiImageQuery('title'),
isBatch,
);
}
if (textFieldsToGenerate.includes('caption')) {
caption = await generateOpenAiImageQuery(
imageBase64,
getAiImageQuery('caption'),
isBatch,
);
}
}
@ -57,6 +61,7 @@ export const generateAiImageQueries = async (
tags = await generateOpenAiImageQuery(
imageBase64,
getAiImageQuery('tags', existingTags),
isBatch,
);
}
@ -64,6 +69,7 @@ export const generateAiImageQueries = async (
semanticDescription = await generateOpenAiImageQuery(
imageBase64,
getAiImageQuery('description-small'),
isBatch,
);
}
}

View File

@ -13,7 +13,6 @@ import { cleanUpAiTextResponse } from '@/photo/ai';
const redis = HAS_REDIS_STORAGE ? Redis.fromEnv() : undefined;
const RATE_LIMIT_IDENTIFIER = 'openai-image-query';
const RATE_LIMIT_MAX_QUERIES_PER_HOUR = 100;
const MODEL = 'gpt-4o';
const openai = AI_TEXT_GENERATION_ENABLED
@ -21,18 +20,24 @@ const openai = AI_TEXT_GENERATION_ENABLED
: undefined;
const ratelimit = redis
? new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(RATE_LIMIT_MAX_QUERIES_PER_HOUR, '1h'),
})
? {
basic: new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(100, '1h'),
}),
batch: new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(1200, '1d'),
}),
}
: undefined;
// Allows 100 requests per hour
const checkRateLimitAndThrow = async () => {
const checkRateLimitAndThrow = async (isBatch?: boolean) => {
if (ratelimit) {
let success = false;
try {
success = (await ratelimit.limit(RATE_LIMIT_IDENTIFIER)).success;
const limiter = isBatch ? ratelimit.batch : ratelimit.basic;
success = (await limiter.limit(RATE_LIMIT_IDENTIFIER)).success;
} catch (e: any) {
console.error('Failed to rate limit OpenAI', e);
throw new Error('Failed to rate limit OpenAI');
@ -92,8 +97,9 @@ export const streamOpenAiImageQuery = async (
export const generateOpenAiImageQuery = async (
imageBase64: string,
query: string,
isBatch?: boolean,
) => {
await checkRateLimitAndThrow();
await checkRateLimitAndThrow(isBatch);
const args = getImageTextArgs(imageBase64, query);