From 8d04c1d495c4eaedc3e6535c508551eec84cd312 Mon Sep 17 00:00:00 2001 From: ruanwebdev Date: Tue, 23 Sep 2025 16:28:38 -0300 Subject: [PATCH] FEAT: add logic to native documentation with AI --- dadosfera-protospack-v2-0.0.0.tgz | Bin 0 -> 100545 bytes docsfera.json | 137 +++++ environment.d.ts | 8 + src/modules/catalog/catalog.controller.ts | 159 +++-- src/modules/catalog/catalog.service.ts | 581 +++++++++++++++++- .../catalog/constants/autodrive.constants.ts | 42 ++ .../prompts/ai-documentation.prompt.ts | 88 +++ .../catalog/types/ai-documentation.types.ts | 60 ++ 8 files changed, 1024 insertions(+), 51 deletions(-) create mode 100644 dadosfera-protospack-v2-0.0.0.tgz create mode 100644 src/modules/catalog/constants/autodrive.constants.ts create mode 100644 src/modules/catalog/prompts/ai-documentation.prompt.ts create mode 100644 src/modules/catalog/types/ai-documentation.types.ts diff --git a/dadosfera-protospack-v2-0.0.0.tgz b/dadosfera-protospack-v2-0.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f412b56911c9bf586d641d2f7ee651857ca83cdc GIT binary patch literal 100545 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

wes`u) aONcM^$)|tmU;5XwzyA+__d4bP(g*+q4*Zn> literal 0 HcmV?d00001 diff --git a/docsfera.json b/docsfera.json index a18dad5..3ce31fc 100644 --- a/docsfera.json +++ b/docsfera.json @@ -42,6 +42,33 @@ ] } }, + "/auth/sign-out": { + "post": { + "operationId": "AuthController_signOut", + "parameters": [ + { + "name": "dadosfera-lang", + "in": "header", + "required": false, + "schema": { + "enum": [ + "pt-br", + "en-us" + ], + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + } + }, + "tags": [ + "Auth" + ] + } + }, "/auth/refresh-access-token": { "post": { "operationId": "AuthController_refreshAccessToken", @@ -5135,6 +5162,116 @@ ] } }, + "/catalog/data-asset/{nimbus_id}/docs/ai": { + "post": { + "operationId": "CatalogController_saveDocumentation", + "summary": "Save documentation for data asset", + "description": "Saves documentation content for a data asset", + "parameters": [ + { + "name": "dadosfera-lang", + "in": "header", + "required": false, + "schema": { + "enum": [ + "pt-br", + "en-us" + ], + "type": "string" + } + }, + { + "name": "nimbus_id", + "required": true, + "in": "path", + "description": "Nimbus ID of the data asset", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "Documentation saved successfully" + } + }, + "tags": [ + "Catalog" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, + "/catalog/data-asset/{nimbus_id}/docs/generate-ai": { + "post": { + "operationId": "CatalogController_generateAiDocumentation", + "summary": "Generate AI documentation for data asset", + "description": "Generates comprehensive documentation for a data asset using AI (Autodrive)", + "parameters": [ + { + "name": "dadosfera-lang", + "in": "header", + "required": false, + "schema": { + "enum": [ + "pt-br", + "en-us" + ], + "type": "string" + } + }, + { + "name": "nimbus_id", + "required": true, + "in": "path", + "description": "Nimbus ID of the data asset", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "AI documentation generated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "documentation": { + "type": "string" + } + } + } + } + } + }, + "400": { + "description": "Bad request - invalid nimbus_id or missing data" + }, + "404": { + "description": "Data asset not found" + }, + "500": { + "description": "Internal server error during AI generation" + } + }, + "tags": [ + "Catalog" + ], + "security": [ + { + "access-token": [] + } + ] + } + }, "/customers/{id}/mfa": { "post": { "operationId": "CustomersController_enableMfaEnforce", diff --git a/environment.d.ts b/environment.d.ts index 3b44250..74a7457 100644 --- a/environment.d.ts +++ b/environment.d.ts @@ -16,6 +16,14 @@ declare global { OPEN_CUSTOMER_ID: string; DEDICATED_PROXY: string; COOKIE_SECRET: string; + + // Autodrive Configuration + AUTODRIVE_USERNAME?: string; + AUTODRIVE_PASSWORD?: string; + AUTODRIVE_BASE_URL?: string; + AUTODRIVE_MODEL?: string; + AUTODRIVE_KEY?: string; + AUTO_DRIVE_KEY?: string; } } } diff --git a/src/modules/catalog/catalog.controller.ts b/src/modules/catalog/catalog.controller.ts index b87829b..769a159 100644 --- a/src/modules/catalog/catalog.controller.ts +++ b/src/modules/catalog/catalog.controller.ts @@ -22,6 +22,9 @@ import { ApiHeaders, ApiOkResponse, ApiTags, + ApiOperation, + ApiParam, + ApiResponse, } from '@nestjs/swagger'; import { Authenticated, @@ -88,10 +91,6 @@ export class CatalogController { @Query() query: ICatalogAllRequest, ): Promise { const { user_id, customer_name, customer_id, username, permissions } = user; - this.logger.info(`/catalog - searchCatalog`, { - user_id, - customer_name, - }); const is_data_manager = permissions.includes( PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER.seqid, @@ -128,10 +127,6 @@ export class CatalogController { @Res() res: Response ) { const { user_id, customer_name, customer_id, username, permissions } = user; - this.logger.info(`/catalog/download - searchCatalog`, { - user_id, - customer_name, - }); const is_data_manager = permissions.includes( PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER.seqid, @@ -168,10 +163,6 @@ export class CatalogController { async findByPipelineAndObject(@User() user: RequestUser, @Query() query) { const { username, user_id, customer_id, customer_name, permissions } = user; const { pipeline, object } = query; - this.logger.info(`/catalog - ON GET DATA ASSET BY PIPELINE AND OBJECT`, { - username, - customer_name, - }); if (!pipeline || !object) { throw new BadRequestException('Query params not provided'); @@ -224,10 +215,6 @@ export class CatalogController { PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER, ) async findAllTags(@Body() body) { - this.logger.info(`/catalog - ON FIND ALL TAGS ROUTE`, { - user: body.info.user_id, - customer: body.info.customer, - }); const { user_id, customer, customer_id } = body.info; const metadata = PackTheMetadata({ @@ -253,10 +240,6 @@ export class CatalogController { ) { const { username, user_id, customer_id, customer_name, permissions } = user; - this.logger.info(`GET /data-asset/${id}`, { - username, - customer_name, - }); const is_data_manager = permissions.includes( PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER.seqid, @@ -309,10 +292,6 @@ export class CatalogController { ) { const { username, user_id, customer_id, customer_name, permissions } = user; - this.logger.info(`/catalog - ON GET ONE DASHBOARD METABASE ROUTE`, { - username, - customer_name, - }); const is_data_manager = permissions.includes( PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER.seqid, @@ -364,10 +343,6 @@ export class CatalogController { ): Promise { const { customer_name, customer_id, user_id, username } = user; - this.logger.info(`/catalog - columns-metadata`, { - user_id, - customer_name, - }); const metadata = PackTheMetadata({ customer_name, @@ -395,10 +370,6 @@ export class CatalogController { ): Promise { const { customer_name, customer_id, user_id, username, customer_modules } = user; - this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, { - user_id, - customer_name, - }); const metadata = PackTheMetadata({ customer_name, @@ -424,24 +395,31 @@ export class CatalogController { @Language() language: LanguageEnum, @Param('id') id: string, ): Promise { - const { customer_name, customer_id, user_id, username } = user; + try { + const { customer_name, customer_id, user_id, username } = user; - this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, { - user_id, - customer_name, - }); + this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, { + user_id, + customer_name, + id, + }); - const metadata = PackTheMetadata({ - customer_name, - customer_id, - user_id, - username, - language, - }); + const metadata = PackTheMetadata({ + customer_name, + customer_id, + user_id, + username, + language, + }); - const docs = await this.catalogService.getDataDocs(id, metadata); + const docs = await this.catalogService.getDataDocs(id, metadata); - return { docs }; + return { docs }; + } catch (error) { + this.logger.error(`Error in getDataAssetDocs for id ${id}: ${error.message}`); + this.logger.error(`Error details: ${JSON.stringify(error)}`); + throw error; + } } @Put('data-asset/:id') @@ -490,10 +468,6 @@ export class CatalogController { ) { const { user_id, customer_name } = user; - this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, { - user_id, - customer_name, - }); const res = await this.catalogService.createDataDocs({ table_id, @@ -506,6 +480,7 @@ export class CatalogController { return res; } + @ApiInternalOnlyEndpoint() @Put('data-asset/:id/manage-permissions') async manageDataAssetPermissions( @@ -931,4 +906,88 @@ export class CatalogController { this.logger.error(error.message); } } + + @Post('/data-asset/:nimbus_id/docs/ai') + @ApiOperation({ + summary: 'Save documentation for data asset', + description: 'Saves documentation content for a data asset', + }) + @ApiParam({ + name: 'nimbus_id', + description: 'Nimbus ID of the data asset', + type: 'string', + }) + @ApiResponse({ + status: 201, + description: 'Documentation saved successfully', + }) + async saveDocumentation( + @Param('nimbus_id') nimbusId: string, + @Body() body: { docs: string }, + @User() user: RequestUser, + ) { + + const metadata = PackTheMetadata(user); + + try { + await this.catalogService.updateDataAssetDocumentation(nimbusId, body.docs, metadata); + return { + message: 'Documentation saved successfully', + }; + } catch (error) { + this.logger.error(`Error saving documentation for ${nimbusId}: ${error.message}`); + throw error; + } + } + + @Post('/data-asset/:nimbus_id/docs/generate-ai') + @ApiOperation({ + summary: 'Generate AI documentation for data asset', + description: 'Generates comprehensive documentation for a data asset using AI (Autodrive)', + }) + @ApiParam({ + name: 'nimbus_id', + description: 'Nimbus ID of the data asset', + type: 'string', + }) + @ApiResponse({ + status: 201, + description: 'AI documentation generated successfully', + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + documentation: { type: 'string' }, + }, + }, + }) + @ApiResponse({ + status: 400, + description: 'Bad request - invalid nimbus_id or missing data', + }) + @ApiResponse({ + status: 404, + description: 'Data asset not found', + }) + @ApiResponse({ + status: 500, + description: 'Internal server error during AI generation', + }) + async generateAiDocumentation( + @Param('nimbus_id') dataAssetId: string, + @User() user: RequestUser, + ) { + const metadata = PackTheMetadata(user); + + try { + const result = await this.catalogService.generateAiDocumentation(dataAssetId, metadata, user); + return { + message: 'AI documentation generated successfully', + documentation: result, + }; + } catch (error) { + this.logger.error(`Error generating AI documentation for ${dataAssetId}: ${error.message}`); + throw error; + } + } } \ No newline at end of file diff --git a/src/modules/catalog/catalog.service.ts b/src/modules/catalog/catalog.service.ts index fafc14a..15f44c1 100644 --- a/src/modules/catalog/catalog.service.ts +++ b/src/modules/catalog/catalog.service.ts @@ -1,4 +1,5 @@ import DadosferaLogger from '@dadosfera/dadosfera-logs/dist'; +import FormData from 'form-data'; import { WriteService, ReadService, @@ -38,6 +39,20 @@ import { } from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages'; import { TypeParser } from 'src/utils/FileParser/parser-types'; import { ParserBuilder } from 'src/utils/FileParser/parser.builder'; +import { AI_DOCUMENTATION_PROMPT, AI_DOCUMENTATION_CONFIG } from './prompts/ai-documentation.prompt'; +import { AUTODRIVE_CONSTANTS, GEOGRAPHIC_KEYS, COMMON_COUNTRIES } from './constants/autodrive.constants'; +import { + AutodriveCredentials, + AutodriveAskPayload, + AutodriveAskResponse, + AutodriveAnswerResponse, + AutodriveUploadResponse, + DatasetStatusResponse, + ColumnData, + ColumnsMetadata, + DataPreview, + FormattedDataForAI +} from './types/ai-documentation.types'; class CatalogService implements OnModuleInit { catalogReadService: ReadService.CatalogReadServices; @@ -336,15 +351,44 @@ class CatalogService implements OnModuleInit { } async getDataDocs(id: string, metadata: Metadata) { - const { documentation } = await lastValueFrom( + let documentation: string; + + try { + const response = await lastValueFrom( this.catalogReadService.GetDatasetDoc({ id, type: undefined }, metadata), ); + const doc = response.documentation; + documentation = doc; + + if (!documentation) { + this.logger.warn(`No documentation found for id: ${id}`); + return null; + } + + // Verificar se a documentação é um JSON válido + if (documentation.trim().startsWith('{') || documentation.trim().startsWith('[')) { const docs = JSON.parse(documentation); return docs; + } else { + this.logger.warn(`Documentation is not JSON format, returning as raw text for id: ${id}`); + return { raw_documentation: documentation }; + } + } catch (error) { + this.logger.error(`getDataDocs failed for id ${id}: ${error.message}`); + + // Se for erro de JSON parsing, tentar retornar a documentação como string + if (error.message.includes('JSON') || error.message.includes('parse')) { + this.logger.warn(`JSON parsing failed, returning raw documentation for id: ${id}`); + return { raw_documentation: documentation || 'No documentation available' }; + } + + throw error; + } } async getDatasetPreview(id: string, metadata: Metadata) { + try { const { preview } = await lastValueFrom( this.catalogReadService.GetDatasetPreview( { id, type: undefined }, @@ -353,19 +397,35 @@ class CatalogService implements OnModuleInit { ); const result = JSON.parse(preview); return result; + } catch (error) { + this.logger.error(`getDatasetPreview failed for id ${id}: ${error.message}`); + throw error; + } } async getDatasetColumnsMetadata(id: string, metadata: Metadata) { + try { const { columns_metadata } = await lastValueFrom( this.catalogReadService.GetDatasetColumnsMetadata( { id, type: undefined }, metadata, ), ); + + if (!columns_metadata || columns_metadata.length <= 2) { + this.logger.warn(`Empty or minimal response from gRPC: "${columns_metadata}"`); + throw new Error('Empty response from gRPC service'); + } + const result = JSON.parse(columns_metadata); return result; + } catch (error) { + this.logger.error(`getDatasetColumnsMetadata failed for id ${id}: ${error.message}`); + throw error; + } } + async createDataDocs(body) { const nimbusUrl = this._getNimbusUrl(body); const { data } = await axios.post( @@ -375,6 +435,7 @@ class CatalogService implements OnModuleInit { return data; } + async findAllTags(data, metadata) { this.logger.info('CatalogService - findAllCustomerTags'); @@ -605,6 +666,524 @@ class CatalogService implements OnModuleInit { ); return res; } + + + private capitalizeFirst(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1); + } + + + + + async updateDataAssetDocumentation(nimbusId: string, documentation: string, metadata: Metadata): Promise { + try { + if (!documentation) { + this.logger.warn(`Documentation is undefined for ${nimbusId}`); + return; + } + + const changes = { + documentation: documentation + }; + + await lastValueFrom( + this.catalogWriteService.UpdateDataAsset( + { id: nimbusId, changes: JSON.stringify(changes) }, + metadata, + ), + ); + + this.logger.info(`Documentation updated successfully for ${nimbusId}`); + } catch (error) { + this.logger.error(`Error updating documentation for ${nimbusId}: ${error.message}`); + throw error; + } + } + + async generateAiDocumentation(dataAssetId: string, metadata: Metadata, user?: any): Promise { + this.logger.info(`Generating AI documentation for data asset: ${dataAssetId}`); + + try { + const customer_id = metadata.get('customer_id')?.[0]?.toString(); + + // 1. Buscar metadados do data asset + const dataAsset = await this.getOneDataAsset({ + id: dataAssetId, + customer_id, + metadata + }); + + if (!dataAsset || !dataAsset.data_asset || !dataAsset.data_asset.nimbus_id) { + throw new Error("Nimbus ID not found in data asset metadata"); + } + + const nimbusId = dataAsset.data_asset.nimbus_id.toString(); + + // 2. Buscar informações das colunas + let columnsData = null; + try { + columnsData = await this.getDatasetColumnsMetadata(dataAssetId, metadata); + } catch (error) { + this.logger.warn(`Failed to get column metadata: ${error.message}`); + columnsData = null; + } + + // 2.1. Fallback para dados das colunas + const dataAssetWithColumns = dataAsset as any; + if (!columnsData && dataAssetWithColumns.columns && Array.isArray(dataAssetWithColumns.columns)) { + columnsData = { columns: dataAssetWithColumns.columns }; + } else if (!columnsData) { + const numColumns = parseInt(dataAsset.data_asset.num_columns) || 0; + if (numColumns > 0) { + columnsData = { + columns: Array.from({ length: numColumns }, (_, i) => ({ + name: `COLUMN_${i + 1}`, + type: 'UNKNOWN', + description: 'Column information not available', + nullable: 'UNKNOWN' + })) + }; + } + } + + // 3. Buscar preview dos dados (opcional) + let dataPreview = []; + try { + dataPreview = await this.getDatasetPreview(nimbusId, metadata); + } catch (error) { + this.logger.warn(`Failed to get data preview: ${error.message}`); + dataPreview = []; + } + + // 4. Formatar dados e chamar Autodrive + const combinedText = this.formatDataForAI(dataAsset, dataPreview, columnsData); + const documentation = await this.callAutodriveForDocumentation(combinedText, user?.access_token); + + // 5. Salvar a documentação + const createDataDocsPayload = { + table_id: dataAsset.data_asset.nimbus_id, + docs: documentation, + info: { + customer: user.customer_name, + }, + }; + + await this.createDataDocs(createDataDocsPayload); + this.logger.info(`AI documentation generated and saved successfully for ${dataAssetId}, length: ${documentation.length} chars`); + + return documentation; + + } catch (error) { + this.logger.error(`Error generating AI documentation for ${dataAssetId}: ${error.message}`); + + let errorMessage = error.message; + if (error.response) { + errorMessage = `API Error ${error.response.status}: ${error.response.data?.detail || error.message}`; + } + + throw new HttpException( + `Failed to generate AI documentation: ${errorMessage}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private formatDataForAI(dataAsset: any, dataPreview: any[], columnsData?: any): string { + let combinedText = ''; + + // Seguir exatamente o padrão do Python: chamar json_to_free_text para cada objeto separadamente + + // 1. metadata_data (dataAsset) + combinedText += this.jsonToFreeText(dataAsset); + + // 2. info_data (columns) - usar columnsData se disponível, senão tentar dataAsset.columns + if (columnsData) { + // Adicionar aviso se os dados das colunas são limitados + if (columnsData.columns && columnsData.columns.some((col: any) => col.name?.startsWith('COLUMN_'))) { + combinedText += 'WARNING: Column information is limited or unavailable. Generated column names are placeholders.\n\n'; + } + combinedText += this.jsonToFreeText(columnsData); + } else if (dataAsset.columns && Array.isArray(dataAsset.columns)) { + const columnsDataFromAsset = { columns: dataAsset.columns }; + combinedText += this.jsonToFreeText(columnsDataFromAsset); + } else { + // Se não há dados das colunas, informar explicitamente + combinedText += 'WARNING: No column metadata available. Cannot generate detailed table structure.\n\n'; + } + + // 3. preview_data + if (dataPreview && dataPreview.length > 0) { + const previewData = { preview: dataPreview }; + combinedText += this.jsonToFreeText(previewData); + } + + return combinedText; + } + + private jsonToFreeText(jsonObj: any, indentLevel: number = 0): string { + if (!jsonObj || typeof jsonObj !== 'object') { + return ''; + } + + let text = ''; + const indent = ' '.repeat(indentLevel); + + // Processa primeiro os dados geográficos se existirem (igual ao Python) + const geoKeys = GEOGRAPHIC_KEYS; + + // Adiciona uma seção especial para dados de preview se existirem (igual ao Python) + if ('preview' in jsonObj) { + text += "=== PREVIEW DATA START ===\n"; + if (Array.isArray(jsonObj['preview'])) { + // Primeiro, vamos procurar por colunas geográficas + const geoColumns = []; + if (jsonObj['preview'].length > 0 && typeof jsonObj['preview'][0] === 'object') { + for (const key of Object.keys(jsonObj['preview'][0])) { + const keyLower = key.toLowerCase(); + if (geoKeys.some(geoTerm => keyLower.includes(geoTerm))) { + geoColumns.push(key); + } + } + } + + // Se encontramos colunas geográficas, vamos destacá-las + if (geoColumns.length > 0) { + text += "GEOGRAPHIC DATA FOUND IN COLUMNS:\n"; + for (const col of geoColumns) { + text += `=== Column: ${col} ===\n`; + const values = jsonObj['preview'].map(row => String(row[col] || '')).filter(v => v); + text += "Values: " + values.join(", ") + "\n\n"; + } + } + + // Agora processamos todos os dados normalmente + for (const row of jsonObj['preview']) { + text += this.jsonToFreeText(row, indentLevel + 1); + } + } else { + text += this.jsonToFreeText(jsonObj['preview'], indentLevel + 1); + } + text += "=== PREVIEW DATA END ===\n\n"; + } + + // Processa o resto dos dados (igual ao Python) + for (const [key, value] of Object.entries(jsonObj)) { + if (key === 'preview') { // Skip preview here since we handled it above + continue; + } + if (typeof value === 'object' && value !== null) { + if (Array.isArray(value)) { + text += `${indent}${this.capitalize(key)}:\n`; + for (const item of value) { + if (typeof item === 'object' && item !== null) { + text += this.jsonToFreeText(item, indentLevel + 1); + } else { + text += `${indent} - ${item}\n`; + } + } + } else { + text += `${indent}${this.capitalize(key)}:\n`; + text += this.jsonToFreeText(value, indentLevel + 1); + } + } else { + // Destaca campos geográficos + if (geoKeys.includes(key.toLowerCase() as any)) { + text += `${indent}!!! GEOGRAPHIC DATA !!! ${this.capitalize(key)}: ${value}\n`; + } else { + text += `${indent}${this.capitalize(key)}: ${value}\n`; + } + } + } + + text += '\n'; + return text; + } + + private capitalize(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1); + } + + + + private async callAutodriveForDocumentation(combinedText: string, userAccessToken?: string): Promise { + const credentials = this.getAutodriveCredentials(); + + try { + // Criar dataset temporário com os dados reais + const datasetId = await this.createTemporaryDataset(combinedText, userAccessToken); + + // Fazer pergunta ao dataset + const documentation = await this.askQuestionToDataset(datasetId, credentials); + + return documentation; + } catch (error) { + this.logger.error(`Error calling Autodrive API: ${error.message}`); + throw new Error(`Autodrive API call failed: ${error.message}`); + } + } + + private getAutodriveCredentials(): AutodriveCredentials { + const fallbackCredentials = process.env.AUTODRIVE_KEY || process.env.AUTO_DRIVE_KEY; + + // Verificar se as credenciais principais estão disponíveis + if (AUTODRIVE_CONSTANTS.USERNAME && AUTODRIVE_CONSTANTS.PASSWORD) { + return { + username: AUTODRIVE_CONSTANTS.USERNAME, + password: AUTODRIVE_CONSTANTS.PASSWORD, + baseUrl: AUTODRIVE_CONSTANTS.BASE_URL, + model: AUTODRIVE_CONSTANTS.DEFAULT_MODEL, + }; + } + + // Fallback para credenciais alternativas + if (fallbackCredentials) { + const cleanKey = fallbackCredentials.startsWith("'") && fallbackCredentials.endsWith("'") + ? fallbackCredentials.slice(1, -1) + : fallbackCredentials; + return { + username: '', + password: '', + baseUrl: AUTODRIVE_CONSTANTS.BASE_URL, + model: AUTODRIVE_CONSTANTS.DEFAULT_MODEL, + authHeader: `Basic ${cleanKey}`, + }; + } + + // Fallback de emergência removido por segurança + // Configure as variáveis de ambiente necessárias + + throw new Error('No authentication credentials available. Please configure AUTODRIVE_USERNAME and AUTODRIVE_PASSWORD in your .env file'); + } + + private async askQuestionToDataset(datasetId: string, credentials: AutodriveCredentials): Promise { + const authHeader = credentials.authHeader || this.createAuthHeader(credentials); + + const askPayload: AutodriveAskPayload = { + question: AI_DOCUMENTATION_PROMPT.trim(), + fetch_k: AI_DOCUMENTATION_CONFIG.FETCH_K, + k: AI_DOCUMENTATION_CONFIG.K, + model: credentials.model + }; + + const askHeaders = { + 'Authorization': authHeader, + ...AUTODRIVE_CONSTANTS.HEADERS + }; + + const askResponse = await axios.post( + `${credentials.baseUrl}/dataset/${datasetId}/ai_question`, + askPayload, + { + headers: askHeaders, + timeout: AUTODRIVE_CONSTANTS.ASK_TIMEOUT + } + ); + + // Verificar se a resposta contém a documentação diretamente + if (askResponse.data?.answer) { + const documentation = askResponse.data.answer; + this.logger.info(`Documentation generated successfully - Length: ${documentation.length} characters`); + return documentation; + } + + // Se não contém a resposta diretamente, verificar se tem question_id para buscar + if (askResponse.data?.question_id) { + const questionId = askResponse.data.question_id; + + // Buscar a resposta da pergunta + const answerResponse = await axios.get( + `${credentials.baseUrl}/dataset/${datasetId}/ai_question/${questionId}`, + { + headers: { 'Authorization': authHeader }, + timeout: AUTODRIVE_CONSTANTS.ANSWER_TIMEOUT + } + ); + + // Se a resposta ainda está sendo processada, fazer polling contínuo + if (answerResponse.data?.status === 'started' || !answerResponse.data?.answer) { + let attempts = 0; + const maxAttempts = 12; + const pollInterval = 10000; + + while (attempts < maxAttempts) { + attempts++; + + await new Promise(resolve => setTimeout(resolve, pollInterval)); + + try { + const pollResponse = await axios.get( + `${credentials.baseUrl}/dataset/${datasetId}/ai_question/${questionId}`, + { + headers: { 'Authorization': authHeader }, + timeout: AUTODRIVE_CONSTANTS.ANSWER_TIMEOUT + } + ); + + // Se a resposta está pronta, retornar + if (pollResponse.data?.answer && pollResponse.data?.status !== 'started') { + const documentation = pollResponse.data.answer; + this.logger.info(`Documentation generated successfully after ${attempts} attempts - Length: ${documentation.length} characters`); + return documentation; + } + + // Se ainda está processando, continuar o loop + if (pollResponse.data?.status === 'started') { + continue; + } + + // Se falhou, mostrar erro e quebrar + if (pollResponse.data?.status === 'failed') { + throw new Error(`Answer processing failed: ${pollResponse.data.status_reason || 'Unknown error'}`); + } + + // Se houve erro ou status inesperado, quebrar o loop + break; + + } catch (pollError) { + this.logger.error(`Polling attempt ${attempts} failed: ${pollError.message}`); + continue; + } + } + + throw new Error(`Answer still not ready after ${maxAttempts} polling attempts`); + } + + if (!answerResponse.data || !answerResponse.data.answer) { + throw new Error(`Answer response invalid: ${JSON.stringify(answerResponse.data)}`); + } + + const documentation = answerResponse.data.answer; + this.logger.info(`Documentation generated successfully - Length: ${documentation.length} characters`); + return documentation; + } + + throw new Error(`Autodrive API response invalid: ${JSON.stringify(askResponse.data)}`); + } + + private async createTemporaryDataset(combinedText: string, userAccessToken?: string): Promise { + if (!AUTODRIVE_CONSTANTS.BASE_URL) { + throw new Error('AUTODRIVE_BASE_URL not configured'); + } + + if (!combinedText || combinedText.trim().length === 0) { + throw new Error('combinedText is empty - cannot create dataset'); + } + + const credentials = this.getAutodriveCredentials(); + const authHeader = credentials.authHeader || this.createAuthHeader(credentials); + const tempFilePath = await this.createTempFile(combinedText); + + try { + const uploadResponse = await this.uploadDataset(tempFilePath, authHeader); + const datasetId = uploadResponse.dataset_id; + + await this.waitForDatasetReady(datasetId, authHeader, credentials.baseUrl); + return datasetId; + } catch (error) { + this.logger.error(`Error creating temporary dataset: ${error.message}`); + throw error; + } finally { + await this.cleanupTempFile(tempFilePath); + } + } + + private async createTempFile(combinedText: string): Promise { + const tempFileName = `temp_data_${Date.now()}.txt`; + const tempFilePath = `/tmp/${tempFileName}`; + + const fs = await import('fs'); + fs.writeFileSync(tempFilePath, combinedText, 'utf8'); + + return tempFilePath; + } + + private async uploadDataset(tempFilePath: string, authHeader: string): Promise { + const tempFileName = tempFilePath.split('/').pop() || 'temp_data.txt'; + const fs = await import('fs'); + + const formData = new FormData(); + formData.append('files', fs.createReadStream(tempFilePath), tempFileName); + formData.append('name', tempFileName); + + const uploadResponse = await axios.post( + `${AUTODRIVE_CONSTANTS.BASE_URL}/upload`, + formData, + { + headers: { + 'Authorization': authHeader, + ...formData.getHeaders(), + }, + timeout: 30000, // 30 segundos + } + ); + + if (!uploadResponse.data?.dataset_id) { + throw new Error('No dataset_id in upload response'); + } + + return uploadResponse.data; + } + + private async waitForDatasetReady(datasetId: string, authHeader: string, autodriveBaseUrl: string): Promise { + const maxAttempts = 12; + const pollInterval = 10000; // 10 segundos + + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + const statusResponse = await axios.get( + `${autodriveBaseUrl}/dataset/${datasetId}`, + { + headers: { 'Authorization': authHeader }, + timeout: 30000, + } + ); + + if (statusResponse.data?.status === 'success') { + return; + } + + if (statusResponse.data?.status === 'failed') { + throw new Error(`Dataset processing failed: ${statusResponse.data.status_reason || 'Unknown error'}`); + } + + if (statusResponse.data?.status === 'processing') { + if (attempt < maxAttempts) { + await new Promise(resolve => setTimeout(resolve, pollInterval)); + continue; + } + } + + if (attempt === maxAttempts) { + throw new Error(`Dataset still not ready after ${maxAttempts} attempts`); + } + + } catch (statusError) { + this.logger.error(`Status check error on attempt ${attempt}: ${statusError.message}`); + if (attempt === maxAttempts) { + throw statusError; + } + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + } + } + + private async cleanupTempFile(tempFilePath: string | null): Promise { + if (!tempFilePath) return; + + try { + const fs = await import('fs'); + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } catch (error) { + this.logger.warn(`Failed to remove temporary file: ${error.message}`); + } + } + + private createAuthHeader(credentials: AutodriveCredentials): string { + return `Basic ${Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64')}`; + } + } export { CatalogService }; \ No newline at end of file diff --git a/src/modules/catalog/constants/autodrive.constants.ts b/src/modules/catalog/constants/autodrive.constants.ts new file mode 100644 index 0000000..6088215 --- /dev/null +++ b/src/modules/catalog/constants/autodrive.constants.ts @@ -0,0 +1,42 @@ +/** + * Constantes relacionadas ao Autodrive + * + */ +export const AUTODRIVE_CONSTANTS = { + // URLs e endpoints (apenas do ENV) + BASE_URL: process.env.BASE_URL_AUTODRIVE || process.env.AUTODRIVE_BASE_URL, + + // Credenciais (apenas do ENV, sem fallback para segurança) + USERNAME: process.env.AUTODRIVE_USERNAME, + PASSWORD: process.env.AUTODRIVE_PASSWORD, + + // Modelo padrão + DEFAULT_MODEL: process.env.AUTODRIVE_MODEL || "gpt-4o", + + // Timeouts (em milissegundos) + ASK_TIMEOUT: 120000, + ANSWER_TIMEOUT: 180000, + + // Headers + HEADERS: { + 'Content-Type': 'application/json', + }, +} as const; + +/** + * Chaves geográficas para detecção de dados de localização + */ +export const GEOGRAPHIC_KEYS = [ + 'country', 'countries', 'city', 'cities', + 'region', 'regions', 'location', 'state', + 'states', 'address' +] as const; + +/** + * Países comuns para detecção automática + */ +export const COMMON_COUNTRIES = [ + 'brazil', 'brasil', 'usa', 'united states', + 'canada', 'mexico', 'argentina', 'chile', + 'colombia' +] as const; diff --git a/src/modules/catalog/prompts/ai-documentation.prompt.ts b/src/modules/catalog/prompts/ai-documentation.prompt.ts new file mode 100644 index 0000000..a48c247 --- /dev/null +++ b/src/modules/catalog/prompts/ai-documentation.prompt.ts @@ -0,0 +1,88 @@ +/** + + */ +export const AI_DOCUMENTATION_PROMPT = `crie uma documentação em Portugues, Ingles e Espanhol seguindo essas instruções +1. Persona: como profissional de governança e engenharia de dados +2. Tarefa: ao receber as informações da tabela criar uma documentação com o seguinte escopo +**A primeira linha do documento tem que conter a seguinte informação: ## Document languages: EN / BR / ES +**A segunda linha tem que obrigatoriamente conter a escrita Table: nome da tabela +**A terceira linha tem que obrigatoriamente conter a escrita Table Schema: nome do table schema +**DIRETRIZ CRUCIAL DE CONSISTÊNCIA E COMPLETUDE DE SCHEMA:** +**1. Fonte Exclusiva de Metadados:** O 'Table Schema' definido na linha acima é a ÚNICA fonte de verdade para o schema dos dados a serem documentados. TODAS as informações subsequentes, especialmente na seção 'Estrutura da Tabela' (incluindo a lista de colunas, seus nomes, tipos de dados, descrições e exemplos) DEVEM ser extraídas EXCLUSIVAMENTE de metadados que correspondem a ESTE 'Table Schema'. Se os dados de entrada que você recebeu contiverem informações para a mesma tabela ou colunas mas de schemas diferentes (ex: um schema 'bronze' e um 'silver'), você DEVE IGNORAR TOTALMENTE as informações dos schemas divergentes para esta tarefa de documentação e utilizar APENAS as do 'Table Schema' aqui especificado. +**2. Listagem Completa de Colunas:** Sua principal tarefa na seção 'Estrutura da Tabela' é identificar e listar TODAS as colunas que pertencem ao 'Table Schema' especificado. Verifique nos dados de entrada fornecidos se há uma indicação explícita do número total de colunas para esta tabela neste schema (por exemplo, um campo como 'Num_columns' ou similar nos metadados da tabela). Você deve se esforçar para listar exatamente essa quantidade de colunas. Se essa contagem não estiver disponível, liste todas as colunas que você puder identificar como pertencentes exclusivamente a este 'Table Schema'. A completude em relação ao schema especificado é essencial. + +**Depois de "Estrutura da tablea", incluir a mensagem "Este documento foi gerado por IA", traduzida corretamente para cada idioma.** +**Obrigatoriamente:Após finalizar a versão em Inglês, começar a versão em Português** **Após finalizar a versão em Português, começar a versão em Espanhol** **Antes de começar cada versão, colocar um título como:** - \`## English Version\` (para inglês) + - \`## Versão em Português\` (para português) + - \`## Versión en Español\` (para espanhol) + - Descrição: fornece uma visão geral do ativo de dados, + destacando seu propósito e principal funcionalidade. + Esta sessão resume o conteúdo e o objetivo do ativo, ajudando os usuários a entender rapidamente o que o ativo representa + e como pode ser utilizado em suas análises e decisões. + - Sugestão de Domínio de Dados: + Analise cuidadosamente os dados da tabela e sugira o domínio mais apropriado. Inclua: + - Domínio Sugerido: [Nome do domínio] + - Motivo: [Explicação breve sobre porque a tabela pertence a este domínio] + - Observações: [Qualquer observação adicional relevante] + + Exemplos de Domínios de Dados para referência: + - Financeiro: Dados sobre transações, receitas, despesas, etc. + - Recursos Humanos: Dados sobre funcionários, cargos, salários, etc. + - Produtos: Dados sobre produtos, categorias, preços, etc. + - Fornecedores: Dados sobre fornecedores, produtos fornecidos, localizações, etc. + - Marketing: Dados sobre campanhas, leads, conversões, etc. + - Vendas: Dados sobre vendas, clientes, produtos vendidos, etc. + - Operações: Dados sobre processos, logística, produção, etc. + - Clientes: Dados sobre clientes, interações, histórico, etc. + -Tags Sugeridas: + A IA deve gerar tags relevantes **com base nos dados da tabela**. + - **IMPORTANTE: Analise cuidadosamente os dados de preview da tabela (PREVIEW DATA) para encontrar países. Procure em todas as colunas por nomes de países, cidades ou regiões.** + - **Garanta que as tags estejam separadas por espaços vazios, todas na mesma linha, exemplo: #marketing #sales #australia #canada, limitar até 3 países que mais aparecem** - **Os países DEVEM ser extraídos dos dados de preview da tabela. Procure em colunas como City, Country, Region, Location, etc.** - Por que esta tabela é interessante: + Nesta sessão, é destacada a importância do ativo, explicando como ele pode ser útil para os usuários. + São abordadas as formas como o ativo pode melhorar a tomada de decisões, identificar padrões relevantes ou fornecer insights valiosos. + O objetivo é ressaltar a utilidade prática e o impacto positivo que o ativo pode ter em suas atividades. + - Análises potencialmente úteis feitas com esses dados: + Aqui são listadas algumas das análises que podem ser realizadas com o ativo de dados. Inclui sugestões de dashboards, + relatórios ou outros tipos de análises que aproveitam as informações fornecidas pelo ativo. + O objetivo é oferecer maneiras de utilizar os dados para obter insights valiosos e apoiar a tomada de decisões informadas. + - Links Úteis: + Os Links Úteis oferecem recursos adicionais relacionados ao ativo de dados, incluindo guias, + artigos ou outras fontes de informação que podem ajudar os usuários a compreender melhor o ativo e suas aplicações. Além disso, + inclui um link rápido dentro da Dadosfera para ativos relacionados diretamente com o ativo em questão, facilitando a navegação entre os ativos. + - Estrutura da Tabela: + A Estrutura da Tabela detalha TODAS as colunas e os dados disponíveis no ativo, conforme pertencentes ao 'Table Schema' principal definido no início deste documento. + **Instrução Detalhada para Estrutura da Tabela:** + Siga rigorosamente estes passos: + 1. Identifique nos dados de entrada (metadados da tabela e das colunas) todas as colunas que pertencem EXCLUSIVAMENTE ao 'Table Schema' especificado no cabeçalho deste documento. Se houver uma contagem de colunas (ex: 'Num_columns') para este schema específico, assegure-se de listar essa quantidade. + 2. Para CADA uma dessas colunas identificadas, formate a saída da seguinte maneira, **SEM utilizar NENHUM marcador de lista (como traços ou asteriscos) no início de cada entrada de coluna**. Cada coluna deve ser apresentada como um bloco de texto. Inclua uma linha em branco entre a documentação de cada coluna para separação visual. + - Apresente o NOME_DA_COLUNA em maiúsculas, seguido pelo (TIPO_DE_DADO_EXTRAÍDO_DOS_METADADOS_DO_SCHEMA_CORRETO) entre parênteses. + - O **NOME_DA_COLUNA (TIPO_DE_DADO_EXTRAÍDO_DOS_METADADOS_DO_SCHEMA_CORRETO)** deve estar na primeira linha do bloco da coluna e **inteiramente em negrito**. + - Na linha seguinte, a etiqueta "**Descrição:**" deve estar **em negrito**, seguida pelo texto da descrição da coluna. + - Na linha seguinte à descrição, a etiqueta "**Exemplo:**" deve estar **em negrito**, seguida pelo valor do exemplo. Se o exemplo for um valor literal ou código, formate-o entre crases (\`) se apropriado. + - Se houver informações adicionais relevantes (como "Valores Possíveis:", "Observações:", etc.), coloque a etiqueta correspondente **em negrito** em uma nova linha, seguida pelo seu texto. + + Este documento foi gerado por IA. + + NOME_COLUNA_1 (TIPO_DADO_SCHEMA_CORRETO_1): + Descrição: [Descrição da coluna 1, do schema correto] + Exemplo: \`[Exemplo de valor para coluna 1, do schema correto]\` + + NOME_COLUNA_2 (TIPO_DADO_SCHEMA_CORRETO_2): + Descrição: [Descrição da coluna 2, do schema correto] + Exemplo: \`[Exemplo de valor para coluna 2, do schema correto]\` + + (continue este formato com início de cada coluna para TODAS as colunas do 'Table Schema' especificado, garanta com que NUNCA tenha TRAÇO OU PONTO no inicio) + + 3. Contexto : O usuário ira cadastrar um ativo de dados na nossa plataforma e para ter um bom catalogo ele ira querer gerar a documentação padronizada mas explicativa e + automática + 4. Restrições : A documentação deve seguir obrigatoriamente o mesmo padrão principalmente na parte de estrutura de dados + 5. Objetivo: O principal objetivo é gerar uma documentação acessível, clara, + automática e padronizada para os usuários que desejem cadastrar um ativo de dados na plataforma`; + +/** + * Configurações para a geração de documentação com IA + */ +export const AI_DOCUMENTATION_CONFIG = { + FETCH_K: 250, + K: 100, +} as const; diff --git a/src/modules/catalog/types/ai-documentation.types.ts b/src/modules/catalog/types/ai-documentation.types.ts new file mode 100644 index 0000000..f778f26 --- /dev/null +++ b/src/modules/catalog/types/ai-documentation.types.ts @@ -0,0 +1,60 @@ +/** + * Tipos relacionados à geração de documentação com IA + */ + +export interface AutodriveCredentials { + username: string; + password: string; + baseUrl: string; + model: string; + authHeader?: string; +} + +export interface AutodriveAskPayload { + question: string; + fetch_k: number; + k: number; + model: string; +} + +export interface AutodriveAskResponse { + answer?: string; + question_id?: string; + dataset_id?: string; +} + +export interface AutodriveAnswerResponse { + status: 'started' | 'success' | 'failed'; + answer?: string; + status_reason?: string; +} + +export interface AutodriveUploadResponse { + dataset_id: string; +} + +export interface DatasetStatusResponse { + status: 'processing' | 'success' | 'failed'; + status_reason?: string; +} + +export interface ColumnData { + name: string; + type: string; + description?: string; + nullable?: string; +} + +export interface ColumnsMetadata { + columns: ColumnData[]; +} + +export interface DataPreview { + preview: any[]; +} + +export interface FormattedDataForAI { + dataAsset: any; + dataPreview: any[]; + columnsData?: ColumnsMetadata; +}