From ac4e0422316a4dcd19151dd7d504e2b3cccbc038 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Fri, 27 Dec 2024 09:46:57 -0500 Subject: [PATCH] feat(core): Add endpoint to create free AI credits (#12362) --- .../ai-free-credits-request.dto.test.ts | 32 ++++++ .../src/dto/ai/ai-free-credits-request.dto.ts | 6 ++ packages/@n8n/api-types/src/dto/index.ts | 1 + packages/cli/package.json | 2 +- packages/cli/src/constants.ts | 3 + .../__tests__/ai.controller.test.ts | 3 +- packages/cli/src/controllers/ai.controller.ts | 49 ++++++++- .../src/credentials/credentials.controller.ts | 4 + .../src/credentials/credentials.service.ts | 1 + .../repositories/credentials.repository.ts | 2 +- packages/cli/src/requests.ts | 1 + packages/cli/src/services/ai.service.ts | 10 ++ .../cli/test/integration/ai/ai.api.test.ts | 99 +++++++++++++++++++ .../credentials/credentials.api.test.ts | 17 +++- .../cli/test/integration/shared/random.ts | 5 +- packages/cli/test/integration/shared/types.ts | 4 +- .../integration/shared/utils/test-server.ts | 3 + packages/workflow/src/Interfaces.ts | 1 + pnpm-lock.yaml | 49 +++++---- 19 files changed, 258 insertions(+), 34 deletions(-) create mode 100644 packages/@n8n/api-types/src/dto/ai/__tests__/ai-free-credits-request.dto.test.ts create mode 100644 packages/@n8n/api-types/src/dto/ai/ai-free-credits-request.dto.ts create mode 100644 packages/cli/test/integration/ai/ai.api.test.ts diff --git a/packages/@n8n/api-types/src/dto/ai/__tests__/ai-free-credits-request.dto.test.ts b/packages/@n8n/api-types/src/dto/ai/__tests__/ai-free-credits-request.dto.test.ts new file mode 100644 index 0000000000..2b61eeaee9 --- /dev/null +++ b/packages/@n8n/api-types/src/dto/ai/__tests__/ai-free-credits-request.dto.test.ts @@ -0,0 +1,32 @@ +import { nanoId } from 'minifaker'; + +import { AiFreeCreditsRequestDto } from '../ai-free-credits-request.dto'; +import 'minifaker/locales/en'; + +describe('AiChatRequestDto', () => { + it('should succeed if projectId is a valid nanoid', () => { + const validRequest = { + projectId: nanoId.nanoid(), + }; + + const result = AiFreeCreditsRequestDto.safeParse(validRequest); + + expect(result.success).toBe(true); + }); + + it('should succeed if no projectId is sent', () => { + const result = AiFreeCreditsRequestDto.safeParse({}); + + expect(result.success).toBe(true); + }); + + it('should fail is projectId invalid value', () => { + const validRequest = { + projectId: '', + }; + + const result = AiFreeCreditsRequestDto.safeParse(validRequest); + + expect(result.success).toBe(false); + }); +}); diff --git a/packages/@n8n/api-types/src/dto/ai/ai-free-credits-request.dto.ts b/packages/@n8n/api-types/src/dto/ai/ai-free-credits-request.dto.ts new file mode 100644 index 0000000000..9f9120d417 --- /dev/null +++ b/packages/@n8n/api-types/src/dto/ai/ai-free-credits-request.dto.ts @@ -0,0 +1,6 @@ +import { z } from 'zod'; +import { Z } from 'zod-class'; + +export class AiFreeCreditsRequestDto extends Z.class({ + projectId: z.string().min(1).optional(), +}) {} diff --git a/packages/@n8n/api-types/src/dto/index.ts b/packages/@n8n/api-types/src/dto/index.ts index 70ea346a39..92e5c56fa0 100644 --- a/packages/@n8n/api-types/src/dto/index.ts +++ b/packages/@n8n/api-types/src/dto/index.ts @@ -1,6 +1,7 @@ export { AiAskRequestDto } from './ai/ai-ask-request.dto'; export { AiChatRequestDto } from './ai/ai-chat-request.dto'; export { AiApplySuggestionRequestDto } from './ai/ai-apply-suggestion-request.dto'; +export { AiFreeCreditsRequestDto } from './ai/ai-free-credits-request.dto'; export { LoginRequestDto } from './auth/login-request.dto'; export { ResolveSignupTokenQueryDto } from './auth/resolve-signup-token-query.dto'; diff --git a/packages/cli/package.json b/packages/cli/package.json index e005ce3025..3afa78ebad 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -94,7 +94,7 @@ "@n8n/permissions": "workspace:*", "@n8n/task-runner": "workspace:*", "@n8n/typeorm": "0.3.20-12", - "@n8n_io/ai-assistant-sdk": "1.12.0", + "@n8n_io/ai-assistant-sdk": "1.13.0", "@n8n_io/license-sdk": "2.13.1", "@oclif/core": "4.0.7", "@rudderstack/rudder-sdk-node": "2.0.9", diff --git a/packages/cli/src/constants.ts b/packages/cli/src/constants.ts index 78d7671e1b..2691191685 100644 --- a/packages/cli/src/constants.ts +++ b/packages/cli/src/constants.ts @@ -174,3 +174,6 @@ export const WsStatusCodes = { CloseAbnormal: 1006, CloseInvalidData: 1007, } as const; + +export const FREE_AI_CREDITS_CREDENTIAL_NAME = 'n8n free OpenAI API credits'; +export const OPEN_AI_API_CREDENTIAL_TYPE = 'openAiApi'; diff --git a/packages/cli/src/controllers/__tests__/ai.controller.test.ts b/packages/cli/src/controllers/__tests__/ai.controller.test.ts index 30785ae938..2cf0a1cdcd 100644 --- a/packages/cli/src/controllers/__tests__/ai.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/ai.controller.test.ts @@ -14,7 +14,8 @@ import { AiController, type FlushableResponse } from '../ai.controller'; describe('AiController', () => { const aiService = mock(); - const controller = new AiController(aiService); + + const controller = new AiController(aiService, mock(), mock()); const request = mock({ user: { id: 'user123' }, diff --git a/packages/cli/src/controllers/ai.controller.ts b/packages/cli/src/controllers/ai.controller.ts index 59499112a3..791c02bec3 100644 --- a/packages/cli/src/controllers/ai.controller.ts +++ b/packages/cli/src/controllers/ai.controller.ts @@ -1,19 +1,32 @@ -import { AiChatRequestDto, AiApplySuggestionRequestDto, AiAskRequestDto } from '@n8n/api-types'; +import { + AiChatRequestDto, + AiApplySuggestionRequestDto, + AiAskRequestDto, + AiFreeCreditsRequestDto, +} from '@n8n/api-types'; import type { AiAssistantSDK } from '@n8n_io/ai-assistant-sdk'; import { Response } from 'express'; import { strict as assert } from 'node:assert'; import { WritableStream } from 'node:stream/web'; +import { FREE_AI_CREDITS_CREDENTIAL_NAME, OPEN_AI_API_CREDENTIAL_TYPE } from '@/constants'; +import { CredentialsService } from '@/credentials/credentials.service'; import { Body, Post, RestController } from '@/decorators'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; +import type { CredentialRequest } from '@/requests'; import { AuthenticatedRequest } from '@/requests'; import { AiService } from '@/services/ai.service'; +import { UserService } from '@/services/user.service'; export type FlushableResponse = Response & { flush: () => void }; @RestController('/ai') export class AiController { - constructor(private readonly aiService: AiService) {} + constructor( + private readonly aiService: AiService, + private readonly credentialsService: CredentialsService, + private readonly userService: UserService, + ) {} @Post('/chat', { rateLimit: { limit: 100 } }) async chat(req: AuthenticatedRequest, res: FlushableResponse, @Body payload: AiChatRequestDto) { @@ -64,4 +77,36 @@ export class AiController { throw new InternalServerError(e.message, e); } } + + @Post('/free-credits') + async aiCredits(req: AuthenticatedRequest, _: Response, @Body payload: AiFreeCreditsRequestDto) { + try { + const aiCredits = await this.aiService.createFreeAiCredits(req.user); + + const credentialProperties: CredentialRequest.CredentialProperties = { + name: FREE_AI_CREDITS_CREDENTIAL_NAME, + type: OPEN_AI_API_CREDENTIAL_TYPE, + data: { + apiKey: aiCredits.apiKey, + url: aiCredits.url, + }, + isManaged: true, + projectId: payload?.projectId, + }; + + const newCredential = await this.credentialsService.createCredential( + credentialProperties, + req.user, + ); + + await this.userService.updateSettings(req.user.id, { + userClaimedAiCredits: true, + }); + + return newCredential; + } catch (e) { + assert(e instanceof Error); + throw new InternalServerError(e.message, e); + } + } } diff --git a/packages/cli/src/credentials/credentials.controller.ts b/packages/cli/src/credentials/credentials.controller.ts index 333b7536d0..934e91f64d 100644 --- a/packages/cli/src/credentials/credentials.controller.ts +++ b/packages/cli/src/credentials/credentials.controller.ts @@ -186,6 +186,10 @@ export class CredentialsController { ); } + if (credential.isManaged) { + throw new BadRequestError('Managed credentials cannot be updated'); + } + const decryptedData = this.credentialsService.decrypt(credential); const preparedCredentialData = await this.credentialsService.prepareUpdateData( req.body, diff --git a/packages/cli/src/credentials/credentials.service.ts b/packages/cli/src/credentials/credentials.service.ts index 67d355083a..84398b5475 100644 --- a/packages/cli/src/credentials/credentials.service.ts +++ b/packages/cli/src/credentials/credentials.service.ts @@ -196,6 +196,7 @@ export class CredentialsService { name: c.name, type: c.type, scopes: c.scopes, + isManaged: c.isManaged, })); } diff --git a/packages/cli/src/databases/repositories/credentials.repository.ts b/packages/cli/src/databases/repositories/credentials.repository.ts index 4c62e30f63..e5aecf1069 100644 --- a/packages/cli/src/databases/repositories/credentials.repository.ts +++ b/packages/cli/src/databases/repositories/credentials.repository.ts @@ -41,7 +41,7 @@ export class CredentialsRepository extends Repository { type Select = Array; const defaultRelations = ['shared', 'shared.project']; - const defaultSelect: Select = ['id', 'name', 'type', 'createdAt', 'updatedAt']; + const defaultSelect: Select = ['id', 'name', 'type', 'isManaged', 'createdAt', 'updatedAt']; if (!listQueryOptions) return { select: defaultSelect, relations: defaultRelations }; diff --git a/packages/cli/src/requests.ts b/packages/cli/src/requests.ts index 12a3533c99..fdde592f28 100644 --- a/packages/cli/src/requests.ts +++ b/packages/cli/src/requests.ts @@ -142,6 +142,7 @@ export declare namespace CredentialRequest { type: string; data: ICredentialDataDecryptedObject; projectId?: string; + isManaged?: boolean; }>; type Create = AuthenticatedRequest<{}, {}, CredentialProperties>; diff --git a/packages/cli/src/services/ai.service.ts b/packages/cli/src/services/ai.service.ts index 74e28ad288..6a39306ef5 100644 --- a/packages/cli/src/services/ai.service.ts +++ b/packages/cli/src/services/ai.service.ts @@ -22,6 +22,7 @@ export class AiService { async init() { const aiAssistantEnabled = this.licenseService.isAiAssistantEnabled(); + if (!aiAssistantEnabled) { return; } @@ -66,4 +67,13 @@ export class AiService { return await this.client.askAi(payload, { id: user.id }); } + + async createFreeAiCredits(user: IUser) { + if (!this.client) { + await this.init(); + } + assert(this.client, 'Assistant client not setup'); + + return await this.client.generateAiCreditsCredentials(user); + } } diff --git a/packages/cli/test/integration/ai/ai.api.test.ts b/packages/cli/test/integration/ai/ai.api.test.ts new file mode 100644 index 0000000000..34255dc72f --- /dev/null +++ b/packages/cli/test/integration/ai/ai.api.test.ts @@ -0,0 +1,99 @@ +import { randomUUID } from 'crypto'; +import { mock } from 'jest-mock-extended'; +import { Container } from 'typedi'; + +import { FREE_AI_CREDITS_CREDENTIAL_NAME, OPEN_AI_API_CREDENTIAL_TYPE } from '@/constants'; +import type { Project } from '@/databases/entities/project'; +import type { User } from '@/databases/entities/user'; +import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; +import { SharedCredentialsRepository } from '@/databases/repositories/shared-credentials.repository'; +import { UserRepository } from '@/databases/repositories/user.repository'; +import { AiService } from '@/services/ai.service'; + +import { createOwner } from '../shared/db/users'; +import * as testDb from '../shared/test-db'; +import type { SuperAgentTest } from '../shared/types'; +import { setupTestServer } from '../shared/utils'; + +const createAiCreditsResponse = { + apiKey: randomUUID(), + url: 'https://api.openai.com', +}; + +Container.set( + AiService, + mock({ + createFreeAiCredits: async () => createAiCreditsResponse, + }), +); + +const testServer = setupTestServer({ endpointGroups: ['ai'] }); + +let owner: User; +let ownerPersonalProject: Project; + +let authOwnerAgent: SuperAgentTest; + +beforeEach(async () => { + await testDb.truncate(['SharedCredentials', 'Credentials']); + + owner = await createOwner(); + + ownerPersonalProject = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail( + owner.id, + ); + + authOwnerAgent = testServer.authAgentFor(owner); +}); + +describe('POST /ai/free-credits', () => { + test('should create OpenAI managed credential', async () => { + // Act + const response = await authOwnerAgent.post('/ai/free-credits').send({ + projectId: ownerPersonalProject.id, + }); + + // Assert + + expect(response.statusCode).toBe(200); + + const { id, name, type, data: encryptedData, scopes } = response.body.data; + + expect(name).toBe(FREE_AI_CREDITS_CREDENTIAL_NAME); + expect(type).toBe(OPEN_AI_API_CREDENTIAL_TYPE); + expect(encryptedData).not.toBe(createAiCreditsResponse); + + expect(scopes).toEqual( + [ + 'credential:create', + 'credential:delete', + 'credential:list', + 'credential:move', + 'credential:read', + 'credential:share', + 'credential:update', + ].sort(), + ); + + const credential = await Container.get(CredentialsRepository).findOneByOrFail({ id }); + + expect(credential.name).toBe(FREE_AI_CREDITS_CREDENTIAL_NAME); + expect(credential.type).toBe(OPEN_AI_API_CREDENTIAL_TYPE); + expect(credential.data).not.toBe(createAiCreditsResponse); + expect(credential.isManaged).toBe(true); + + const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({ + relations: { project: true, credentials: true }, + where: { credentialsId: credential.id }, + }); + + expect(sharedCredential.project.id).toBe(ownerPersonalProject.id); + expect(sharedCredential.credentials.name).toBe(FREE_AI_CREDITS_CREDENTIAL_NAME); + expect(sharedCredential.credentials.isManaged).toBe(true); + + const user = await Container.get(UserRepository).findOneOrFail({ where: { id: owner.id } }); + + expect(user.settings?.userClaimedAiCredits).toBe(true); + }); +}); diff --git a/packages/cli/test/integration/credentials/credentials.api.test.ts b/packages/cli/test/integration/credentials/credentials.api.test.ts index 5f40850e59..eb9712fefd 100644 --- a/packages/cli/test/integration/credentials/credentials.api.test.ts +++ b/packages/cli/test/integration/credentials/credentials.api.test.ts @@ -87,6 +87,7 @@ describe('GET /credentials', () => { validateMainCredentialData(credential); expect('data' in credential).toBe(false); expect(savedCredentialsIds).toContain(credential.id); + expect('isManaged' in credential).toBe(true); }); }); @@ -1035,6 +1036,19 @@ describe('PATCH /credentials/:id', () => { expect(response.statusCode).toBe(403); }); + + test('should fail with a 400 is credential is managed', async () => { + const { id } = await saveCredential(randomCredentialPayload({ isManaged: true }), { + user: owner, + role: 'credential:owner', + }); + + const response = await authOwnerAgent + .patch(`/credentials/${id}`) + .send(randomCredentialPayload()); + + expect(response.statusCode).toBe(400); + }); }); describe('GET /credentials/new', () => { @@ -1188,10 +1202,11 @@ const INVALID_PAYLOADS = [ ]; function validateMainCredentialData(credential: ListQuery.Credentials.WithOwnedByAndSharedWith) { - const { name, type, sharedWithProjects, homeProject } = credential; + const { name, type, sharedWithProjects, homeProject, isManaged } = credential; expect(typeof name).toBe('string'); expect(typeof type).toBe('string'); + expect(typeof isManaged).toBe('boolean'); if (sharedWithProjects) { expect(Array.isArray(sharedWithProjects)).toBe(true); diff --git a/packages/cli/test/integration/shared/random.ts b/packages/cli/test/integration/shared/random.ts index a51fae3a05..e556c4f512 100644 --- a/packages/cli/test/integration/shared/random.ts +++ b/packages/cli/test/integration/shared/random.ts @@ -37,10 +37,13 @@ const randomTopLevelDomain = () => chooseRandomly(POPULAR_TOP_LEVEL_DOMAINS); export const randomName = () => randomString(4, 8).toLowerCase(); -export const randomCredentialPayload = (): CredentialPayload => ({ +export const randomCredentialPayload = ({ + isManaged = false, +}: { isManaged?: boolean } = {}): CredentialPayload => ({ name: randomName(), type: randomName(), data: { accessToken: randomString(6, 16) }, + isManaged, }); export const uniqueId = () => uuid(); diff --git a/packages/cli/test/integration/shared/types.ts b/packages/cli/test/integration/shared/types.ts index 2afe6ec328..2a789e4f00 100644 --- a/packages/cli/test/integration/shared/types.ts +++ b/packages/cli/test/integration/shared/types.ts @@ -42,7 +42,8 @@ type EndpointGroup = | 'role' | 'dynamic-node-parameters' | 'apiKeys' - | 'evaluation'; + | 'evaluation' + | 'ai'; export interface SetupProps { endpointGroups?: EndpointGroup[]; @@ -68,6 +69,7 @@ export type CredentialPayload = { name: string; type: string; data: ICredentialDataDecryptedObject; + isManaged?: boolean; }; export type SaveCredentialFunction = ( diff --git a/packages/cli/test/integration/shared/utils/test-server.ts b/packages/cli/test/integration/shared/utils/test-server.ts index 8b63320008..52e97f8f31 100644 --- a/packages/cli/test/integration/shared/utils/test-server.ts +++ b/packages/cli/test/integration/shared/utils/test-server.ts @@ -283,6 +283,9 @@ export const setupTestServer = ({ await import('@/evaluation.ee/test-definitions.controller.ee'); await import('@/evaluation.ee/test-runs.controller.ee'); break; + + case 'ai': + await import('@/controllers/ai.controller'); } } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 1856eac6ff..4ab59ac433 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2783,6 +2783,7 @@ export interface IUserSettings { allowSSOManualLogin?: boolean; npsSurvey?: NpsSurveyState; easyAIWorkflowOnboarded?: boolean; + userClaimedAiCredits?: boolean; } export interface IProcessedDataConfig { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf87db10a9..b361ef7b13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -434,7 +434,7 @@ importers: version: 3.666.0(@aws-sdk/client-sts@3.666.0) '@getzep/zep-cloud': specifier: 1.0.12 - version: 1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(e4rnrwhosnp2xiru36mqgdy2bu)) + version: 1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i)) '@getzep/zep-js': specifier: 0.9.0 version: 0.9.0 @@ -461,7 +461,7 @@ importers: version: 0.3.1(@aws-sdk/client-sso-oidc@3.666.0(@aws-sdk/client-sts@3.666.0))(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) '@langchain/community': specifier: 0.3.15 - version: 0.3.15(vc5hvyy27o4cmm4jplsptc2fqm) + version: 0.3.15(v4qhcw25bevfr6xzz4fnsvjiqe) '@langchain/core': specifier: 'catalog:' version: 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) @@ -548,7 +548,7 @@ importers: version: 23.0.1 langchain: specifier: 0.3.6 - version: 0.3.6(e4rnrwhosnp2xiru36mqgdy2bu) + version: 0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i) lodash: specifier: 'catalog:' version: 4.17.21 @@ -788,8 +788,8 @@ importers: specifier: 0.3.20-12 version: 0.3.20-12(@sentry/node@8.42.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.11.0)(pg@8.12.0)(redis@4.6.14)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@18.16.16)(typescript@5.7.2)) '@n8n_io/ai-assistant-sdk': - specifier: 1.12.0 - version: 1.12.0 + specifier: 1.13.0 + version: 1.13.0 '@n8n_io/license-sdk': specifier: 2.13.1 version: 2.13.1 @@ -4326,8 +4326,8 @@ packages: engines: {node: '>=18.10', pnpm: '>=9.6'} hasBin: true - '@n8n_io/ai-assistant-sdk@1.12.0': - resolution: {integrity: sha512-ddIGzUn8icxWwl49PLSpl/Gfb0bCIGpqvWtZWqC3GIBeb51Nul6E4e3cIyDYOFlZmWWr/BDKsN0wskm2s/jkdg==} + '@n8n_io/ai-assistant-sdk@1.13.0': + resolution: {integrity: sha512-16kftFTeX3/lBinHJaBK0OL1lB4FpPaUoHX4h25AkvgHvmjUHpWNY2ZtKos0rY89+pkzDsNxMZqSUkeKU45iRg==} engines: {node: '>=20.15', pnpm: '>=8.14'} '@n8n_io/license-sdk@2.13.1': @@ -12996,6 +12996,9 @@ packages: vue-component-type-helpers@2.1.10: resolution: {integrity: sha512-lfgdSLQKrUmADiSV6PbBvYgQ33KF3Ztv6gP85MfGaGaSGMTXORVaHT1EHfsqCgzRNBstPKYDmvAV9Do5CmJ07A==} + vue-component-type-helpers@2.2.0: + resolution: {integrity: sha512-cYrAnv2me7bPDcg9kIcGwjJiSB6Qyi08+jLDo9yuvoFQjzHiPTzML7RnkJB1+3P6KMsX/KbCD4QE3Tv/knEllw==} + vue-demi@0.14.10: resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} engines: {node: '>=12'} @@ -15678,7 +15681,7 @@ snapshots: '@gar/promisify@1.1.3': optional: true - '@getzep/zep-cloud@1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(e4rnrwhosnp2xiru36mqgdy2bu))': + '@getzep/zep-cloud@1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i))': dependencies: form-data: 4.0.0 node-fetch: 2.7.0(encoding@0.1.13) @@ -15687,7 +15690,7 @@ snapshots: zod: 3.23.8 optionalDependencies: '@langchain/core': 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) - langchain: 0.3.6(e4rnrwhosnp2xiru36mqgdy2bu) + langchain: 0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i) transitivePeerDependencies: - encoding @@ -16151,7 +16154,7 @@ snapshots: - aws-crt - encoding - '@langchain/community@0.3.15(vc5hvyy27o4cmm4jplsptc2fqm)': + '@langchain/community@0.3.15(v4qhcw25bevfr6xzz4fnsvjiqe)': dependencies: '@ibm-cloud/watsonx-ai': 1.1.2 '@langchain/core': 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) @@ -16161,7 +16164,7 @@ snapshots: flat: 5.0.2 ibm-cloud-sdk-core: 5.1.0 js-yaml: 4.1.0 - langchain: 0.3.6(e4rnrwhosnp2xiru36mqgdy2bu) + langchain: 0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i) langsmith: 0.2.3(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) uuid: 10.0.0 zod: 3.23.8 @@ -16174,7 +16177,7 @@ snapshots: '@aws-sdk/client-s3': 3.666.0 '@aws-sdk/credential-provider-node': 3.666.0(@aws-sdk/client-sso-oidc@3.666.0(@aws-sdk/client-sts@3.666.0))(@aws-sdk/client-sts@3.666.0) '@azure/storage-blob': 12.18.0(encoding@0.1.13) - '@getzep/zep-cloud': 1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(e4rnrwhosnp2xiru36mqgdy2bu)) + '@getzep/zep-cloud': 1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i)) '@getzep/zep-js': 0.9.0 '@google-ai/generativelanguage': 2.6.0(encoding@0.1.13) '@google-cloud/storage': 7.12.1(encoding@0.1.13) @@ -16546,7 +16549,7 @@ snapshots: acorn: 8.12.1 acorn-walk: 8.3.4 - '@n8n_io/ai-assistant-sdk@1.12.0': {} + '@n8n_io/ai-assistant-sdk@1.13.0': {} '@n8n_io/license-sdk@2.13.1': dependencies: @@ -18024,7 +18027,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.5.13(typescript@5.7.2) - vue-component-type-helpers: 2.1.10 + vue-component-type-helpers: 2.2.0 '@supabase/auth-js@2.65.0': dependencies: @@ -19458,14 +19461,6 @@ snapshots: transitivePeerDependencies: - debug - axios@1.7.4(debug@4.3.7): - dependencies: - follow-redirects: 1.15.6(debug@4.3.7) - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - axios@1.7.7: dependencies: follow-redirects: 1.15.6(debug@4.3.6) @@ -22317,7 +22312,7 @@ snapshots: '@types/debug': 4.1.12 '@types/node': 18.16.16 '@types/tough-cookie': 4.0.2 - axios: 1.7.4(debug@4.3.7) + axios: 1.7.4 camelcase: 6.3.0 debug: 4.3.7 dotenv: 16.4.5 @@ -22327,7 +22322,7 @@ snapshots: isstream: 0.1.2 jsonwebtoken: 9.0.2 mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.7.4) + retry-axios: 2.6.0(axios@1.7.4(debug@4.3.7)) tough-cookie: 4.1.3 transitivePeerDependencies: - supports-color @@ -23326,7 +23321,7 @@ snapshots: kuler@2.0.0: {} - langchain@0.3.6(e4rnrwhosnp2xiru36mqgdy2bu): + langchain@0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i): dependencies: '@langchain/core': 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) '@langchain/openai': 0.3.14(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) @@ -25696,7 +25691,7 @@ snapshots: ret@0.1.15: {} - retry-axios@2.6.0(axios@1.7.4): + retry-axios@2.6.0(axios@1.7.4(debug@4.3.7)): dependencies: axios: 1.7.4 @@ -27337,6 +27332,8 @@ snapshots: vue-component-type-helpers@2.1.10: {} + vue-component-type-helpers@2.2.0: {} + vue-demi@0.14.10(vue@3.5.13(typescript@5.7.2)): dependencies: vue: 3.5.13(typescript@5.7.2)