Vercel/src/services/openai.ts
2024-05-10 14:55:47 -05:00

75 lines
2.0 KiB
TypeScript

'use server';
import { streamText } from 'ai';
import { createStreamableValue } from 'ai/rsc';
import { createOpenAI } from '@ai-sdk/openai';
import { kv } from '@vercel/kv';
import { Ratelimit } from '@upstash/ratelimit';
import { AI_TEXT_GENERATION_ENABLED, HAS_VERCEL_KV } from '@/site/config';
import { safelyRunAdminServerAction } from '@/auth';
import { removeBase64Prefix } from '@/utility/image';
const RATE_LIMIT_IDENTIFIER = 'openai-image-query';
const RATE_LIMIT_MAX_QUERIES_PER_HOUR = 100;
const openai = AI_TEXT_GENERATION_ENABLED
? createOpenAI({ apiKey: process.env.OPENAI_SECRET_KEY })
: undefined;
// Allows 100 requests per hour
const ratelimit = HAS_VERCEL_KV
? new Ratelimit({
redis: kv,
limiter: Ratelimit.slidingWindow(RATE_LIMIT_MAX_QUERIES_PER_HOUR, '1h'),
})
: undefined;
export const streamOpenAiImageQuery = async (
imageBase64: string,
query: string,
) => {
return safelyRunAdminServerAction(async () => {
if (ratelimit) {
let success = false;
try {
success = (await ratelimit.limit(RATE_LIMIT_IDENTIFIER)).success;
} catch (e: any) {
console.error('Failed to rate limit OpenAI', e);
throw new Error('Failed to rate limit OpenAI');
}
if (!success) {
console.error('OpenAI rate limit exceeded');
throw new Error('OpenAI rate limit exceeded');
}
}
const stream = createStreamableValue('');
if (openai) {
(async () => {
const { textStream } = await streamText({
model: openai('gpt-4-vision-preview'),
messages: [{
'role': 'user',
'content': [
{
'type': 'text',
'text': query,
}, {
'type': 'image',
'image': removeBase64Prefix(imageBase64),
},
],
}],
});
for await (const delta of textStream) {
stream.update(delta);
}
stream.done();
})();
}
return stream.value;
});
};