feat: Add Agent Evaluation System and Refactor AI Modules (#16111)
## Summary This PR introduces a comprehensive agent evaluation system and refactors the AI module structure for better organization. ## Key Changes ### 🎯 Agent Evaluation System - Added **Agent Turn Evaluation** entities, DTOs, and database schema - New GraphQL mutations: `evaluateAgentTurn` and `runEvaluationInput` - Added `evaluationInputs` field to Agent entity for storing test inputs - New `AgentTurnGraderService` for automatic turn evaluation - Added evaluation UI with new **Evals** and **Logs** tabs in agent detail pages ### 🏗️ Entity & Module Refactoring - Renamed `AgentChatMessage` → `AgentMessage` for clarity - Consolidated chat entities: `AgentMessage`, `AgentTurn`, and `AgentChatThread` - Reorganized AI modules under `ai/` subdirectory structure - Updated imports across codebase to reflect new module paths ### 🤖 New Agents & Roles - Added **Dashboard Builder Agent** for dashboard creation and management - Added **Dashboard Manager Role** with appropriate permissions - Updated role permissions to be more granular (users vs agents vs API keys) ### 🔐 Permission System Updates - Added `HTTP_REQUEST_TOOL` permission flag - Updated Workflow Manager role permissions (restricted tool access) - Enhanced permission flag types to differentiate between user/agent/API key contexts - Added `isRelevantForAgents`, `isRelevantForApiKeys`, `isRelevantForUsers` to permission flags ### 📨 Message Role Enhancement - Added `system` role to `AgentMessageRole` enum (alongside user/assistant) - Updated message handling to support system prompts ### 🎨 UI/UX Improvements - New tabs in agent detail: **Evals** and **Logs** - Added turn detail page: `/ai/agents/:agentId/turns/:turnId` - Fixed text overflow in `SettingsListItemCardContent` - Updated role applicability labels ("Assignable to Workspace Members") ### 🛠️ Technical Improvements - Fixed Zod schema validation for UUID and Date fields (use string validators) - Updated `ToolRegistryService` to properly register HTTP tool with permission flag - Enhanced error handling in agent execution services - Updated database migrations for new entity schema ## Database Migrations - `1764210000000-add-system-role-to-agent-message.ts` - `1764220000000-add-evaluation-inputs-to-agent.ts` - `1764200000000-add-agent-turn-evaluation.ts` - `1764100000000-refactor-agent-chat-entities.ts` ## Testing - [ ] Agent evaluation flow tested - [ ] Dashboard Builder agent tested - [ ] Permission system validated - [ ] UI tabs and navigation tested - [ ] Database migrations run successfully ## Breaking Changes ⚠️ **Entity Rename**: `AgentChatMessage` renamed to `AgentMessage` - GraphQL queries need updating ## Related Issues <!-- Link any related issues here --> ## Screenshots <!-- Add screenshots if applicable -->
This commit is contained in:
@@ -53,6 +53,7 @@ export type Agent = {
|
||||
applicationId?: Maybe<Scalars['UUID']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
description?: Maybe<Scalars['String']>;
|
||||
evaluationInputs: Array<Scalars['String']>;
|
||||
icon?: Maybe<Scalars['String']>;
|
||||
id: Scalars['UUID'];
|
||||
isCustom: Scalars['Boolean'];
|
||||
@@ -67,17 +68,32 @@ export type Agent = {
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type AgentChatMessage = {
|
||||
__typename?: 'AgentChatMessage';
|
||||
export type AgentChatThread = {
|
||||
__typename?: 'AgentChatThread';
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['UUID'];
|
||||
parts: Array<AgentChatMessagePart>;
|
||||
role: Scalars['String'];
|
||||
threadId: Scalars['UUID'];
|
||||
title?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type AgentChatMessagePart = {
|
||||
__typename?: 'AgentChatMessagePart';
|
||||
export type AgentIdInput = {
|
||||
/** The id of the agent. */
|
||||
id: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type AgentMessage = {
|
||||
__typename?: 'AgentMessage';
|
||||
agentId?: Maybe<Scalars['UUID']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['UUID'];
|
||||
parts: Array<AgentMessagePart>;
|
||||
role: Scalars['String'];
|
||||
threadId: Scalars['UUID'];
|
||||
turnId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type AgentMessagePart = {
|
||||
__typename?: 'AgentMessagePart';
|
||||
createdAt: Scalars['DateTime'];
|
||||
errorDetails?: Maybe<Scalars['JSON']>;
|
||||
errorMessage?: Maybe<Scalars['String']>;
|
||||
@@ -105,17 +121,23 @@ export type AgentChatMessagePart = {
|
||||
type: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AgentChatThread = {
|
||||
__typename?: 'AgentChatThread';
|
||||
export type AgentTurn = {
|
||||
__typename?: 'AgentTurn';
|
||||
agentId?: Maybe<Scalars['UUID']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
evaluations: Array<AgentTurnEvaluation>;
|
||||
id: Scalars['UUID'];
|
||||
title?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
messages: Array<AgentMessage>;
|
||||
threadId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type AgentIdInput = {
|
||||
/** The id of the agent. */
|
||||
export type AgentTurnEvaluation = {
|
||||
__typename?: 'AgentTurnEvaluation';
|
||||
comment?: Maybe<Scalars['String']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['UUID'];
|
||||
score: Scalars['Int'];
|
||||
turnId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type AggregateChartConfiguration = {
|
||||
@@ -751,6 +773,7 @@ export type CoreViewSort = {
|
||||
|
||||
export type CreateAgentInput = {
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
evaluationInputs?: InputMaybe<Array<Scalars['String']>>;
|
||||
icon?: InputMaybe<Scalars['String']>;
|
||||
label: Scalars['String'];
|
||||
modelConfiguration?: InputMaybe<Scalars['JSON']>;
|
||||
@@ -1830,6 +1853,7 @@ export type Mutation = {
|
||||
emailPasswordResetLink: EmailPasswordResetLinkOutput;
|
||||
enablePostgresProxy: PostgresCredentials;
|
||||
endSubscriptionTrialPeriod: BillingEndTrialPeriodOutput;
|
||||
evaluateAgentTurn: AgentTurnEvaluation;
|
||||
executeOneServerlessFunction: ServerlessFunctionExecutionResult;
|
||||
generateApiKeyToken: ApiKeyToken;
|
||||
generateTransientToken: TransientTokenOutput;
|
||||
@@ -1850,6 +1874,7 @@ export type Mutation = {
|
||||
restorePageLayoutWidget: PageLayoutWidget;
|
||||
retryJobs: RetryJobsResponse;
|
||||
revokeApiKey?: Maybe<ApiKey>;
|
||||
runEvaluationInput: AgentTurn;
|
||||
runWorkflowVersion: RunWorkflowVersionOutput;
|
||||
saveImapSmtpCaldavAccount: ImapSmtpCaldavConnectionSuccess;
|
||||
sendInvitations: SendInvitationsOutput;
|
||||
@@ -2380,6 +2405,11 @@ export type MutationEmailPasswordResetLinkArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationEvaluateAgentTurnArgs = {
|
||||
turnId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationExecuteOneServerlessFunctionArgs = {
|
||||
input: ExecuteServerlessFunctionInput;
|
||||
};
|
||||
@@ -2484,6 +2514,12 @@ export type MutationRevokeApiKeyArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationRunEvaluationInputArgs = {
|
||||
agentId: Scalars['UUID'];
|
||||
input: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationRunWorkflowVersionArgs = {
|
||||
input: RunWorkflowVersionInput;
|
||||
};
|
||||
@@ -3084,6 +3120,7 @@ export enum PermissionFlagType {
|
||||
DATA_MODEL = 'DATA_MODEL',
|
||||
DOWNLOAD_FILE = 'DOWNLOAD_FILE',
|
||||
EXPORT_CSV = 'EXPORT_CSV',
|
||||
HTTP_REQUEST_TOOL = 'HTTP_REQUEST_TOOL',
|
||||
IMPERSONATE = 'IMPERSONATE',
|
||||
IMPORT_CSV = 'IMPORT_CSV',
|
||||
LAYOUTS = 'LAYOUTS',
|
||||
@@ -3172,10 +3209,11 @@ export type PublishServerlessFunctionInput = {
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
agentTurns: Array<AgentTurn>;
|
||||
apiKey?: Maybe<ApiKey>;
|
||||
apiKeys: Array<ApiKey>;
|
||||
billingPortalSession: BillingSessionOutput;
|
||||
chatMessages: Array<AgentChatMessage>;
|
||||
chatMessages: Array<AgentMessage>;
|
||||
chatThread: AgentChatThread;
|
||||
chatThreads: Array<AgentChatThread>;
|
||||
checkUserExists: CheckUserExistOutput;
|
||||
@@ -3257,6 +3295,11 @@ export type Query = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryAgentTurnsArgs = {
|
||||
agentId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryApiKeyArgs = {
|
||||
input: GetApiKeyInput;
|
||||
};
|
||||
@@ -4124,6 +4167,7 @@ export type UuidFilterComparison = {
|
||||
|
||||
export type UpdateAgentInput = {
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
evaluationInputs?: InputMaybe<Array<Scalars['String']>>;
|
||||
icon?: InputMaybe<Scalars['String']>;
|
||||
id: Scalars['UUID'];
|
||||
label?: InputMaybe<Scalars['String']>;
|
||||
@@ -4904,7 +4948,7 @@ export type WorkspaceUrlsAndId = {
|
||||
workspaceUrls: WorkspaceUrls;
|
||||
};
|
||||
|
||||
export type AgentFieldsFragment = { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string };
|
||||
export type AgentFieldsFragment = { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string };
|
||||
|
||||
export type AssignRoleToAgentMutationVariables = Exact<{
|
||||
agentId: Scalars['UUID'];
|
||||
@@ -4924,14 +4968,21 @@ export type CreateOneAgentMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateOneAgentMutation = { __typename?: 'Mutation', createOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string } };
|
||||
export type CreateOneAgentMutation = { __typename?: 'Mutation', createOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string } };
|
||||
|
||||
export type DeleteOneAgentMutationVariables = Exact<{
|
||||
input: AgentIdInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type DeleteOneAgentMutation = { __typename?: 'Mutation', deleteOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string } };
|
||||
export type DeleteOneAgentMutation = { __typename?: 'Mutation', deleteOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string } };
|
||||
|
||||
export type EvaluateAgentTurnMutationVariables = Exact<{
|
||||
turnId: Scalars['UUID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type EvaluateAgentTurnMutation = { __typename?: 'Mutation', evaluateAgentTurn: { __typename?: 'AgentTurnEvaluation', id: string, turnId: string, score: number, comment?: string | null, createdAt: string } };
|
||||
|
||||
export type RemoveRoleFromAgentMutationVariables = Exact<{
|
||||
agentId: Scalars['UUID'];
|
||||
@@ -4940,31 +4991,46 @@ export type RemoveRoleFromAgentMutationVariables = Exact<{
|
||||
|
||||
export type RemoveRoleFromAgentMutation = { __typename?: 'Mutation', removeRoleFromAgent: boolean };
|
||||
|
||||
export type RunEvaluationInputMutationVariables = Exact<{
|
||||
agentId: Scalars['UUID'];
|
||||
input: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type RunEvaluationInputMutation = { __typename?: 'Mutation', runEvaluationInput: { __typename?: 'AgentTurn', id: string, threadId: string, agentId?: string | null, createdAt: string, evaluations: Array<{ __typename?: 'AgentTurnEvaluation', id: string, score: number, comment?: string | null, createdAt: string }> } };
|
||||
|
||||
export type UpdateOneAgentMutationVariables = Exact<{
|
||||
input: UpdateAgentInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateOneAgentMutation = { __typename?: 'Mutation', updateOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string } };
|
||||
export type UpdateOneAgentMutation = { __typename?: 'Mutation', updateOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string } };
|
||||
|
||||
export type FindManyAgentsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindManyAgentsQuery = { __typename?: 'Query', findManyAgents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string }> };
|
||||
export type FindManyAgentsQuery = { __typename?: 'Query', findManyAgents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string }> };
|
||||
|
||||
export type FindOneAgentQueryVariables = Exact<{
|
||||
id: Scalars['UUID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type FindOneAgentQuery = { __typename?: 'Query', findOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string } };
|
||||
export type FindOneAgentQuery = { __typename?: 'Query', findOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string } };
|
||||
|
||||
export type GetAgentTurnsQueryVariables = Exact<{
|
||||
agentId: Scalars['UUID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetAgentTurnsQuery = { __typename?: 'Query', agentTurns: Array<{ __typename?: 'AgentTurn', id: string, threadId: string, agentId?: string | null, createdAt: string, evaluations: Array<{ __typename?: 'AgentTurnEvaluation', id: string, score: number, comment?: string | null, createdAt: string }>, messages: Array<{ __typename?: 'AgentMessage', id: string, role: string, createdAt: string, parts: Array<{ __typename?: 'AgentMessagePart', id: string, type: string, textContent?: string | null, reasoningContent?: string | null, toolName?: string | null, toolCallId?: string | null, toolInput?: any | null, toolOutput?: any | null, errorMessage?: string | null, state?: string | null, errorDetails?: any | null, sourceUrlSourceId?: string | null, sourceUrlUrl?: string | null, sourceUrlTitle?: string | null, sourceDocumentSourceId?: string | null, sourceDocumentMediaType?: string | null, sourceDocumentTitle?: string | null, sourceDocumentFilename?: string | null, fileMediaType?: string | null, fileFilename?: string | null, fileUrl?: string | null, providerMetadata?: any | null }> }> }> };
|
||||
|
||||
export type GetChatMessagesQueryVariables = Exact<{
|
||||
threadId: Scalars['UUID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetChatMessagesQuery = { __typename?: 'Query', chatMessages: Array<{ __typename?: 'AgentChatMessage', id: string, threadId: string, role: string, createdAt: string, parts: Array<{ __typename?: 'AgentChatMessagePart', id: string, messageId: string, orderIndex: number, type: string, textContent?: string | null, reasoningContent?: string | null, toolName?: string | null, toolCallId?: string | null, toolInput?: any | null, toolOutput?: any | null, state?: string | null, errorMessage?: string | null, errorDetails?: any | null, sourceUrlSourceId?: string | null, sourceUrlUrl?: string | null, sourceUrlTitle?: string | null, sourceDocumentSourceId?: string | null, sourceDocumentMediaType?: string | null, sourceDocumentTitle?: string | null, sourceDocumentFilename?: string | null, fileMediaType?: string | null, fileFilename?: string | null, fileUrl?: string | null, providerMetadata?: any | null, createdAt: string }> }> };
|
||||
export type GetChatMessagesQuery = { __typename?: 'Query', chatMessages: Array<{ __typename?: 'AgentMessage', id: string, threadId: string, turnId: string, role: string, createdAt: string, parts: Array<{ __typename?: 'AgentMessagePart', id: string, messageId: string, orderIndex: number, type: string, textContent?: string | null, reasoningContent?: string | null, toolName?: string | null, toolCallId?: string | null, toolInput?: any | null, toolOutput?: any | null, state?: string | null, errorMessage?: string | null, errorDetails?: any | null, sourceUrlSourceId?: string | null, sourceUrlUrl?: string | null, sourceUrlTitle?: string | null, sourceDocumentSourceId?: string | null, sourceDocumentMediaType?: string | null, sourceDocumentTitle?: string | null, sourceDocumentFilename?: string | null, fileMediaType?: string | null, fileFilename?: string | null, fileUrl?: string | null, providerMetadata?: any | null, createdAt: string }> }> };
|
||||
|
||||
export type GetChatThreadsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -4990,7 +5056,7 @@ export type UpdateOneApplicationVariableMutationVariables = Exact<{
|
||||
|
||||
export type UpdateOneApplicationVariableMutation = { __typename?: 'Mutation', updateOneApplicationVariable: boolean };
|
||||
|
||||
export type ApplicationFieldsFragment = { __typename?: 'Application', id: string, name: string, description: string, version: string, universalIdentifier: string, canBeUninstalled: boolean, applicationVariables: Array<{ __typename?: 'ApplicationVariable', id: string, key: string, value: string, description: string, isSecret: boolean }>, agents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string }>, objects: Array<{ __typename?: 'Object', id: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, isUIReadOnly: boolean, createdAt: string, updatedAt: string, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, applicationId?: string | null, shortcut?: string | null, isLabelSyncedWithName: boolean, isSearchable: boolean, duplicateCriteria?: Array<Array<string>> | null, indexMetadataList: Array<{ __typename?: 'Index', id: string, createdAt: string, updatedAt: string, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, isCustom?: boolean | null, indexFieldMetadataList: Array<{ __typename?: 'IndexField', id: string, fieldMetadataId: string, createdAt: string, updatedAt: string, order: number }> }>, fieldsList: Array<{ __typename?: 'Field', id: string, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isUIReadOnly?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: string, updatedAt: string, defaultValue?: any | null, options?: any | null, settings?: any | null, isLabelSyncedWithName?: boolean | null, relation?: { __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } } | null, morphRelations?: Array<{ __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } }> | null }> }>, serverlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: string, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, publishedVersions: Array<string>, handlerPath: string, handlerName: string, createdAt: string, updatedAt: string, cronTriggers?: Array<{ __typename?: 'CronTrigger', id: string, settings: any, createdAt: string, updatedAt: string }> | null, databaseEventTriggers?: Array<{ __typename?: 'DatabaseEventTrigger', id: string, settings: any, createdAt: string, updatedAt: string }> | null, routeTriggers?: Array<{ __typename?: 'RouteTrigger', id: string, path: string, isAuthRequired: boolean, httpMethod: HttpMethod, createdAt: string, updatedAt: string }> | null }> };
|
||||
export type ApplicationFieldsFragment = { __typename?: 'Application', id: string, name: string, description: string, version: string, universalIdentifier: string, canBeUninstalled: boolean, applicationVariables: Array<{ __typename?: 'ApplicationVariable', id: string, key: string, value: string, description: string, isSecret: boolean }>, agents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string }>, objects: Array<{ __typename?: 'Object', id: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, isUIReadOnly: boolean, createdAt: string, updatedAt: string, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, applicationId?: string | null, shortcut?: string | null, isLabelSyncedWithName: boolean, isSearchable: boolean, duplicateCriteria?: Array<Array<string>> | null, indexMetadataList: Array<{ __typename?: 'Index', id: string, createdAt: string, updatedAt: string, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, isCustom?: boolean | null, indexFieldMetadataList: Array<{ __typename?: 'IndexField', id: string, fieldMetadataId: string, createdAt: string, updatedAt: string, order: number }> }>, fieldsList: Array<{ __typename?: 'Field', id: string, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isUIReadOnly?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: string, updatedAt: string, defaultValue?: any | null, options?: any | null, settings?: any | null, isLabelSyncedWithName?: boolean | null, relation?: { __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } } | null, morphRelations?: Array<{ __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } }> | null }> }>, serverlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: string, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, publishedVersions: Array<string>, handlerPath: string, handlerName: string, createdAt: string, updatedAt: string, cronTriggers?: Array<{ __typename?: 'CronTrigger', id: string, settings: any, createdAt: string, updatedAt: string }> | null, databaseEventTriggers?: Array<{ __typename?: 'DatabaseEventTrigger', id: string, settings: any, createdAt: string, updatedAt: string }> | null, routeTriggers?: Array<{ __typename?: 'RouteTrigger', id: string, path: string, isAuthRequired: boolean, httpMethod: HttpMethod, createdAt: string, updatedAt: string }> | null }> };
|
||||
|
||||
export type FindManyApplicationsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -5002,7 +5068,7 @@ export type FindOneApplicationQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type FindOneApplicationQuery = { __typename?: 'Query', findOneApplication: { __typename?: 'Application', id: string, name: string, description: string, version: string, universalIdentifier: string, canBeUninstalled: boolean, applicationVariables: Array<{ __typename?: 'ApplicationVariable', id: string, key: string, value: string, description: string, isSecret: boolean }>, agents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string }>, objects: Array<{ __typename?: 'Object', id: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, isUIReadOnly: boolean, createdAt: string, updatedAt: string, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, applicationId?: string | null, shortcut?: string | null, isLabelSyncedWithName: boolean, isSearchable: boolean, duplicateCriteria?: Array<Array<string>> | null, indexMetadataList: Array<{ __typename?: 'Index', id: string, createdAt: string, updatedAt: string, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, isCustom?: boolean | null, indexFieldMetadataList: Array<{ __typename?: 'IndexField', id: string, fieldMetadataId: string, createdAt: string, updatedAt: string, order: number }> }>, fieldsList: Array<{ __typename?: 'Field', id: string, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isUIReadOnly?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: string, updatedAt: string, defaultValue?: any | null, options?: any | null, settings?: any | null, isLabelSyncedWithName?: boolean | null, relation?: { __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } } | null, morphRelations?: Array<{ __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } }> | null }> }>, serverlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: string, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, publishedVersions: Array<string>, handlerPath: string, handlerName: string, createdAt: string, updatedAt: string, cronTriggers?: Array<{ __typename?: 'CronTrigger', id: string, settings: any, createdAt: string, updatedAt: string }> | null, databaseEventTriggers?: Array<{ __typename?: 'DatabaseEventTrigger', id: string, settings: any, createdAt: string, updatedAt: string }> | null, routeTriggers?: Array<{ __typename?: 'RouteTrigger', id: string, path: string, isAuthRequired: boolean, httpMethod: HttpMethod, createdAt: string, updatedAt: string }> | null }> } };
|
||||
export type FindOneApplicationQuery = { __typename?: 'Query', findOneApplication: { __typename?: 'Application', id: string, name: string, description: string, version: string, universalIdentifier: string, canBeUninstalled: boolean, applicationVariables: Array<{ __typename?: 'ApplicationVariable', id: string, key: string, value: string, description: string, isSecret: boolean }>, agents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string }>, objects: Array<{ __typename?: 'Object', id: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, isUIReadOnly: boolean, createdAt: string, updatedAt: string, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, applicationId?: string | null, shortcut?: string | null, isLabelSyncedWithName: boolean, isSearchable: boolean, duplicateCriteria?: Array<Array<string>> | null, indexMetadataList: Array<{ __typename?: 'Index', id: string, createdAt: string, updatedAt: string, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, isCustom?: boolean | null, indexFieldMetadataList: Array<{ __typename?: 'IndexField', id: string, fieldMetadataId: string, createdAt: string, updatedAt: string, order: number }> }>, fieldsList: Array<{ __typename?: 'Field', id: string, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isUIReadOnly?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: string, updatedAt: string, defaultValue?: any | null, options?: any | null, settings?: any | null, isLabelSyncedWithName?: boolean | null, relation?: { __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } } | null, morphRelations?: Array<{ __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } }> | null }> }>, serverlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: string, name: string, description?: string | null, runtime: string, timeoutSeconds: number, latestVersion?: string | null, publishedVersions: Array<string>, handlerPath: string, handlerName: string, createdAt: string, updatedAt: string, cronTriggers?: Array<{ __typename?: 'CronTrigger', id: string, settings: any, createdAt: string, updatedAt: string }> | null, databaseEventTriggers?: Array<{ __typename?: 'DatabaseEventTrigger', id: string, settings: any, createdAt: string, updatedAt: string }> | null, routeTriggers?: Array<{ __typename?: 'RouteTrigger', id: string, path: string, isAuthRequired: boolean, httpMethod: HttpMethod, createdAt: string, updatedAt: string }> | null }> } };
|
||||
|
||||
export type UploadFileMutationVariables = Exact<{
|
||||
file: Scalars['Upload'];
|
||||
@@ -5782,7 +5848,7 @@ export type UpsertPermissionFlagsMutation = { __typename?: 'Mutation', upsertPer
|
||||
export type GetRolesQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }>, agents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, applicationId?: string | null, createdAt: string, updatedAt: string }>, apiKeys: Array<{ __typename?: 'ApiKeyForRole', id: string, name: string, expiresAt: string, revokedAt?: string | null }>, permissionFlags?: Array<{ __typename?: 'PermissionFlag', id: string, flag: PermissionFlagType, roleId: string }> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, fieldPermissions?: Array<{ __typename?: 'FieldPermission', objectMetadataId: string, fieldMetadataId: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null, id: string, roleId: string }> | null }> };
|
||||
export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }>, agents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array<string>, applicationId?: string | null, createdAt: string, updatedAt: string }>, apiKeys: Array<{ __typename?: 'ApiKeyForRole', id: string, name: string, expiresAt: string, revokedAt?: string | null }>, permissionFlags?: Array<{ __typename?: 'PermissionFlag', id: string, flag: PermissionFlagType, roleId: string }> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, fieldPermissions?: Array<{ __typename?: 'FieldPermission', objectMetadataId: string, fieldMetadataId: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null, id: string, roleId: string }> | null }> };
|
||||
|
||||
export type CreateApprovedAccessDomainMutationVariables = Exact<{
|
||||
input: CreateApprovedAccessDomainInput;
|
||||
@@ -6435,6 +6501,7 @@ export const AgentFieldsFragmentDoc = gql`
|
||||
roleId
|
||||
isCustom
|
||||
modelConfiguration
|
||||
evaluationInputs
|
||||
applicationId
|
||||
createdAt
|
||||
updatedAt
|
||||
@@ -7211,6 +7278,43 @@ export function useDeleteOneAgentMutation(baseOptions?: Apollo.MutationHookOptio
|
||||
export type DeleteOneAgentMutationHookResult = ReturnType<typeof useDeleteOneAgentMutation>;
|
||||
export type DeleteOneAgentMutationResult = Apollo.MutationResult<DeleteOneAgentMutation>;
|
||||
export type DeleteOneAgentMutationOptions = Apollo.BaseMutationOptions<DeleteOneAgentMutation, DeleteOneAgentMutationVariables>;
|
||||
export const EvaluateAgentTurnDocument = gql`
|
||||
mutation EvaluateAgentTurn($turnId: UUID!) {
|
||||
evaluateAgentTurn(turnId: $turnId) {
|
||||
id
|
||||
turnId
|
||||
score
|
||||
comment
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type EvaluateAgentTurnMutationFn = Apollo.MutationFunction<EvaluateAgentTurnMutation, EvaluateAgentTurnMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useEvaluateAgentTurnMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useEvaluateAgentTurnMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useEvaluateAgentTurnMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [evaluateAgentTurnMutation, { data, loading, error }] = useEvaluateAgentTurnMutation({
|
||||
* variables: {
|
||||
* turnId: // value for 'turnId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useEvaluateAgentTurnMutation(baseOptions?: Apollo.MutationHookOptions<EvaluateAgentTurnMutation, EvaluateAgentTurnMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<EvaluateAgentTurnMutation, EvaluateAgentTurnMutationVariables>(EvaluateAgentTurnDocument, options);
|
||||
}
|
||||
export type EvaluateAgentTurnMutationHookResult = ReturnType<typeof useEvaluateAgentTurnMutation>;
|
||||
export type EvaluateAgentTurnMutationResult = Apollo.MutationResult<EvaluateAgentTurnMutation>;
|
||||
export type EvaluateAgentTurnMutationOptions = Apollo.BaseMutationOptions<EvaluateAgentTurnMutation, EvaluateAgentTurnMutationVariables>;
|
||||
export const RemoveRoleFromAgentDocument = gql`
|
||||
mutation RemoveRoleFromAgent($agentId: UUID!) {
|
||||
removeRoleFromAgent(agentId: $agentId)
|
||||
@@ -7242,6 +7346,49 @@ export function useRemoveRoleFromAgentMutation(baseOptions?: Apollo.MutationHook
|
||||
export type RemoveRoleFromAgentMutationHookResult = ReturnType<typeof useRemoveRoleFromAgentMutation>;
|
||||
export type RemoveRoleFromAgentMutationResult = Apollo.MutationResult<RemoveRoleFromAgentMutation>;
|
||||
export type RemoveRoleFromAgentMutationOptions = Apollo.BaseMutationOptions<RemoveRoleFromAgentMutation, RemoveRoleFromAgentMutationVariables>;
|
||||
export const RunEvaluationInputDocument = gql`
|
||||
mutation RunEvaluationInput($agentId: UUID!, $input: String!) {
|
||||
runEvaluationInput(agentId: $agentId, input: $input) {
|
||||
id
|
||||
threadId
|
||||
agentId
|
||||
createdAt
|
||||
evaluations {
|
||||
id
|
||||
score
|
||||
comment
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type RunEvaluationInputMutationFn = Apollo.MutationFunction<RunEvaluationInputMutation, RunEvaluationInputMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useRunEvaluationInputMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useRunEvaluationInputMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useRunEvaluationInputMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [runEvaluationInputMutation, { data, loading, error }] = useRunEvaluationInputMutation({
|
||||
* variables: {
|
||||
* agentId: // value for 'agentId'
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useRunEvaluationInputMutation(baseOptions?: Apollo.MutationHookOptions<RunEvaluationInputMutation, RunEvaluationInputMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<RunEvaluationInputMutation, RunEvaluationInputMutationVariables>(RunEvaluationInputDocument, options);
|
||||
}
|
||||
export type RunEvaluationInputMutationHookResult = ReturnType<typeof useRunEvaluationInputMutation>;
|
||||
export type RunEvaluationInputMutationResult = Apollo.MutationResult<RunEvaluationInputMutation>;
|
||||
export type RunEvaluationInputMutationOptions = Apollo.BaseMutationOptions<RunEvaluationInputMutation, RunEvaluationInputMutationVariables>;
|
||||
export const UpdateOneAgentDocument = gql`
|
||||
mutation UpdateOneAgent($input: UpdateAgentInput!) {
|
||||
updateOneAgent(input: $input) {
|
||||
@@ -7344,11 +7491,85 @@ export function useFindOneAgentLazyQuery(baseOptions?: Apollo.LazyQueryHookOptio
|
||||
export type FindOneAgentQueryHookResult = ReturnType<typeof useFindOneAgentQuery>;
|
||||
export type FindOneAgentLazyQueryHookResult = ReturnType<typeof useFindOneAgentLazyQuery>;
|
||||
export type FindOneAgentQueryResult = Apollo.QueryResult<FindOneAgentQuery, FindOneAgentQueryVariables>;
|
||||
export const GetAgentTurnsDocument = gql`
|
||||
query GetAgentTurns($agentId: UUID!) {
|
||||
agentTurns(agentId: $agentId) {
|
||||
id
|
||||
threadId
|
||||
agentId
|
||||
createdAt
|
||||
evaluations {
|
||||
id
|
||||
score
|
||||
comment
|
||||
createdAt
|
||||
}
|
||||
messages {
|
||||
id
|
||||
role
|
||||
createdAt
|
||||
parts {
|
||||
id
|
||||
type
|
||||
textContent
|
||||
reasoningContent
|
||||
toolName
|
||||
toolCallId
|
||||
toolInput
|
||||
toolOutput
|
||||
errorMessage
|
||||
state
|
||||
errorDetails
|
||||
sourceUrlSourceId
|
||||
sourceUrlUrl
|
||||
sourceUrlTitle
|
||||
sourceDocumentSourceId
|
||||
sourceDocumentMediaType
|
||||
sourceDocumentTitle
|
||||
sourceDocumentFilename
|
||||
fileMediaType
|
||||
fileFilename
|
||||
fileUrl
|
||||
providerMetadata
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetAgentTurnsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetAgentTurnsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetAgentTurnsQuery` 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 } = useGetAgentTurnsQuery({
|
||||
* variables: {
|
||||
* agentId: // value for 'agentId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetAgentTurnsQuery(baseOptions: Apollo.QueryHookOptions<GetAgentTurnsQuery, GetAgentTurnsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetAgentTurnsQuery, GetAgentTurnsQueryVariables>(GetAgentTurnsDocument, options);
|
||||
}
|
||||
export function useGetAgentTurnsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetAgentTurnsQuery, GetAgentTurnsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetAgentTurnsQuery, GetAgentTurnsQueryVariables>(GetAgentTurnsDocument, options);
|
||||
}
|
||||
export type GetAgentTurnsQueryHookResult = ReturnType<typeof useGetAgentTurnsQuery>;
|
||||
export type GetAgentTurnsLazyQueryHookResult = ReturnType<typeof useGetAgentTurnsLazyQuery>;
|
||||
export type GetAgentTurnsQueryResult = Apollo.QueryResult<GetAgentTurnsQuery, GetAgentTurnsQueryVariables>;
|
||||
export const GetChatMessagesDocument = gql`
|
||||
query GetChatMessages($threadId: UUID!) {
|
||||
chatMessages(threadId: $threadId) {
|
||||
id
|
||||
threadId
|
||||
turnId
|
||||
role
|
||||
createdAt
|
||||
parts {
|
||||
|
||||
@@ -53,6 +53,7 @@ export type Agent = {
|
||||
applicationId?: Maybe<Scalars['UUID']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
description?: Maybe<Scalars['String']>;
|
||||
evaluationInputs: Array<Scalars['String']>;
|
||||
icon?: Maybe<Scalars['String']>;
|
||||
id: Scalars['UUID'];
|
||||
isCustom: Scalars['Boolean'];
|
||||
@@ -67,17 +68,32 @@ export type Agent = {
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type AgentChatMessage = {
|
||||
__typename?: 'AgentChatMessage';
|
||||
export type AgentChatThread = {
|
||||
__typename?: 'AgentChatThread';
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['UUID'];
|
||||
parts: Array<AgentChatMessagePart>;
|
||||
role: Scalars['String'];
|
||||
threadId: Scalars['UUID'];
|
||||
title?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type AgentChatMessagePart = {
|
||||
__typename?: 'AgentChatMessagePart';
|
||||
export type AgentIdInput = {
|
||||
/** The id of the agent. */
|
||||
id: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type AgentMessage = {
|
||||
__typename?: 'AgentMessage';
|
||||
agentId?: Maybe<Scalars['UUID']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['UUID'];
|
||||
parts: Array<AgentMessagePart>;
|
||||
role: Scalars['String'];
|
||||
threadId: Scalars['UUID'];
|
||||
turnId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type AgentMessagePart = {
|
||||
__typename?: 'AgentMessagePart';
|
||||
createdAt: Scalars['DateTime'];
|
||||
errorDetails?: Maybe<Scalars['JSON']>;
|
||||
errorMessage?: Maybe<Scalars['String']>;
|
||||
@@ -105,17 +121,23 @@ export type AgentChatMessagePart = {
|
||||
type: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AgentChatThread = {
|
||||
__typename?: 'AgentChatThread';
|
||||
export type AgentTurn = {
|
||||
__typename?: 'AgentTurn';
|
||||
agentId?: Maybe<Scalars['UUID']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
evaluations: Array<AgentTurnEvaluation>;
|
||||
id: Scalars['UUID'];
|
||||
title?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
messages: Array<AgentMessage>;
|
||||
threadId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type AgentIdInput = {
|
||||
/** The id of the agent. */
|
||||
export type AgentTurnEvaluation = {
|
||||
__typename?: 'AgentTurnEvaluation';
|
||||
comment?: Maybe<Scalars['String']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['UUID'];
|
||||
score: Scalars['Int'];
|
||||
turnId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type AggregateChartConfiguration = {
|
||||
@@ -751,6 +773,7 @@ export type CoreViewSort = {
|
||||
|
||||
export type CreateAgentInput = {
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
evaluationInputs?: InputMaybe<Array<Scalars['String']>>;
|
||||
icon?: InputMaybe<Scalars['String']>;
|
||||
label: Scalars['String'];
|
||||
modelConfiguration?: InputMaybe<Scalars['JSON']>;
|
||||
@@ -3018,6 +3041,7 @@ export enum PermissionFlagType {
|
||||
DATA_MODEL = 'DATA_MODEL',
|
||||
DOWNLOAD_FILE = 'DOWNLOAD_FILE',
|
||||
EXPORT_CSV = 'EXPORT_CSV',
|
||||
HTTP_REQUEST_TOOL = 'HTTP_REQUEST_TOOL',
|
||||
IMPERSONATE = 'IMPERSONATE',
|
||||
IMPORT_CSV = 'IMPORT_CSV',
|
||||
LAYOUTS = 'LAYOUTS',
|
||||
@@ -3980,6 +4004,7 @@ export type UuidFilterComparison = {
|
||||
|
||||
export type UpdateAgentInput = {
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
evaluationInputs?: InputMaybe<Array<Scalars['String']>>;
|
||||
icon?: InputMaybe<Scalars['String']>;
|
||||
id: Scalars['UUID'];
|
||||
label?: InputMaybe<Scalars['String']>;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useRecoilValue } from 'recoil';
|
||||
import { Avatar, IconSparkles } from 'twenty-ui/display';
|
||||
|
||||
import { AgentChatFilePreview } from '@/ai/components/internal/AgentChatFilePreview';
|
||||
import { AgentChatMessageRole } from '@/ai/constants/AgentChatMessageRole';
|
||||
import { AgentMessageRole } from '@/ai/constants/AgentMessageRole';
|
||||
|
||||
import { AIChatAssistantMessageRenderer } from '@/ai/components/AIChatAssistantMessageRenderer';
|
||||
import { AIChatErrorMessage } from '@/ai/components/AIChatErrorMessage';
|
||||
@@ -161,17 +161,17 @@ export const AIChatMessage = ({
|
||||
);
|
||||
|
||||
const showError =
|
||||
isDefined(error) && message.role === AgentChatMessageRole.ASSISTANT;
|
||||
isDefined(error) && message.role === AgentMessageRole.ASSISTANT;
|
||||
|
||||
const fileParts = message.parts.filter((part) => part.type === 'file');
|
||||
|
||||
return (
|
||||
<StyledMessageBubble
|
||||
key={message.id}
|
||||
isUser={message.role === AgentChatMessageRole.USER}
|
||||
isUser={message.role === AgentMessageRole.USER}
|
||||
>
|
||||
<StyledMessageRow>
|
||||
{message.role === AgentChatMessageRole.ASSISTANT && (
|
||||
{message.role === AgentMessageRole.ASSISTANT && (
|
||||
<StyledAvatarContainer>
|
||||
<Avatar
|
||||
size="sm"
|
||||
@@ -181,15 +181,13 @@ export const AIChatMessage = ({
|
||||
/>
|
||||
</StyledAvatarContainer>
|
||||
)}
|
||||
{message.role === AgentChatMessageRole.USER && (
|
||||
{message.role === AgentMessageRole.USER && (
|
||||
<StyledAvatarContainer isUser>
|
||||
<Avatar size="sm" placeholder="U" type="rounded" />
|
||||
</StyledAvatarContainer>
|
||||
)}
|
||||
<StyledMessageContainer>
|
||||
<StyledMessageText
|
||||
isUser={message.role === AgentChatMessageRole.USER}
|
||||
>
|
||||
<StyledMessageText isUser={message.role === AgentMessageRole.USER}>
|
||||
<AIChatAssistantMessageRenderer
|
||||
isLastMessageStreaming={isLastMessageStreaming}
|
||||
messageParts={message.parts}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum AgentChatMessageRole {
|
||||
USER = 'user',
|
||||
ASSISTANT = 'assistant',
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum AgentMessageRole {
|
||||
SYSTEM = 'system',
|
||||
USER = 'user',
|
||||
ASSISTANT = 'assistant',
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export const AGENT_FRAGMENT = gql`
|
||||
roleId
|
||||
isCustom
|
||||
modelConfiguration
|
||||
evaluationInputs
|
||||
applicationId
|
||||
createdAt
|
||||
updatedAt
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const EVALUATE_AGENT_TURN = gql`
|
||||
mutation EvaluateAgentTurn($turnId: UUID!) {
|
||||
evaluateAgentTurn(turnId: $turnId) {
|
||||
id
|
||||
turnId
|
||||
score
|
||||
comment
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const RUN_EVALUATION_INPUT = gql`
|
||||
mutation RunEvaluationInput($agentId: UUID!, $input: String!) {
|
||||
runEvaluationInput(agentId: $agentId, input: $input) {
|
||||
id
|
||||
threadId
|
||||
agentId
|
||||
createdAt
|
||||
evaluations {
|
||||
id
|
||||
score
|
||||
comment
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,47 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_AGENT_TURNS = gql`
|
||||
query GetAgentTurns($agentId: UUID!) {
|
||||
agentTurns(agentId: $agentId) {
|
||||
id
|
||||
threadId
|
||||
agentId
|
||||
createdAt
|
||||
evaluations {
|
||||
id
|
||||
score
|
||||
comment
|
||||
createdAt
|
||||
}
|
||||
messages {
|
||||
id
|
||||
role
|
||||
createdAt
|
||||
parts {
|
||||
id
|
||||
type
|
||||
textContent
|
||||
reasoningContent
|
||||
toolName
|
||||
toolCallId
|
||||
toolInput
|
||||
toolOutput
|
||||
errorMessage
|
||||
state
|
||||
errorDetails
|
||||
sourceUrlSourceId
|
||||
sourceUrlUrl
|
||||
sourceUrlTitle
|
||||
sourceDocumentSourceId
|
||||
sourceDocumentMediaType
|
||||
sourceDocumentTitle
|
||||
sourceDocumentFilename
|
||||
fileMediaType
|
||||
fileFilename
|
||||
fileUrl
|
||||
providerMetadata
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -5,6 +5,7 @@ export const GET_CHAT_MESSAGES = gql`
|
||||
chatMessages(threadId: $threadId) {
|
||||
id
|
||||
threadId
|
||||
turnId
|
||||
role
|
||||
createdAt
|
||||
parts {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { type AgentChatMessage } from '~/generated-metadata/graphql';
|
||||
import { type AgentMessage } from '~/generated-metadata/graphql';
|
||||
|
||||
export const AgentChatMessagesComponentInstanceContext =
|
||||
createComponentInstanceContext();
|
||||
|
||||
export const agentChatMessagesComponentState = createComponentState<
|
||||
AgentChatMessage[]
|
||||
AgentMessage[]
|
||||
>({
|
||||
key: 'agentChatMessagesComponentState',
|
||||
defaultValue: [],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { mapDBPartToUIMessagePart } from '@/ai/utils/mapDBPartToUIMessagePart';
|
||||
import { type ExtendedUIMessage } from 'twenty-shared/ai';
|
||||
import { type AgentChatMessage } from '~/generated/graphql';
|
||||
import { type AgentMessage } from '~/generated/graphql';
|
||||
|
||||
export const mapDBMessagesToUIMessages = (
|
||||
dbMessages: AgentChatMessage[],
|
||||
dbMessages: AgentMessage[],
|
||||
): ExtendedUIMessage[] => {
|
||||
return dbMessages.map((dbMessage) => ({
|
||||
id: dbMessage.id,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type ReasoningUIPart, type ToolUIPart } from 'ai';
|
||||
import { type ExtendedUIMessagePart } from 'twenty-shared/ai';
|
||||
import { type AgentChatMessagePart } from '~/generated/graphql';
|
||||
import { type AgentMessagePart } from '~/generated/graphql';
|
||||
|
||||
export const mapDBPartToUIMessagePart = (
|
||||
part: AgentChatMessagePart,
|
||||
part: AgentMessagePart,
|
||||
): ExtendedUIMessagePart => {
|
||||
switch (part.type) {
|
||||
case 'text':
|
||||
|
||||
@@ -165,6 +165,12 @@ const SettingsAgentForm = lazy(() =>
|
||||
})),
|
||||
);
|
||||
|
||||
const SettingsAgentTurnDetail = lazy(() =>
|
||||
import('~/pages/settings/ai/SettingsAgentTurnDetail').then((module) => ({
|
||||
default: module.SettingsAgentTurnDetail,
|
||||
})),
|
||||
);
|
||||
|
||||
const SettingsWorkspaceMembers = lazy(() =>
|
||||
import('~/pages/settings/SettingsWorkspaceMembers').then((module) => ({
|
||||
default: module.SettingsWorkspaceMembers,
|
||||
@@ -422,6 +428,10 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => (
|
||||
path={SettingsPath.AIAgentDetail}
|
||||
element={<SettingsAgentForm mode="edit" />}
|
||||
/>
|
||||
<Route
|
||||
path={SettingsPath.AIAgentTurnDetail}
|
||||
element={<SettingsAgentTurnDetail />}
|
||||
/>
|
||||
<Route path={SettingsPath.Billing} element={<SettingsBilling />} />
|
||||
<Route path={SettingsPath.Domain} element={<SettingsDomain />} />
|
||||
<Route
|
||||
|
||||
+10
-2
@@ -33,9 +33,17 @@ const StyledRightContainer = styled.div`
|
||||
`;
|
||||
|
||||
const StyledContent = styled.div`
|
||||
flex: 1 0 auto;
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.span`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.span`
|
||||
@@ -81,7 +89,7 @@ export const SettingsListItemCardContent = ({
|
||||
/>
|
||||
)}
|
||||
<StyledContent>
|
||||
{label}
|
||||
<StyledLabel>{label}</StyledLabel>
|
||||
{!!description && <StyledDescription>{description}</StyledDescription>}
|
||||
</StyledContent>
|
||||
<StyledRightContainer>
|
||||
|
||||
+93
-17
@@ -68,78 +68,117 @@ export const SettingsRolePermissionsSettingsSection = ({
|
||||
name: t`API Keys & Webhooks`,
|
||||
description: t`Manage API keys and webhooks`,
|
||||
Icon: IconCode,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.WORKSPACE,
|
||||
name: t`Workspace`,
|
||||
description: t`Set global workspace preferences`,
|
||||
Icon: IconSettings,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.WORKSPACE_MEMBERS,
|
||||
name: t`Users`,
|
||||
description: t`Add or remove users`,
|
||||
Icon: IconUsers,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.ROLES,
|
||||
name: t`Roles`,
|
||||
description: t`Define user roles and access levels`,
|
||||
Icon: IconLockOpen,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.DATA_MODEL,
|
||||
name: t`Data Model`,
|
||||
description: t`Edit data structure and fields`,
|
||||
Icon: IconHierarchy,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.SECURITY,
|
||||
name: t`Security`,
|
||||
description: t`Manage security policies`,
|
||||
Icon: IconKey,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.WORKFLOWS,
|
||||
name: t`Workflows`,
|
||||
description: t`Manage workflows`,
|
||||
Icon: IconSettingsAutomation,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.SSO_BYPASS,
|
||||
name: t`SSO Bypass`,
|
||||
description: t`Enable bypass options`,
|
||||
Icon: IconShield,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: false,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.IMPERSONATE,
|
||||
name: t`Impersonate`,
|
||||
description: t`Impersonate workspace users`,
|
||||
Icon: IconSpy,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: false,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.APPLICATIONS,
|
||||
name: t`Applications`,
|
||||
description: t`Install and manage applications`,
|
||||
Icon: IconApps,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.LAYOUTS,
|
||||
name: t`Layouts`,
|
||||
description: t`Customize page layouts and UI structure`,
|
||||
Icon: IconLayoutSidebarRightCollapse,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.BILLING,
|
||||
name: t`Billing`,
|
||||
description: t`Manage billing and subscriptions`,
|
||||
Icon: IconCreditCard,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: false,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.AI_SETTINGS,
|
||||
name: t`AI`,
|
||||
description: t`Create and configure AI agents`,
|
||||
Icon: IconSparkles,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -153,30 +192,67 @@ export const SettingsRolePermissionsSettingsSection = ({
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter based on role assignment capabilities
|
||||
const canBeAssignedOnlyToAgents =
|
||||
settingsDraftRole.canBeAssignedToAgents &&
|
||||
!settingsDraftRole.canBeAssignedToUsers &&
|
||||
!settingsDraftRole.canBeAssignedToApiKeys;
|
||||
|
||||
const canBeAssignedOnlyToApiKeys =
|
||||
settingsDraftRole.canBeAssignedToApiKeys &&
|
||||
!settingsDraftRole.canBeAssignedToUsers &&
|
||||
!settingsDraftRole.canBeAssignedToAgents;
|
||||
|
||||
const canBeAssignedOnlyToUsers =
|
||||
settingsDraftRole.canBeAssignedToUsers &&
|
||||
!settingsDraftRole.canBeAssignedToAgents &&
|
||||
!settingsDraftRole.canBeAssignedToApiKeys;
|
||||
|
||||
if (canBeAssignedOnlyToAgents && !permission.isRelevantForAgents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canBeAssignedOnlyToApiKeys && !permission.isRelevantForApiKeys) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canBeAssignedOnlyToUsers && !permission.isRelevantForUsers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [isAIEnabled, isApplicationEnabled]);
|
||||
}, [isAIEnabled, isApplicationEnabled, settingsDraftRole]);
|
||||
|
||||
const shouldShowAllAccessToggle =
|
||||
!settingsDraftRole.canBeAssignedToAgents ||
|
||||
settingsDraftRole.canBeAssignedToUsers;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title title={t`Settings`} description={t`Settings permissions`} />
|
||||
<StyledCard rounded>
|
||||
<SettingsOptionCardContentToggle
|
||||
Icon={IconSettings}
|
||||
title={t`Settings All Access`}
|
||||
description={t`Ability to edit all settings`}
|
||||
checked={settingsDraftRole.canUpdateAllSettings}
|
||||
disabled={!isEditable}
|
||||
onChange={() => {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
canUpdateAllSettings: !settingsDraftRole.canUpdateAllSettings,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledCard>
|
||||
{shouldShowAllAccessToggle && (
|
||||
<StyledCard rounded>
|
||||
<SettingsOptionCardContentToggle
|
||||
Icon={IconSettings}
|
||||
title={t`Settings All Access`}
|
||||
description={t`Ability to edit all settings`}
|
||||
checked={settingsDraftRole.canUpdateAllSettings}
|
||||
disabled={!isEditable}
|
||||
onChange={() => {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
canUpdateAllSettings: !settingsDraftRole.canUpdateAllSettings,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledCard>
|
||||
)}
|
||||
<AnimatedExpandableContainer
|
||||
isExpanded={!settingsDraftRole.canUpdateAllSettings}
|
||||
isExpanded={
|
||||
!shouldShowAllAccessToggle || !settingsDraftRole.canUpdateAllSettings
|
||||
}
|
||||
dimension="height"
|
||||
animationDurations={{
|
||||
opacity: 0.2,
|
||||
|
||||
+90
-16
@@ -10,6 +10,7 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
H2Title,
|
||||
IconApi,
|
||||
IconAt,
|
||||
IconDownload,
|
||||
IconFileExport,
|
||||
@@ -62,6 +63,9 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Chat with AI agents and use AI features`,
|
||||
Icon: IconSparkles,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.UPLOAD_FILE,
|
||||
@@ -69,6 +73,9 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Allow uploading files and attachments`,
|
||||
Icon: IconFileUpload,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.DOWNLOAD_FILE,
|
||||
@@ -76,6 +83,9 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Allow downloading files and attachments`,
|
||||
Icon: IconDownload,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.SEND_EMAIL_TOOL,
|
||||
@@ -83,6 +93,19 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Send emails via connected accounts`,
|
||||
Icon: IconMail,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.HTTP_REQUEST_TOOL,
|
||||
name: t`HTTP Request`,
|
||||
description: t`Make HTTP requests to external APIs`,
|
||||
Icon: IconApi,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: false,
|
||||
isRelevantForUsers: false,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.IMPORT_CSV,
|
||||
@@ -90,6 +113,9 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Allow importing data from CSV files`,
|
||||
Icon: IconFileImport,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.EXPORT_CSV,
|
||||
@@ -97,6 +123,9 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Allow exporting data to CSV files`,
|
||||
Icon: IconFileExport,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.CONNECTED_ACCOUNTS,
|
||||
@@ -104,6 +133,9 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Sync email and calendar accounts`,
|
||||
Icon: IconAt,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: false,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.PROFILE_INFORMATION,
|
||||
@@ -111,6 +143,9 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Edit own profile information`,
|
||||
Icon: IconUser,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: false,
|
||||
isRelevantForApiKeys: false,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
{
|
||||
key: PermissionFlagType.VIEWS,
|
||||
@@ -118,6 +153,9 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
description: t`Create, edit, and delete workspace views`,
|
||||
Icon: IconTable,
|
||||
isToolPermission: true,
|
||||
isRelevantForAgents: true,
|
||||
isRelevantForApiKeys: true,
|
||||
isRelevantForUsers: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -125,29 +163,65 @@ export const SettingsRolePermissionsToolSection = ({
|
||||
if (permission.key === PermissionFlagType.AI && !isAIEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const canBeAssignedOnlyToAgents =
|
||||
settingsDraftRole.canBeAssignedToAgents &&
|
||||
!settingsDraftRole.canBeAssignedToUsers &&
|
||||
!settingsDraftRole.canBeAssignedToApiKeys;
|
||||
|
||||
const canBeAssignedOnlyToApiKeys =
|
||||
settingsDraftRole.canBeAssignedToApiKeys &&
|
||||
!settingsDraftRole.canBeAssignedToUsers &&
|
||||
!settingsDraftRole.canBeAssignedToAgents;
|
||||
|
||||
const canBeAssignedOnlyToUsers =
|
||||
settingsDraftRole.canBeAssignedToUsers &&
|
||||
!settingsDraftRole.canBeAssignedToAgents &&
|
||||
!settingsDraftRole.canBeAssignedToApiKeys;
|
||||
|
||||
if (canBeAssignedOnlyToAgents && !permission.isRelevantForAgents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canBeAssignedOnlyToApiKeys && !permission.isRelevantForApiKeys) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canBeAssignedOnlyToUsers && !permission.isRelevantForUsers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const shouldShowAllAccessToggle =
|
||||
!settingsDraftRole.canBeAssignedToAgents ||
|
||||
settingsDraftRole.canBeAssignedToUsers;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title title={t`Actions`} description={t`Actions permissions`} />
|
||||
<StyledCard rounded>
|
||||
<SettingsOptionCardContentToggle
|
||||
Icon={IconTool}
|
||||
title={t`All Actions Access`}
|
||||
description={t`Grants permission to perform all available actions without restriction`}
|
||||
checked={settingsDraftRole.canAccessAllTools}
|
||||
disabled={!isEditable}
|
||||
onChange={() => {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
canAccessAllTools: !settingsDraftRole.canAccessAllTools,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledCard>
|
||||
{shouldShowAllAccessToggle && (
|
||||
<StyledCard rounded>
|
||||
<SettingsOptionCardContentToggle
|
||||
Icon={IconTool}
|
||||
title={t`All Actions Access`}
|
||||
description={t`Grants permission to perform all available actions without restriction`}
|
||||
checked={settingsDraftRole.canAccessAllTools}
|
||||
disabled={!isEditable}
|
||||
onChange={() => {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
canAccessAllTools: !settingsDraftRole.canAccessAllTools,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledCard>
|
||||
)}
|
||||
<AnimatedExpandableContainer
|
||||
isExpanded={!settingsDraftRole.canAccessAllTools}
|
||||
isExpanded={
|
||||
!shouldShowAllAccessToggle || !settingsDraftRole.canAccessAllTools
|
||||
}
|
||||
dimension="height"
|
||||
animationDurations={{
|
||||
opacity: 0.2,
|
||||
|
||||
+3
@@ -7,4 +7,7 @@ export type SettingsRolePermissionsSettingPermission = {
|
||||
description: string;
|
||||
Icon: IconComponent;
|
||||
isToolPermission?: boolean;
|
||||
isRelevantForAgents?: boolean;
|
||||
isRelevantForApiKeys?: boolean;
|
||||
isRelevantForUsers?: boolean;
|
||||
};
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ export const SettingsRoleApplicability = ({
|
||||
const options = [
|
||||
{
|
||||
key: 'canBeAssignedToUsers' as const,
|
||||
label: t`Assignable to team members`,
|
||||
label: t`Assignable to Workspace Members`,
|
||||
Icon: IconUsers,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
@@ -16,7 +17,12 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { AppPath, SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
|
||||
import { IconLock, IconSettings } from 'twenty-ui/display';
|
||||
import {
|
||||
IconList,
|
||||
IconListCheck,
|
||||
IconLock,
|
||||
IconSettings,
|
||||
} from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import {
|
||||
type CreateAgentInput,
|
||||
@@ -27,10 +33,12 @@ import {
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { SettingsAgentDetailSkeletonLoader } from './components/SettingsAgentDetailSkeletonLoader';
|
||||
import { SettingsAgentEvalsTab } from './components/SettingsAgentEvalsTab';
|
||||
import { SettingsAgentLogsTab } from './components/SettingsAgentLogsTab';
|
||||
import { SettingsAgentRoleTab } from './components/SettingsAgentRoleTab';
|
||||
import { SettingsAgentSettingsTab } from './components/SettingsAgentSettingsTab';
|
||||
import { SETTINGS_AGENT_DETAIL_TABS } from './constants/SettingsAgentDetailTabs';
|
||||
@@ -54,6 +62,9 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
const navigateApp = useNavigateApp();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const [isReadonlyMode, setIsReadonlyMode] = useState(false);
|
||||
const [originalFormValues, setOriginalFormValues] = useState<
|
||||
ReturnType<typeof useSettingsAgentFormState>['formValues'] | null
|
||||
>(null);
|
||||
|
||||
const isEditMode = mode === 'edit';
|
||||
const isCreateMode = mode === 'create';
|
||||
@@ -82,7 +93,7 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
if (isDefined(agent.applicationId)) {
|
||||
setIsReadonlyMode(true);
|
||||
}
|
||||
resetForm({
|
||||
const initialValues = {
|
||||
name: agent.name,
|
||||
label: agent.label,
|
||||
description: agent.description,
|
||||
@@ -93,7 +104,10 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
isCustom: agent.isCustom,
|
||||
modelConfiguration: agent.modelConfiguration || {},
|
||||
responseFormat: agent.responseFormat || { type: 'text', schema: {} },
|
||||
});
|
||||
evaluationInputs: agent.evaluationInputs || [],
|
||||
};
|
||||
resetForm(initialValues);
|
||||
setOriginalFormValues(initialValues);
|
||||
} else {
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Agent not found`,
|
||||
@@ -130,6 +144,94 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
isDefined(formValues.role) &&
|
||||
!isDeeplyEqual(settingsDraftRole, settingsPersistedRole);
|
||||
|
||||
const autoSave = useDebouncedCallback(async () => {
|
||||
if (
|
||||
isCreateMode ||
|
||||
isReadonlyMode ||
|
||||
!validateForm() ||
|
||||
isSubmitting ||
|
||||
!agent
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasChanges =
|
||||
originalFormValues && !isDeeplyEqual(formValues, originalFormValues);
|
||||
|
||||
if (!hasChanges && !isRoleDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
if (isRoleDirty && isDefined(formValues.role)) {
|
||||
try {
|
||||
await saveDraftRoleToDB();
|
||||
} catch (error) {
|
||||
if (error instanceof ApolloError) {
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
} else {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to save role permissions: ${errorMessage}`,
|
||||
});
|
||||
}
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await updateAgent({
|
||||
variables: {
|
||||
input: {
|
||||
id: agent.id,
|
||||
name: formValues.name || '',
|
||||
label: formValues.label,
|
||||
description: formValues.description,
|
||||
icon: formValues.icon,
|
||||
modelId: formValues.modelId,
|
||||
roleId: formValues.role,
|
||||
prompt: formValues.prompt,
|
||||
modelConfiguration: formValues.modelConfiguration,
|
||||
responseFormat: formValues.responseFormat,
|
||||
evaluationInputs: formValues.evaluationInputs,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
setOriginalFormValues({ ...formValues });
|
||||
} catch (error) {
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, 1_000);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEditMode && !loading && isDefined(originalFormValues)) {
|
||||
autoSave();
|
||||
}
|
||||
}, [
|
||||
formValues,
|
||||
isRoleDirty,
|
||||
isEditMode,
|
||||
loading,
|
||||
originalFormValues,
|
||||
autoSave,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
autoSave.flush();
|
||||
};
|
||||
}, [autoSave]);
|
||||
|
||||
if (!isCreateMode && !loading && !agent) {
|
||||
return null;
|
||||
}
|
||||
@@ -147,6 +249,16 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
title: t`Role`,
|
||||
Icon: IconLock,
|
||||
},
|
||||
{
|
||||
id: SETTINGS_AGENT_DETAIL_TABS.TABS_IDS.EVALS,
|
||||
title: t`Evals`,
|
||||
Icon: IconListCheck,
|
||||
},
|
||||
{
|
||||
id: SETTINGS_AGENT_DETAIL_TABS.TABS_IDS.LOGS,
|
||||
title: t`Logs`,
|
||||
Icon: IconList,
|
||||
},
|
||||
];
|
||||
|
||||
const handleSave = async () => {
|
||||
@@ -192,6 +304,7 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
prompt: formValues.prompt,
|
||||
modelConfiguration: formValues.modelConfiguration,
|
||||
responseFormat: formValues.responseFormat,
|
||||
evaluationInputs: formValues.evaluationInputs,
|
||||
};
|
||||
|
||||
await createAgent({
|
||||
@@ -218,6 +331,7 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
prompt: formValues.prompt,
|
||||
modelConfiguration: formValues.modelConfiguration,
|
||||
responseFormat: formValues.responseFormat,
|
||||
evaluationInputs: formValues.evaluationInputs,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -275,6 +389,26 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
agent={agent}
|
||||
/>
|
||||
);
|
||||
|
||||
case SETTINGS_AGENT_DETAIL_TABS.TABS_IDS.EVALS:
|
||||
return (
|
||||
<SettingsAgentEvalsTab
|
||||
agentId={agentId}
|
||||
evaluationInputs={formValues.evaluationInputs}
|
||||
onEvaluationInputsChange={(inputs) =>
|
||||
handleFieldChange('evaluationInputs', inputs)
|
||||
}
|
||||
disabled={
|
||||
process.env.NODE_ENV === 'development'
|
||||
? isReadonlyMode
|
||||
: isReadonlyMode || (isEditMode ? !agent?.isCustom : false)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
case SETTINGS_AGENT_DETAIL_TABS.TABS_IDS.LOGS:
|
||||
return <SettingsAgentLogsTab agentId={agentId} />;
|
||||
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
@@ -286,7 +420,7 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => {
|
||||
<SubMenuTopBarContainer
|
||||
title={title}
|
||||
actionButton={
|
||||
isCreateMode || (isEditMode && agent?.isCustom) ? (
|
||||
isCreateMode ? (
|
||||
<SaveAndCancelButtons
|
||||
onSave={handleSave}
|
||||
onCancel={handleCancel}
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
import { AIChatAssistantMessageRenderer } from '@/ai/components/AIChatAssistantMessageRenderer';
|
||||
import { mapDBMessagesToUIMessages } from '@/ai/utils/mapDBMessagesToUIMessages';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath } from 'twenty-shared/utils';
|
||||
import { H2Title, Status } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { GET_AGENT_TURNS } from '@/ai/graphql/queries/getAgentTurns';
|
||||
|
||||
const StyledTable = styled(Table)`
|
||||
margin-top: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
const StyledTableHeaderRow = styled(TableRow)`
|
||||
grid-template-columns: 140px 80px 1fr;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
grid-template-columns: 140px 80px 1fr;
|
||||
`;
|
||||
|
||||
const StyledDateCell = styled(TableCell)`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`;
|
||||
|
||||
const StyledScoreCell = styled(TableCell)`
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledCommentCell = styled(TableCell)`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledMessagesContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledMessageBubble = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledMessageRole = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const StyledMessageContent = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
max-width: 100%;
|
||||
`;
|
||||
|
||||
export const SettingsAgentTurnDetail = () => {
|
||||
const { agentId, turnId } = useParams<{
|
||||
agentId: string;
|
||||
turnId: string;
|
||||
}>();
|
||||
|
||||
const { data, loading } = useQuery(GET_AGENT_TURNS, {
|
||||
variables: { agentId: agentId || '' },
|
||||
skip: !agentId,
|
||||
});
|
||||
|
||||
const turn = data?.agentTurns?.find((t: any) => t.id === turnId);
|
||||
|
||||
const getScoreColor = (score: number) => {
|
||||
if (score >= 80) return 'green';
|
||||
if (score >= 60) return 'orange';
|
||||
return 'red';
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={t`Turn Details`}
|
||||
links={[
|
||||
{
|
||||
children: t`Workspace`,
|
||||
href: getSettingsPath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: t`AI`, href: getSettingsPath(SettingsPath.AI) },
|
||||
{
|
||||
children: t`Agent`,
|
||||
href: getSettingsPath(SettingsPath.AIAgentDetail).replace(
|
||||
':agentId',
|
||||
agentId || '',
|
||||
),
|
||||
},
|
||||
{ children: t`Turn` },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Skeleton height={200} />
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
}
|
||||
|
||||
if (!turn) {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={t`Turn Not Found`}
|
||||
links={[
|
||||
{
|
||||
children: t`Workspace`,
|
||||
href: getSettingsPath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: t`AI`, href: getSettingsPath(SettingsPath.AI) },
|
||||
{ children: t`Turn` },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<div>{t`Turn not found`}</div>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={t`Turn Details`}
|
||||
links={[
|
||||
{
|
||||
children: t`Workspace`,
|
||||
href: getSettingsPath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: t`AI`, href: getSettingsPath(SettingsPath.AI) },
|
||||
{
|
||||
children: t`Agent`,
|
||||
href: getSettingsPath(SettingsPath.AIAgentDetail).replace(
|
||||
':agentId',
|
||||
agentId || '',
|
||||
),
|
||||
},
|
||||
{ children: t`Turn` },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`Messages`}
|
||||
description={new Date(turn.createdAt).toLocaleString('en-US', {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'short',
|
||||
})}
|
||||
/>
|
||||
{turn.messages && turn.messages.length > 0 ? (
|
||||
<StyledMessagesContainer>
|
||||
{mapDBMessagesToUIMessages(
|
||||
[...turn.messages]
|
||||
.filter((msg: any) => msg.parts && msg.parts.length > 0)
|
||||
.sort((a: any, b: any) => {
|
||||
if (a.role === 'user' && b.role === 'assistant') return -1;
|
||||
if (a.role === 'assistant' && b.role === 'user') return 1;
|
||||
return (
|
||||
new Date(a.createdAt).getTime() -
|
||||
new Date(b.createdAt).getTime()
|
||||
);
|
||||
}),
|
||||
).map((message) => {
|
||||
const roleLabel =
|
||||
message.role === 'user'
|
||||
? t`User`
|
||||
: message.role === 'system'
|
||||
? t`System`
|
||||
: t`Assistant`;
|
||||
return (
|
||||
<StyledMessageBubble key={message.id}>
|
||||
<StyledMessageRole>{roleLabel}</StyledMessageRole>
|
||||
<StyledMessageContent>
|
||||
<AIChatAssistantMessageRenderer
|
||||
messageParts={message.parts}
|
||||
isLastMessageStreaming={false}
|
||||
/>
|
||||
</StyledMessageContent>
|
||||
</StyledMessageBubble>
|
||||
);
|
||||
})}
|
||||
</StyledMessagesContainer>
|
||||
) : (
|
||||
<div>{t`No messages found for this turn`}</div>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<H2Title title={t`Evaluations`} />
|
||||
{turn.evaluations && turn.evaluations.length > 0 ? (
|
||||
<StyledTable>
|
||||
<StyledTableHeaderRow>
|
||||
<TableHeader>{t`Date`}</TableHeader>
|
||||
<TableHeader>{t`Score`}</TableHeader>
|
||||
<TableHeader>{t`Comment`}</TableHeader>
|
||||
</StyledTableHeaderRow>
|
||||
{[...turn.evaluations]
|
||||
.sort(
|
||||
(a: any, b: any) =>
|
||||
new Date(b.createdAt).getTime() -
|
||||
new Date(a.createdAt).getTime(),
|
||||
)
|
||||
.map((evaluation: any) => (
|
||||
<StyledTableRow key={evaluation.id}>
|
||||
<StyledDateCell>
|
||||
{new Date(evaluation.createdAt).toLocaleDateString(
|
||||
'en-US',
|
||||
{
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
},
|
||||
)}
|
||||
</StyledDateCell>
|
||||
<StyledScoreCell>
|
||||
<Status
|
||||
color={getScoreColor(evaluation.score)}
|
||||
text={`${evaluation.score}`}
|
||||
/>
|
||||
</StyledScoreCell>
|
||||
<StyledCommentCell>
|
||||
{evaluation.comment || t`No comment`}
|
||||
</StyledCommentCell>
|
||||
</StyledTableRow>
|
||||
))}
|
||||
</StyledTable>
|
||||
) : (
|
||||
<div>{t`No evaluations yet for this turn`}</div>
|
||||
)}
|
||||
</Section>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,193 @@
|
||||
import { RUN_EVALUATION_INPUT } from '@/ai/graphql/mutations/runEvaluationInput';
|
||||
import { SettingsListCard } from '@/settings/components/SettingsListCard';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
IconDotsVertical,
|
||||
IconMessage,
|
||||
IconPlayerPlay,
|
||||
IconPlus,
|
||||
IconTrash,
|
||||
} from 'twenty-ui/display';
|
||||
import { Button, LightIconButton } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const DELETE_EVAL_INPUT_MODAL_ID = 'delete-eval-input-modal';
|
||||
|
||||
const StyledInputContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
margin-bottom: ${({ theme }) => theme.spacing(6)};
|
||||
margin-top: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledEmptyMessage = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`;
|
||||
|
||||
type SettingsAgentEvalsTabProps = {
|
||||
agentId: string;
|
||||
evaluationInputs: string[];
|
||||
onEvaluationInputsChange: (inputs: string[]) => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
type EvalInput = {
|
||||
id: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const SettingsAgentEvalsTab = ({
|
||||
agentId,
|
||||
evaluationInputs,
|
||||
onEvaluationInputsChange,
|
||||
disabled = false,
|
||||
}: SettingsAgentEvalsTabProps) => {
|
||||
const [newInput, setNewInput] = useState('');
|
||||
const [inputToDelete, setInputToDelete] = useState<string | null>(null);
|
||||
const { openModal } = useModal();
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const [runEvaluationInput] = useMutation(RUN_EVALUATION_INPUT, {
|
||||
onCompleted: () => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Evaluation input executed successfully`,
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to execute evaluation input`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const evalInputs: EvalInput[] = evaluationInputs.map((text) => ({
|
||||
id: uuidv4(),
|
||||
text,
|
||||
}));
|
||||
|
||||
const handleAddInput = () => {
|
||||
if (newInput.trim() !== '') {
|
||||
onEvaluationInputsChange([...evaluationInputs, newInput.trim()]);
|
||||
setNewInput('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteInput = () => {
|
||||
if (inputToDelete !== null) {
|
||||
const index = evalInputs.findIndex((input) => input.id === inputToDelete);
|
||||
if (index !== -1) {
|
||||
const newInputs = [...evaluationInputs];
|
||||
newInputs.splice(index, 1);
|
||||
onEvaluationInputsChange(newInputs);
|
||||
}
|
||||
setInputToDelete(null);
|
||||
}
|
||||
};
|
||||
|
||||
const openDeleteModal = (id: string) => {
|
||||
setInputToDelete(id);
|
||||
openModal(DELETE_EVAL_INPUT_MODAL_ID);
|
||||
};
|
||||
|
||||
const handleRunInput = (text: string, itemId: string) => {
|
||||
runEvaluationInput({
|
||||
variables: { agentId, input: text },
|
||||
});
|
||||
closeDropdown(`eval-input-dropdown-${itemId}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Section>
|
||||
<StyledInputContainer>
|
||||
<TextInput
|
||||
placeholder={t`Add test input for evaluation (e.g., "Find all customers in NY")`}
|
||||
value={newInput}
|
||||
onChange={setNewInput}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
handleAddInput();
|
||||
}
|
||||
}}
|
||||
disabled={disabled}
|
||||
fullWidth
|
||||
/>
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
variant="primary"
|
||||
accent="blue"
|
||||
size="small"
|
||||
title={t`Add`}
|
||||
onClick={handleAddInput}
|
||||
disabled={disabled || !newInput.trim()}
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
|
||||
{evalInputs.length > 0 ? (
|
||||
<SettingsListCard
|
||||
items={evalInputs}
|
||||
getItemLabel={(item) => item.text}
|
||||
RowIcon={IconMessage}
|
||||
RowRightComponent={({ item }) => (
|
||||
<Dropdown
|
||||
dropdownId={`eval-input-dropdown-${item.id}`}
|
||||
dropdownPlacement="right-start"
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
Icon={IconDotsVertical}
|
||||
accent="tertiary"
|
||||
disabled={disabled}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconPlayerPlay}
|
||||
text={t`Run`}
|
||||
onClick={() => handleRunInput(item.text, item.id)}
|
||||
/>
|
||||
<MenuItem
|
||||
accent="danger"
|
||||
LeftIcon={IconTrash}
|
||||
text={t`Delete`}
|
||||
onClick={() => openDeleteModal(item.id)}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
hasFooter={false}
|
||||
/>
|
||||
) : (
|
||||
<StyledEmptyMessage>{t`No evaluation inputs yet. Add your first test input above.`}</StyledEmptyMessage>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
<ConfirmationModal
|
||||
modalId={DELETE_EVAL_INPUT_MODAL_ID}
|
||||
title={t`Delete Evaluation Input`}
|
||||
subtitle={t`Are you sure you want to delete this evaluation input?`}
|
||||
onConfirmClick={handleDeleteInput}
|
||||
confirmButtonText={t`Delete`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,209 @@
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath } from 'twenty-shared/utils';
|
||||
import { IconChevronRight, Status } from 'twenty-ui/display';
|
||||
import { Button, LightIconButton } from 'twenty-ui/input';
|
||||
import {
|
||||
AnimatedPlaceholder,
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
} from 'twenty-ui/layout';
|
||||
import { UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { EVALUATE_AGENT_TURN } from '@/ai/graphql/mutations/evaluateAgentTurn';
|
||||
import { GET_AGENT_TURNS } from '@/ai/graphql/queries/getAgentTurns';
|
||||
|
||||
const StyledTable = styled(Table)`
|
||||
margin-top: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
const StyledTableHeaderRow = styled(TableRow)`
|
||||
grid-template-columns: 140px 80px 1fr 40px;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
grid-template-columns: 140px 80px 1fr 40px;
|
||||
`;
|
||||
|
||||
const StyledScoreCell = styled(TableCell)`
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledDateCell = styled(TableCell)`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`;
|
||||
|
||||
const StyledInputCell = styled(TableCell)`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledActionCell = styled(TableCell)`
|
||||
justify-content: flex-end;
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
type SettingsAgentLogsTabProps = {
|
||||
agentId: string;
|
||||
};
|
||||
|
||||
export const SettingsAgentLogsTab = ({
|
||||
agentId,
|
||||
}: SettingsAgentLogsTabProps) => {
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const { data, loading, refetch } = useQuery(GET_AGENT_TURNS, {
|
||||
variables: { agentId },
|
||||
skip: !agentId,
|
||||
});
|
||||
|
||||
const [evaluateTurn, { loading: evaluating }] = useMutation(
|
||||
EVALUATE_AGENT_TURN,
|
||||
{
|
||||
onCompleted: () => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Turn evaluated successfully`,
|
||||
});
|
||||
refetch();
|
||||
},
|
||||
onError: () => {
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to evaluate turn`,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const turns = data?.agentTurns || [];
|
||||
|
||||
const getLatestEvaluation = (evaluations: any[]) => {
|
||||
if (!evaluations || evaluations.length === 0) return null;
|
||||
return [...evaluations].sort(
|
||||
(a, b) =>
|
||||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
||||
)[0];
|
||||
};
|
||||
|
||||
const getScoreColor = (score: number) => {
|
||||
if (score >= 80) return 'green';
|
||||
if (score >= 60) return 'orange';
|
||||
return 'red';
|
||||
};
|
||||
|
||||
const getUserMessageInput = (messages: any[]) => {
|
||||
const userMessage = messages?.find((message) => message.role === 'user');
|
||||
if (!userMessage) return null;
|
||||
|
||||
const textParts = userMessage.parts
|
||||
?.filter((part: any) => part.type === 'text' && part.textContent)
|
||||
.map((part: any) => part.textContent);
|
||||
|
||||
return textParts?.join(' ') || null;
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<StyledTable>
|
||||
<StyledTableHeaderRow>
|
||||
<TableHeader>{t`Date`}</TableHeader>
|
||||
<TableHeader>{t`Score`}</TableHeader>
|
||||
<TableHeader>{t`Input`}</TableHeader>
|
||||
<TableHeader />
|
||||
</StyledTableHeaderRow>
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<Skeleton height={48} borderRadius={4} key={index} />
|
||||
))}
|
||||
</StyledTable>
|
||||
);
|
||||
}
|
||||
|
||||
if (turns.length === 0) {
|
||||
return (
|
||||
<AnimatedPlaceholderEmptyContainer>
|
||||
<AnimatedPlaceholder type="emptyTimeline" />
|
||||
<AnimatedPlaceholderEmptyTextContainer>
|
||||
<AnimatedPlaceholderEmptyTitle>
|
||||
{t`No logs yet`}
|
||||
</AnimatedPlaceholderEmptyTitle>
|
||||
<AnimatedPlaceholderEmptySubTitle>
|
||||
{t`Agent interactions will appear here once the agent is used in conversations`}
|
||||
</AnimatedPlaceholderEmptySubTitle>
|
||||
</AnimatedPlaceholderEmptyTextContainer>
|
||||
</AnimatedPlaceholderEmptyContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledTable>
|
||||
<StyledTableHeaderRow>
|
||||
<TableHeader>{t`Date`}</TableHeader>
|
||||
<TableHeader>{t`Score`}</TableHeader>
|
||||
<TableHeader>{t`Input`}</TableHeader>
|
||||
<TableHeader />
|
||||
</StyledTableHeaderRow>
|
||||
{turns.map((turn: any) => {
|
||||
const latestEvaluation = getLatestEvaluation(turn.evaluations);
|
||||
const userInput = getUserMessageInput(turn.messages);
|
||||
|
||||
return (
|
||||
<StyledTableRow key={turn.id}>
|
||||
<StyledDateCell>
|
||||
{new Date(turn.createdAt).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</StyledDateCell>
|
||||
<StyledScoreCell>
|
||||
{latestEvaluation ? (
|
||||
<Status
|
||||
color={getScoreColor(latestEvaluation.score)}
|
||||
text={`${latestEvaluation.score}`}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
size="small"
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
evaluateTurn({ variables: { turnId: turn.id } })
|
||||
}
|
||||
disabled={evaluating}
|
||||
title={t`Evaluate`}
|
||||
/>
|
||||
)}
|
||||
</StyledScoreCell>
|
||||
<StyledInputCell>{userInput || t`No input`}</StyledInputCell>
|
||||
<StyledActionCell>
|
||||
{latestEvaluation && (
|
||||
<UndecoratedLink
|
||||
to={getSettingsPath(SettingsPath.AIAgentTurnDetail)
|
||||
.replace(':agentId', agentId)
|
||||
.replace(':turnId', turn.id)}
|
||||
>
|
||||
<LightIconButton
|
||||
Icon={IconChevronRight}
|
||||
title={t`View all evaluations`}
|
||||
accent="tertiary"
|
||||
/>
|
||||
</UndecoratedLink>
|
||||
)}
|
||||
</StyledActionCell>
|
||||
</StyledTableRow>
|
||||
);
|
||||
})}
|
||||
</StyledTable>
|
||||
);
|
||||
};
|
||||
@@ -3,5 +3,7 @@ export const SETTINGS_AGENT_DETAIL_TABS = {
|
||||
TABS_IDS: {
|
||||
ROLE: 'role',
|
||||
SETTINGS: 'settings',
|
||||
EVALS: 'evals',
|
||||
LOGS: 'logs',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ export const useSettingsAgentFormState = (mode: 'create' | 'edit') => {
|
||||
additionalProperties: false as const,
|
||||
},
|
||||
},
|
||||
evaluationInputs: [],
|
||||
});
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
@@ -70,6 +71,7 @@ export const useSettingsAgentFormState = (mode: 'create' | 'edit') => {
|
||||
additionalProperties: false as const,
|
||||
},
|
||||
},
|
||||
evaluationInputs: [],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ export const settingsAIAgentFormSchema = z.object({
|
||||
schema: z.custom<AgentResponseSchema>().optional(),
|
||||
})
|
||||
.optional(),
|
||||
evaluationInputs: z.array(z.string()).default([]),
|
||||
});
|
||||
|
||||
export type SettingsAIAgentFormValues = z.infer<
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
import {
|
||||
DEFAULT_FAST_MODEL,
|
||||
DEFAULT_SMART_MODEL,
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
} from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
|
||||
export class AddFastAndSmartModelsToWorkspace1763997530458
|
||||
implements MigrationInterface
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddAgentIdToAgentChatMessage1764081474225
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddAgentIdToAgentChatMessage1764081474225';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentChatMessage" ADD "agentId" uuid`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agent" ALTER COLUMN "modelId" SET DEFAULT 'default-smart-model'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_f3cab3cd2160867060a2812a3d" ON "core"."agentChatMessage" ("agentId") `,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_f3cab3cd2160867060a2812a3d"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agent" ALTER COLUMN "modelId" SET DEFAULT 'auto'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentChatMessage" DROP COLUMN "agentId"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class RefactorAgentChatEntities1764100000000
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'RefactorAgentChatEntities1764100000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Drop old tables and their constraints (data loss acceptable)
|
||||
await queryRunner.query(
|
||||
`DROP TABLE IF EXISTS "core"."agentChatMessagePart" CASCADE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TABLE IF EXISTS "core"."agentChatMessage" CASCADE`,
|
||||
);
|
||||
|
||||
// Create agentTurn table
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "core"."agentTurn" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "threadId" uuid NOT NULL, "agentId" uuid, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_0e3f599ba7cf6a02fc940d9f18d" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_3be906dca9d5b50fbfe40e33f0" ON "core"."agentTurn" ("threadId") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_e6d7c07f32e6f0f08cf639d4f5" ON "core"."agentTurn" ("agentId") `,
|
||||
);
|
||||
|
||||
// Create agentMessage enum and table
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "core"."agentMessage_role_enum" AS ENUM('user', 'assistant')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "core"."agentMessage" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "threadId" uuid NOT NULL, "turnId" uuid NOT NULL, "agentId" uuid, "role" "core"."agentMessage_role_enum" NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_8c2e7b0c3c9e1b7a9e5e3f4d5c6" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_4c31daa882e3130534995bf90c" ON "core"."agentMessage" ("threadId") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_87dbab10ac94d9a091f8efaa67" ON "core"."agentMessage" ("turnId") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_48c75cb32ff0d2887ef0dc547f" ON "core"."agentMessage" ("agentId") `,
|
||||
);
|
||||
|
||||
// Create agentMessagePart table
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "core"."agentMessagePart" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "messageId" uuid NOT NULL, "orderIndex" integer NOT NULL, "type" character varying NOT NULL, "textContent" text, "reasoningContent" text, "toolName" character varying, "toolCallId" character varying, "toolInput" jsonb, "toolOutput" jsonb, "state" character varying, "errorMessage" text, "errorDetails" jsonb, "sourceUrlSourceId" character varying, "sourceUrlUrl" character varying, "sourceUrlTitle" character varying, "sourceDocumentSourceId" character varying, "sourceDocumentMediaType" character varying, "sourceDocumentTitle" character varying, "sourceDocumentFilename" character varying, "fileMediaType" character varying, "fileFilename" character varying, "fileUrl" character varying, "providerMetadata" jsonb, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_7e8c9f0b1a2b3c4d5e6f7a8b9c0" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_2aff9daad5cc3b5e15ca717334" ON "core"."agentMessagePart" ("messageId") `,
|
||||
);
|
||||
|
||||
// Add foreign key constraints
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentTurn" ADD CONSTRAINT "FK_3be906dca9d5b50fbfe40e33f07" FOREIGN KEY ("threadId") REFERENCES "core"."agentChatThread"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentMessage" ADD CONSTRAINT "FK_4c31daa882e3130534995bf90ca" FOREIGN KEY ("threadId") REFERENCES "core"."agentChatThread"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentMessage" ADD CONSTRAINT "FK_87dbab10ac94d9a091f8efaa67b" FOREIGN KEY ("turnId") REFERENCES "core"."agentTurn"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentMessagePart" ADD CONSTRAINT "FK_2aff9daad5cc3b5e15ca7173342" FOREIGN KEY ("messageId") REFERENCES "core"."agentMessage"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
// Drop foreign key constraints
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentMessagePart" DROP CONSTRAINT "FK_2aff9daad5cc3b5e15ca7173342"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentMessage" DROP CONSTRAINT "FK_87dbab10ac94d9a091f8efaa67b"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentMessage" DROP CONSTRAINT "FK_4c31daa882e3130534995bf90ca"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentTurn" DROP CONSTRAINT "FK_3be906dca9d5b50fbfe40e33f07"`,
|
||||
);
|
||||
|
||||
// Drop indexes
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_2aff9daad5cc3b5e15ca717334"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_48c75cb32ff0d2887ef0dc547f"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_87dbab10ac94d9a091f8efaa67"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_4c31daa882e3130534995bf90c"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_e6d7c07f32e6f0f08cf639d4f5"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_3be906dca9d5b50fbfe40e33f0"`,
|
||||
);
|
||||
|
||||
// Drop new tables
|
||||
await queryRunner.query(`DROP TABLE "core"."agentMessagePart"`);
|
||||
await queryRunner.query(`DROP TABLE "core"."agentMessage"`);
|
||||
await queryRunner.query(`DROP TYPE "core"."agentMessage_role_enum"`);
|
||||
await queryRunner.query(`DROP TABLE "core"."agentTurn"`);
|
||||
|
||||
// Recreate old tables with enum
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "core"."agentChatMessage_role_enum" AS ENUM('user', 'assistant')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "core"."agentChatMessage" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "threadId" uuid NOT NULL, "agentId" uuid, "role" "core"."agentChatMessage_role_enum" NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_f54a95b34e98d94251bce37a180" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_cd5b23d4e471b630137b3017ba" ON "core"."agentChatMessage" ("threadId") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_f3cab3cd2160867060a2812a3d" ON "core"."agentChatMessage" ("agentId") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "core"."agentChatMessagePart" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "messageId" uuid NOT NULL, "orderIndex" integer NOT NULL, "type" character varying NOT NULL, "textContent" text, "reasoningContent" text, "toolName" character varying, "toolCallId" character varying, "toolInput" jsonb, "toolOutput" jsonb, "state" character varying, "errorMessage" text, "errorDetails" jsonb, "sourceUrlSourceId" character varying, "sourceUrlUrl" character varying, "sourceUrlTitle" character varying, "sourceDocumentSourceId" character varying, "sourceDocumentMediaType" character varying, "sourceDocumentTitle" character varying, "sourceDocumentFilename" character varying, "fileMediaType" character varying, "fileFilename" character varying, "fileUrl" character varying, "providerMetadata" jsonb, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_c28499bb0699730d41e57e1fe23" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_5d4b48eeebfa7b23cd2226a874" ON "core"."agentChatMessagePart" ("messageId") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentChatMessage" ADD CONSTRAINT "FK_cd5b23d4e471b630137b3017ba6" FOREIGN KEY ("threadId") REFERENCES "core"."agentChatThread"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."agentChatMessagePart" ADD CONSTRAINT "FK_5d4b48eeebfa7b23cd2226a874f" FOREIGN KEY ("messageId") REFERENCES "core"."agentChatMessage"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddAgentTurnEvaluation1764200000000 implements MigrationInterface {
|
||||
name = 'AddAgentTurnEvaluation1764200000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE "core"."agentTurnEvaluation" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"turnId" uuid NOT NULL,
|
||||
"score" int NOT NULL,
|
||||
"comment" text,
|
||||
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_agentTurnEvaluation" PRIMARY KEY ("id")
|
||||
)
|
||||
`);
|
||||
|
||||
await queryRunner.query(`
|
||||
CREATE INDEX "IDX_c94f072dbd3c11f7df51db5293"
|
||||
ON "core"."agentTurnEvaluation" ("turnId")
|
||||
`);
|
||||
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "core"."agentTurnEvaluation"
|
||||
ADD CONSTRAINT "FK_c94f072dbd3c11f7df51db52934"
|
||||
FOREIGN KEY ("turnId")
|
||||
REFERENCES "core"."agentTurn"("id")
|
||||
ON DELETE CASCADE ON UPDATE NO ACTION
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "core"."agentTurnEvaluation"
|
||||
DROP CONSTRAINT "FK_c94f072dbd3c11f7df51db52934"
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP INDEX "core"."IDX_c94f072dbd3c11f7df51db5293"
|
||||
`);
|
||||
await queryRunner.query(`DROP TABLE "core"."agentTurnEvaluation"`);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddSystemRoleToAgentMessage1764210000000
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddSystemRoleToAgentMessage1764210000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TYPE "core"."agentMessage_role_enum"
|
||||
ADD VALUE IF NOT EXISTS 'system'
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(_queryRunner: QueryRunner): Promise<void> {
|
||||
// PostgreSQL doesn't support removing enum values
|
||||
// We would need to recreate the enum type to remove the value
|
||||
// which is more complex and risky, so we leave it as is
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddEvaluationInputsToAgent1764220000000
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddEvaluationInputsToAgent1764220000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "core"."agent"
|
||||
ADD COLUMN "evaluationInputs" text[] NOT NULL DEFAULT '{}'
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "core"."agent"
|
||||
DROP COLUMN "evaluationInputs"
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,10 @@ import { MCPMetadataToolsService } from 'src/engine/api/mcp/services/tools/mcp-m
|
||||
import { UpdateToolsService } from 'src/engine/api/mcp/services/tools/update.tools.service';
|
||||
import { MetadataQueryBuilderModule } from 'src/engine/api/rest/metadata/query-builder/metadata-query-builder.module';
|
||||
import { RestApiModule } from 'src/engine/api/rest/rest-api.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai-tools/ai-tools.module';
|
||||
import { TokenModule } from 'src/engine/core-modules/auth/token/token.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai/ai-tools/ai-tools.module';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
+1
-1
@@ -8,10 +8,10 @@ import { MCP_SERVER_METADATA } from 'src/engine/api/mcp/constants/mcp.const';
|
||||
import { type JsonRpc } from 'src/engine/api/mcp/dtos/json-rpc';
|
||||
import { McpProtocolService } from 'src/engine/api/mcp/services/mcp-protocol.service';
|
||||
import { McpToolExecutorService } from 'src/engine/api/mcp/services/mcp-tool-executor.service';
|
||||
import { ToolService } from 'src/engine/metadata-modules/ai-tools/services/tool.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ToolService } from 'src/engine/metadata-modules/ai/ai-tools/services/tool.service';
|
||||
import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
|
||||
@@ -6,10 +6,10 @@ import { Repository } from 'typeorm';
|
||||
import { type JsonRpc } from 'src/engine/api/mcp/dtos/json-rpc';
|
||||
import { McpToolExecutorService } from 'src/engine/api/mcp/services/mcp-tool-executor.service';
|
||||
import { wrapJsonRpcResponse } from 'src/engine/api/mcp/utils/wrap-jsonrpc-response.util';
|
||||
import { ToolService } from 'src/engine/metadata-modules/ai-tools/services/tool.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ToolService } from 'src/engine/metadata-modules/ai/ai-tools/services/tool.service';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { ADMIN_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/admin-role';
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
|
||||
import { ApplicationVariableEntity } from 'src/engine/core-modules/applicationVariable/application-variable.entity';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ApplicationEntity } from 'src/engine/core-modules/application/applicati
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { WorkspaceFlatApplicationMapCacheService } from 'src/engine/core-modules/application/services/workspace-flat-application-map-cache.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
|
||||
@Module({
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ApplicationVariableEntityDTO } from 'src/engine/core-modules/applicationVariable/dtos/application-variable.dto';
|
||||
import { AgentDTO } from 'src/engine/metadata-modules/ai-agent/dtos/agent.dto';
|
||||
import { AgentDTO } from 'src/engine/metadata-modules/ai/ai-agent/dtos/agent.dto';
|
||||
import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
import { ServerlessFunctionDTO } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto';
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-
|
||||
import { MessageQueueModule } from 'src/engine/core-modules/message-queue/message-queue.module';
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai-billing/ai-billing.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai-models/ai-models.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai-tools/ai-tools.module';
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai/ai-tools/ai-tools.module';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
|
||||
|
||||
+2
-2
@@ -2,11 +2,11 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/support.interface';
|
||||
|
||||
import { ClientConfigService } from 'src/engine/core-modules/client-config/services/client-config.service';
|
||||
import {
|
||||
type ModelId,
|
||||
ModelProvider,
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { ClientConfigService } from 'src/engine/core-modules/client-config/services/client-config.service';
|
||||
} from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
|
||||
import { ClientConfigController } from './client-config.controller';
|
||||
|
||||
|
||||
+4
-4
@@ -2,14 +2,14 @@ import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/support.interface';
|
||||
|
||||
import {
|
||||
ModelId,
|
||||
ModelProvider,
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { BillingTrialPeriodDTO } from 'src/engine/core-modules/billing/dtos/billing-trial-period.dto';
|
||||
import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { AuthProvidersDTO } from 'src/engine/core-modules/workspace/dtos/public-workspace-data-output';
|
||||
import {
|
||||
ModelId,
|
||||
ModelProvider,
|
||||
} from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
|
||||
registerEnumType(FeatureFlagKey, {
|
||||
name: 'FeatureFlagKey',
|
||||
|
||||
+1
-1
@@ -3,12 +3,12 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
import { NodeEnvironment } from 'src/engine/core-modules/twenty-config/interfaces/node-environment.interface';
|
||||
import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/support.interface';
|
||||
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
|
||||
import { ClientConfigService } from 'src/engine/core-modules/client-config/services/client-config.service';
|
||||
import { DomainServerConfigService } from 'src/engine/core-modules/domain/domain-server-config/services/domain-server-config.service';
|
||||
import { PUBLIC_FEATURE_FLAGS } from 'src/engine/core-modules/feature-flag/constants/public-feature-flag.const';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
|
||||
describe('ClientConfigService', () => {
|
||||
let service: ClientConfigService;
|
||||
|
||||
+3
-3
@@ -12,14 +12,14 @@ import {
|
||||
import { DomainServerConfigService } from 'src/engine/core-modules/domain/domain-server-config/services/domain-server-config.service';
|
||||
import { PUBLIC_FEATURE_FLAGS } from 'src/engine/core-modules/feature-flag/constants/public-feature-flag.const';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { convertCentsToBillingCredits } from 'src/engine/metadata-modules/ai-billing/utils/convert-cents-to-billing-credits.util';
|
||||
import { convertCentsToBillingCredits } from 'src/engine/metadata-modules/ai/ai-billing/utils/convert-cents-to-billing-credits.util';
|
||||
import {
|
||||
AI_MODELS,
|
||||
DEFAULT_FAST_MODEL,
|
||||
DEFAULT_SMART_MODEL,
|
||||
ModelProvider,
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
} from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
|
||||
@Injectable()
|
||||
export class ClientConfigService {
|
||||
|
||||
@@ -5,13 +5,10 @@ import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { WorkspaceQueryRunnerModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module';
|
||||
import { ActorModule } from 'src/engine/core-modules/actor/actor.module';
|
||||
import { AdminPanelModule } from 'src/engine/core-modules/admin-panel/admin-panel.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai-models/ai-models.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai-tools/ai-tools.module';
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai-billing/ai-billing.module';
|
||||
import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module';
|
||||
import { AppTokenModule } from 'src/engine/core-modules/app-token/app-token.module';
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { ApplicationSyncModule } from 'src/engine/core-modules/application/application-sync.module';
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { ApprovedAccessDomainModule } from 'src/engine/core-modules/approved-access-domain/approved-access-domain.module';
|
||||
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
|
||||
import { BillingWebhookModule } from 'src/engine/core-modules/billing-webhook/billing-webhook.module';
|
||||
@@ -57,6 +54,9 @@ import { WebhookModule } from 'src/engine/core-modules/webhook/webhook.module';
|
||||
import { WorkflowApiModule } from 'src/engine/core-modules/workflow/workflow-api.module';
|
||||
import { WorkspaceInvitationModule } from 'src/engine/core-modules/workspace-invitation/workspace-invitation.module';
|
||||
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai/ai-tools/ai-tools.module';
|
||||
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
|
||||
import { SubscriptionsModule } from 'src/engine/subscriptions/subscriptions.module';
|
||||
import { TrashCleanupModule } from 'src/engine/trash-cleanup/trash-cleanup.module';
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ const isFieldAvailable = (field: FieldMetadataEntity, forResponse: boolean) => {
|
||||
const getFieldZodType = (field: FieldMetadataEntity): z.ZodTypeAny => {
|
||||
switch (field.type) {
|
||||
case FieldMetadataType.UUID:
|
||||
return z.uuidv4();
|
||||
return z.string().uuidv4();
|
||||
|
||||
case FieldMetadataType.TEXT:
|
||||
case FieldMetadataType.RICH_TEXT:
|
||||
@@ -41,7 +41,7 @@ const getFieldZodType = (field: FieldMetadataEntity): z.ZodTypeAny => {
|
||||
return z.string().datetime();
|
||||
|
||||
case FieldMetadataType.DATE:
|
||||
return z.date();
|
||||
return z.string().date();
|
||||
|
||||
case FieldMetadataType.NUMBER: {
|
||||
const settings =
|
||||
|
||||
+13
-1
@@ -17,7 +17,19 @@ export class ToolRegistryService {
|
||||
private readonly twentyConfigService: TwentyConfigService,
|
||||
) {
|
||||
this.toolFactories = new Map<ToolType, () => Tool>([
|
||||
[ToolType.HTTP_REQUEST, () => new HttpTool(twentyConfigService)],
|
||||
[
|
||||
ToolType.HTTP_REQUEST,
|
||||
() => {
|
||||
const httpTool = new HttpTool(twentyConfigService);
|
||||
|
||||
return {
|
||||
description: httpTool.description,
|
||||
inputSchema: httpTool.inputSchema,
|
||||
execute: (params) => httpTool.execute(params),
|
||||
flag: PermissionFlagType.HTTP_REQUEST_TOOL,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
ToolType.SEND_EMAIL,
|
||||
() => ({
|
||||
|
||||
@@ -32,12 +32,12 @@ import { PublicDomainEntity } from 'src/engine/core-modules/public-domain/public
|
||||
import { WorkspaceSSOIdentityProviderEntity } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { WebhookEntity } from 'src/engine/core-modules/webhook/webhook.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import {
|
||||
DEFAULT_FAST_MODEL,
|
||||
DEFAULT_SMART_MODEL,
|
||||
type ModelId,
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
} from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
import { ViewFieldDTO } from 'src/engine/metadata-modules/view-field/dtos/view-field.dto';
|
||||
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
|
||||
@@ -29,7 +29,7 @@ import { WorkspaceWorkspaceMemberListener } from 'src/engine/core-modules/worksp
|
||||
import { workspaceAutoResolverOpts } from 'src/engine/core-modules/workspace/workspace.auto-resolver-opts';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.resolver';
|
||||
import { AiAgentModule } from 'src/engine/metadata-modules/ai-agent/ai-agent.module';
|
||||
import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
import { AgentChatMessagePartDTO } from './agent-chat-message-part.dto';
|
||||
|
||||
@ObjectType('AgentChatMessage')
|
||||
export class AgentChatMessageDTO {
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@Field(() => UUIDScalarType)
|
||||
threadId: string;
|
||||
|
||||
@Field()
|
||||
role: 'user' | 'assistant';
|
||||
|
||||
@Field(() => [AgentChatMessagePartDTO])
|
||||
parts: AgentChatMessagePartDTO[];
|
||||
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
} from 'typeorm';
|
||||
|
||||
import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai-chat/entities/agent-chat-thread.entity';
|
||||
|
||||
import { AgentChatMessagePartEntity } from './agent-chat-message-part.entity';
|
||||
|
||||
export enum AgentChatMessageRole {
|
||||
USER = 'user',
|
||||
ASSISTANT = 'assistant',
|
||||
}
|
||||
|
||||
@Entity('agentChatMessage')
|
||||
export class AgentChatMessageEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column('uuid')
|
||||
@Index()
|
||||
threadId: string;
|
||||
|
||||
@ManyToOne(() => AgentChatThreadEntity, (thread) => thread.messages, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'threadId' })
|
||||
thread: Relation<AgentChatThreadEntity>;
|
||||
|
||||
@Column({ type: 'enum', enum: AgentChatMessageRole })
|
||||
role: AgentChatMessageRole;
|
||||
|
||||
@OneToMany(() => AgentChatMessagePartEntity, (part) => part.message)
|
||||
parts: Relation<AgentChatMessagePartEntity[]>;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai-models/ai-models.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai-tools/ai-tools.module';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
|
||||
import { AiRouterService } from './ai-router.service';
|
||||
|
||||
import { AiRouterPlanGeneratorService } from './services/ai-router-plan-generator.service';
|
||||
import { AiRouterStrategyDeciderService } from './services/ai-router-strategy-decider.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([AgentEntity, WorkspaceEntity]),
|
||||
AiModelsModule,
|
||||
AiToolsModule,
|
||||
ObjectMetadataModule,
|
||||
],
|
||||
providers: [
|
||||
AiRouterService,
|
||||
AiRouterStrategyDeciderService,
|
||||
AiRouterPlanGeneratorService,
|
||||
],
|
||||
exports: [AiRouterService],
|
||||
})
|
||||
export class AiRouterModule {}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai/ai-tools/ai-tools.module';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
|
||||
import { AgentMessagePartEntity } from './entities/agent-message-part.entity';
|
||||
import { AgentMessageEntity } from './entities/agent-message.entity';
|
||||
import { AgentTurnEntity } from './entities/agent-turn.entity';
|
||||
import { AgentAsyncExecutorService } from './services/agent-async-executor.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AiBillingModule,
|
||||
AiModelsModule,
|
||||
AiToolsModule,
|
||||
TypeOrmModule.forFeature([
|
||||
AgentMessageEntity,
|
||||
AgentMessagePartEntity,
|
||||
AgentTurnEntity,
|
||||
RoleTargetsEntity,
|
||||
]),
|
||||
],
|
||||
providers: [AgentAsyncExecutorService],
|
||||
exports: [
|
||||
AgentAsyncExecutorService,
|
||||
TypeOrmModule.forFeature([
|
||||
AgentMessageEntity,
|
||||
AgentMessagePartEntity,
|
||||
AgentTurnEntity,
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class AiAgentExecutionModule {}
|
||||
+5
-4
@@ -1,12 +1,12 @@
|
||||
import { Field, Int, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { JSONValue } from 'ai';
|
||||
import { IsDateString } from 'class-validator';
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@ObjectType('AgentChatMessagePart')
|
||||
export class AgentChatMessagePartDTO {
|
||||
@ObjectType('AgentMessagePart')
|
||||
export class AgentMessagePartDTO {
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@@ -77,8 +77,9 @@ export class AgentChatMessagePartDTO {
|
||||
fileUrl?: string;
|
||||
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
providerMetadata?: Record<string, Record<string, JSONValue>>;
|
||||
providerMetadata?: Record<string, unknown>;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IsDateString } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { AgentMessagePartDTO } from 'src/engine/metadata-modules/ai/ai-agent-execution/dtos/agent-message-part.dto';
|
||||
|
||||
@ObjectType('AgentMessage')
|
||||
export class AgentMessageDTO {
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@Field(() => UUIDScalarType)
|
||||
threadId: string;
|
||||
|
||||
@Field(() => UUIDScalarType)
|
||||
turnId: string;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
agentId: string | null;
|
||||
|
||||
@Field()
|
||||
role: 'system' | 'user' | 'assistant';
|
||||
|
||||
@Field(() => [AgentMessagePartDTO])
|
||||
parts: AgentMessagePartDTO[];
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IsDateString } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { AgentMessageDTO } from 'src/engine/metadata-modules/ai/ai-agent-execution/dtos/agent-message.dto';
|
||||
import { AgentTurnEvaluationDTO } from 'src/engine/metadata-modules/ai/ai-agent-monitor/dtos/agent-turn-evaluation.dto';
|
||||
|
||||
@ObjectType('AgentTurn')
|
||||
export class AgentTurnDTO {
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@Field(() => UUIDScalarType)
|
||||
threadId: string;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
agentId: string | null;
|
||||
|
||||
@Field(() => [AgentTurnEvaluationDTO])
|
||||
evaluations: AgentTurnEvaluationDTO[];
|
||||
|
||||
@Field(() => [AgentMessageDTO])
|
||||
messages: AgentMessageDTO[];
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
}
|
||||
+5
-5
@@ -10,10 +10,10 @@ import {
|
||||
Relation,
|
||||
} from 'typeorm';
|
||||
|
||||
import { AgentChatMessageEntity } from './agent-chat-message.entity';
|
||||
import { AgentMessageEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message.entity';
|
||||
|
||||
@Entity('agentChatMessagePart')
|
||||
export class AgentChatMessagePartEntity {
|
||||
@Entity('agentMessagePart')
|
||||
export class AgentMessagePartEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@@ -21,11 +21,11 @@ export class AgentChatMessagePartEntity {
|
||||
@Index()
|
||||
messageId: string;
|
||||
|
||||
@ManyToOne(() => AgentChatMessageEntity, (message) => message.parts, {
|
||||
@ManyToOne(() => AgentMessageEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'messageId' })
|
||||
message: Relation<AgentChatMessageEntity>;
|
||||
message: Relation<AgentMessageEntity>;
|
||||
|
||||
@Column({ type: 'int' })
|
||||
orderIndex: number;
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
} from 'typeorm';
|
||||
|
||||
import { AgentMessagePartEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message-part.entity';
|
||||
import { AgentTurnEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-turn.entity';
|
||||
import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity';
|
||||
|
||||
export enum AgentMessageRole {
|
||||
SYSTEM = 'system',
|
||||
USER = 'user',
|
||||
ASSISTANT = 'assistant',
|
||||
}
|
||||
|
||||
@Entity('agentMessage')
|
||||
export class AgentMessageEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column('uuid')
|
||||
@Index()
|
||||
threadId: string;
|
||||
|
||||
@ManyToOne(() => AgentChatThreadEntity, (thread) => thread.messages, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'threadId' })
|
||||
thread: Relation<AgentChatThreadEntity>;
|
||||
|
||||
@Column('uuid')
|
||||
@Index()
|
||||
turnId: string;
|
||||
|
||||
@ManyToOne(() => AgentTurnEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'turnId' })
|
||||
turn: Relation<AgentTurnEntity>;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true })
|
||||
@Index()
|
||||
agentId: string | null;
|
||||
|
||||
@Column({ type: 'enum', enum: AgentMessageRole })
|
||||
role: AgentMessageRole;
|
||||
|
||||
@OneToMany(() => AgentMessagePartEntity, (part) => part.message)
|
||||
parts: Relation<AgentMessagePartEntity[]>;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
} from 'typeorm';
|
||||
|
||||
import { AgentMessageEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message.entity';
|
||||
import { AgentTurnEvaluationEntity } from 'src/engine/metadata-modules/ai/ai-agent-monitor/entities/agent-turn-evaluation.entity';
|
||||
import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity';
|
||||
|
||||
@Entity('agentTurn')
|
||||
export class AgentTurnEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column('uuid')
|
||||
@Index()
|
||||
threadId: string;
|
||||
|
||||
@ManyToOne(() => AgentChatThreadEntity, (thread) => thread.turns, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'threadId' })
|
||||
thread: Relation<AgentChatThreadEntity>;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true })
|
||||
@Index()
|
||||
agentId: string | null;
|
||||
|
||||
@OneToMany(() => AgentMessageEntity, (message) => message.turn)
|
||||
messages: Relation<AgentMessageEntity[]>;
|
||||
|
||||
@OneToMany(() => AgentTurnEvaluationEntity, (evaluation) => evaluation.turn)
|
||||
evaluations: Relation<AgentTurnEvaluationEntity[]>;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
}
|
||||
+11
-11
@@ -11,24 +11,24 @@ import {
|
||||
import { type ActorMetadata } from 'twenty-shared/types';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai-models/constants/ai-telemetry.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
import { ToolAdapterService } from 'src/engine/metadata-modules/ai-tools/services/tool-adapter.service';
|
||||
import { ToolService } from 'src/engine/metadata-modules/ai-tools/services/tool.service';
|
||||
import { AgentExecutionResult } from 'src/engine/metadata-modules/ai-agent/services/agent-execution.service';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import {
|
||||
AgentException,
|
||||
AgentExceptionCode,
|
||||
} from 'src/engine/metadata-modules/ai-agent/agent.exception';
|
||||
import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai-agent/constants/agent-config.const';
|
||||
import { AGENT_SYSTEM_PROMPTS } from 'src/engine/metadata-modules/ai-agent/constants/agent-system-prompts.const';
|
||||
} from 'src/engine/metadata-modules/ai/ai-agent/agent.exception';
|
||||
import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-config.const';
|
||||
import { AGENT_SYSTEM_PROMPTS } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-system-prompts.const';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { AgentExecutionResult } from 'src/engine/metadata-modules/ai/ai-agent/services/agent-execution.service';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-telemetry.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
import { ToolAdapterService } from 'src/engine/metadata-modules/ai/ai-tools/services/tool-adapter.service';
|
||||
import { ToolService } from 'src/engine/metadata-modules/ai/ai-tools/services/tool.service';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
@Injectable()
|
||||
export class AiAgentExecutorService {
|
||||
private readonly logger = new Logger(AiAgentExecutorService.name);
|
||||
export class AgentAsyncExecutorService {
|
||||
private readonly logger = new Logger(AgentAsyncExecutorService.name);
|
||||
constructor(
|
||||
private readonly aiModelRegistryService: AiModelRegistryService,
|
||||
private readonly toolAdapterService: ToolAdapterService,
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { type ToolUIPart } from 'ai';
|
||||
import { type ExtendedUIMessagePart } from 'twenty-shared/ai';
|
||||
|
||||
import { type AgentChatMessagePartEntity } from 'src/engine/metadata-modules/ai-chat/entities/agent-chat-message-part.entity';
|
||||
import { type AgentMessagePartEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message-part.entity';
|
||||
|
||||
const isToolPart = (part: ExtendedUIMessagePart): part is ToolUIPart => {
|
||||
return part.type.includes('tool-') && 'toolCallId' in part;
|
||||
@@ -10,9 +10,9 @@ const isToolPart = (part: ExtendedUIMessagePart): part is ToolUIPart => {
|
||||
export const mapUIMessagePartsToDBParts = (
|
||||
uiMessageParts: ExtendedUIMessagePart[],
|
||||
messageId: string,
|
||||
): Partial<AgentChatMessagePartEntity>[] => {
|
||||
): Partial<AgentMessagePartEntity>[] => {
|
||||
return uiMessageParts.map((part, index) => {
|
||||
const basePart: Partial<AgentChatMessagePartEntity> = {
|
||||
const basePart: Partial<AgentMessagePartEntity> = {
|
||||
messageId,
|
||||
orderIndex: index,
|
||||
type: part.type,
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AiAgentExecutionModule } from 'src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module';
|
||||
import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { AiChatModule } from 'src/engine/metadata-modules/ai/ai-chat/ai-chat.module';
|
||||
import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
|
||||
import { AgentTurnEvaluationEntity } from './entities/agent-turn-evaluation.entity';
|
||||
import { AgentTurnResolver } from './resolvers/agent-turn.resolver';
|
||||
import { AgentTurnGraderService } from './services/agent-turn-grader.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
AgentTurnEvaluationEntity,
|
||||
AgentEntity,
|
||||
AgentChatThreadEntity,
|
||||
]),
|
||||
AiAgentModule,
|
||||
AiAgentExecutionModule,
|
||||
AiChatModule,
|
||||
AiModelsModule,
|
||||
PermissionsModule,
|
||||
],
|
||||
providers: [AgentTurnGraderService, AgentTurnResolver],
|
||||
exports: [AgentTurnGraderService],
|
||||
})
|
||||
export class AiAgentMonitorModule {}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { Field, Int, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IsDateString } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@ObjectType('AgentTurnEvaluation')
|
||||
export class AgentTurnEvaluationDTO {
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@Field(() => UUIDScalarType)
|
||||
turnId: string;
|
||||
|
||||
@Field(() => Int)
|
||||
score: number;
|
||||
|
||||
@Field({ nullable: true })
|
||||
comment?: string;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
} from 'typeorm';
|
||||
|
||||
import { AgentTurnEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-turn.entity';
|
||||
|
||||
@Entity('agentTurnEvaluation')
|
||||
export class AgentTurnEvaluationEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column('uuid')
|
||||
@Index()
|
||||
turnId: string;
|
||||
|
||||
@ManyToOne(() => AgentTurnEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'turnId' })
|
||||
turn: Relation<AgentTurnEntity>;
|
||||
|
||||
@Column({ type: 'int' })
|
||||
score: number;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
comment: string | null;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { AgentTurnDTO } from 'src/engine/metadata-modules/ai/ai-agent-execution/dtos/agent-turn.dto';
|
||||
import { AgentTurnEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-turn.entity';
|
||||
import { AgentAsyncExecutorService } from 'src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service';
|
||||
import { AgentTurnEvaluationDTO } from 'src/engine/metadata-modules/ai/ai-agent-monitor/dtos/agent-turn-evaluation.dto';
|
||||
import { AgentTurnGraderService } from 'src/engine/metadata-modules/ai/ai-agent-monitor/services/agent-turn-grader.service';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity';
|
||||
import { AgentChatService } from 'src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service';
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard, SettingsPermissionGuard(PermissionFlagType.AI))
|
||||
@Resolver()
|
||||
export class AgentTurnResolver {
|
||||
constructor(
|
||||
@InjectRepository(AgentTurnEntity)
|
||||
private readonly turnRepository: Repository<AgentTurnEntity>,
|
||||
@InjectRepository(AgentChatThreadEntity)
|
||||
private readonly threadRepository: Repository<AgentChatThreadEntity>,
|
||||
@InjectRepository(AgentEntity)
|
||||
private readonly agentRepository: Repository<AgentEntity>,
|
||||
private readonly graderService: AgentTurnGraderService,
|
||||
private readonly aiAgentExecutorService: AgentAsyncExecutorService,
|
||||
private readonly agentChatService: AgentChatService,
|
||||
) {}
|
||||
|
||||
@Query(() => [AgentTurnDTO])
|
||||
async agentTurns(
|
||||
@Args('agentId', { type: () => UUIDScalarType }) agentId: string,
|
||||
): Promise<AgentTurnDTO[]> {
|
||||
const turns = await this.turnRepository.find({
|
||||
where: { agentId },
|
||||
relations: ['evaluations', 'messages', 'messages.parts'],
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
|
||||
return turns as unknown as AgentTurnDTO[];
|
||||
}
|
||||
|
||||
@Mutation(() => AgentTurnEvaluationDTO)
|
||||
async evaluateAgentTurn(
|
||||
@Args('turnId', { type: () => UUIDScalarType }) turnId: string,
|
||||
): Promise<AgentTurnEvaluationDTO> {
|
||||
const evaluation = await this.graderService.evaluateTurn(turnId);
|
||||
|
||||
return evaluation as unknown as AgentTurnEvaluationDTO;
|
||||
}
|
||||
|
||||
@Mutation(() => AgentTurnDTO)
|
||||
async runEvaluationInput(
|
||||
@Args('agentId', { type: () => UUIDScalarType }) agentId: string,
|
||||
@Args('input') input: string,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@AuthUserWorkspaceId() userWorkspaceId: string,
|
||||
): Promise<AgentTurnDTO> {
|
||||
const thread = this.threadRepository.create({
|
||||
userWorkspaceId,
|
||||
title: `Eval: ${input.substring(0, 50)}...`,
|
||||
});
|
||||
const savedThread = await this.threadRepository.save(thread);
|
||||
|
||||
const turn = this.turnRepository.create({
|
||||
threadId: savedThread.id,
|
||||
agentId,
|
||||
});
|
||||
const savedTurn = await this.turnRepository.save(turn);
|
||||
|
||||
const agent = await this.agentRepository.findOne({
|
||||
where: { id: agentId },
|
||||
});
|
||||
|
||||
await this.agentChatService.addMessage({
|
||||
threadId: savedThread.id,
|
||||
turnId: savedTurn.id,
|
||||
uiMessage: {
|
||||
role: 'user',
|
||||
parts: [{ type: 'text', text: input }],
|
||||
},
|
||||
});
|
||||
|
||||
const executionResult = await this.aiAgentExecutorService.executeAgent({
|
||||
agent,
|
||||
userPrompt: input,
|
||||
});
|
||||
|
||||
await this.agentChatService.addMessage({
|
||||
threadId: savedThread.id,
|
||||
turnId: savedTurn.id,
|
||||
agentId: agent?.id,
|
||||
uiMessage: {
|
||||
role: 'assistant',
|
||||
parts: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(executionResult.result) || '',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await this.graderService.evaluateTurn(savedTurn.id);
|
||||
|
||||
const turnWithEvaluations = await this.turnRepository.findOne({
|
||||
where: { id: savedTurn.id },
|
||||
relations: ['evaluations', 'messages', 'messages.parts'],
|
||||
});
|
||||
|
||||
if (!turnWithEvaluations) {
|
||||
throw new Error('Turn not found after execution');
|
||||
}
|
||||
|
||||
return turnWithEvaluations as unknown as AgentTurnDTO;
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { generateText } from 'ai';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { AgentMessageEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message.entity';
|
||||
import { AgentTurnEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-turn.entity';
|
||||
import { AgentTurnEvaluationEntity } from 'src/engine/metadata-modules/ai/ai-agent-monitor/entities/agent-turn-evaluation.entity';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-telemetry.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
|
||||
@Injectable()
|
||||
export class AgentTurnGraderService {
|
||||
private readonly logger = new Logger(AgentTurnGraderService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(AgentTurnEntity)
|
||||
private readonly turnRepository: Repository<AgentTurnEntity>,
|
||||
@InjectRepository(AgentTurnEvaluationEntity)
|
||||
private readonly evaluationRepository: Repository<AgentTurnEvaluationEntity>,
|
||||
private readonly aiModelRegistryService: AiModelRegistryService,
|
||||
) {}
|
||||
|
||||
async evaluateTurn(turnId: string): Promise<AgentTurnEvaluationEntity> {
|
||||
const turn = await this.turnRepository.findOne({
|
||||
where: { id: turnId },
|
||||
relations: ['messages', 'messages.parts'],
|
||||
});
|
||||
|
||||
if (!turn) {
|
||||
throw new Error(`Turn ${turnId} not found`);
|
||||
}
|
||||
|
||||
const { score, comment } = await this.evaluateWithAI(turn);
|
||||
|
||||
const evaluation = this.evaluationRepository.create({
|
||||
turnId,
|
||||
score,
|
||||
comment,
|
||||
});
|
||||
|
||||
return this.evaluationRepository.save(evaluation);
|
||||
}
|
||||
|
||||
private async evaluateWithAI(
|
||||
turn: AgentTurnEntity & { messages: AgentMessageEntity[] },
|
||||
): Promise<{ score: number; comment: string }> {
|
||||
try {
|
||||
const defaultModel = this.aiModelRegistryService.getDefaultSpeedModel();
|
||||
|
||||
if (!defaultModel) {
|
||||
this.logger.warn('No default AI model available for evaluation');
|
||||
|
||||
return this.getFallbackEvaluation(turn);
|
||||
}
|
||||
|
||||
const evaluationContext = this.buildEvaluationContext(turn);
|
||||
|
||||
const prompt = `You are evaluating an AI agent's performance on a single turn (user request + agent response).
|
||||
|
||||
${evaluationContext}
|
||||
|
||||
Evaluate this agent turn based on:
|
||||
1. **Task Completion**: Did the agent accomplish what the user asked?
|
||||
2. **Tool Usage**: Were tools used correctly and appropriately?
|
||||
3. **Response Quality**: Is the response clear, accurate, and helpful?
|
||||
4. **Error Handling**: Were errors handled gracefully?
|
||||
|
||||
Provide:
|
||||
- A score from 0 to 100 (0 = complete failure, 100 = perfect)
|
||||
- A brief comment explaining the score (max 200 characters)
|
||||
|
||||
Respond ONLY with valid JSON in this exact format:
|
||||
{"score": <number>, "comment": "<string>"}`;
|
||||
|
||||
const result = await generateText({
|
||||
model: defaultModel.model,
|
||||
prompt,
|
||||
temperature: 0.3,
|
||||
experimental_telemetry: AI_TELEMETRY_CONFIG,
|
||||
});
|
||||
|
||||
const parsed = JSON.parse(result.text);
|
||||
|
||||
return {
|
||||
score: Math.max(0, Math.min(100, Math.round(parsed.score))),
|
||||
comment: (parsed.comment || 'Evaluation completed').substring(0, 500),
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to evaluate turn with AI:', error);
|
||||
|
||||
return this.getFallbackEvaluation(turn);
|
||||
}
|
||||
}
|
||||
|
||||
private buildEvaluationContext(
|
||||
turn: AgentTurnEntity & { messages: AgentMessageEntity[] },
|
||||
): string {
|
||||
const userMessages = turn.messages.filter((m) => m.role === 'user');
|
||||
const assistantMessages = turn.messages.filter(
|
||||
(m) => m.role === 'assistant',
|
||||
);
|
||||
|
||||
const userText = userMessages
|
||||
.flatMap((m) => m.parts || [])
|
||||
.filter((p) => p.textContent)
|
||||
.map((p) => p.textContent)
|
||||
.join('\n');
|
||||
|
||||
const assistantParts = assistantMessages.flatMap((m) => m.parts || []);
|
||||
|
||||
const assistantText = assistantParts
|
||||
.filter((p) => p.textContent)
|
||||
.map((p) => p.textContent)
|
||||
.join('\n');
|
||||
|
||||
const toolCalls = assistantParts
|
||||
.filter((p) => p.toolName)
|
||||
.map((p) => ({
|
||||
tool: p.toolName,
|
||||
hasError: !!p.errorMessage,
|
||||
error: p.errorMessage,
|
||||
}));
|
||||
|
||||
const errors = assistantParts
|
||||
.filter((p) => p.errorMessage)
|
||||
.map((p) => p.errorMessage);
|
||||
|
||||
let context = `**User Request:**\n${userText || '(no text)'}\n\n`;
|
||||
|
||||
context += `**Agent Response:**\n${assistantText || '(no text response)'}\n\n`;
|
||||
|
||||
if (toolCalls.length > 0) {
|
||||
context += `**Tools Used:**\n${toolCalls.map((t) => `- ${t.tool}${t.hasError ? ' (FAILED)' : ''}`).join('\n')}\n\n`;
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
context += `**Errors:**\n${errors.map((e) => `- ${e}`).join('\n')}\n\n`;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private getFallbackEvaluation(
|
||||
turn: AgentTurnEntity & { messages: AgentMessageEntity[] },
|
||||
): {
|
||||
score: number;
|
||||
comment: string;
|
||||
} {
|
||||
const parts = turn.messages.flatMap((m) => m.parts || []);
|
||||
const errorCount = parts.filter((p) => p.errorMessage).length;
|
||||
const hasResponse = parts.some((p) => p.textContent);
|
||||
const toolCount = parts.filter((p) => p.toolName).length;
|
||||
|
||||
let score = 100;
|
||||
|
||||
if (errorCount > 0) {
|
||||
score -= errorCount * 30;
|
||||
}
|
||||
|
||||
if (!hasResponse) {
|
||||
score -= 50;
|
||||
}
|
||||
|
||||
const comments = [];
|
||||
|
||||
if (errorCount > 0) {
|
||||
comments.push(`${errorCount} error(s)`);
|
||||
}
|
||||
if (toolCount > 0) {
|
||||
comments.push(`${toolCount} tool(s) used`);
|
||||
}
|
||||
if (!hasResponse) {
|
||||
comments.push('No response');
|
||||
}
|
||||
|
||||
return {
|
||||
score: Math.max(0, score),
|
||||
comment: comments.length > 0 ? comments.join('; ') : 'Completed',
|
||||
};
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -3,12 +3,12 @@ import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { type Repository } from 'typeorm';
|
||||
|
||||
import { type ModelId } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import {
|
||||
AgentException,
|
||||
AgentExceptionCode,
|
||||
} from 'src/engine/metadata-modules/ai-agent/agent.exception';
|
||||
} from 'src/engine/metadata-modules/ai/ai-agent/agent.exception';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { type ModelId } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
|
||||
+2
-2
@@ -3,11 +3,11 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { In, IsNull, Not, Repository } from 'typeorm';
|
||||
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import {
|
||||
AgentException,
|
||||
AgentExceptionCode,
|
||||
} from 'src/engine/metadata-modules/ai-agent/agent.exception';
|
||||
} from 'src/engine/metadata-modules/ai/ai-agent/agent.exception';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { AiAgentRoleService } from 'src/engine/metadata-modules/ai-agent-role/ai-agent-role.service';
|
||||
import { type CreateAgentInput } from 'src/engine/metadata-modules/ai-agent/dtos/create-agent.input';
|
||||
import { type UpdateAgentInput } from 'src/engine/metadata-modules/ai-agent/dtos/update-agent.input';
|
||||
import { AiAgentRoleService } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.service';
|
||||
import { type CreateAgentInput } from 'src/engine/metadata-modules/ai/ai-agent/dtos/create-agent.input';
|
||||
import { type UpdateAgentInput } from 'src/engine/metadata-modules/ai/ai-agent/dtos/update-agent.input';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/compute-metadata-name-from-label.util';
|
||||
|
||||
+10
-8
@@ -10,11 +10,11 @@ import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module';
|
||||
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||
import { UserModule } from 'src/engine/core-modules/user/user.module';
|
||||
import { AiAgentRoleModule } from 'src/engine/metadata-modules/ai-agent-role/ai-agent-role.module';
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai-billing/ai-billing.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai-models/ai-models.module';
|
||||
import { AiRouterModule } from 'src/engine/metadata-modules/ai-router/ai-router.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai-tools/ai-tools.module';
|
||||
import { AiAgentRoleModule } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.module';
|
||||
import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module';
|
||||
import { AiChatRouterModule } from 'src/engine/metadata-modules/ai/ai-chat-router/ai-chat-router.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai/ai-tools/ai-tools.module';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
@@ -48,7 +48,7 @@ import { AgentToolGeneratorService } from './services/agent-tool-generator.servi
|
||||
FileModule,
|
||||
ObjectMetadataModule,
|
||||
PermissionsModule,
|
||||
AiRouterModule,
|
||||
AiChatRouterModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
TokenModule,
|
||||
@@ -63,17 +63,19 @@ import { AgentToolGeneratorService } from './services/agent-tool-generator.servi
|
||||
AgentService,
|
||||
AgentExecutionService,
|
||||
AgentModelConfigService,
|
||||
AgentToolGeneratorService,
|
||||
AgentPlanExecutorService,
|
||||
AgentToolGeneratorService,
|
||||
AgentTitleGenerationService,
|
||||
AgentActorContextService,
|
||||
],
|
||||
exports: [
|
||||
AgentService,
|
||||
AgentExecutionService,
|
||||
AgentToolGeneratorService,
|
||||
AgentPlanExecutorService,
|
||||
AgentToolGeneratorService,
|
||||
AgentTitleGenerationService,
|
||||
AgentActorContextService,
|
||||
AgentModelConfigService,
|
||||
TypeOrmModule.forFeature([AgentEntity]),
|
||||
],
|
||||
})
|
||||
+5
-2
@@ -10,8 +10,8 @@ import {
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ModelId } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { ModelConfiguration } from 'src/engine/metadata-modules/ai-agent/types/modelConfiguration';
|
||||
import { ModelConfiguration } from 'src/engine/metadata-modules/ai/ai-agent/types/modelConfiguration';
|
||||
import { ModelId } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
|
||||
@ObjectType('Agent')
|
||||
export class AgentDTO {
|
||||
@@ -73,4 +73,7 @@ export class AgentDTO {
|
||||
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
modelConfiguration: ModelConfiguration;
|
||||
|
||||
@Field(() => [String])
|
||||
evaluationInputs: string[];
|
||||
}
|
||||
+9
-2
@@ -1,6 +1,7 @@
|
||||
import { Field, HideField, InputType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsArray,
|
||||
IsNotEmpty,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
@@ -10,8 +11,8 @@ import {
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ModelId } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { ModelConfiguration } from 'src/engine/metadata-modules/ai-agent/types/modelConfiguration';
|
||||
import { ModelConfiguration } from 'src/engine/metadata-modules/ai/ai-agent/types/modelConfiguration';
|
||||
import { ModelId } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
|
||||
@InputType()
|
||||
export class CreateAgentInput {
|
||||
@@ -60,6 +61,12 @@ export class CreateAgentInput {
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
modelConfiguration?: ModelConfiguration;
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
@Field(() => [String], { nullable: true })
|
||||
evaluationInputs?: string[];
|
||||
|
||||
@HideField()
|
||||
standardId?: string;
|
||||
|
||||
+9
-2
@@ -1,6 +1,7 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsArray,
|
||||
IsNotEmpty,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
@@ -10,8 +11,8 @@ import {
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ModelId } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { ModelConfiguration } from 'src/engine/metadata-modules/ai-agent/types/modelConfiguration';
|
||||
import { ModelConfiguration } from 'src/engine/metadata-modules/ai/ai-agent/types/modelConfiguration';
|
||||
import { ModelId } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
|
||||
@InputType()
|
||||
export class UpdateAgentInput {
|
||||
@@ -64,4 +65,10 @@ export class UpdateAgentInput {
|
||||
@IsOptional()
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
modelConfiguration?: ModelConfiguration;
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
@Field(() => [String], { nullable: true })
|
||||
evaluationInputs?: string[];
|
||||
}
|
||||
+9
-6
@@ -13,13 +13,13 @@ import {
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/workspace-sync/interfaces/syncable-entity.interface';
|
||||
|
||||
import {
|
||||
ModelId,
|
||||
DEFAULT_SMART_MODEL,
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AgentResponseFormat } from 'src/engine/metadata-modules/ai-agent/types/agent-response-format.type';
|
||||
import { ModelConfiguration } from 'src/engine/metadata-modules/ai-agent/types/modelConfiguration';
|
||||
import { AgentResponseFormat } from 'src/engine/metadata-modules/ai/ai-agent/types/agent-response-format.type';
|
||||
import { ModelConfiguration } from 'src/engine/metadata-modules/ai/ai-agent/types/modelConfiguration';
|
||||
import {
|
||||
DEFAULT_SMART_MODEL,
|
||||
ModelId,
|
||||
} from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
|
||||
@Entity('agent')
|
||||
@Index('IDX_AGENT_ID_DELETED_AT', ['id', 'deletedAt'])
|
||||
@@ -81,4 +81,7 @@ export class AgentEntity
|
||||
|
||||
@Column({ nullable: true, type: 'jsonb' })
|
||||
modelConfiguration: ModelConfiguration;
|
||||
|
||||
@Column({ type: 'text', array: true, default: '{}' })
|
||||
evaluationInputs: string[];
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@ import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/use
|
||||
import {
|
||||
AgentException,
|
||||
AgentExceptionCode,
|
||||
} from 'src/engine/metadata-modules/ai-agent/agent.exception';
|
||||
} from 'src/engine/metadata-modules/ai/ai-agent/agent.exception';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
+17
-18
@@ -15,29 +15,28 @@ import { getAppPath } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
import { getAllSelectableColumnNames } from 'src/engine/api/utils/get-all-selectable-column-names.utils';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai-models/constants/ai-telemetry.const';
|
||||
import { AIBillingService } from 'src/engine/metadata-modules/ai-billing/services/ai-billing.service';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AgentService } from 'src/engine/metadata-modules/ai-agent/agent.service';
|
||||
import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai-agent/constants/agent-config.const';
|
||||
import { AGENT_SYSTEM_PROMPTS } from 'src/engine/metadata-modules/ai-agent/constants/agent-system-prompts.const';
|
||||
import { AgentActorContextService } from 'src/engine/metadata-modules/ai-agent/services/agent-actor-context.service';
|
||||
import { type RecordIdsByObjectMetadataNameSingularType } from 'src/engine/metadata-modules/ai-agent/types/recordIdsByObjectMetadataNameSingular.type';
|
||||
import { type ToolHints } from 'src/engine/metadata-modules/ai-router/types/tool-hints.interface';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import {
|
||||
AgentException,
|
||||
AgentExceptionCode,
|
||||
} from 'src/engine/metadata-modules/ai-agent/agent.exception';
|
||||
import { repairToolCall } from 'src/engine/metadata-modules/ai-agent/utils/repair-tool-call.util';
|
||||
|
||||
import { AgentToolGeneratorService } from './agent-tool-generator.service';
|
||||
import { AgentModelConfigService } from './agent-model-config.service';
|
||||
} from 'src/engine/metadata-modules/ai/ai-agent/agent.exception';
|
||||
import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service';
|
||||
import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-config.const';
|
||||
import { AGENT_SYSTEM_PROMPTS } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-system-prompts.const';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { AgentActorContextService } from 'src/engine/metadata-modules/ai/ai-agent/services/agent-actor-context.service';
|
||||
import { AgentModelConfigService } from 'src/engine/metadata-modules/ai/ai-agent/services/agent-model-config.service';
|
||||
import { AgentToolGeneratorService } from 'src/engine/metadata-modules/ai/ai-agent/services/agent-tool-generator.service';
|
||||
import { type RecordIdsByObjectMetadataNameSingularType } from 'src/engine/metadata-modules/ai/ai-agent/types/recordIdsByObjectMetadataNameSingular.type';
|
||||
import { repairToolCall } from 'src/engine/metadata-modules/ai/ai-agent/utils/repair-tool-call.util';
|
||||
import { AIBillingService } from 'src/engine/metadata-modules/ai/ai-billing/services/ai-billing.service';
|
||||
import { type ToolHints } from 'src/engine/metadata-modules/ai/ai-chat-router/types/tool-hints.interface';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-telemetry.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
export interface AgentExecutionResult {
|
||||
result: object;
|
||||
+4
-4
@@ -5,10 +5,10 @@ import { openai } from '@ai-sdk/openai';
|
||||
import { ProviderOptions } from '@ai-sdk/provider-utils';
|
||||
import { ToolSet } from 'ai';
|
||||
|
||||
import { ModelProvider } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { RegisteredAIModel } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai-agent/constants/agent-config.const';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-config.const';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { ModelProvider } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { RegisteredAIModel } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
|
||||
@Injectable()
|
||||
export class AgentModelConfigService {
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { type RecordIdsByObjectMetadataNameSingularType } from 'src/engine/metadata-modules/ai-agent/types/recordIdsByObjectMetadataNameSingular.type';
|
||||
import { type PlanStep } from 'src/engine/metadata-modules/ai-router/types/router-result.interface';
|
||||
import { type RecordIdsByObjectMetadataNameSingularType } from 'src/engine/metadata-modules/ai/ai-agent/types/recordIdsByObjectMetadataNameSingular.type';
|
||||
import { type PlanStep } from 'src/engine/metadata-modules/ai/ai-chat-router/types/router-result.interface';
|
||||
import { standardAgentDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents';
|
||||
|
||||
import { AgentExecutionService } from './agent-execution.service';
|
||||
+2
-2
@@ -2,8 +2,8 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { generateText } from 'ai';
|
||||
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai-models/constants/ai-telemetry.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-telemetry.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
|
||||
@Injectable()
|
||||
export class AgentTitleGenerationService {
|
||||
+4
-4
@@ -6,11 +6,11 @@ import { Repository } from 'typeorm';
|
||||
|
||||
import type { ActorMetadata } from 'twenty-shared/types';
|
||||
|
||||
import { ToolAdapterService } from 'src/engine/metadata-modules/ai-tools/services/tool-adapter.service';
|
||||
import { ToolService } from 'src/engine/metadata-modules/ai-tools/services/tool.service';
|
||||
import { SearchArticlesTool } from 'src/engine/core-modules/tool/tools/search-articles-tool/search-articles-tool';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import type { ToolHints } from 'src/engine/metadata-modules/ai-router/types/tool-hints.interface';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import type { ToolHints } from 'src/engine/metadata-modules/ai/ai-chat-router/types/tool-hints.interface';
|
||||
import { ToolAdapterService } from 'src/engine/metadata-modules/ai/ai-tools/services/tool-adapter.service';
|
||||
import { ToolService } from 'src/engine/metadata-modules/ai/ai-tools/services/tool.service';
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { HELPER_AGENT } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/helper-agent';
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
|
||||
|
||||
import { isWorkflowRelatedObject } from 'src/engine/metadata-modules/ai-agent/utils/is-workflow-related-object.util';
|
||||
import { isWorkflowRelatedObject } from 'src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-related-object.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
|
||||
describe('isWorkflowRelatedObject', () => {
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { generateObject, type LanguageModel, NoSuchToolError } from 'ai';
|
||||
import { type z } from 'zod';
|
||||
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai-models/constants/ai-telemetry.const';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-telemetry.const';
|
||||
|
||||
type ToolCall = {
|
||||
type: 'tool-call';
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AIBillingService } from 'src/engine/metadata-modules/ai-billing/services/ai-billing.service';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai-models/ai-models.module';
|
||||
import { AIBillingService } from 'src/engine/metadata-modules/ai/ai-billing/services/ai-billing.service';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module';
|
||||
|
||||
@Module({
|
||||
+2
-2
@@ -2,9 +2,9 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { BILLING_FEATURE_USED } from 'src/engine/core-modules/billing/constants/billing-feature-used.constant';
|
||||
import { BillingMeterEventName } from 'src/engine/core-modules/billing/enums/billing-meter-event-names';
|
||||
import { AIBillingService } from 'src/engine/metadata-modules/ai/ai-billing/services/ai-billing.service';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { AIBillingService } from 'src/engine/metadata-modules/ai-billing/services/ai-billing.service';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
|
||||
describe('AIBillingService', () => {
|
||||
let service: AIBillingService;
|
||||
+3
-3
@@ -2,12 +2,12 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { LanguageModelUsage } from 'ai';
|
||||
|
||||
import { type ModelId } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
import { convertCentsToBillingCredits } from 'src/engine/metadata-modules/ai-billing/utils/convert-cents-to-billing-credits.util';
|
||||
import { BILLING_FEATURE_USED } from 'src/engine/core-modules/billing/constants/billing-feature-used.constant';
|
||||
import { BillingMeterEventName } from 'src/engine/core-modules/billing/enums/billing-meter-event-names';
|
||||
import { type BillingUsageEvent } from 'src/engine/core-modules/billing/types/billing-usage-event.type';
|
||||
import { convertCentsToBillingCredits } from 'src/engine/metadata-modules/ai/ai-billing/utils/convert-cents-to-billing-credits.util';
|
||||
import { type ModelId } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
|
||||
@Injectable()
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { DOLLAR_TO_CREDIT_MULTIPLIER } from 'src/engine/metadata-modules/ai-billing/constants/dollar-to-credit-multiplier';
|
||||
import { DOLLAR_TO_CREDIT_MULTIPLIER } from 'src/engine/metadata-modules/ai/ai-billing/constants/dollar-to-credit-multiplier';
|
||||
|
||||
// Converts cost in cents to cost in credits
|
||||
// Formula: credits = (cents / 100) * DOLLAR_TO_CREDIT_MULTIPLIER
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { AiToolsModule } from 'src/engine/metadata-modules/ai/ai-tools/ai-tools.module';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
|
||||
import { AiChatRouterService } from './ai-chat-router.service';
|
||||
|
||||
import { AiChatRouterPlanGeneratorService } from './services/ai-chat-router-plan-generator.service';
|
||||
import { AiChatRouterStrategyDeciderService } from './services/ai-chat-router-strategy-decider.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([AgentEntity, WorkspaceEntity]),
|
||||
AiModelsModule,
|
||||
AiToolsModule,
|
||||
ObjectMetadataModule,
|
||||
],
|
||||
providers: [
|
||||
AiChatRouterService,
|
||||
AiChatRouterStrategyDeciderService,
|
||||
AiChatRouterPlanGeneratorService,
|
||||
],
|
||||
exports: [AiChatRouterService],
|
||||
})
|
||||
export class AiChatRouterModule {}
|
||||
+12
-12
@@ -4,24 +4,24 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { type UIDataTypes, type UIMessage, type UITools } from 'ai';
|
||||
import { IsNull, type Repository } from 'typeorm';
|
||||
|
||||
import { type ModelId } from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import { isWorkflowRelatedObject } from 'src/engine/metadata-modules/ai-agent/utils/is-workflow-related-object.util';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { isWorkflowRelatedObject } from 'src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-related-object.util';
|
||||
import { type ModelId } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { ObjectMetadataServiceV2 } from 'src/engine/metadata-modules/object-metadata/object-metadata-v2.service';
|
||||
import { HELPER_AGENT } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/helper-agent';
|
||||
|
||||
import { AiRouterPlanGeneratorService } from './services/ai-router-plan-generator.service';
|
||||
import { AiChatRouterPlanGeneratorService } from './services/ai-chat-router-plan-generator.service';
|
||||
import {
|
||||
AiRouterStrategyDeciderService,
|
||||
AiChatRouterStrategyDeciderService,
|
||||
type StrategyDecision,
|
||||
} from './services/ai-router-strategy-decider.service';
|
||||
} from './services/ai-chat-router-strategy-decider.service';
|
||||
import {
|
||||
type RouterDebugInfo,
|
||||
type UnifiedRouterResult,
|
||||
} from './types/router-result.interface';
|
||||
import { type ToolHints } from './types/tool-hints.interface';
|
||||
|
||||
export interface AiRouterContext {
|
||||
export interface AiChatRouterContext {
|
||||
messages: UIMessage<unknown, UIDataTypes, UITools>[];
|
||||
workspaceId: string;
|
||||
fastModel: ModelId;
|
||||
@@ -29,19 +29,19 @@ export interface AiRouterContext {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AiRouterService {
|
||||
private readonly logger = new Logger(AiRouterService.name);
|
||||
export class AiChatRouterService {
|
||||
private readonly logger = new Logger(AiChatRouterService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(AgentEntity)
|
||||
private readonly agentRepository: Repository<AgentEntity>,
|
||||
private readonly strategyDecider: AiRouterStrategyDeciderService,
|
||||
private readonly planGenerator: AiRouterPlanGeneratorService,
|
||||
private readonly strategyDecider: AiChatRouterStrategyDeciderService,
|
||||
private readonly planGenerator: AiChatRouterPlanGeneratorService,
|
||||
private readonly objectMetadataService: ObjectMetadataServiceV2,
|
||||
) {}
|
||||
|
||||
async routeMessage(
|
||||
context: AiRouterContext,
|
||||
context: AiChatRouterContext,
|
||||
includeDebugInfo = false,
|
||||
): Promise<UnifiedRouterResult> {
|
||||
try {
|
||||
+7
-7
@@ -8,18 +8,18 @@ import {
|
||||
} from 'ai';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { type AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { type ExecutionPlan } from 'src/engine/metadata-modules/ai/ai-chat-router/types/router-result.interface';
|
||||
import {
|
||||
DEFAULT_SMART_MODEL,
|
||||
type ModelId,
|
||||
} from 'src/engine/metadata-modules/ai-models/constants/ai-models.const';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai-models/constants/ai-telemetry.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai-models/services/ai-model-registry.service';
|
||||
import { type AgentEntity } from 'src/engine/metadata-modules/ai-agent/entities/agent.entity';
|
||||
import { type ExecutionPlan } from 'src/engine/metadata-modules/ai-router/types/router-result.interface';
|
||||
} from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { AI_TELEMETRY_CONFIG } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-telemetry.const';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
|
||||
@Injectable()
|
||||
export class AiRouterPlanGeneratorService {
|
||||
private readonly logger = new Logger(AiRouterPlanGeneratorService.name);
|
||||
export class AiChatRouterPlanGeneratorService {
|
||||
private readonly logger = new Logger(AiChatRouterPlanGeneratorService.name);
|
||||
|
||||
constructor(
|
||||
private readonly aiModelRegistryService: AiModelRegistryService,
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user