mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
feat(core): Add endpoint to create free AI credits (#12362)
This commit is contained in:
parent
c00b95e08f
commit
ac4e042231
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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(),
|
||||
}) {}
|
|
@ -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';
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -14,7 +14,8 @@ import { AiController, type FlushableResponse } from '../ai.controller';
|
|||
|
||||
describe('AiController', () => {
|
||||
const aiService = mock<AiService>();
|
||||
const controller = new AiController(aiService);
|
||||
|
||||
const controller = new AiController(aiService, mock(), mock());
|
||||
|
||||
const request = mock<AuthenticatedRequest>({
|
||||
user: { id: 'user123' },
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -196,6 +196,7 @@ export class CredentialsService {
|
|||
name: c.name,
|
||||
type: c.type,
|
||||
scopes: c.scopes,
|
||||
isManaged: c.isManaged,
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ export class CredentialsRepository extends Repository<CredentialsEntity> {
|
|||
type Select = Array<keyof CredentialsEntity>;
|
||||
|
||||
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 };
|
||||
|
||||
|
|
|
@ -142,6 +142,7 @@ export declare namespace CredentialRequest {
|
|||
type: string;
|
||||
data: ICredentialDataDecryptedObject;
|
||||
projectId?: string;
|
||||
isManaged?: boolean;
|
||||
}>;
|
||||
|
||||
type Create = AuthenticatedRequest<{}, {}, CredentialProperties>;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
99
packages/cli/test/integration/ai/ai.api.test.ts
Normal file
99
packages/cli/test/integration/ai/ai.api.test.ts
Normal file
|
@ -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<AiService>({
|
||||
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);
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2783,6 +2783,7 @@ export interface IUserSettings {
|
|||
allowSSOManualLogin?: boolean;
|
||||
npsSurvey?: NpsSurveyState;
|
||||
easyAIWorkflowOnboarded?: boolean;
|
||||
userClaimedAiCredits?: boolean;
|
||||
}
|
||||
|
||||
export interface IProcessedDataConfig {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue