Files
v3-api/src/ai-service/ai-service.service.ts

535 lines
17 KiB
TypeScript

import { HttpStatus, Injectable } from '@nestjs/common';
import axios from 'axios';
import { BaseResponseDTO } from 'src/common/dto/base-response.dto';
import * as FormData from 'form-data';
import * as fs from 'fs';
import * as path from 'path';
@Injectable()
export class AiServiceService {
/** Headers for every AI HTTP call (`X-API-Key` from `AI_API_KEY`). */
private aiHeaders(extra: Record<string, string> = {}): Record<string, string> {
const headers: Record<string, string> = {
accept: 'application/json',
...extra,
};
const key = process.env.AI_API_KEY?.trim();
if (key) {
headers['X-API-Key'] = key;
}
return headers;
}
async ask(body, session) {
try {
if (!session) {
const data = JSON.stringify({
user_input: body.user_input ?? body.question,
chat_history_raw: body.chat_history_raw ?? [],
user_insurance_data: body.user_insurance_data,
user_installments_data: body.user_installments_data,
});
let axiosConfig = {
method: 'post',
maxBodyLength: Infinity,
url: process.env.AI_QUERY_URL,
headers: this.aiHeaders({ 'Content-Type': 'application/json' }),
data: data,
};
const chat = await axios.request(axiosConfig);
const aiResponse = chat.data.response;
return aiResponse;
} else {
let transformedData = body;
let axiosConfig = {
method: 'post',
maxBodyLength: Infinity,
url: process.env.AI_QUERY_URL,
headers: this.aiHeaders({ 'Content-Type': 'application/json' }),
data: transformedData,
};
const newChat = await axios.request(axiosConfig);
if (!newChat.data && newChat.data.response) throw new Error('AI_ERROR');
const aiResponse = newChat.data.response;
return aiResponse;
}
} catch (err) {
console.log(err);
if (err.isAxiosError) {
const status = err.response?.status || HttpStatus.INTERNAL_SERVER_ERROR;
const message = err.response?.data || 'Internal Server Error';
return new BaseResponseDTO(status, 'ai_service_error', {
error: message,
});
}
return new BaseResponseDTO(
HttpStatus.BAD_REQUEST,
'something_wrong',
null,
);
}
}
async wrapUp(data) {
try {
let axiosConfig = {
method: 'post',
maxBodyLength: Infinity,
url: process.env.AI_WRAPUP_URL,
headers: this.aiHeaders({ 'Content-Type': 'application/json' }),
data: data,
};
const wrapup = await axios.request(axiosConfig);
const aiResponse = wrapup.data.title;
return aiResponse;
} catch (err) {
console.log(err);
throw new BaseResponseDTO(err.status, err.response, null);
}
}
async aiUploadTxt(file) {
try {
console.log(`[aiUploadTxt] Attempting to upload file: ${file.originalName}`);
const formData = new FormData();
const fileStream = fs.createReadStream(file.filePath);
formData.append('file', fileStream, {
filename: file.originalName,
contentType: file.mimetype
});
// Upload the file
const uploadResponse = await axios.post(process.env.AI_UPLOAD_URL, formData, {
headers: this.aiHeaders(formData.getHeaders() as Record<string, string>),
timeout: 30000
});
console.log(`[aiUploadTxt] File upload response for ${file.originalName}:`, uploadResponse.data);
// Check if upload was successful
if (uploadResponse.status === 200) {
console.log(`[aiUploadTxt] File ${file.originalName} uploaded successfully. Initiating QA generation.`);
const qaResponse = await axios.post(
`${process.env.AI_QA_GENERATOR_URL}?file_name=${encodeURIComponent(file.originalName)}`,
{},
{
headers: this.aiHeaders(),
timeout: 120000 // 2 minutes timeout for QA generation
}
);
console.log('QA Generation response:', qaResponse.data);
// Consider any HTTP 200 as success regardless of body shape
if (qaResponse.status === 200) {
return {
success: true,
fileName: file.originalName,
};
}
throw new Error('QA generation failed');
} else {
throw new Error('File upload failed');
}
} catch (err) {
console.error('Error uploading file to AI service:', err);
// Handle different types of errors
let status = 500;
let errorData = null;
if (err.response) {
// The request was made and the server responded with a status code
status = err.response.status;
errorData = err.response.data;
} else if (err.request) {
// The request was made but no response was received
errorData = 'No response received from AI service';
} else {
// Something happened in setting up the request
errorData = err.message;
}
throw new BaseResponseDTO(status, errorData, null);
}
}
async aiUploadCsv(file) {
try {
console.log(`[aiUploadCsv] Attempting to upload file: ${file.originalName}`);
const formData = new FormData();
formData.append('file', fs.createReadStream(file.filePath), {
filename: file.originalName,
contentType: file.mimetype,
});
const axiosConfig = {
method: 'post',
url: process.env.AI_UPLOAD_URL,
headers: this.aiHeaders(formData.getHeaders() as Record<string, string>),
data: formData,
};
const response = await axios.request(axiosConfig);
console.log(`[aiUploadCsv] File upload successful for ${file.originalName}. Response:`, response.data);
return response.data;
} catch (err) {
console.error('[aiUploadCsv] Error uploading file to AI service:', err);
// Handle different types of errors
let status = 500;
let errorData = null;
if (err.response) {
// The request was made and the server responded with a status code
status = err.response.status;
errorData = err.response.data;
} else if (err.request) {
// The request was made but no response was received
errorData = 'No response received from AI service';
} else {
// Something happened in setting up the request
errorData = err.message;
}
throw new BaseResponseDTO(status, errorData, null);
}
}
async newCollection(collection_name: string, filename: string, description: string) {
try {
console.log(`[newCollection] Attempting to create new collection: ${collection_name} with filename: ${filename} and description: ${description}`);
const params = new URLSearchParams();
params.append('collection_name', collection_name);
params.append('filename', filename);
params.append('description', description);
const axiosConfig = {
method: 'post',
url: `${process.env.AI_COLLECTION_URL}/new-collection?${params.toString()}`,
headers: this.aiHeaders(),
};
const response = await axios.request(axiosConfig);
console.log(`[newCollection] Collection ${collection_name} created successfully. Response:`, response.data);
return response.data;
} catch (err) {
console.error('[newCollection] Error creating new collection:', err);
// Handle different types of errors
let status = 500;
let errorData = null;
if (err.response) {
// The request was made and the server responded with a status code
status = err.response.status;
errorData = err.response.data;
} else if (err.request) {
// The request was made but no response was received
errorData = 'No response received from AI service';
} else {
// Something happened in setting up the request
errorData = err.message;
}
throw new BaseResponseDTO(status, errorData, null);
}
}
async getCollections() {
try {
console.log(`[getCollections] Attempting to retrieve all collections.`);
const axiosConfig = {
method: 'get',
url: `${process.env.AI_COLLECTION_URL}/get-collections`,
headers: this.aiHeaders(),
};
const response = await axios.request(axiosConfig);
console.log(`[getCollections] Collections retrieved successfully. Response:`, response.data);
return response.data;
} catch (err) {
console.error('[getCollections] Error getting collections:', err);
// Handle different types of errors
let status = 500;
let errorData = null;
if (err.response) {
// The request was made and the server responded with a status code
status = err.response.status;
errorData = err.response.data;
} else if (err.request) {
// The request was made but no response was received
errorData = 'No response received from AI service';
} else {
// Something happened in setting up the request
errorData = err.message;
}
throw new BaseResponseDTO(status, errorData, null);
}
}
async updateCollection(collection_name: string, filename: string, description: string) {
try {
console.log(`[updateCollection] Attempting to update collection: ${collection_name} with filename: ${filename} and description: ${description}`);
const params = new URLSearchParams();
params.append('collection_name', collection_name);
params.append('filename', filename);
params.append('description', description);
const axiosConfig = {
method: 'post',
url: `${process.env.AI_COLLECTION_URL}/update-collection?${params.toString()}`,
headers: this.aiHeaders(),
};
const response = await axios.request(axiosConfig);
console.log(`[updateCollection] Collection ${collection_name} updated successfully. Response:`, response.data);
return response.data;
} catch (err) {
console.error('[updateCollection] Error updating collection:', err);
// Handle different types of errors
let status = 500;
let errorData = null;
if (err.response) {
// The request was made and the server responded with a status code
status = err.response.status;
errorData = err.response.data;
} else if (err.request) {
// The request was made but no response was received
errorData = 'No response received from AI service';
} else {
// Something happened in setting up the request
errorData = err.message;
}
throw new BaseResponseDTO(status, errorData, null);
}
}
async replaceCollection(collection_name: string, filename: string, description?: string) {
try {
console.log(`[replaceCollection] Attempting to replace collection: ${collection_name} with filename: ${filename}${description ? ` and description: ${description}` : ''}`);
const params = new URLSearchParams();
params.append('collection_name', collection_name);
params.append('filename', filename);
if (description) {
params.append('description', description);
}
const axiosConfig = {
method: 'post',
url: `${process.env.AI_COLLECTION_URL}/replace-collection?${params.toString()}`,
headers: this.aiHeaders(),
};
const response = await axios.request(axiosConfig);
console.log(`[replaceCollection] Collection ${collection_name} replaced successfully. Response:`, response.data);
return response.data;
} catch (err) {
console.error('Error replacing collection:', err);
// Handle different types of errors
let status = 500;
let errorData = null;
if (err.response) {
// The request was made and the server responded with a status code
status = err.response.status;
errorData = err.response.data;
} else if (err.request) {
// The request was made but no response was received
errorData = 'No response received from AI service';
} else {
// Something happened in setting up the request
errorData = err.message;
}
throw new BaseResponseDTO(status, errorData, null);
}
}
async deactivateCollection(collection_name: string) {
try {
console.log(`[deactivateCollection] Attempting to deactivate collection: ${collection_name}`);
const params = new URLSearchParams();
params.append('collection_name', collection_name);
const axiosConfig = {
method: 'post',
url: `${process.env.AI_COLLECTION_URL}/deactivate-collection?${params.toString()}`,
headers: this.aiHeaders(),
};
const response = await axios.request(axiosConfig);
console.log(`[deactivateCollection] Collection ${collection_name} deactivated successfully. Response:`, response.data);
return response.data;
} catch (err) {
console.error('[deactivateCollection] Error deactivating collection:', err);
// Handle different types of errors
let status = 500;
let errorData = null;
if (err.response) {
// The request was made and the server responded with a status code
status = err.response.status;
errorData = err.response.data;
} else if (err.request) {
// The request was made but no response was received
errorData = 'No response received from AI service';
} else {
// Something happened in setting up the request
errorData = err.message;
}
throw new BaseResponseDTO(status, errorData, null);
}
}
private collectionBaseUrl(): string {
return String(process.env.AI_COLLECTION_URL || '').replace(/\/$/, '');
}
private throwAiError(err: any): never {
let status = 500;
let errorData: unknown = null;
if (err?.response) {
status = err.response.status;
errorData = err.response.data;
} else if (err?.request) {
errorData = 'No response received from AI service';
} else {
errorData = err?.message ?? err;
}
throw new BaseResponseDTO(status, errorData as any, null);
}
async getCollectionsWithDescriptions() {
try {
const response = await axios.get(
`${this.collectionBaseUrl()}/get-collections-with-descriptions`,
{ headers: this.aiHeaders(), timeout: 60_000 },
);
return response.data;
} catch (err) {
this.throwAiError(err);
}
}
async getCollectionDescription(collectionName: string) {
try {
const response = await axios.get(
`${this.collectionBaseUrl()}/get-collection-description/${encodeURIComponent(collectionName)}`,
{ headers: this.aiHeaders(), timeout: 60_000 },
);
return response.data;
} catch (err) {
this.throwAiError(err);
}
}
async checkCollections() {
try {
const response = await axios.get(
`${this.collectionBaseUrl()}/check-collections`,
{ headers: this.aiHeaders(), timeout: 60_000 },
);
return response.data;
} catch (err) {
this.throwAiError(err);
}
}
async exportCollectionPage(
collectionName: string,
page = 1,
pageSize = 50,
) {
try {
const response = await axios.get(
`${this.collectionBaseUrl()}/sync/export/${encodeURIComponent(collectionName)}`,
{
params: { page, page_size: pageSize },
headers: this.aiHeaders(),
timeout: 120_000,
},
);
return response.data as {
collection: string;
page: number;
page_size: number;
total_items: number;
total_pages: number;
items: Array<{ id: string; q: string; a: string }>;
};
} catch (err) {
this.throwAiError(err);
}
}
async createExportItem(
collectionName: string,
body: { q: string; a: string },
) {
try {
const response = await axios.post(
`${this.collectionBaseUrl()}/sync/export/${encodeURIComponent(collectionName)}`,
body,
{
headers: this.aiHeaders({ 'Content-Type': 'application/json' }),
timeout: 60_000,
},
);
return response.data;
} catch (err) {
this.throwAiError(err);
}
}
async updateExportItem(
collectionName: string,
itemId: string,
body: { q: string; a: string },
) {
try {
const response = await axios.put(
`${this.collectionBaseUrl()}/sync/export/${encodeURIComponent(collectionName)}/${encodeURIComponent(itemId)}`,
body,
{
headers: this.aiHeaders({ 'Content-Type': 'application/json' }),
timeout: 60_000,
},
);
return response.data;
} catch (err) {
this.throwAiError(err);
}
}
async deleteExportItem(collectionName: string, itemId: string) {
try {
const response = await axios.delete(
`${this.collectionBaseUrl()}/sync/export/${encodeURIComponent(collectionName)}/${encodeURIComponent(itemId)}`,
{
headers: this.aiHeaders(),
timeout: 60_000,
},
);
return response.data;
} catch (err) {
this.throwAiError(err);
}
}
}