From efc0b1bf5a498f3d1206769976fd3d6a80964025 Mon Sep 17 00:00:00 2001 From: csehatt741 <77381875+csehatt741@users.noreply.github.com> Date: Tue, 18 Mar 2025 20:13:24 +0100 Subject: [PATCH] Feature/support filters in AI prompt API (#4431) * Support filters in AI prompt API * Update changelog --- CHANGELOG.md | 4 +++ .../api/src/app/endpoints/ai/ai.controller.ts | 27 +++++++++++++++++-- apps/api/src/app/endpoints/ai/ai.module.ts | 2 ++ apps/api/src/app/endpoints/ai/ai.service.ts | 4 +++ .../analysis/analysis-page.component.ts | 5 +++- apps/client/src/app/services/data.service.ts | 14 ++++++++-- 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6651ff32e..ea12f2ca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added support for filtering in the _Copy AI prompt to clipboard_ actions on the analysis page (experimental) + ### Changed - Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) diff --git a/apps/api/src/app/endpoints/ai/ai.controller.ts b/apps/api/src/app/endpoints/ai/ai.controller.ts index 910abbf96..980d5607c 100644 --- a/apps/api/src/app/endpoints/ai/ai.controller.ts +++ b/apps/api/src/app/endpoints/ai/ai.controller.ts @@ -1,5 +1,6 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { DEFAULT_CURRENCY, DEFAULT_LANGUAGE_CODE @@ -8,7 +9,14 @@ import { AiPromptResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import type { AiPromptMode, RequestWithUser } from '@ghostfolio/common/types'; -import { Controller, Get, Inject, Param, UseGuards } from '@nestjs/common'; +import { + Controller, + Get, + Inject, + Param, + Query, + UseGuards +} from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; @@ -18,6 +26,7 @@ import { AiService } from './ai.service'; export class AiController { public constructor( private readonly aiService: AiService, + private readonly apiService: ApiService, @Inject(REQUEST) private readonly request: RequestWithUser ) {} @@ -25,9 +34,23 @@ export class AiController { @HasPermission(permissions.readAiPrompt) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async getPrompt( - @Param('mode') mode: AiPromptMode + @Param('mode') mode: AiPromptMode, + @Query('accounts') filterByAccounts?: string, + @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, + @Query('symbol') filterBySymbol?: string, + @Query('tags') filterByTags?: string ): Promise { + const filters = this.apiService.buildFiltersFromQueryParams({ + filterByAccounts, + filterByAssetClasses, + filterByDataSource, + filterBySymbol, + filterByTags + }); + const prompt = await this.aiService.getPrompt({ + filters, mode, impersonationId: undefined, languageCode: diff --git a/apps/api/src/app/endpoints/ai/ai.module.ts b/apps/api/src/app/endpoints/ai/ai.module.ts index 5a30f3264..584f29956 100644 --- a/apps/api/src/app/endpoints/ai/ai.module.ts +++ b/apps/api/src/app/endpoints/ai/ai.module.ts @@ -7,6 +7,7 @@ import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.servic import { RulesService } from '@ghostfolio/api/app/portfolio/rules.service'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; +import { ApiModule } from '@ghostfolio/api/services/api/api.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; @@ -25,6 +26,7 @@ import { AiService } from './ai.service'; @Module({ controllers: [AiController], imports: [ + ApiModule, ConfigurationModule, DataProviderModule, ExchangeRateDataModule, diff --git a/apps/api/src/app/endpoints/ai/ai.service.ts b/apps/api/src/app/endpoints/ai/ai.service.ts index d9090d77c..8807e67bf 100644 --- a/apps/api/src/app/endpoints/ai/ai.service.ts +++ b/apps/api/src/app/endpoints/ai/ai.service.ts @@ -1,4 +1,5 @@ import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; +import { Filter } from '@ghostfolio/common/interfaces'; import type { AiPromptMode } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; @@ -8,12 +9,14 @@ export class AiService { public constructor(private readonly portfolioService: PortfolioService) {} public async getPrompt({ + filters, impersonationId, languageCode, mode, userCurrency, userId }: { + filters?: Filter[]; impersonationId: string; languageCode: string; mode: AiPromptMode; @@ -21,6 +24,7 @@ export class AiService { userId: string; }) { const { holdings } = await this.portfolioService.getDetails({ + filters, impersonationId, userId }); diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 4aee450e6..2bd3096d4 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -161,7 +161,10 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { } this.dataService - .fetchPrompt(mode) + .fetchPrompt({ + mode, + filters: this.userService.getFilters() + }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ prompt }) => { this.clipboard.copy(prompt); diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 76f9c4867..4eba3fffa 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -653,8 +653,18 @@ export class DataService { return this.http.get('/api/v1/portfolio/report'); } - public fetchPrompt(mode: AiPromptMode) { - return this.http.get(`/api/v1/ai/prompt/${mode}`); + public fetchPrompt({ + filters, + mode + }: { + filters?: Filter[]; + mode: AiPromptMode; + }) { + const params = this.buildFiltersAsQueryParams({ filters }); + + return this.http.get(`/api/v1/ai/prompt/${mode}`, { + params + }); } public fetchPublicPortfolio(aAccessId: string) {