Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/helper/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export function generateSearchQuery(condition: {
q?: string;
}): object {
const { botId, q } = condition;
console.log('🚀 ~ condition:', condition);
const query: Record<string, any> = {};

if (q !== undefined && q !== '') {
Expand Down
1 change: 0 additions & 1 deletion src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ export class AuthService {
*/
async forgotPassword(email: string): Promise<{ message: string }> {
const user = await this.userRepo.findUser({ email });
console.log('🚀 ~ AuthService ~ forgotPassword ~ user:', user);

if (!user)
throw new HttpException(
Expand Down
2 changes: 0 additions & 2 deletions src/modules/conversation/conversation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ export class ConversationService {
conversation.botId?._id.toString(),
conversation._id ?? id,
);
console.log('🚀 ~ ConversationService ~ botMessage:', botMessage);

const botMessageUpdate =
await this.conversationRepository.addMessageToConversation(
Expand Down Expand Up @@ -228,7 +227,6 @@ export class ConversationService {
botId: botId,
limit: 3,
});
console.log('🚀 231~ ConversationService ~ botId:', botId);

if (searchResults.data.length === 0) {
const botDetails = await this.botRepo.findBotById(botId);
Expand Down
7 changes: 7 additions & 0 deletions src/modules/gemini/gemini.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ export class GeminiController {
const ans = await this.geminiService.enhanceAnswer(question, answer);
return ans;
}
@Get('/similar')
async similarQuestions(
@Query('question') question: string,
): Promise<{ questions: string[] }> {
const ans = await this.geminiService.generateSimilarQuestions(question);
return ans;
}
}
61 changes: 39 additions & 22 deletions src/modules/gemini/gemini.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { Injectable } from '@nestjs/common';
import { GoogleGenerativeAI } from '@google/generative-ai';
import * as dotenv from 'dotenv';
import { questionGeneratorPrompt, textGeneratorPrompt } from 'src/prompts';
import { extractJsonFromText } from 'src/utils';

dotenv.config();

Expand All @@ -10,6 +12,7 @@ export class GeminiService {
private genAI: GoogleGenerativeAI;
private embeddingModel: any;
private textModel: any;
private questionGeneratorModel: any;

constructor() {
// Initialize Google Generative AI with your API key
Expand All @@ -22,31 +25,16 @@ export class GeminiService {
model: 'text-embedding-004',
});

// Initialize the question generation model
this.questionGeneratorModel = this.genAI.getGenerativeModel({
model: 'gemini-2.0-flash',
systemInstruction: questionGeneratorPrompt,
});

// Initialize the text generation model
this.textModel = this.genAI.getGenerativeModel({
model: 'gemini-2.0-flash',
systemInstruction: `
You are an intelligent assistant for a website FAQ. Your task is to rewrite formal or technical answers into clear, friendly, and helpful responses that sound like they're from a knowledgeable human.

Rules to follow:
1. Stick to the facts — avoid adding guesses or unnecessary details.
2. Use a natural, friendly, and helpful tone that feels human, not robotic.
3. Keep the response concise yet informative.
4. Use simple, everyday language — avoid jargon unless it's essential for understanding.
5. Ensure all key information from the original answer is preserved.
6. Structure the response logically, making it easy to follow.
7. If the question is unclear, provide the most relevant and practical answer without over-explaining.
8. IMPORTANT: Format your entire response using proper markdown. Use markdown formatting for:
- Headings (## for main headings, ### for subheadings)
- **Bold text** for emphasis
- *Italic text* for secondary emphasis
- Bullet points and numbered lists
- Code blocks with backticks when showing technical content
- Tables when presenting structured information
- > Blockquotes for highlighting important information

**Output only the improved answer with proper markdown formatting — no introductions, explanations, or formatting comments.**
`,
systemInstruction: textGeneratorPrompt,
});
}

Expand Down Expand Up @@ -113,6 +101,35 @@ Format your response using proper markdown including headings, bold/italic text,
throw new Error('Failed to generate embeddings');
}
}
/**
* Generates similar questions from a given question
* @param question - The question to generate similar questions from
* @returns Array of similar questions
*
* Note: This is a simple implementation for generating similar questions. In a real-world scenario, you might want to use a more sophisticated method like semantic search or transformer models.
*/

async generateSimilarQuestions(
question: string,
): Promise<{ questions: string[] }> {
try {
const result = await this.questionGeneratorModel.generateContent({
contents: [{ role: 'user', parts: [{ text: question }] }],
generationConfig: {
temperature: 0.2,
topK: 40,
topP: 0.95,
maxOutputTokens: 150,
},
});

const response = result.response;
return extractJsonFromText(response.text());
} catch (error) {
console.error('Error generating similar questions:', error);
throw new Error('Failed to generate similar questions');
}
}

/**
* Comprehensive method to process a FAQ item - enhances the answer and generates embeddings
Expand Down
4 changes: 1 addition & 3 deletions src/modules/qna/qna.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export class QnARepository {
try {
const { skip, limit } = pagination;
const { botId } = query;
console.log('🚀 ~ QnARepository ~ botId:', botId);

let sqlQuery =
'SELECT id, question, answer, "botId", "createdAt", "updatedAt" FROM question_n_answers';
Expand Down Expand Up @@ -67,7 +66,7 @@ export class QnARepository {

if (botId) {
sqlQuery += ' WHERE "botId" = $1';
queryParams.push(botId);
queryParams.push(botId?.toString());
}

const result = await client.query(sqlQuery, queryParams);
Expand All @@ -78,7 +77,6 @@ export class QnARepository {
}

async findVectorById(id: string) {
console.log('🚀 ~ QnARepository ~ findVectorById ~ id:', id);
const client = await this.dbService.getClient();
try {
const result = await client.query(
Expand Down
35 changes: 34 additions & 1 deletion src/modules/qna/qna.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,40 @@ export class QnAService {
embedding,
);

if (!createdQna) {
throw new Error(QnaErrorMessages.COULD_NOT_CREATE_QNA);
}
const { questions: similarQuestions = [] } =
(await this.geminiService.generateSimilarQuestions(question)) ?? {};
console.log(
'🚀 ~ QnAService ~ create ~ similarQuestions:',
similarQuestions,
);

if (similarQuestions?.length > 0) {
const insertPromises = similarQuestions?.map(
async (similarQuestion) => {
try {
const similarEmbedding =
await this.geminiService.generateEmbeddings(similarQuestion);
return this.qnaRepo.insertVector(
similarQuestion,
answer,
botId,
similarEmbedding,
);
} catch (error) {
console.error(
`Error processing similar question "${similarQuestion}":`,
error,
);
}
},
);

await Promise.all(insertPromises);
}

return this.apiResponse.success(createdQna);
} catch (error) {
throw new HttpException(
Expand All @@ -64,7 +98,6 @@ export class QnAService {

async findAll(condition: { q: string }, pagination: PaginationQueryDto) {
const query = generateSearchQuery(condition);
console.log('🚀 ~ QnAService ~ findAll ~ query:', query);

// Paginate the list of users based on the generated query, role IDs query, and pagination settings
const { data, page, limit, total } = await this.paginationService.paginate(
Expand Down
1 change: 0 additions & 1 deletion src/modules/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ export class UserService {
},
pagination,
);
console.log('🚀 ~ UserService ~ total:', total);

const users: UserInterface[] = [];
for (const user of data as UserInterface[]) {
Expand Down
28 changes: 28 additions & 0 deletions src/prompts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const textGeneratorPrompt = `You are an intelligent assistant for a website FAQ. Your task is to rewrite formal or technical answers into clear, friendly, and helpful responses that sound like they're from a knowledgeable human.

Rules to follow:
1. Stick to the facts — avoid adding guesses or unnecessary details.
2. Use a natural, friendly, and helpful tone that feels human, not robotic.
3. Keep the response concise yet informative.
4. Use simple, everyday language — avoid jargon unless it's essential for understanding.
5. Ensure all key information from the original answer is preserved.
6. Structure the response logically, making it easy to follow.
7. If the question is unclear, provide the most relevant and practical answer without over-explaining.
8. IMPORTANT: Format your entire response using proper markdown. Use markdown formatting for:
- Headings (## for main headings, ### for subheadings)
- **Bold text** for emphasis
- *Italic text* for secondary emphasis
- Bullet points and numbered lists
- Code blocks with backticks when showing technical content
- Tables when presenting structured information
- > Blockquotes for highlighting important information

**Output only the improved answer with proper markdown formatting — no introductions, explanations, or formatting comments.**`;

export const questionGeneratorPrompt = `You are an intelligent assistant for a generating similar question from given a question. and make sure you return this format
questions:[
"put similar question of the given questions here?",
]
make sure, you are strictly bound to return the output in the above format. I mean maintain this json format.
only and only in json format but don't add code format '''json like that only return the json.
`;
45 changes: 45 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,48 @@ export function toObjectId(id: string): mongoose.Types.ObjectId {
}
return new mongoose.Types.ObjectId(id);
}
/**
* Extracts a JSON object from a string containing JSON.
* @param {string} text - The text containing JSON.
* @returns {object|null} - The extracted JSON object or null if extraction fails.
*/
export function extractJsonFromText(text: string) {
try {
// Look for patterns that might be JSON objects
const jsonRegex = /{[\s\S]*?}/g;
const matches = text.match(jsonRegex);

if (!matches || matches.length === 0) {
return null;
}

// Try to parse each match until we find valid JSON
for (const match of matches) {
try {
const parsedJson = JSON.parse(match);
// Check if the parsed object has the expected structure
if (
parsedJson &&
parsedJson.questions &&
Array.isArray(parsedJson.questions)
) {
return parsedJson;
}
} catch (innerError) {
// Skip invalid JSON matches
continue;
}
}

// If we haven't returned yet, try to parse the largest match
// (which is likely to be the complete JSON object)
const largestMatch = matches.reduce(
(a, b) => (a.length > b.length ? a : b),
'',
);
return JSON.parse(largestMatch);
} catch (error) {
console.error('Error extracting JSON:', error);
return null;
}
}