91 lines
2.8 KiB
JavaScript
91 lines
2.8 KiB
JavaScript
import {
|
|
buildFinanceChatMessages,
|
|
buildTransactionEnrichmentMessages,
|
|
} from '../../lib/aiPrompt.js'
|
|
import { useSettingsStore } from '../../stores/settings.js'
|
|
import {
|
|
applyAiEnrichment,
|
|
fetchRecentTransactionsForAi,
|
|
markTransactionAiFailed,
|
|
markTransactionAiRunning,
|
|
} from '../transactionService.js'
|
|
import { DeepSeekProvider } from './deepseekProvider.js'
|
|
|
|
const clampThreshold = (value) => {
|
|
const numeric = Number(value)
|
|
if (!Number.isFinite(numeric)) return 0.9
|
|
return Math.min(Math.max(numeric, 0), 1)
|
|
}
|
|
|
|
const createProvider = (settingsStore) => {
|
|
if (settingsStore.aiProvider !== 'deepseek') {
|
|
throw new Error(`Unsupported AI provider: ${settingsStore.aiProvider}`)
|
|
}
|
|
|
|
return new DeepSeekProvider({
|
|
apiKey: settingsStore.aiApiKey,
|
|
model: settingsStore.aiModel,
|
|
})
|
|
}
|
|
|
|
export const hasAiAccess = (settingsStore = useSettingsStore()) => !!settingsStore.aiApiKey
|
|
|
|
export const isAiReady = (settingsStore = useSettingsStore()) =>
|
|
!!settingsStore.aiAutoCategoryEnabled && hasAiAccess(settingsStore)
|
|
|
|
export const maybeEnrichTransactionWithAi = async (transaction, options = {}) => {
|
|
if (!transaction?.id || transaction.entryType === 'transfer') return transaction
|
|
|
|
const settingsStore = useSettingsStore()
|
|
if (!isAiReady(settingsStore)) {
|
|
return transaction
|
|
}
|
|
|
|
try {
|
|
const running = await markTransactionAiRunning(transaction.id, settingsStore.aiModel)
|
|
if (running) {
|
|
options.onProgress?.(running)
|
|
}
|
|
|
|
const provider = createProvider(settingsStore)
|
|
const similarTransactions = await fetchRecentTransactionsForAi(6, transaction.id)
|
|
const messages = buildTransactionEnrichmentMessages({
|
|
transaction,
|
|
similarTransactions: similarTransactions.filter((item) => item.entryType !== 'transfer'),
|
|
})
|
|
const result = await provider.enrichTransaction({ messages })
|
|
const threshold = clampThreshold(settingsStore.aiAutoApplyThreshold)
|
|
|
|
const updated = await applyAiEnrichment(transaction.id, result.normalized, {
|
|
applyCategory: result.normalized.confidence >= threshold,
|
|
model: result.model,
|
|
})
|
|
|
|
return updated || transaction
|
|
} catch (error) {
|
|
console.warn('[ai] transaction enrichment failed', error)
|
|
const failed = await markTransactionAiFailed(
|
|
transaction.id,
|
|
error?.message || 'AI enrichment failed',
|
|
settingsStore.aiModel,
|
|
)
|
|
return failed || transaction
|
|
}
|
|
}
|
|
|
|
export const askFinanceAssistant = async ({ question, transactions = [], conversation = [] }) => {
|
|
const settingsStore = useSettingsStore()
|
|
if (!hasAiAccess(settingsStore)) {
|
|
throw new Error('请先在设置页填写 DeepSeek API Key')
|
|
}
|
|
|
|
const provider = createProvider(settingsStore)
|
|
const messages = buildFinanceChatMessages({
|
|
question,
|
|
transactions,
|
|
conversation,
|
|
})
|
|
|
|
return provider.chat({ messages })
|
|
}
|