chore: bump package versions + fix linter

This commit is contained in:
Vasily Zubarev
2025-07-23 10:34:49 +02:00
parent dee915ffd6
commit 8897134901
6 changed files with 98 additions and 95 deletions

View File

@@ -1,9 +1,9 @@
"use server" "use server"
import { ActionState } from "@/lib/actions" import { ActionState } from "@/lib/actions"
import { AnalyzeAttachment } from "./attachments"
import { updateFile } from "@/models/files" import { updateFile } from "@/models/files"
import { getSettings, getLLMSettings } from "@/models/settings" import { getLLMSettings, getSettings } from "@/models/settings"
import { AnalyzeAttachment } from "./attachments"
import { requestLLM } from "./providers/llmProvider" import { requestLLM } from "./providers/llmProvider"
export type AnalysisResult = { export type AnalysisResult = {
@@ -18,7 +18,6 @@ export async function analyzeTransaction(
fileId: string, fileId: string,
userId: string userId: string
): Promise<ActionState<AnalysisResult>> { ): Promise<ActionState<AnalysisResult>> {
const settings = await getSettings(userId) const settings = await getSettings(userId)
const llmSettings = getLLMSettings(settings) const llmSettings = getLLMSettings(settings)
@@ -45,7 +44,7 @@ export async function analyzeTransaction(
success: true, success: true,
data: { data: {
output: result, output: result,
tokensUsed: tokensUsed tokensUsed: tokensUsed,
}, },
} }
} catch (error) { } catch (error) {

View File

@@ -30,79 +30,76 @@ export interface LLMResponse {
async function requestLLMUnified(config: LLMConfig, req: LLMRequest): Promise<LLMResponse> { async function requestLLMUnified(config: LLMConfig, req: LLMRequest): Promise<LLMResponse> {
try { try {
const temperature = 0; const temperature = 0
let model: any; let model: any
if (config.provider === "openai") { if (config.provider === "openai") {
model = new ChatOpenAI({ model = new ChatOpenAI({
apiKey: config.apiKey, apiKey: config.apiKey,
model: config.model, model: config.model,
temperature: temperature, temperature: temperature,
}); })
} else if (config.provider === "google") { } else if (config.provider === "google") {
model = new ChatGoogleGenerativeAI({ model = new ChatGoogleGenerativeAI({
apiKey: config.apiKey, apiKey: config.apiKey,
model: config.model, model: config.model,
temperature: temperature, temperature: temperature,
}); })
} else if (config.provider === "mistral") { } else if (config.provider === "mistral") {
model = new ChatMistralAI({ model = new ChatMistralAI({
apiKey: config.apiKey, apiKey: config.apiKey,
model: config.model, model: config.model,
temperature: temperature, temperature: temperature,
}); })
} else { } else {
return { return {
output: {}, output: {},
provider: config.provider, provider: config.provider,
error: "Unknown provider", error: "Unknown provider",
}; }
} }
const structuredModel = model.withStructuredOutput(req.schema, { 'name': 'transaction'}); const structuredModel = model.withStructuredOutput(req.schema, { name: "transaction" })
let message_content: any = [{ type: "text", text: req.prompt }]; let message_content: any = [{ type: "text", text: req.prompt }]
if (req.attachments && req.attachments.length > 0) { if (req.attachments && req.attachments.length > 0) {
const images = req.attachments.map(att => ({ const images = req.attachments.map((att) => ({
type: "image_url", type: "image_url",
image_url: { image_url: {
url: `data:${att.contentType};base64,${att.base64}` url: `data:${att.contentType};base64,${att.base64}`,
}, },
})); }))
message_content.push(...images); message_content.push(...images)
} }
const messages: BaseMessage[] = [ const messages: BaseMessage[] = [new HumanMessage({ content: message_content })]
new HumanMessage({ content: message_content })
];
const response = await structuredModel.invoke(messages); const response = await structuredModel.invoke(messages)
return { return {
output: response, output: response,
provider: config.provider, provider: config.provider,
}; }
} catch (error: any) { } catch (error: any) {
return { return {
output: {}, output: {},
provider: config.provider, provider: config.provider,
error: error instanceof Error ? error.message : `${config.provider} request failed`, error: error instanceof Error ? error.message : `${config.provider} request failed`,
}; }
} }
} }
export async function requestLLM(settings: LLMSettings, req: LLMRequest): Promise<LLMResponse> { export async function requestLLM(settings: LLMSettings, req: LLMRequest): Promise<LLMResponse> {
for (const config of settings.providers) { for (const config of settings.providers) {
if (!config.apiKey || !config.model) { if (!config.apiKey || !config.model) {
console.info('Skipping provider:', config.provider); console.info("Skipping provider:", config.provider)
continue; continue
} }
console.info('Use provider:', config.provider); console.info("Use provider:", config.provider)
const response = await requestLLMUnified(config, req); const response = await requestLLMUnified(config, req)
if (!response.error) { if (!response.error) {
return response; return response
} } else {
else {
console.error(response.error) console.error(response.error)
} }
} }
@@ -111,5 +108,5 @@ export async function requestLLM(settings: LLMSettings, req: LLMRequest): Promis
output: {}, output: {},
provider: settings.providers[0]?.provider || "openai", provider: settings.providers[0]?.provider || "openai",
error: "All LLM providers failed or are not configured", error: "All LLM providers failed or are not configured",
}; }
} }

View File

@@ -16,7 +16,8 @@ export const fieldsToJsonSchema = (fields: Field[]) => {
...schemaProperties, ...schemaProperties,
items: { items: {
type: "array", type: "array",
description: "Separate items, products or transactions in the file which have own name and price or sum. Find all items!", description:
"Separate items, products or transactions in the file which have own name and price or sum. Find all items!",
items: { items: {
type: "object", type: "object",
properties: schemaProperties, properties: schemaProperties,

View File

@@ -7,19 +7,22 @@ import { fieldsToJsonSchema } from "@/ai/schema"
import { transactionFormSchema } from "@/forms/transactions" import { transactionFormSchema } from "@/forms/transactions"
import { ActionState } from "@/lib/actions" import { ActionState } from "@/lib/actions"
import { getCurrentUser, isAiBalanceExhausted, isSubscriptionExpired } from "@/lib/auth" import { getCurrentUser, isAiBalanceExhausted, isSubscriptionExpired } from "@/lib/auth"
import config from "@/lib/config" import {
import { getTransactionFileUploadPath, getUserUploadsDirectory, safePathJoin, unsortedFilePath } from "@/lib/files" getDirectorySize,
getTransactionFileUploadPath,
getUserUploadsDirectory,
safePathJoin,
unsortedFilePath,
} from "@/lib/files"
import { DEFAULT_PROMPT_ANALYSE_NEW_FILE } from "@/models/defaults" import { DEFAULT_PROMPT_ANALYSE_NEW_FILE } from "@/models/defaults"
import { createFile, deleteFile, getFileById, updateFile } from "@/models/files" import { createFile, deleteFile, getFileById, updateFile } from "@/models/files"
import { createTransaction, updateTransactionFiles, TransactionData } from "@/models/transactions" import { createTransaction, TransactionData, updateTransactionFiles } from "@/models/transactions"
import { updateUser } from "@/models/users" import { updateUser } from "@/models/users"
import { Category, Field, File, Project, Transaction } from "@/prisma/client" import { Category, Field, File, Project, Transaction } from "@/prisma/client"
import { mkdir, rename } from "fs/promises" import { randomUUID } from "crypto"
import { mkdir, readFile, rename, writeFile } from "fs/promises"
import { revalidatePath } from "next/cache" import { revalidatePath } from "next/cache"
import path from "path" import path from "path"
import { randomUUID } from "crypto"
import { readFile, writeFile } from "fs/promises"
import { getDirectorySize } from "@/lib/files"
export async function analyzeFileAction( export async function analyzeFileAction(
file: File, file: File,
@@ -31,15 +34,15 @@ export async function analyzeFileAction(
const user = await getCurrentUser() const user = await getCurrentUser()
if (!file || file.userId !== user.id) { if (!file || file.userId !== user.id) {
return { success: false, error: "File not found or does not belong to the user" } return { success: false, error: "File not found or does not belong to the user" }
} }
if (isAiBalanceExhausted(user)) { if (isAiBalanceExhausted(user)) {
return { return {
success: false, success: false,
error: "You used all of your pre-paid AI scans, please upgrade your account or buy new subscription plan", error: "You used all of your pre-paid AI scans, please upgrade your account or buy new subscription plan",
}
} }
}
if (isSubscriptionExpired(user)) { if (isSubscriptionExpired(user)) {
return { return {

View File

@@ -1,11 +1,11 @@
import { Card, CardDescription, CardTitle } from "@/components/ui/card" import { Card, CardDescription, CardTitle } from "@/components/ui/card"
import { ColoredText } from "@/components/ui/colored-text" import { ColoredText } from "@/components/ui/colored-text"
import config from "@/lib/config" import config from "@/lib/config"
import { PROVIDERS } from "@/lib/llm-providers"
import { getSelfHostedUser } from "@/models/users" import { getSelfHostedUser } from "@/models/users"
import { ShieldAlert } from "lucide-react" import { ShieldAlert } from "lucide-react"
import Image from "next/image" import Image from "next/image"
import { redirect } from "next/navigation" import { redirect } from "next/navigation"
import { PROVIDERS } from "@/lib/llm-providers"
import SelfHostedSetupFormClient from "./setup-form-client" import SelfHostedSetupFormClient from "./setup-form-client"
export default async function SelfHostedWelcomePage() { export default async function SelfHostedWelcomePage() {
@@ -32,7 +32,6 @@ export default async function SelfHostedWelcomePage() {
redirect(config.selfHosted.redirectUrl) redirect(config.selfHosted.redirectUrl)
} }
// Собираем дефолтные ключи для всех провайдеров
const defaultProvider = PROVIDERS[0].key const defaultProvider = PROVIDERS[0].key
const defaultApiKeys: Record<string, string> = { const defaultApiKeys: Record<string, string> = {
openai: config.ai.openaiApiKey ?? "", openai: config.ai.openaiApiKey ?? "",

100
package-lock.json generated
View File

@@ -941,9 +941,9 @@
} }
}, },
"node_modules/@eslint/config-array": { "node_modules/@eslint/config-array": {
"version": "0.19.2", "version": "0.21.0",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
"integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -956,9 +956,9 @@
} }
}, },
"node_modules/@eslint/config-helpers": { "node_modules/@eslint/config-helpers": {
"version": "0.1.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
"integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==", "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@@ -966,9 +966,9 @@
} }
}, },
"node_modules/@eslint/core": { "node_modules/@eslint/core": {
"version": "0.12.0", "version": "0.15.1",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
"integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -979,9 +979,9 @@
} }
}, },
"node_modules/@eslint/eslintrc": { "node_modules/@eslint/eslintrc": {
"version": "3.3.0", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
"integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1003,13 +1003,16 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.22.0", "version": "9.31.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz",
"integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==", "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://eslint.org/donate"
} }
}, },
"node_modules/@eslint/object-schema": { "node_modules/@eslint/object-schema": {
@@ -1023,13 +1026,13 @@
} }
}, },
"node_modules/@eslint/plugin-kit": { "node_modules/@eslint/plugin-kit": {
"version": "0.2.7", "version": "0.3.4",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
"integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/core": "^0.12.0", "@eslint/core": "^0.15.1",
"levn": "^0.4.1" "levn": "^0.4.1"
}, },
"engines": { "engines": {
@@ -5558,9 +5561,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.14.1", "version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
@@ -7101,20 +7104,20 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.22.0", "version": "9.31.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz",
"integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==", "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.19.2", "@eslint/config-array": "^0.21.0",
"@eslint/config-helpers": "^0.1.0", "@eslint/config-helpers": "^0.3.0",
"@eslint/core": "^0.12.0", "@eslint/core": "^0.15.0",
"@eslint/eslintrc": "^3.3.0", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.22.0", "@eslint/js": "9.31.0",
"@eslint/plugin-kit": "^0.2.7", "@eslint/plugin-kit": "^0.3.1",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2", "@humanwhocodes/retry": "^0.4.2",
@@ -7125,9 +7128,9 @@
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.6",
"debug": "^4.3.2", "debug": "^4.3.2",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.3.0", "eslint-scope": "^8.4.0",
"eslint-visitor-keys": "^4.2.0", "eslint-visitor-keys": "^4.2.1",
"espree": "^10.3.0", "espree": "^10.4.0",
"esquery": "^1.5.0", "esquery": "^1.5.0",
"esutils": "^2.0.2", "esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
@@ -7433,9 +7436,9 @@
} }
}, },
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "8.3.0", "version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
@@ -7450,9 +7453,9 @@
} }
}, },
"node_modules/eslint-visitor-keys": { "node_modules/eslint-visitor-keys": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@@ -7463,15 +7466,15 @@
} }
}, },
"node_modules/espree": { "node_modules/espree": {
"version": "10.3.0", "version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"acorn": "^8.14.0", "acorn": "^8.15.0",
"acorn-jsx": "^5.3.2", "acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -7744,9 +7747,9 @@
} }
}, },
"node_modules/form-data": { "node_modules/form-data": {
"version": "4.0.2", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true, "peer": true,
@@ -7754,6 +7757,7 @@
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0", "es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
}, },
"engines": { "engines": {