Automatic category from expense title (#80)

* environment variable

* random category draft

* get category from ai

* input limit and documentation

* use watch

* use field.name

* prettier

* presigned upload, readme warning, category to string util

* prettier

* check whether feature is enabled

* use process.env

* improved prompt to return id only

* remove console.debug

* show loader

* share class name

* prettier

* use template literals

* rename format util

* prettier
This commit is contained in:
Mert Demir
2024-02-05 02:23:11 +09:00
committed by GitHub
parent 10fd69404a
commit fb49fb596a
8 changed files with 136 additions and 15 deletions

View File

@@ -1,7 +1,9 @@
'use server'
import { getCategories } from '@/lib/api'
import { env } from '@/lib/env'
import { formatCategoryForAIPrompt } from '@/lib/utils'
import OpenAI from 'openai'
import { ChatCompletionCreateParamsNonStreaming } from 'openai/resources/index.mjs'
const openai = new OpenAI({ apiKey: env.OPENAI_API_KEY })
@@ -9,7 +11,7 @@ export async function extractExpenseInformationFromImage(imageUrl: string) {
'use server'
const categories = await getCategories()
const body = {
const body: ChatCompletionCreateParamsNonStreaming = {
model: 'gpt-4-vision-preview',
messages: [
{
@@ -21,7 +23,7 @@ export async function extractExpenseInformationFromImage(imageUrl: string) {
This image contains a receipt.
Read the total amount and store it as a non-formatted number without any other text or currency.
Then guess the category for this receipt amoung the following categories and store its ID: ${categories.map(
({ id, grouping, name }) => `"${grouping}/${name}" (ID: ${id})`,
(category) => formatCategoryForAIPrompt(category),
)}.
Guess the expenses date and store it as yyyy-mm-dd.
Guess a title for the expense.
@@ -35,7 +37,7 @@ export async function extractExpenseInformationFromImage(imageUrl: string) {
},
],
}
const completion = await openai.chat.completions.create(body as any)
const completion = await openai.chat.completions.create(body)
const [amountString, categoryId, date, title] = completion.choices
.at(0)

View File

@@ -29,7 +29,7 @@ import { useMediaQuery } from '@/lib/hooks'
import { formatExpenseDate } from '@/lib/utils'
import { Category } from '@prisma/client'
import { ChevronRight, FileQuestion, Loader2, Receipt } from 'lucide-react'
import { getImageData, useS3Upload } from 'next-s3-upload'
import { getImageData, usePresignedUpload } from 'next-s3-upload'
import Image from 'next/image'
import { useRouter } from 'next/navigation'
import { PropsWithChildren, ReactNode, useState } from 'react'
@@ -46,7 +46,7 @@ export function CreateFromReceiptButton({
categories,
}: Props) {
const [pending, setPending] = useState(false)
const { uploadToS3, FileInput, openFileDialog } = useS3Upload()
const { uploadToS3, FileInput, openFileDialog } = usePresignedUpload()
const { toast } = useToast()
const router = useRouter()
const [receiptInfo, setReceiptInfo] = useState<