File size: 7,689 Bytes
78549c1
 
 
 
 
 
 
2e5073c
 
 
 
 
 
 
78549c1
 
 
 
 
 
 
 
1660a60
2e5073c
 
 
78549c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e5073c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78549c1
2e5073c
 
 
 
 
78549c1
 
 
 
 
 
 
2e5073c
 
 
 
 
 
 
 
 
 
 
 
 
78549c1
 
 
 
 
 
2e5073c
 
 
 
 
 
 
 
 
 
 
 
 
78549c1
2e5073c
 
78549c1
 
 
 
 
 
 
 
 
 
 
2e5073c
78549c1
2e5073c
 
 
 
 
78549c1
 
 
 
2e5073c
 
78549c1
2e5073c
78549c1
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import os
import gradio as gr
from groq import Groq
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
import PyPDF2
import re
from collections import Counter
from sklearn.metrics.pairwise import cosine_similarity
import logging

# Setup logging
logging.basicConfig(filename='query_logs.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')

# Grog API key (Use environment variable or replace it with your actual API key)
grog_api_key = "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"

# Initialize groq API client
client = Groq(api_key=grog_api_key)

# Path to the already uploaded book
book_path = 'Generative_AI_Foundations_in_Python_Discover_key_techniques_and.pdf'

# Cache system to store previous responses
cache = {}

# Check if the file exists
if os.path.exists(book_path):
    print(f"Book found at: {book_path}")
else:
    print("Book not found!")

# Function to read the PDF file
def read_pdf(file_path):
    with open(file_path, 'rb') as file:
        reader = PyPDF2.PdfReader(file)
        number_of_pages = len(reader.pages)
        text = ""
        for page_num in range(number_of_pages):
            page = reader.pages[page_num]
            text += page.extract_text()
        return text

# Read the PDF content
book_text = read_pdf(book_path)
print(book_text[:1000])  # Print first 1000 characters of the book for verification

# Vectorization of the extracted PDF content
def vectorize_text(text):
    try:
        # Use Sentence Transformer to create embeddings
        model = SentenceTransformer('all-MiniLM-L6-v2')
        sentences = text.split('\n')  # Split text into sentences for vectorization
        embeddings = model.encode(sentences, show_progress_bar=True)

        # Create FAISS index for similarity search
        index = faiss.IndexFlatL2(embeddings.shape[1])  # L2 distance index
        index.add(np.array(embeddings))  # Add embeddings to the index
        print(f"Added {len(sentences)} sentences to the vector store.")

        return index, sentences
    except Exception as e:
        print(f"Error during vectorization: {str(e)}")
        return None, None

# Vectorize the extracted PDF text
vector_index, sentences = vectorize_text(book_text)

# Check if the vectorization was successful
if vector_index:
    print("Vectorization complete.")
else:
    print("Vectorization failed.")

# Function to generate embeddings for the query using the SentenceTransformer
def generate_query_embedding(query, sentence_transformer_model):
    return sentence_transformer_model.encode([query])

# Function to check relevancy and handle out-of-bounds queries
def check_relevancy(D, threshold=0.4):
    if D[0][0] > threshold:
        return False
    return True

# Function to generate diverse responses from the LLM with varied parameters
def generate_diverse_responses(client, prompt, n=3):
    responses = []
    for i in range(n):
        temperature = 0.5 + (i * 0.2)  # Vary temperature from 0.5 to 0.9
        top_p = 0.9 - (i * 0.2)        # Vary top-p from 0.9 to 0.7
        try:
            chat_completion = client.chat.completions.create(
                messages=[{
                    "role": "user",
                    "content": prompt,
                }],
                model="llama3-8b-8192",
                temperature=temperature,
                top_p=top_p
            )
            responses.append(chat_completion.choices[0].message.content)
        except Exception as e:
            logging.error(f"Error generating response: {str(e)}")
            responses.append("Sorry, an error occurred while generating this response.")
    return responses

# Function to aggregate responses based on similarity and voting mechanism
def aggregate_responses(responses):
    # Use a simple voting mechanism to select the most common response
    response_counter = Counter(responses)
    most_common_response = response_counter.most_common(1)[0][0]
    
    # Rank responses by semantic similarity to the first response
    model = SentenceTransformer('all-MiniLM-L6-v2')
    embeddings = model.encode(responses)
    first_embedding = embeddings[0].reshape(1, -1)
    
    similarities = cosine_similarity(first_embedding, embeddings)[0]
    top_response_index = np.argmax(similarities)

    # Return the most similar response to the first response
    return responses[top_response_index]

# Function to generate answers using the groq API with Llama model
def generate_answer_with_grog(query, vector_index, sentences, sentence_transformer_model):
    # Check cache for previous queries
    if query in cache:
        logging.info(f"Cache hit for query: {query}")
        return cache[query]

    try:
        # Get the query embedding using the sentence transformer
        query_embedding = generate_query_embedding(query, sentence_transformer_model)

        # Perform similarity search on the vector store (vector index)
        D, I = vector_index.search(np.array(query_embedding), k=5)  # Find top 5 similar sentences

        # If no relevant content found, generate a fallback response
        if len(I[0]) == 0 or D[0][0] > 1.0:
            fallback_response = f"I couldn't find anything relevant in the document, but here's a general answer to your query: {query}"
            chat_completion = client.chat.completions.create(
                messages=[{
                    "role": "user",
                    "content": fallback_response,
                }],
                model="llama3-8b-8192",
            )
            cache[query] = chat_completion.choices[0].message.content
            return cache[query]

        # Retrieve the most relevant sentences
        relevant_sentences = [sentences[i] for i in I[0]]

        # Combine the relevant sentences for the final query
        combined_text = " ".join(relevant_sentences)

        # Create a prompt with the relevant content
        final_prompt = f"**Relevant Information:**\n\n '{combined_text}'\n\n **Answer:** {query}"

        # Generate diverse responses using the groq API
        responses = generate_diverse_responses(client, final_prompt)

        # Aggregate the responses to ensure stability and variety
        final_response = aggregate_responses(responses)

        # Cache the response for future queries
        cache[query] = final_response
        return final_response

    except Exception as e:
        logging.error(f"Error during answer generation with groq API: {str(e)}")
        return f"Error during answer generation: {str(e)}"

# Gradio app function
def gradio_interface(query):
    global vector_index, sentences

    # Initialize the sentence transformer model
    sentence_transformer_model = SentenceTransformer('all-MiniLM-L6-v2')

    if vector_index is None or sentences is None:
        return "Vector index or sentences not initialized properly."

    # Generate the answer using the groq API and Llama model with varied responses
    answer = generate_answer_with_grog(query, vector_index, sentences, sentence_transformer_model)
    
    # Log the query and answer for monitoring
    logging.info(f"Query: {query}, Answer: {answer}")
    
    return f"### Here's your response:\n\n{answer}"

# Create the Gradio interface
iface = gr.Interface(
    fn=gradio_interface,
    inputs=gr.Textbox(label="Enter your query"),
    outputs="markdown",  # Use markdown output for better formatting
    title="Generative_AI_Foundations_in_Python PDF-based Query Answering",
    description="Ask any question about the content in the uploaded PDF and receive diverse, reliable answers."
)

# Launch the Gradio app
if __name__ == "__main__":
    iface.launch()