UNPKG

actual-moneymoney

Version:

An importer for syncing MoneyMoney accounts and transactions to Actual.

120 lines (114 loc) 5.61 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import OpenAI from 'openai'; class PayeeTransformer { constructor(config, logger) { this.config = config; this.logger = logger; if (!config.openAiApiKey) { throw new Error('An OpenAPI API key is required for payee transformation. Please set the key in the configuration file.'); } this.openai = new OpenAI({ apiKey: config.openAiApiKey, }); } transformPayees(payeeList) { return __awaiter(this, void 0, void 0, function* () { var _a; const prompt = this.generatePrompt(); if (payeeList.length === 0) { this.logger.debug('No payees to transform. Returning empty object.'); return {}; } try { this.logger.debug(`Starting payee transformation...`, [ `Payees: ${payeeList.length}`, `Model: ${this.config.openAiModel}`, ]); // Validate model before proceeding const model = yield this.getConfiguredModel(); const response = yield this.openai.chat.completions.create({ model, messages: [ { role: 'system', content: prompt }, { role: 'user', content: payeeList.join('\n') }, ], response_format: { type: 'json_object', }, temperature: 0, }); const output = (_a = response.choices[0].message) === null || _a === void 0 ? void 0 : _a.content; try { return JSON.parse(output); } catch (parseError) { this.logger.error(`Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : 'Unknown error'}`); this.logger.debug(`Raw response: ${output}`); return null; } } catch (error) { if (error instanceof Error) { this.logger.error(`Error in payee transformation: ${error.message}`); } return null; } }); } getConfiguredModel() { return __awaiter(this, void 0, void 0, function* () { let availableModels; if (PayeeTransformer.availableModels) { this.logger.debug('Found available models in cache.'); availableModels = PayeeTransformer.availableModels; } else { this.logger.debug('Listing available models...'); const modelsIterator = yield this.openai.models.list(); availableModels = (yield Array.fromAsync(modelsIterator)).map((m) => m.id); PayeeTransformer.availableModels = availableModels; } this.logger.debug(`Found ${availableModels.length} available models.`); if (!availableModels.includes(this.config.openAiModel)) { this.logger.error(`The specified model '${this.config.openAiModel}' is invalid. The following models are available:`, availableModels); throw new Error('Invalid OpenAI model specified.'); } return this.config.openAiModel; }); } generatePrompt() { var _a; if ((_a = this.config.prompt) === null || _a === void 0 ? void 0 : _a.trim()) { return this.config.prompt; } return ` You are now a model trained to classify bank account transactions. You will receive a list of payees as they appear in the source transactions, and you will return a cleaned up, human-readable version of the payees. Return a JSON formatted object, where the old payee names are mapped to the new ones. Make sure the new payee names are clear and concise, and omit any unnecessary details. For example, if the payee is "Amazon.com", you should return "Amazon". If the payee is "AMAZON.COM/BILLWA", you should also return "Amazon". You are free to make some assumptions about the payees. If you don't know the payee, return "Unknown". If for some reason you cannot create a JSON object, return {}. Examples: Input: - Output: {} Input: Amzn Mktp US*1234567890 Output: { "Amzn Mktp US*1234567890": "Amazon" } Input: AMAZON.COM/BILLWA\nAMAZON.COM Output: { "AMAZON.COM/BILLWA": "Amazon", "AMAZON.COM": "Amazon" } If there is no list, return an empty object. Do not under any circumstances return anything that is not valid JSON. `; } } PayeeTransformer.availableModels = null; export default PayeeTransformer;