CosmickVisions commited on
Commit
095ee1e
·
verified ·
1 Parent(s): c05f975

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +397 -23
app.py CHANGED
@@ -11,10 +11,14 @@ import fitz # PyMuPDF
11
  import base64
12
  from PIL import Image
13
  import io
 
 
 
 
14
 
15
  # Load environment variables
16
  load_dotenv()
17
- client = groq.Client(api_key=os.getenv("GROQ_API_KEY"))
18
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
19
 
20
  # Directory to store FAISS indexes
@@ -28,31 +32,47 @@ user_vectorstores = {}
28
  # Custom CSS for Tech theme
29
  custom_css = """
30
  :root {
31
- --primary-color: #008080; /* Teal */
32
- --secondary-color: #006666; /* Dark Teal */
33
- --light-background: #E0FFFF; /* Light Cyan */
34
- --dark-text: #333333;
35
  --white: #FFFFFF;
36
- --border-color: #E5E7EB;
 
 
 
 
37
  }
38
- body { background-color: var(--light-background); font-family: 'Inter', sans-serif; }
39
  .container { max-width: 1200px !important; margin: 0 auto !important; padding: 10px; }
40
- .header { background-color: var(--white); border-bottom: 2px solid var(--border-color); padding: 15px 0; margin-bottom: 20px; border-radius: 12px 12px 0 0; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
41
- .header-title { color: var(--secondary-color); font-size: 1.8rem; font-weight: 700; text-align: center; }
42
  .header-subtitle { color: var(--dark-text); font-size: 1rem; text-align: center; margin-top: 5px; }
43
- .chat-container { border-radius: 12px !important; box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important; background-color: var(--white) !important; border: 1px solid var(--border-color) !important; min-height: 500px; }
44
  .message-user { background-color: var(--primary-color) !important; color: var(--white) !important; border-radius: 18px 18px 4px 18px !important; padding: 12px 16px !important; margin-left: auto !important; max-width: 80% !important; }
45
- .message-bot { background-color: #F0F0F0 !important; color: var(--dark-text) !important; border-radius: 18px 18px 18px 4px !important; padding: 12px 16px !important; margin-right: auto !important; max-width: 80% !important; }
46
  .input-area { background-color: var(--white) !important; border-top: 1px solid var(--border-color) !important; padding: 12px !important; border-radius: 0 0 12px 12px !important; }
47
- .input-box { border: 1px solid var(--border-color) !important; border-radius: 24px !important; padding: 12px 16px !important; box-shadow: 0 2px 4px rgba(0,0,0,0.05) !important; }
48
- .send-btn { background-color: var(--secondary-color) !important; border-radius: 24px !important; color: var(--white) !important; padding: 10px 20px !important; font-weight: 500 !important; }
49
- .clear-btn { background-color: #F0F0F0 !important; border: 1px solid var(--border-color) !important; border-radius: 24px !important; color: var(--dark-text) !important; padding: 8px 16px !important; font-weight: 500 !important; }
50
- .pdf-viewer-container { border-radius: 12px !important; box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important; background-color: var(--white) !important; border: 1px solid var(--border-color) !important; padding: 20px; }
51
- .pdf-viewer-image { max-width: 100%; height: auto; border: 1px solid var(--border-color); border-radius: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
52
- .stats-box { background-color: #E0F0F0; padding: 10px; border-radius: 8px; margin-top: 10px; }
 
 
 
 
 
 
 
 
 
 
 
 
53
  """
54
 
55
- # Function to process PDF files (unchanged)
56
  def process_pdf(pdf_file):
57
  if pdf_file is None:
58
  return None, "No file uploaded", {"page_images": [], "total_pages": 0, "total_words": 0}
@@ -100,7 +120,54 @@ def generate_response(message, session_id, model_name, history):
100
  docs = vectorstore.similarity_search(message, k=3)
101
  if docs:
102
  context = "\n\nRelevant information from uploaded PDF:\n" + "\n".join(f"- {doc.page_content}" for doc in docs)
103
- system_prompt = "You are a technical assistant specializing in analyzing tech manuals, whitepapers, and documentation."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  if context:
105
  system_prompt += " Use the following context to answer the question if relevant: " + context
106
  completion = client.chat.completions.create(
@@ -119,7 +186,7 @@ def generate_response(message, session_id, model_name, history):
119
  history.append((message, f"Error generating response: {str(e)}"))
120
  return history
121
 
122
- # Functions to update PDF viewer (unchanged)
123
  def update_pdf_viewer(pdf_state):
124
  if not pdf_state["total_pages"]:
125
  return 0, None, "No PDF uploaded yet"
@@ -142,6 +209,230 @@ def update_image(page_num, pdf_state):
142
  print(f"Error decoding image: {e}")
143
  return None
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  # Gradio interface
146
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
147
  current_session_id = gr.State(None)
@@ -162,6 +453,54 @@ with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
162
  value="llama3-70b-8192",
163
  label="Select Groq Model"
164
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  with gr.Column(scale=2, min_width=600):
166
  with gr.Tabs():
167
  with gr.TabItem("PDF Viewer"):
@@ -169,16 +508,25 @@ with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
169
  page_slider = gr.Slider(minimum=1, maximum=1, step=1, label="Page Number", value=1)
170
  pdf_image = gr.Image(label="PDF Page", type="pil", elem_classes="pdf-viewer-image")
171
  stats_display = gr.Markdown("No PDF uploaded yet", elem_classes="stats-box")
 
 
 
 
 
 
 
 
 
172
 
173
  with gr.Row(elem_classes="container"):
174
  with gr.Column(scale=2, min_width=600):
175
  chatbot = gr.Chatbot(height=500, bubble_full_width=False, show_copy_button=True, elem_classes="chat-container")
176
  with gr.Row():
177
- msg = gr.Textbox(show_label=False, placeholder="Ask about your technical document...", scale=5)
178
  send_btn = gr.Button("Send", scale=1)
179
  clear_btn = gr.Button("Clear Conversation")
180
 
181
- # Event Handlers (unchanged)
182
  upload_button.click(
183
  process_pdf,
184
  inputs=[pdf_file],
@@ -212,7 +560,33 @@ with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
212
  inputs=[page_slider, pdf_state],
213
  outputs=[pdf_image]
214
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
  # Launch the app
217
  if __name__ == "__main__":
218
- demo.launch()
 
11
  import base64
12
  from PIL import Image
13
  import io
14
+ import requests
15
+ import json
16
+ import re
17
+ from datetime import datetime, timedelta
18
 
19
  # Load environment variables
20
  load_dotenv()
21
+ client = groq.Client(api_key=os.getenv("GROQ_TECH_API_KEY"))
22
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
23
 
24
  # Directory to store FAISS indexes
 
32
  # Custom CSS for Tech theme
33
  custom_css = """
34
  :root {
35
+ --primary-color: #4285F4; /* Google Blue */
36
+ --secondary-color: #34A853; /* Google Green */
37
+ --light-background: #F8F9FA;
38
+ --dark-text: #202124;
39
  --white: #FFFFFF;
40
+ --border-color: #DADCE0;
41
+ --code-bg: #F1F3F4;
42
+ --code-text: #37474F;
43
+ --error-color: #EA4335; /* Google Red */
44
+ --warning-color: #FBBC04; /* Google Yellow */
45
  }
46
+ body { background-color: var(--light-background); font-family: 'Google Sans', 'Roboto', sans-serif; }
47
  .container { max-width: 1200px !important; margin: 0 auto !important; padding: 10px; }
48
+ .header { background-color: var(--white); border-bottom: 1px solid var(--border-color); padding: 15px 0; margin-bottom: 20px; border-radius: 12px 12px 0 0; box-shadow: 0 1px 2px rgba(0,0,0,0.05); }
49
+ .header-title { color: var(--primary-color); font-size: 1.8rem; font-weight: 700; text-align: center; }
50
  .header-subtitle { color: var(--dark-text); font-size: 1rem; text-align: center; margin-top: 5px; }
51
+ .chat-container { border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important; background-color: var(--white) !important; border: 1px solid var(--border-color) !important; min-height: 500px; }
52
  .message-user { background-color: var(--primary-color) !important; color: var(--white) !important; border-radius: 18px 18px 4px 18px !important; padding: 12px 16px !important; margin-left: auto !important; max-width: 80% !important; }
53
+ .message-bot { background-color: #F1F3F4 !important; color: var(--dark-text) !important; border-radius: 18px 18px 18px 4px !important; padding: 12px 16px !important; margin-right: auto !important; max-width: 80% !important; }
54
  .input-area { background-color: var(--white) !important; border-top: 1px solid var(--border-color) !important; padding: 12px !important; border-radius: 0 0 12px 12px !important; }
55
+ .input-box { border: 1px solid var(--border-color) !important; border-radius: 24px !important; padding: 12px 16px !important; box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important; }
56
+ .send-btn { background-color: var(--primary-color) !important; border-radius: 24px !important; color: var(--white) !important; padding: 10px 20px !important; font-weight: 500 !important; }
57
+ .clear-btn { background-color: #F1F3F4 !important; border: 1px solid var(--border-color) !important; border-radius: 24px !important; color: var(--dark-text) !important; padding: 8px 16px !important; font-weight: 500 !important; }
58
+ .pdf-viewer-container { border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important; background-color: var(--white) !important; border: 1px solid var(--border-color) !important; padding: 20px; }
59
+ .pdf-viewer-image { max-width: 100%; height: auto; border: 1px solid var(--border-color); border-radius: 8px; box-shadow: 0 1px 2px rgba(0,0,0,0.05); }
60
+ .stats-box { background-color: #E8F0FE; padding: 10px; border-radius: 8px; margin-top: 10px; }
61
+ .tool-container { background-color: var(--white); border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); padding: 15px; margin-bottom: 20px; border: 1px solid var(--border-color); }
62
+ .code-block { background-color: var(--code-bg); color: var(--code-text); padding: 12px; border-radius: 8px; font-family: 'Roboto Mono', monospace; overflow-x: auto; margin: 10px 0; border-left: 3px solid var(--primary-color); }
63
+ .repo-card { border: 1px solid var(--border-color); padding: 15px; margin: 10px 0; border-radius: 8px; background-color: var(--white); }
64
+ .repo-name { color: var(--primary-color); font-weight: bold; font-size: 1.1rem; margin-bottom: 5px; }
65
+ .repo-description { color: var(--dark-text); font-size: 0.9rem; margin-bottom: 10px; }
66
+ .repo-stats { display: flex; gap: 15px; color: #5F6368; font-size: 0.85rem; }
67
+ .repo-stat { display: flex; align-items: center; gap: 5px; }
68
+ .qa-card { border-left: 3px solid var(--secondary-color); padding: 10px 15px; margin: 15px 0; background-color: #F8F9FA; border-radius: 0 8px 8px 0; }
69
+ .qa-title { font-weight: bold; color: var(--dark-text); margin-bottom: 5px; }
70
+ .qa-body { color: var(--dark-text); font-size: 0.95rem; margin-bottom: 10px; }
71
+ .qa-meta { display: flex; justify-content: space-between; color: #5F6368; font-size: 0.85rem; }
72
+ .tag { background-color: #E8F0FE; color: var(--primary-color); padding: 4px 8px; border-radius: 4px; font-size: 0.8rem; margin-right: 5px; display: inline-block; }
73
  """
74
 
75
+ # Function to process PDF files
76
  def process_pdf(pdf_file):
77
  if pdf_file is None:
78
  return None, "No file uploaded", {"page_images": [], "total_pages": 0, "total_words": 0}
 
120
  docs = vectorstore.similarity_search(message, k=3)
121
  if docs:
122
  context = "\n\nRelevant information from uploaded PDF:\n" + "\n".join(f"- {doc.page_content}" for doc in docs)
123
+
124
+ # Check if it's a GitHub repo search
125
+ if re.match(r'^/github\s+.+', message, re.IGNORECASE):
126
+ query = re.sub(r'^/github\s+', '', message, flags=re.IGNORECASE)
127
+ repo_results = search_github_repos(query)
128
+ if repo_results:
129
+ response = "**GitHub Repository Search Results:**\n\n"
130
+ for repo in repo_results[:3]: # Limit to top 3 results
131
+ response += f"**[{repo['name']}]({repo['html_url']})**\n"
132
+ if repo['description']:
133
+ response += f"{repo['description']}\n"
134
+ response += f"⭐ {repo['stargazers_count']} | 🍴 {repo['forks_count']} | Language: {repo['language'] or 'Not specified'}\n"
135
+ response += f"Updated: {repo['updated_at'][:10]}\n\n"
136
+ history.append((message, response))
137
+ return history
138
+ else:
139
+ history.append((message, "No GitHub repositories found for your query."))
140
+ return history
141
+
142
+ # Check if it's a Stack Overflow search
143
+ if re.match(r'^/stack\s+.+', message, re.IGNORECASE):
144
+ query = re.sub(r'^/stack\s+', '', message, flags=re.IGNORECASE)
145
+ qa_results = search_stackoverflow(query)
146
+ if qa_results:
147
+ response = "**Stack Overflow Search Results:**\n\n"
148
+ for qa in qa_results[:3]: # Limit to top 3 results
149
+ response += f"**[{qa['title']}]({qa['link']})**\n"
150
+ response += f"Score: {qa['score']} | Answers: {qa['answer_count']}\n"
151
+ if 'tags' in qa and qa['tags']:
152
+ response += f"Tags: {', '.join(qa['tags'][:5])}\n"
153
+ response += f"Asked: {qa['creation_date']}\n\n"
154
+ history.append((message, response))
155
+ return history
156
+ else:
157
+ history.append((message, "No Stack Overflow questions found for your query."))
158
+ return history
159
+
160
+ # Check if it's a code explanation request
161
+ code_match = re.search(r'/explain\s+```(?:.+?)?\n(.+?)```', message, re.DOTALL)
162
+ if code_match:
163
+ code = code_match.group(1).strip()
164
+ explanation = explain_code(code)
165
+ history.append((message, explanation))
166
+ return history
167
+
168
+ system_prompt = "You are a technical assistant specializing in software development, programming, and IT topics."
169
+ system_prompt += " Format code snippets with proper markdown code blocks with language specified."
170
+ system_prompt += " For technical explanations, be precise and include examples where helpful."
171
  if context:
172
  system_prompt += " Use the following context to answer the question if relevant: " + context
173
  completion = client.chat.completions.create(
 
186
  history.append((message, f"Error generating response: {str(e)}"))
187
  return history
188
 
189
+ # Functions to update PDF viewer
190
  def update_pdf_viewer(pdf_state):
191
  if not pdf_state["total_pages"]:
192
  return 0, None, "No PDF uploaded yet"
 
209
  print(f"Error decoding image: {e}")
210
  return None
211
 
212
+ # GitHub API integration
213
+ def search_github_repos(query, sort="stars", order="desc", per_page=10):
214
+ """Search for GitHub repositories"""
215
+ try:
216
+ github_token = os.getenv("GITHUB_TOKEN", "")
217
+ headers = {}
218
+ if github_token:
219
+ headers["Authorization"] = f"token {github_token}"
220
+
221
+ params = {
222
+ "q": query,
223
+ "sort": sort,
224
+ "order": order,
225
+ "per_page": per_page
226
+ }
227
+
228
+ response = requests.get(
229
+ "https://api.github.com/search/repositories",
230
+ headers=headers,
231
+ params=params
232
+ )
233
+
234
+ if response.status_code != 200:
235
+ print(f"GitHub API Error: {response.status_code} - {response.text}")
236
+ return []
237
+
238
+ data = response.json()
239
+ return data.get("items", [])
240
+ except Exception as e:
241
+ print(f"Error in GitHub search: {e}")
242
+ return []
243
+
244
+ # Stack Overflow API integration
245
+ def search_stackoverflow(query, sort="votes", site="stackoverflow", pagesize=10):
246
+ """Search for questions on Stack Overflow"""
247
+ try:
248
+ params = {
249
+ "order": "desc",
250
+ "sort": sort,
251
+ "site": site,
252
+ "pagesize": pagesize,
253
+ "intitle": query
254
+ }
255
+
256
+ response = requests.get(
257
+ "https://api.stackexchange.com/2.3/search/advanced",
258
+ params=params
259
+ )
260
+
261
+ if response.status_code != 200:
262
+ print(f"Stack Exchange API Error: {response.status_code} - {response.text}")
263
+ return []
264
+
265
+ data = response.json()
266
+
267
+ # Process results to convert Unix timestamps to readable dates
268
+ for item in data.get("items", []):
269
+ if "creation_date" in item:
270
+ item["creation_date"] = datetime.fromtimestamp(item["creation_date"]).strftime("%Y-%m-%d")
271
+
272
+ return data.get("items", [])
273
+ except Exception as e:
274
+ print(f"Error in Stack Overflow search: {e}")
275
+ return []
276
+
277
+ def get_stackoverflow_answers(question_id, site="stackoverflow"):
278
+ """Get answers for a specific question on Stack Overflow"""
279
+ try:
280
+ params = {
281
+ "order": "desc",
282
+ "sort": "votes",
283
+ "site": site,
284
+ "filter": "withbody" # Include the answer body in the response
285
+ }
286
+
287
+ response = requests.get(
288
+ f"https://api.stackexchange.com/2.3/questions/{question_id}/answers",
289
+ params=params
290
+ )
291
+
292
+ if response.status_code != 200:
293
+ print(f"Stack Exchange API Error: {response.status_code} - {response.text}")
294
+ return []
295
+
296
+ data = response.json()
297
+
298
+ # Process results
299
+ for item in data.get("items", []):
300
+ if "creation_date" in item:
301
+ item["creation_date"] = datetime.fromtimestamp(item["creation_date"]).strftime("%Y-%m-%d")
302
+
303
+ return data.get("items", [])
304
+ except Exception as e:
305
+ print(f"Error getting Stack Overflow answers: {e}")
306
+ return []
307
+
308
+ def explain_code(code):
309
+ """Explain code using LLM"""
310
+ try:
311
+ system_prompt = "You are an expert programmer and code reviewer. Your task is to explain the provided code in a clear, concise manner. Include:"
312
+ system_prompt += "\n1. What the code does (high-level overview)"
313
+ system_prompt += "\n2. Key functions/components and their purposes"
314
+ system_prompt += "\n3. Potential issues or optimization opportunities"
315
+ system_prompt += "\n4. Any best practices that are followed or violated"
316
+
317
+ completion = client.chat.completions.create(
318
+ model="llama3-70b-8192", # Using more capable model for code explanation
319
+ messages=[
320
+ {"role": "system", "content": system_prompt},
321
+ {"role": "user", "content": f"Explain this code:\n```\n{code}\n```"}
322
+ ],
323
+ temperature=0.3,
324
+ max_tokens=1024
325
+ )
326
+
327
+ explanation = completion.choices[0].message.content
328
+ return f"**Code Explanation:**\n\n{explanation}"
329
+ except Exception as e:
330
+ return f"Error explaining code: {str(e)}"
331
+
332
+ def perform_repo_search(query, language, sort_by, min_stars):
333
+ """Perform GitHub repository search with UI parameters"""
334
+ try:
335
+ if not query:
336
+ return "Please enter a search query"
337
+
338
+ # Build the search query with filters
339
+ search_query = query
340
+ if language and language != "any":
341
+ search_query += f" language:{language}"
342
+ if min_stars and min_stars != "0":
343
+ search_query += f" stars:>={min_stars}"
344
+
345
+ # Map sort_by to GitHub API parameters
346
+ sort_param = "stars"
347
+ if sort_by == "updated":
348
+ sort_param = "updated"
349
+ elif sort_by == "forks":
350
+ sort_param = "forks"
351
+
352
+ results = search_github_repos(search_query, sort=sort_param)
353
+
354
+ if not results:
355
+ return "No repositories found. Try different search terms."
356
+
357
+ # Format results as markdown
358
+ markdown = "## GitHub Repository Search Results\n\n"
359
+
360
+ for i, repo in enumerate(results, 1):
361
+ markdown += f"### {i}. [{repo['full_name']}]({repo['html_url']})\n\n"
362
+
363
+ if repo['description']:
364
+ markdown += f"{repo['description']}\n\n"
365
+
366
+ markdown += f"**Language:** {repo['language'] or 'Not specified'}\n"
367
+ markdown += f"**Stars:** {repo['stargazers_count']} | **Forks:** {repo['forks_count']} | **Watchers:** {repo['watchers_count']}\n"
368
+ markdown += f"**Created:** {repo['created_at'][:10]} | **Updated:** {repo['updated_at'][:10]}\n\n"
369
+
370
+ if repo.get('topics'):
371
+ markdown += f"**Topics:** {', '.join(repo['topics'])}\n\n"
372
+
373
+ if repo.get('license') and repo['license'].get('name'):
374
+ markdown += f"**License:** {repo['license']['name']}\n\n"
375
+
376
+ markdown += f"[View Repository]({repo['html_url']}) | [Clone URL]({repo['clone_url']})\n\n"
377
+ markdown += "---\n\n"
378
+
379
+ return markdown
380
+ except Exception as e:
381
+ return f"Error searching for repositories: {str(e)}"
382
+
383
+ def perform_stack_search(query, tag, sort_by):
384
+ """Perform Stack Overflow search with UI parameters"""
385
+ try:
386
+ if not query:
387
+ return "Please enter a search query"
388
+
389
+ # Add tag to query if specified
390
+ if tag and tag != "any":
391
+ query_with_tag = f"{query} [tag:{tag}]"
392
+ else:
393
+ query_with_tag = query
394
+
395
+ # Map sort_by to Stack Exchange API parameters
396
+ sort_param = "votes"
397
+ if sort_by == "newest":
398
+ sort_param = "creation"
399
+ elif sort_by == "activity":
400
+ sort_param = "activity"
401
+
402
+ results = search_stackoverflow(query_with_tag, sort=sort_param)
403
+
404
+ if not results:
405
+ return "No questions found. Try different search terms."
406
+
407
+ # Format results as markdown
408
+ markdown = "## Stack Overflow Search Results\n\n"
409
+
410
+ for i, question in enumerate(results, 1):
411
+ markdown += f"### {i}. [{question['title']}]({question['link']})\n\n"
412
+
413
+ # Score and answer stats
414
+ markdown += f"**Score:** {question['score']} | **Answers:** {question['answer_count']}"
415
+ if question.get('is_answered'):
416
+ markdown += " ✓ (Accepted answer available)"
417
+ markdown += "\n\n"
418
+
419
+ # Tags
420
+ if question.get('tags'):
421
+ markdown += "**Tags:** "
422
+ for tag in question['tags']:
423
+ markdown += f"`{tag}` "
424
+ markdown += "\n\n"
425
+
426
+ # Asked info
427
+ markdown += f"**Asked:** {question['creation_date']} | **Views:** {question.get('view_count', 'N/A')}\n\n"
428
+
429
+ markdown += f"[View Question]({question['link']})\n\n"
430
+ markdown += "---\n\n"
431
+
432
+ return markdown
433
+ except Exception as e:
434
+ return f"Error searching Stack Overflow: {str(e)}"
435
+
436
  # Gradio interface
437
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
438
  current_session_id = gr.State(None)
 
453
  value="llama3-70b-8192",
454
  label="Select Groq Model"
455
  )
456
+
457
+ # Tech Tools Section
458
+ gr.Markdown("### Developer Tools", elem_classes="tool-title")
459
+ with gr.Box(elem_classes="tool-container"):
460
+ with gr.Tabs():
461
+ with gr.TabItem("GitHub Search"):
462
+ repo_query = gr.Textbox(label="Search Query", placeholder="Enter keywords to search for repositories")
463
+ with gr.Row():
464
+ language = gr.Dropdown(
465
+ choices=["any", "JavaScript", "Python", "Java", "C++", "TypeScript", "Go", "Rust", "PHP", "C#"],
466
+ value="any",
467
+ label="Language"
468
+ )
469
+ min_stars = gr.Dropdown(
470
+ choices=["0", "10", "50", "100", "1000", "10000"],
471
+ value="0",
472
+ label="Min Stars"
473
+ )
474
+ sort_by = gr.Dropdown(
475
+ choices=["stars", "forks", "updated"],
476
+ value="stars",
477
+ label="Sort By"
478
+ )
479
+ repo_search_btn = gr.Button("Search Repositories")
480
+
481
+ with gr.TabItem("Stack Overflow"):
482
+ stack_query = gr.Textbox(label="Search Query", placeholder="Enter your technical question")
483
+ with gr.Row():
484
+ tag = gr.Dropdown(
485
+ choices=["any", "python", "javascript", "java", "c++", "react", "node.js", "android", "ios", "sql"],
486
+ value="any",
487
+ label="Tag"
488
+ )
489
+ so_sort_by = gr.Dropdown(
490
+ choices=["votes", "newest", "activity"],
491
+ value="votes",
492
+ label="Sort By"
493
+ )
494
+ so_search_btn = gr.Button("Search Stack Overflow")
495
+
496
+ with gr.TabItem("Code Explainer"):
497
+ code_input = gr.Textbox(
498
+ label="Code to Explain",
499
+ placeholder="Paste your code here...",
500
+ lines=10
501
+ )
502
+ explain_btn = gr.Button("Explain Code")
503
+
504
  with gr.Column(scale=2, min_width=600):
505
  with gr.Tabs():
506
  with gr.TabItem("PDF Viewer"):
 
508
  page_slider = gr.Slider(minimum=1, maximum=1, step=1, label="Page Number", value=1)
509
  pdf_image = gr.Image(label="PDF Page", type="pil", elem_classes="pdf-viewer-image")
510
  stats_display = gr.Markdown("No PDF uploaded yet", elem_classes="stats-box")
511
+
512
+ with gr.TabItem("GitHub Results"):
513
+ repo_results = gr.Markdown("Search for repositories to see results here")
514
+
515
+ with gr.TabItem("Stack Overflow Results"):
516
+ stack_results = gr.Markdown("Search for questions to see results here")
517
+
518
+ with gr.TabItem("Code Explanation"):
519
+ code_explanation = gr.Markdown("Paste your code and click 'Explain Code' to see an explanation here")
520
 
521
  with gr.Row(elem_classes="container"):
522
  with gr.Column(scale=2, min_width=600):
523
  chatbot = gr.Chatbot(height=500, bubble_full_width=False, show_copy_button=True, elem_classes="chat-container")
524
  with gr.Row():
525
+ msg = gr.Textbox(show_label=False, placeholder="Ask about your document, type /github to search repos, or /stack to search Stack Overflow...", scale=5)
526
  send_btn = gr.Button("Send", scale=1)
527
  clear_btn = gr.Button("Clear Conversation")
528
 
529
+ # Event Handlers
530
  upload_button.click(
531
  process_pdf,
532
  inputs=[pdf_file],
 
560
  inputs=[page_slider, pdf_state],
561
  outputs=[pdf_image]
562
  )
563
+
564
+ # Tech tool handlers
565
+ repo_search_btn.click(
566
+ perform_repo_search,
567
+ inputs=[repo_query, language, sort_by, min_stars],
568
+ outputs=[repo_results]
569
+ )
570
+
571
+ so_search_btn.click(
572
+ perform_stack_search,
573
+ inputs=[stack_query, tag, so_sort_by],
574
+ outputs=[stack_results]
575
+ )
576
+
577
+ explain_btn.click(
578
+ explain_code,
579
+ inputs=[code_input],
580
+ outputs=[code_explanation]
581
+ )
582
+
583
+ # Add footer with attribution
584
+ gr.HTML("""
585
+ <div style="text-align: center; margin-top: 20px; padding: 10px; color: #666; font-size: 0.8rem; border-top: 1px solid #eee;">
586
+ Created by Calvin Allen Crawford
587
+ </div>
588
+ """)
589
 
590
  # Launch the app
591
  if __name__ == "__main__":
592
+ demo.launch()