feat: Migrate documentation to Mintlify and implement Helper Agent with search functionality (#15443)
This commit is contained in:
@@ -11,8 +11,11 @@ import { ToolAdapterService } from 'src/engine/core-modules/ai/services/tool-ada
|
||||
import { ToolService } from 'src/engine/core-modules/ai/services/tool.service';
|
||||
import { TokenModule } from 'src/engine/core-modules/auth/token/token.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 { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { RecordCrudModule } from 'src/engine/core-modules/record-crud/record-crud.module';
|
||||
import { ToolRegistryService } from 'src/engine/core-modules/tool/services/tool-registry.service';
|
||||
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 { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
@@ -22,8 +25,6 @@ import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/wor
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { MessagingModule } from 'src/modules/messaging/messaging.module';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
@@ -51,6 +52,7 @@ import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
AIBillingService,
|
||||
McpService,
|
||||
SendEmailTool,
|
||||
SearchArticlesTool,
|
||||
],
|
||||
exports: [
|
||||
AiService,
|
||||
@@ -61,6 +63,7 @@ import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
ToolRegistryService,
|
||||
McpService,
|
||||
SendEmailTool,
|
||||
SearchArticlesTool,
|
||||
],
|
||||
})
|
||||
export class AiModule {}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const SearchArticlesInputZodSchema = z.object({
|
||||
query: z
|
||||
.string()
|
||||
.describe('The search query to find relevant help articles about Twenty'),
|
||||
});
|
||||
|
||||
export const SearchArticlesToolParametersZodSchema = z.object({
|
||||
loadingMessage: z
|
||||
.string()
|
||||
.describe(
|
||||
'A clear, human-readable status message describing the search being performed. This will be shown to the user while the tool is being called, so phrase it as a present-tense status update (e.g., "Searching help articles for..."). Explain what you are searching for in natural language.',
|
||||
),
|
||||
input: SearchArticlesInputZodSchema,
|
||||
});
|
||||
|
||||
export type SearchArticlesInput = z.infer<typeof SearchArticlesInputZodSchema>;
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
import { SearchArticlesToolParametersZodSchema } from 'src/engine/core-modules/tool/tools/search-articles-tool/search-articles-tool.schema';
|
||||
import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
|
||||
import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.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';
|
||||
|
||||
@Injectable()
|
||||
export class SearchArticlesTool implements Tool {
|
||||
description =
|
||||
'Search Twenty documentation and help articles to find information about features, setup, usage, and troubleshooting.';
|
||||
inputSchema = SearchArticlesToolParametersZodSchema;
|
||||
|
||||
constructor(private readonly twentyConfigService: TwentyConfigService) {}
|
||||
|
||||
async execute(parameters: ToolInput): Promise<ToolOutput> {
|
||||
const { query } = parameters;
|
||||
|
||||
try {
|
||||
const MINTLIFY_API_KEY = this.twentyConfigService.get('MINTLIFY_API_KEY');
|
||||
const MINTLIFY_SUBDOMAIN =
|
||||
this.twentyConfigService.get('MINTLIFY_SUBDOMAIN');
|
||||
|
||||
const useDirectApi = MINTLIFY_API_KEY && MINTLIFY_SUBDOMAIN;
|
||||
|
||||
const endpoint = useDirectApi
|
||||
? `https://api-dsc.mintlify.com/v1/search/${MINTLIFY_SUBDOMAIN}`
|
||||
: 'https://twenty-help-search.com/search/twenty';
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...(useDirectApi && { Authorization: `Bearer ${MINTLIFY_API_KEY}` }),
|
||||
};
|
||||
|
||||
const response = await axios.post(
|
||||
endpoint,
|
||||
{ query, pageSize: 10 },
|
||||
{ headers },
|
||||
);
|
||||
|
||||
const results = response.data;
|
||||
|
||||
if (results.length === 0) {
|
||||
return {
|
||||
success: true,
|
||||
message: `No help articles found for "${query}"`,
|
||||
result: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Found ${results.length} relevant help article${results.length === 1 ? '' : 's'} for "${query}"`,
|
||||
result: results,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorDetail = axios.isAxiosError(error)
|
||||
? error.response?.data?.message || error.message
|
||||
: error instanceof Error
|
||||
? error.message
|
||||
: 'Documentation search failed';
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `Failed to search help articles for "${query}"`,
|
||||
error: errorDetail,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1262,6 +1262,26 @@ export class ConfigVariables {
|
||||
@ValidateIf((env) => env.IS_MAPS_AND_ADDRESS_AUTOCOMPLETE_ENABLED)
|
||||
GOOGLE_MAP_API_KEY: string;
|
||||
|
||||
@ConfigVariablesMetadata({
|
||||
group: ConfigVariablesGroup.OTHER,
|
||||
isSensitive: true,
|
||||
description: 'Mintlify API key for documentation search',
|
||||
isEnvOnly: true,
|
||||
type: ConfigVariableType.STRING,
|
||||
})
|
||||
@IsOptional()
|
||||
MINTLIFY_API_KEY: string;
|
||||
|
||||
@ConfigVariablesMetadata({
|
||||
group: ConfigVariablesGroup.OTHER,
|
||||
isSensitive: true,
|
||||
description: 'Mintlify subdomain for documentation search',
|
||||
isEnvOnly: true,
|
||||
type: ConfigVariableType.STRING,
|
||||
})
|
||||
@IsOptional()
|
||||
MINTLIFY_SUBDOMAIN: string;
|
||||
|
||||
@ConfigVariablesMetadata({
|
||||
group: ConfigVariablesGroup.AWS_SES_SETTINGS,
|
||||
description: 'AWS region',
|
||||
|
||||
Reference in New Issue
Block a user