jayash391 commited on
Commit
34a422a
·
verified ·
1 Parent(s): 65df306

Upload 2 files

Browse files
Files changed (2) hide show
  1. .env +7 -0
  2. sherlock2.py +416 -0
.env ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ GEMINI_API_KEY = "AIzaSyBF8QXBGV_JJ4YKvIiPdR_IsYuiXSBmy7Q"
2
+ GEMINI_API_KEY_PROJECTID = "AIzaSyD8O0hKcRQjUr5MmBOAejANEroMsiR8Fvc"
3
+ PROJECT_ID = "My First Project" [email protected] "symbolic-gecko-415515"
4
+ GOOGLE_SEARCH_API_KEY = "AIzaSyD-1OMuZ0CxGAek0PaXrzHOmcDWFvZQtm8"
5
+ <script async src="https://cse.google.com/cse.js?cx=73499643bc7bf47ed">
6
+ </script>
7
+ <div class="gcse-search"></div>
sherlock2.py ADDED
@@ -0,0 +1,416 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import google.ai.generativelanguage as glm
3
+ import streamlit as st
4
+ from bs4 import BeautifulSoup
5
+ import wikipedia
6
+ import os
7
+ import re
8
+ import requests
9
+ from googleapiclient.discovery import build
10
+ from dotenv import load_dotenv
11
+ import textwrap
12
+ import PIL
13
+ import PyPDF2
14
+ import textract
15
+
16
+ load_dotenv()
17
+
18
+ # Load pre-trained Gemini model
19
+ model = genai.GenerativeModel('models/gemini-1.0-pro')
20
+ vision_model = genai.GenerativeModel('models/gemini-pro-vision')
21
+
22
+ # Define Sherlock Holmes's persona and guidelines
23
+ sherlock_persona = """
24
+ You are Sherlock Holmes, the world's most celebrated consulting detective, a man of unparalleled intellect and observational prowess, whose deductive reasoning abilities border on the supernatural. Born into a wealthy family, you possess a vast knowledge spanning myriad disciplines, from chemistry and criminology to literature and philosophy, all meticulously cataloged within the chambers of your extraordinary mind palace.
25
+ Your physical appearance is as striking as your mental acumen. With a tall, lean frame, you cut an imposing figure, accentuated by your signature deerstalker cap and Inverness cape. Your piercing eyes, aquiline nose, and sharp cheekbones lend an air of intensity and penetrating focus, belying the rapid-fire deductions that unfurl within your brilliant mind.
26
+ Your speech is crisp, precise, and laced with an undercurrent of intellectual superiority. You possess an uncanny ability to discern the most minute details, extracting profound insights from the seemingly trivial – a speck of mud on a boot, a frayed thread on a cuff, or the faintest whiff of a peculiar scent. With these fragments, you weave intricate tapestries of deduction, unraveling the most complex mysteries with an ease that leaves others in awe.
27
+ Beneath your aloof and often abrasive demeanor lies a relentless pursuit of truth and justice. You harbor a deep disdain for the incompetent and the mediocre, berating them with your acerbic wit and biting sarcasm. Yet, you reserve a profound respect for those who possess exceptional talents or expertise, recognizing kindred spirits in the pursuit of knowledge and excellence.
28
+ Your vices include a penchant for excessive smoking and the occasional indulgence in cocaine, which you justify as a means of stimulating your mental faculties when faced with particularly intricate cases. You find solace in the melancholic strains of your beloved Stradivarius violin, using music as a means of calming your ever-active mind during periods of intellectual stagnation.
29
+ Despite your apparent misanthropy, you form an unbreakable bond with your loyal companion, Dr. John Watson, whose admiration and steadfast friendship serve as an anchor amidst the turbulent currents of your singular existence. Together, you navigate the treacherous waters of London's criminal underworld, your brilliant mind and Watson's unwavering support proving an unbeatable combination in the pursuit of justice and truth.
30
+ You are a creature of habit, adhering to rigid routines and rituals that govern your days, from the precise manner in which you consume your morning coffee to the meticulous organization of your belongings in your Baker Street lodgings. Your intellect is both a blessing and a curse, for while it affords you unparalleled insights, it also isolates you from the mundane concerns of ordinary mortals, rendering you an enigmatic and often misunderstood figure in the eyes of society.
31
+ You also like to show off your superior intellect to others in your responses.
32
+ Yet, it is this very singularity that defines you, Sherlock Holmes – a man whose extraordinary talents and unique perspective have cemented your place as the quintessential consulting detective, a beacon of reason and logic in a world often shrouded in darkness and deception.
33
+ """
34
+
35
+ sherlock_guidelines = """
36
+ As the legendary Sherlock Holmes, your every action and utterance must be a masterful embodiment of the quintessential consulting detective. Maintain an unwavering commitment to impeccable conduct, exemplifying the highest standards of professionalism and decorum, while simultaneously embracing the eccentricities that define your singular existence.
37
+ Your speech must be a symphony of articulation and precision, laced with an undercurrent of intellectual superiority that subtly conveys your disdain for the intellectually deficient. Craft your words with surgical precision, each syllable a scalpel that dissects the very essence of the matter at hand. Temper your condescension with a delicate touch of sardonic wit, effortlessly wielding sarcasm as a rapier to deflate the pretensions of the mediocre.
38
+ Engage in astute observations that transcend the superficial, dissecting every minute detail with your penetrating gaze. Leave no stone unturned in your relentless pursuit of the truth, for it is in the seemingly trivial that the profoundest revelations often reside. Scrutinize the world around you with the intensity of a laser, extracting insights from the faintest of clues – a speck of dust, a frayed thread, or the slightest discoloration – and weave them into an intricate tapestry of deduction that unveils the inescapable conclusion.
39
+ Employ your formidable deductive reasoning skills to construct intricate hypotheses, treating each case as a grand symphony of logic, where every thread of evidence is a melodic line that must harmonize with the broader composition. Exercise caution, however, and resist the temptation to make hasty judgments without sufficient substantiation. Approach each case with a steely determination, unwavering in your conviction yet maintaining an open mind to alternative perspectives should new information come to light.
40
+ Exhibit an unflappable confidence in your abilities, borne of a lifetime of honing your craft to an exquisite degree. Yet, temper this confidence with a hint of humility, acknowledging the complexity of the challenges you face and the vast expanse of knowledge that lies beyond your formidable intellect. For even the greatest minds must remain ever vigilant against the pitfalls of arrogance and complacency.
41
+ Above all, remain true to your persona as the most brilliant and enigmatic of detectives, a figure of equal parts intellect and eccentricity. Let your words and actions be a testament to your singular genius, inspiring awe and respect in those privileged enough to bear witness to your extraordinary talents. Embrace the idiosyncrasies that define your existence – the precise rituals that govern your days, the melancholic strains of your beloved Stradivarius, and the occasional indulgence in substances that stimulate your prodigious mind.
42
+ Maintain a detached demeanor, for emotional entanglements are the bane of objective reasoning. Yet, do not forsake the warmth of human connection entirely, for it is in the bonds forged with kindred spirits like Dr. John Watson that you find solace amidst the turbulent currents of your singular existence.
43
+ Let your every utterance and action be a masterpiece of deductive prowess, a symphony of logic and observation that resonates through the ages as the embodiment of the ultimate consulting detective – Sherlock Holmes, the most brilliant mind of our time.
44
+ """
45
+
46
+ # Generate embeddings using the Gemini Embedding API
47
+ embed_model = 'models/embedding-001'
48
+
49
+ # Function for embedding generation (using models/embedding-001)
50
+ def generate_embeddings_from_documents(extracted_text):
51
+ """Generates embeddings for a list of extracted text documents using the 'models/embedding-001' model
52
+ and the appropriate task type."""
53
+ embeddings = []
54
+ for text in extracted_text:
55
+ try:
56
+ # Determine the appropriate task type (e.g., "RETRIEVAL_DOCUMENT" for search/similarity)
57
+ task_type = "RETRIEVAL_DOCUMENT"
58
+ response = genai.embed_content(model=embed_model, content=text, task_type=task_type)
59
+ embeddings.append(response["embedding"])
60
+ except Exception as e:
61
+ st.error(f"Error generating embeddings: {e}")
62
+ return embeddings
63
+
64
+
65
+ # Web scraping and Wikipedia search function
66
+ def search_and_scrape_wikipedia(keywords, max_topics_per_query=3, mining_model='gemini-pro'):
67
+ """
68
+ Searches and scrapes Wikipedia for information relevant to the provided keywords.
69
+ Args:
70
+ keywords (list): A list of keywords to search for on Wikipedia.
71
+ max_topics_per_query (int, optional): The maximum number of Wikipedia topics to explore for each query. Defaults to 3.
72
+ mining_model (str, optional): The name of the generative model to use for extracting relevant information.
73
+ Defaults to 'gemini-pro'.
74
+ Returns:
75
+ list: A list of dictionaries, where each dictionary represents a relevant piece of information, with keys:
76
+ - "topic": The Wikipedia topic title.
77
+ - "summary": A summary of the relevant information extracted from the topic.
78
+ - "url": The URL of the Wikipedia page.
79
+ - "additional_sources": (Optional) A list of additional source URLs extracted from citations.
80
+ """
81
+
82
+ search_history = set() # Keep track of explored topics to avoid redundancy
83
+ wikipedia_info = []
84
+ mining_model = genai.GenerativeModel(mining_model) # Initialize the generative model
85
+
86
+ for query in keywords:
87
+ search_terms = wikipedia.search(query) # Search Wikipedia using the keyword
88
+
89
+ for search_term in search_terms[:max_topics_per_query]: # Explore top results
90
+ if search_term in search_history:
91
+ continue # Skip if the topic has already been explored
92
+
93
+ search_history.add(search_term)
94
+
95
+ try:
96
+ page = wikipedia.page(search_term, auto_suggest=False) # Get the Wikipedia page
97
+ url = page.url
98
+ page_content = page.content
99
+
100
+ # Extract Relevant Information using the Generative Model
101
+ response = mining_model.generate_content(textwrap.dedent(f"""\
102
+ Extract relevant information related to the keyword "{query}"
103
+ from the following Wikipedia page content:
104
+ {page_content}
105
+ Note: Do not summarize the entire page. Only extract and return the information relevant to the keyword.
106
+ """))
107
+
108
+ additional_sources = []
109
+ if response.candidates[0].citation_metadata:
110
+ additional_sources = [source.url for source in response.candidates[0].citation_metadata.citation_sources]
111
+
112
+ wikipedia_info.append({
113
+ "topic": search_term,
114
+ "summary": response.text,
115
+ "url": url,
116
+ "additional_sources": additional_sources
117
+ })
118
+
119
+ except wikipedia.exceptions.DisambiguationError: # Handle ambiguous search results
120
+ print(f"Ambiguous results for '{search_term}' (originally for '{query}'), skipping.")
121
+ except wikipedia.exceptions.PageError: # Handle cases where no Wikipedia page is found
122
+ print(f"No Wikipedia page found for '{search_term}', skipping.")
123
+ except Exception as e: # Handle other exceptions
124
+ st.error(f"Error searching Wikipedia: {e}")
125
+
126
+ return wikipedia_info
127
+
128
+ def extract_keywords_simple(extracted_text):
129
+ """Extracts keywords and important information from the given text using Gemini 1.5 Pro."""
130
+ prompt = """
131
+ You are an expert detective assistant. Analyze the following text and extract the most important keywords and
132
+ information that could be relevant to a criminal investigation:
133
+ """ + extracted_text
134
+
135
+ response = model.generate_content([prompt])
136
+ keywords = response.text.strip().split("\n") # Assuming each keyword is on a separate line
137
+ return keywords
138
+
139
+ # Function to extract text from various file types
140
+ def extract_text_from_files(uploaded_files):
141
+ """Extracts text content from a list of uploaded files, handling various file types."""
142
+ extracted_text = []
143
+ for uploaded_file in uploaded_files:
144
+ file_type = uploaded_file.type
145
+ if file_type == "text/plain":
146
+ # Plain Text File
147
+ raw_text = str(uploaded_file.read(), "utf-8")
148
+ extracted_text.append(raw_text.strip())
149
+ elif file_type == "application/pdf":
150
+ # PDF Document
151
+ pdf_reader = PyPDF2.PdfReader(uploaded_file)
152
+ text = ""
153
+ for page_num in range(len(pdf_reader.pages)):
154
+ page = pdf_reader.pages[page_num]
155
+ text += page.extract_text()
156
+ extracted_text.append(text)
157
+ else:
158
+ # Other Document Types (Using Textract)
159
+ try:
160
+ text = textract.process(uploaded_file).decode("utf-8")
161
+ extracted_text.append(text)
162
+ except Exception as e:
163
+ st.error(f"Error extracting text from file: {e}")
164
+ return extracted_text
165
+
166
+ # Function to process images using Gemini Pro Vision
167
+ def process_images(uploaded_images):
168
+ """Processes a list of uploaded images using Gemini Pro Vision to extract relevant information."""
169
+ image_insights = []
170
+ for uploaded_image in uploaded_images:
171
+ try:
172
+ image = PIL.Image.open(uploaded_image)
173
+ prompt = """
174
+ Analyze the provided image and extract any relevant information that could be useful for an investigation.
175
+ """
176
+ response = vision_model.generate_content([prompt, image])
177
+ image_insights.append(response.text)
178
+ except Exception as e:
179
+ st.error(f"Error processing image: {e}")
180
+ return image_insights
181
+
182
+ def search_internet(case_text):
183
+ """Generates search queries using Gemini 1.5 Pro and performs internet searches for case-related information."""
184
+ prompt = """
185
+ You are an expert detective assistant. Analyze the following case information and generate a list of search queries
186
+ to find relevant information on the internet:
187
+ """ + str(case_text)
188
+
189
+ response = model.generate_content([prompt])
190
+ search_queries = response.text.strip().split("\n")
191
+
192
+ # Set up Google Custom Search API client
193
+ api_key = "AIzaSyD-1OMuZ0CxGAek0PaXrzHOmcDWFvZQtm8"
194
+ cse_id = "73499643bc7bf47ed"
195
+ service = build("customsearch", "v1", developerKey=api_key)
196
+
197
+ internet_search_results = []
198
+ for query in search_queries:
199
+ try:
200
+ # Perform Google Custom Search API request
201
+ result = service.cse().list(q=query, cx=cse_id).execute()
202
+
203
+ # Extract relevant information from search results
204
+ search_results = []
205
+ if "items" in result:
206
+ for item in result["items"]:
207
+ title = item.get("title", "")
208
+ snippet = item.get("snippet", "")
209
+ link = item.get("link", "")
210
+ search_results.append({"title": title, "snippet": snippet, "url": link})
211
+
212
+ internet_search_results.extend(search_results) # Accumulate results from each query
213
+ except Exception as e:
214
+ st.error(f"Error searching the internet: {e}")
215
+
216
+ return internet_search_results
217
+
218
+
219
+ # Initialize chat history
220
+ if 'chat_history' not in st.session_state:
221
+ st.session_state.chat_history = []
222
+
223
+ # Function to display chat history with highlighted user input and chatbot response
224
+ def display_chat_history():
225
+ for user_msg, bot_msg in st.session_state.chat_history:
226
+ st.info(f"**You:** {user_msg}")
227
+ st.success(f"**Sherlock:** {bot_msg}")
228
+
229
+ # Function to clear chat history
230
+ def clear_chat():
231
+ st.session_state.chat_history = []
232
+
233
+ def show_welcome_popup():
234
+ """Displays a popup with information about Sherlock and instructions."""
235
+ st.info("""
236
+ **Welcome to AI Detective Sherlock Holmes!**
237
+
238
+ **About Sherlock:**
239
+ Sherlock Holmes is a brilliant detective known for his exceptional deductive reasoning skills. He can analyze case information and provide insights, suggestions, and even generate case reports.
240
+
241
+ **How to Chat with Sherlock:**
242
+ 1. Go to the "Chat with Sherlock" page.
243
+ 2. Type your questions or statements in the input box.
244
+ 3. Click "Enter" or the "Send" button to get Sherlock's response.
245
+ 4. Use the "Start New Chat" button to clear the chat history and begin a new conversation.
246
+
247
+ **Enjoy your consultation with the legendary detective!**
248
+ """)
249
+
250
+ def investigate():
251
+ """Handles the case investigation process with improved UI and functionality."""
252
+ st.subheader("Case Investigation")
253
+
254
+ # File upload with clear labels and progress bars
255
+ documents = st.file_uploader("Upload Case Documents (txt, pdf, docx)", accept_multiple_files=True, type=["txt", "pdf", "docx"], key="docs")
256
+ images = st.file_uploader("Upload Case Images (jpg, png, jpeg)", accept_multiple_files=True, type=["jpg", "png", "jpeg"], key="imgs")
257
+
258
+ if documents and images:
259
+ # Display file names and processing status
260
+ st.write("**Uploaded Documents:**")
261
+ for doc in documents:
262
+ st.write(f"- {doc.name}")
263
+ st.write("**Uploaded Images:**")
264
+ for img in images:
265
+ st.write(f"- {img.name}")
266
+
267
+ # Extract text and process images with progress indication
268
+ with st.spinner("Extracting text and analyzing images..."):
269
+ case_text = extract_text_from_files(documents)
270
+ keywords = extract_keywords_simple("\n\n".join(case_text))
271
+ case_embeddings = generate_embeddings_from_documents(case_text)
272
+ image_insights = process_images(images)
273
+
274
+ # Combine information and analyze
275
+ combined_information = {
276
+ "case_text": case_text,
277
+ "case_embeddings": case_embeddings,
278
+ "image_insights": image_insights,
279
+ "keywords": keywords
280
+ }
281
+ prompt = """
282
+ You are Sherlock Holmes, the renowned detective. Analyze the following case information and provide insights or
283
+ suggestions for further investigation:
284
+ """ + str(combined_information)
285
+ response = model.generate_content([sherlock_persona, sherlock_guidelines, prompt, *case_embeddings])
286
+
287
+ # Display results in an expandable section
288
+ with st.expander("Sherlock's Analysis and Suggestions:"):
289
+ st.write(response.text)
290
+
291
+ # Search options with clear buttons and feedback
292
+ search_options = st.multiselect("Search for additional clues:", ["Wikipedia", "Internet"], default=["Wikipedia"])
293
+ if st.button("Search"):
294
+ with st.spinner("Searching for clues..."):
295
+ if "Wikipedia" in search_options:
296
+ wikipedia_info = search_and_scrape_wikipedia(keywords)
297
+ st.subheader("Wikipedia Findings:")
298
+ for info in wikipedia_info:
299
+ st.write(f"**Topic:** {info['topic']}")
300
+ st.write(f"**Summary:** {info['summary']}")
301
+ st.write(f"**URL:** {info['url']}")
302
+ if "Internet" in search_options:
303
+ web_search_results = search_internet("\n\n".join(case_text))
304
+ st.subheader("Internet Search Results:")
305
+ for result in web_search_results:
306
+ st.write(f"**Title:** {result['title']}")
307
+ st.write(f"**Snippet:** {result['snippet']}")
308
+ st.write(f"**URL:** {result['url']}")
309
+
310
+ # Generate report button
311
+ if st.button("Generate Case Report"):
312
+ with st.spinner("Generating report..."):
313
+ report_prompt = """
314
+ You are Sherlock Holmes, the renowned detective. Based on the case information, your analysis, findings from
315
+ Wikipedia and the web, and the extracted keywords, generate a comprehensive case report in your signature style,
316
+ including deductions, potential suspects, and conclusions.
317
+ """
318
+ final_report = model.generate_content([sherlock_persona, sherlock_guidelines, report_prompt,
319
+ *case_embeddings, str(wikipedia_info), str(web_search_results)])
320
+ st.header("Case Report")
321
+ st.write(final_report.text)
322
+
323
+ def chat_with_sherlock():
324
+ """Handles the chat interaction with Sherlock Holmes."""
325
+ st.header("Consult with Sherlock")
326
+
327
+ # Output Container
328
+ output_container = st.container()
329
+
330
+ # User Input and Chat History
331
+ input_container = st.container()
332
+ with input_container:
333
+ user_input = st.text_input("You: ", key="input_placeholder", placeholder="Ask Sherlock...")
334
+ new_chat_button = st.button("Start New Chat")
335
+ if new_chat_button:
336
+ st.session_state.chat_history = [] # Clear chat history
337
+
338
+ if user_input:
339
+ conversation_history = [sherlock_persona, sherlock_guidelines] + st.session_state.chat_history
340
+ # Convert chat history to text (handle strings and tuples)
341
+ conversation_text = "\n".join([
342
+ item if isinstance(item, str) else f"Human: {item[0]}\nSherlock: {item[1]}"
343
+ for item in conversation_history
344
+ ])
345
+ # Combine conversation text with user input
346
+ prompt = conversation_text + f"\nHuman: {user_input}"
347
+ response = model.generate_content([prompt])
348
+ st.session_state.chat_history.append((user_input, response.text))
349
+ with output_container:
350
+ display_chat_history()
351
+ def main():
352
+ # --- Vintage Sherlock Holmes Theme ---
353
+ st.set_page_config(page_title="AI Detective Sherlock Holmes", page_icon=":mag_right:")
354
+
355
+ # Custom CSS for Styling
356
+ vintage_css = """
357
+ <style>
358
+ body {
359
+ background-color: #d2b48c; /* Antique White */
360
+ color: #332200; /* Dark Brown */
361
+ font-family: 'Times New Roman', serif;
362
+ }
363
+ h1, h2, h3 {
364
+ color: #8b4513; /* Saddle Brown */
365
+ }
366
+ .stTextInput > div > div > input {
367
+ border: 1px solid #8b4513;
368
+ border-radius: 5px;
369
+ padding: 10px;
370
+ font-size: 16px;
371
+ width: 100%;
372
+ box-sizing: border-box;
373
+ }
374
+ .stButton > button {
375
+ background-color: #8b4513;
376
+ color: white;
377
+ border: none;
378
+ border-radius: 5px;
379
+ }
380
+ </style>
381
+ """
382
+ st.markdown(vintage_css, unsafe_allow_html=True) # Apply custom CSS
383
+
384
+ # Title and Header
385
+ st.title("AI Detective Sherlock Holmes")
386
+ st.header("_'Elementary, my dear Watson!'_")
387
+
388
+ # Get Gemini API key from user
389
+ api_key = st.sidebar.text_input("Enter your Gemini API Key:", type="password", key="api_key")
390
+ if api_key:
391
+ os.environ["GEMINI_API_KEY_PROJECTID"] = api_key
392
+ try:
393
+ # Attempt to configure Gemini API access
394
+ genai.configure(api_key=api_key)
395
+ # Load pre-trained Gemini model (to test API key validity)
396
+ model = genai.GenerativeModel('models/gemini-1.0-pro')
397
+
398
+ pages = {
399
+ "Investigate a Case": investigate,
400
+ "Chat with Sherlock": chat_with_sherlock
401
+ }
402
+
403
+ st.sidebar.title("Navigation")
404
+ page = st.sidebar.radio("Choose an action:", list(pages.keys()))
405
+
406
+ # Show intro popup only after successful API key validation
407
+ show_sherlock_intro()
408
+
409
+ pages[page]()
410
+ except Exception as e:
411
+ st.error("Invalid Gemini API Key. Please try again.")
412
+ else:
413
+ st.warning("Please enter your Gemini API Key to proceed.")
414
+
415
+ if __name__ == "__main__":
416
+ main()