forked from Chatbot/v3-api
first commit v3 initialiazed a temporary repository for darmanet client
This commit is contained in:
534
src/ai-service/ai-service.service.ts
Normal file
534
src/ai-service/ai-service.service.ts
Normal file
@@ -0,0 +1,534 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user