Create AI image request hook

This commit is contained in:
Sam Becker 2024-03-19 21:55:18 -05:00
parent 0fcfa1b3c1
commit 9110325702
3 changed files with 112 additions and 17 deletions

View File

@ -3,7 +3,7 @@ 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?',
title: 'Provide a short title for 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',

View File

@ -0,0 +1,32 @@
import { useCallback, useState } from 'react';
import { streamImageQueryAction } from '../actions';
import { readStreamableValue } from 'ai/rsc';
import { ImageQuery } from '.';
export default function useImageQuery(
imageBase64: string | undefined,
query: ImageQuery,
) {
const [text, setText] = useState('');
const [isLoading, setIsLoading] = useState(false);
const request = useCallback(async () => {
if (imageBase64) {
setIsLoading(true);
const textStream = await streamImageQueryAction(
imageBase64 ?? '',
query,
);
for await (const text of readStreamableValue(textStream)) {
setText(text ?? '');
}
setIsLoading(false);
}
}, [imageBase64, query]);
return [
request,
text,
isLoading,
] as const;
};

View File

@ -31,6 +31,7 @@ import { Tags, sortTagsObjectWithoutFavs } from '@/tag';
import { formatCount, formatCountDescriptive } from '@/utility/string';
import { readStreamableValue } from 'ai/rsc';
import Spinner from '@/components/Spinner';
import useImageQuery from '../ai/useImageQuery';
const THUMBNAIL_SIZE = 300;
@ -122,24 +123,72 @@ export default function PhotoForm({
}
}, []);
const [aiTags, setAiTags] = useState('');
const [isLoadingAi, setIsLoadingAi] = useState(false);
const [
requestTitle,
title,
isLoadingTitle,
] = useImageQuery(imageData, 'title');
const [aiTags, setAiTags] = useState({
two: '',
three: '',
});
const [isLoadingAi, setIsLoadingAi] = useState({
two: false,
three: false,
});
return (
<div className="space-y-8 max-w-[38rem]">
<button onClick={async () => {
setIsLoadingAi(true);
const textStream = await streamImageQueryAction(
imageData ?? '',
'description',
);
for await (const text of readStreamableValue(textStream)) {
setAiTags(text ?? '');
}
setIsLoadingAi(false);
}}>
Generate Text
</button>
<div className="flex gap-2">
<button onClick={requestTitle}>
Title
</button>
<button onClick={async () => {
setIsLoadingAi(current => ({
...current,
two: true,
}));
const textStream = await streamImageQueryAction(
imageData ?? '',
'tags',
);
for await (const text of readStreamableValue(textStream)) {
setAiTags(current => ({
...current,
two: text ?? '',
}));
}
setIsLoadingAi(current => ({
...current,
two: false,
}));
}}>
Tags
</button>
<button onClick={async () => {
setIsLoadingAi(current => ({
...current,
three: true,
}));
const textStream = await streamImageQueryAction(
imageData ?? '',
'description',
);
for await (const text of readStreamableValue(textStream)) {
setAiTags(current => ({
...current,
three: text ?? '',
}));
}
setIsLoadingAi(current => ({
...current,
three: false,
}));
}}>
Description
</button>
</div>
<div className="flex gap-2">
<ImageBlurFallback
alt="Upload"
@ -172,7 +221,21 @@ export default function PhotoForm({
/>}
</div>
<p>
AI RESPONSE: {aiTags} {isLoadingAi && <>
AI RESPONSE 01: {title} {isLoadingTitle && <>
<span className="inline-flex translate-y-[1.5px]">
<Spinner />
</span>
</>}
</p>
<p>
AI RESPONSE 02: {aiTags.two} {isLoadingAi.two && <>
<span className="inline-flex translate-y-[1.5px]">
<Spinner />
</span>
</>}
</p>
<p>
AI RESPONSE 01: {aiTags.three} {isLoadingAi.three && <>
<span className="inline-flex translate-y-[1.5px]">
<Spinner />
</span>