| import { createGoogleGenerativeAI } from "@ai-sdk/google"; |
| import { generateObject } from "ai"; |
| import { documentClassifierPrompt, imageClassifierPrompt } from "../prompt"; |
| import { documentClassifierSchema, imageClassifierSchema } from "../schema"; |
| import type { |
| DocumentClassifierImageRequest, |
| DocumentClassifierRequest, |
| } from "../types"; |
|
|
| const GOOGLE_API_KEY = process.env.GOOGLE_GENERATIVE_AI_API_KEY!; |
|
|
| const google = createGoogleGenerativeAI({ |
| apiKey: GOOGLE_API_KEY, |
| }); |
|
|
| export class DocumentClassifier { |
| async #processDocument({ content }: DocumentClassifierRequest) { |
| const result = await generateObject({ |
| model: google("gemini-3-flash-preview"), |
| schema: documentClassifierSchema, |
| temperature: 0.1, |
| messages: [ |
| { |
| role: "system", |
| content: documentClassifierPrompt, |
| }, |
| { |
| role: "user", |
| content, |
| }, |
| ], |
| }); |
|
|
| |
| if (!result.object.title) { |
| const retryPrompt = `${documentClassifierPrompt} |
| |
| CRITICAL: The previous attempt returned a null title, which is not acceptable. You MUST provide a title. Even if the document is unclear, construct a descriptive title from available information. Examples of acceptable titles even for unclear documents: |
| - "Business Document - [Date if available]" |
| - "Invoice from [Company Name if visible]" |
| - "Receipt from [Store Name if visible]" |
| - "Contract Document - [Date if available]" |
| Never return null for the title field.`; |
|
|
| const retryResult = await generateObject({ |
| model: google("gemini-3-flash-preview"), |
| schema: documentClassifierSchema, |
| temperature: 0.1, |
| messages: [ |
| { |
| role: "system", |
| content: retryPrompt, |
| }, |
| { |
| role: "user", |
| content, |
| }, |
| ], |
| }); |
|
|
| return retryResult.object; |
| } |
|
|
| return result.object; |
| } |
|
|
| async #processImage(request: DocumentClassifierImageRequest) { |
| const result = await generateObject({ |
| model: google("gemini-3-flash-preview"), |
| schema: imageClassifierSchema, |
| temperature: 0.1, |
| messages: [ |
| { |
| role: "system", |
| content: imageClassifierPrompt, |
| }, |
| { |
| role: "user", |
| content: [ |
| { |
| type: "image", |
| image: request.content, |
| }, |
| ], |
| }, |
| ], |
| }); |
|
|
| |
| if (!result.object.title) { |
| const retryPrompt = `${imageClassifierPrompt} |
| |
| CRITICAL: The previous attempt returned a null title, which is not acceptable. You MUST provide a title. Even if the image is unclear, construct a descriptive title from visible information. Use OCR to extract text if needed. Examples of acceptable titles even for unclear images: |
| - "Receipt from [Store Name if visible] - [Date if visible]" |
| - "Invoice from [Company Name if visible]" |
| - "Business Document Image - [Date if visible]" |
| - "Product Photo - [Product Name if visible]" |
| Never return null for the title field.`; |
|
|
| const retryResult = await generateObject({ |
| model: google("gemini-3-flash-preview"), |
| schema: imageClassifierSchema, |
| temperature: 0.1, |
| messages: [ |
| { |
| role: "system", |
| content: retryPrompt, |
| }, |
| { |
| role: "user", |
| content: [ |
| { |
| type: "image", |
| image: request.content, |
| }, |
| ], |
| }, |
| ], |
| }); |
|
|
| return retryResult.object; |
| } |
|
|
| return result.object; |
| } |
|
|
| public async classifyDocument(request: DocumentClassifierRequest) { |
| const result = await this.#processDocument(request); |
|
|
| return result; |
| } |
|
|
| public async classifyImage(request: DocumentClassifierImageRequest) { |
| const result = await this.#processImage(request); |
|
|
| return result; |
| } |
| } |
|
|