refactor(twenty-server): consolidate AI tool provider architecture (#16355)
## Summary Consolidates the AI tool provider architecture by creating a single `ToolProviderService` as the entry point for all tool generation. This removes multiple intermediate services and simplifies the codebase. ## Changes ### New Architecture - **`ToolProviderService`**: Single service for all tool generation with: - `getTools(spec)` - Get tools by category with permissions - `getToolByType(type)` - Get specific tool for workflow execution - **`ToolCategory` enum**: Declarative specification of tool types: - `DATABASE_CRUD` - Record CRUD operations - `ACTION` - HTTP requests, email sending, article search - `WORKFLOW` - Workflow management tools - `METADATA` - Object/field metadata tools - `NATIVE_MODEL` - Model-specific tools (e.g., web search) - **`ToolSpecification` type**: Clean API for requesting tools with permissions ### Removed - `AiToolsModule` - No longer needed - `ToolService` - Logic inlined into ToolProviderService - `ToolAdapterService` - Logic inlined into ToolProviderService - `ToolRegistryService` - Logic inlined into ToolProviderService ### Updated - All consumers (agents, chat, MCP, workflows) now use `ToolProviderService` - Test files updated accordingly ## Stats - **547 insertions, 1146 deletions** (net ~600 lines removed) - 4 services deleted - 1 module deleted ## Testing - [x] Typecheck passes - [x] Lint passes
This commit is contained in:
@@ -35,7 +35,6 @@ import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
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';
|
||||
|
||||
@@ -47,7 +46,6 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi
|
||||
PermissionsModule,
|
||||
AiBillingModule,
|
||||
AiModelsModule,
|
||||
AiToolsModule,
|
||||
WorkspaceDomainsModule,
|
||||
TypeOrmModule.forFeature([
|
||||
BillingSubscriptionEntity,
|
||||
|
||||
@@ -56,7 +56,6 @@ import { WorkspaceInvitationModule } from 'src/engine/core-modules/workspace-inv
|
||||
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';
|
||||
@@ -130,7 +129,6 @@ import { FileModule } from './file/file.module';
|
||||
}),
|
||||
CacheStorageModule,
|
||||
AiModelsModule,
|
||||
AiToolsModule,
|
||||
AiBillingModule,
|
||||
ServerlessModule.forRootAsync({
|
||||
useFactory: serverlessModuleFactory,
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// Injection token for WorkflowToolWorkspaceService to break circular dependency:
|
||||
// ToolProviderModule -> WorkflowToolsModule -> WorkflowTriggerModule
|
||||
// -> WorkflowRunnerModule -> WorkflowExecutorModule -> AiAgentActionModule
|
||||
// -> AiAgentExecutionModule -> ToolProviderModule
|
||||
export const WORKFLOW_TOOL_SERVICE_TOKEN = Symbol(
|
||||
'WORKFLOW_TOOL_SERVICE_TOKEN',
|
||||
);
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
export enum ToolCategory {
|
||||
DATABASE_CRUD = 'DATABASE_CRUD',
|
||||
ACTION = 'ACTION',
|
||||
WORKFLOW = 'WORKFLOW',
|
||||
METADATA = 'METADATA',
|
||||
NATIVE_MODEL = 'NATIVE_MODEL',
|
||||
}
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
import { Inject, Injectable, Logger, Optional } from '@nestjs/common';
|
||||
|
||||
import { type ToolSet } from 'ai';
|
||||
|
||||
import { CreateRecordService } from 'src/engine/core-modules/record-crud/services/create-record.service';
|
||||
import { DeleteRecordService } from 'src/engine/core-modules/record-crud/services/delete-record.service';
|
||||
import { FindRecordsService } from 'src/engine/core-modules/record-crud/services/find-records.service';
|
||||
import { UpdateRecordService } from 'src/engine/core-modules/record-crud/services/update-record.service';
|
||||
import { createDirectRecordToolsFactory } from 'src/engine/core-modules/record-crud/tool-factory/direct-record-tools.factory';
|
||||
import { PerObjectToolGeneratorService } from 'src/engine/core-modules/tool-generator/services/per-object-tool-generator.service';
|
||||
import { WORKFLOW_TOOL_SERVICE_TOKEN } from 'src/engine/core-modules/tool-provider/constants/workflow-tool-service.token';
|
||||
import { ToolCategory } from 'src/engine/core-modules/tool-provider/enums/tool-category.enum';
|
||||
import { type ToolSpecification } from 'src/engine/core-modules/tool-provider/types/tool-specification.type';
|
||||
import { ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum';
|
||||
import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool';
|
||||
import { SearchArticlesTool } from 'src/engine/core-modules/tool/tools/search-articles-tool/search-articles-tool';
|
||||
import { SendEmailTool } from 'src/engine/core-modules/tool/tools/send-email-tool/send-email-tool';
|
||||
import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
|
||||
import { type Tool } from 'src/engine/core-modules/tool/types/tool.type';
|
||||
import { AgentModelConfigService } from 'src/engine/metadata-modules/ai/ai-models/services/agent-model-config.service';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
import { FieldMetadataToolsFactory } from 'src/engine/metadata-modules/field-metadata/tools/field-metadata-tools.factory';
|
||||
import { ObjectMetadataToolsFactory } from 'src/engine/metadata-modules/object-metadata/tools/object-metadata-tools.factory';
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
// Type-only import to avoid circular dependency at file level
|
||||
import type { WorkflowToolWorkspaceService } from 'src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service';
|
||||
|
||||
// Tool definition with optional permission flag
|
||||
type ActionTool = {
|
||||
tool: Tool;
|
||||
flag?: PermissionFlagType;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class ToolProviderService {
|
||||
private readonly logger = new Logger(ToolProviderService.name);
|
||||
private readonly actionTools: Map<ToolType, ActionTool>;
|
||||
|
||||
constructor(
|
||||
// Action tools (individual tools)
|
||||
private readonly httpTool: HttpTool,
|
||||
private readonly sendEmailTool: SendEmailTool,
|
||||
private readonly searchArticlesTool: SearchArticlesTool,
|
||||
// Database CRUD tools
|
||||
private readonly perObjectToolGenerator: PerObjectToolGeneratorService,
|
||||
private readonly createRecordService: CreateRecordService,
|
||||
private readonly updateRecordService: UpdateRecordService,
|
||||
private readonly deleteRecordService: DeleteRecordService,
|
||||
private readonly findRecordsService: FindRecordsService,
|
||||
// Workflow tools - optional to avoid circular dependency with WorkflowExecutorModule.
|
||||
// When used from workflow context, this will be null (and workflow tools aren't
|
||||
// needed anyway since agents in workflows shouldn't create other workflows).
|
||||
// When used from chat context, WorkflowToolsModule provides this service.
|
||||
@Optional()
|
||||
@Inject(WORKFLOW_TOOL_SERVICE_TOKEN)
|
||||
private readonly workflowToolService: WorkflowToolWorkspaceService | null,
|
||||
// Metadata tools
|
||||
private readonly objectMetadataToolsFactory: ObjectMetadataToolsFactory,
|
||||
private readonly fieldMetadataToolsFactory: FieldMetadataToolsFactory,
|
||||
// Native model tools
|
||||
private readonly agentModelConfigService: AgentModelConfigService,
|
||||
private readonly aiModelRegistryService: AiModelRegistryService,
|
||||
// Permissions
|
||||
private readonly permissionsService: PermissionsService,
|
||||
) {
|
||||
this.actionTools = new Map([
|
||||
[
|
||||
ToolType.HTTP_REQUEST,
|
||||
{
|
||||
tool: this.httpTool,
|
||||
flag: PermissionFlagType.HTTP_REQUEST_TOOL,
|
||||
},
|
||||
],
|
||||
[
|
||||
ToolType.SEND_EMAIL,
|
||||
{
|
||||
tool: this.sendEmailTool,
|
||||
flag: PermissionFlagType.SEND_EMAIL_TOOL,
|
||||
},
|
||||
],
|
||||
[
|
||||
ToolType.SEARCH_ARTICLES,
|
||||
{
|
||||
tool: this.searchArticlesTool,
|
||||
// No permission flag - available to all agents
|
||||
},
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// Get a specific tool by type (used by workflow executor)
|
||||
getToolByType(toolType: ToolType): Tool {
|
||||
const actionTool = this.actionTools.get(toolType);
|
||||
|
||||
if (!actionTool) {
|
||||
throw new Error(`Unknown tool type: ${toolType}`);
|
||||
}
|
||||
|
||||
return actionTool.tool;
|
||||
}
|
||||
|
||||
async getTools(spec: ToolSpecification): Promise<ToolSet> {
|
||||
const tools: ToolSet = {};
|
||||
|
||||
for (const category of spec.categories) {
|
||||
const categoryTools = await this.getToolsForCategory(category, spec);
|
||||
|
||||
Object.assign(tools, categoryTools);
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Generated ${Object.keys(tools).length} tools for categories: [${spec.categories.join(', ')}]`,
|
||||
);
|
||||
|
||||
if (spec.wrapWithErrorContext) {
|
||||
return this.wrapToolsWithErrorContext(tools);
|
||||
}
|
||||
|
||||
return tools;
|
||||
}
|
||||
|
||||
private async getToolsForCategory(
|
||||
category: ToolCategory,
|
||||
spec: ToolSpecification,
|
||||
): Promise<ToolSet> {
|
||||
switch (category) {
|
||||
case ToolCategory.DATABASE_CRUD:
|
||||
return this.getDatabaseTools(spec);
|
||||
case ToolCategory.ACTION:
|
||||
return this.getActionTools(spec);
|
||||
case ToolCategory.WORKFLOW:
|
||||
return this.getWorkflowTools(spec);
|
||||
case ToolCategory.METADATA:
|
||||
return this.getMetadataTools(spec);
|
||||
case ToolCategory.NATIVE_MODEL:
|
||||
return this.getNativeModelTools(spec);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private async getDatabaseTools(spec: ToolSpecification): Promise<ToolSet> {
|
||||
if (!spec.rolePermissionConfig) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const factory = createDirectRecordToolsFactory({
|
||||
createRecordService: this.createRecordService,
|
||||
updateRecordService: this.updateRecordService,
|
||||
deleteRecordService: this.deleteRecordService,
|
||||
findRecordsService: this.findRecordsService,
|
||||
});
|
||||
|
||||
return this.perObjectToolGenerator.generate(
|
||||
{
|
||||
workspaceId: spec.workspaceId,
|
||||
rolePermissionConfig: spec.rolePermissionConfig,
|
||||
actorContext: spec.actorContext,
|
||||
},
|
||||
[factory],
|
||||
spec.toolHints,
|
||||
);
|
||||
}
|
||||
|
||||
private async getActionTools(spec: ToolSpecification): Promise<ToolSet> {
|
||||
const tools: ToolSet = {};
|
||||
|
||||
for (const [toolType, { tool, flag }] of this.actionTools) {
|
||||
if (!flag) {
|
||||
// No permission flag - available to all
|
||||
tools[toolType.toLowerCase()] = {
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
execute: async (parameters: { input: ToolInput }) =>
|
||||
tool.execute(parameters.input),
|
||||
};
|
||||
} else if (spec.rolePermissionConfig && spec.workspaceId) {
|
||||
const hasPermission = await this.permissionsService.hasToolPermission(
|
||||
spec.rolePermissionConfig,
|
||||
spec.workspaceId,
|
||||
flag,
|
||||
);
|
||||
|
||||
if (hasPermission) {
|
||||
tools[toolType.toLowerCase()] = {
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
execute: async (parameters: { input: ToolInput }) =>
|
||||
tool.execute(parameters.input),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tools;
|
||||
}
|
||||
|
||||
private async getWorkflowTools(spec: ToolSpecification): Promise<ToolSet> {
|
||||
// Workflow tools are optional - not available when called from workflow context
|
||||
// to avoid circular dependencies (agents in workflows shouldn't create workflows)
|
||||
if (!this.workflowToolService) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!spec.rolePermissionConfig) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const hasWorkflowPermission =
|
||||
await this.permissionsService.checkRolesPermissions(
|
||||
spec.rolePermissionConfig,
|
||||
spec.workspaceId,
|
||||
PermissionFlagType.WORKFLOWS,
|
||||
);
|
||||
|
||||
if (!hasWorkflowPermission) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const workflowTools = this.workflowToolService.generateWorkflowTools(
|
||||
spec.workspaceId,
|
||||
spec.rolePermissionConfig,
|
||||
);
|
||||
|
||||
const recordStepTools =
|
||||
await this.workflowToolService.generateRecordStepConfiguratorTools(
|
||||
spec.workspaceId,
|
||||
spec.rolePermissionConfig,
|
||||
spec.toolHints,
|
||||
);
|
||||
|
||||
return { ...workflowTools, ...recordStepTools };
|
||||
}
|
||||
|
||||
private async getMetadataTools(spec: ToolSpecification): Promise<ToolSet> {
|
||||
if (spec.rolePermissionConfig) {
|
||||
const hasDataModelPermission =
|
||||
await this.permissionsService.checkRolesPermissions(
|
||||
spec.rolePermissionConfig,
|
||||
spec.workspaceId,
|
||||
PermissionFlagType.DATA_MODEL,
|
||||
);
|
||||
|
||||
if (!hasDataModelPermission) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const objectMetadataTools = this.objectMetadataToolsFactory.generateTools(
|
||||
spec.workspaceId,
|
||||
);
|
||||
|
||||
const fieldMetadataTools = this.fieldMetadataToolsFactory.generateTools(
|
||||
spec.workspaceId,
|
||||
);
|
||||
|
||||
return { ...objectMetadataTools, ...fieldMetadataTools };
|
||||
}
|
||||
|
||||
private async getNativeModelTools(spec: ToolSpecification): Promise<ToolSet> {
|
||||
if (!spec.agent) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const registeredModel =
|
||||
await this.aiModelRegistryService.resolveModelForAgent(spec.agent);
|
||||
|
||||
return this.agentModelConfigService.getNativeModelTools(
|
||||
registeredModel,
|
||||
spec.agent,
|
||||
);
|
||||
}
|
||||
|
||||
private wrapToolsWithErrorContext(tools: ToolSet): ToolSet {
|
||||
const wrappedTools: ToolSet = {};
|
||||
|
||||
for (const [toolName, tool] of Object.entries(tools)) {
|
||||
if (!tool.execute) {
|
||||
wrappedTools[toolName] = tool;
|
||||
continue;
|
||||
}
|
||||
|
||||
const originalExecute = tool.execute;
|
||||
|
||||
wrappedTools[toolName] = {
|
||||
...tool,
|
||||
execute: async (...args: Parameters<typeof originalExecute>) => {
|
||||
try {
|
||||
return await originalExecute(...args);
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
message: errorMessage,
|
||||
tool: toolName,
|
||||
suggestion: this.generateErrorSuggestion(
|
||||
toolName,
|
||||
errorMessage,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return wrappedTools;
|
||||
}
|
||||
|
||||
private generateErrorSuggestion(
|
||||
toolName: string,
|
||||
errorMessage: string,
|
||||
): string {
|
||||
const lowerError = errorMessage.toLowerCase();
|
||||
|
||||
if (
|
||||
lowerError.includes('not found') ||
|
||||
lowerError.includes('does not exist')
|
||||
) {
|
||||
return 'Verify the ID or name exists with a search query first';
|
||||
}
|
||||
|
||||
if (
|
||||
lowerError.includes('permission') ||
|
||||
lowerError.includes('forbidden') ||
|
||||
lowerError.includes('unauthorized')
|
||||
) {
|
||||
return 'This operation requires elevated permissions or a different role';
|
||||
}
|
||||
|
||||
if (lowerError.includes('invalid') || lowerError.includes('validation')) {
|
||||
return 'Check the tool schema for valid parameter formats and types';
|
||||
}
|
||||
|
||||
if (
|
||||
lowerError.includes('duplicate') ||
|
||||
lowerError.includes('already exists')
|
||||
) {
|
||||
return 'A record with this identifier already exists. Try updating instead of creating';
|
||||
}
|
||||
|
||||
if (lowerError.includes('required') || lowerError.includes('missing')) {
|
||||
return 'Required fields are missing. Check which fields are mandatory for this operation';
|
||||
}
|
||||
|
||||
return 'Try adjusting the parameters or using a different approach';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
|
||||
import { RecordCrudModule } from 'src/engine/core-modules/record-crud/record-crud.module';
|
||||
import { ToolGeneratorModule } from 'src/engine/core-modules/tool-generator/tool-generator.module';
|
||||
import { ToolModule } from 'src/engine/core-modules/tool/tool.module';
|
||||
import { AiAgentExecutionModule } from 'src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module';
|
||||
import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-models.module';
|
||||
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
|
||||
import { ToolProviderService } from './services/tool-provider.service';
|
||||
|
||||
// NOTE: This module does NOT import WorkflowToolsModule to avoid circular dependency:
|
||||
// ToolProviderModule -> WorkflowToolsModule -> WorkflowTriggerModule
|
||||
// -> WorkflowRunnerModule -> WorkflowExecutorModule -> AiAgentActionModule
|
||||
// -> AiAgentExecutionModule -> ToolProviderModule
|
||||
//
|
||||
// Instead, WorkflowToolWorkspaceService is an optional dependency that must be
|
||||
// provided by the importing module (e.g., AiChatModule imports WorkflowToolsModule).
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ToolModule,
|
||||
ToolGeneratorModule,
|
||||
RecordCrudModule,
|
||||
AiModelsModule,
|
||||
// forwardRef needed: AiAgentExecutionModule imports ToolProviderModule
|
||||
forwardRef(() => AiAgentExecutionModule),
|
||||
ObjectMetadataModule,
|
||||
FieldMetadataModule,
|
||||
PermissionsModule,
|
||||
],
|
||||
providers: [ToolProviderService],
|
||||
exports: [ToolProviderService],
|
||||
})
|
||||
export class ToolProviderModule {}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { type ActorMetadata } from 'twenty-shared/types';
|
||||
|
||||
import { type ToolCategory } from 'src/engine/core-modules/tool-provider/enums/tool-category.enum';
|
||||
import { type ToolHints } from 'src/engine/metadata-modules/ai/ai-chat-router/types/tool-hints.interface';
|
||||
import { type FlatAgentWithRoleId } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
export type ToolSpecification = {
|
||||
workspaceId: string;
|
||||
categories: ToolCategory[];
|
||||
rolePermissionConfig?: RolePermissionConfig;
|
||||
actorContext?: ActorMetadata;
|
||||
agent?: FlatAgentWithRoleId | null;
|
||||
toolHints?: ToolHints;
|
||||
wrapWithErrorContext?: boolean;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
export enum ToolType {
|
||||
HTTP_REQUEST = 'HTTP_REQUEST',
|
||||
SEND_EMAIL = 'SEND_EMAIL',
|
||||
SEARCH_ARTICLES = 'SEARCH_ARTICLES',
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum';
|
||||
import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool';
|
||||
import { SendEmailTool } from 'src/engine/core-modules/tool/tools/send-email-tool/send-email-tool';
|
||||
import { type SendEmailInput } from 'src/engine/core-modules/tool/tools/send-email-tool/types/send-email-input.type';
|
||||
import { type Tool } from 'src/engine/core-modules/tool/types/tool.type';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
|
||||
@Injectable()
|
||||
export class ToolRegistryService {
|
||||
private readonly toolFactories: Map<ToolType, () => Tool>;
|
||||
|
||||
constructor(
|
||||
private readonly sendEmailTool: SendEmailTool,
|
||||
private readonly twentyConfigService: TwentyConfigService,
|
||||
) {
|
||||
this.toolFactories = new Map<ToolType, () => Tool>([
|
||||
[
|
||||
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,
|
||||
() => ({
|
||||
description: this.sendEmailTool.description,
|
||||
inputSchema: this.sendEmailTool.inputSchema,
|
||||
execute: (params) =>
|
||||
this.sendEmailTool.execute(params as SendEmailInput),
|
||||
flag: PermissionFlagType.SEND_EMAIL_TOOL,
|
||||
}),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
getTool(toolType: ToolType): Tool {
|
||||
const factory = this.toolFactories.get(toolType);
|
||||
|
||||
if (!factory) {
|
||||
throw new Error(`Unknown tool type: ${toolType}`);
|
||||
}
|
||||
|
||||
return factory();
|
||||
}
|
||||
|
||||
getAllToolTypes(): ToolType[] {
|
||||
return Array.from(this.toolFactories.keys());
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { ToolRegistryService } from 'src/engine/core-modules/tool/services/tool-registry.service';
|
||||
import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool';
|
||||
import { SearchArticlesTool } from 'src/engine/core-modules/tool/tools/search-articles-tool/search-articles-tool';
|
||||
import { SendEmailTool } from 'src/engine/core-modules/tool/tools/send-email-tool/send-email-tool';
|
||||
@@ -15,7 +14,7 @@ import { MessagingImportManagerModule } from 'src/modules/messaging/message-impo
|
||||
TypeOrmModule.forFeature([FileEntity]),
|
||||
FileModule,
|
||||
],
|
||||
providers: [HttpTool, SendEmailTool, SearchArticlesTool, ToolRegistryService],
|
||||
exports: [ToolRegistryService],
|
||||
providers: [HttpTool, SendEmailTool, SearchArticlesTool],
|
||||
exports: [HttpTool, SendEmailTool, SearchArticlesTool],
|
||||
})
|
||||
export class ToolModule {}
|
||||
|
||||
+3
-4
@@ -5,8 +5,7 @@ import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/featu
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||
import { ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum';
|
||||
import { ToolRegistryService } from 'src/engine/core-modules/tool/services/tool-registry.service';
|
||||
import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool';
|
||||
import { CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-step-input.dto';
|
||||
import { DeleteWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/delete-workflow-version-step-input.dto';
|
||||
import { DuplicateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/duplicate-workflow-version-step-input.dto';
|
||||
@@ -47,7 +46,7 @@ export class WorkflowVersionStepResolver {
|
||||
private readonly workflowVersionStepWorkspaceService: WorkflowVersionStepWorkspaceService,
|
||||
private readonly workflowRunnerWorkspaceService: WorkflowRunnerWorkspaceService,
|
||||
private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService,
|
||||
private readonly toolRegistryService: ToolRegistryService,
|
||||
private readonly httpTool: HttpTool,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
@@ -153,7 +152,7 @@ export class WorkflowVersionStepResolver {
|
||||
@Args('input')
|
||||
{ url, method, headers, body }: TestHttpRequestInput,
|
||||
): Promise<TestHttpRequestOutput> {
|
||||
return this.toolRegistryService.getTool(ToolType.HTTP_REQUEST).execute({
|
||||
return this.httpTool.execute({
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
|
||||
Reference in New Issue
Block a user