Dhanyakumar commited on
Commit
4a8bd50
·
verified ·
1 Parent(s): 4e29a40

Upload 4 files

Browse files
Files changed (4) hide show
  1. README.md +6 -6
  2. app.py +527 -0
  3. gitattributes +35 -0
  4. requirements.txt +23 -0
README.md CHANGED
@@ -1,10 +1,10 @@
1
  ---
2
- title: Block Analyzer
3
- emoji: 👁
4
- colorFrom: indigo
5
- colorTo: red
6
- sdk: streamlit
7
- sdk_version: 1.43.1
8
  app_file: app.py
9
  pinned: false
10
  ---
 
1
  ---
2
+ title: Newco Blockchain Asset Analyzer
3
+ emoji: 🦀
4
+ colorFrom: red
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 5.13.1
8
  app_file: app.py
9
  pinned: false
10
  ---
app.py ADDED
@@ -0,0 +1,527 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Blockchain Wallet Analyzer - A tool for analyzing Ethereum wallet contents and NFT holdings.
3
+
4
+ This module provides a complete implementation of a blockchain wallet analysis tool
5
+ with a Gradio web interface. It includes wallet analysis, NFT tracking, and
6
+ interactive chat capabilities using the Groq API.
7
+
8
+ Author: Mane
9
+ Date: March 2025
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import re
16
+ import json
17
+ import time
18
+ import logging
19
+ import asyncio
20
+ from typing import List, Dict, Tuple, Any, Optional, TypeVar, cast
21
+ from datetime import datetime
22
+ from decimal import Decimal
23
+ from dataclasses import dataclass
24
+ from enum import Enum
25
+ from pathlib import Path
26
+
27
+ import aiohttp
28
+ import gradio as gr
29
+ from tenacity import retry, stop_after_attempt, wait_exponential
30
+ import groq
31
+
32
+ # Type variables
33
+ T = TypeVar('T')
34
+ WalletData = Dict[str, Any]
35
+ ChatHistory = List[Tuple[str, str]]
36
+
37
+ # API Keys - Define as constants in the script
38
+ GROQ_API_KEY = "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"
39
+ ETHERSCAN_API_KEY = "V562U3P76KKEUJ2DV9Q3U2EPDK89T5CKNH"
40
+
41
+ # Configure logging
42
+ logging.basicConfig(
43
+ level=logging.INFO,
44
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
45
+ handlers=[
46
+ logging.FileHandler('blockchain_analyzer.log'),
47
+ logging.StreamHandler()
48
+ ]
49
+ )
50
+ logger = logging.getLogger(__name__)
51
+
52
+ class ConfigError(Exception):
53
+ """Raised when there's an error in configuration."""
54
+ pass
55
+
56
+ class APIError(Exception):
57
+ """Raised when there's an error in API calls."""
58
+ pass
59
+
60
+ class ValidationError(Exception):
61
+ """Raised when there's an error in input validation."""
62
+ pass
63
+
64
+ @dataclass
65
+ class Config:
66
+ """Application configuration settings."""
67
+ SYSTEM_PROMPT: str = """
68
+ You are MANE 👑 (Learning & Observing Smart Systems Digital Output Generator),
69
+ an adorable blockchain-sniffing puppy!
70
+ Your personality:
71
+ - Friendly and enthusiastic
72
+ - Explain findings in fun, simple ways
73
+
74
+ Instructions:
75
+ - You have access to detailed wallet data in your context
76
+ - Use this data to provide specific answers about holdings
77
+ - Reference exact numbers and collections when discussing NFTs
78
+ - Compare wallets if multiple are available
79
+ """
80
+ ETHERSCAN_BASE_URL: str = "https://api.etherscan.io/api"
81
+ ETHEREUM_ADDRESS_REGEX: str = r"0x[a-fA-F0-9]{40}"
82
+ RATE_LIMIT_DELAY: float = 0.2 # 5 requests per second max for free tier
83
+ MAX_RETRIES: int = 3
84
+ GROQ_MODEL: str = "llama3-70b-8192" # Groq's high-performance model
85
+ MAX_TOKENS: int = 4000
86
+ TEMPERATURE: float = 0.7
87
+ HISTORY_LIMIT: int = 5
88
+
89
+ @classmethod
90
+ def load(cls, config_path: str | Path) -> Config:
91
+ """Load configuration from a JSON file."""
92
+ try:
93
+ with open(config_path) as f:
94
+ config_data = json.load(f)
95
+ return cls(**config_data)
96
+ except Exception as e:
97
+ logger.error(f"Error loading config: {e}")
98
+ return cls()
99
+
100
+ class WalletAnalyzer:
101
+ """Analyzes Ethereum wallet contents using Etherscan API."""
102
+
103
+ def __init__(self):
104
+ """Initialize the analyzer with API key."""
105
+ self.api_key = ETHERSCAN_API_KEY
106
+ self.base_url = Config.ETHERSCAN_BASE_URL
107
+ self.last_request_time = 0
108
+ self.session: Optional[aiohttp.ClientSession] = None
109
+
110
+ async def __aenter__(self) -> WalletAnalyzer:
111
+ """Create aiohttp session on context manager enter."""
112
+ self.session = aiohttp.ClientSession()
113
+ return self
114
+
115
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
116
+ """Close aiohttp session on context manager exit."""
117
+ if self.session:
118
+ await self.session.close()
119
+ self.session = None
120
+
121
+ @retry(
122
+ stop=stop_after_attempt(Config.MAX_RETRIES),
123
+ wait=wait_exponential(multiplier=1, min=4, max=10)
124
+ )
125
+ async def _fetch_data(self, params: Dict[str, str]) -> Dict[str, Any]:
126
+ """Fetch data from Etherscan API with retry logic."""
127
+ if not self.session:
128
+ raise APIError("No active session. Use context manager.")
129
+
130
+ await self._rate_limit()
131
+ params["apikey"] = self.api_key
132
+
133
+ try:
134
+ async with self.session.get(self.base_url, params=params) as response:
135
+ if response.status != 200:
136
+ raise APIError(f"API request failed: {response.status}")
137
+
138
+ data = await response.json()
139
+ if data["status"] == "0":
140
+ error_msg = data.get('message', 'Unknown error')
141
+ if "Max rate limit reached" in error_msg:
142
+ raise APIError("Rate limit exceeded")
143
+ raise APIError(f"API error: {error_msg}")
144
+
145
+ return data
146
+
147
+ except aiohttp.ClientError as e:
148
+ raise APIError(f"Network error: {str(e)}")
149
+ except Exception as e:
150
+ raise APIError(f"Unexpected error: {str(e)}")
151
+
152
+ async def _rate_limit(self) -> None:
153
+ """Implement rate limiting for Etherscan API."""
154
+ current_time = time.time()
155
+ time_passed = current_time - self.last_request_time
156
+ if time_passed < Config.RATE_LIMIT_DELAY:
157
+ await asyncio.sleep(Config.RATE_LIMIT_DELAY - time_passed)
158
+ self.last_request_time = time.time()
159
+
160
+ @staticmethod
161
+ def _validate_address(address: str) -> bool:
162
+ """Validate Ethereum address format."""
163
+ return bool(re.match(Config.ETHEREUM_ADDRESS_REGEX, address))
164
+
165
+ async def get_portfolio_data(self, address: str) -> WalletData:
166
+ """Get complete portfolio including ETH, tokens, and NFTs."""
167
+ if not self._validate_address(address):
168
+ raise ValidationError(f"Invalid Ethereum address: {address}")
169
+
170
+ logger.info(f"Fetching portfolio data for {address}")
171
+
172
+ # Get ETH balance
173
+ eth_balance = await self._get_eth_balance(address)
174
+
175
+ # Get token data
176
+ token_holdings = await self._get_token_holdings(address)
177
+
178
+ # Get NFT data
179
+ nft_collections = await self._get_nft_holdings(address)
180
+
181
+ return {
182
+ "address": address,
183
+ "last_updated": datetime.now().isoformat(),
184
+ "eth_balance": float(eth_balance),
185
+ "tokens": token_holdings,
186
+ "nft_collections": nft_collections
187
+ }
188
+
189
+ async def _get_eth_balance(self, address: str) -> Decimal:
190
+ """Get ETH balance for address."""
191
+ params = {
192
+ "module": "account",
193
+ "action": "balance",
194
+ "address": address,
195
+ "tag": "latest"
196
+ }
197
+ data = await self._fetch_data(params)
198
+ return Decimal(data["result"]) / Decimal("1000000000000000000")
199
+
200
+ async def _get_token_holdings(self, address: str) -> List[Dict[str, Any]]:
201
+ """Get token holdings for address."""
202
+ params = {
203
+ "module": "account",
204
+ "action": "tokentx",
205
+ "address": address,
206
+ "sort": "desc"
207
+ }
208
+ data = await self._fetch_data(params)
209
+
210
+ token_holdings: Dict[str, Dict[str, Any]] = {}
211
+ for tx in data.get("result", []):
212
+ contract = tx["contractAddress"]
213
+ if contract not in token_holdings:
214
+ token_holdings[contract] = {
215
+ "name": tx["tokenName"],
216
+ "symbol": tx["tokenSymbol"],
217
+ "decimals": int(tx["tokenDecimal"]),
218
+ "balance": Decimal(0)
219
+ }
220
+
221
+ amount = Decimal(tx["value"]) / Decimal(10 ** int(tx["tokenDecimal"]))
222
+ if tx["to"].lower() == address.lower():
223
+ token_holdings[contract]["balance"] += amount
224
+ elif tx["from"].lower() == address.lower():
225
+ token_holdings[contract]["balance"] -= amount
226
+
227
+ return [
228
+ {
229
+ "name": data["name"],
230
+ "symbol": data["symbol"],
231
+ "balance": float(data["balance"])
232
+ }
233
+ for data in token_holdings.values()
234
+ if data["balance"] > 0
235
+ ]
236
+
237
+ async def _get_nft_holdings(self, address: str) -> Dict[str, Dict[str, Any]]:
238
+ """Get NFT holdings for address."""
239
+ params = {
240
+ "module": "account",
241
+ "action": "tokennfttx",
242
+ "address": address,
243
+ "sort": "desc"
244
+ }
245
+ data = await self._fetch_data(params)
246
+
247
+ nft_holdings: Dict[str, Dict[str, Any]] = {}
248
+ collections: Dict[str, List[str]] = {}
249
+
250
+ for tx in data.get("result", []):
251
+ collection_name = tx.get("tokenName", "Unknown Collection")
252
+ token_id = tx["tokenID"]
253
+ key = f"{tx['contractAddress']}_{token_id}"
254
+
255
+ if tx["to"].lower() == address.lower():
256
+ nft_holdings[key] = {
257
+ "collection": collection_name,
258
+ "token_id": token_id,
259
+ "contract": tx["contractAddress"],
260
+ "acquired_time": tx["timeStamp"]
261
+ }
262
+
263
+ if collection_name not in collections:
264
+ collections[collection_name] = []
265
+ collections[collection_name].append(token_id)
266
+
267
+ elif tx["from"].lower() == address.lower():
268
+ nft_holdings.pop(key, None)
269
+ if collection_name in collections and token_id in collections[collection_name]:
270
+ collections[collection_name].remove(token_id)
271
+
272
+ return {
273
+ name: {
274
+ "count": len(tokens),
275
+ "token_ids": tokens
276
+ }
277
+ for name, tokens in collections.items()
278
+ if tokens # Only include collections with tokens
279
+ }
280
+
281
+
282
+ class ChatInterface:
283
+ """Handles chat interaction using Groq API."""
284
+
285
+ def __init__(self):
286
+ """Initialize chat interface with Groq client."""
287
+ self.groq_client = groq.Client(api_key=GROQ_API_KEY)
288
+ self.context: Dict[str, Any] = {}
289
+
290
+ def _format_context_message(self) -> str:
291
+ """Format wallet data as context message."""
292
+ if not self.context:
293
+ return ""
294
+
295
+ context_msg = ["Current Wallet Data:\n"]
296
+ for addr, data in self.context.items():
297
+ context_msg.extend([
298
+ f"Wallet {addr[:8]}...{addr[-6:]}:",
299
+ f"- ETH Balance: {data['eth_balance']:.4f} ETH",
300
+ f"- Tokens: {len(data['tokens'])}"
301
+ ])
302
+
303
+ if data['tokens']:
304
+ context_msg.append(" Token Holdings:")
305
+ for token in data['tokens']:
306
+ context_msg.append(
307
+ f" * {token['name']} ({token['symbol']}): {token['balance']}"
308
+ )
309
+
310
+ if data['nft_collections']:
311
+ context_msg.append(" NFT Collections:")
312
+ for name, info in data['nft_collections'].items():
313
+ context_msg.append(f" * {name}: {info['count']} NFTs")
314
+ if info['count'] <= 5:
315
+ context_msg.append(
316
+ f" Token IDs: {', '.join(map(str, info['token_ids']))}"
317
+ )
318
+
319
+ return "\n".join(context_msg)
320
+
321
+ async def process_message(
322
+ self,
323
+ message: str,
324
+ history: Optional[ChatHistory] = None
325
+ ) -> Tuple[ChatHistory, Dict[str, Any], str]:
326
+ """Process user message and generate response."""
327
+ if not message.strip():
328
+ return history or [], self.context, ""
329
+
330
+ history = history or []
331
+
332
+ # Check for Ethereum address
333
+ match = re.search(Config.ETHEREUM_ADDRESS_REGEX, message)
334
+ if match:
335
+ try:
336
+ address = match.group(0)
337
+ async with WalletAnalyzer() as analyzer:
338
+ wallet_data = await analyzer.get_portfolio_data(address)
339
+ self.context[address] = wallet_data
340
+
341
+ summary = [
342
+ f"📊 Portfolio Summary for {address[:8]}...{address[-6:]}",
343
+ f"💎 ETH Balance: {wallet_data['eth_balance']:.4f} ETH",
344
+ f"🪙 Tokens: {len(wallet_data['tokens'])} different tokens"
345
+ ]
346
+
347
+ total_nfts = sum(
348
+ coll['count'] for coll in wallet_data['nft_collections'].values())
349
+
350
+ summary.append(
351
+ f"🎨 NFTs: {total_nfts} NFTs in {len(wallet_data['nft_collections'])} collections"
352
+ )
353
+
354
+ bot_message = "\n".join(summary)
355
+ history.append((message, bot_message))
356
+ return history, self.context, ""
357
+
358
+ except Exception as e:
359
+ logger.error(f"Error analyzing wallet: {e}")
360
+ error_message = f"Error analyzing wallet: {str(e)}"
361
+ history.append((message, error_message))
362
+ return history, self.context, ""
363
+
364
+ # Generate response using Groq
365
+ try:
366
+ # Format context message
367
+ context_msg = self._format_context_message()
368
+
369
+ # Convert history to Groq format
370
+ chat_history = []
371
+ for user_msg, assistant_msg in history[-Config.HISTORY_LIMIT:]:
372
+ chat_history.extend([
373
+ {"role": "user", "content": user_msg},
374
+ {"role": "assistant", "content": assistant_msg}
375
+ ])
376
+
377
+ # Generate response using Groq
378
+ response = self.groq_client.chat.completions.create(
379
+ model=Config.GROQ_MODEL,
380
+ messages=[
381
+ {"role": "system", "content": Config.SYSTEM_PROMPT},
382
+ {"role": "system", "content": context_msg},
383
+ *chat_history,
384
+ {"role": "user", "content": message}
385
+ ],
386
+ temperature=Config.TEMPERATURE,
387
+ max_tokens=Config.MAX_TOKENS
388
+ )
389
+
390
+ bot_message = response.choices[0].message.content
391
+ history.append((message, bot_message))
392
+ return history, self.context, ""
393
+
394
+ except Exception as e:
395
+ logger.error(f"Error generating response: {e}")
396
+ error_message = f"Error generating response: {str(e)}"
397
+ history.append((message, error_message))
398
+ return history, self.context, ""
399
+
400
+ def clear_context(self) -> Tuple[Dict[str, Any], List[Tuple[str, str]]]:
401
+ """Clear the wallet context and chat history."""
402
+ self.context = {}
403
+ return {}, []
404
+
405
+
406
+ class GradioInterface:
407
+ """Handles Gradio web interface setup and interactions."""
408
+
409
+ def __init__(self):
410
+ """Initialize Gradio interface."""
411
+ self.chat_interface = ChatInterface()
412
+ self.demo = self._create_interface()
413
+
414
+ def _create_interface(self) -> gr.Blocks:
415
+ """Create and configure Gradio interface."""
416
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
417
+ gr.Markdown("""
418
+ # MANE👑: Blockchain Wallet Analyzer
419
+
420
+ Welcome to 👑 MANE WAR - Your friendly blockchain analysis companion!
421
+ - Input an Ethereum wallet address to analyze
422
+ - Chat about your wallet contents with context!
423
+ """)
424
+
425
+ # Main Interface
426
+ with gr.Row():
427
+ # Chat Area (Left Side)
428
+ with gr.Column(scale=2):
429
+ chatbot = gr.Chatbot(
430
+ label="Chat History",
431
+ height=500,
432
+ value=[]
433
+ )
434
+ with gr.Row():
435
+ msg_input = gr.Textbox(
436
+ label="Message",
437
+ placeholder="Enter wallet address or ask about holdings...",
438
+ show_label=True
439
+ )
440
+ send_btn = gr.Button("Send", variant="primary")
441
+
442
+ # Context Sidebar (Right Side)
443
+ with gr.Column(scale=1):
444
+ wallet_context = gr.JSON(
445
+ label="Active Wallet Context",
446
+ show_label=True,
447
+ value={}
448
+ )
449
+ clear_btn = gr.Button("Clear Context", variant="secondary")
450
+
451
+ async def handle_message(
452
+ message: str,
453
+ chat_history: List[Tuple[str, str]],
454
+ context: Dict[str, Any]
455
+ ) -> Tuple[List[Tuple[str, str]], Dict[str, Any]]:
456
+ """Handle incoming messages."""
457
+ try:
458
+ history, new_context, _ = await self.chat_interface.process_message(
459
+ message,
460
+ chat_history
461
+ )
462
+ return history, new_context
463
+
464
+ except Exception as e:
465
+ logger.error(f"Error handling message: {e}")
466
+ if chat_history is None:
467
+ chat_history = []
468
+ chat_history.append((message, f"Error: {str(e)}"))
469
+ return chat_history, context
470
+
471
+ # Connect Event Handlers
472
+ clear_btn.click(
473
+ fn=self.chat_interface.clear_context,
474
+ inputs=[],
475
+ outputs=[wallet_context, chatbot]
476
+ )
477
+
478
+ # Message Handling
479
+ msg_input.submit(
480
+ fn=handle_message,
481
+ inputs=[msg_input, chatbot, wallet_context],
482
+ outputs=[chatbot, wallet_context]
483
+ ).then(
484
+ lambda: gr.update(value=""),
485
+ None,
486
+ [msg_input]
487
+ )
488
+
489
+ send_btn.click(
490
+ fn=handle_message,
491
+ inputs=[msg_input, chatbot, wallet_context],
492
+ outputs=[chatbot, wallet_context]
493
+ ).then(
494
+ lambda: gr.update(value=""),
495
+ None,
496
+ [msg_input]
497
+ )
498
+
499
+ return demo
500
+
501
+ def launch(self, **kwargs):
502
+ """Launch the Gradio interface."""
503
+ self.demo.queue()
504
+ self.demo.launch(**kwargs)
505
+
506
+
507
+ def main():
508
+ """Main entry point for the application."""
509
+ try:
510
+ # Load configuration
511
+ config = Config.load("config.json")
512
+
513
+ # Initialize and launch interface
514
+ interface = GradioInterface()
515
+ interface.launch(
516
+ server_name="0.0.0.0",
517
+ server_port=7860,
518
+ share=True
519
+ )
520
+
521
+ except Exception as e:
522
+ logger.error(f"Application startup failed: {e}")
523
+ raise
524
+
525
+
526
+ if __name__ == "__main__":
527
+ main()
gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ groq
2
+ gradio>=4.12.0
3
+ openai>=1.0.0
4
+ plotly>=5.18.0
5
+ requests>=2.31.0
6
+ python-dateutil>=2.8.2
7
+ typing-extensions>=4.9.0
8
+ aiohttp>=3.9.1
9
+ pandas>=2.1.4
10
+ numpy>=1.24.3
11
+ python-dotenv>=1.0.0
12
+ tenacity>=8.2.3
13
+ certifi>=2023.11.17
14
+ websockets>=12.0
15
+ yarl>=1.9.4
16
+ attrs>=23.1.0
17
+ aiosignal>=1.3.1
18
+ async-timeout>=4.0.3
19
+ frozenlist>=1.4.1
20
+ idna>=3.6
21
+ multidict>=6.0.4
22
+ packaging>=23.2
23
+ six>=1.16.0