From 4da5c3cac9a2b3b9870e303614130949db238376 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 11 May 2026 08:04:49 +0300 Subject: [PATCH] Task/upgrade ai to version 6.0.174 (#6828) * Upgrade ai * @openrouter/ai-sdk-provider to version 2.9.0 * ai to version 6.0.174 * Add AI service health check and improve layout * Update changelog --- CHANGELOG.md | 2 + apps/api/src/app/endpoints/ai/ai.module.ts | 1 + apps/api/src/app/endpoints/ai/ai.service.ts | 13 +- apps/api/src/app/health/health.controller.ts | 31 ++- apps/api/src/app/health/health.module.ts | 2 + .../src/app/pages/api/api-page.component.ts | 65 ++++- apps/client/src/app/pages/api/api-page.html | 244 ++++++++++++------ .../app/pages/api/interfaces/interfaces.ts | 5 + libs/common/src/lib/interfaces/index.ts | 2 + .../ai-service-health-response.interface.ts | 3 + package-lock.json | 202 ++++----------- package.json | 4 +- 12 files changed, 335 insertions(+), 239 deletions(-) create mode 100644 apps/client/src/app/pages/api/interfaces/interfaces.ts create mode 100644 libs/common/src/lib/interfaces/responses/ai-service-health-response.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f7d95cb7..b32868336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Migrated various components from `NgClass` to class bindings - Refreshed the cryptocurrencies list - Upgraded `@ionic/angular` from version `8.8.1` to `8.8.5` +- Upgraded `@openrouter/ai-sdk-provider` from version `0.7.2` to `2.9.0` +- Upgraded `ai` from version `4.3.16` to `6.0.174` - Upgraded `bull-board` from version `6.20.3` to `7.0.0` - Upgraded `countries-and-timezones` from version `3.8.0` to `3.9.0` - Upgraded `fuse.js` from version `7.1.0` to `7.3.0` diff --git a/apps/api/src/app/endpoints/ai/ai.module.ts b/apps/api/src/app/endpoints/ai/ai.module.ts index eab4ecf8b..5267f40c8 100644 --- a/apps/api/src/app/endpoints/ai/ai.module.ts +++ b/apps/api/src/app/endpoints/ai/ai.module.ts @@ -28,6 +28,7 @@ import { AiService } from './ai.service'; @Module({ controllers: [AiController], + exports: [AiService], imports: [ ActivitiesModule, ApiModule, diff --git a/apps/api/src/app/endpoints/ai/ai.service.ts b/apps/api/src/app/endpoints/ai/ai.service.ts index d07768d69..362f4a728 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 { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PROPERTY_API_KEY_OPENROUTER, @@ -36,11 +37,18 @@ export class AiService { ]; public constructor( + private readonly configurationService: ConfigurationService, private readonly portfolioService: PortfolioService, private readonly propertyService: PropertyService ) {} - public async generateText({ prompt }: { prompt: string }) { + public async generateText({ + prompt, + requestTimeout = this.configurationService.get('REQUEST_TIMEOUT') + }: { + prompt: string; + requestTimeout?: number; + }) { const openRouterApiKey = await this.propertyService.getByKey( PROPERTY_API_KEY_OPENROUTER ); @@ -55,7 +63,8 @@ export class AiService { return generateText({ prompt, - model: openRouterService.chat(openRouterModel) + model: openRouterService.chat(openRouterModel), + timeout: requestTimeout }); } diff --git a/apps/api/src/app/health/health.controller.ts b/apps/api/src/app/health/health.controller.ts index 5542ae933..35f3fa348 100644 --- a/apps/api/src/app/health/health.controller.ts +++ b/apps/api/src/app/health/health.controller.ts @@ -1,5 +1,7 @@ +import { AiService } from '@ghostfolio/api/app/endpoints/ai/ai.service'; import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; import { + AiServiceHealthResponse, DataEnhancerHealthResponse, DataProviderHealthResponse } from '@ghostfolio/common/interfaces'; @@ -9,6 +11,7 @@ import { Get, HttpException, HttpStatus, + Logger, Param, Res, UseInterceptors @@ -21,7 +24,10 @@ import { HealthService } from './health.service'; @Controller('health') export class HealthController { - public constructor(private readonly healthService: HealthService) {} + public constructor( + private readonly aiService: AiService, + private readonly healthService: HealthService + ) {} @Get() public async getHealth(@Res() response: Response) { @@ -40,6 +46,29 @@ export class HealthController { } } + @Get('ai') + public async getHealthOfAiService( + @Res() response: Response + ): Promise> { + try { + const { text } = await this.aiService.generateText({ + prompt: `Reply with the word "OK" and nothing else.` + }); + + if (text === 'OK') { + return response + .status(HttpStatus.OK) + .json({ status: getReasonPhrase(StatusCodes.OK) }); + } + } catch (error) { + Logger.error(error, 'HealthController'); + } + + return response + .status(HttpStatus.SERVICE_UNAVAILABLE) + .json({ status: getReasonPhrase(StatusCodes.SERVICE_UNAVAILABLE) }); + } + @Get('data-enhancer/:name') public async getHealthOfDataEnhancer( @Param('name') name: string, diff --git a/apps/api/src/app/health/health.module.ts b/apps/api/src/app/health/health.module.ts index b8c4d5810..c36924121 100644 --- a/apps/api/src/app/health/health.module.ts +++ b/apps/api/src/app/health/health.module.ts @@ -1,3 +1,4 @@ +import { AiModule } from '@ghostfolio/api/app/endpoints/ai/ai.module'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { DataEnhancerModule } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.module'; @@ -12,6 +13,7 @@ import { HealthService } from './health.service'; @Module({ controllers: [HealthController], imports: [ + AiModule, DataEnhancerModule, DataProviderModule, PropertyModule, diff --git a/apps/client/src/app/pages/api/api-page.component.ts b/apps/client/src/app/pages/api/api-page.component.ts index 357a08bbd..e75a51c73 100644 --- a/apps/client/src/app/pages/api/api-page.component.ts +++ b/apps/client/src/app/pages/api/api-page.component.ts @@ -4,6 +4,7 @@ import { } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { + AiServiceHealthResponse, DataProviderGhostfolioAssetProfileResponse, DataProviderGhostfolioStatusResponse, DividendsResponse, @@ -13,27 +14,42 @@ import { } from '@ghostfolio/common/interfaces'; import { CommonModule } from '@angular/common'; -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { + HttpClient, + HttpErrorResponse, + HttpHeaders, + HttpParams +} from '@angular/common/http'; import { Component, DestroyRef, OnInit } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { MatCardModule } from '@angular/material/card'; import { format, startOfYear } from 'date-fns'; -import { map, Observable } from 'rxjs'; +import { isObject } from 'lodash'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; +import { catchError, map, Observable, of, OperatorFunction } from 'rxjs'; + +import { FetchFailure, FetchResult } from './interfaces/interfaces'; @Component({ host: { class: 'page' }, - imports: [CommonModule], + imports: [CommonModule, MatCardModule, NgxSkeletonLoaderModule], selector: 'gf-api-page', styleUrls: ['./api-page.scss'], templateUrl: './api-page.html' }) export class GfApiPageComponent implements OnInit { - public assetProfile$: Observable; - public dividends$: Observable; - public historicalData$: Observable; - public isinLookupItems$: Observable; - public lookupItems$: Observable; - public quotes$: Observable; - public status$: Observable; + public aiServiceHealth$: Observable>; + public assetProfile$: Observable< + FetchResult + >; + public dividends$: Observable>; + public historicalData$: Observable< + FetchResult + >; + public isinLookupItems$: Observable>; + public lookupItems$: Observable>; + public quotes$: Observable>; + public status$: Observable>; private apiKey: string; @@ -45,6 +61,7 @@ export class GfApiPageComponent implements OnInit { public ngOnInit() { this.apiKey = prompt($localize`Please enter your Ghostfolio API key:`); + this.aiServiceHealth$ = this.fetchAiServiceHealth(); this.assetProfile$ = this.fetchAssetProfile({ symbol: 'AAPL' }); this.dividends$ = this.fetchDividends({ symbol: 'KO' }); this.historicalData$ = this.fetchHistoricalData({ symbol: 'AAPL' }); @@ -54,13 +71,33 @@ export class GfApiPageComponent implements OnInit { this.status$ = this.fetchStatus(); } + public isFetchFailure(value: unknown): value is FetchFailure { + return isObject(value) && value !== null && 'fetchError' in value; + } + + private catchFetchFailure(): OperatorFunction { + return catchError(({ error }: HttpErrorResponse) => { + const body = error as { message?: string; status?: string }; + const status = body?.status ?? 'Error'; + const fetchError = body?.message ? `${status}: ${body.message}` : status; + + return of({ fetchError }); + }) as OperatorFunction; + } + + private fetchAiServiceHealth() { + return this.http + .get('/api/v1/health/ai') + .pipe(this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef)); + } + private fetchAssetProfile({ symbol }: { symbol: string }) { return this.http .get( `/api/v1/data-providers/ghostfolio/asset-profile/${symbol}`, { headers: this.getHeaders() } ) - .pipe(takeUntilDestroyed(this.destroyRef)); + .pipe(this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef)); } private fetchDividends({ symbol }: { symbol: string }) { @@ -80,6 +117,7 @@ export class GfApiPageComponent implements OnInit { map(({ dividends }) => { return dividends; }), + this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef) ); } @@ -101,6 +139,7 @@ export class GfApiPageComponent implements OnInit { map(({ historicalData }) => { return historicalData; }), + this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef) ); } @@ -127,6 +166,7 @@ export class GfApiPageComponent implements OnInit { map(({ items }) => { return items; }), + this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef) ); } @@ -143,6 +183,7 @@ export class GfApiPageComponent implements OnInit { map(({ quotes }) => { return quotes; }), + this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef) ); } @@ -153,7 +194,7 @@ export class GfApiPageComponent implements OnInit { '/api/v2/data-providers/ghostfolio/status', { headers: this.getHeaders() } ) - .pipe(takeUntilDestroyed(this.destroyRef)); + .pipe(this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef)); } private getHeaders() { diff --git a/apps/client/src/app/pages/api/api-page.html b/apps/client/src/app/pages/api/api-page.html index 3c43484e6..07f5ec981 100644 --- a/apps/client/src/app/pages/api/api-page.html +++ b/apps/client/src/app/pages/api/api-page.html @@ -1,77 +1,173 @@
-
-

Status

-
{{ status$ | async | json }}
-
-
-

Asset Profile

-
{{ assetProfile$ | async | json }}
-
-
-

Lookup

- @if (lookupItems$) { - @let symbols = lookupItems$ | async; -
    - @for (item of symbols; track item.symbol) { -
  • {{ item.name }} ({{ item.symbol }})
  • - } -
- } -
-
-

Lookup (ISIN)

- @if (isinLookupItems$) { - @let symbols = isinLookupItems$ | async; -
    - @for (item of symbols; track item.symbol) { -
  • {{ item.name }} ({{ item.symbol }})
  • - } -
- } -
-
-

Quotes

- @if (quotes$) { - @let quotes = quotes$ | async; -
    - @for (quote of quotes | keyvalue; track quote) { -
  • - {{ quote.key }}: {{ quote.value.marketPrice }} - {{ quote.value.currency }} -
  • - } -
- } -
-
-

Historical

- @if (historicalData$) { - @let historicalData = historicalData$ | async; -
    - @for ( - historicalDataItem of historicalData | keyvalue; - track historicalDataItem - ) { -
  • - {{ historicalDataItem.key }}: - {{ historicalDataItem.value.marketPrice }} -
  • - } -
- } -
-
-

Dividends

- @if (dividends$) { - @let dividends = dividends$ | async; -
    - @for (dividend of dividends | keyvalue; track dividend) { -
  • - {{ dividend.key }}: - {{ dividend.value.marketPrice }} -
  • - } -
- } +
+
+ + + AI Service Health + + + @let aiServiceHealth = aiServiceHealth$ | async; + @if (isFetchFailure(aiServiceHealth)) { + 🔴 {{ aiServiceHealth.fetchError }} + } @else if (aiServiceHealth) { + 🟢 {{ aiServiceHealth.status }} + } @else { + + } + + + + + Status + + + @let status = status$ | async; + @if (isFetchFailure(status)) { + 🔴 {{ status.fetchError }} + } @else if (status) { +
{{ status | json }}
+ } @else { + + } +
+
+ + + Asset Profile + + + @let assetProfile = assetProfile$ | async; + @if (isFetchFailure(assetProfile)) { + 🔴 {{ assetProfile.fetchError }} + } @else if (assetProfile) { +
{{ assetProfile | json }}
+ } @else { + + } +
+
+ + + Lookup + + + @let symbols = lookupItems$ | async; + @if (isFetchFailure(symbols)) { + 🔴 {{ symbols.fetchError }} + } @else if (symbols) { +
    + @for (item of symbols; track item.symbol) { +
  • {{ item.name }} ({{ item.symbol }})
  • + } +
+ } @else { + + } +
+
+ + + Lookup (ISIN) + + + @let isinSymbols = isinLookupItems$ | async; + @if (isFetchFailure(isinSymbols)) { + 🔴 {{ isinSymbols.fetchError }} + } @else if (isinSymbols) { +
    + @for (item of isinSymbols; track item.symbol) { +
  • {{ item.name }} ({{ item.symbol }})
  • + } +
+ } @else { + + } +
+
+ + + Quotes + + + @let quotes = quotes$ | async; + @if (isFetchFailure(quotes)) { + 🔴 {{ quotes.fetchError }} + } @else if (quotes) { +
    + @for (quote of quotes | keyvalue; track quote) { +
  • + {{ quote.key }}: {{ quote.value.marketPrice }} + {{ quote.value.currency }} +
  • + } +
+ } @else { + + } +
+
+ + + Historical + + + @let historicalData = historicalData$ | async; + @if (isFetchFailure(historicalData)) { + 🔴 {{ historicalData.fetchError }} + } @else if (historicalData) { +
    + @for (item of historicalData | keyvalue; track item) { +
  • {{ item.key }}: {{ item.value.marketPrice }}
  • + } +
+ } @else { + + } +
+
+ + + Dividends + + + @let dividends = dividends$ | async; + @if (isFetchFailure(dividends)) { + 🔴 {{ dividends.fetchError }} + } @else if (dividends) { +
    + @for (dividend of dividends | keyvalue; track dividend) { +
  • {{ dividend.key }}: {{ dividend.value.marketPrice }}
  • + } +
+ } @else { + + } +
+
+
diff --git a/apps/client/src/app/pages/api/interfaces/interfaces.ts b/apps/client/src/app/pages/api/interfaces/interfaces.ts new file mode 100644 index 000000000..07de2b2f7 --- /dev/null +++ b/apps/client/src/app/pages/api/interfaces/interfaces.ts @@ -0,0 +1,5 @@ +export interface FetchFailure { + fetchError: string; +} + +export type FetchResult = T | FetchFailure; diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index ad747d94e..89874da60 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -44,6 +44,7 @@ import type { ActivityResponse } from './responses/activity-response.interface'; import type { AdminUserResponse } from './responses/admin-user-response.interface'; import type { AdminUsersResponse } from './responses/admin-users-response.interface'; import type { AiPromptResponse } from './responses/ai-prompt-response.interface'; +import type { AiServiceHealthResponse } from './responses/ai-service-health-response.interface'; import type { ApiKeyResponse } from './responses/api-key-response.interface'; import type { AssetResponse } from './responses/asset-response.interface'; import type { BenchmarkMarketDataDetailsResponse } from './responses/benchmark-market-data-details-response.interface'; @@ -117,6 +118,7 @@ export { AdminUserResponse, AdminUsersResponse, AiPromptResponse, + AiServiceHealthResponse, ApiKeyResponse, AssertionCredentialJSON, AssetClassSelectorOption, diff --git a/libs/common/src/lib/interfaces/responses/ai-service-health-response.interface.ts b/libs/common/src/lib/interfaces/responses/ai-service-health-response.interface.ts new file mode 100644 index 000000000..58ffafafc --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/ai-service-health-response.interface.ts @@ -0,0 +1,3 @@ +export interface AiServiceHealthResponse { + status: string; +} diff --git a/package-lock.json b/package-lock.json index d061349b4..70873b53f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,12 +40,12 @@ "@nestjs/platform-express": "11.1.19", "@nestjs/schedule": "6.1.3", "@nestjs/serve-static": "5.0.5", - "@openrouter/ai-sdk-provider": "0.7.2", + "@openrouter/ai-sdk-provider": "2.9.0", "@prisma/adapter-pg": "7.7.0", "@prisma/client": "7.7.0", "@simplewebauthn/browser": "13.2.2", "@simplewebauthn/server": "13.2.2", - "ai": "4.3.16", + "ai": "6.0.174", "alphavantage": "2.2.0", "big.js": "7.0.1", "bootstrap": "4.6.2", @@ -175,74 +175,50 @@ "dev": true, "license": "MIT" }, - "node_modules/@ai-sdk/provider": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", - "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/provider-utils": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz", - "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==", + "node_modules/@ai-sdk/gateway": { + "version": "3.0.109", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.109.tgz", + "integrity": "sha512-r6dOqThjODp1vOhGRJg2OCmyB/ZOQtGx1esZ2SDvwDX5XoX8dBqYaYjLg8MPXTzMGJSgOkJyCxWgUcZtAl16pw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "nanoid": "^3.3.8", - "secure-json-parse": "^2.7.0" + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.26", + "@vercel/oidc": "3.2.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "zod": "^3.23.8" + "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/react": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", - "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", + "node_modules/@ai-sdk/provider": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.10.tgz", + "integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider-utils": "2.2.8", - "@ai-sdk/ui-utils": "1.2.11", - "swr": "^2.2.5", - "throttleit": "2.1.0" + "json-schema": "^0.4.0" }, "engines": { "node": ">=18" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } } }, - "node_modules/@ai-sdk/ui-utils": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", - "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", + "node_modules/@ai-sdk/provider-utils": { + "version": "4.0.26", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.26.tgz", + "integrity": "sha512-CsKNLKsOpvPujRlIYvoz+Ybw+kGn7J4/fIZa/58+R7iWLLfwn6ifE2G6Yq8K9XvH/I/3bzaDAJ3NhRwEMsLBKQ==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8", - "zod-to-json-schema": "^3.24.1" + "@ai-sdk/provider": "3.0.10", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.8" }, "engines": { "node": ">=18" }, "peerDependencies": { - "zod": "^3.23.8" + "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/@algolia/abtesting": { @@ -10810,20 +10786,16 @@ } }, "node_modules/@openrouter/ai-sdk-provider": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-0.7.2.tgz", - "integrity": "sha512-Fry2mV7uGGJRmP9JntTZRc8ElESIk7AJNTacLbF6Syoeb5k8d7HPGkcK9rTXDlqBb8HgU1hOKtz23HojesTmnw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-2.9.0.tgz", + "integrity": "sha512-Seva+NCa0WUQnJIUE5GzHsUv1WTIeyqwz0ELl2VtS6NP+eF+77yCXGFVOMbvoCM7QMjlnhv7931e89R+8pJdcQ==", "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8" - }, "engines": { "node": ">=18" }, "peerDependencies": { - "ai": "^4.3.16", - "zod": "^3.25.34" + "ai": "^6.0.0", + "zod": "^3.25.0 || ^4.0.0" } }, "node_modules/@opentelemetry/api": { @@ -14455,12 +14427,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/diff-match-patch": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", - "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", - "license": "MIT" - }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -15986,6 +15952,15 @@ "d3-transition": "^3.0.1" } }, + "node_modules/@vercel/oidc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz", + "integrity": "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" + } + }, "node_modules/@vitejs/plugin-basic-ssl": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.4.tgz", @@ -16440,29 +16415,21 @@ } }, "node_modules/ai": { - "version": "4.3.16", - "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.16.tgz", - "integrity": "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==", + "version": "6.0.174", + "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.174.tgz", + "integrity": "sha512-bTrfLUWHWtkjzWyCY4bmyuk4Qvmj4S4NSNsXyNSVVqkmftQNtxRj7dzUoMeQDBBwlJO6fC7m2Q/lNOPqQQfAGA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "1.1.3", - "@ai-sdk/provider-utils": "2.2.8", - "@ai-sdk/react": "1.2.12", - "@ai-sdk/ui-utils": "1.2.11", - "@opentelemetry/api": "1.9.0", - "jsondiffpatch": "0.6.0" + "@ai-sdk/gateway": "3.0.109", + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.26", + "@opentelemetry/api": "1.9.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } + "zod": "^3.25.76 || ^4.1.8" } }, "node_modules/ajv": { @@ -20844,7 +20811,9 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -20922,12 +20891,6 @@ "node": ">=0.3.1" } }, - "node_modules/diff-match-patch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", - "license": "Apache-2.0" - }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -22137,10 +22100,9 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", - "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", - "dev": true, + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.8.tgz", + "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -28868,35 +28830,6 @@ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "license": "MIT" }, - "node_modules/jsondiffpatch": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", - "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", - "license": "MIT", - "dependencies": { - "@types/diff-match-patch": "^1.0.36", - "chalk": "^5.3.0", - "diff-match-patch": "^1.0.5" - }, - "bin": { - "jsondiffpatch": "bin/jsondiffpatch.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/jsondiffpatch/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", @@ -29727,6 +29660,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "devOptional": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -33634,6 +33568,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "devOptional": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" @@ -35337,12 +35272,6 @@ "dev": true, "license": "MIT" }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", - "license": "BSD-3-Clause" - }, "node_modules/select": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", @@ -36838,19 +36767,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/swr": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.4.1.tgz", - "integrity": "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -37254,18 +37170,6 @@ "tslib": "^2" } }, - "node_modules/throttleit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", - "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -38654,6 +38558,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "dev": true, "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -40490,6 +40395,7 @@ "version": "3.25.2", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, "license": "ISC", "peerDependencies": { "zod": "^3.25.28 || ^4" diff --git a/package.json b/package.json index 9ed027e9c..ebc8a775e 100644 --- a/package.json +++ b/package.json @@ -85,12 +85,12 @@ "@nestjs/platform-express": "11.1.19", "@nestjs/schedule": "6.1.3", "@nestjs/serve-static": "5.0.5", - "@openrouter/ai-sdk-provider": "0.7.2", + "@openrouter/ai-sdk-provider": "2.9.0", "@prisma/adapter-pg": "7.7.0", "@prisma/client": "7.7.0", "@simplewebauthn/browser": "13.2.2", "@simplewebauthn/server": "13.2.2", - "ai": "4.3.16", + "ai": "6.0.174", "alphavantage": "2.2.0", "big.js": "7.0.1", "bootstrap": "4.6.2",