Refactor AI server action code

This commit is contained in:
Sam Becker 2024-03-19 20:07:56 -05:00
parent 137b718fb7
commit fdd392bf25
4 changed files with 39 additions and 26 deletions

View File

@ -44,7 +44,7 @@ export const {
}, },
}); });
export const safelyRunServerAdminAction = async <T>( export const safelyRunAdminServerAction = async <T>(
callback: () => T, callback: () => T,
): Promise<T> => { ): Promise<T> => {
const session = await auth(); const session = await auth();

View File

@ -33,10 +33,11 @@ import {
import { extractExifDataFromBlobPath } from './server'; import { extractExifDataFromBlobPath } from './server';
import { TAG_FAVS, isTagFavs } from '@/tag'; import { TAG_FAVS, isTagFavs } from '@/tag';
import { convertPhotoToPhotoDbInsert } from '.'; import { convertPhotoToPhotoDbInsert } from '.';
import { safelyRunServerAdminAction } from '@/auth'; import { safelyRunAdminServerAction } from '@/auth';
import { ImageQuery, streamImageQuery } from './ai';
export async function createPhotoAction(formData: FormData) { export async function createPhotoAction(formData: FormData) {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
const photo = convertFormDataToPhotoDbInsert(formData, true); const photo = convertFormDataToPhotoDbInsert(formData, true);
const updatedUrl = await convertUploadToPhoto(photo.url, photo.id); const updatedUrl = await convertUploadToPhoto(photo.url, photo.id);
@ -52,7 +53,7 @@ export async function createPhotoAction(formData: FormData) {
} }
export async function updatePhotoAction(formData: FormData) { export async function updatePhotoAction(formData: FormData) {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
const photo = convertFormDataToPhotoDbInsert(formData); const photo = convertFormDataToPhotoDbInsert(formData);
await sqlUpdatePhoto(photo); await sqlUpdatePhoto(photo);
@ -67,7 +68,7 @@ export async function toggleFavoritePhotoAction(
photoId: string, photoId: string,
shouldRedirect?: boolean, shouldRedirect?: boolean,
) { ) {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
const photo = await getPhoto(photoId); const photo = await getPhoto(photoId);
if (photo) { if (photo) {
const { tags } = photo; const { tags } = photo;
@ -88,7 +89,7 @@ export async function deletePhotoAction(
photoUrl: string, photoUrl: string,
shouldRedirect?: boolean, shouldRedirect?: boolean,
) { ) {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
await sqlDeletePhoto(photoId).then(() => deleteStorageUrl(photoUrl)); await sqlDeletePhoto(photoId).then(() => deleteStorageUrl(photoUrl));
revalidateAllKeysAndPaths(); revalidateAllKeysAndPaths();
if (shouldRedirect) { if (shouldRedirect) {
@ -98,7 +99,7 @@ export async function deletePhotoAction(
}; };
export async function deletePhotoFormAction(formData: FormData) { export async function deletePhotoFormAction(formData: FormData) {
return safelyRunServerAdminAction(async () => return safelyRunAdminServerAction(async () =>
deletePhotoAction( deletePhotoAction(
formData.get('id') as string, formData.get('id') as string,
formData.get('url') as string, formData.get('url') as string,
@ -107,7 +108,7 @@ export async function deletePhotoFormAction(formData: FormData) {
}; };
export async function deletePhotoTagGloballyAction(formData: FormData) { export async function deletePhotoTagGloballyAction(formData: FormData) {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
const tag = formData.get('tag') as string; const tag = formData.get('tag') as string;
await sqlDeletePhotoTagGlobally(tag); await sqlDeletePhotoTagGlobally(tag);
@ -118,7 +119,7 @@ export async function deletePhotoTagGloballyAction(formData: FormData) {
} }
export async function renamePhotoTagGloballyAction(formData: FormData) { export async function renamePhotoTagGloballyAction(formData: FormData) {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
const tag = formData.get('tag') as string; const tag = formData.get('tag') as string;
const updatedTag = formData.get('updatedTag') as string; const updatedTag = formData.get('updatedTag') as string;
@ -132,7 +133,7 @@ export async function renamePhotoTagGloballyAction(formData: FormData) {
} }
export async function deleteBlobPhotoAction(formData: FormData) { export async function deleteBlobPhotoAction(formData: FormData) {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
await deleteStorageUrl(formData.get('url') as string); await deleteStorageUrl(formData.get('url') as string);
revalidateAdminPaths(); revalidateAdminPaths();
@ -146,7 +147,7 @@ export async function deleteBlobPhotoAction(formData: FormData) {
export async function getExifDataAction( export async function getExifDataAction(
photoFormPrevious: Partial<PhotoFormData>, photoFormPrevious: Partial<PhotoFormData>,
): Promise<Partial<PhotoFormData>> { ): Promise<Partial<PhotoFormData>> {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
const { url } = photoFormPrevious; const { url } = photoFormPrevious;
if (url) { if (url) {
const { photoFormExif } = await extractExifDataFromBlobPath(url); const { photoFormExif } = await extractExifDataFromBlobPath(url);
@ -159,7 +160,7 @@ export async function getExifDataAction(
} }
export async function syncPhotoExifDataAction(formData: FormData) { export async function syncPhotoExifDataAction(formData: FormData) {
return safelyRunServerAdminAction(async () => { return safelyRunAdminServerAction(async () => {
const photoId = formData.get('id') as string; const photoId = formData.get('id') as string;
if (photoId) { if (photoId) {
const photo = await getPhoto(photoId); const photo = await getPhoto(photoId);
@ -179,5 +180,13 @@ export async function syncPhotoExifDataAction(formData: FormData) {
} }
export async function syncCacheAction() { export async function syncCacheAction() {
return safelyRunServerAdminAction(revalidateAllKeysAndPaths); return safelyRunAdminServerAction(revalidateAllKeysAndPaths);
}
export async function streamImageQueryAction(
imageBase64: string,
query: ImageQuery,
) {
return safelyRunAdminServerAction(async () =>
streamImageQuery(imageBase64, query));
} }

13
src/photo/ai.ts Normal file
View File

@ -0,0 +1,13 @@
import { streamOpenAiImageQuery } from '@/services/openai';
export type ImageQuery = 'title' | 'caption' | 'tags' | 'description';
export const IMAGE_QUERIES: Record<ImageQuery, string> = {
title: 'What is the title of this image?',
caption: 'What is the caption of this image?',
tags: 'Describe this image three or less comma-separated keywords',
description: 'Describe this image in detail',
};
export const streamImageQuery = (imageBase64: string, query: ImageQuery) =>
streamOpenAiImageQuery(imageBase64, IMAGE_QUERIES[query]);

View File

@ -5,7 +5,10 @@ import { createStreamableValue, render } from 'ai/rsc';
const provider = new OpenAI({ apiKey: process.env.OPENAI_SECRET_KEY }); const provider = new OpenAI({ apiKey: process.env.OPENAI_SECRET_KEY });
const streamImageQueryRaw = async (imageBase64: string, query: string) => { export const streamOpenAiImageQuery = async (
imageBase64: string,
query: string,
) => {
const stream = createStreamableValue(''); const stream = createStreamableValue('');
render({ render({
@ -36,15 +39,3 @@ const streamImageQueryRaw = async (imageBase64: string, query: string) => {
return stream.value; return stream.value;
}; };
export type ImageQuery = 'title' | 'caption' | 'tags' | 'description';
export const IMAGE_QUERIES: Record<ImageQuery, string> = {
title: 'What is the title of this image?',
caption: 'What is the caption of this image?',
tags: 'Describe this image three or less comma-separated keywords',
description: 'Describe this image in detail',
};
export const streamImageQuery = (imageBase64: string, query: ImageQuery) =>
streamImageQueryRaw(imageBase64, IMAGE_QUERIES[query]);