shukdevdattaEX commited on
Commit
4bd696b
Β·
verified Β·
1 Parent(s): 3667b6c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -123
app.py CHANGED
@@ -6,46 +6,91 @@ from datetime import datetime
6
  from typing import List, Dict, Any, Optional, Union
7
  import threading
8
  import re
 
 
9
 
10
  # Import Groq
11
  from groq import Groq
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  class CreativeAgenticAI:
14
  """
15
- Creative Agentic AI Chat Tool using Groq's models with browser search and compound models
16
  """
17
 
18
- def __init__(self, api_key: str, model: str = "compound-beta"):
19
  """
20
  Initialize the Creative Agentic AI system.
21
 
22
  Args:
23
- api_key: Groq API key
24
- model: Which Groq model to use
 
25
  """
26
- self.api_key = api_key
27
- if not self.api_key:
28
- raise ValueError("No API key provided")
 
 
 
29
 
30
- self.client = Groq(api_key=self.api_key)
31
  self.model = model
 
 
32
  self.conversation_history = []
33
 
34
  # Available models with their capabilities
35
  self.available_models = {
36
- "compound-beta": {"supports_web_search": True, "supports_browser_search": False},
37
- "compound-beta-mini": {"supports_web_search": True, "supports_browser_search": False},
38
- "openai/gpt-oss-20b": {"supports_web_search": False, "supports_browser_search": True},
39
  }
40
 
41
- def chat(self, message: str,
42
- include_domains: List[str] = None,
43
- exclude_domains: List[str] = None,
44
- system_prompt: str = None,
45
- temperature: float = 0.7,
46
- max_tokens: int = 1024,
47
- search_type: str = "auto",
48
- force_search: bool = False) -> Dict:
49
  """
50
  Send a message to the AI and get a response with flexible search options
51
 
@@ -123,8 +168,7 @@ IMPORTANT: When you search the web and find information, you MUST:
123
  "messages": messages,
124
  "model": self.model,
125
  "temperature": temperature,
126
- "max_completion_tokens": max_tokens if self._supports_browser_search() else None,
127
- "max_tokens": max_tokens if not self._supports_browser_search() else None,
128
  }
129
 
130
  # Add domain filtering for compound models
@@ -154,9 +198,14 @@ IMPORTANT: When you search the web and find information, you MUST:
154
  params["tool_choice"] = tool_choice
155
 
156
  try:
157
- # Make the API call
158
- response = self.client.chat.completions.create(**params)
159
- content = response.choices[0].message.content
 
 
 
 
 
160
 
161
  # Extract tool usage information and enhance it
162
  tool_info = self._extract_tool_info(response)
@@ -225,18 +274,15 @@ IMPORTANT: When you search the web and find information, you MUST:
225
  "tool_name": getattr(tool, "name", "unknown"),
226
  }
227
 
228
- # Extract search queries and results
229
  if hasattr(tool, "input"):
230
  tool_input = str(tool.input)
231
  tool_dict["input"] = tool_input
232
- # Try to extract search query
233
  if "search" in tool_dict["tool_name"].lower():
234
  tool_info["search_queries"].append(tool_input)
235
 
236
  if hasattr(tool, "output"):
237
  tool_output = str(tool.output)
238
  tool_dict["output"] = tool_output
239
- # Try to extract URLs from output
240
  urls = self._extract_urls(tool_output)
241
  tool_info["sources_found"].extend(urls)
242
 
@@ -268,18 +314,16 @@ IMPORTANT: When you search the web and find information, you MUST:
268
  """Extract URLs from text"""
269
  url_pattern = r'https?://[^\s<>"]{2,}'
270
  urls = re.findall(url_pattern, text)
271
- return list(set(urls)) # Remove duplicates
272
 
273
  def _enhance_citations(self, content: str, tool_info: Dict) -> str:
274
  """Enhance content with better citation formatting"""
275
  if not tool_info or not tool_info.get("sources_found"):
276
  return content
277
 
278
- # Add sources section if not already present
279
  if "Sources Used:" not in content and "sources:" not in content.lower():
280
  sources_section = "\n\n---\n\n### πŸ“š Sources Used:\n"
281
- for i, url in enumerate(tool_info["sources_found"][:10], 1): # Limit to 10 sources
282
- # Try to extract domain name for better formatting
283
  domain = self._extract_domain(url)
284
  sources_section += f"{i}. [{domain}]({url})\n"
285
 
@@ -292,7 +336,6 @@ IMPORTANT: When you search the web and find information, you MUST:
292
  try:
293
  if url.startswith(('http://', 'https://')):
294
  domain = url.split('/')[2]
295
- # Remove www. prefix if present
296
  if domain.startswith('www.'):
297
  domain = domain[4:]
298
  return domain
@@ -322,27 +365,37 @@ IMPORTANT: When you search the web and find information, you MUST:
322
  ai_instance = None
323
  api_key_status = "Not Set"
324
 
325
- def validate_api_key(api_key: str, model: str) -> str:
326
- """Validate Groq API key and initialize AI instance"""
327
  global ai_instance, api_key_status
328
 
329
- if not api_key or len(api_key.strip()) < 10:
330
  api_key_status = "Invalid ❌"
331
- return "❌ Please enter a valid API key (should be longer than 10 characters)"
 
 
 
 
332
 
333
  try:
334
- # Test the API key
335
- client = Groq(api_key=api_key)
336
- # Try a simple request to validate
337
- test_response = client.chat.completions.create(
338
- messages=[{"role": "user", "content": "Hello"}],
339
- model=model,
340
- max_completion_tokens=10 if model in ["openai/gpt-oss-20b", "llama-3.3-70b-versatile", "llama-3.1-70b-versatile", "mixtral-8x7b-32768"] else None,
341
- max_tokens=10 if model in ["compound-beta", "compound-beta-mini"] else None
342
- )
 
 
 
 
 
 
343
 
344
  # Create AI instance
345
- ai_instance = CreativeAgenticAI(api_key=api_key, model=model)
346
  api_key_status = "Valid βœ…"
347
 
348
  model_info = ai_instance.get_model_info()
@@ -354,12 +407,12 @@ def validate_api_key(api_key: str, model: str) -> str:
354
 
355
  cap_text = " | ".join(capabilities) if capabilities else "πŸ’¬ Chat Only"
356
 
357
- return f"βœ… API Key Valid! NeuroScope AI is ready.\n\n**Model:** {model}\n**Capabilities:** {cap_text}\n**Status:** Connected and ready for chat!"
358
 
359
  except Exception as e:
360
  api_key_status = "Invalid ❌"
361
  ai_instance = None
362
- return f"❌ Error validating API key: {str(e)}\n\nPlease check your API key and try again."
363
 
364
  def update_model(model: str) -> str:
365
  """Update the model selection"""
@@ -375,9 +428,9 @@ def update_model(model: str) -> str:
375
  capabilities.append("πŸ” Browser Search Tools")
376
 
377
  cap_text = " | ".join(capabilities) if capabilities else "πŸ’¬ Chat Only"
378
- return f"βœ… Model updated to: **{model}**\n**Capabilities:** {cap_text}"
379
  else:
380
- return "⚠️ Please set your API key first"
381
 
382
  def get_search_options(model: str) -> gr.update:
383
  """Get available search options based on model"""
@@ -392,39 +445,35 @@ def get_search_options(model: str) -> gr.update:
392
  if model_info.get("supports_browser_search"):
393
  options.extend(["browser_search", "auto"])
394
 
395
- # Remove duplicates while preserving order
396
  options = list(dict.fromkeys(options))
397
-
398
  default_value = "auto" if "auto" in options else "none"
399
  return gr.update(choices=options, value=default_value)
400
 
401
- def chat_with_ai(message: str,
402
- include_domains: str,
403
- exclude_domains: str,
404
- system_prompt: str,
405
- temperature: float,
406
- max_tokens: int,
407
- search_type: str,
408
- force_search: bool,
409
- history: List) -> tuple:
410
  """Main chat function"""
411
  global ai_instance
412
 
413
  if not ai_instance:
414
- error_msg = "⚠️ Please set your Groq API key first!"
415
  history.append([message, error_msg])
416
  return history, ""
417
 
418
  if not message.strip():
419
  return history, ""
420
 
421
- # Process domain lists
422
  include_list = [d.strip() for d in include_domains.split(",")] if include_domains.strip() else []
423
  exclude_list = [d.strip() for d in exclude_domains.split(",")] if exclude_domains.strip() else []
424
 
425
  try:
426
- # Get AI response
427
- response = ai_instance.chat(
428
  message=message,
429
  include_domains=include_list if include_list else None,
430
  exclude_domains=exclude_list if exclude_list else None,
@@ -435,10 +484,8 @@ def chat_with_ai(message: str,
435
  force_search=force_search
436
  )
437
 
438
- # Format response
439
  ai_response = response["content"]
440
 
441
- # Add enhanced tool usage info
442
  if response.get("tool_usage"):
443
  tool_info = response["tool_usage"]
444
  tool_summary = []
@@ -457,7 +504,6 @@ def chat_with_ai(message: str,
457
  if tool_summary:
458
  ai_response += f"\n\n*{' | '.join(tool_summary)}*"
459
 
460
- # Add search type info
461
  search_info = []
462
  if response.get("search_type_used") and response["search_type_used"] != "none":
463
  search_info.append(f"πŸ” Search type: {response['search_type_used']}")
@@ -465,7 +511,6 @@ def chat_with_ai(message: str,
465
  if force_search:
466
  search_info.append("⚑ Forced search enabled")
467
 
468
- # Add domain filtering info
469
  if include_list or exclude_list:
470
  filter_info = []
471
  if include_list:
@@ -477,7 +522,6 @@ def chat_with_ai(message: str,
477
  if search_info:
478
  ai_response += f"\n\n*🌐 Search settings: {' | '.join(search_info)}*"
479
 
480
- # Add to history
481
  history.append([message, ai_response])
482
 
483
  return history, ""
@@ -497,7 +541,6 @@ def clear_chat_history():
497
  def create_gradio_app():
498
  """Create the main Gradio application"""
499
 
500
- # Custom CSS for better styling
501
  css = """
502
  .container {
503
  max-width: 1200px;
@@ -553,15 +596,13 @@ def create_gradio_app():
553
  """
554
 
555
  with gr.Blocks(css=css, title="πŸ€– Creative Agentic AI Chat", theme=gr.themes.Ocean()) as app:
556
- # Header
557
  gr.HTML("""
558
  <div class="header">
559
  <h1>πŸ€– NeuroScope-AI Enhanced</h1>
560
- <p>Powered by Groq's Models with Web Search, Browser Search & Agentic Capabilities</p>
561
  </div>
562
  """)
563
 
564
- # NeuroScope AI Section
565
  with gr.Group():
566
  with gr.Accordion("πŸ€– NeuroScope AI Enhanced", open=False, elem_id="neuroscope-accordion"):
567
  gr.Markdown("""
@@ -573,26 +614,22 @@ def create_gradio_app():
573
  - 🎯 **Model Flexibility**: Choose the right model for your task
574
  """)
575
 
576
- # IMPORTANT Section with Enhanced Search Info
577
  with gr.Group():
578
  with gr.Accordion("πŸ” IMPORTANT - Enhanced Search Capabilities!", open=True, elem_id="neuroscope-accordion"):
579
  gr.Markdown("""
580
  <div class="search-info">
581
  <h3>πŸš€ NEW: Multiple Search Types Available!</h3>
582
 
583
- <h4>🌐 Web Search Models (Compound Models)</h4>
584
  <ul>
585
  <li><strong>compound-beta:</strong> Most powerful with domain filtering</li>
586
  <li><strong>compound-beta-mini:</strong> Faster with domain filtering</li>
587
  <li><strong>Features:</strong> Include/exclude domains, autonomous web search</li>
588
  </ul>
589
 
590
- <h4>πŸ” Browser Search Models (Tool-based Models)</h4>
591
  <ul>
592
  <li><strong>openai/gpt-oss-20b:</strong> Fast browser search capabilities</li>
593
- <li><strong>llama-3.3-70b-versatile:</strong> Advanced reasoning with search</li>
594
- <li><strong>llama-3.1-70b-versatile:</strong> Reliable with search tools</li>
595
- <li><strong>mixtral-8x7b-32768:</strong> Large context with search</li>
596
  <li><strong>Features:</strong> Real-time browser search, current information</li>
597
  </ul>
598
  </div>
@@ -609,15 +646,20 @@ def create_gradio_app():
609
  </div>
610
  """)
611
 
612
- # API Key and Model Selection Section
613
  with gr.Row():
614
  with gr.Column(scale=2):
615
- api_key = gr.Textbox(
616
  label="πŸ”‘ Groq API Key",
617
  placeholder="Enter your Groq API key here...",
618
  type="password",
619
  info="Get your API key from: https://console.groq.com/"
620
  )
 
 
 
 
 
 
621
  with gr.Column(scale=2):
622
  model_selection = gr.Radio(
623
  choices=[
@@ -632,13 +674,11 @@ def create_gradio_app():
632
  with gr.Column(scale=1):
633
  connect_btn = gr.Button("πŸ”— Connect", variant="primary", size="lg")
634
 
635
- # Status display
636
  status_display = gr.Markdown("### πŸ“Š Status: Not connected", elem_classes=["status-box"])
637
 
638
- # Connect button functionality
639
  connect_btn.click(
640
- fn=validate_api_key,
641
- inputs=[api_key, model_selection],
642
  outputs=[status_display]
643
  )
644
 
@@ -648,7 +688,6 @@ def create_gradio_app():
648
  outputs=[status_display]
649
  )
650
 
651
- # Main Chat Interface
652
  with gr.Tab("πŸ’¬ Chat"):
653
  chatbot = gr.Chatbot(
654
  label="Creative AI Assistant with Enhanced Search",
@@ -668,7 +707,6 @@ def create_gradio_app():
668
  send_btn = gr.Button("πŸ“€ Send", variant="primary")
669
  clear_btn = gr.Button("πŸ—‘οΈ Clear", variant="secondary")
670
 
671
- # Search Settings
672
  with gr.Accordion("πŸ” Search Settings", open=False, elem_id="neuroscope-accordion"):
673
  with gr.Row():
674
  search_type = gr.Radio(
@@ -683,14 +721,12 @@ def create_gradio_app():
683
  info="Force AI to search even for general questions"
684
  )
685
 
686
- # Update search options when model changes
687
  model_selection.change(
688
  fn=get_search_options,
689
  inputs=[model_selection],
690
  outputs=[search_type]
691
  )
692
 
693
- # Domain Filtering Section (only for web search models)
694
  with gr.Accordion("🌐 Domain Filtering (Web Search Models Only)", open=False, elem_id="neuroscope-accordion"):
695
  gr.Markdown("""
696
  <div class="domain-info">
@@ -717,7 +753,6 @@ def create_gradio_app():
717
  info="Never search these domains (compound models only)"
718
  )
719
 
720
- # Advanced Settings
721
  with gr.Accordion("βš™οΈ Advanced Settings", open=False, elem_id="neuroscope-accordion"):
722
  with gr.Row():
723
  temperature = gr.Slider(
@@ -744,7 +779,6 @@ def create_gradio_app():
744
  info="Leave empty to use default creative assistant prompt with enhanced citations"
745
  )
746
 
747
- # Model Comparison Section
748
  with gr.Accordion("πŸ“Š Model Comparison Guide", open=False, elem_id="neuroscope-accordion"):
749
  gr.Markdown("""
750
  ### πŸ” Choose Your Model Based on Task:
@@ -752,21 +786,22 @@ def create_gradio_app():
752
  **For Academic Research & Domain-Specific Search:**
753
  - `compound-beta` or `compound-beta-mini` with include domains (*.edu, arxiv.org)
754
  - Best for: Research papers, academic sources, filtered searches
 
755
 
756
  **For Current Events & Real-Time Information:**
757
- - `openai/gpt-oss-20b` or `llama-3.3-70b-versatile` with browser search
758
  - Best for: News, current events, real-time data
 
759
 
760
  **For General Knowledge & Creative Tasks:**
761
  - Any model with search type = "auto" or "none"
762
  - Best for: Creative writing, general questions, analysis
763
 
764
  **For Programming & Technical Documentation:**
765
- - `llama-3.1-70b-versatile` with browser search, or compound models with tech domains
766
  - Best for: Code help, documentation, technical guides
767
  """)
768
 
769
- # Domain Examples Section
770
  with gr.Accordion("πŸ”— Common Domain Examples", open=False, elem_id="neuroscope-accordion"):
771
  gr.Markdown("""
772
  **Academic & Research:**
@@ -788,24 +823,23 @@ def create_gradio_app():
788
  - `*.gov`, `*.org`, `un.org`, `worldbank.org`, `imf.org`
789
  """)
790
 
791
- # How to Use Section
792
  with gr.Accordion("πŸ“– How to Use This Enhanced App", open=False, elem_id="neuroscope-accordion"):
793
  gr.Markdown("""
794
  ### πŸš€ Getting Started
795
- 1. **Enter your Groq API Key** - Get one from [console.groq.com](https://console.groq.com/)
796
  2. **Select a model** - Choose based on your search needs:
797
- - **Compound models**: For web search with domain filtering
798
- - **Tool-based models**: For browser search with real-time data
799
  3. **Configure search settings** - Choose search type and options
800
- 4. **Click Connect** - Validate your key and connect to the AI
801
  5. **Start chatting!** - Type your message and get intelligent responses with citations
802
 
803
  ### 🎯 Key Features
804
- - **Dual Search Capabilities**: Web search + Browser search depending on model
805
  - **Smart Citations**: Automatic source linking and citation formatting
806
  - **Domain Filtering**: Control which websites the AI searches (compound models)
807
  - **Real-time Search**: Get current information with browser search tools
808
- - **Model Flexibility**: Choose the right model for your specific task
809
  - **Enhanced Tool Visibility**: See exactly what search tools were used
810
 
811
  ### πŸ’‘ Tips for Best Results
@@ -816,7 +850,7 @@ def create_gradio_app():
816
  - Use "Force Search" for the most current information
817
 
818
  **For Current Events:**
819
- - Use tool-based models (openai/gpt-oss-20b)
820
  - Set search type to "browser_search"
821
  - Enable "Force Search" for real-time data
822
 
@@ -826,19 +860,18 @@ def create_gradio_app():
826
  - Use higher temperature (0.8-1.0) for more creativity
827
  """)
828
 
829
- # Sample Examples Section
830
  with gr.Accordion("🎯 Sample Examples to Test Enhanced Search", open=False, elem_id="neuroscope-accordion"):
831
  gr.Markdown("""
832
  <div class="example-box">
833
  <h4>πŸ”¬ Research & Analysis (Test Different Models)</h4>
834
 
835
- **Compound Model + Domain Filtering:**
836
  - Query: "What are the latest breakthroughs in quantum computing?"
837
  - Model: compound-beta
838
  - Include domains: "arxiv.org, *.edu, nature.com"
839
  - Search type: web_search
840
 
841
- **Browser Search Model:**
842
  - Same query with openai/gpt-oss-20b
843
  - Search type: browser_search
844
  - Force search: enabled
@@ -847,12 +880,12 @@ def create_gradio_app():
847
 
848
  **Real-time News:**
849
  - Query: "What happened in AI industry this week?"
850
- - Model: openai/gpt-oss-20b
851
  - Search type: browser_search
852
  - Force search: enabled
853
 
854
  **Compare with Web Search:**
855
- - Same query with compound-beta
856
  - Include domains: "reuters.com, bbc.com, techcrunch.com"
857
 
858
  <h4>πŸ’» Programming & Tech (Model Comparison)</h4>
@@ -861,7 +894,7 @@ def create_gradio_app():
861
  - Query: "How to implement OAuth 2.0 in Python Flask?"
862
  - Try with both model types:
863
  - compound-beta with "github.com, docs.python.org, stackoverflow.com"
864
- - openai/gpt-oss-20b with browser_search
865
 
866
  <h4>🎨 Creative Tasks (No Search Needed)</h4>
867
  - Query: "Write a short story about AI and humans working together"
@@ -872,18 +905,17 @@ def create_gradio_app():
872
 
873
  **Financial Data (Real-time):**
874
  - Query: "Current cryptocurrency market trends"
875
- - Model: openai/gpt-oss-20b
876
  - Search type: browser_search
877
  - Force search: enabled
878
 
879
  **Business Analysis (Filtered):**
880
  - Query: "Cryptocurrency adoption in enterprise"
881
- - Model: compound-beta
882
  - Include domains: "bloomberg.com, wsj.com, harvard.edu"
883
  </div>
884
  """)
885
 
886
- # Event handlers
887
  send_btn.click(
888
  fn=chat_with_ai,
889
  inputs=[msg, include_domains, exclude_domains, system_prompt, temperature, max_tokens, search_type, force_search, chatbot],
@@ -901,31 +933,30 @@ def create_gradio_app():
901
  outputs=[chatbot]
902
  )
903
 
904
- # Footer
905
  with gr.Accordion("πŸš€ About This Enhanced NeuroScope AI", open=True, elem_id="neuroscope-accordion"):
906
  gr.Markdown("""
907
  **Enhanced Creative Agentic AI Chat Tool** with dual search capabilities:
908
 
909
  ### πŸ†• **New in This Version:**
910
- - πŸ” **Browser Search Integration**: Real-time search with tool-based models
911
- - 🌐 **Dual Search System**: Web search (compound) + Browser search (tool-based)
912
- - 🎯 **Model Flexibility**: 6 different models for different tasks
913
  - ⚑ **Force Search Option**: Make AI search even for general questions
914
  - πŸ”§ **Enhanced Tool Visibility**: See exactly what search tools were used
915
- - πŸ“Š **Model Comparison Guide**: Choose the right model for your task
916
 
917
  ### πŸ† **Core Features:**
918
  - πŸ”— **Automatic Source Citations**: Every response includes clickable links to sources
919
  - πŸ“š **Sources Used Section**: Dedicated section showing all websites referenced
920
  - 🌐 **Smart Domain Filtering**: Control search scope (compound models)
921
- - πŸ” **Real-time Browser Search**: Current information (tool-based models)
922
  - πŸ’¬ **Conversational Memory**: Maintains context throughout the session
923
  - βš™οΈ **Full Customization**: Adjust all parameters and prompts
924
  - 🎨 **Creative & Analytical**: Optimized for both creative and research tasks
925
 
926
  ### πŸ› οΈ **Technical Details:**
927
- - **Compound Models**: compound-beta, compound-beta-mini (web search + domain filtering)
928
- - **Tool-based Models**: openai/gpt-oss-20b, llama models, mixtral (browser search tools)
929
  - **Automatic Search Type Detection**: AI chooses best search method
930
  - **Enhanced Error Handling**: Robust error management and user feedback
931
  - **Real-time Status Updates**: Live feedback on model capabilities and search settings
@@ -937,7 +968,5 @@ def create_gradio_app():
937
  if __name__ == "__main__":
938
  app = create_gradio_app()
939
  app.launch(
940
- share=True,
941
- server_name="0.0.0.0",
942
- server_port=7860
943
  )
 
6
  from typing import List, Dict, Any, Optional, Union
7
  import threading
8
  import re
9
+ import aiohttp
10
+ import asyncio
11
 
12
  # Import Groq
13
  from groq import Groq
14
 
15
+ class ChutesClient:
16
+ """Client for interacting with Chutes API"""
17
+
18
+ def __init__(self, api_key: str):
19
+ self.api_key = api_key
20
+ self.base_url = "https://llm.chutes.ai/v1"
21
+
22
+ async def chat_completions_create(self, **kwargs) -> Dict:
23
+ """Make async request to Chutes chat completions endpoint"""
24
+ headers = {
25
+ "Authorization": f"Bearer {self.api_key}",
26
+ "Content-Type": "application/json"
27
+ }
28
+
29
+ # Prepare the body
30
+ body = {
31
+ "model": kwargs.get("model", "openai/gpt-oss-20b"),
32
+ "messages": kwargs.get("messages", []),
33
+ "stream": False, # Non-streaming for simplicity
34
+ "max_tokens": kwargs.get("max_tokens", 1024),
35
+ "temperature": kwargs.get("temperature", 0.7)
36
+ }
37
+
38
+ # Add tool calls if present
39
+ if "tools" in kwargs and kwargs["tools"]:
40
+ body["tools"] = kwargs["tools"]
41
+ body["tool_choice"] = kwargs.get("tool_choice", "auto")
42
+
43
+ async with aiohttp.ClientSession() as session:
44
+ async with session.post(
45
+ f"{self.base_url}/chat/completions",
46
+ headers=headers,
47
+ json=body
48
+ ) as response:
49
+ if response.status != 200:
50
+ raise Exception(f"Chutes API error: {await response.text()}")
51
+ return await response.json()
52
+
53
  class CreativeAgenticAI:
54
  """
55
+ Creative Agentic AI Chat Tool using Groq and Chutes models with browser search and compound models
56
  """
57
 
58
+ def __init__(self, groq_api_key: str, chutes_api_key: str, model: str = "compound-beta"):
59
  """
60
  Initialize the Creative Agentic AI system.
61
 
62
  Args:
63
+ groq_api_key: Groq API key
64
+ chutes_api_key: Chutes API key
65
+ model: Which model to use
66
  """
67
+ self.groq_api_key = groq_api_key
68
+ self.chutes_api_key = chutes_api_key
69
+ if not self.groq_api_key and model != "openai/gpt-oss-20b":
70
+ raise ValueError("No Groq API key provided")
71
+ if not self.chutes_api_key and model == "openai/gpt-oss-20b":
72
+ raise ValueError("No Chutes API key provided")
73
 
 
74
  self.model = model
75
+ self.groq_client = Groq(api_key=self.groq_api_key) if self.groq_api_key else None
76
+ self.chutes_client = ChutesClient(api_key=self.chutes_api_key) if self.chutes_api_key else None
77
  self.conversation_history = []
78
 
79
  # Available models with their capabilities
80
  self.available_models = {
81
+ "compound-beta": {"supports_web_search": True, "supports_browser_search": False, "api": "groq"},
82
+ "compound-beta-mini": {"supports_web_search": True, "supports_browser_search": False, "api": "groq"},
83
+ "openai/gpt-oss-20b": {"supports_web_search": False, "supports_browser_search": True, "api": "chutes"},
84
  }
85
 
86
+ async def chat(self, message: str,
87
+ include_domains: List[str] = None,
88
+ exclude_domains: List[str] = None,
89
+ system_prompt: str = None,
90
+ temperature: float = 0.7,
91
+ max_tokens: int = 1024,
92
+ search_type: str = "auto",
93
+ force_search: bool = False) -> Dict:
94
  """
95
  Send a message to the AI and get a response with flexible search options
96
 
 
168
  "messages": messages,
169
  "model": self.model,
170
  "temperature": temperature,
171
+ "max_tokens": max_tokens,
 
172
  }
173
 
174
  # Add domain filtering for compound models
 
198
  params["tool_choice"] = tool_choice
199
 
200
  try:
201
+ # Make the API call based on model
202
+ if self.available_models[self.model]["api"] == "chutes":
203
+ response = await self.chutes_client.chat_completions_create(**params)
204
+ else:
205
+ params["max_completion_tokens"] = params.pop("max_tokens", None)
206
+ response = self.groq_client.chat.completions.create(**params)
207
+
208
+ content = response["choices"][0]["message"]["content"]
209
 
210
  # Extract tool usage information and enhance it
211
  tool_info = self._extract_tool_info(response)
 
274
  "tool_name": getattr(tool, "name", "unknown"),
275
  }
276
 
 
277
  if hasattr(tool, "input"):
278
  tool_input = str(tool.input)
279
  tool_dict["input"] = tool_input
 
280
  if "search" in tool_dict["tool_name"].lower():
281
  tool_info["search_queries"].append(tool_input)
282
 
283
  if hasattr(tool, "output"):
284
  tool_output = str(tool.output)
285
  tool_dict["output"] = tool_output
 
286
  urls = self._extract_urls(tool_output)
287
  tool_info["sources_found"].extend(urls)
288
 
 
314
  """Extract URLs from text"""
315
  url_pattern = r'https?://[^\s<>"]{2,}'
316
  urls = re.findall(url_pattern, text)
317
+ return list(set(urls))
318
 
319
  def _enhance_citations(self, content: str, tool_info: Dict) -> str:
320
  """Enhance content with better citation formatting"""
321
  if not tool_info or not tool_info.get("sources_found"):
322
  return content
323
 
 
324
  if "Sources Used:" not in content and "sources:" not in content.lower():
325
  sources_section = "\n\n---\n\n### πŸ“š Sources Used:\n"
326
+ for i, url in enumerate(tool_info["sources_found"][:10], 1):
 
327
  domain = self._extract_domain(url)
328
  sources_section += f"{i}. [{domain}]({url})\n"
329
 
 
336
  try:
337
  if url.startswith(('http://', 'https://')):
338
  domain = url.split('/')[2]
 
339
  if domain.startswith('www.'):
340
  domain = domain[4:]
341
  return domain
 
365
  ai_instance = None
366
  api_key_status = "Not Set"
367
 
368
+ async def validate_api_keys(groq_api_key: str, chutes_api_key: str, model: str) -> str:
369
+ """Validate both Groq and Chutes API keys and initialize AI instance"""
370
  global ai_instance, api_key_status
371
 
372
+ if model == "openai/gpt-oss-20b" and not chutes_api_key:
373
  api_key_status = "Invalid ❌"
374
+ return "❌ Please enter a valid Chutes API key for the selected model"
375
+
376
+ if model in ["compound-beta", "compound-beta-mini"] and not groq_api_key:
377
+ api_key_status = "Invalid ❌"
378
+ return "❌ Please enter a valid Groq API key for the selected model"
379
 
380
  try:
381
+ # Test API keys based on model
382
+ if model == "openai/gpt-oss-20b":
383
+ chutes_client = ChutesClient(api_key=chutes_api_key)
384
+ await chutes_client.chat_completions_create(
385
+ messages=[{"role": "user", "content": "Hello"}],
386
+ model=model,
387
+ max_tokens=10
388
+ )
389
+ else:
390
+ groq_client = Groq(api_key=groq_api_key)
391
+ groq_client.chat.completions.create(
392
+ messages=[{"role": "user", "content": "Hello"}],
393
+ model=model,
394
+ max_tokens=10
395
+ )
396
 
397
  # Create AI instance
398
+ ai_instance = CreativeAgenticAI(groq_api_key=groq_api_key, chutes_api_key=chutes_api_key, model=model)
399
  api_key_status = "Valid βœ…"
400
 
401
  model_info = ai_instance.get_model_info()
 
407
 
408
  cap_text = " | ".join(capabilities) if capabilities else "πŸ’¬ Chat Only"
409
 
410
+ return f"βœ… API Keys Valid! NeuroScope AI is ready.\n\n**Model:** {model}\n**Capabilities:** {cap_text}\n**API:** {model_info.get('api', 'unknown')}\n**Status:** Connected and ready for chat!"
411
 
412
  except Exception as e:
413
  api_key_status = "Invalid ❌"
414
  ai_instance = None
415
+ return f"❌ Error validating API key: {str(e)}\n\nPlease check your API keys and try again."
416
 
417
  def update_model(model: str) -> str:
418
  """Update the model selection"""
 
428
  capabilities.append("πŸ” Browser Search Tools")
429
 
430
  cap_text = " | ".join(capabilities) if capabilities else "πŸ’¬ Chat Only"
431
+ return f"βœ… Model updated to: **{model}**\n**Capabilities:** {cap_text}\n**API:** {model_info.get('api', 'unknown')}"
432
  else:
433
+ return "⚠️ Please set your API keys first"
434
 
435
  def get_search_options(model: str) -> gr.update:
436
  """Get available search options based on model"""
 
445
  if model_info.get("supports_browser_search"):
446
  options.extend(["browser_search", "auto"])
447
 
 
448
  options = list(dict.fromkeys(options))
 
449
  default_value = "auto" if "auto" in options else "none"
450
  return gr.update(choices=options, value=default_value)
451
 
452
+ async def chat_with_ai(message: str,
453
+ include_domains: str,
454
+ exclude_domains: str,
455
+ system_prompt: str,
456
+ temperature: float,
457
+ max_tokens: int,
458
+ search_type: str,
459
+ force_search: bool,
460
+ history: List) -> tuple:
461
  """Main chat function"""
462
  global ai_instance
463
 
464
  if not ai_instance:
465
+ error_msg = "⚠️ Please set your API keys first!"
466
  history.append([message, error_msg])
467
  return history, ""
468
 
469
  if not message.strip():
470
  return history, ""
471
 
 
472
  include_list = [d.strip() for d in include_domains.split(",")] if include_domains.strip() else []
473
  exclude_list = [d.strip() for d in exclude_domains.split(",")] if exclude_domains.strip() else []
474
 
475
  try:
476
+ response = await ai_instance.chat(
 
477
  message=message,
478
  include_domains=include_list if include_list else None,
479
  exclude_domains=exclude_list if exclude_list else None,
 
484
  force_search=force_search
485
  )
486
 
 
487
  ai_response = response["content"]
488
 
 
489
  if response.get("tool_usage"):
490
  tool_info = response["tool_usage"]
491
  tool_summary = []
 
504
  if tool_summary:
505
  ai_response += f"\n\n*{' | '.join(tool_summary)}*"
506
 
 
507
  search_info = []
508
  if response.get("search_type_used") and response["search_type_used"] != "none":
509
  search_info.append(f"πŸ” Search type: {response['search_type_used']}")
 
511
  if force_search:
512
  search_info.append("⚑ Forced search enabled")
513
 
 
514
  if include_list or exclude_list:
515
  filter_info = []
516
  if include_list:
 
522
  if search_info:
523
  ai_response += f"\n\n*🌐 Search settings: {' | '.join(search_info)}*"
524
 
 
525
  history.append([message, ai_response])
526
 
527
  return history, ""
 
541
  def create_gradio_app():
542
  """Create the main Gradio application"""
543
 
 
544
  css = """
545
  .container {
546
  max-width: 1200px;
 
596
  """
597
 
598
  with gr.Blocks(css=css, title="πŸ€– Creative Agentic AI Chat", theme=gr.themes.Ocean()) as app:
 
599
  gr.HTML("""
600
  <div class="header">
601
  <h1>πŸ€– NeuroScope-AI Enhanced</h1>
602
+ <p>Powered by Groq and Chutes Models with Web Search, Browser Search & Agentic Capabilities</p>
603
  </div>
604
  """)
605
 
 
606
  with gr.Group():
607
  with gr.Accordion("πŸ€– NeuroScope AI Enhanced", open=False, elem_id="neuroscope-accordion"):
608
  gr.Markdown("""
 
614
  - 🎯 **Model Flexibility**: Choose the right model for your task
615
  """)
616
 
 
617
  with gr.Group():
618
  with gr.Accordion("πŸ” IMPORTANT - Enhanced Search Capabilities!", open=True, elem_id="neuroscope-accordion"):
619
  gr.Markdown("""
620
  <div class="search-info">
621
  <h3>πŸš€ NEW: Multiple Search Types Available!</h3>
622
 
623
+ <h4>🌐 Web Search Models (Groq API)</h4>
624
  <ul>
625
  <li><strong>compound-beta:</strong> Most powerful with domain filtering</li>
626
  <li><strong>compound-beta-mini:</strong> Faster with domain filtering</li>
627
  <li><strong>Features:</strong> Include/exclude domains, autonomous web search</li>
628
  </ul>
629
 
630
+ <h4>πŸ” Browser Search Models (Chutes API)</h4>
631
  <ul>
632
  <li><strong>openai/gpt-oss-20b:</strong> Fast browser search capabilities</li>
 
 
 
633
  <li><strong>Features:</strong> Real-time browser search, current information</li>
634
  </ul>
635
  </div>
 
646
  </div>
647
  """)
648
 
 
649
  with gr.Row():
650
  with gr.Column(scale=2):
651
+ groq_api_key = gr.Textbox(
652
  label="πŸ”‘ Groq API Key",
653
  placeholder="Enter your Groq API key here...",
654
  type="password",
655
  info="Get your API key from: https://console.groq.com/"
656
  )
657
+ chutes_api_key = gr.Textbox(
658
+ label="πŸ”‘ Chutes API Key",
659
+ placeholder="Enter your Chutes API key here...",
660
+ type="password",
661
+ info="Required for openai/gpt-oss-20b model"
662
+ )
663
  with gr.Column(scale=2):
664
  model_selection = gr.Radio(
665
  choices=[
 
674
  with gr.Column(scale=1):
675
  connect_btn = gr.Button("πŸ”— Connect", variant="primary", size="lg")
676
 
 
677
  status_display = gr.Markdown("### πŸ“Š Status: Not connected", elem_classes=["status-box"])
678
 
 
679
  connect_btn.click(
680
+ fn=validate_api_keys,
681
+ inputs=[groq_api_key, chutes_api_key, model_selection],
682
  outputs=[status_display]
683
  )
684
 
 
688
  outputs=[status_display]
689
  )
690
 
 
691
  with gr.Tab("πŸ’¬ Chat"):
692
  chatbot = gr.Chatbot(
693
  label="Creative AI Assistant with Enhanced Search",
 
707
  send_btn = gr.Button("πŸ“€ Send", variant="primary")
708
  clear_btn = gr.Button("πŸ—‘οΈ Clear", variant="secondary")
709
 
 
710
  with gr.Accordion("πŸ” Search Settings", open=False, elem_id="neuroscope-accordion"):
711
  with gr.Row():
712
  search_type = gr.Radio(
 
721
  info="Force AI to search even for general questions"
722
  )
723
 
 
724
  model_selection.change(
725
  fn=get_search_options,
726
  inputs=[model_selection],
727
  outputs=[search_type]
728
  )
729
 
 
730
  with gr.Accordion("🌐 Domain Filtering (Web Search Models Only)", open=False, elem_id="neuroscope-accordion"):
731
  gr.Markdown("""
732
  <div class="domain-info">
 
753
  info="Never search these domains (compound models only)"
754
  )
755
 
 
756
  with gr.Accordion("βš™οΈ Advanced Settings", open=False, elem_id="neuroscope-accordion"):
757
  with gr.Row():
758
  temperature = gr.Slider(
 
779
  info="Leave empty to use default creative assistant prompt with enhanced citations"
780
  )
781
 
 
782
  with gr.Accordion("πŸ“Š Model Comparison Guide", open=False, elem_id="neuroscope-accordion"):
783
  gr.Markdown("""
784
  ### πŸ” Choose Your Model Based on Task:
 
786
  **For Academic Research & Domain-Specific Search:**
787
  - `compound-beta` or `compound-beta-mini` with include domains (*.edu, arxiv.org)
788
  - Best for: Research papers, academic sources, filtered searches
789
+ - API: Groq
790
 
791
  **For Current Events & Real-Time Information:**
792
+ - `openai/gpt-oss-20b` with browser search
793
  - Best for: News, current events, real-time data
794
+ - API: Chutes
795
 
796
  **For General Knowledge & Creative Tasks:**
797
  - Any model with search type = "auto" or "none"
798
  - Best for: Creative writing, general questions, analysis
799
 
800
  **For Programming & Technical Documentation:**
801
+ - `openai/gpt-oss-20b` with browser search, or compound models with tech domains
802
  - Best for: Code help, documentation, technical guides
803
  """)
804
 
 
805
  with gr.Accordion("πŸ”— Common Domain Examples", open=False, elem_id="neuroscope-accordion"):
806
  gr.Markdown("""
807
  **Academic & Research:**
 
823
  - `*.gov`, `*.org`, `un.org`, `worldbank.org`, `imf.org`
824
  """)
825
 
 
826
  with gr.Accordion("πŸ“– How to Use This Enhanced App", open=False, elem_id="neuroscope-accordion"):
827
  gr.Markdown("""
828
  ### πŸš€ Getting Started
829
+ 1. **Enter your API Keys** - Groq from [console.groq.com](https://console.groq.com/), Chutes for openai/gpt-oss-20b
830
  2. **Select a model** - Choose based on your search needs:
831
+ - **Compound models** (Groq): For web search with domain filtering
832
+ - **openai/gpt-oss-20b** (Chutes): For browser search with real-time data
833
  3. **Configure search settings** - Choose search type and options
834
+ 4. **Click Connect** - Validate your keys and connect to the AI
835
  5. **Start chatting!** - Type your message and get intelligent responses with citations
836
 
837
  ### 🎯 Key Features
838
+ - **Dual Search Capabilities**: Web search (compound) + Browser search (Chutes)
839
  - **Smart Citations**: Automatic source linking and citation formatting
840
  - **Domain Filtering**: Control which websites the AI searches (compound models)
841
  - **Real-time Search**: Get current information with browser search tools
842
+ - **Model Flexibility**: Choose the right model and API for your task
843
  - **Enhanced Tool Visibility**: See exactly what search tools were used
844
 
845
  ### πŸ’‘ Tips for Best Results
 
850
  - Use "Force Search" for the most current information
851
 
852
  **For Current Events:**
853
+ - Use openai/gpt-oss-20b (Chutes)
854
  - Set search type to "browser_search"
855
  - Enable "Force Search" for real-time data
856
 
 
860
  - Use higher temperature (0.8-1.0) for more creativity
861
  """)
862
 
 
863
  with gr.Accordion("🎯 Sample Examples to Test Enhanced Search", open=False, elem_id="neuroscope-accordion"):
864
  gr.Markdown("""
865
  <div class="example-box">
866
  <h4>πŸ”¬ Research & Analysis (Test Different Models)</h4>
867
 
868
+ **Compound Model + Domain Filtering (Groq):**
869
  - Query: "What are the latest breakthroughs in quantum computing?"
870
  - Model: compound-beta
871
  - Include domains: "arxiv.org, *.edu, nature.com"
872
  - Search type: web_search
873
 
874
+ **Browser Search Model (Chutes):**
875
  - Same query with openai/gpt-oss-20b
876
  - Search type: browser_search
877
  - Force search: enabled
 
880
 
881
  **Real-time News:**
882
  - Query: "What happened in AI industry this week?"
883
+ - Model: openai/gpt-oss-20b (Chutes)
884
  - Search type: browser_search
885
  - Force search: enabled
886
 
887
  **Compare with Web Search:**
888
+ - Same query with compound-beta (Groq)
889
  - Include domains: "reuters.com, bbc.com, techcrunch.com"
890
 
891
  <h4>πŸ’» Programming & Tech (Model Comparison)</h4>
 
894
  - Query: "How to implement OAuth 2.0 in Python Flask?"
895
  - Try with both model types:
896
  - compound-beta with "github.com, docs.python.org, stackoverflow.com"
897
+ - openai/gpt-oss-20b (Chutes) with browser_search
898
 
899
  <h4>🎨 Creative Tasks (No Search Needed)</h4>
900
  - Query: "Write a short story about AI and humans working together"
 
905
 
906
  **Financial Data (Real-time):**
907
  - Query: "Current cryptocurrency market trends"
908
+ - Model: openai/gpt-oss-20b (Chutes)
909
  - Search type: browser_search
910
  - Force search: enabled
911
 
912
  **Business Analysis (Filtered):**
913
  - Query: "Cryptocurrency adoption in enterprise"
914
+ - Model: compound-beta (Groq)
915
  - Include domains: "bloomberg.com, wsj.com, harvard.edu"
916
  </div>
917
  """)
918
 
 
919
  send_btn.click(
920
  fn=chat_with_ai,
921
  inputs=[msg, include_domains, exclude_domains, system_prompt, temperature, max_tokens, search_type, force_search, chatbot],
 
933
  outputs=[chatbot]
934
  )
935
 
 
936
  with gr.Accordion("πŸš€ About This Enhanced NeuroScope AI", open=True, elem_id="neuroscope-accordion"):
937
  gr.Markdown("""
938
  **Enhanced Creative Agentic AI Chat Tool** with dual search capabilities:
939
 
940
  ### πŸ†• **New in This Version:**
941
+ - πŸ” **Browser Search Integration**: Real-time search with Chutes API
942
+ - 🌐 **Dual Search System**: Web search (Groq) + Browser search (Chutes)
943
+ - 🎯 **Model Flexibility**: Multiple models across two APIs
944
  - ⚑ **Force Search Option**: Make AI search even for general questions
945
  - πŸ”§ **Enhanced Tool Visibility**: See exactly what search tools were used
946
+ - πŸ“Š **Model Comparison Guide**: Choose the right model and API for your task
947
 
948
  ### πŸ† **Core Features:**
949
  - πŸ”— **Automatic Source Citations**: Every response includes clickable links to sources
950
  - πŸ“š **Sources Used Section**: Dedicated section showing all websites referenced
951
  - 🌐 **Smart Domain Filtering**: Control search scope (compound models)
952
+ - πŸ” **Real-time Browser Search**: Current information (Chutes model)
953
  - πŸ’¬ **Conversational Memory**: Maintains context throughout the session
954
  - βš™οΈ **Full Customization**: Adjust all parameters and prompts
955
  - 🎨 **Creative & Analytical**: Optimized for both creative and research tasks
956
 
957
  ### πŸ› οΈ **Technical Details:**
958
+ - **Compound Models (Groq)**: compound-beta, compound-beta-mini (web search + domain filtering)
959
+ - **Tool-based Model (Chutes)**: openai/gpt-oss-20b (browser search tools)
960
  - **Automatic Search Type Detection**: AI chooses best search method
961
  - **Enhanced Error Handling**: Robust error management and user feedback
962
  - **Real-time Status Updates**: Live feedback on model capabilities and search settings
 
968
  if __name__ == "__main__":
969
  app = create_gradio_app()
970
  app.launch(
971
+ share=True
 
 
972
  )