Browse Source

Refactoring

pull/5025/head
Thomas Kaul 3 days ago
parent
commit
5240200b18
  1. 40
      apps/api/src/app/endpoints/ai/ai.controller.ts
  2. 2
      apps/api/src/app/endpoints/ai/ai.module.ts
  3. 43
      apps/api/src/app/endpoints/ai/ai.service.ts
  4. 3
      apps/api/src/services/data-provider/data-provider.module.ts
  5. 1
      libs/common/src/lib/config.ts
  6. 6
      package-lock.json

40
apps/api/src/app/endpoints/ai/ai.controller.ts

@ -10,14 +10,11 @@ import {
Get, Get,
Inject, Inject,
Param, Param,
Post,
Query, Query,
Res,
UseGuards UseGuards
} from '@nestjs/common'; } from '@nestjs/common';
import { REQUEST } from '@nestjs/core'; import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { Response } from 'express';
import { AiService } from './ai.service'; import { AiService } from './ai.service';
@ -29,43 +26,6 @@ export class AiController {
@Inject(REQUEST) private readonly request: RequestWithUser @Inject(REQUEST) private readonly request: RequestWithUser
) {} ) {}
@Post('completion')
@HasPermission(permissions.readAiPrompt)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
public async getCompletion(
@Res() response: Response,
@Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string,
@Query('dataSource') filterByDataSource?: string,
@Query('symbol') filterBySymbol?: string,
@Query('tags') filterByTags?: string
) {
const filters = this.apiService.buildFiltersFromQueryParams({
filterByAccounts,
filterByAssetClasses,
filterByDataSource,
filterBySymbol,
filterByTags
});
const result = await this.aiService.getCompletion({
filters,
mode: 'analysis',
impersonationId: undefined,
languageCode: this.request.user.Settings.settings.language,
userCurrency: this.request.user.Settings.settings.baseCurrency,
userId: this.request.user.id
});
response.setHeader('Content-Type', 'text/plain');
for await (const chunk of result.textStream) {
response.write(chunk);
}
response.end();
}
@Get('prompt/:mode') @Get('prompt/:mode')
@HasPermission(permissions.readAiPrompt) @HasPermission(permissions.readAiPrompt)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseGuards(AuthGuard('jwt'), HasPermissionGuard)

2
apps/api/src/app/endpoints/ai/ai.module.ts

@ -17,6 +17,7 @@ import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impe
import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
@ -39,6 +40,7 @@ import { AiService } from './ai.service';
OrderModule, OrderModule,
PortfolioSnapshotQueueModule, PortfolioSnapshotQueueModule,
PrismaModule, PrismaModule,
PropertyModule,
RedisCacheModule, RedisCacheModule,
SymbolProfileModule, SymbolProfileModule,
UserModule UserModule

43
apps/api/src/app/endpoints/ai/ai.service.ts

@ -9,7 +9,7 @@ import type { AiPromptMode } from '@ghostfolio/common/types';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { createOpenRouter } from '@openrouter/ai-sdk-provider'; import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { streamText } from 'ai'; import { generateText } from 'ai';
@Injectable() @Injectable()
export class AiService { export class AiService {
@ -18,45 +18,22 @@ export class AiService {
private readonly propertyService: PropertyService private readonly propertyService: PropertyService
) {} ) {}
public async getCompletion({ public async generateText({ prompt }: { prompt: string }) {
filters, const openRouterApiKey = (await this.propertyService.getByKey(
impersonationId,
languageCode,
mode,
userCurrency,
userId
}: {
filters?: Filter[];
impersonationId: string;
languageCode: string;
mode: AiPromptMode;
userCurrency: string;
userId: string;
}) {
const prompt = await this.getPrompt({
filters,
impersonationId,
languageCode,
mode,
userCurrency,
userId
});
const openRouterApiKey = await this.propertyService.getByKey(
PROPERTY_API_KEY_OPENROUTER PROPERTY_API_KEY_OPENROUTER
); )) as string;
const openRouterModel = await this.propertyService.getByKey( const openRouterModel = (await this.propertyService.getByKey(
PROPERTY_OPENROUTER_MODEL PROPERTY_OPENROUTER_MODEL
); )) as string;
const { chat } = createOpenRouter({ const openRouterService = createOpenRouter({
apiKey: openRouterApiKey as string apiKey: openRouterApiKey
}); });
return streamText({ return generateText({
prompt, prompt,
model: chat(openRouterModel as string) model: openRouterService.chat(openRouterModel)
}); });
} }

3
apps/api/src/services/data-provider/data-provider.module.ts

@ -79,6 +79,7 @@ import { DataProviderService } from './data-provider.service';
] ]
}, },
YahooFinanceDataEnhancerService YahooFinanceDataEnhancerService
] ],
exports: [DataProviderService, ManualService, YahooFinanceService]
}) })
export class DataProviderModule {} export class DataProviderModule {}

1
libs/common/src/lib/config.ts

@ -113,7 +113,6 @@ export const NUMERICAL_PRECISION_THRESHOLD = 100000;
export const PROPERTY_API_KEY_GHOSTFOLIO = 'API_KEY_GHOSTFOLIO'; export const PROPERTY_API_KEY_GHOSTFOLIO = 'API_KEY_GHOSTFOLIO';
export const PROPERTY_API_KEY_OPENROUTER = 'API_KEY_OPENROUTER'; export const PROPERTY_API_KEY_OPENROUTER = 'API_KEY_OPENROUTER';
export const PROPERTY_BENCHMARKS = 'BENCHMARKS'; export const PROPERTY_BENCHMARKS = 'BENCHMARKS';
export const PROPERTY_BETTER_UPTIME_MONITOR_ID = 'BETTER_UPTIME_MONITOR_ID'; export const PROPERTY_BETTER_UPTIME_MONITOR_ID = 'BETTER_UPTIME_MONITOR_ID';
export const PROPERTY_COUNTRIES_OF_SUBSCRIBERS = 'COUNTRIES_OF_SUBSCRIBERS'; export const PROPERTY_COUNTRIES_OF_SUBSCRIBERS = 'COUNTRIES_OF_SUBSCRIBERS';

6
package-lock.json

@ -36003,9 +36003,9 @@
} }
}, },
"node_modules/zod-to-json-schema": { "node_modules/zod-to-json-schema": {
"version": "3.24.5", "version": "3.24.6",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
"integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
"license": "ISC", "license": "ISC",
"peerDependencies": { "peerDependencies": {
"zod": "^3.24.1" "zod": "^3.24.1"

Loading…
Cancel
Save