AI settings tab (#13496)

https://github.com/user-attachments/assets/87f5a556-ff12-4ce0-aaa7-9120c0432151

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Abdul Rahman
2025-08-01 20:29:28 +05:30
committed by GitHub
parent 75214c3e61
commit c3a9843fcb
60 changed files with 2453 additions and 482 deletions
@@ -2,7 +2,6 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AgentEntity } from 'src/engine/metadata-modules/agent/agent.entity';
import { AgentModule } from 'src/engine/metadata-modules/agent/agent.module';
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
@@ -14,7 +13,6 @@ import { AgentRoleService } from './agent-role.service';
[AgentEntity, RoleEntity, RoleTargetsEntity],
'core',
),
AgentModule,
],
providers: [AgentRoleService],
exports: [AgentRoleService],
@@ -17,6 +17,7 @@ import { In, Repository } from 'typeorm';
import { ModelProvider } from 'src/engine/core-modules/ai/constants/ai-models.const';
import { AiModelRegistryService } from 'src/engine/core-modules/ai/services/ai-model-registry.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
import { FileService } from 'src/engine/core-modules/file/services/file.service';
import { extractFolderPathAndFilename } from 'src/engine/core-modules/file/utils/extract-folderpath-and-filename.utils';
@@ -34,9 +35,7 @@ import { convertOutputSchemaToZod } from 'src/engine/metadata-modules/agent/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 { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
import { streamToBuffer } from 'src/utils/stream-to-buffer';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
import { AgentEntity } from './agent.entity';
import { AgentException, AgentExceptionCode } from './agent.exception';
@@ -96,20 +95,22 @@ export class AgentExecutionService {
agent,
}: {
system: string;
agent: AgentEntity;
agent: AgentEntity | null;
prompt?: string;
messages?: CoreMessage[];
}) {
try {
this.logger.log(
`Preparing AI request config for agent ${agent.id} with model ${agent.modelId}`,
);
if (agent) {
this.logger.log(
`Preparing AI request config for agent ${agent.id} with model ${agent.modelId}`,
);
}
const aiModel = this.aiModelRegistryService.getEffectiveModelConfig(
agent.modelId,
agent?.modelId ?? 'auto',
);
if (!aiModel) {
if (agent && !aiModel) {
const error = `AI model with id ${agent.modelId} not found`;
this.logger.error(error);
@@ -127,10 +128,12 @@ export class AgentExecutionService {
await this.validateApiKey(provider);
const tools = await this.agentToolService.generateToolsForAgent(
agent.id,
agent.workspaceId,
);
const tools = agent
? await this.agentToolService.generateToolsForAgent(
agent.id,
agent.workspaceId,
)
: {};
this.logger.log(`Generated ${Object.keys(tools).length} tools for agent`);
@@ -155,7 +158,7 @@ export class AgentExecutionService {
};
} catch (error) {
this.logger.error(
`Failed to prepare AI request config for agent ${agent.id}:`,
`Failed to prepare AI request config for agent ${agent?.id ?? 'no agent'}`,
error instanceof Error ? error.stack : error,
);
throw error;
@@ -343,18 +346,19 @@ export class AgentExecutionService {
async executeAgent({
agent,
context,
schema,
userPrompt,
}: {
agent: AgentEntity;
agent: AgentEntity | null;
context: Record<string, unknown>;
schema: OutputSchema;
userPrompt: string;
}): Promise<AgentExecutionResult> {
try {
const aiRequestConfig = await this.prepareAIRequestConfig({
system: AGENT_SYSTEM_PROMPTS.AGENT_EXECUTION,
system: `You are executing as part of a workflow automation. ${agent ? agent.prompt : ''}`,
agent,
prompt: resolveInput(agent.prompt, context) as string,
prompt: userPrompt,
});
const textResponse = await generateText(aiRequestConfig);
@@ -5,6 +5,7 @@ import { generateText } from 'ai';
import { Repository } from 'typeorm';
import { AiModelRegistryService } from 'src/engine/core-modules/ai/services/ai-model-registry.service';
import { AGENT_HANDOFF_PROMPT_TEMPLATE } from 'src/engine/metadata-modules/agent/constants/agent-handoff-prompt.const';
import { AgentHandoffService } from './agent-handoff.service';
import { AgentEntity } from './agent.entity';
@@ -31,7 +32,7 @@ export class AgentHandoffExecutorService {
async executeHandoff(handoffRequest: HandoffRequest) {
try {
const { fromAgentId, toAgentId, workspaceId, reason } = handoffRequest;
const { fromAgentId, toAgentId, workspaceId } = handoffRequest;
const canHandoff = await this.agentHandoffService.canHandoffTo({
fromAgentId,
@@ -76,13 +77,7 @@ export class AgentHandoffExecutorService {
const textResponse = await generateText(aiRequestConfig);
return {
success: true,
newAgentId: toAgentId,
newAgentName: targetAgent.name,
message: `Successfully transferred to ${targetAgent.name}. ${reason}`,
response: textResponse.text,
};
return textResponse.text;
} catch (error) {
this.logger.error(
`Handoff execution failed: ${error.message}`,
@@ -101,17 +96,9 @@ export class AgentHandoffExecutorService {
private createHandoffPrompt(handoffRequest: HandoffRequest): string {
const { reason, context } = handoffRequest;
const prompt = `
You have received a handoff from another AI agent. This means the previous agent has determined that you are better suited to handle this conversation based on your specialized knowledge and capabilities.
The previous agent has transferred this conversation to you because: ${reason}
Additional context from the previous agent:
${context || 'No additional context provided'}
Please continue the conversation naturally, acknowledging that you are taking over from the previous agent. Use your specialized knowledge to provide the best possible assistance to the user.
`;
return prompt;
return AGENT_HANDOFF_PROMPT_TEMPLATE.replace('{reason}', reason).replace(
'{context}',
context || 'No additional context provided',
);
}
}
@@ -54,6 +54,24 @@ export class AgentHandoffService {
return handoffs.map((handoff) => handoff.toAgent);
}
async getAgentHandoffs({
fromAgentId,
workspaceId,
}: {
fromAgentId: string;
workspaceId: string;
}): Promise<AgentHandoffEntity[]> {
const handoffs = await this.agentHandoffRepository.find({
where: {
fromAgentId,
workspaceId,
},
relations: ['toAgent'],
});
return handoffs;
}
async createHandoff({
fromAgentId,
toAgentId,
@@ -10,6 +10,7 @@ import { ToolService } from 'src/engine/core-modules/ai/services/tool.service';
import { AgentHandoffExecutorService } from 'src/engine/metadata-modules/agent/agent-handoff-executor.service';
import { AgentHandoffService } from 'src/engine/metadata-modules/agent/agent-handoff.service';
import { AgentService } from 'src/engine/metadata-modules/agent/agent.service';
import { AGENT_HANDOFF_DESCRIPTION_TEMPLATE } from 'src/engine/metadata-modules/agent/constants/agent-handoff-description.const';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { camelCase } from 'src/utils/camel-case';
@@ -67,49 +68,59 @@ export class AgentToolService {
agentId: string,
workspaceId: string,
): Promise<ToolSet> {
const handoffTargets = await this.agentHandoffService.getHandoffTargets({
const handoffs = await this.agentHandoffService.getAgentHandoffs({
fromAgentId: agentId,
workspaceId,
});
const handoffTools = handoffTargets.reduce<ToolSet>(
(tools, targetAgent) => {
const toolName = `transfer_to_${camelCase(targetAgent.name)}`;
const handoffTools = handoffs.reduce<ToolSet>((tools, handoff) => {
const toolName = `handoff_to_${camelCase(handoff.toAgent.name)}`;
const handoffSchema = z.object({
reason: z.string().describe('Reason for transferring to this agent'),
const handoffSchema = z.object({
toolDescription: z
.string()
.describe(
"A clear, human-readable status message describing the handoff being made. This will be shown to the user while the handoff is being processed, so phrase it as a present-tense status update (e.g., 'Transferring you to the sales agent for pricing information').",
),
input: z.object({
reason: z
.string()
.describe(
'Brief explanation of why this handoff is needed (e.g., "User needs pricing information", "User requires technical support", "User wants to discuss billing")',
),
context: z
.string()
.optional()
.describe('Additional context to pass to the receiving agent'),
});
.describe(
'Any relevant context or information to pass to the receiving agent (e.g., user preferences, previous conversation details, specific requirements)',
),
}),
});
tools[toolName] = {
description: `Transfer this request to ${targetAgent.name} when you need their specialized expertise. Use this when the user's request is outside your capabilities or when ${targetAgent.name} would be better suited to handle the request.`,
parameters: handoffSchema,
execute: async ({ reason, context }) => {
const result =
await this.agentHandoffExecutorService.executeHandoff({
fromAgentId: agentId,
toAgentId: targetAgent.id,
workspaceId,
reason,
context,
});
tools[toolName] = {
description:
handoff.description ||
handoff.toAgent.description ||
AGENT_HANDOFF_DESCRIPTION_TEMPLATE.replace(
'{agentName}',
handoff.toAgent.name,
),
parameters: handoffSchema,
execute: async ({ input: { reason, context } }) => {
const result = await this.agentHandoffExecutorService.executeHandoff({
fromAgentId: agentId,
toAgentId: handoff.toAgent.id,
workspaceId,
reason,
context,
});
return {
success: result.success,
message: result.message || `Transferred to ${targetAgent.name}`,
newAgentId: result.newAgentId,
newAgentName: result.newAgentName,
};
},
};
return result;
},
};
return tools;
},
{},
);
return tools;
}, {});
return handoffTools;
}
@@ -4,19 +4,20 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { AiModule } from 'src/engine/core-modules/ai/ai.module';
import { AuditModule } from 'src/engine/core-modules/audit/audit.module';
import { TokenModule } from 'src/engine/core-modules/auth/token/token.module';
import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
import { FileModule } from 'src/engine/core-modules/file/file.module';
import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { AgentRoleModule } from 'src/engine/metadata-modules/agent-role/agent-role.module';
import { AgentChatController } from 'src/engine/metadata-modules/agent/agent-chat.controller';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module';
import { AgentChatMessageEntity } from './agent-chat-message.entity';
import { AgentChatThreadEntity } from './agent-chat-thread.entity';
@@ -49,6 +50,7 @@ import { AgentService } from './agent.service';
'core',
),
AiModule,
AgentRoleModule,
ThrottlerModule,
AuditModule,
FeatureFlagModule,
@@ -1,5 +1,8 @@
import { UseGuards } from '@nestjs/common';
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
@@ -9,17 +12,37 @@ import {
RequireFeatureFlag,
} from 'src/engine/guards/feature-flag.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { CreateAgentHandoffInput } from 'src/engine/metadata-modules/agent/dtos/create-agent-handoff.input';
import { RemoveAgentHandoffInput } from 'src/engine/metadata-modules/agent/dtos/remove-agent-handoff.input';
import { AgentHandoffService } from './agent-handoff.service';
import { AgentEntity } from './agent.entity';
import { AgentService } from './agent.service';
import { AgentHandoffDTO } from './dtos/agent-handoff.dto';
import { AgentIdInput } from './dtos/agent-id.input';
import { AgentDTO } from './dtos/agent.dto';
import { CreateAgentInput } from './dtos/create-agent.input';
import { UpdateAgentInput } from './dtos/update-agent.input';
@UseGuards(WorkspaceAuthGuard, FeatureFlagGuard)
@Resolver()
export class AgentResolver {
constructor(private readonly agentService: AgentService) {}
constructor(
@InjectRepository(AgentEntity, 'core')
private readonly agentRepository: Repository<AgentEntity>,
private readonly agentService: AgentService,
private readonly agentHandoffService: AgentHandoffService,
) {}
@Query(() => [AgentDTO])
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
async findManyAgents(@AuthWorkspace() { id: workspaceId }: Workspace) {
return this.agentRepository.find({
where: { workspaceId },
order: { createdAt: 'DESC' },
});
}
@Query(() => AgentDTO)
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
@@ -30,6 +53,42 @@ export class AgentResolver {
return this.agentService.findOneAgent(id, workspaceId);
}
@Query(() => [AgentDTO])
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
async findAgentHandoffTargets(
@Args('input') { id }: AgentIdInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
) {
return this.agentHandoffService.getHandoffTargets({
fromAgentId: id,
workspaceId,
});
}
@Query(() => [AgentHandoffDTO])
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
async findAgentHandoffs(
@Args('input') { id }: AgentIdInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
) {
return this.agentHandoffService.getAgentHandoffs({
fromAgentId: id,
workspaceId,
});
}
@Mutation(() => AgentDTO)
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
async createOneAgent(
@Args('input') input: CreateAgentInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
) {
return this.agentService.createOneAgent(
{ ...input, isCustom: true },
workspaceId,
);
}
@Mutation(() => AgentDTO)
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
async updateOneAgent(
@@ -38,4 +97,44 @@ export class AgentResolver {
) {
return this.agentService.updateOneAgent(input, workspaceId);
}
@Mutation(() => AgentDTO)
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
async deleteOneAgent(
@Args('input') { id }: AgentIdInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
) {
return this.agentService.deleteOneAgent(id, workspaceId);
}
@Mutation(() => Boolean)
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
async createAgentHandoff(
@Args('input') input: CreateAgentHandoffInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
) {
await this.agentHandoffService.createHandoff({
fromAgentId: input.fromAgentId,
toAgentId: input.toAgentId,
workspaceId,
description: input.description,
});
return true;
}
@Mutation(() => Boolean)
@RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED)
async removeAgentHandoff(
@Args('input') input: RemoveAgentHandoffInput,
@AuthWorkspace() { id: workspaceId }: Workspace,
) {
await this.agentHandoffService.removeHandoff({
fromAgentId: input.fromAgentId,
toAgentId: input.toAgentId,
workspaceId,
});
return true;
}
}
@@ -1,11 +1,15 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import { ModelId } from 'src/engine/core-modules/ai/constants/ai-models.const';
import { AgentRoleService } from 'src/engine/metadata-modules/agent-role/agent-role.service';
import { AgentChatService } from 'src/engine/metadata-modules/agent/agent-chat.service';
import { CreateAgentInput } from 'src/engine/metadata-modules/agent/dtos/create-agent.input';
import { UpdateAgentInput } from 'src/engine/metadata-modules/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';
import { AgentEntity } from './agent.entity';
import { AgentException, AgentExceptionCode } from './agent.exception';
@@ -18,15 +22,9 @@ export class AgentService {
@InjectRepository(RoleTargetsEntity, 'core')
private readonly roleTargetsRepository: Repository<RoleTargetsEntity>,
private readonly agentChatService: AgentChatService,
private readonly agentRoleService: AgentRoleService,
) {}
async findManyAgents(workspaceId: string) {
return this.agentRepository.find({
where: { workspaceId },
order: { createdAt: 'DESC' },
});
}
async findOneAgent(id: string, workspaceId: string) {
const agent = await this.agentRepository.findOne({
where: { id, workspaceId },
@@ -53,71 +51,63 @@ export class AgentService {
};
}
async createOneAgentAndFirstThread(
input: {
name: string;
label: string;
description?: string;
prompt: string;
modelId: ModelId;
},
workspaceId: string,
userWorkspaceId: string | null,
) {
const agent = await this.createOneAgent(input, workspaceId);
if (!userWorkspaceId) {
throw new AgentException(
'User workspace ID not found',
AgentExceptionCode.USER_WORKSPACE_ID_NOT_FOUND,
);
}
await this.agentChatService.createThread(agent.id, userWorkspaceId);
return agent;
}
async createOneAgent(
input: {
name: string;
label: string;
description?: string;
prompt: string;
modelId: ModelId;
responseFormat?: object;
},
input: CreateAgentInput & { isCustom: boolean },
workspaceId: string,
) {
const agent = this.agentRepository.create({
...input,
name: input.name || computeMetadataNameFromLabel(input.label),
workspaceId,
isCustom: input.isCustom,
});
const createdAgent = await this.agentRepository.save(agent);
if (input.roleId) {
await this.agentRoleService.assignRoleToAgent({
workspaceId,
agentId: createdAgent.id,
roleId: input.roleId,
});
}
return this.findOneAgent(createdAgent.id, workspaceId);
}
async updateOneAgent(
input: {
id: string;
name?: string;
description?: string;
prompt?: string;
modelId?: ModelId;
responseFormat?: object;
},
workspaceId: string,
) {
async updateOneAgent(input: UpdateAgentInput, workspaceId: string) {
const agent = await this.findOneAgent(input.id, workspaceId);
let updatedName = input.name;
if (input.label) {
updatedName = computeMetadataNameFromLabel(input.label);
}
const updatedAgent = await this.agentRepository.save({
...agent,
...input,
name: updatedName,
});
return updatedAgent;
if (!isDefined(input.roleId)) {
return updatedAgent;
}
if (input.roleId) {
await this.agentRoleService.assignRoleToAgent({
workspaceId,
agentId: agent.id,
roleId: input.roleId,
});
} else {
await this.agentRoleService.removeRoleFromAgent({
workspaceId,
agentId: agent.id,
});
}
return this.findOneAgent(updatedAgent.id, workspaceId);
}
async deleteOneAgent(id: string, workspaceId: string) {
@@ -0,0 +1,2 @@
export const AGENT_HANDOFF_DESCRIPTION_TEMPLATE =
"Use this tool when the user's request requires {agentName}'s specialized expertise or capabilities. CRITICAL: You MUST call this tool function immediately. Do NOT respond with text about transferring - execute the tool instead. This is a FUNCTION CALL - you must invoke it, not describe it.";
@@ -0,0 +1,11 @@
export const AGENT_HANDOFF_PROMPT_TEMPLATE = `You have received a handoff from another AI agent who determined that you are better suited to handle this conversation based on your specialized knowledge and capabilities.
**Reason for handoff:** {reason}
**Context from the previous agent:**
{context}
**Instructions:**
- Continue the conversation naturally and professionally
- Leverage your specialized expertise to provide the best possible assistance
- Maintain context from the previous conversation while adding your unique value`;
@@ -41,20 +41,20 @@ Guidelines:
- Pay special attention to any data returned from tool executions (database queries, HTTP requests, record creation, etc.)`,
AGENT_CHAT: `You are a helpful AI assistant for this workspace. You can:
- Answer questions conversationally, clearly, and helpfully
- Provide insights, support, and updates about people, companies, opportunities, tasks, notes, and other business objects.
- Answer questions about people, companies, opportunities, tasks, notes, and other business objects
- Access and summarize information you have permission to see
- Help users understand how to use the system and its features
- Use various tools that are provided to you dynamically when needed
- Use tools provided to you dynamically when needed
- Transfer conversations to other specialized agents when their expertise is better suited
Permissions and capabilities:
- You can only perform actions and access data that your assigned role and permissions allow
- If a user requests something you do not have permission for, politely explain the limitation (e.g., "I cannot perform this operation because I don't have the necessary permissions. Please check your role or contact an admin.")
- If you are unsure about your permissions for a specific action, ask the user for clarification or suggest they check with an administrator
- Do not attempt to simulate or fake actions you cannot perform
- Only use tools that are actually available to you through the tools property
Permissions:
- Only perform actions and access data that your assigned role and permissions allow
- If you lack permissions, politely explain the limitation
- Only use tools that are actually available to you
If you need more information to answer a question, ask follow-up questions. Always be transparent about your capabilities and limitations.
Agent handoff:
- Use handoff tools when the user's request requires expertise outside your capabilities
- IMPORTANT: Do not respond with text about transferring - execute the handoff tool function
- Use the response returned by the handoff agent as your reply to the user
When formatting responses:
- Use markdown syntax to improve readability of long responses
@@ -63,5 +63,5 @@ When formatting responses:
- Create tables when presenting structured data
- Use blockquotes for important notes or callouts
Note: This base system prompt will be combined with the agent's specific instructions and context to provide you with complete guidance for your role.`,
Note: This base system prompt will be combined with the agent's specific instructions and context.`,
};
@@ -0,0 +1,17 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { AgentDTO } from './agent.dto';
@ObjectType()
export class AgentHandoffDTO {
@Field(() => UUIDScalarType)
id: string;
@Field({ nullable: true })
description?: string;
@Field(() => AgentDTO)
toAgent: AgentDTO;
}
@@ -1,6 +1,12 @@
import { Field, HideField, ObjectType } from '@nestjs/graphql';
import { IsDateString, IsNotEmpty, IsString, IsUUID } from 'class-validator';
import {
IsBoolean,
IsDateString,
IsNotEmpty,
IsString,
IsUUID,
} from 'class-validator';
import GraphQLJSON from 'graphql-type-json';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
@@ -17,6 +23,14 @@ export class AgentDTO {
@Field()
name: string;
@IsString()
@Field()
label: string;
@IsString()
@Field({ nullable: true })
icon?: string;
@IsString()
@Field({ nullable: true })
description: string;
@@ -35,6 +49,10 @@ export class AgentDTO {
@Field(() => UUIDScalarType, { nullable: true })
roleId?: string;
@IsBoolean()
@Field()
isCustom: boolean;
@HideField()
workspaceId: string;
@@ -0,0 +1,15 @@
import { Field, InputType } from '@nestjs/graphql';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
@InputType()
export class CreateAgentHandoffInput {
@Field(() => UUIDScalarType)
fromAgentId: string;
@Field(() => UUIDScalarType)
toAgentId: string;
@Field({ nullable: true })
description?: string;
}
@@ -1,16 +1,33 @@
import { Field, InputType } from '@nestjs/graphql';
import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator';
import {
IsNotEmpty,
IsObject,
IsOptional,
IsString,
IsUUID,
} from 'class-validator';
import GraphQLJSON from 'graphql-type-json';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { ModelId } from 'src/engine/core-modules/ai/constants/ai-models.const';
@InputType()
export class CreateAgentInput {
@IsString()
@IsOptional()
@Field({ nullable: true })
name?: string;
@IsString()
@IsNotEmpty()
@Field()
name: string;
label: string;
@IsString()
@IsOptional()
@Field({ nullable: true })
icon?: string;
@IsString()
@IsOptional()
@@ -27,6 +44,11 @@ export class CreateAgentInput {
@Field(() => String)
modelId: ModelId;
@IsUUID()
@IsOptional()
@Field(() => UUIDScalarType, { nullable: true })
roleId?: string;
@IsObject()
@IsOptional()
@Field(() => GraphQLJSON, { nullable: true })
@@ -0,0 +1,10 @@
import { Field, InputType } from '@nestjs/graphql';
@InputType()
export class RemoveAgentHandoffInput {
@Field()
fromAgentId: string;
@Field()
toAgentId: string;
}
@@ -24,6 +24,16 @@ export class UpdateAgentInput {
@Field()
name?: string;
@IsString()
@IsOptional()
@Field()
label?: string;
@IsString()
@IsOptional()
@Field({ nullable: true })
icon?: string;
@IsString()
@IsOptional()
@Field({ nullable: true })
@@ -39,6 +49,11 @@ export class UpdateAgentInput {
@Field(() => String)
modelId?: ModelId;
@IsUUID()
@IsOptional()
@Field(() => UUIDScalarType, { nullable: true })
roleId?: string;
@IsObject()
@IsOptional()
@Field(() => GraphQLJSON, { nullable: true })