90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
/* eslint-disable max-len */
|
|
|
|
import { Tags } from '@/tag';
|
|
|
|
export type AiAutoGeneratedField =
|
|
'title' |
|
|
'caption' |
|
|
'tags' |
|
|
'semantic'
|
|
|
|
export const AI_AUTO_GENERATED_FIELDS_ALL: AiAutoGeneratedField[] = [
|
|
'title',
|
|
'caption',
|
|
'tags',
|
|
'semantic',
|
|
];
|
|
|
|
export const AI_AUTO_GENERATED_FIELDS_DEFAULT: AiAutoGeneratedField[] = [
|
|
'title',
|
|
'tags',
|
|
'semantic',
|
|
];
|
|
|
|
export const parseAiAutoGeneratedFieldsString = (
|
|
text = AI_AUTO_GENERATED_FIELDS_DEFAULT.join(','),
|
|
): AiAutoGeneratedField[] => {
|
|
const textFormatted = text.trim().toLocaleLowerCase();
|
|
if (textFormatted === 'none') {
|
|
return [];
|
|
} else if (textFormatted === 'all') {
|
|
return AI_AUTO_GENERATED_FIELDS_ALL;
|
|
} else {
|
|
const fields = textFormatted
|
|
.toLocaleLowerCase()
|
|
.split(',')
|
|
.map(field => field.trim())
|
|
.filter(field => AI_AUTO_GENERATED_FIELDS_ALL
|
|
.includes(field as AiAutoGeneratedField));
|
|
return fields as AiAutoGeneratedField[];
|
|
}
|
|
};
|
|
|
|
export type AiImageQuery =
|
|
'title' |
|
|
'caption' |
|
|
'title-and-caption' |
|
|
'tags' |
|
|
'description-small' |
|
|
'description' |
|
|
'description-large' |
|
|
'description-semantic';
|
|
|
|
export const getAiImageQuery = (
|
|
query: AiImageQuery,
|
|
existingTags: Tags = [],
|
|
): string => {
|
|
switch (query) {
|
|
case 'title': return 'Write a compelling title for this image in 3 words or less';
|
|
case 'caption': return 'Write a pithy caption for this image in 6 words or less and no punctuation';
|
|
case 'title-and-caption': return 'Write a compelling title and pithy caption of 8 words or less for this image, using the format Title: "title" Caption: "caption"';
|
|
case 'tags':
|
|
const tagQuery = 'Describe this image in 1-2 comma-separated unique keywords, with no adjective or adverbs. Avoid using general terms like "nature," "travel," "architecture," or "sky." Use terms that are highly specific to the image and not redundant.';
|
|
const tags = existingTags.map(({ tag }) => tag).join(', ');
|
|
return tags
|
|
? `${tagQuery}. Consider using some of these existing tags, but only if they are relevant: ${tags}.`
|
|
: tagQuery;
|
|
case 'description-small': return 'Describe this image succinctly without the initial text "This image shows" or "This is a picture of"';
|
|
case 'description': return 'Describe this image';
|
|
case 'description-large': return 'Describe this image in detail';
|
|
case 'description-semantic': return 'List up to 5 things in this image without description as a comma-separated list';
|
|
}
|
|
};
|
|
|
|
export const parseTitleAndCaption = (text: string) => {
|
|
const matches = text.includes('Title')
|
|
? text.match(/^[`'"]*Title: ["']*(.*?)["']*[ ]*Caption: ["']*(.*?)\.*["']*[`'"]*$/)
|
|
: text.match(/^(.*?): (.*?)$/);
|
|
|
|
return {
|
|
title: matches?.[1] ?? '',
|
|
caption: matches?.[2] ?? '',
|
|
};
|
|
};
|
|
|
|
export const cleanUpAiTextResponse = (text: string) =>
|
|
text
|
|
.replaceAll('\n', ' ')
|
|
.replaceAll('"', '')
|
|
.replace(/\.$/, '');
|