import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PortfolioTableService } from '@ghostfolio/api/services/portfolio-table/portfolio-table.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PROPERTY_API_KEY_OPENROUTER, PROPERTY_OPENROUTER_MODEL } from '@ghostfolio/common/config'; import { Filter } from '@ghostfolio/common/interfaces'; import type { AiPromptMode } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; import { createOpenRouter } from '@openrouter/ai-sdk-provider'; import { generateText } from 'ai'; @Injectable() export class AiService { public constructor( private readonly configurationService: ConfigurationService, private readonly portfolioTableService: PortfolioTableService, private readonly propertyService: PropertyService ) {} public async generateText({ prompt, requestTimeout = this.configurationService.get('REQUEST_TIMEOUT') }: { prompt: string; requestTimeout?: number; }) { const openRouterApiKey = await this.propertyService.getByKey( PROPERTY_API_KEY_OPENROUTER ); const openRouterModel = await this.propertyService.getByKey( PROPERTY_OPENROUTER_MODEL ); const openRouterService = createOpenRouter({ apiKey: openRouterApiKey }); return generateText({ prompt, model: openRouterService.chat(openRouterModel), timeout: requestTimeout }); } public async getPrompt({ filters, languageCode, mode, userCurrency, userId }: { filters?: Filter[]; languageCode: string; mode: AiPromptMode; userCurrency: string; userId: string; }) { const holdingsSection = await this.portfolioTableService.getHoldingsTable({ filters, languageCode, userId }); if (mode === 'portfolio') { return holdingsSection; } return [ `You are a neutral financial assistant. Please analyze the following investment portfolio (base currency being ${userCurrency}) in simple words.`, holdingsSection, 'Structure your answer with these sections:', 'Overview: Briefly summarize the portfolio’s composition and allocation rationale.', 'Risk Assessment: Identify potential risks, including market volatility, concentration, and sectoral imbalances.', 'Advantages: Highlight strengths, focusing on growth potential, diversification, or other benefits.', 'Disadvantages: Point out weaknesses, such as overexposure or lack of defensive assets.', 'Target Group: Discuss who this portfolio might suit (e.g., risk tolerance, investment goals, life stages, and experience levels).', 'Optimization Ideas: Offer ideas to complement the portfolio, ensuring they are constructive and neutral in tone.', 'Conclusion: Provide a concise summary highlighting key insights.', `Provide your answer in the following language: ${languageCode}.` ].join('\n'); } }