@accounter/server
Version:
Accounter GraphQL server
128 lines (126 loc) • 4.83 kB
JavaScript
import { __decorate } from "tslib";
import { generateText, Output } from 'ai';
import { Injectable, Scope } from 'graphql-modules';
import stripIndent from 'strip-indent';
import { z } from 'zod';
import { anthropic } from '@ai-sdk/anthropic';
import { Currency, DocumentType } from '../../shared/enums.js';
const documentDataSchema = z.object({
type: z.enum(DocumentType).nullable().optional().describe('The type of financial document'),
issuer: z.string().optional().describe('Legal name of the organization that issued the document'),
recipient: z
.string()
.nullable()
.optional()
.describe('Legal name and details of the entity to whom the document is addressed'),
fullAmount: z
.number()
.nullable()
.optional()
.describe('Total monetary amount including taxes and all charges'),
currency: z.enum(Currency).optional().describe('ISO 4217 currency code'),
vatAmount: z
.number()
.nullable()
.optional()
.describe('Value Added Tax amount if separately specified on the document'),
date: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.nullable()
.optional()
.describe('Document issue date in ISO 8601 format (YYYY-MM-DD)'),
referenceCode: z
.string()
.nullable()
.optional()
.describe('Complete document identifier including any separators (e.g., dashes, slashes)'),
allocationNumber: z
.string()
.length(9)
.nullable()
.optional()
.describe('Should be empty if no VAT amount. Unique document 9-digits allocation number (מספר הקצאה). Usually last 9 digits of a longer number.'),
description: z
.string()
.nullable()
.optional()
.describe('Additional description or remarks found on the document'),
});
const SUPPORTED_FILE_TYPES = [
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'application/pdf',
];
function isSupportedFileType(value) {
return SUPPORTED_FILE_TYPES.includes(value);
}
let AnthropicProvider = class AnthropicProvider {
/**
* Convert File or Blob to Base64
* @param fileOrBlob File or Blob to convert
* @returns Base64 encoded string
*/
async fileToBase64(fileOrBlob) {
const buffer = await fileOrBlob.arrayBuffer();
const base64string = Buffer.from(buffer).toString('base64');
return base64string;
}
/**
* Extract invoice details using Anthropic API
* @param fileOrBlob File or Blob to process
* @returns Parsed invoice data
*/
async extractInvoiceDetails(fileOrBlob) {
const fileType = fileOrBlob.type.toLowerCase();
if (!isSupportedFileType(fileType)) {
throw new Error('Unsupported file type. Please provide an image or PDF.');
}
const fileData = await this.fileToBase64(fileOrBlob);
const { output } = await generateText({
model: anthropic('claude-sonnet-4-5'),
output: Output.object({ schema: documentDataSchema }),
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: stripIndent(`Please analyze the provided document and extract:
- Document type
- Issuer and recipient details
- Monetary amounts (total and VAT)
- Date and reference numbers
- Allocation number (if VAT exists and applicable)
- Description or remarks
Note that some receipts (e.g. by Stripe) carry the invoice details; pay extra attention not to misclassify them as INVOICE_RECEIPT.
Return only a JSON object without any explanation. Use NULL value for missing values, allocation number is optional.`),
},
fileType === 'application/pdf'
? {
type: 'file',
data: fileData,
mediaType: fileType,
}
: {
type: 'image',
image: fileData,
mediaType: fileType,
},
],
},
],
});
return output;
}
};
AnthropicProvider = __decorate([
Injectable({
scope: Scope.Singleton,
global: true,
})
], AnthropicProvider);
export { AnthropicProvider };
//# sourceMappingURL=anthropic.js.map