diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 97705b937cba..6947f8aa34a4 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -28,13 +28,6 @@ export type Scalars = { Upload: { input: any; output: any; } }; -export type AisqlQueryResult = { - __typename?: 'AISQLQueryResult'; - queryFailedErrorMessage?: Maybe; - sqlQuery: Scalars['String']['output']; - sqlQueryResult?: Maybe; -}; - export type ActivateWorkspaceInput = { displayName?: InputMaybe; }; @@ -862,7 +855,6 @@ export type Query = { findOneRemoteServerById: RemoteServer; findWorkspaceFromInviteHash: Workspace; findWorkspaceInvitations: Array; - getAISQLQuery: AisqlQueryResult; getAvailablePackages: Scalars['JSON']['output']; getPostgresCredentials?: Maybe; getProductPrices: ProductPricesEntity; @@ -930,11 +922,6 @@ export type QueryFindWorkspaceFromInviteHashArgs = { }; -export type QueryGetAisqlQueryArgs = { - text: Scalars['String']['input']; -}; - - export type QueryGetProductPricesArgs = { product: Scalars['String']['input']; }; diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 9e930133b3fb..846e7bed50af 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1,5 +1,5 @@ -import * as Apollo from '@apollo/client'; import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -21,13 +21,6 @@ export type Scalars = { Upload: any; }; -export type AisqlQueryResult = { - __typename?: 'AISQLQueryResult'; - queryFailedErrorMessage?: Maybe; - sqlQuery: Scalars['String']; - sqlQueryResult?: Maybe; -}; - export type ActivateWorkspaceInput = { displayName?: InputMaybe; }; @@ -160,13 +153,7 @@ export type ClientConfig = { support: Support; }; -export type CreateServerlessFunctionFromFileInput = { - description?: InputMaybe; - name: Scalars['String']; -}; - export type CreateServerlessFunctionInput = { - code: Scalars['String']; description?: InputMaybe; name: Scalars['String']; }; @@ -302,6 +289,36 @@ export type GetServerlessFunctionSourceCodeInput = { version?: Scalars['String']; }; +export type IndexConnection = { + __typename?: 'IndexConnection'; + /** Array of edges. */ + edges: Array; + /** Paging information */ + pageInfo: PageInfo; +}; + +export type IndexIndexFieldMetadatasConnection = { + __typename?: 'IndexIndexFieldMetadatasConnection'; + /** Array of edges. */ + edges: Array; + /** Paging information */ + pageInfo: PageInfo; +}; + +export type IndexObjectMetadataConnection = { + __typename?: 'IndexObjectMetadataConnection'; + /** Array of edges. */ + edges: Array; + /** Paging information */ + pageInfo: PageInfo; +}; + +/** Type of the index */ +export enum IndexType { + Btree = 'BTREE', + Gin = 'GIN' +} + export type InvalidatePassword = { __typename?: 'InvalidatePassword'; /** Boolean that confirms query was dispatched */ @@ -344,7 +361,6 @@ export type Mutation = { createOneAppToken: AppToken; createOneObject: Object; createOneServerlessFunction: ServerlessFunction; - createOneServerlessFunctionFromFile: ServerlessFunction; deactivateWorkflowVersion: Scalars['Boolean']; deleteCurrentWorkspace: Workspace; deleteOneObject: Object; @@ -426,12 +442,6 @@ export type MutationCreateOneServerlessFunctionArgs = { }; -export type MutationCreateOneServerlessFunctionFromFileArgs = { - file: Scalars['Upload']; - input: CreateServerlessFunctionFromFileInput; -}; - - export type MutationDeactivateWorkflowVersionArgs = { workflowVersionId: Scalars['String']; }; @@ -520,9 +530,8 @@ export type MutationSignUpArgs = { export type MutationTrackArgs = { - data: Scalars['JSON']; - sessionId: Scalars['String']; - type: Scalars['String']; + action: Scalars['String']; + payload: Scalars['JSON']; }; @@ -589,6 +598,14 @@ export type ObjectFieldsConnection = { pageInfo: PageInfo; }; +export type ObjectIndexMetadatasConnection = { + __typename?: 'ObjectIndexMetadatasConnection'; + /** Array of edges. */ + edges: Array; + /** Paging information */ + pageInfo: PageInfo; +}; + /** Onboarding status */ export enum OnboardingStatus { Completed = 'COMPLETED', @@ -654,15 +671,16 @@ export type Query = { currentWorkspace: Workspace; findWorkspaceFromInviteHash: Workspace; findWorkspaceInvitations: Array; - getAISQLQuery: AisqlQueryResult; getAvailablePackages: Scalars['JSON']; getPostgresCredentials?: Maybe; getProductPrices: ProductPricesEntity; - getServerlessFunctionSourceCode?: Maybe; + getServerlessFunctionSourceCode?: Maybe; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal; getTimelineThreadsFromPersonId: TimelineThreadsWithTotal; + index: Index; + indexMetadatas: IndexConnection; object: Object; objects: ObjectConnection; serverlessFunction: ServerlessFunction; @@ -692,11 +710,6 @@ export type QueryFindWorkspaceFromInviteHashArgs = { }; -export type QueryGetAisqlQueryArgs = { - text: Scalars['String']; -}; - - export type QueryGetProductPricesArgs = { product: Scalars['String']; }; @@ -831,7 +844,6 @@ export type ServerlessFunction = { latestVersion?: Maybe; name: Scalars['String']; runtime: Scalars['String']; - sourceCodeHash: Scalars['String']; syncStatus: ServerlessFunctionSyncStatus; updatedAt: Scalars['DateTime']; }; @@ -1028,7 +1040,7 @@ export type UpdateOneObjectInput = { }; export type UpdateServerlessFunctionInput = { - code: Scalars['String']; + code: Scalars['JSON']; description?: InputMaybe; /** Id of the serverless function to execute */ id: Scalars['UUID']; @@ -1213,6 +1225,7 @@ export type Field = { isCustom?: Maybe; isNullable?: Maybe; isSystem?: Maybe; + isUnique?: Maybe; label: Scalars['String']; name: Scalars['String']; object?: Maybe; @@ -1241,6 +1254,71 @@ export type FieldFilter = { or?: InputMaybe>; }; +export type Index = { + __typename?: 'index'; + createdAt: Scalars['DateTime']; + id: Scalars['UUID']; + indexFieldMetadatas: IndexIndexFieldMetadatasConnection; + indexType: IndexType; + indexWhereClause?: Maybe; + isCustom?: Maybe; + isUnique: Scalars['Boolean']; + name: Scalars['String']; + objectMetadata: IndexObjectMetadataConnection; + updatedAt: Scalars['DateTime']; +}; + + +export type IndexIndexFieldMetadatasArgs = { + filter?: IndexFieldFilter; + paging?: CursorPaging; +}; + + +export type IndexObjectMetadataArgs = { + filter?: ObjectFilter; + paging?: CursorPaging; +}; + +export type IndexEdge = { + __typename?: 'indexEdge'; + /** Cursor for this node. */ + cursor: Scalars['ConnectionCursor']; + /** The node containing the index */ + node: Index; +}; + +export type IndexField = { + __typename?: 'indexField'; + createdAt: Scalars['DateTime']; + fieldMetadataId: Scalars['UUID']; + id: Scalars['UUID']; + order: Scalars['Float']; + updatedAt: Scalars['DateTime']; +}; + +export type IndexFieldEdge = { + __typename?: 'indexFieldEdge'; + /** Cursor for this node. */ + cursor: Scalars['ConnectionCursor']; + /** The node containing the indexField */ + node: IndexField; +}; + +export type IndexFieldFilter = { + and?: InputMaybe>; + fieldMetadataId?: InputMaybe; + id?: InputMaybe; + or?: InputMaybe>; +}; + +export type IndexFilter = { + and?: InputMaybe>; + id?: InputMaybe; + isCustom?: InputMaybe; + or?: InputMaybe>; +}; + export type Object = { __typename?: 'object'; createdAt: Scalars['DateTime']; @@ -1250,6 +1328,7 @@ export type Object = { icon?: Maybe; id: Scalars['UUID']; imageIdentifierFieldMetadataId?: Maybe; + indexMetadatas: ObjectIndexMetadatasConnection; isActive: Scalars['Boolean']; isCustom: Scalars['Boolean']; isRemote: Scalars['Boolean']; @@ -1268,6 +1347,12 @@ export type ObjectFieldsArgs = { paging?: CursorPaging; }; + +export type ObjectIndexMetadatasArgs = { + filter?: IndexFilter; + paging?: CursorPaging; +}; + export type ObjectEdge = { __typename?: 'objectEdge'; /** Cursor for this node. */ @@ -1276,6 +1361,16 @@ export type ObjectEdge = { node: Object; }; +export type ObjectFilter = { + and?: InputMaybe>; + id?: InputMaybe; + isActive?: InputMaybe; + isCustom?: InputMaybe; + isRemote?: InputMaybe; + isSystem?: InputMaybe; + or?: InputMaybe>; +}; + export type Relation = { __typename?: 'relation'; createdAt: Scalars['DateTime']; @@ -1347,8 +1442,8 @@ export type GetTimelineThreadsFromPersonIdQueryVariables = Exact<{ export type GetTimelineThreadsFromPersonIdQuery = { __typename?: 'Query', getTimelineThreadsFromPersonId: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } }; export type TrackMutationVariables = Exact<{ - action: Scalars['String']; - payload: Scalars['JSON']; + type: Scalars['String']; + data: Scalars['JSON']; }>; @@ -1511,13 +1606,6 @@ export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string] export type SkipSyncEmailOnboardingStepMutation = { __typename?: 'Mutation', skipSyncEmailOnboardingStep: { __typename?: 'OnboardingStepSuccess', success: boolean } }; -export type GetAisqlQueryQueryVariables = Exact<{ - text: Scalars['String']; -}>; - - -export type GetAisqlQueryQuery = { __typename?: 'Query', getAISQLQuery: { __typename?: 'AISQLQueryResult', sqlQuery: string, sqlQueryResult?: string | null, queryFailedErrorMessage?: string | null } }; - export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }; export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>; @@ -1941,8 +2029,8 @@ export type GetTimelineThreadsFromPersonIdQueryHookResult = ReturnType; export type GetTimelineThreadsFromPersonIdQueryResult = Apollo.QueryResult; export const TrackDocument = gql` - mutation Track($action: String!, $payload: JSON!) { - track(action: $action, payload: $payload) { + mutation Track($type: String!, $data: JSON!) { + track(action: $type, payload: $data) { success } } @@ -1962,8 +2050,8 @@ export type TrackMutationFn = Apollo.MutationFunction; export type SkipSyncEmailOnboardingStepMutationResult = Apollo.MutationResult; export type SkipSyncEmailOnboardingStepMutationOptions = Apollo.BaseMutationOptions; -export const GetAisqlQueryDocument = gql` - query GetAISQLQuery($text: String!) { - getAISQLQuery(text: $text) { - sqlQuery - sqlQueryResult - queryFailedErrorMessage - } -} - `; - -/** - * __useGetAisqlQueryQuery__ - * - * To run a query within a React component, call `useGetAisqlQueryQuery` and pass it any options that fit your needs. - * When your component renders, `useGetAisqlQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGetAisqlQueryQuery({ - * variables: { - * text: // value for 'text' - * }, - * }); - */ -export function useGetAisqlQueryQuery(baseOptions: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(GetAisqlQueryDocument, options); - } -export function useGetAisqlQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(GetAisqlQueryDocument, options); - } -export type GetAisqlQueryQueryHookResult = ReturnType; -export type GetAisqlQueryLazyQueryHookResult = ReturnType; -export type GetAisqlQueryQueryResult = Apollo.QueryResult; export const DeleteUserAccountDocument = gql` mutation DeleteUserAccount { deleteUser { diff --git a/packages/twenty-front/src/modules/analytics/graphql/queries/track.ts b/packages/twenty-front/src/modules/analytics/graphql/queries/track.ts index 04a5d4487293..461c1f5172e9 100644 --- a/packages/twenty-front/src/modules/analytics/graphql/queries/track.ts +++ b/packages/twenty-front/src/modules/analytics/graphql/queries/track.ts @@ -1,8 +1,8 @@ import { gql } from '@apollo/client'; export const TRACK = gql` - mutation Track($type: String!, $sessionId: String!, $data: JSON!) { - track(type: $type, sessionId: $sessionId, data: $data) { + mutation Track($type: String!, $data: JSON!) { + track(action: $type, payload: $data) { success } } diff --git a/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts b/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts index 9136b83fcd04..8e11e55f9a08 100644 --- a/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts +++ b/packages/twenty-front/src/modules/apollo/services/__tests__/apollo.factory.test.ts @@ -41,8 +41,8 @@ const makeRequest = async () => { await client.mutate({ mutation: gql` - mutation Track($type: String!, $sessionId: String!, $data: JSON!) { - track(type: $type, sessionId: $sessionId, data: $data) { + mutation Track($type: String!, $data: JSON!) { + track(action: $type, payload: $data) { success } } diff --git a/packages/twenty-front/src/modules/search/queries/getTextToSQL.ts b/packages/twenty-front/src/modules/search/queries/getTextToSQL.ts deleted file mode 100644 index 6758209824b6..000000000000 --- a/packages/twenty-front/src/modules/search/queries/getTextToSQL.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { gql } from '@apollo/client'; - -export const getCopilot = gql` - query GetAISQLQuery($text: String!) { - getAISQLQuery(text: $text) { - sqlQuery - sqlQueryResult - queryFailedErrorMessage - } - } -`; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts index 1e166806be8d..d7e1e9b5a065 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts @@ -17,8 +17,6 @@ import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repos import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; -import { WorkspaceQueryRunnerService } from './workspace-query-runner.service'; - import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listener'; @Module({ @@ -36,12 +34,11 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen FeatureFlagModule, ], providers: [ - WorkspaceQueryRunnerService, ...workspaceQueryRunnerFactories, EntityEventsToDbListener, TelemetryListener, RecordPositionBackfillCommand, ], - exports: [WorkspaceQueryRunnerService, ...workspaceQueryRunnerFactories], + exports: [...workspaceQueryRunnerFactories], }) export class WorkspaceQueryRunnerModule {} diff --git a/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.module.ts b/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.module.ts deleted file mode 100644 index 0e0e9997c192..000000000000 --- a/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; -import { UserModule } from 'src/engine/core-modules/user/user.module'; -import { AISQLQueryResolver } from 'src/engine/core-modules/ai-sql-query/ai-sql-query.resolver'; -import { AISQLQueryService } from 'src/engine/core-modules/ai-sql-query/ai-sql-query.service'; -import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { WorkspaceQueryRunnerModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module'; -import { LLMChatModelModule } from 'src/engine/core-modules/llm-chat-model/llm-chat-model.module'; -import { EnvironmentModule } from 'src/engine/core-modules/environment/environment.module'; -import { LLMTracingModule } from 'src/engine/core-modules/llm-tracing/llm-tracing.module'; -import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; -import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module'; -@Module({ - imports: [ - WorkspaceDataSourceModule, - WorkspaceQueryRunnerModule, - UserModule, - TypeOrmModule.forFeature([FeatureFlagEntity], 'core'), - LLMChatModelModule, - LLMTracingModule, - EnvironmentModule, - ObjectMetadataModule, - WorkspaceSyncMetadataModule, - ], - exports: [], - providers: [AISQLQueryResolver, AISQLQueryService], -}) -export class AISQLQueryModule {} diff --git a/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.prompt-templates.ts b/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.prompt-templates.ts deleted file mode 100644 index 3b100c0b7711..000000000000 --- a/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.prompt-templates.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { PromptTemplate } from '@langchain/core/prompts'; - -export const sqlGenerationPromptTemplate = PromptTemplate.fromTemplate<{ - llmOutputJsonSchema: string; - sqlCreateTableStatements: string; - userQuestion: string; -}>(`Always respond following this JSON Schema: {llmOutputJsonSchema} - -Based on the table schema below, write a PostgreSQL query that would answer the user's question. All column names must be enclosed in double quotes. - -{sqlCreateTableStatements} - -Question: {userQuestion} -SQL Query:`); diff --git a/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.resolver.ts b/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.resolver.ts deleted file mode 100644 index c739c7e3ba06..000000000000 --- a/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.resolver.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { ForbiddenException, UseGuards } from '@nestjs/common'; -import { Args, ArgsType, Field, Query, Resolver } from '@nestjs/graphql'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Repository } from 'typeorm'; - -import { AISQLQueryService } from 'src/engine/core-modules/ai-sql-query/ai-sql-query.service'; -import { AISQLQueryResult } from 'src/engine/core-modules/ai-sql-query/dtos/ai-sql-query-result.dto'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; -import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { User } from 'src/engine/core-modules/user/user.entity'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; -import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; -import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; - -@ArgsType() -class GetAISQLQueryArgs { - @Field(() => String) - text: string; -} - -@UseGuards(WorkspaceAuthGuard, UserAuthGuard) -@Resolver(() => AISQLQueryResult) -export class AISQLQueryResolver { - constructor( - private readonly aiSqlQueryService: AISQLQueryService, - @InjectRepository(FeatureFlagEntity, 'core') - private readonly featureFlagRepository: Repository, - ) {} - - @Query(() => AISQLQueryResult) - async getAISQLQuery( - @AuthWorkspace() { id: workspaceId }: Workspace, - @AuthUser() user: User, - @Args() { text }: GetAISQLQueryArgs, - ) { - const isCopilotEnabledFeatureFlag = - await this.featureFlagRepository.findOneBy({ - workspaceId, - key: FeatureFlagKey.IsCopilotEnabled, - value: true, - }); - - if (!isCopilotEnabledFeatureFlag?.value) { - throw new ForbiddenException( - `${FeatureFlagKey.IsCopilotEnabled} feature flag is disabled`, - ); - } - - const traceMetadata = { - userId: user.id, - userEmail: user.email, - }; - - return this.aiSqlQueryService.generateAndExecute( - workspaceId, - text, - traceMetadata, - ); - } -} diff --git a/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.service.ts b/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.service.ts deleted file mode 100644 index 81cd32ec4a35..000000000000 --- a/packages/twenty-server/src/engine/core-modules/ai-sql-query/ai-sql-query.service.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; - -import { StructuredOutputParser } from '@langchain/core/output_parsers'; -import { RunnableSequence } from '@langchain/core/runnables'; -import groupBy from 'lodash.groupby'; -import { DataSource, QueryFailedError } from 'typeorm'; -import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; - -import { WorkspaceQueryRunnerService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service'; -import { sqlGenerationPromptTemplate } from 'src/engine/core-modules/ai-sql-query/ai-sql-query.prompt-templates'; -import { AISQLQueryResult } from 'src/engine/core-modules/ai-sql-query/dtos/ai-sql-query-result.dto'; -import { LLMChatModelService } from 'src/engine/core-modules/llm-chat-model/llm-chat-model.service'; -import { LLMTracingService } from 'src/engine/core-modules/llm-tracing/llm-tracing.service'; -import { DEFAULT_LABEL_IDENTIFIER_FIELD_NAME } from 'src/engine/metadata-modules/object-metadata/object-metadata.constants'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; -import { StandardObjectFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-object.factory'; - -@Injectable() -export class AISQLQueryService { - private readonly logger = new Logger(AISQLQueryService.name); - constructor( - private readonly workspaceDataSourceService: WorkspaceDataSourceService, - private readonly workspaceQueryRunnerService: WorkspaceQueryRunnerService, - private readonly llmChatModelService: LLMChatModelService, - private readonly llmTracingService: LLMTracingService, - private readonly standardObjectFactory: StandardObjectFactory, - ) {} - - private getLabelIdentifierName( - objectMetadata: ObjectMetadataEntity, - _dataSourceId, - _workspaceId, - _workspaceFeatureFlagsMap, - ): string | undefined { - const customObjectLabelIdentifierFieldMetadata = objectMetadata.fields.find( - (fieldMetadata) => - fieldMetadata.id === objectMetadata.labelIdentifierFieldMetadataId, - ); - - /* const standardObjectMetadataCollection = this.standardObjectFactory.create( - standardObjectMetadataDefinitions, - { workspaceId, dataSourceId }, - workspaceFeatureFlagsMap, - ); - - const standardObjectLabelIdentifierFieldMetadata = - standardObjectMetadataCollection - .find( - (standardObjectMetadata) => - standardObjectMetadata.nameSingular === objectMetadata.nameSingular, - ) - ?.fields.find( - (field: PartialFieldMetadata) => - field.name === DEFAULT_LABEL_IDENTIFIER_FIELD_NAME, - ) as PartialFieldMetadata; */ - - const labelIdentifierFieldMetadata = - customObjectLabelIdentifierFieldMetadata; /*?? - standardObjectLabelIdentifierFieldMetadata*/ - - return ( - labelIdentifierFieldMetadata?.name ?? DEFAULT_LABEL_IDENTIFIER_FIELD_NAME - ); - } - - private async getColInfosByTableName(dataSource: DataSource) { - const { schema } = dataSource.options as PostgresConnectionOptions; - - // From LangChain sql_utils.ts - const sqlQuery = `SELECT - t.table_name, - c.* - FROM - information_schema.tables t - JOIN information_schema.columns c - ON t.table_name = c.table_name - WHERE - t.table_schema = '${schema}' - AND c.table_schema = '${schema}' - ORDER BY - t.table_name, - c.ordinal_position;`; - const colInfos = await dataSource.query< - { - table_name: string; - column_name: string; - data_type: string | undefined; - is_nullable: 'YES' | 'NO'; - }[] - >(sqlQuery); - - return groupBy(colInfos, (colInfo) => colInfo.table_name); - } - - private getCreateTableStatement(tableName: string, colInfos: any[]) { - return `CREATE TABLE ${tableName} (\n ${colInfos - .map( - (colInfo) => - `${colInfo.column_name} ${colInfo.data_type} ${ - colInfo.is_nullable === 'YES' ? '' : 'NOT NULL' - }`, - ) - .join(', ')});`; - } - - private getRelationDescriptions() { - // TODO - Construct sentences like the following: - // investorId: a foreign key referencing the person table, indicating the investor who owns this portfolio company. - return ''; - } - - private getTableDescription(tableName: string, colInfos: any[]) { - return [ - this.getCreateTableStatement(tableName, colInfos), - this.getRelationDescriptions(), - ].join('\n'); - } - - private async getWorkspaceSchemaDescription( - dataSource: DataSource, - ): Promise { - const colInfoByTableName = await this.getColInfosByTableName(dataSource); - - return Object.entries(colInfoByTableName) - .map(([tableName, colInfos]) => - this.getTableDescription(tableName, colInfos), - ) - .join('\n\n'); - } - - private async generateWithDataSource( - workspaceId: string, - workspaceDataSource: DataSource, - userQuestion: string, - traceMetadata: Record = {}, - ) { - const workspaceSchemaName = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - workspaceDataSource.setOptions({ - schema: workspaceSchemaName, - }); - - const workspaceSchemaDescription = - await this.getWorkspaceSchemaDescription(workspaceDataSource); - - const llmOutputSchema = z.object({ - sqlQuery: z.string(), - }); - - const llmOutputJsonSchema = JSON.stringify( - zodToJsonSchema(llmOutputSchema), - ); - - const structuredOutputParser = - StructuredOutputParser.fromZodSchema(llmOutputSchema); - - const sqlQueryGeneratorChain = RunnableSequence.from([ - sqlGenerationPromptTemplate, - this.llmChatModelService.getJSONChatModel(), - structuredOutputParser, - ]); - - const metadata = { - workspaceId, - ...traceMetadata, - }; - const tracingCallbackHandler = - this.llmTracingService.getCallbackHandler(metadata); - - const { sqlQuery } = await sqlQueryGeneratorChain.invoke( - { - llmOutputJsonSchema, - sqlCreateTableStatements: workspaceSchemaDescription, - userQuestion, - }, - { - callbacks: [tracingCallbackHandler], - }, - ); - - return sqlQuery; - } - - async generate( - workspaceId: string, - userQuestion: string, - traceMetadata: Record = {}, - ) { - const workspaceDataSource = - await this.workspaceDataSourceService.connectToWorkspaceDataSource( - workspaceId, - ); - - return this.generateWithDataSource( - workspaceId, - workspaceDataSource, - userQuestion, - traceMetadata, - ); - } - - async generateAndExecute( - workspaceId: string, - userQuestion: string, - traceMetadata: Record = {}, - ): Promise { - const workspaceDataSource = - await this.workspaceDataSourceService.connectToWorkspaceDataSource( - workspaceId, - ); - - const sqlQuery = await this.generateWithDataSource( - workspaceId, - workspaceDataSource, - userQuestion, - traceMetadata, - ); - - try { - const sqlQueryResult: Record[] = - await this.workspaceQueryRunnerService.executeSQL( - workspaceDataSource, - workspaceId, - sqlQuery, - ); - - return { - sqlQuery, - sqlQueryResult: JSON.stringify(sqlQueryResult), - }; - } catch (error) { - if (error instanceof QueryFailedError) { - return { - sqlQuery, - queryFailedErrorMessage: error.message, - }; - } - - this.logger.error(error.message, error.stack); - - return { - sqlQuery, - }; - } - } -} diff --git a/packages/twenty-server/src/engine/core-modules/ai-sql-query/dtos/ai-sql-query-result.dto.ts b/packages/twenty-server/src/engine/core-modules/ai-sql-query/dtos/ai-sql-query-result.dto.ts deleted file mode 100644 index 1046631f3253..000000000000 --- a/packages/twenty-server/src/engine/core-modules/ai-sql-query/dtos/ai-sql-query-result.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Field, ObjectType } from '@nestjs/graphql'; - -import { IsOptional } from 'class-validator'; - -@ObjectType('AISQLQueryResult') -export class AISQLQueryResult { - @Field(() => String) - sqlQuery: string; - - @Field(() => String, { nullable: true }) - @IsOptional() - sqlQueryResult?: string; - - @Field(() => String, { nullable: true }) - @IsOptional() - queryFailedErrorMessage?: string; -} diff --git a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts index 2e2df06c4d8e..af651c18c5d2 100644 --- a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts +++ b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts @@ -3,7 +3,6 @@ import { HttpAdapterHost } from '@nestjs/core'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { ActorModule } from 'src/engine/core-modules/actor/actor.module'; -import { AISQLQueryModule } from 'src/engine/core-modules/ai-sql-query/ai-sql-query.module'; import { AppTokenModule } from 'src/engine/core-modules/app-token/app-token.module'; import { AuthModule } from 'src/engine/core-modules/auth/auth.module'; import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; @@ -62,7 +61,6 @@ import { FileModule } from './file/file.module'; UserModule, WorkspaceModule, WorkspaceInvitationModule, - AISQLQueryModule, PostgresCredentialsModule, WorkflowTriggerApiModule, WorkspaceEventEmitterModule,