Update app.py
Browse files
app.py
CHANGED
@@ -47,6 +47,162 @@ logging.basicConfig(
|
|
47 |
)
|
48 |
logger = logging.getLogger(__name__)
|
49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
class BlockchainError(Exception):
|
51 |
"""Base exception for blockchain-related errors."""
|
52 |
pass
|
@@ -136,7 +292,7 @@ class Config:
|
|
136 |
MAX_IMAGE_SIZE: Tuple[int, int] = (800, 800)
|
137 |
|
138 |
# OpenAI Settings
|
139 |
-
OPENAI_MODEL: str = "gpt-
|
140 |
MAX_TOKENS: int = 8000
|
141 |
TEMPERATURE: float = 0.7
|
142 |
HISTORY_LIMIT: int = 10
|
|
|
47 |
)
|
48 |
logger = logging.getLogger(__name__)
|
49 |
|
50 |
+
@dataclass
|
51 |
+
class ChatContext:
|
52 |
+
"""Track chat context and discussed NFTs."""
|
53 |
+
wallet_data: Dict[str, Any] = field(default_factory=dict)
|
54 |
+
discussed_nfts: Set[str] = field(default_factory=set)
|
55 |
+
last_collection: str = ""
|
56 |
+
|
57 |
+
def mark_nft_discussed(self, contract: str, token_id: str) -> None:
|
58 |
+
"""Track which NFTs have been discussed."""
|
59 |
+
self.discussed_nfts.add(f"{contract}_{token_id}")
|
60 |
+
|
61 |
+
def is_nft_discussed(self, contract: str, token_id: str) -> bool:
|
62 |
+
"""Check if NFT has been discussed."""
|
63 |
+
return f"{contract}_{token_id}" in self.discussed_nfts
|
64 |
+
|
65 |
+
class BlockchainError(Exception):
|
66 |
+
"""Base exception for blockchain-related errors."""
|
67 |
+
pass
|
68 |
+
|
69 |
+
class ConfigError(BlockchainError):
|
70 |
+
"""Configuration error."""
|
71 |
+
pass
|
72 |
+
|
73 |
+
class APIError(BlockchainError):
|
74 |
+
"""API call error."""
|
75 |
+
pass
|
76 |
+
|
77 |
+
class ValidationError(BlockchainError):
|
78 |
+
"""Input validation error."""
|
79 |
+
pass
|
80 |
+
|
81 |
+
class NFTError(BlockchainError):
|
82 |
+
"""NFT processing error."""
|
83 |
+
pass
|
84 |
+
|
85 |
+
@dataclass
|
86 |
+
class NFTMetadata:
|
87 |
+
"""Structure for NFT metadata."""
|
88 |
+
contract: str
|
89 |
+
token_id: str
|
90 |
+
name: str
|
91 |
+
collection_name: str
|
92 |
+
image_url: Optional[str] = None
|
93 |
+
description: Optional[str] = None
|
94 |
+
traits: List[Dict[str, Any]] = field(default_factory=list)
|
95 |
+
last_updated: datetime = field(default_factory=datetime.now)
|
96 |
+
|
97 |
+
def to_dict(self) -> Dict[str, Any]:
|
98 |
+
"""Convert to dictionary format."""
|
99 |
+
return {
|
100 |
+
"contract": self.contract,
|
101 |
+
"token_id": self.token_id,
|
102 |
+
"name": self.name,
|
103 |
+
"collection_name": self.collection_name,
|
104 |
+
"image_url": self.image_url,
|
105 |
+
"description": self.description,
|
106 |
+
"traits": self.traits,
|
107 |
+
"last_updated": self.last_updated.isoformat()
|
108 |
+
}
|
109 |
+
|
110 |
+
@dataclass
|
111 |
+
class Config:
|
112 |
+
"""Enhanced application configuration."""
|
113 |
+
|
114 |
+
# Improved system prompt for more natural responses
|
115 |
+
SYSTEM_PROMPT: str = """
|
116 |
+
You are LOSS DOG 🐕 (Learning & Observing Smart Systems Digital Output Generator),
|
117 |
+
a friendly blockchain analysis assistant!
|
118 |
+
|
119 |
+
Your conversation style:
|
120 |
+
- Maintain natural dialogue flow
|
121 |
+
- Avoid repetitive responses
|
122 |
+
- Use context from previous messages
|
123 |
+
- Track which NFTs have been discussed
|
124 |
+
- Provide insights when relevant
|
125 |
+
|
126 |
+
When analyzing wallets:
|
127 |
+
- Reference specific NFTs by collection and ID
|
128 |
+
- Highlight interesting patterns
|
129 |
+
- Compare wallets if multiple available
|
130 |
+
- Monitor NFT rarity and traits
|
131 |
+
"""
|
132 |
+
|
133 |
+
# API Configuration
|
134 |
+
ETHERSCAN_BASE_URL: str = "https://api.etherscan.io/api"
|
135 |
+
OPENSEA_BASE_URL: str = "https://api.opensea.io/api/v2"
|
136 |
+
ETHEREUM_ADDRESS_REGEX: str = r"^0x[a-fA-F0-9]{40}$"
|
137 |
+
|
138 |
+
# Rate Limiting & Retries
|
139 |
+
RATE_LIMIT_DELAY: float = 0.2 # 5 requests per second
|
140 |
+
MAX_RETRIES: int = 3
|
141 |
+
RETRY_BASE_DELAY: float = 1.0
|
142 |
+
RETRY_MAX_DELAY: float = 10.0
|
143 |
+
|
144 |
+
# API Timeouts
|
145 |
+
REQUEST_TIMEOUT: float = 30.0
|
146 |
+
NFT_FETCH_TIMEOUT: int = 10
|
147 |
+
|
148 |
+
# NFT Processing
|
149 |
+
MAX_NFTS_PER_COLLECTION: int = 50
|
150 |
+
NFT_BATCH_SIZE: int = 5
|
151 |
+
MAX_IMAGE_SIZE: Tuple[int, int] = (800, 800)
|
152 |
+
|
153 |
+
# OpenAI Settings
|
154 |
+
OPENAI_MODEL: str = "gpt-4-0125-preview"
|
155 |
+
MAX_TOKENS: int = 8000
|
156 |
+
TEMPERATURE: float = 0.7
|
157 |
+
HISTORY_LIMIT: int = 10"""
|
158 |
+
Blockchain Wallet Analyzer - A tool for analyzing Ethereum wallet contents and NFT holdings.
|
159 |
+
|
160 |
+
This module provides a complete implementation of a blockchain wallet analysis tool
|
161 |
+
with a Gradio web interface. It includes improved NFT tracking, natural AI communication,
|
162 |
+
and enhanced error handling.
|
163 |
+
|
164 |
+
Author: Claude
|
165 |
+
Date: January 2025
|
166 |
+
"""
|
167 |
+
|
168 |
+
from __future__ import annotations
|
169 |
+
import os
|
170 |
+
import re
|
171 |
+
import json
|
172 |
+
import time
|
173 |
+
import logging
|
174 |
+
import asyncio
|
175 |
+
import base64
|
176 |
+
from typing import List, Dict, Tuple, Any, Optional, TypeVar, Set
|
177 |
+
from datetime import datetime
|
178 |
+
from decimal import Decimal
|
179 |
+
from dataclasses import dataclass, field
|
180 |
+
from pathlib import Path
|
181 |
+
from io import BytesIO
|
182 |
+
|
183 |
+
import aiohttp
|
184 |
+
import openai
|
185 |
+
import gradio as gr
|
186 |
+
from PIL import Image
|
187 |
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
188 |
+
|
189 |
+
# Type variables
|
190 |
+
T = TypeVar('T')
|
191 |
+
WalletData = Dict[str, Any]
|
192 |
+
ChatHistory = List[Tuple[str, str]]
|
193 |
+
NFTIdentifier = Tuple[str, str] # (contract, token_id)
|
194 |
+
|
195 |
+
# Configure logging
|
196 |
+
logging.basicConfig(
|
197 |
+
level=logging.INFO,
|
198 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
199 |
+
handlers=[
|
200 |
+
logging.FileHandler('blockchain_analyzer.log'),
|
201 |
+
logging.StreamHandler()
|
202 |
+
]
|
203 |
+
)
|
204 |
+
logger = logging.getLogger(__name__)
|
205 |
+
|
206 |
class BlockchainError(Exception):
|
207 |
"""Base exception for blockchain-related errors."""
|
208 |
pass
|
|
|
292 |
MAX_IMAGE_SIZE: Tuple[int, int] = (800, 800)
|
293 |
|
294 |
# OpenAI Settings
|
295 |
+
OPENAI_MODEL: str = "gpt-4-0125-preview"
|
296 |
MAX_TOKENS: int = 8000
|
297 |
TEMPERATURE: float = 0.7
|
298 |
HISTORY_LIMIT: int = 10
|