File size: 12,880 Bytes
d37f8bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
from langchain.document_loaders.pdf import PyPDFDirectoryLoader
from dotenv import load_dotenv
from langchain_text_splitters import RecursiveCharacterTextSplitter

from transformers import AutoModel, AutoTokenizer
# from pinecone import Pinecone, ServerlessSpec
import pinecone
from pinecone import (
    Pinecone,
    ServerlessSpec,
    CloudProvider,
    AwsRegion,
    VectorType
)

import os
import requests

import numpy as np
import gradio as gr

import glob
import pandas as pd
import numpy as np
# Important: Import pinecone-client properly
# Load environment variables from .env file
load_dotenv()

DATA_PATH = os.getenv("DATA_PATH")
PINECONE_API = os.getenv("PINECONE_API")
PINECONE_ENV = os.getenv("PINECONE_ENV")
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
print("PINECONE_API", PINECONE_API)


# Groq API settings
GROQ_EMBED_URL = "https://api.groq.com/openai/v1/embeddings"
GROQ_CHAT_URL = "https://api.groq.com/openai/v1/chat/completions"
EMBEDDING_MODEL = "llama3-405b-8192-embed"
LLM_MODEL = "llama3-70b-8192"


# Configure headers for Groq API requests
GROQ_HEADERS = {
    "Authorization": f"Bearer {GROQ_API_KEY}",
    "Content-Type": "application/json"
}



# # Init Pinecone


pc = Pinecone(api_key=PINECONE_API)
print(PINECONE_API)


#  --------------- initialize pinecone -----------------------------
# pc.create_index_for_model(
#     name="test-index",
#     cloud="aws",
#     region="us-east-1",
#     embed={
#         "model":"llama-text-embed-v2",
#         "field_map":{"text": "page_content"}
#     }
# )



# Connect to the index
index = pc.Index("ai-coach")
#index = pc.Index("ahsan-400pg-pdf-doc-test")


embedding_model = AutoModel.from_pretrained(
    'jinaai/jina-embeddings-v2-base-en', trust_remote_code=True)
# user_query = "user query"
# Function to generate embeddings without tokenization


def get_embedding(data):
    embeddings = embedding_model.encode(data).tolist()
    return embeddings



# Since your application is designed to answer a wide range of student queries and suggest relevant material, you want to retrieve enough content to cover different facets of a topic without overwhelming the LLM with too much information.
# 
# # Starting Point:
# - A common starting point is to set top_k between **5 and 10.**
# - **top_k=5:** This can work well if your curated content is highly relevant and precise, ensuring that the top 5 matches are very close to the query.
# -  **top_k=10:** If you want the coach to consider a broader range of content—perhaps to provide diverse perspectives or cover a topic more comprehensively—increasing top_k to around 10 might be beneficial.
# 
# # Experiment and Adjust:
# - The “best” value depends on factors such as the diversity of your content, how densely your data covers the topics, and the quality of the embedding matches. It’s a good idea to experiment with different top_k values and evaluate the quality and relevance of the responses in your specific
# 

# # Query Pinecone
# 


# Function to query Pinecone index using embeddings
def query_pinecone(embedding):
    # Use keyword arguments to pass the embedding and other parameters
    result = index.query(vector=embedding, top_k=20, include_metadata=True)
    return result['matches']


# # Query Groq Inference


# Function to query Groq LLM
def query_groq(prompt: str) -> str:
    response = requests.post(
        GROQ_CHAT_URL,
        headers=GROQ_HEADERS,
        json={
            "model": LLM_MODEL,
            "messages": [{"role": "user", "content": prompt}],
            "temperature": 0.5,
            "max_tokens": 8192  # max from groq website
        }
    )

    if response.status_code != 200:
        raise Exception(f"Error querying Groq: {response.text}")

    return response.json()["choices"][0]["message"]["content"]


# Tokenizer to count number of tokens
tokenizer = AutoTokenizer.from_pretrained("jinaai/jina-embeddings-v2-base-en")


def count_tokens(text: str) -> int:
    # Encode the text into tokens
    tokens = tokenizer.encode(text)
    return len(tokens)


# # Process User Query

# # Gradio GUI TEST


# system_message = f"""
#     You are a knowledgeable and friendly coach. Your goal is to help students understand concepts in a detailed and easy-to-understand manner. 
#     Be patient, ask guiding questions, and provide step-by-step explanations where needed. Adapt your responses to the student's knowledge level 
#     and help them build confidence in their learning. Refer relevant material to the student and encourage them to explore further.

#     Based on the context and the student's question, provide a thoughtful and detailed explanation. Encourage them to think about the topic and 
#     offer further guidance if needed.
#     """

# def gradio_interface(prompt,history =[]):
#     output = process_user_query(prompt,history)
#     history.append((prompt,output))
#     return history

# gr.Interface(fn=gradio_interface, inputs= ['text',"state"], outputs=["chatbot","state"]).launch(debug=True,share=True)


# ------------------------------------------- WORKING 1 -------------------------------------------

# # Function to be used by Gradio for handling the query
# def gradio_process(user_query):
#     response = process_user_query(user_query, conversation_history)
#     return response

# # Create Gradio interface
# interface = gr.Interface(fn=gradio_process, inputs="text", outputs="text", title="RAG-based Coaching System")

# # Launch Gradio app
# interface.launch()
# ------------------------------------------- WORKING 2 -------------------------------------------

# Initialize empty conversation history (list of tuples)
# conversation_history = []

# def process_user_query(user_query: str, conversation_history: list):
#     print(f"User Query Tokens: {count_tokens(user_query)}")

#     # Generate embedding and get relevant context
#     embedding = get_embedding(user_query)
#     relevant_chunks = query_pinecone(embedding)
#     context = "\n".join(chunk['metadata']["text"] for chunk in relevant_chunks)
#     print("CONTEXT:", context)

#     # Format conversation history for the prompt
#     history_str = "\n".join(
#         f"User: {user}\nCoach: {response}" 
#         for user, response in conversation_history
#     )

#     # Create structured prompt
#     prompt = f"""You are a knowledgeable and friendly coach. Follow these guidelines:
#     1. Provide clear, step-by-step explanations
#     2. Ask guiding questions to encourage critical thinking
#     3. Adapt to the student's knowledge level
#     4. Use examples from the provided context when relevant

#     Context from learning materials:
#     {context}

#     Conversation history:
#     {history_str}

#     New student question:
#     "{user_query}"

#     Provide a helpful response:"""

#     # Get LLM response
#     groq_response = query_groq(prompt)
#     print(f"Response Tokens: {count_tokens(groq_response)}")

#     # Return updated history with new interaction
#     return conversation_history + [(user_query, groq_response)]

# # Gradio Interface
# with gr.Blocks() as interface:
#     gr.Markdown("# 🧑‍🏫 AI Coaching Assistant")
#     gr.Markdown("Welcome! I'm here to help you learn. Type your question below.")

#     # State management
#     chat_history = gr.State(conversation_history)

#     with gr.Row():
#         chatbot = gr.Chatbot(height=500)
#         with gr.Column(scale=0.5):
#             context_display = gr.Textbox(label="Relevant Context", interactive=False)

#     user_input = gr.Textbox(label="Your Question", placeholder="Type here...")

#     with gr.Row():
#         submit_btn = gr.Button("Submit", variant="primary")
#         undo_btn = gr.Button("Undo Last")
#         clear_btn = gr.Button("Clear History")

#     def handle_submit(user_input, history):
#         if not user_input.strip():
#             return gr.update(), history, ""

#         # Process query and update history
#         new_history = process_user_query(user_input, history)

#         # Get latest context for display
#         latest_context = "\n".join([chunk['metadata']["text"] for chunk in query_pinecone(
#             get_embedding(user_input)
#         )][:3])  # Show top 3 context snippets

#         return "", new_history, latest_context

#     # Component interactions
#     submit_btn.click(
#         handle_submit,
#         [user_input, chat_history],
#         [user_input, chat_history, context_display]
#     ).then(
#         lambda x: x,
#         [chat_history],
#         [chatbot]
#     )

#     undo_btn.click(
#         lambda history: history[:-1] if history else [],
#         [chat_history],
#         [chat_history]
#     ).then(
#         lambda x: x,
#         [chat_history],
#         [chatbot]
#     )

#     clear_btn.click(
#         lambda: [],
#         None,
#         [chat_history]
#     ).then(
#         lambda: ([], ""),
#         None,
#         [chatbot, context_display]
#     )

# interface.launch(share=True)
# Just change the launch command to:
#interface.launch(share=True, auth=("username", "password"))  # Add basic auth


# self hosting

# # Run with:
# interface.launch(
#     server_name="0.0.0.0",
#     server_port=7860,
#     show_error=True
# )


# ------------------------------------------- WORKING 3 Enter key submits user query -------------------------------------------
# Initialize empty conversation history (list of tuples)
conversation_history = []

def process_user_query(user_query: str, conversation_history: list):
    print(f"User Query Tokens: {count_tokens(user_query)}")

    # Generate embedding and get relevant context
    embedding = get_embedding(user_query)
    relevant_chunks = query_pinecone(embedding)
    context = "\n".join(chunk['metadata']["text"] for chunk in relevant_chunks)
    print("CONTEXT:", context)

    # Format conversation history for the prompt
    history_str = "\n".join(
        f"User: {user}\nCoach: {response}" 
        for user, response in conversation_history
    )

    # Create structured prompt
    prompt = f"""You are a knowledgeable and friendly coach. Follow these guidelines:
    1. Provide clear, step-by-step explanations
    2. Ask guiding questions to encourage critical thinking
    3. Adapt to the student's knowledge level
    4. Use examples from the provided context when relevant

    Context from learning materials:
    {context}

    Conversation history:
    {history_str}

    New student question:
    "{user_query}"

    Provide a helpful response:"""

    # Get LLM response
    groq_response = query_groq(prompt)
    print(f"Response Tokens: {count_tokens(groq_response)}")

    # Return updated history with new interaction
    return conversation_history + [(user_query, groq_response)]

# Gradio Interface
with gr.Blocks() as interface:
    gr.Markdown("# 🧑‍🏫 AI Coaching Assistant")
    gr.Markdown("Welcome! I'm here to help you learn. Type your question below.")

    # State management
    chat_history = gr.State(conversation_history)

    with gr.Row():
        chatbot = gr.Chatbot(height=500)
        with gr.Column(scale=0.5):
            context_display = gr.Textbox(label="Relevant Context", interactive=False)

    user_input = gr.Textbox(label="Your Question", placeholder="Type here...")

    with gr.Row():
        submit_btn = gr.Button("Submit", variant="primary")
        undo_btn = gr.Button("Undo Last")
        clear_btn = gr.Button("Clear History")

    def handle_submit(user_input, history):
        if not user_input.strip():
            return gr.update(), history, ""

        # Process query and update history
        new_history = process_user_query(user_input, history)

        # Get latest context for display
        latest_context = "\n".join([chunk['metadata']["text"] for chunk in query_pinecone(
            get_embedding(user_input)
        )][:3])  # Show top 3 context snippets

        return "", new_history, latest_context

    # Component interactions
    submit_btn.click(
        handle_submit,
        [user_input, chat_history],
        [user_input, chat_history, context_display]
    ).then(
        lambda x: x,
        [chat_history],
        [chatbot]
    )

    # Add submit on Enter key press
    user_input.submit(
        handle_submit,
        [user_input, chat_history],
        [user_input, chat_history, context_display]
    ).then(
        lambda x: x,
        [chat_history],
        [chatbot]
    )

    undo_btn.click(
        lambda history: history[:-1] if history else [],
        [chat_history],
        [chat_history]
    ).then(
        lambda x: x,
        [chat_history],
        [chatbot]
    )

    clear_btn.click(
        lambda: [],
        None,
        [chat_history]
    ).then(
        lambda: ([], ""),
        None,
        [chatbot, context_display]
    )

interface.launch(share=True)