shukdevdattaEX commited on
Commit
a5263a0
Β·
verified Β·
1 Parent(s): d21e7f5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1022 -0
app.py CHANGED
@@ -0,0 +1,1022 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import time
4
+ import gradio as gr
5
+ from datetime import datetime
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 CreativeAgenticAI:
16
+ """
17
+ Creative Agentic AI Chat Tool using multiple providers (Groq and Chutes)
18
+ """
19
+
20
+ def __init__(self, groq_api_key: str = None, chutes_api_key: str = None, provider: str = "groq", model: str = None):
21
+ """
22
+ Initialize the Creative Agentic AI system.
23
+
24
+ Args:
25
+ groq_api_key: Groq API key
26
+ chutes_api_key: Chutes API key
27
+ provider: Which provider to use ('groq' or 'chutes')
28
+ model: Which model to use
29
+ """
30
+ self.groq_api_key = groq_api_key
31
+ self.chutes_api_key = chutes_api_key
32
+ self.provider = provider
33
+ self.conversation_history = []
34
+
35
+ # Initialize clients based on provider
36
+ if provider == "groq" and groq_api_key:
37
+ if not groq_api_key:
38
+ raise ValueError("No Groq API key provided")
39
+ self.groq_client = Groq(api_key=groq_api_key)
40
+ self.model = model or "compound-beta"
41
+ elif provider == "chutes" and chutes_api_key:
42
+ if not chutes_api_key:
43
+ raise ValueError("No Chutes API key provided")
44
+ self.model = model or "openai/gpt-oss-20b"
45
+ else:
46
+ raise ValueError(f"Invalid provider or missing API key for {provider}")
47
+
48
+ async def _chutes_chat_async(self, messages: List[Dict], temperature: float = 0.7, max_tokens: int = 1024) -> str:
49
+ """
50
+ Async method for Chutes API chat
51
+ """
52
+ headers = {
53
+ "Authorization": f"Bearer {self.chutes_api_key}",
54
+ "Content-Type": "application/json"
55
+ }
56
+
57
+ body = {
58
+ "model": self.model,
59
+ "messages": messages,
60
+ "stream": False, # Set to False for simpler handling
61
+ "max_tokens": max_tokens,
62
+ "temperature": temperature
63
+ }
64
+
65
+ async with aiohttp.ClientSession() as session:
66
+ async with session.post(
67
+ "https://llm.chutes.ai/v1/chat/completions",
68
+ headers=headers,
69
+ json=body
70
+ ) as response:
71
+ if response.status == 200:
72
+ result = await response.json()
73
+ return result['choices'][0]['message']['content']
74
+ else:
75
+ error_text = await response.text()
76
+ raise Exception(f"Chutes API error: {response.status} - {error_text}")
77
+
78
+ def _chutes_chat_sync(self, messages: List[Dict], temperature: float = 0.7, max_tokens: int = 1024) -> str:
79
+ """
80
+ Synchronous wrapper for Chutes API chat
81
+ """
82
+ try:
83
+ loop = asyncio.get_event_loop()
84
+ except RuntimeError:
85
+ loop = asyncio.new_event_loop()
86
+ asyncio.set_event_loop(loop)
87
+
88
+ return loop.run_until_complete(
89
+ self._chutes_chat_async(messages, temperature, max_tokens)
90
+ )
91
+
92
+ def chat(self, message: str,
93
+ include_domains: List[str] = None,
94
+ exclude_domains: List[str] = None,
95
+ system_prompt: str = None,
96
+ temperature: float = 0.7,
97
+ max_tokens: int = 1024) -> Dict:
98
+ """
99
+ Send a message to the AI and get a response
100
+
101
+ Args:
102
+ message: User's message
103
+ include_domains: List of domains to include for web search (Groq only)
104
+ exclude_domains: List of domains to exclude from web search (Groq only)
105
+ system_prompt: Custom system prompt
106
+ temperature: Model temperature (0.0-2.0)
107
+ max_tokens: Maximum tokens in response
108
+
109
+ Returns:
110
+ AI response with metadata
111
+ """
112
+ # Enhanced system prompt for better citation behavior
113
+ if not system_prompt:
114
+ if self.provider == "groq":
115
+ citation_instruction = """
116
+ IMPORTANT: When you search the web and find information, you MUST:
117
+ 1. Always cite your sources with clickable links in this format: [Source Title](URL)
118
+ 2. Include multiple diverse sources when possible
119
+ 3. Show which specific websites you used for each claim
120
+ 4. At the end of your response, provide a "Sources Used" section with all the links
121
+ 5. Be transparent about which information comes from which source
122
+ """
123
+
124
+ domain_context = ""
125
+ if include_domains:
126
+ domain_context = f"\nYou are restricted to searching ONLY these domains: {', '.join(include_domains)}. Make sure to find and cite sources specifically from these domains."
127
+ elif exclude_domains:
128
+ domain_context = f"\nAvoid searching these domains: {', '.join(exclude_domains)}. Search everywhere else on the web."
129
+
130
+ system_prompt = f"""You are a creative and intelligent AI assistant with agentic capabilities.
131
+ You can search the web, analyze information, and provide comprehensive responses.
132
+ Be helpful, creative, and engaging while maintaining accuracy.
133
+
134
+ {citation_instruction}
135
+ {domain_context}
136
+
137
+ Your responses should be well-structured, informative, and properly cited with working links."""
138
+ else:
139
+ # Simpler system prompt for Chutes (no web search capabilities)
140
+ system_prompt = """You are a creative and intelligent AI assistant.
141
+ Be helpful, creative, and engaging while maintaining accuracy.
142
+ Your responses should be well-structured, informative, and comprehensive."""
143
+
144
+ # Build messages
145
+ messages = [{"role": "system", "content": system_prompt}]
146
+
147
+ # Add conversation history (last 10 exchanges)
148
+ messages.extend(self.conversation_history[-20:]) # Last 10 user-assistant pairs
149
+
150
+ # Add current message with domain filtering context (Groq only)
151
+ enhanced_message = message
152
+ if self.provider == "groq" and (include_domains or exclude_domains):
153
+ filter_context = []
154
+ if include_domains:
155
+ filter_context.append(f"ONLY search these domains: {', '.join(include_domains)}")
156
+ if exclude_domains:
157
+ filter_context.append(f"EXCLUDE these domains: {', '.join(exclude_domains)}")
158
+ enhanced_message += f"\n\n[Domain Filtering: {' | '.join(filter_context)}]"
159
+
160
+ messages.append({"role": "user", "content": enhanced_message})
161
+
162
+ try:
163
+ if self.provider == "groq":
164
+ return self._handle_groq_chat(messages, include_domains, exclude_domains, temperature, max_tokens, message)
165
+ elif self.provider == "chutes":
166
+ return self._handle_chutes_chat(messages, temperature, max_tokens, message)
167
+ else:
168
+ raise ValueError(f"Unknown provider: {self.provider}")
169
+
170
+ except Exception as e:
171
+ error_msg = f"Error: {str(e)}"
172
+ self.conversation_history.append({"role": "user", "content": message})
173
+ self.conversation_history.append({"role": "assistant", "content": error_msg})
174
+
175
+ return {
176
+ "content": error_msg,
177
+ "timestamp": datetime.now().isoformat(),
178
+ "model": self.model,
179
+ "provider": self.provider,
180
+ "tool_usage": None,
181
+ "error": str(e)
182
+ }
183
+
184
+ def _handle_groq_chat(self, messages: List[Dict], include_domains: List[str], exclude_domains: List[str],
185
+ temperature: float, max_tokens: int, original_message: str) -> Dict:
186
+ """Handle Groq API chat"""
187
+ # Set up API parameters
188
+ params = {
189
+ "messages": messages,
190
+ "model": self.model,
191
+ "temperature": temperature,
192
+ "max_tokens": max_tokens
193
+ }
194
+
195
+ # Add domain filtering if specified
196
+ if include_domains and include_domains[0].strip():
197
+ params["include_domains"] = [domain.strip() for domain in include_domains if domain.strip()]
198
+ if exclude_domains and exclude_domains[0].strip():
199
+ params["exclude_domains"] = [domain.strip() for domain in exclude_domains if domain.strip()]
200
+
201
+ # Make the API call
202
+ response = self.groq_client.chat.completions.create(**params)
203
+ content = response.choices[0].message.content
204
+
205
+ # Extract tool usage information and enhance it
206
+ tool_info = self._extract_tool_info(response)
207
+
208
+ # Process content to enhance citations
209
+ processed_content = self._enhance_citations(content, tool_info)
210
+
211
+ # Add to conversation history
212
+ self.conversation_history.append({"role": "user", "content": original_message})
213
+ self.conversation_history.append({"role": "assistant", "content": processed_content})
214
+
215
+ # Create response object
216
+ return {
217
+ "content": processed_content,
218
+ "timestamp": datetime.now().isoformat(),
219
+ "model": self.model,
220
+ "provider": "groq",
221
+ "tool_usage": tool_info,
222
+ "parameters": {
223
+ "temperature": temperature,
224
+ "max_tokens": max_tokens,
225
+ "include_domains": include_domains,
226
+ "exclude_domains": exclude_domains
227
+ }
228
+ }
229
+
230
+ def _handle_chutes_chat(self, messages: List[Dict], temperature: float, max_tokens: int, original_message: str) -> Dict:
231
+ """Handle Chutes API chat"""
232
+ content = self._chutes_chat_sync(messages, temperature, max_tokens)
233
+
234
+ # Add to conversation history
235
+ self.conversation_history.append({"role": "user", "content": original_message})
236
+ self.conversation_history.append({"role": "assistant", "content": content})
237
+
238
+ # Create response object
239
+ return {
240
+ "content": content,
241
+ "timestamp": datetime.now().isoformat(),
242
+ "model": self.model,
243
+ "provider": "chutes",
244
+ "tool_usage": None, # Chutes doesn't have tool usage info
245
+ "parameters": {
246
+ "temperature": temperature,
247
+ "max_tokens": max_tokens
248
+ }
249
+ }
250
+
251
+ def _extract_tool_info(self, response) -> Dict:
252
+ """Extract tool usage information in a JSON serializable format (Groq only)"""
253
+ tool_info = {
254
+ "tools_used": [],
255
+ "search_queries": [],
256
+ "sources_found": []
257
+ }
258
+
259
+ if hasattr(response.choices[0].message, 'executed_tools'):
260
+ tools = response.choices[0].message.executed_tools
261
+ if tools:
262
+ for tool in tools:
263
+ tool_dict = {
264
+ "tool_type": getattr(tool, "type", "unknown"),
265
+ "tool_name": getattr(tool, "name", "unknown"),
266
+ }
267
+
268
+ # Extract search queries and results
269
+ if hasattr(tool, "input"):
270
+ tool_input = str(tool.input)
271
+ tool_dict["input"] = tool_input
272
+ # Try to extract search query
273
+ if "search" in tool_dict["tool_name"].lower():
274
+ tool_info["search_queries"].append(tool_input)
275
+
276
+ if hasattr(tool, "output"):
277
+ tool_output = str(tool.output)
278
+ tool_dict["output"] = tool_output
279
+ # Try to extract URLs from output
280
+ urls = self._extract_urls(tool_output)
281
+ tool_info["sources_found"].extend(urls)
282
+
283
+ tool_info["tools_used"].append(tool_dict)
284
+
285
+ return tool_info
286
+
287
+ def _extract_urls(self, text: str) -> List[str]:
288
+ """Extract URLs from text"""
289
+ url_pattern = r'https?://[^\s<>"]{2,}'
290
+ urls = re.findall(url_pattern, text)
291
+ return list(set(urls)) # Remove duplicates
292
+
293
+ def _enhance_citations(self, content: str, tool_info: Dict) -> str:
294
+ """Enhance content with better citation formatting (Groq only)"""
295
+ if not tool_info or not tool_info.get("sources_found"):
296
+ return content
297
+
298
+ # Add sources section if not already present
299
+ if "Sources Used:" not in content and "sources:" not in content.lower():
300
+ sources_section = "\n\n---\n\n### πŸ“š Sources Used:\n"
301
+ for i, url in enumerate(tool_info["sources_found"][:10], 1): # Limit to 10 sources
302
+ # Try to extract domain name for better formatting
303
+ domain = self._extract_domain(url)
304
+ sources_section += f"{i}. [{domain}]({url})\n"
305
+
306
+ content += sources_section
307
+
308
+ return content
309
+
310
+ def _extract_domain(self, url: str) -> str:
311
+ """Extract domain name from URL for display"""
312
+ try:
313
+ if url.startswith(('http://', 'https://')):
314
+ domain = url.split('/')[2]
315
+ # Remove www. prefix if present
316
+ if domain.startswith('www.'):
317
+ domain = domain[4:]
318
+ return domain
319
+ return url
320
+ except:
321
+ return url
322
+
323
+ def clear_history(self):
324
+ """Clear conversation history"""
325
+ self.conversation_history = []
326
+
327
+ def get_history_summary(self) -> str:
328
+ """Get a summary of conversation history"""
329
+ if not self.conversation_history:
330
+ return "No conversation history"
331
+
332
+ user_messages = [msg for msg in self.conversation_history if msg["role"] == "user"]
333
+ assistant_messages = [msg for msg in self.conversation_history if msg["role"] == "assistant"]
334
+
335
+ return f"Conversation: {len(user_messages)} user messages, {len(assistant_messages)} assistant responses"
336
+
337
+ # Global variables
338
+ ai_instance = None
339
+ current_provider = "groq"
340
+ api_key_status = {"groq": "Not Set", "chutes": "Not Set"}
341
+
342
+ def validate_api_keys(groq_api_key: str, chutes_api_key: str, provider: str, model: str) -> str:
343
+ """Validate API keys and initialize AI instance"""
344
+ global ai_instance, current_provider, api_key_status
345
+
346
+ current_provider = provider
347
+
348
+ if provider == "groq":
349
+ if not groq_api_key or len(groq_api_key.strip()) < 10:
350
+ api_key_status["groq"] = "Invalid ❌"
351
+ return "❌ Please enter a valid Groq API key (should be longer than 10 characters)"
352
+
353
+ try:
354
+ # Test the Groq API key
355
+ client = Groq(api_key=groq_api_key)
356
+ test_response = client.chat.completions.create(
357
+ messages=[{"role": "user", "content": "Hello"}],
358
+ model=model,
359
+ max_tokens=10
360
+ )
361
+
362
+ # Create AI instance
363
+ ai_instance = CreativeAgenticAI(groq_api_key=groq_api_key, provider="groq", model=model)
364
+ api_key_status["groq"] = "Valid βœ…"
365
+
366
+ return f"βœ… Groq API Key Valid! Creative Agentic AI is ready.\n\n**Provider:** Groq\n**Model:** {model}\n**Status:** Connected with web search capabilities!"
367
+
368
+ except Exception as e:
369
+ api_key_status["groq"] = "Invalid ❌"
370
+ ai_instance = None
371
+ return f"❌ Error validating Groq API key: {str(e)}\n\nPlease check your API key and try again."
372
+
373
+ elif provider == "chutes":
374
+ if not chutes_api_key or len(chutes_api_key.strip()) < 10:
375
+ api_key_status["chutes"] = "Invalid ❌"
376
+ return "❌ Please enter a valid Chutes API key (should be longer than 10 characters)"
377
+
378
+ try:
379
+ # Test the Chutes API key with a simple request
380
+ test_ai = CreativeAgenticAI(chutes_api_key=chutes_api_key, provider="chutes", model=model)
381
+ test_response = test_ai._chutes_chat_sync(
382
+ [{"role": "user", "content": "Hello"}],
383
+ temperature=0.7,
384
+ max_tokens=10
385
+ )
386
+
387
+ # Create AI instance
388
+ ai_instance = CreativeAgenticAI(chutes_api_key=chutes_api_key, provider="chutes", model=model)
389
+ api_key_status["chutes"] = "Valid βœ…"
390
+
391
+ return f"βœ… Chutes API Key Valid! Creative AI is ready.\n\n**Provider:** Chutes\n**Model:** {model}\n**Status:** Connected (text generation focused)!"
392
+
393
+ except Exception as e:
394
+ api_key_status["chutes"] = "Invalid ❌"
395
+ ai_instance = None
396
+ return f"❌ Error validating Chutes API key: {str(e)}\n\nPlease check your API key and try again."
397
+
398
+ def get_available_models(provider: str) -> List[str]:
399
+ """Get available models for the selected provider"""
400
+ if provider == "groq":
401
+ return ["compound-beta", "compound-beta-mini"]
402
+ elif provider == "chutes":
403
+ return ["openai/gpt-oss-20b", "meta-llama/llama-3.1-8b-instruct", "anthropic/claude-3-sonnet"]
404
+ return []
405
+
406
+ def update_model_choices(provider: str):
407
+ """Update model choices based on provider selection"""
408
+ models = get_available_models(provider)
409
+ return gr.Radio(choices=models, value=models[0] if models else None, label=f"🧠 {provider.title()} Models")
410
+
411
+ def chat_with_ai(message: str,
412
+ include_domains: str,
413
+ exclude_domains: str,
414
+ system_prompt: str,
415
+ temperature: float,
416
+ max_tokens: int,
417
+ history: List) -> tuple:
418
+ """Main chat function"""
419
+ global ai_instance, current_provider
420
+
421
+ if not ai_instance:
422
+ error_msg = f"⚠️ Please set your {current_provider.title()} API key first!"
423
+ history.append([message, error_msg])
424
+ return history, ""
425
+
426
+ if not message.strip():
427
+ return history, ""
428
+
429
+ # Process domain lists (only for Groq)
430
+ include_list = None
431
+ exclude_list = None
432
+ if current_provider == "groq":
433
+ include_list = [d.strip() for d in include_domains.split(",")] if include_domains.strip() else []
434
+ exclude_list = [d.strip() for d in exclude_domains.split(",")] if exclude_domains.strip() else []
435
+
436
+ try:
437
+ # Get AI response
438
+ response = ai_instance.chat(
439
+ message=message,
440
+ include_domains=include_list if include_list else None,
441
+ exclude_domains=exclude_list if exclude_list else None,
442
+ system_prompt=system_prompt if system_prompt.strip() else None,
443
+ temperature=temperature,
444
+ max_tokens=int(max_tokens)
445
+ )
446
+
447
+ # Format response
448
+ ai_response = response["content"]
449
+
450
+ # Add enhanced tool usage info (Groq only)
451
+ if response.get("tool_usage") and current_provider == "groq":
452
+ tool_info = response["tool_usage"]
453
+ tool_summary = []
454
+
455
+ if tool_info.get("search_queries"):
456
+ tool_summary.append(f"πŸ” Search queries: {len(tool_info['search_queries'])}")
457
+
458
+ if tool_info.get("sources_found"):
459
+ tool_summary.append(f"πŸ“„ Sources found: {len(tool_info['sources_found'])}")
460
+
461
+ if tool_info.get("tools_used"):
462
+ tool_summary.append(f"πŸ”§ Tools used: {len(tool_info['tools_used'])}")
463
+
464
+ if tool_summary:
465
+ ai_response += f"\n\n*{' | '.join(tool_summary)}*"
466
+
467
+ # Add domain filtering info (Groq only)
468
+ if current_provider == "groq" and (include_list or exclude_list):
469
+ filter_info = []
470
+ if include_list:
471
+ filter_info.append(f"βœ… Included domains: {', '.join(include_list)}")
472
+ if exclude_list:
473
+ filter_info.append(f"❌ Excluded domains: {', '.join(exclude_list)}")
474
+
475
+ ai_response += f"\n\n*🌐 Domain filtering applied: {' | '.join(filter_info)}*"
476
+
477
+ # Add provider info
478
+ ai_response += f"\n\n*πŸ€– Powered by: {current_provider.title()} ({response.get('model', 'unknown')})*"
479
+
480
+ # Add to history
481
+ history.append([message, ai_response])
482
+
483
+ return history, ""
484
+
485
+ except Exception as e:
486
+ error_msg = f"❌ Error: {str(e)}"
487
+ history.append([message, error_msg])
488
+ return history, ""
489
+
490
+ def clear_chat_history():
491
+ """Clear the chat history"""
492
+ global ai_instance
493
+ if ai_instance:
494
+ ai_instance.clear_history()
495
+ return []
496
+
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;
504
+ margin: 0 auto;
505
+ }
506
+ .header {
507
+ text-align: center;
508
+ background: linear-gradient(to right, #00ff94, #00b4db);
509
+ color: white;
510
+ padding: 20px;
511
+ border-radius: 10px;
512
+ margin-bottom: 20px;
513
+ }
514
+ .status-box {
515
+ background-color: #f8f9fa;
516
+ border: 1px solid #dee2e6;
517
+ border-radius: 8px;
518
+ padding: 15px;
519
+ margin: 10px 0;
520
+ }
521
+ .example-box {
522
+ background-color: #e8f4fd;
523
+ border-left: 4px solid #007bff;
524
+ padding: 15px;
525
+ margin: 10px 0;
526
+ border-radius: 0 8px 8px 0;
527
+ }
528
+ .domain-info {
529
+ background-color: #fff3cd;
530
+ border: 1px solid #ffeaa7;
531
+ border-radius: 8px;
532
+ padding: 15px;
533
+ margin: 10px 0;
534
+ }
535
+ .citation-info {
536
+ background-color: #d1ecf1;
537
+ border: 1px solid #bee5eb;
538
+ border-radius: 8px;
539
+ padding: 15px;
540
+ margin: 10px 0;
541
+ }
542
+ .provider-info {
543
+ background-color: #f8d7da;
544
+ border: 1px solid #f5c6cb;
545
+ border-radius: 8px;
546
+ padding: 15px;
547
+ margin: 10px 0;
548
+ }
549
+ #neuroscope-accordion {
550
+ background: linear-gradient(to right, #00ff94, #00b4db);
551
+ border-radius: 8px;
552
+ }
553
+ """
554
+
555
+ with gr.Blocks(css=css, title="πŸ€– Multi-Provider Creative Agentic AI Chat", theme=gr.themes.Ocean()) as app:
556
+
557
+ # Header
558
+ gr.HTML("""
559
+ <div class="header">
560
+ <h1>πŸ€– NeuroScope-AI Enhanced</h1>
561
+ <p>Multi-Provider AI Chat Tool - Powered by Groq's Compound Models & Chutes API</p>
562
+ </div>
563
+ """)
564
+
565
+ # Provider Selection
566
+ with gr.Group():
567
+ with gr.Accordion("πŸ€– Multi-Provider NeuroScope AI", open=False, elem_id="neuroscope-accordion"):
568
+ gr.Markdown("""
569
+ **Enhanced with Multiple AI Providers:**
570
+ - 🧠 Intelligence (Neuro) - Now supports Groq & Chutes
571
+ - πŸ” Advanced capabilities (Scope) - Web search with Groq, powerful text generation with Chutes
572
+ - πŸ€– AI capabilities (AI) - Multiple model options
573
+ - ⚑ Precision & Speed (Scope) - Choose the best provider for your needs
574
+ """)
575
+
576
+ # Provider and API Key Section
577
+ with gr.Row():
578
+ with gr.Column():
579
+ provider_selection = gr.Radio(
580
+ choices=["groq", "chutes"],
581
+ label="🏒 AI Provider",
582
+ value="groq",
583
+ info="Choose your AI provider"
584
+ )
585
+
586
+ # API Key inputs
587
+ groq_api_key = gr.Textbox(
588
+ label="πŸ”‘ Groq API Key",
589
+ placeholder="Enter your Groq API key here...",
590
+ type="password",
591
+ info="Get your API key from: https://console.groq.com/",
592
+ visible=True
593
+ )
594
+
595
+ chutes_api_key = gr.Textbox(
596
+ label="πŸ”‘ Chutes API Key",
597
+ placeholder="Enter your Chutes API key here...",
598
+ type="password",
599
+ info="Get your API key from: https://chutes.ai/",
600
+ visible=False
601
+ )
602
+
603
+ with gr.Column():
604
+ model_selection = gr.Radio(
605
+ choices=get_available_models("groq"),
606
+ label="🧠 Groq Models",
607
+ value="compound-beta",
608
+ info="compound-beta: More powerful | compound-beta-mini: Faster"
609
+ )
610
+
611
+ connect_btn = gr.Button("πŸ”— Connect", variant="primary", size="lg")
612
+
613
+ # Status display
614
+ status_display = gr.Markdown("### πŸ“Š Status: Not connected", elem_classes=["status-box"])
615
+
616
+ # Provider Information
617
+ with gr.Group():
618
+ with gr.Accordion("🏒 Provider Comparison", open=False, elem_id="neuroscope-accordion"):
619
+ gr.Markdown("""
620
+ <div class="provider-info">
621
+ <h3>πŸ†š Groq vs Chutes Comparison</h3>
622
+
623
+ **πŸš€ Groq (Compound Models)**
624
+ - βœ… **Web Search Capabilities** - Can search the internet and cite sources
625
+ - βœ… **Agentic Tools** - Advanced tool usage and autonomous web browsing
626
+ - βœ… **Domain Filtering** - Control which websites to search
627
+ - βœ… **Citation System** - Automatic source linking and references
628
+ - ⚑ **Ultra-fast inference** - Groq's hardware acceleration
629
+ - 🧠 **Models**: compound-beta, compound-beta-mini
630
+
631
+ **🎯 Chutes**
632
+ - βœ… **Multiple Model Access** - Various open-source and commercial models
633
+ - βœ… **Cost-effective** - Competitive pricing
634
+ - βœ… **High-quality text generation** - Excellent for creative writing and analysis
635
+ - ⚑ **Good performance** - Reliable and fast responses
636
+ - 🧠 **Models**: GPT-OSS-20B, Llama 3.1, Claude 3 Sonnet, and more
637
+ - ❌ **No web search** - Relies on training data only
638
+
639
+ **πŸ’‘ Use Groq when you need:**
640
+ - Real-time information and web search
641
+ - Research with source citations
642
+ - Domain-specific searches
643
+ - Agentic AI capabilities
644
+
645
+ **πŸ’‘ Use Chutes when you need:**
646
+ - Pure text generation and analysis
647
+ - Creative writing tasks
648
+ - Cost-effective AI access
649
+ - Variety of model options
650
+ </div>
651
+ """)
652
+
653
+ # Update UI based on provider selection
654
+ def update_provider_ui(provider):
655
+ groq_visible = provider == "groq"
656
+ chutes_visible = provider == "chutes"
657
+ models = get_available_models(provider)
658
+
659
+ return (
660
+ gr.update(visible=groq_visible), # groq_api_key
661
+ gr.update(visible=chutes_visible), # chutes_api_key
662
+ gr.update(choices=models, value=models[0] if models else None,
663
+ label=f"🧠 {provider.title()} Models"), # model_selection
664
+ gr.update(visible=groq_visible), # domain filtering sections
665
+ gr.update(visible=groq_visible), # include domains
666
+ gr.update(visible=groq_visible) # exclude domains
667
+ )
668
+
669
+ provider_selection.change(
670
+ fn=update_provider_ui,
671
+ inputs=[provider_selection],
672
+ outputs=[groq_api_key, chutes_api_key, model_selection] # We'll add domain filtering updates later
673
+ )
674
+
675
+ # Connect button functionality
676
+ connect_btn.click(
677
+ fn=validate_api_keys,
678
+ inputs=[groq_api_key, chutes_api_key, provider_selection, model_selection],
679
+ outputs=[status_display]
680
+ )
681
+
682
+ # Main Chat Interface
683
+ with gr.Tab("πŸ’¬ Chat"):
684
+ chatbot = gr.Chatbot(
685
+ label="Multi-Provider Creative AI Assistant",
686
+ height=500,
687
+ show_label=True,
688
+ bubble_full_width=False,
689
+ show_copy_button=True
690
+ )
691
+
692
+ with gr.Row():
693
+ msg = gr.Textbox(
694
+ label="Your Message",
695
+ placeholder="Type your message here...",
696
+ lines=3
697
+ )
698
+ with gr.Column():
699
+ send_btn = gr.Button("πŸ“€ Send", variant="primary")
700
+ clear_btn = gr.Button("πŸ—‘οΈ Clear", variant="secondary")
701
+
702
+ # Advanced Settings
703
+ with gr.Accordion("βš™οΈ Advanced Settings", open=False, elem_id="neuroscope-accordion"):
704
+ with gr.Row():
705
+ temperature = gr.Slider(
706
+ minimum=0.0,
707
+ maximum=2.0,
708
+ value=0.7,
709
+ step=0.1,
710
+ label="🌑️ Temperature",
711
+ info="Higher = more creative, Lower = more focused"
712
+ )
713
+ max_tokens = gr.Slider(
714
+ minimum=100,
715
+ maximum=4000,
716
+ value=1024,
717
+ step=100,
718
+ label="πŸ“ Max Tokens",
719
+ info="Maximum length of response"
720
+ )
721
+
722
+ system_prompt = gr.Textbox(
723
+ label="🎭 Custom System Prompt",
724
+ placeholder="Override the default system prompt...",
725
+ lines=2,
726
+ info="Leave empty to use provider-optimized default prompt"
727
+ )
728
+
729
+ # Domain Filtering Section (Groq only)
730
+ with gr.Group() as domain_group:
731
+ with gr.Accordion("🌐 Domain Filtering (Groq Web Search Only)", open=False, elem_id="neuroscope-accordion"):
732
+ gr.Markdown("""
733
+ <div class="domain-info">
734
+ <h4>πŸ” Domain Filtering Guide (Groq Only)</h4>
735
+ <p><strong>Note:</strong> Domain filtering only works with Groq's compound models that have web search capabilities.</p>
736
+ <p>Control which websites the AI can search when answering your questions:</p>
737
+ <ul>
738
+ <li><strong>Include Domains:</strong> Only search these domains (comma-separated)</li>
739
+ <li><strong>Exclude Domains:</strong> Never search these domains (comma-separated)</li>
740
+ <li><strong>Examples:</strong> arxiv.org, *.edu, github.com, stackoverflow.com</li>
741
+ <li><strong>Wildcards:</strong> Use *.edu for all educational domains</li>
742
+ </ul>
743
+ <p><strong>New:</strong> Domain filtering status will be shown in responses!</p>
744
+ </div>
745
+ """)
746
+
747
+ with gr.Row():
748
+ include_domains = gr.Textbox(
749
+ label="βœ… Include Domains (comma-separated)",
750
+ placeholder="arxiv.org, *.edu, github.com, stackoverflow.com",
751
+ info="Only search these domains"
752
+ )
753
+ exclude_domains = gr.Textbox(
754
+ label="❌ Exclude Domains (comma-separated)",
755
+ placeholder="wikipedia.org, reddit.com, twitter.com",
756
+ info="Never search these domains"
757
+ )
758
+
759
+ with gr.Accordion("πŸ”— Common Domain Examples", open=False, elem_id="neuroscope-accordion"):
760
+ gr.Markdown("""
761
+ **Academic & Research:**
762
+ - `arxiv.org`, `*.edu`, `scholar.google.com`, `researchgate.net`
763
+
764
+ **Technology & Programming:**
765
+ - `github.com`, `stackoverflow.com`, `docs.python.org`, `developer.mozilla.org`
766
+
767
+ **News & Media:**
768
+ - `reuters.com`, `bbc.com`, `npr.org`, `apnews.com`
769
+
770
+ **Business & Finance:**
771
+ - `bloomberg.com`, `wsj.com`, `nasdaq.com`, `sec.gov`
772
+
773
+ **Science & Medicine:**
774
+ - `nature.com`, `science.org`, `pubmed.ncbi.nlm.nih.gov`, `who.int`
775
+ """)
776
+
777
+ # Update provider UI function with domain filtering
778
+ def update_provider_ui_complete(provider):
779
+ groq_visible = provider == "groq"
780
+ chutes_visible = provider == "chutes"
781
+ models = get_available_models(provider)
782
+
783
+ return (
784
+ gr.update(visible=groq_visible), # groq_api_key
785
+ gr.update(visible=chutes_visible), # chutes_api_key
786
+ gr.update(choices=models, value=models[0] if models else None,
787
+ label=f"🧠 {provider.title()} Models"), # model_selection
788
+ gr.update(visible=groq_visible), # domain_group
789
+ )
790
+
791
+ provider_selection.change(
792
+ fn=update_provider_ui_complete,
793
+ inputs=[provider_selection],
794
+ outputs=[groq_api_key, chutes_api_key, model_selection, domain_group]
795
+ )
796
+
797
+ # IMPORTANT Section with Citation Info
798
+ with gr.Group():
799
+ with gr.Accordion("πŸ“š IMPORTANT - Citations & Multi-Provider Features!", open=False, elem_id="neuroscope-accordion"):
800
+ gr.Markdown("""
801
+ <div class="citation-info">
802
+ <h3>πŸ†• Multi-Provider Enhancement</h3>
803
+ <p>This enhanced version now supports both Groq and Chutes AI providers:</p>
804
+ <ul>
805
+ <li><strong>πŸš€ Groq Integration:</strong> Agentic AI with web search, citations, and tool usage</li>
806
+ <li><strong>🎯 Chutes Integration:</strong> Multiple AI models for text generation and analysis</li>
807
+ <li><strong>πŸ”„ Easy Switching:</strong> Switch between providers based on your needs</li>
808
+ <li><strong>πŸ“Š Provider Comparison:</strong> Clear information about each provider's strengths</li>
809
+ </ul>
810
+
811
+ <h3>πŸ”— Groq Enhanced Citation System</h3>
812
+ <p>When using Groq, you get:</p>
813
+ <ul>
814
+ <li><strong>Automatic Source Citations:</strong> All responses include clickable links to sources</li>
815
+ <li><strong>Sources Used Section:</strong> Dedicated section showing all websites referenced</li>
816
+ <li><strong>Domain Filtering Verification:</strong> Clear indication when domain filtering is applied</li>
817
+ <li><strong>Search Query Tracking:</strong> Shows what queries were made to find information</li>
818
+ </ul>
819
+
820
+ <h3>🎯 Chutes Model Access</h3>
821
+ <p>When using Chutes, you get access to:</p>
822
+ <ul>
823
+ <li><strong>Multiple Models:</strong> GPT-OSS-20B, Llama 3.1, Claude 3 Sonnet</li>
824
+ <li><strong>Cost-Effective:</strong> Competitive pricing for high-quality AI</li>
825
+ <li><strong>Specialized Tasks:</strong> Optimized for creative writing and analysis</li>
826
+ <li><strong>Reliable Performance:</strong> Consistent and fast responses</li>
827
+ </ul>
828
+ </div>
829
+
830
+ ### πŸ” **Web Search Behavior (Groq Only)**
831
+
832
+ **No Domains Specified:**
833
+ - AI operates with **unrestricted web search capabilities**.
834
+ - Compound models autonomously search the **entire internet** for the most relevant and up-to-date information.
835
+ - AI has complete freedom to use its **agentic tools** and browse **any website** it finds useful.
836
+
837
+ **Include Domains Specified (e.g., `arxiv.org`, `*.edu`):**
838
+ - AI is restricted to search **only the specified domains**.
839
+ - Acts as a **strict whitelist**, making the AI **laser-focused** on your chosen sources.
840
+ - Ensures information is sourced from **preferred or authoritative domains** (e.g., academic or research-focused).
841
+
842
+ **Exclude Domains Specified (e.g., `wikipedia.org`, `reddit.com`):**
843
+ - AI searches the entire web **except the listed domains**.
844
+ - Useful for **filtering out unreliable or unwanted sources**.
845
+ - Allows broad search with **targeted exclusions**.
846
+
847
+ **Both Include and Exclude Domains Specified:**
848
+ - **Only the include domains** are used for searching.
849
+ - **Exclude list is ignored** because the include list already restricts search scope.
850
+ - Guarantees AI pulls content **exclusively from whitelisted domains**, regardless of the excluded ones.
851
+
852
+ ---
853
+
854
+ ### 🎭 **Custom System Prompt Feature**
855
+
856
+ Allows complete override of the AI's **default personality and behavior** for both providers.
857
+ You can redefine the AI to act as:
858
+ - A **professional business consultant**
859
+ - A **coding mentor**
860
+ - A **creative writer**
861
+ - A **specific character or persona**
862
+ - Provides full control to **reshape the AI's tone, expertise, and conversational style** with a single prompt.
863
+ """)
864
+
865
+ # How to Use Section
866
+ with gr.Accordion("πŸ“– How to Use This Enhanced Multi-Provider App", open=False, elem_id="neuroscope-accordion"):
867
+ gr.Markdown("""
868
+ ### πŸš€ Getting Started
869
+ 1. **Choose your AI Provider** - Select between Groq (web search + agentic) or Chutes (text generation)
870
+ 2. **Enter your API Key** -
871
+ - Groq: Get one from [console.groq.com](https://console.groq.com/)
872
+ - Chutes: Get one from [chutes.ai](https://chutes.ai/)
873
+ 3. **Select a model** - Choose from provider-specific model options
874
+ 4. **Click Connect** - Validate your key and connect to the AI
875
+ 5. **Start chatting!** - Type your message and get intelligent responses
876
+
877
+ ### 🎯 Key Features
878
+ **πŸš€ Groq Features:**
879
+ - **Agentic AI**: The AI can use tools and search the web autonomously
880
+ - **Smart Citations**: Automatic source linking and citation formatting
881
+ - **Domain Filtering**: Control which websites the AI searches
882
+ - **Ultra-fast**: Groq's hardware-accelerated inference
883
+
884
+ **🎯 Chutes Features:**
885
+ - **Multiple Models**: Access to various open-source and commercial models
886
+ - **Cost-Effective**: Competitive pricing for AI access
887
+ - **High Quality**: Excellent text generation and analysis
888
+ - **Model Variety**: Choose the best model for your specific task
889
+
890
+ **πŸ”„ Universal Features:**
891
+ - **Memory**: Maintains conversation context throughout the session
892
+ - **Customizable**: Adjust temperature, tokens, and system prompts
893
+ - **Provider Switching**: Easy switching between AI providers
894
+
895
+ ### πŸ’‘ Tips for Best Results
896
+ **For Groq:**
897
+ - Be specific in your questions for better web search results
898
+ - Use domain filtering for specialized research
899
+ - Check the "Sources Used" section for all references
900
+ - Try different domain combinations to see varied results
901
+
902
+ **For Chutes:**
903
+ - Experiment with different models for different tasks
904
+ - Use higher temperatures for creative tasks
905
+ - Leverage the variety of available models (GPT, Llama, Claude)
906
+ - Perfect for tasks that don't require real-time information
907
+
908
+ **General:**
909
+ - Adjust temperature: higher for creativity, lower for precision
910
+ - Try different system prompts for different conversation styles
911
+ - Use the provider that best fits your current task
912
+ """)
913
+
914
+ # Sample Examples Section
915
+ with gr.Accordion("🎯 Sample Examples to Test Both Providers", open=False, elem_id="neuroscope-accordion"):
916
+ gr.Markdown("""
917
+ <div class="example-box">
918
+ <h4>πŸ†š Provider Comparison Examples</h4>
919
+ <p>Try the same prompts with both providers to see the difference:</p>
920
+
921
+ <h4>πŸ”¬ Research & Analysis</h4>
922
+ <ul>
923
+ <li><strong>Groq (with web search):</strong> "What are the latest breakthroughs in quantum computing in 2024?"</li>
924
+ <li><strong>Chutes (knowledge-based):</strong> "Explain the fundamental principles of quantum computing"</li>
925
+ <li><strong>Groq with domains:</strong> Same question with "arxiv.org, *.edu" in include domains</li>
926
+ </ul>
927
+
928
+ <h4>πŸ’» Programming & Tech</h4>
929
+ <ul>
930
+ <li><strong>Groq:</strong> "What are the current best practices for React 18 in 2024?"</li>
931
+ <li><strong>Chutes:</strong> "Write a comprehensive React component with hooks and best practices"</li>
932
+ <li><strong>Groq filtered:</strong> Same with "github.com, stackoverflow.com" included</li>
933
+ </ul>
934
+
935
+ <h4>🎨 Creative Tasks (Great for Chutes)</h4>
936
+ <ul>
937
+ <li>"Write a short story about AI and humans working together"</li>
938
+ <li>"Create a marketing plan for a sustainable fashion brand"</li>
939
+ <li>"Generate ideas for a mobile app that helps with mental health"</li>
940
+ <li>"Write a poem about the beauty of code"</li>
941
+ </ul>
942
+
943
+ <h4>πŸ“Š Business & Analysis</h4>
944
+ <ul>
945
+ <li><strong>Groq:</strong> "What are the current trends in cryptocurrency markets?"</li>
946
+ <li><strong>Chutes:</strong> "Analyze the pros and cons of different investment strategies"</li>
947
+ <li><strong>Groq filtered:</strong> Crypto question with "bloomberg.com, wsj.com" included</li>
948
+ </ul>
949
+
950
+ <h4>🧠 Model-Specific Testing (Chutes)</h4>
951
+ <ul>
952
+ <li><strong>GPT-OSS-20B:</strong> "Explain complex scientific concepts in simple terms"</li>
953
+ <li><strong>Llama 3.1:</strong> "Help me debug this Python code and explain the solution"</li>
954
+ <li><strong>Claude 3 Sonnet:</strong> "Analyze this business scenario and provide strategic recommendations"</li>
955
+ </ul>
956
+ </div>
957
+ """)
958
+
959
+ # Event handlers
960
+ send_btn.click(
961
+ fn=chat_with_ai,
962
+ inputs=[msg, include_domains, exclude_domains, system_prompt, temperature, max_tokens, chatbot],
963
+ outputs=[chatbot, msg]
964
+ )
965
+
966
+ msg.submit(
967
+ fn=chat_with_ai,
968
+ inputs=[msg, include_domains, exclude_domains, system_prompt, temperature, max_tokens, chatbot],
969
+ outputs=[chatbot, msg]
970
+ )
971
+
972
+ clear_btn.click(
973
+ fn=clear_chat_history,
974
+ outputs=[chatbot]
975
+ )
976
+
977
+ # Footer
978
+ with gr.Accordion("πŸš€ About This Enhanced Multi-Provider Tool", open=True, elem_id="neuroscope-accordion"):
979
+ gr.Markdown("""
980
+ **Enhanced Multi-Provider Creative Agentic AI Chat Tool** with dual API support:
981
+
982
+ **πŸ†• New Multi-Provider Features:**
983
+ - πŸš€ **Groq Integration**: Agentic AI with web search, citations, and tool usage
984
+ - 🎯 **Chutes Integration**: Multiple AI models for diverse text generation tasks
985
+ - πŸ”„ **Provider Switching**: Easy switching between different AI providers
986
+ - πŸ“Š **Provider Comparison**: Clear information about each provider's strengths
987
+ - 🧠 **Multiple Models**: Access to various AI models through both providers
988
+
989
+ **πŸš€ Groq Features:**
990
+ - πŸ”— **Automatic Source Citations**: Every response includes clickable links to sources
991
+ - πŸ“š **Sources Used Section**: Dedicated section showing all websites referenced
992
+ - 🌐 **Domain Filtering Verification**: Clear indication when filtering is applied
993
+ - πŸ” **Search Query Tracking**: Shows what queries were made
994
+ - ⚑ **Enhanced Tool Usage Display**: Better visibility into AI's research process
995
+ - πŸ” Web search with domain filtering
996
+ - 🧠 Advanced AI reasoning with tool usage
997
+
998
+ **🎯 Chutes Features:**
999
+ - πŸ€– **Multiple AI Models**: GPT-OSS-20B, Llama 3.1, Claude 3 Sonnet
1000
+ - πŸ’° **Cost-Effective**: Competitive pricing for AI access
1001
+ - 🎨 **Creative Excellence**: Optimized for writing and analysis tasks
1002
+ - ⚑ **Reliable Performance**: Consistent and fast responses
1003
+
1004
+ **πŸ”„ Universal Features:**
1005
+ - πŸ’¬ Conversational memory and context
1006
+ - βš™οΈ Customizable parameters and prompts
1007
+ - 🎨 Creative and analytical capabilities
1008
+ - 🌟 Enhanced user interface with provider-specific optimizations
1009
+
1010
+ **πŸ’‘ Choose Your Provider:**
1011
+ - **Use Groq** when you need real-time information, web search, and citations
1012
+ - **Use Chutes** when you need pure text generation, creative writing, or cost-effective AI access
1013
+ """)
1014
+
1015
+ return app
1016
+
1017
+ # Main execution
1018
+ if __name__ == "__main__":
1019
+ app = create_gradio_app()
1020
+ app.launch(
1021
+ share=True
1022
+ )