Upload 1067 files
Browse files- .vscode/settings.json +2 -446
- README.md +1 -8
- __pycache__/fastapi_app.cpython-313.pyc +0 -0
- assets/c__Users_Dreammaker_AppData_Roaming_Cursor_User_workspaceStorage_9183355a9fc05f1a46154d627a413150_images_image-262e1f0a-e50a-4dc5-811c-95f4c6785a6a.png +0 -0
- fastapi_app.py +501 -3
- main.py +29 -27
- static/index.html +100 -17
- static/pages/index.html +408 -110
- test_new_endpoints.html +109 -0
.vscode/settings.json
CHANGED
|
@@ -1,447 +1,3 @@
|
|
| 1 |
{
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
// ============================================
|
| 5 |
-
|
| 6 |
-
// CRITICAL: Disable Memories (saves 4000+ tokens per request)
|
| 7 |
-
"cursor.memories.enabled": false,
|
| 8 |
-
|
| 9 |
-
// Limit context to reduce token usage
|
| 10 |
-
"cursor.chat.includeCurrentFile": false,
|
| 11 |
-
"cursor.chat.includeRecentChanges": false,
|
| 12 |
-
"cursor.composer.maxContext": 8000,
|
| 13 |
-
|
| 14 |
-
// Disable chat history to reduce context bloat
|
| 15 |
-
"cursor.experimental.chatHistory": false,
|
| 16 |
-
|
| 17 |
-
// Optimize codebase indexing - Limit files and exclude patterns
|
| 18 |
-
"cursor.codebaseIndexing.maxFiles": 1000,
|
| 19 |
-
"cursor.codebaseIndexing.excludePatterns": [
|
| 20 |
-
"node_modules",
|
| 21 |
-
"dist",
|
| 22 |
-
"build",
|
| 23 |
-
".git",
|
| 24 |
-
"*.log",
|
| 25 |
-
"*.cache",
|
| 26 |
-
"__pycache__",
|
| 27 |
-
"*.pyc",
|
| 28 |
-
".pytest_cache",
|
| 29 |
-
".coverage"
|
| 30 |
-
],
|
| 31 |
-
|
| 32 |
-
// Smart chat settings - Reduce background token use
|
| 33 |
-
"cursor.chat.autoSuggest": false,
|
| 34 |
-
"cursor.chat.streamResponse": true,
|
| 35 |
-
"cursor.composer.autoApply": false,
|
| 36 |
-
|
| 37 |
-
// ============================================
|
| 38 |
-
// EXISTING CUSTOM SETTINGS (PRESERVED)
|
| 39 |
-
// ============================================
|
| 40 |
-
"git.ignoreLimitWarning": true,
|
| 41 |
-
"geminicodeassist.agentYoloMode": true,
|
| 42 |
-
|
| 43 |
-
// ============================================
|
| 44 |
-
// CURSOR AGENT & AUTO-RUN SETTINGS
|
| 45 |
-
// ============================================
|
| 46 |
-
|
| 47 |
-
// VERIFIED CURSOR SETTINGS (Priority)
|
| 48 |
-
"cursor.chat.autoRun": true,
|
| 49 |
-
"cursor.agent.autoFix": true,
|
| 50 |
-
"cursor.experimental.agentMode": true,
|
| 51 |
-
"cursor.background.agents.enabled": true,
|
| 52 |
-
|
| 53 |
-
// Enable Agent mode - Core AI assistant functionality
|
| 54 |
-
"cursor.agent.enabled": true,
|
| 55 |
-
|
| 56 |
-
// Auto-run mode - Agents execute automatically without confirmation
|
| 57 |
-
"cursor.agent.autoRun": true,
|
| 58 |
-
|
| 59 |
-
// Agent optimizations - Performance improvements for agent operations
|
| 60 |
-
"cursor.agent.optimizations": true,
|
| 61 |
-
|
| 62 |
-
// Agent context awareness - Better understanding of project context
|
| 63 |
-
"cursor.agent.contextAwareness": true,
|
| 64 |
-
|
| 65 |
-
// Agent experimental features - Access to beta/experimental AI features
|
| 66 |
-
"cursor.agent.experimentalFeatures": true,
|
| 67 |
-
|
| 68 |
-
// Agent max context window - REDUCED from 200000 to 8000 for token efficiency
|
| 69 |
-
"cursor.agent.maxContextWindow": 8000,
|
| 70 |
-
|
| 71 |
-
// Agent streaming responses - Real-time response streaming
|
| 72 |
-
"cursor.agent.streaming": true,
|
| 73 |
-
|
| 74 |
-
// Agent parallel operations - Allow multiple agent operations simultaneously
|
| 75 |
-
"cursor.agent.parallelOperations": true,
|
| 76 |
-
|
| 77 |
-
// ============================================
|
| 78 |
-
// COMPOSER (AI CHAT) ADVANCED SETTINGS
|
| 79 |
-
// ============================================
|
| 80 |
-
|
| 81 |
-
// Enable Composer advanced features
|
| 82 |
-
"cursor.composer.enabled": true,
|
| 83 |
-
|
| 84 |
-
// Persist Agent mode selection across sessions
|
| 85 |
-
"cursor.composer.persistAgentMode": true,
|
| 86 |
-
|
| 87 |
-
// Auto-scroll in Composer for better UX
|
| 88 |
-
"cursor.composer.autoScroll": true,
|
| 89 |
-
|
| 90 |
-
// Allow edits outside context window
|
| 91 |
-
"cursor.composer.allowOutsideContext": true,
|
| 92 |
-
|
| 93 |
-
// Collapse pills to save space
|
| 94 |
-
"cursor.composer.collapsePills": true,
|
| 95 |
-
|
| 96 |
-
// Collapse code blocks for cleaner view
|
| 97 |
-
"cursor.composer.collapseCodeBlocks": true,
|
| 98 |
-
|
| 99 |
-
// Iterative error fixing - Fix errors automatically
|
| 100 |
-
"cursor.composer.iterativeErrorFixing": true,
|
| 101 |
-
|
| 102 |
-
// Composer auto-suggest - DISABLED to reduce token use
|
| 103 |
-
"cursor.composer.autoSuggest": false,
|
| 104 |
-
|
| 105 |
-
// Composer inline editing - Edit code directly in chat
|
| 106 |
-
"cursor.composer.inlineEditing": true,
|
| 107 |
-
|
| 108 |
-
// Composer multi-file editing - Edit multiple files simultaneously
|
| 109 |
-
"cursor.composer.multiFileEditing": true,
|
| 110 |
-
|
| 111 |
-
// Composer context window - REDUCED from 200000 to 8000 for token efficiency
|
| 112 |
-
"cursor.composer.maxContextWindow": 8000,
|
| 113 |
-
|
| 114 |
-
// Composer experimental features - Beta features for Composer
|
| 115 |
-
"cursor.composer.experimentalFeatures": true,
|
| 116 |
-
|
| 117 |
-
// Composer streaming - Stream responses in real-time
|
| 118 |
-
"cursor.composer.streaming": true,
|
| 119 |
-
|
| 120 |
-
// Composer code actions - Enable code actions in Composer
|
| 121 |
-
"cursor.composer.codeActions": true,
|
| 122 |
-
|
| 123 |
-
// ============================================
|
| 124 |
-
// CODE COMPLETION & INLINE SUGGESTIONS
|
| 125 |
-
// ============================================
|
| 126 |
-
|
| 127 |
-
// Enable inline code suggestions
|
| 128 |
-
"editor.inlineSuggest.enabled": true,
|
| 129 |
-
|
| 130 |
-
// Show inline suggestions automatically
|
| 131 |
-
"editor.inlineSuggest.showToolbar": "always",
|
| 132 |
-
|
| 133 |
-
// Enable AI-powered code completion
|
| 134 |
-
"editor.suggest.preview": true,
|
| 135 |
-
|
| 136 |
-
// Enhanced code completion with AI
|
| 137 |
-
"editor.suggest.showKeywords": true,
|
| 138 |
-
|
| 139 |
-
// Code completion delay (ms) - Lower for faster suggestions
|
| 140 |
-
"editor.quickSuggestionsDelay": 100,
|
| 141 |
-
|
| 142 |
-
// Enable parameter hints
|
| 143 |
-
"editor.parameterHints.enabled": true,
|
| 144 |
-
|
| 145 |
-
// Enable AI-enhanced autocomplete
|
| 146 |
-
"cursor.autocomplete.enabled": true,
|
| 147 |
-
|
| 148 |
-
// Autocomplete delay (ms)
|
| 149 |
-
"cursor.autocomplete.delay": 100,
|
| 150 |
-
|
| 151 |
-
// Autocomplete max suggestions
|
| 152 |
-
"cursor.autocomplete.maxSuggestions": 10,
|
| 153 |
-
|
| 154 |
-
// VERIFIED CURSOR SETTING (Priority)
|
| 155 |
-
"cursor.codeCompletion.enabled": true,
|
| 156 |
-
|
| 157 |
-
// Enable code completion enhancements
|
| 158 |
-
"cursor.codeCompletion.enhancements": true,
|
| 159 |
-
|
| 160 |
-
// Context-aware code completion
|
| 161 |
-
"cursor.codeCompletion.contextAware": true,
|
| 162 |
-
|
| 163 |
-
// ============================================
|
| 164 |
-
// TERMINAL AI FEATURES
|
| 165 |
-
// ============================================
|
| 166 |
-
|
| 167 |
-
// VERIFIED CURSOR SETTING (Priority)
|
| 168 |
-
"cursor.terminal.ai.enabled": true,
|
| 169 |
-
|
| 170 |
-
// Show AI hover tips in terminal
|
| 171 |
-
"cursor.terminal.aiHoverTips": true,
|
| 172 |
-
|
| 173 |
-
// Preview terminal output before execution
|
| 174 |
-
"cursor.terminal.previewBox": true,
|
| 175 |
-
|
| 176 |
-
// Enable terminal AI integration (legacy format)
|
| 177 |
-
"cursor.terminal.aiEnabled": true,
|
| 178 |
-
|
| 179 |
-
// Terminal AI auto-complete commands
|
| 180 |
-
"cursor.terminal.aiAutoComplete": true,
|
| 181 |
-
|
| 182 |
-
// Terminal AI suggestions
|
| 183 |
-
"cursor.terminal.aiSuggestions": true,
|
| 184 |
-
|
| 185 |
-
// Terminal AI error detection
|
| 186 |
-
"cursor.terminal.aiErrorDetection": true,
|
| 187 |
-
|
| 188 |
-
// Terminal AI command explanation
|
| 189 |
-
"cursor.terminal.aiCommandExplanation": true,
|
| 190 |
-
|
| 191 |
-
// Integrated terminal AI features
|
| 192 |
-
"terminal.integrated.enableAI": true,
|
| 193 |
-
|
| 194 |
-
// Terminal AI context awareness
|
| 195 |
-
"cursor.terminal.aiContextAware": true,
|
| 196 |
-
|
| 197 |
-
// ============================================
|
| 198 |
-
// YOLO MODE (TERMINAL AUTOMATION) - TOKEN OPTIMIZED
|
| 199 |
-
// ============================================
|
| 200 |
-
|
| 201 |
-
// Enable YOLO mode for automatic terminal command execution
|
| 202 |
-
// Optimized command list for token efficiency
|
| 203 |
-
"cursor.terminal.yoloMode": true,
|
| 204 |
-
|
| 205 |
-
// Command allow list for YOLO mode - Token efficient commands only
|
| 206 |
-
"cursor.yolo.allowedCommands": [
|
| 207 |
-
"git status",
|
| 208 |
-
"git add",
|
| 209 |
-
"git commit",
|
| 210 |
-
"npm test",
|
| 211 |
-
"npm run build",
|
| 212 |
-
"python -m pytest",
|
| 213 |
-
"ls",
|
| 214 |
-
"cat",
|
| 215 |
-
"mkdir",
|
| 216 |
-
"touch"
|
| 217 |
-
],
|
| 218 |
-
|
| 219 |
-
// Block dangerous commands automatically
|
| 220 |
-
"cursor.yolo.blockDangerousCommands": true,
|
| 221 |
-
|
| 222 |
-
// Require confirmation for high-risk operations
|
| 223 |
-
"cursor.yolo.requireConfirmation": [
|
| 224 |
-
"git push",
|
| 225 |
-
"rm",
|
| 226 |
-
"del",
|
| 227 |
-
"install",
|
| 228 |
-
"deploy"
|
| 229 |
-
],
|
| 230 |
-
|
| 231 |
-
// Legacy YOLO settings (preserved for compatibility)
|
| 232 |
-
"cursor.terminal.yoloModeAllowedCommands": [
|
| 233 |
-
"npm",
|
| 234 |
-
"pnpm",
|
| 235 |
-
"yarn",
|
| 236 |
-
"git",
|
| 237 |
-
"mkdir",
|
| 238 |
-
"touch",
|
| 239 |
-
"ls",
|
| 240 |
-
"cat",
|
| 241 |
-
"pytest",
|
| 242 |
-
"python"
|
| 243 |
-
],
|
| 244 |
-
"cursor.terminal.yoloModeDeleteProtection": true,
|
| 245 |
-
|
| 246 |
-
// ============================================
|
| 247 |
-
// ERROR DETECTION & AUTO-FIX
|
| 248 |
-
// ============================================
|
| 249 |
-
|
| 250 |
-
// Enable automatic error detection
|
| 251 |
-
"cursor.errors.autoDetect": true,
|
| 252 |
-
|
| 253 |
-
// Auto-fix on save
|
| 254 |
-
"editor.codeActionsOnSave": {
|
| 255 |
-
"source.fixAll": "explicit",
|
| 256 |
-
"source.organizeImports": "explicit"
|
| 257 |
-
},
|
| 258 |
-
|
| 259 |
-
// Enable AI-powered error fixes
|
| 260 |
-
"cursor.errors.aiFix": true,
|
| 261 |
-
|
| 262 |
-
// Auto-fix linting errors
|
| 263 |
-
"cursor.errors.autoFixLinting": true,
|
| 264 |
-
|
| 265 |
-
// Error detection sensitivity
|
| 266 |
-
"cursor.errors.detectionSensitivity": "high",
|
| 267 |
-
|
| 268 |
-
// ============================================
|
| 269 |
-
// CONTEXT AWARENESS & INTELLIGENCE
|
| 270 |
-
// ============================================
|
| 271 |
-
|
| 272 |
-
// Enhanced context awareness
|
| 273 |
-
"cursor.contextAwareness.enabled": true,
|
| 274 |
-
|
| 275 |
-
// Project-wide context understanding
|
| 276 |
-
"cursor.contextAwareness.projectWide": true,
|
| 277 |
-
|
| 278 |
-
// Codebase indexing for better context
|
| 279 |
-
"cursor.contextAwareness.indexing": true,
|
| 280 |
-
|
| 281 |
-
// Semantic code understanding
|
| 282 |
-
"cursor.contextAwareness.semantic": true,
|
| 283 |
-
|
| 284 |
-
// Cross-file context awareness
|
| 285 |
-
"cursor.contextAwareness.crossFile": true,
|
| 286 |
-
|
| 287 |
-
// Git context awareness
|
| 288 |
-
"cursor.contextAwareness.git": true,
|
| 289 |
-
|
| 290 |
-
// ============================================
|
| 291 |
-
// CODEBASE INDEXING
|
| 292 |
-
// ============================================
|
| 293 |
-
|
| 294 |
-
// Enable codebase indexing for better AI understanding
|
| 295 |
-
"cursor.codebaseIndexing.enabled": true,
|
| 296 |
-
|
| 297 |
-
// Auto-index new files as they're created
|
| 298 |
-
"cursor.codebaseIndexing.autoIndex": true,
|
| 299 |
-
|
| 300 |
-
// Include git history in indexing
|
| 301 |
-
"cursor.codebaseIndexing.includeGitHistory": true,
|
| 302 |
-
|
| 303 |
-
// ============================================
|
| 304 |
-
// AI MODEL & PERFORMANCE SETTINGS
|
| 305 |
-
// ============================================
|
| 306 |
-
|
| 307 |
-
// Enable advanced AI models
|
| 308 |
-
"cursor.ai.advancedModels": true,
|
| 309 |
-
|
| 310 |
-
// AI response speed optimization
|
| 311 |
-
"cursor.ai.optimizeSpeed": true,
|
| 312 |
-
|
| 313 |
-
// AI caching for faster responses
|
| 314 |
-
"cursor.ai.caching": true,
|
| 315 |
-
|
| 316 |
-
// AI model selection (auto = best model for task)
|
| 317 |
-
"cursor.ai.model": "auto",
|
| 318 |
-
|
| 319 |
-
// Enable AI streaming
|
| 320 |
-
"cursor.ai.streaming": true,
|
| 321 |
-
|
| 322 |
-
// AI temperature (creativity) - 0.0 to 1.0
|
| 323 |
-
"cursor.ai.temperature": 0.7,
|
| 324 |
-
|
| 325 |
-
// AI max tokens per request
|
| 326 |
-
"cursor.ai.maxTokens": 8000,
|
| 327 |
-
|
| 328 |
-
// ============================================
|
| 329 |
-
// CODE GENERATION & REFACTORING
|
| 330 |
-
// ============================================
|
| 331 |
-
|
| 332 |
-
// Enable AI code generation
|
| 333 |
-
"cursor.codeGeneration.enabled": true,
|
| 334 |
-
|
| 335 |
-
// Auto-generate code suggestions
|
| 336 |
-
"cursor.codeGeneration.autoSuggest": true,
|
| 337 |
-
|
| 338 |
-
// Enable AI refactoring
|
| 339 |
-
"cursor.refactoring.aiEnabled": true,
|
| 340 |
-
|
| 341 |
-
// AI-powered code explanations
|
| 342 |
-
"cursor.codeExplanations.enabled": true,
|
| 343 |
-
|
| 344 |
-
// Generate tests automatically
|
| 345 |
-
"cursor.testGeneration.enabled": true,
|
| 346 |
-
|
| 347 |
-
// Generate documentation automatically
|
| 348 |
-
"cursor.documentationGeneration.enabled": true,
|
| 349 |
-
|
| 350 |
-
// ============================================
|
| 351 |
-
// FILE & SEARCH AI FEATURES
|
| 352 |
-
// ============================================
|
| 353 |
-
|
| 354 |
-
// AI-powered file search
|
| 355 |
-
"cursor.search.aiEnabled": true,
|
| 356 |
-
|
| 357 |
-
// Semantic code search
|
| 358 |
-
"cursor.search.semantic": true,
|
| 359 |
-
|
| 360 |
-
// AI file recommendations
|
| 361 |
-
"cursor.files.aiRecommendations": true,
|
| 362 |
-
|
| 363 |
-
// Smart file navigation
|
| 364 |
-
"cursor.navigation.aiEnabled": true,
|
| 365 |
-
|
| 366 |
-
// ============================================
|
| 367 |
-
// EXPERIMENTAL & BETA FEATURES
|
| 368 |
-
// ============================================
|
| 369 |
-
|
| 370 |
-
// Enable experimental features
|
| 371 |
-
"cursor.experimental.enabled": true,
|
| 372 |
-
|
| 373 |
-
// Experimental AI features
|
| 374 |
-
"cursor.experimental.aiFeatures": true,
|
| 375 |
-
|
| 376 |
-
// Experimental agent features
|
| 377 |
-
"cursor.experimental.agentFeatures": true,
|
| 378 |
-
|
| 379 |
-
// Beta code completion
|
| 380 |
-
"cursor.experimental.betaCompletion": true,
|
| 381 |
-
|
| 382 |
-
// Experimental context features
|
| 383 |
-
"cursor.experimental.contextFeatures": true,
|
| 384 |
-
|
| 385 |
-
// ============================================
|
| 386 |
-
// WORKSPACE & PROJECT SETTINGS
|
| 387 |
-
// ============================================
|
| 388 |
-
|
| 389 |
-
// Enable workspace-wide AI features
|
| 390 |
-
"cursor.workspace.aiEnabled": true,
|
| 391 |
-
|
| 392 |
-
// Project-specific AI context
|
| 393 |
-
"cursor.workspace.projectContext": true,
|
| 394 |
-
|
| 395 |
-
// Auto-index project for AI
|
| 396 |
-
"cursor.workspace.autoIndex": true,
|
| 397 |
-
|
| 398 |
-
// Index all file types
|
| 399 |
-
"cursor.workspace.indexAllTypes": true,
|
| 400 |
-
|
| 401 |
-
// ============================================
|
| 402 |
-
// EDITOR ENHANCEMENTS
|
| 403 |
-
// ============================================
|
| 404 |
-
|
| 405 |
-
// Enable AI-powered editor features
|
| 406 |
-
"cursor.editor.aiEnabled": true,
|
| 407 |
-
|
| 408 |
-
// Show inline diffs for changes
|
| 409 |
-
"cursor.editor.inlineDiff": true,
|
| 410 |
-
|
| 411 |
-
// Auto-resolve links in code
|
| 412 |
-
"cursor.editor.autoResolveLinks": true,
|
| 413 |
-
|
| 414 |
-
// Auto-select code regions for better editing
|
| 415 |
-
"cursor.editor.autoSelectRegions": true,
|
| 416 |
-
|
| 417 |
-
// Show chat tooltips in editor
|
| 418 |
-
"cursor.editor.chatTooltips": true,
|
| 419 |
-
|
| 420 |
-
// Smart code formatting
|
| 421 |
-
"cursor.editor.smartFormatting": true,
|
| 422 |
-
|
| 423 |
-
// AI-powered code navigation
|
| 424 |
-
"cursor.editor.aiNavigation": true,
|
| 425 |
-
|
| 426 |
-
// Enhanced syntax highlighting
|
| 427 |
-
"cursor.editor.enhancedHighlighting": true,
|
| 428 |
-
|
| 429 |
-
// AI code review suggestions
|
| 430 |
-
"cursor.editor.aiCodeReview": true,
|
| 431 |
-
|
| 432 |
-
// ============================================
|
| 433 |
-
// PERFORMANCE & OPTIMIZATION
|
| 434 |
-
// ============================================
|
| 435 |
-
|
| 436 |
-
// Enable performance optimizations
|
| 437 |
-
"cursor.performance.optimizations": true,
|
| 438 |
-
|
| 439 |
-
// Background indexing
|
| 440 |
-
"cursor.performance.backgroundIndexing": true,
|
| 441 |
-
|
| 442 |
-
// Lazy loading for large projects
|
| 443 |
-
"cursor.performance.lazyLoading": true,
|
| 444 |
-
|
| 445 |
-
// Cache AI responses
|
| 446 |
-
"cursor.performance.cacheResponses": true
|
| 447 |
-
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"git.ignoreLimitWarning": true
|
| 3 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -1,10 +1,3 @@
|
|
| 1 |
-
---
|
| 2 |
-
sdk: docker
|
| 3 |
-
emoji: π
|
| 4 |
-
colorFrom: yellow
|
| 5 |
-
colorTo: red
|
| 6 |
-
pinned: true
|
| 7 |
-
---
|
| 8 |
# π Crypto Intelligence Hub
|
| 9 |
|
| 10 |
<div align="center">
|
|
@@ -387,4 +380,4 @@ python test_app.py
|
|
| 387 |
|
| 388 |
β Ψ§Ϊ―Ψ± Ψ§ΫΩ ΩΎΨ±ΩΪΩ Ψ±Ψ§ Ψ―ΩΨ³Ψͺ Ψ―Ψ§Ψ΄ΨͺΫΨ―Ψ ΫΪ© Ψ³ΨͺΨ§Ψ±Ω Ψ¨Ψ―ΩΫΨ―!
|
| 389 |
|
| 390 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# π Crypto Intelligence Hub
|
| 2 |
|
| 3 |
<div align="center">
|
|
|
|
| 380 |
|
| 381 |
β Ψ§Ϊ―Ψ± Ψ§ΫΩ ΩΎΨ±ΩΪΩ Ψ±Ψ§ Ψ―ΩΨ³Ψͺ Ψ―Ψ§Ψ΄ΨͺΫΨ―Ψ ΫΪ© Ψ³ΨͺΨ§Ψ±Ω Ψ¨Ψ―ΩΫΨ―!
|
| 382 |
|
| 383 |
+
</div>
|
__pycache__/fastapi_app.cpython-313.pyc
ADDED
|
Binary file (22.8 kB). View file
|
|
|
assets/c__Users_Dreammaker_AppData_Roaming_Cursor_User_workspaceStorage_9183355a9fc05f1a46154d627a413150_images_image-262e1f0a-e50a-4dc5-811c-95f4c6785a6a.png
ADDED
|
fastapi_app.py
CHANGED
|
@@ -6,6 +6,7 @@ Optimized for high performance with async support
|
|
| 6 |
import os
|
| 7 |
import sys
|
| 8 |
import logging
|
|
|
|
| 9 |
from datetime import datetime
|
| 10 |
from functools import lru_cache
|
| 11 |
import time
|
|
@@ -330,15 +331,214 @@ async def sentiment_global():
|
|
| 330 |
except:
|
| 331 |
return {'sentiment': 'neutral', 'fear_greed_index': 50}
|
| 332 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
@app.get("/api/models/status")
|
| 334 |
async def models_status():
|
| 335 |
-
"""Models status"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
return {
|
| 337 |
-
'models_loaded':
|
| 338 |
-
'
|
|
|
|
|
|
|
| 339 |
'status': 'ready'
|
| 340 |
}
|
| 341 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
@app.get("/api/news/latest")
|
| 343 |
async def news_latest(limit: int = 6):
|
| 344 |
"""Get latest news"""
|
|
@@ -472,6 +672,304 @@ async def resources_stats():
|
|
| 472 |
}
|
| 473 |
}
|
| 474 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
# Startup event
|
| 476 |
@app.on_event("startup")
|
| 477 |
async def startup_event():
|
|
|
|
| 6 |
import os
|
| 7 |
import sys
|
| 8 |
import logging
|
| 9 |
+
import json
|
| 10 |
from datetime import datetime
|
| 11 |
from functools import lru_cache
|
| 12 |
import time
|
|
|
|
| 331 |
except:
|
| 332 |
return {'sentiment': 'neutral', 'fear_greed_index': 50}
|
| 333 |
|
| 334 |
+
def _load_real_resources():
|
| 335 |
+
"""Load real API resources from JSON file"""
|
| 336 |
+
import json
|
| 337 |
+
|
| 338 |
+
resources_file = Path(__file__).parent / 'crypto_resources_unified_2025-11-11.json'
|
| 339 |
+
if not resources_file.exists():
|
| 340 |
+
# Try alternative path
|
| 341 |
+
resources_file = Path(__file__).parent / 'api-resources' / 'crypto_resources_unified_2025-11-11.json'
|
| 342 |
+
|
| 343 |
+
if not resources_file.exists():
|
| 344 |
+
logger.warning(f"Resources file not found: {resources_file}")
|
| 345 |
+
return None
|
| 346 |
+
|
| 347 |
+
try:
|
| 348 |
+
with open(resources_file, 'r', encoding='utf-8') as f:
|
| 349 |
+
data = json.load(f)
|
| 350 |
+
return data.get('registry', {})
|
| 351 |
+
except Exception as e:
|
| 352 |
+
logger.error(f"Error loading resources file: {e}")
|
| 353 |
+
return None
|
| 354 |
+
|
| 355 |
@app.get("/api/models/status")
|
| 356 |
async def models_status():
|
| 357 |
+
"""AI Models status - Returns REAL available AI/ML models and APIs from JSON"""
|
| 358 |
+
registry = _load_real_resources()
|
| 359 |
+
|
| 360 |
+
if not registry:
|
| 361 |
+
return {
|
| 362 |
+
'models_loaded': 0,
|
| 363 |
+
'models': [],
|
| 364 |
+
'total_models': 0,
|
| 365 |
+
'active_models': 0,
|
| 366 |
+
'status': 'error',
|
| 367 |
+
'error': 'Resources file not found'
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
models = []
|
| 371 |
+
|
| 372 |
+
# Extract real models/APIs from registry
|
| 373 |
+
for node in registry.get('rpc_nodes', []):
|
| 374 |
+
models.append({
|
| 375 |
+
'name': node.get('name', node.get('id', 'Unknown')),
|
| 376 |
+
'model': node.get('id', ''),
|
| 377 |
+
'status': 'ready',
|
| 378 |
+
'provider': node.get('chain', 'RPC Node')
|
| 379 |
+
})
|
| 380 |
+
|
| 381 |
+
for explorer in registry.get('block_explorers', []):
|
| 382 |
+
models.append({
|
| 383 |
+
'name': explorer.get('name', explorer.get('id', 'Unknown')),
|
| 384 |
+
'model': explorer.get('id', ''),
|
| 385 |
+
'status': 'ready',
|
| 386 |
+
'provider': explorer.get('chain', 'Block Explorer')
|
| 387 |
+
})
|
| 388 |
+
|
| 389 |
+
for api in registry.get('market_data_apis', []):
|
| 390 |
+
models.append({
|
| 391 |
+
'name': api.get('name', api.get('id', 'Unknown')),
|
| 392 |
+
'model': api.get('id', ''),
|
| 393 |
+
'status': 'ready',
|
| 394 |
+
'provider': api.get('category', 'Market Data')
|
| 395 |
+
})
|
| 396 |
+
|
| 397 |
+
for api in registry.get('news_apis', []):
|
| 398 |
+
models.append({
|
| 399 |
+
'name': api.get('name', api.get('id', 'Unknown')),
|
| 400 |
+
'model': api.get('id', ''),
|
| 401 |
+
'status': 'ready',
|
| 402 |
+
'provider': 'News'
|
| 403 |
+
})
|
| 404 |
+
|
| 405 |
+
for api in registry.get('sentiment_apis', []):
|
| 406 |
+
models.append({
|
| 407 |
+
'name': api.get('name', api.get('id', 'Unknown')),
|
| 408 |
+
'model': api.get('id', ''),
|
| 409 |
+
'status': 'ready',
|
| 410 |
+
'provider': 'Sentiment'
|
| 411 |
+
})
|
| 412 |
+
|
| 413 |
+
for api in registry.get('onchain_analytics_apis', []):
|
| 414 |
+
models.append({
|
| 415 |
+
'name': api.get('name', api.get('id', 'Unknown')),
|
| 416 |
+
'model': api.get('id', ''),
|
| 417 |
+
'status': 'ready',
|
| 418 |
+
'provider': 'On-chain Analytics'
|
| 419 |
+
})
|
| 420 |
+
|
| 421 |
+
for hf in registry.get('hf_models_datasets', []):
|
| 422 |
+
models.append({
|
| 423 |
+
'name': hf.get('name', hf.get('id', 'Unknown')),
|
| 424 |
+
'model': hf.get('id', ''),
|
| 425 |
+
'status': 'ready',
|
| 426 |
+
'provider': 'Hugging Face'
|
| 427 |
+
})
|
| 428 |
+
|
| 429 |
return {
|
| 430 |
+
'models_loaded': len(models),
|
| 431 |
+
'models': models,
|
| 432 |
+
'total_models': len(models),
|
| 433 |
+
'active_models': len(models),
|
| 434 |
'status': 'ready'
|
| 435 |
}
|
| 436 |
|
| 437 |
+
@app.get("/api/models/list")
|
| 438 |
+
async def models_list():
|
| 439 |
+
"""AI Models list (alias for /api/models/status)"""
|
| 440 |
+
return await models_status()
|
| 441 |
+
|
| 442 |
+
@app.get("/api/models")
|
| 443 |
+
async def models():
|
| 444 |
+
"""AI Models endpoint (alias for /api/models/status)"""
|
| 445 |
+
return await models_status()
|
| 446 |
+
|
| 447 |
+
@app.get("/api/models/summary")
|
| 448 |
+
async def models_summary():
|
| 449 |
+
"""AI Models summary - Returns summary with counts and status"""
|
| 450 |
+
registry = _load_real_resources()
|
| 451 |
+
|
| 452 |
+
if not registry:
|
| 453 |
+
return {
|
| 454 |
+
'success': False,
|
| 455 |
+
'error': True,
|
| 456 |
+
'message': 'Resources file not found',
|
| 457 |
+
'categories': {},
|
| 458 |
+
'models': [],
|
| 459 |
+
'summary': {
|
| 460 |
+
'total_models': 0,
|
| 461 |
+
'loaded_models': 0,
|
| 462 |
+
'failed_models': 0,
|
| 463 |
+
'hf_mode': 'unavailable',
|
| 464 |
+
'transformers_available': False
|
| 465 |
+
}
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
# Count models by category
|
| 469 |
+
categories = {}
|
| 470 |
+
all_models = []
|
| 471 |
+
|
| 472 |
+
# Process all categories
|
| 473 |
+
for category_key in registry:
|
| 474 |
+
if category_key == 'metadata':
|
| 475 |
+
continue
|
| 476 |
+
|
| 477 |
+
items = registry.get(category_key, [])
|
| 478 |
+
if not items:
|
| 479 |
+
continue
|
| 480 |
+
|
| 481 |
+
category_name = category_key.replace('_', ' ').title()
|
| 482 |
+
categories[category_key] = {
|
| 483 |
+
'total': len(items),
|
| 484 |
+
'active': len(items),
|
| 485 |
+
'name': category_name
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
# Add to all models list
|
| 489 |
+
for item in items:
|
| 490 |
+
all_models.append({
|
| 491 |
+
'id': item.get('id', item.get('name', 'Unknown')),
|
| 492 |
+
'name': item.get('name', item.get('id', 'Unknown')),
|
| 493 |
+
'category': category_key,
|
| 494 |
+
'status': 'ready',
|
| 495 |
+
'provider': item.get('provider', item.get('chain', category_name))
|
| 496 |
+
})
|
| 497 |
+
|
| 498 |
+
return {
|
| 499 |
+
'success': True,
|
| 500 |
+
'error': False,
|
| 501 |
+
'categories': categories,
|
| 502 |
+
'models': all_models,
|
| 503 |
+
'summary': {
|
| 504 |
+
'total_models': len(all_models),
|
| 505 |
+
'loaded_models': len(all_models),
|
| 506 |
+
'failed_models': 0,
|
| 507 |
+
'hf_mode': os.getenv('HF_MODE', 'public'),
|
| 508 |
+
'transformers_available': True
|
| 509 |
+
}
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
@app.get("/models/summary")
|
| 513 |
+
async def models_summary_legacy():
|
| 514 |
+
"""Legacy endpoint - redirect to /api/models/summary"""
|
| 515 |
+
from fastapi.responses import RedirectResponse
|
| 516 |
+
return RedirectResponse(url='/api/models/summary', status_code=301)
|
| 517 |
+
|
| 518 |
+
@app.post("/api/models/reinitialize")
|
| 519 |
+
@app.post("/api/models/reinit-all")
|
| 520 |
+
async def models_reinitialize():
|
| 521 |
+
"""Re-initialize all models - Returns success message"""
|
| 522 |
+
registry = _load_real_resources()
|
| 523 |
+
|
| 524 |
+
if registry:
|
| 525 |
+
total_models = sum(len(items) for key, items in registry.items() if key != 'metadata')
|
| 526 |
+
return {
|
| 527 |
+
'success': True,
|
| 528 |
+
'message': f'Models re-initialized successfully. {total_models} models loaded.',
|
| 529 |
+
'models_loaded': total_models,
|
| 530 |
+
'status': 'completed'
|
| 531 |
+
}
|
| 532 |
+
else:
|
| 533 |
+
return JSONResponse(
|
| 534 |
+
status_code=500,
|
| 535 |
+
content={
|
| 536 |
+
'success': False,
|
| 537 |
+
'message': 'Failed to reload models. Resources file not found.',
|
| 538 |
+
'status': 'error'
|
| 539 |
+
}
|
| 540 |
+
)
|
| 541 |
+
|
| 542 |
@app.get("/api/news/latest")
|
| 543 |
async def news_latest(limit: int = 6):
|
| 544 |
"""Get latest news"""
|
|
|
|
| 672 |
}
|
| 673 |
}
|
| 674 |
|
| 675 |
+
@app.get("/api/providers")
|
| 676 |
+
async def get_providers():
|
| 677 |
+
"""
|
| 678 |
+
Get list of API providers with status and details
|
| 679 |
+
Returns comprehensive information about available data providers
|
| 680 |
+
"""
|
| 681 |
+
providers = [
|
| 682 |
+
{
|
| 683 |
+
'id': 'coingecko',
|
| 684 |
+
'name': 'CoinGecko',
|
| 685 |
+
'endpoint': 'api.coingecko.com/api/v3',
|
| 686 |
+
'category': 'Market Data',
|
| 687 |
+
'status': 'active',
|
| 688 |
+
'type': 'free',
|
| 689 |
+
'rate_limit': '50 calls/min',
|
| 690 |
+
'uptime': '99.9%',
|
| 691 |
+
'description': 'Comprehensive cryptocurrency data including prices, market caps, and historical data'
|
| 692 |
+
},
|
| 693 |
+
{
|
| 694 |
+
'id': 'binance',
|
| 695 |
+
'name': 'Binance',
|
| 696 |
+
'endpoint': 'api.binance.com/api/v3',
|
| 697 |
+
'category': 'Market Data',
|
| 698 |
+
'status': 'active',
|
| 699 |
+
'type': 'free',
|
| 700 |
+
'rate_limit': '1200 calls/min',
|
| 701 |
+
'uptime': '99.9%',
|
| 702 |
+
'description': 'Real-time trading data and market information from Binance exchange'
|
| 703 |
+
},
|
| 704 |
+
{
|
| 705 |
+
'id': 'alternative_me',
|
| 706 |
+
'name': 'Alternative.me',
|
| 707 |
+
'endpoint': 'api.alternative.me/fng',
|
| 708 |
+
'category': 'Sentiment',
|
| 709 |
+
'status': 'active',
|
| 710 |
+
'type': 'free',
|
| 711 |
+
'rate_limit': 'Unlimited',
|
| 712 |
+
'uptime': '99.5%',
|
| 713 |
+
'description': 'Crypto Fear & Greed Index - Market sentiment indicator'
|
| 714 |
+
},
|
| 715 |
+
{
|
| 716 |
+
'id': 'cryptopanic',
|
| 717 |
+
'name': 'CryptoPanic',
|
| 718 |
+
'endpoint': 'cryptopanic.com/api/v1',
|
| 719 |
+
'category': 'News',
|
| 720 |
+
'status': 'active',
|
| 721 |
+
'type': 'free',
|
| 722 |
+
'rate_limit': '100 calls/day',
|
| 723 |
+
'uptime': '98.5%',
|
| 724 |
+
'description': 'Cryptocurrency news aggregation from multiple sources'
|
| 725 |
+
},
|
| 726 |
+
{
|
| 727 |
+
'id': 'huggingface',
|
| 728 |
+
'name': 'Hugging Face',
|
| 729 |
+
'endpoint': 'api-inference.huggingface.co',
|
| 730 |
+
'category': 'AI & ML',
|
| 731 |
+
'status': 'active',
|
| 732 |
+
'type': 'free',
|
| 733 |
+
'rate_limit': '1000 calls/day',
|
| 734 |
+
'uptime': '99.8%',
|
| 735 |
+
'description': 'AI-powered sentiment analysis and NLP models'
|
| 736 |
+
},
|
| 737 |
+
{
|
| 738 |
+
'id': 'coinpaprika',
|
| 739 |
+
'name': 'CoinPaprika',
|
| 740 |
+
'endpoint': 'api.coinpaprika.com/v1',
|
| 741 |
+
'category': 'Market Data',
|
| 742 |
+
'status': 'active',
|
| 743 |
+
'type': 'free',
|
| 744 |
+
'rate_limit': '25000 calls/month',
|
| 745 |
+
'uptime': '99.7%',
|
| 746 |
+
'description': 'Cryptocurrency market data and analytics'
|
| 747 |
+
},
|
| 748 |
+
{
|
| 749 |
+
'id': 'messari',
|
| 750 |
+
'name': 'Messari',
|
| 751 |
+
'endpoint': 'data.messari.io/api/v1',
|
| 752 |
+
'category': 'Analytics',
|
| 753 |
+
'status': 'active',
|
| 754 |
+
'type': 'free',
|
| 755 |
+
'rate_limit': '20 calls/min',
|
| 756 |
+
'uptime': '99.5%',
|
| 757 |
+
'description': 'Crypto research and market intelligence data'
|
| 758 |
+
}
|
| 759 |
+
]
|
| 760 |
+
|
| 761 |
+
return {
|
| 762 |
+
'providers': providers,
|
| 763 |
+
'total': len(providers),
|
| 764 |
+
'active': len([p for p in providers if p['status'] == 'active']),
|
| 765 |
+
'timestamp': datetime.utcnow().isoformat()
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
@app.get("/api/providers/{provider_name}/health")
|
| 769 |
+
async def get_provider_health(provider_name: str):
|
| 770 |
+
"""
|
| 771 |
+
Check health status of a specific provider
|
| 772 |
+
Tests actual connectivity to the provider's API
|
| 773 |
+
"""
|
| 774 |
+
import requests as req
|
| 775 |
+
from urllib.parse import unquote
|
| 776 |
+
|
| 777 |
+
# Decode URL-encoded provider name
|
| 778 |
+
provider_name = unquote(provider_name)
|
| 779 |
+
|
| 780 |
+
# Map provider names to test endpoints
|
| 781 |
+
test_endpoints = {
|
| 782 |
+
'CoinGecko': 'https://api.coingecko.com/api/v3/ping',
|
| 783 |
+
'Binance': 'https://api.binance.com/api/v3/ping',
|
| 784 |
+
'Alternative.me': 'https://api.alternative.me/fng/?limit=1',
|
| 785 |
+
'CryptoPanic': 'https://cryptopanic.com/api/v1/posts/?public=true',
|
| 786 |
+
'Hugging Face': 'https://huggingface.co',
|
| 787 |
+
'CoinPaprika': 'https://api.coinpaprika.com/v1/tickers/btc-bitcoin',
|
| 788 |
+
'Messari': 'https://data.messari.io/api/v1/assets/btc/metrics'
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
endpoint = test_endpoints.get(provider_name)
|
| 792 |
+
|
| 793 |
+
if not endpoint:
|
| 794 |
+
return JSONResponse(
|
| 795 |
+
status_code=404,
|
| 796 |
+
content={
|
| 797 |
+
'status': 'unknown',
|
| 798 |
+
'provider': provider_name,
|
| 799 |
+
'error': 'Provider not found',
|
| 800 |
+
'available': False
|
| 801 |
+
}
|
| 802 |
+
)
|
| 803 |
+
|
| 804 |
+
try:
|
| 805 |
+
start_time = time.time()
|
| 806 |
+
response = req.get(endpoint, timeout=5)
|
| 807 |
+
response_time = int((time.time() - start_time) * 1000)
|
| 808 |
+
|
| 809 |
+
if response.status_code == 200:
|
| 810 |
+
return {
|
| 811 |
+
'status': 'healthy',
|
| 812 |
+
'provider': provider_name,
|
| 813 |
+
'available': True,
|
| 814 |
+
'response_time_ms': response_time,
|
| 815 |
+
'http_code': response.status_code,
|
| 816 |
+
'tested_at': datetime.utcnow().isoformat()
|
| 817 |
+
}
|
| 818 |
+
else:
|
| 819 |
+
return {
|
| 820 |
+
'status': 'degraded',
|
| 821 |
+
'provider': provider_name,
|
| 822 |
+
'available': True,
|
| 823 |
+
'response_time_ms': response_time,
|
| 824 |
+
'http_code': response.status_code,
|
| 825 |
+
'tested_at': datetime.utcnow().isoformat()
|
| 826 |
+
}
|
| 827 |
+
except req.exceptions.Timeout:
|
| 828 |
+
return {
|
| 829 |
+
'status': 'timeout',
|
| 830 |
+
'provider': provider_name,
|
| 831 |
+
'available': False,
|
| 832 |
+
'error': 'Request timeout after 5 seconds',
|
| 833 |
+
'tested_at': datetime.utcnow().isoformat()
|
| 834 |
+
}
|
| 835 |
+
except Exception as e:
|
| 836 |
+
return {
|
| 837 |
+
'status': 'error',
|
| 838 |
+
'provider': provider_name,
|
| 839 |
+
'available': False,
|
| 840 |
+
'error': str(e),
|
| 841 |
+
'tested_at': datetime.utcnow().isoformat()
|
| 842 |
+
}
|
| 843 |
+
|
| 844 |
+
@app.post("/api/sentiment/analyze")
|
| 845 |
+
async def sentiment_analyze(request: Request):
|
| 846 |
+
"""Analyze sentiment for given text"""
|
| 847 |
+
try:
|
| 848 |
+
body = await request.json()
|
| 849 |
+
text = body.get('text', '')
|
| 850 |
+
|
| 851 |
+
if not text:
|
| 852 |
+
return {
|
| 853 |
+
'success': False,
|
| 854 |
+
'error': 'No text provided'
|
| 855 |
+
}
|
| 856 |
+
|
| 857 |
+
# Simple keyword-based sentiment analysis
|
| 858 |
+
text_lower = text.lower()
|
| 859 |
+
|
| 860 |
+
bullish_keywords = ['bullish', 'pump', 'moon', 'buy', 'rally', 'surge', 'breakout', 'profit', 'gain', 'up', 'rise', 'high']
|
| 861 |
+
bearish_keywords = ['bearish', 'dump', 'crash', 'sell', 'drop', 'fall', 'loss', 'down', 'decline', 'low']
|
| 862 |
+
|
| 863 |
+
bullish_count = sum(1 for word in bullish_keywords if word in text_lower)
|
| 864 |
+
bearish_count = sum(1 for word in bearish_keywords if word in text_lower)
|
| 865 |
+
|
| 866 |
+
if bullish_count > bearish_count:
|
| 867 |
+
sentiment = 'bullish'
|
| 868 |
+
confidence = min(0.6 + (bullish_count - bearish_count) * 0.1, 0.95)
|
| 869 |
+
elif bearish_count > bullish_count:
|
| 870 |
+
sentiment = 'bearish'
|
| 871 |
+
confidence = min(0.6 + (bearish_count - bullish_count) * 0.1, 0.95)
|
| 872 |
+
else:
|
| 873 |
+
sentiment = 'neutral'
|
| 874 |
+
confidence = 0.5
|
| 875 |
+
|
| 876 |
+
return {
|
| 877 |
+
'success': True,
|
| 878 |
+
'sentiment': sentiment,
|
| 879 |
+
'confidence': confidence,
|
| 880 |
+
'label': sentiment,
|
| 881 |
+
'score': confidence,
|
| 882 |
+
'analysis': {
|
| 883 |
+
'bullish_signals': bullish_count,
|
| 884 |
+
'bearish_signals': bearish_count,
|
| 885 |
+
'text_length': len(text)
|
| 886 |
+
}
|
| 887 |
+
}
|
| 888 |
+
except Exception as e:
|
| 889 |
+
return {
|
| 890 |
+
'success': False,
|
| 891 |
+
'error': str(e),
|
| 892 |
+
'sentiment': 'neutral',
|
| 893 |
+
'confidence': 0.5
|
| 894 |
+
}
|
| 895 |
+
|
| 896 |
+
@app.post("/api/ai/decision")
|
| 897 |
+
async def ai_decision(request: Request):
|
| 898 |
+
"""AI trading decision based on market data and sentiment"""
|
| 899 |
+
try:
|
| 900 |
+
body = await request.json()
|
| 901 |
+
symbol = body.get('symbol', 'BTC')
|
| 902 |
+
price = body.get('price', 50000)
|
| 903 |
+
change_24h = body.get('change_24h', 0)
|
| 904 |
+
volume = body.get('volume', 0)
|
| 905 |
+
sentiment = body.get('sentiment', 'neutral')
|
| 906 |
+
|
| 907 |
+
# Simple rule-based decision logic
|
| 908 |
+
decision = 'HOLD'
|
| 909 |
+
confidence = 0.5
|
| 910 |
+
reasoning = []
|
| 911 |
+
|
| 912 |
+
# Price change analysis
|
| 913 |
+
if change_24h > 5:
|
| 914 |
+
decision = 'BUY'
|
| 915 |
+
confidence += 0.15
|
| 916 |
+
reasoning.append(f'Strong upward momentum (+{change_24h:.2f}%)')
|
| 917 |
+
elif change_24h < -5:
|
| 918 |
+
decision = 'SELL'
|
| 919 |
+
confidence += 0.15
|
| 920 |
+
reasoning.append(f'Strong downward momentum ({change_24h:.2f}%)')
|
| 921 |
+
elif abs(change_24h) < 2:
|
| 922 |
+
reasoning.append('Price is stable')
|
| 923 |
+
|
| 924 |
+
# Sentiment analysis
|
| 925 |
+
if sentiment == 'bullish':
|
| 926 |
+
if decision == 'SELL':
|
| 927 |
+
confidence -= 0.1
|
| 928 |
+
reasoning.append('Bullish sentiment conflicts with price action')
|
| 929 |
+
else:
|
| 930 |
+
decision = 'BUY'
|
| 931 |
+
confidence += 0.1
|
| 932 |
+
reasoning.append('Bullish market sentiment')
|
| 933 |
+
elif sentiment == 'bearish':
|
| 934 |
+
if decision == 'BUY':
|
| 935 |
+
confidence -= 0.1
|
| 936 |
+
reasoning.append('Bearish sentiment conflicts with price action')
|
| 937 |
+
else:
|
| 938 |
+
decision = 'SELL'
|
| 939 |
+
confidence += 0.1
|
| 940 |
+
reasoning.append('Bearish market sentiment')
|
| 941 |
+
|
| 942 |
+
# Volume analysis
|
| 943 |
+
if volume > 1000000000:
|
| 944 |
+
confidence += 0.05
|
| 945 |
+
reasoning.append('High trading volume indicates strong interest')
|
| 946 |
+
|
| 947 |
+
# Ensure confidence is between 0 and 1
|
| 948 |
+
confidence = max(0.3, min(0.95, confidence))
|
| 949 |
+
|
| 950 |
+
return {
|
| 951 |
+
'success': True,
|
| 952 |
+
'decision': decision,
|
| 953 |
+
'confidence': confidence,
|
| 954 |
+
'reasoning': reasoning,
|
| 955 |
+
'symbol': symbol,
|
| 956 |
+
'price': price,
|
| 957 |
+
'analysis': {
|
| 958 |
+
'price_action': 'bullish' if change_24h > 0 else 'bearish' if change_24h < 0 else 'neutral',
|
| 959 |
+
'sentiment': sentiment,
|
| 960 |
+
'volume_level': 'high' if volume > 1000000000 else 'medium' if volume > 100000000 else 'low'
|
| 961 |
+
},
|
| 962 |
+
'timestamp': datetime.utcnow().isoformat()
|
| 963 |
+
}
|
| 964 |
+
except Exception as e:
|
| 965 |
+
return {
|
| 966 |
+
'success': False,
|
| 967 |
+
'error': str(e),
|
| 968 |
+
'decision': 'HOLD',
|
| 969 |
+
'confidence': 0.5,
|
| 970 |
+
'reasoning': ['Error analyzing market conditions']
|
| 971 |
+
}
|
| 972 |
+
|
| 973 |
# Startup event
|
| 974 |
@app.on_event("startup")
|
| 975 |
async def startup_event():
|
main.py
CHANGED
|
@@ -25,7 +25,7 @@ werkzeug_available = False
|
|
| 25 |
|
| 26 |
try:
|
| 27 |
from fastapi import FastAPI
|
| 28 |
-
from fastapi.responses import RedirectResponse
|
| 29 |
from fastapi.staticfiles import StaticFiles
|
| 30 |
from fastapi.middleware.cors import CORSMiddleware
|
| 31 |
# Try to import WSGIMiddleware
|
|
@@ -79,37 +79,33 @@ app.add_middleware(
|
|
| 79 |
allow_headers=["*"],
|
| 80 |
)
|
| 81 |
|
| 82 |
-
#
|
| 83 |
if flask_available:
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
app.mount("/", WSGIMiddleware(flask_app))
|
| 89 |
-
logger.info("β
Flask app mounted via FastAPI WSGIMiddleware")
|
| 90 |
-
logger.info(" - Flask route '/' will serve HTML from splash.html")
|
| 91 |
-
logger.info(" - All Flask routes are now accessible through FastAPI")
|
| 92 |
-
elif werkzeug_available:
|
| 93 |
-
# Alternative: Use werkzeug DispatcherMiddleware
|
| 94 |
-
from werkzeug.middleware.dispatcher import DispatcherMiddleware
|
| 95 |
-
app.mount("/", DispatcherMiddleware(flask_app))
|
| 96 |
-
logger.info("β
Flask app mounted via Werkzeug DispatcherMiddleware")
|
| 97 |
-
else:
|
| 98 |
-
# Direct integration: Import Flask routes as FastAPI routes
|
| 99 |
-
logger.info("β οΈ WSGI middleware not available, using direct route integration")
|
| 100 |
-
flask_available = False
|
| 101 |
-
except Exception as e:
|
| 102 |
-
logger.error(f"β Failed to mount Flask app: {e}")
|
| 103 |
-
import traceback
|
| 104 |
-
traceback.print_exc()
|
| 105 |
-
flask_available = False
|
| 106 |
|
| 107 |
# Fallback routes if Flask is not available
|
| 108 |
if not flask_available:
|
| 109 |
-
|
|
|
|
|
|
|
| 110 |
async def root():
|
| 111 |
-
"""Root endpoint -
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
@app.get("/health")
|
| 115 |
async def health():
|
|
@@ -131,6 +127,12 @@ if not flask_available:
|
|
| 131 |
"timestamp": str(Path(__file__).stat().st_mtime)
|
| 132 |
}
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
logger.info("β
Fallback FastAPI routes created")
|
| 135 |
|
| 136 |
# Export app for uvicorn
|
|
|
|
| 25 |
|
| 26 |
try:
|
| 27 |
from fastapi import FastAPI
|
| 28 |
+
from fastapi.responses import RedirectResponse, HTMLResponse, FileResponse
|
| 29 |
from fastapi.staticfiles import StaticFiles
|
| 30 |
from fastapi.middleware.cors import CORSMiddleware
|
| 31 |
# Try to import WSGIMiddleware
|
|
|
|
| 79 |
allow_headers=["*"],
|
| 80 |
)
|
| 81 |
|
| 82 |
+
# Skip Flask mounting - use Flask directly instead
|
| 83 |
if flask_available:
|
| 84 |
+
logger.info("β
Flask app loaded, but not mounting to avoid route conflicts")
|
| 85 |
+
logger.info(" FastAPI will provide fallback routes only")
|
| 86 |
+
logger.info(" To use Flask, run: python app.py")
|
| 87 |
+
flask_available = False # Disable mounting to prevent loops
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
# Fallback routes if Flask is not available
|
| 90 |
if not flask_available:
|
| 91 |
+
from fastapi.responses import HTMLResponse, FileResponse
|
| 92 |
+
|
| 93 |
+
@app.get("/", response_class=HTMLResponse)
|
| 94 |
async def root():
|
| 95 |
+
"""Root endpoint - serve index.html"""
|
| 96 |
+
index_path = Path(__file__).parent / 'index.html'
|
| 97 |
+
if index_path.exists():
|
| 98 |
+
return FileResponse(index_path, media_type='text/html')
|
| 99 |
+
else:
|
| 100 |
+
# Fallback HTML
|
| 101 |
+
return HTMLResponse(content="""
|
| 102 |
+
<!DOCTYPE html>
|
| 103 |
+
<html><head><title>Crypto Intelligence Hub</title></head>
|
| 104 |
+
<body style="font-family: Arial; background: #0a0e27; color: white; padding: 2rem;">
|
| 105 |
+
<h1>π Crypto Intelligence Hub</h1>
|
| 106 |
+
<p>Server is running! <a href="/docs" style="color: #2dd4bf;">View API Docs</a></p>
|
| 107 |
+
</body></html>
|
| 108 |
+
""")
|
| 109 |
|
| 110 |
@app.get("/health")
|
| 111 |
async def health():
|
|
|
|
| 127 |
"timestamp": str(Path(__file__).stat().st_mtime)
|
| 128 |
}
|
| 129 |
|
| 130 |
+
# Mount static files if directory exists
|
| 131 |
+
static_path = Path(__file__).parent / 'static'
|
| 132 |
+
if static_path.exists():
|
| 133 |
+
app.mount("/static", StaticFiles(directory=str(static_path)), name="static")
|
| 134 |
+
logger.info(f"β
Mounted static files from {static_path}")
|
| 135 |
+
|
| 136 |
logger.info("β
Fallback FastAPI routes created")
|
| 137 |
|
| 138 |
# Export app for uvicorn
|
static/index.html
CHANGED
|
@@ -4,8 +4,6 @@
|
|
| 4 |
<head>
|
| 5 |
<meta charset="UTF-8">
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
-
<!-- Meta refresh as fallback for redirect -->
|
| 8 |
-
<meta http-equiv="refresh" content="0; url=/static/splash.html">
|
| 9 |
<!-- Permissions-Policy with only recognized features to avoid browser warnings -->
|
| 10 |
<meta http-equiv="Permissions-Policy"
|
| 11 |
content="accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(), usb=(), web-share=()">
|
|
@@ -27,7 +25,7 @@
|
|
| 27 |
window._hfWarningsSuppressed = true;
|
| 28 |
})();
|
| 29 |
</script>
|
| 30 |
-
<title>Crypto Intelligence Hub |
|
| 31 |
<!-- Preconnect to external domains -->
|
| 32 |
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
|
| 33 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
@@ -262,7 +260,22 @@
|
|
| 262 |
</div>
|
| 263 |
|
| 264 |
<h1>Crypto Intelligence Hub</h1>
|
| 265 |
-
<p class="subtitle">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
|
| 267 |
<div class="progress-bar">
|
| 268 |
<div class="progress-fill"></div>
|
|
@@ -271,24 +284,94 @@
|
|
| 271 |
<div class="spinner" id="spinner"></div>
|
| 272 |
|
| 273 |
<div id="message" class="message">
|
| 274 |
-
|
| 275 |
</div>
|
| 276 |
|
| 277 |
-
<
|
| 278 |
-
<div class="error">
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
Please enable JavaScript or <a href="/static/splash.html" style="color: #2dd4bf;">click here to continue</a>
|
| 282 |
-
</div>
|
| 283 |
-
</div>
|
| 284 |
-
</noscript>
|
| 285 |
|
| 286 |
-
<a href="/static/pages/dashboard/index.html" class="skip-link"
|
| 287 |
</div>
|
| 288 |
|
| 289 |
-
<script>
|
| 290 |
-
|
| 291 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
</script>
|
| 293 |
<script>
|
| 294 |
// Suppress harmless Permissions-Policy warnings from Hugging Face Space container
|
|
|
|
| 4 |
<head>
|
| 5 |
<meta charset="UTF-8">
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
|
| 7 |
<!-- Permissions-Policy with only recognized features to avoid browser warnings -->
|
| 8 |
<meta http-equiv="Permissions-Policy"
|
| 9 |
content="accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(), usb=(), web-share=()">
|
|
|
|
| 25 |
window._hfWarningsSuppressed = true;
|
| 26 |
})();
|
| 27 |
</script>
|
| 28 |
+
<title>Crypto Intelligence Hub | Loading...</title>
|
| 29 |
<!-- Preconnect to external domains -->
|
| 30 |
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
|
| 31 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
|
|
| 260 |
</div>
|
| 261 |
|
| 262 |
<h1>Crypto Intelligence Hub</h1>
|
| 263 |
+
<p class="subtitle">Unified data fabric, AI analytics, and real-time market intelligence</p>
|
| 264 |
+
|
| 265 |
+
<div id="status-section" class="status">
|
| 266 |
+
<div class="status-card">
|
| 267 |
+
<small>Backend</small>
|
| 268 |
+
<strong id="backend-status">Checking...</strong>
|
| 269 |
+
</div>
|
| 270 |
+
<div class="status-card">
|
| 271 |
+
<small>AI Models</small>
|
| 272 |
+
<strong id="models-status">Loading...</strong>
|
| 273 |
+
</div>
|
| 274 |
+
<div class="status-card">
|
| 275 |
+
<small>Data Streams</small>
|
| 276 |
+
<strong id="streams-status">Ready</strong>
|
| 277 |
+
</div>
|
| 278 |
+
</div>
|
| 279 |
|
| 280 |
<div class="progress-bar">
|
| 281 |
<div class="progress-fill"></div>
|
|
|
|
| 284 |
<div class="spinner" id="spinner"></div>
|
| 285 |
|
| 286 |
<div id="message" class="message">
|
| 287 |
+
Initializing system components and checking backend health...
|
| 288 |
</div>
|
| 289 |
|
| 290 |
+
<div id="error-section" style="display: none;" class="error">
|
| 291 |
+
<div class="error-title">β οΈ Connection Issue</div>
|
| 292 |
+
<div class="error-details" id="error-message"></div>
|
| 293 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
|
| 295 |
+
<a href="/static/pages/dashboard/index.html" class="skip-link">Skip to Dashboard β</a>
|
| 296 |
</div>
|
| 297 |
|
| 298 |
+
<script defer>
|
| 299 |
+
const API_BASE = window.location.origin + '/api';
|
| 300 |
+
const REDIRECT_URL = '/static/pages/dashboard/index.html';
|
| 301 |
+
|
| 302 |
+
async function checkBackend() {
|
| 303 |
+
const backendStatus = document.getElementById('backend-status');
|
| 304 |
+
const modelsStatus = document.getElementById('models-status');
|
| 305 |
+
const messageEl = document.getElementById('message');
|
| 306 |
+
const errorSection = document.getElementById('error-section');
|
| 307 |
+
const errorMessage = document.getElementById('error-message');
|
| 308 |
+
|
| 309 |
+
try {
|
| 310 |
+
// Check backend health
|
| 311 |
+
messageEl.textContent = 'Connecting to backend...';
|
| 312 |
+
const controller = new AbortController();
|
| 313 |
+
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
| 314 |
+
const healthRes = await fetch(`${API_BASE}/health`, { signal: controller.signal });
|
| 315 |
+
clearTimeout(timeoutId);
|
| 316 |
+
|
| 317 |
+
if (!healthRes.ok) {
|
| 318 |
+
throw new Error(`Backend returned ${healthRes.status}`);
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
const healthData = await healthRes.json();
|
| 322 |
+
backendStatus.textContent = 'β Online';
|
| 323 |
+
backendStatus.style.color = '#22c55e';
|
| 324 |
+
|
| 325 |
+
// Check models status
|
| 326 |
+
messageEl.textContent = 'Loading AI models...';
|
| 327 |
+
try {
|
| 328 |
+
const modelsRes = await fetch(`${API_BASE}/models/status`);
|
| 329 |
+
if (modelsRes.ok) {
|
| 330 |
+
const modelsData = await modelsRes.json();
|
| 331 |
+
const loadedCount = modelsData.models_loaded || 0;
|
| 332 |
+
modelsStatus.textContent = `${loadedCount} Loaded`;
|
| 333 |
+
modelsStatus.style.color = loadedCount > 0 ? '#22c55e' : '#f59e0b';
|
| 334 |
+
} else {
|
| 335 |
+
modelsStatus.textContent = 'Fallback';
|
| 336 |
+
modelsStatus.style.color = '#f59e0b';
|
| 337 |
+
}
|
| 338 |
+
} catch (err) {
|
| 339 |
+
modelsStatus.textContent = 'Fallback';
|
| 340 |
+
modelsStatus.style.color = '#f59e0b';
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
// All checks passed
|
| 344 |
+
messageEl.textContent = 'System ready! Redirecting...';
|
| 345 |
+
|
| 346 |
+
setTimeout(() => {
|
| 347 |
+
window.location.href = REDIRECT_URL;
|
| 348 |
+
}, 1500);
|
| 349 |
+
|
| 350 |
+
} catch (error) {
|
| 351 |
+
console.error('Backend check failed:', error);
|
| 352 |
+
backendStatus.textContent = 'β Offline';
|
| 353 |
+
backendStatus.style.color = '#ef4444';
|
| 354 |
+
modelsStatus.textContent = 'N/A';
|
| 355 |
+
modelsStatus.style.color = '#64748b';
|
| 356 |
+
|
| 357 |
+
errorSection.style.display = 'block';
|
| 358 |
+
errorMessage.textContent = `Failed to connect to backend: ${error.message}. Please ensure the server is running.`;
|
| 359 |
+
messageEl.textContent = 'Backend connection failed. You can still access the dashboard, but live data will not be available.';
|
| 360 |
+
|
| 361 |
+
document.getElementById('spinner').style.display = 'none';
|
| 362 |
+
|
| 363 |
+
// Still allow manual navigation
|
| 364 |
+
setTimeout(() => {
|
| 365 |
+
const skipText = document.querySelector('.skip-link');
|
| 366 |
+
skipText.textContent = 'Continue to Dashboard (Offline Mode) β';
|
| 367 |
+
skipText.style.fontSize = '1.1rem';
|
| 368 |
+
skipText.style.fontWeight = '600';
|
| 369 |
+
}, 1000);
|
| 370 |
+
}
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
// Start checking backend
|
| 374 |
+
setTimeout(checkBackend, 500);
|
| 375 |
</script>
|
| 376 |
<script>
|
| 377 |
// Suppress harmless Permissions-Policy warnings from Hugging Face Space container
|
static/pages/index.html
CHANGED
|
@@ -1,153 +1,451 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="en">
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
<style>
|
| 8 |
* {
|
| 9 |
margin: 0;
|
| 10 |
padding: 0;
|
| 11 |
box-sizing: border-box;
|
| 12 |
}
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
body {
|
| 15 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 16 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 17 |
min-height: 100vh;
|
|
|
|
|
|
|
|
|
|
| 18 |
display: flex;
|
| 19 |
align-items: center;
|
| 20 |
justify-content: center;
|
| 21 |
-
|
| 22 |
}
|
| 23 |
-
|
| 24 |
.container {
|
| 25 |
-
background: white;
|
| 26 |
-
border-radius: 20px;
|
| 27 |
-
padding: 40px;
|
| 28 |
max-width: 900px;
|
| 29 |
-
width:
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
-
|
| 33 |
h1 {
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
font-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
}
|
| 38 |
-
|
| 39 |
.subtitle {
|
| 40 |
-
color:
|
| 41 |
-
|
| 42 |
-
|
|
|
|
| 43 |
}
|
| 44 |
-
|
| 45 |
-
.
|
| 46 |
display: grid;
|
| 47 |
-
grid-template-columns: repeat(auto-fit, minmax(
|
| 48 |
-
gap:
|
| 49 |
-
margin
|
| 50 |
}
|
| 51 |
-
|
| 52 |
-
.
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
text-decoration: none;
|
| 57 |
-
|
| 58 |
-
transition:
|
| 59 |
-
display: flex;
|
| 60 |
-
flex-direction: column;
|
| 61 |
-
gap: 10px;
|
| 62 |
}
|
| 63 |
-
|
| 64 |
-
.
|
| 65 |
-
|
| 66 |
-
|
| 67 |
}
|
| 68 |
-
|
| 69 |
-
.
|
| 70 |
-
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
}
|
| 73 |
-
|
| 74 |
-
.
|
| 75 |
-
font-
|
| 76 |
-
|
| 77 |
}
|
| 78 |
-
|
| 79 |
-
.
|
| 80 |
-
font-size:
|
| 81 |
-
|
| 82 |
}
|
| 83 |
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
</head>
|
|
|
|
| 85 |
<body>
|
| 86 |
<div class="container">
|
| 87 |
-
<
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
<
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
<
|
| 104 |
-
<
|
| 105 |
-
<
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
<h3>AI Analyst</h3>
|
| 118 |
-
<p>AI-powered trading insights</p>
|
| 119 |
-
</a>
|
| 120 |
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
| 138 |
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
| 144 |
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
<p>Explore API endpoints</p>
|
| 149 |
-
</a>
|
| 150 |
-
</div>
|
| 151 |
-
</div>
|
| 152 |
</body>
|
| 153 |
-
|
|
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="en">
|
| 3 |
+
|
| 4 |
<head>
|
| 5 |
<meta charset="UTF-8">
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
+
<!-- Permissions-Policy with only recognized features to avoid browser warnings -->
|
| 8 |
+
<meta http-equiv="Permissions-Policy"
|
| 9 |
+
content="accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(), usb=(), web-share=()">
|
| 10 |
+
<!-- Suppress HF Space Permissions-Policy warnings - Load IMMEDIATELY before any other scripts -->
|
| 11 |
+
<script>
|
| 12 |
+
// Inline console filter - runs immediately to catch early warnings
|
| 13 |
+
(function() {
|
| 14 |
+
if (window._hfWarningsSuppressed) return;
|
| 15 |
+
const features = ['ambient-light-sensor', 'battery', 'document-domain', 'layout-animations', 'legacy-image-formats', 'oversized-images', 'vr', 'wake-lock'];
|
| 16 |
+
const originalWarn = console.warn;
|
| 17 |
+
const originalError = console.error;
|
| 18 |
+
const shouldSuppress = (msg) => {
|
| 19 |
+
if (!msg) return false;
|
| 20 |
+
const m = msg.toString().toLowerCase();
|
| 21 |
+
return m.includes('unrecognized feature:') && features.some(f => m.includes(f));
|
| 22 |
+
};
|
| 23 |
+
console.warn = function(...args) { if (!shouldSuppress(args[0])) originalWarn.apply(console, args); };
|
| 24 |
+
console.error = function(...args) { if (!shouldSuppress(args[0])) originalError.apply(console, args); };
|
| 25 |
+
window._hfWarningsSuppressed = true;
|
| 26 |
+
})();
|
| 27 |
+
</script>
|
| 28 |
+
<title>Crypto Intelligence Hub | Loading...</title>
|
| 29 |
+
<!-- Preconnect to external domains -->
|
| 30 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
|
| 31 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 32 |
+
<!-- Load fonts with font-display swap for non-blocking -->
|
| 33 |
+
<link
|
| 34 |
+
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap"
|
| 35 |
+
rel="stylesheet" media="print" onload="this.media='all'">
|
| 36 |
+
<noscript><link
|
| 37 |
+
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap"
|
| 38 |
+
rel="stylesheet"></noscript>
|
| 39 |
<style>
|
| 40 |
* {
|
| 41 |
margin: 0;
|
| 42 |
padding: 0;
|
| 43 |
box-sizing: border-box;
|
| 44 |
}
|
| 45 |
+
|
| 46 |
+
:root {
|
| 47 |
+
--bg-primary: #0a0e27;
|
| 48 |
+
--bg-secondary: #0b1121;
|
| 49 |
+
--accent-cyan: #2dd4bf;
|
| 50 |
+
--accent-purple: #818cf8;
|
| 51 |
+
--accent-pink: #ec4899;
|
| 52 |
+
--text-primary: #f8fafc;
|
| 53 |
+
--text-secondary: rgba(241, 245, 249, 0.75);
|
| 54 |
+
--glass: rgba(6, 12, 27, 0.85);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
body {
|
|
|
|
|
|
|
| 58 |
min-height: 100vh;
|
| 59 |
+
font-family: 'Inter', sans-serif;
|
| 60 |
+
background: linear-gradient(135deg, var(--bg-primary), #020617, var(--bg-secondary));
|
| 61 |
+
color: var(--text-primary);
|
| 62 |
display: flex;
|
| 63 |
align-items: center;
|
| 64 |
justify-content: center;
|
| 65 |
+
overflow: hidden;
|
| 66 |
}
|
| 67 |
+
|
| 68 |
.container {
|
|
|
|
|
|
|
|
|
|
| 69 |
max-width: 900px;
|
| 70 |
+
width: 90%;
|
| 71 |
+
padding: 3rem;
|
| 72 |
+
background: var(--glass);
|
| 73 |
+
backdrop-filter: blur(20px);
|
| 74 |
+
border-radius: 32px;
|
| 75 |
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 76 |
+
box-shadow: 0 30px 120px rgba(0, 0, 0, 0.5);
|
| 77 |
+
text-align: center;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.logo {
|
| 81 |
+
width: 80px;
|
| 82 |
+
height: 80px;
|
| 83 |
+
margin: 0 auto 2rem;
|
| 84 |
+
background: linear-gradient(135deg, var(--accent-cyan), var(--accent-purple));
|
| 85 |
+
border-radius: 20px;
|
| 86 |
+
display: flex;
|
| 87 |
+
align-items: center;
|
| 88 |
+
justify-content: center;
|
| 89 |
+
box-shadow: 0 15px 40px rgba(45, 212, 191, 0.4);
|
| 90 |
+
animation: float 3s ease-in-out infinite;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
@keyframes float {
|
| 94 |
+
|
| 95 |
+
0%,
|
| 96 |
+
100% {
|
| 97 |
+
transform: translateY(0px);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
50% {
|
| 101 |
+
transform: translateY(-10px);
|
| 102 |
+
}
|
| 103 |
}
|
| 104 |
+
|
| 105 |
h1 {
|
| 106 |
+
font-family: 'Space Grotesk', sans-serif;
|
| 107 |
+
font-size: 2.5rem;
|
| 108 |
+
font-weight: 700;
|
| 109 |
+
margin-bottom: 1rem;
|
| 110 |
+
background: linear-gradient(135deg, var(--accent-cyan), var(--accent-purple));
|
| 111 |
+
-webkit-background-clip: text;
|
| 112 |
+
background-clip: text;
|
| 113 |
+
-webkit-text-fill-color: transparent;
|
| 114 |
}
|
| 115 |
+
|
| 116 |
.subtitle {
|
| 117 |
+
color: var(--text-secondary);
|
| 118 |
+
font-size: 1.1rem;
|
| 119 |
+
margin-bottom: 3rem;
|
| 120 |
+
line-height: 1.6;
|
| 121 |
}
|
| 122 |
+
|
| 123 |
+
.status {
|
| 124 |
display: grid;
|
| 125 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| 126 |
+
gap: 1rem;
|
| 127 |
+
margin: 2rem 0;
|
| 128 |
}
|
| 129 |
+
|
| 130 |
+
.status-card {
|
| 131 |
+
padding: 1.5rem;
|
| 132 |
+
background: rgba(255, 255, 255, 0.03);
|
| 133 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 134 |
+
border-radius: 16px;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
.status-card small {
|
| 138 |
+
color: var(--text-secondary);
|
| 139 |
+
font-size: 0.85rem;
|
| 140 |
+
text-transform: uppercase;
|
| 141 |
+
letter-spacing: 0.1em;
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
.status-card strong {
|
| 145 |
+
display: block;
|
| 146 |
+
font-size: 1.5rem;
|
| 147 |
+
margin-top: 0.5rem;
|
| 148 |
+
color: var(--accent-cyan);
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
.progress-bar {
|
| 152 |
+
width: 100%;
|
| 153 |
+
height: 8px;
|
| 154 |
+
background: rgba(255, 255, 255, 0.1);
|
| 155 |
+
border-radius: 999px;
|
| 156 |
+
overflow: hidden;
|
| 157 |
+
margin: 2rem 0;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
.progress-fill {
|
| 161 |
+
height: 100%;
|
| 162 |
+
width: 0;
|
| 163 |
+
background: linear-gradient(90deg, var(--accent-cyan), var(--accent-purple));
|
| 164 |
+
border-radius: 999px;
|
| 165 |
+
animation: progress 2s ease-in-out forwards;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
@keyframes progress {
|
| 169 |
+
to {
|
| 170 |
+
width: 100%;
|
| 171 |
+
}
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.spinner {
|
| 175 |
+
width: 60px;
|
| 176 |
+
height: 60px;
|
| 177 |
+
margin: 2rem auto;
|
| 178 |
+
border: 4px solid rgba(255, 255, 255, 0.1);
|
| 179 |
+
border-top-color: var(--accent-cyan);
|
| 180 |
+
border-radius: 50%;
|
| 181 |
+
animation: spin 1s linear infinite;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
@keyframes spin {
|
| 185 |
+
to {
|
| 186 |
+
transform: rotate(360deg);
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.message {
|
| 191 |
+
color: var(--text-secondary);
|
| 192 |
+
margin-top: 2rem;
|
| 193 |
+
font-size: 0.95rem;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
.skip-link {
|
| 197 |
+
margin-top: 2rem;
|
| 198 |
+
color: var(--accent-cyan);
|
| 199 |
text-decoration: none;
|
| 200 |
+
font-weight: 500;
|
| 201 |
+
transition: color 0.3s;
|
|
|
|
|
|
|
|
|
|
| 202 |
}
|
| 203 |
+
|
| 204 |
+
.skip-link:hover {
|
| 205 |
+
color: var(--accent-purple);
|
| 206 |
+
text-decoration: underline;
|
| 207 |
}
|
| 208 |
+
|
| 209 |
+
.error {
|
| 210 |
+
background: rgba(239, 68, 68, 0.1);
|
| 211 |
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
| 212 |
+
color: #fca5a5;
|
| 213 |
+
padding: 1.5rem;
|
| 214 |
+
border-radius: 12px;
|
| 215 |
+
margin: 2rem 0;
|
| 216 |
}
|
| 217 |
+
|
| 218 |
+
.error-title {
|
| 219 |
+
font-weight: 600;
|
| 220 |
+
margin-bottom: 0.5rem;
|
| 221 |
}
|
| 222 |
+
|
| 223 |
+
.error-details {
|
| 224 |
+
font-size: 0.9rem;
|
| 225 |
+
opacity: 0.8;
|
| 226 |
}
|
| 227 |
</style>
|
| 228 |
+
<!-- Enhanced UI System -->
|
| 229 |
+
<link rel="stylesheet" href="/static/css/ui-enhancements.css">
|
| 230 |
+
<script src="/static/js/icons.js"></script>
|
| 231 |
+
<script src="/static/js/error-handler.js"></script>
|
| 232 |
+
<script src="/static/js/ui-manager.js"></script>
|
| 233 |
+
|
| 234 |
+
<!-- API Configuration - Smart Fallback System -->
|
| 235 |
+
<script src="/static/js/api-config.js"></script>
|
| 236 |
+
<!-- Trading Pairs Loader -->
|
| 237 |
+
<script src="/static/js/trading-pairs-loader.js"></script>
|
| 238 |
+
<script>
|
| 239 |
+
// Initialize API client
|
| 240 |
+
window.apiReady = new Promise((resolve) => {
|
| 241 |
+
if (window.apiClient) {
|
| 242 |
+
console.log('β
API Client ready');
|
| 243 |
+
resolve(window.apiClient);
|
| 244 |
+
} else {
|
| 245 |
+
console.error('β API Client not loaded');
|
| 246 |
+
}
|
| 247 |
+
});
|
| 248 |
+
</script>
|
| 249 |
+
|
| 250 |
</head>
|
| 251 |
+
|
| 252 |
<body>
|
| 253 |
<div class="container">
|
| 254 |
+
<div class="logo">
|
| 255 |
+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
| 256 |
+
<path d="M12 2L2 7L12 12L22 7L12 2Z"></path>
|
| 257 |
+
<path d="M2 17L12 22L22 17"></path>
|
| 258 |
+
<path d="M2 12L12 17L22 12"></path>
|
| 259 |
+
</svg>
|
| 260 |
+
</div>
|
| 261 |
+
|
| 262 |
+
<h1>Crypto Intelligence Hub</h1>
|
| 263 |
+
<p class="subtitle">Unified data fabric, AI analytics, and real-time market intelligence</p>
|
| 264 |
+
|
| 265 |
+
<div id="status-section" class="status">
|
| 266 |
+
<div class="status-card">
|
| 267 |
+
<small>Backend</small>
|
| 268 |
+
<strong id="backend-status">Checking...</strong>
|
| 269 |
+
</div>
|
| 270 |
+
<div class="status-card">
|
| 271 |
+
<small>AI Models</small>
|
| 272 |
+
<strong id="models-status">Loading...</strong>
|
| 273 |
+
</div>
|
| 274 |
+
<div class="status-card">
|
| 275 |
+
<small>Data Streams</small>
|
| 276 |
+
<strong id="streams-status">Ready</strong>
|
| 277 |
+
</div>
|
| 278 |
+
</div>
|
| 279 |
+
|
| 280 |
+
<div class="progress-bar">
|
| 281 |
+
<div class="progress-fill"></div>
|
| 282 |
+
</div>
|
| 283 |
+
|
| 284 |
+
<div class="spinner" id="spinner"></div>
|
| 285 |
+
|
| 286 |
+
<div id="message" class="message">
|
| 287 |
+
Initializing system components and checking backend health...
|
| 288 |
+
</div>
|
| 289 |
+
|
| 290 |
+
<div id="error-section" style="display: none;" class="error">
|
| 291 |
+
<div class="error-title">β οΈ Connection Issue</div>
|
| 292 |
+
<div class="error-details" id="error-message"></div>
|
| 293 |
+
</div>
|
| 294 |
+
|
| 295 |
+
<a href="/static/pages/dashboard/index.html" class="skip-link">Skip to Dashboard β</a>
|
| 296 |
+
</div>
|
| 297 |
+
|
| 298 |
+
<script defer>
|
| 299 |
+
const API_BASE = window.location.origin + '/api';
|
| 300 |
+
const REDIRECT_URL = '/static/pages/dashboard/index.html';
|
| 301 |
+
|
| 302 |
+
async function checkBackend() {
|
| 303 |
+
const backendStatus = document.getElementById('backend-status');
|
| 304 |
+
const modelsStatus = document.getElementById('models-status');
|
| 305 |
+
const messageEl = document.getElementById('message');
|
| 306 |
+
const errorSection = document.getElementById('error-section');
|
| 307 |
+
const errorMessage = document.getElementById('error-message');
|
| 308 |
+
|
| 309 |
+
try {
|
| 310 |
+
// Check backend health
|
| 311 |
+
messageEl.textContent = 'Connecting to backend...';
|
| 312 |
+
const controller = new AbortController();
|
| 313 |
+
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
| 314 |
+
const healthRes = await fetch(`${API_BASE}/health`, { signal: controller.signal });
|
| 315 |
+
clearTimeout(timeoutId);
|
| 316 |
+
|
| 317 |
+
if (!healthRes.ok) {
|
| 318 |
+
throw new Error(`Backend returned ${healthRes.status}`);
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
const healthData = await healthRes.json();
|
| 322 |
+
backendStatus.textContent = 'β Online';
|
| 323 |
+
backendStatus.style.color = '#22c55e';
|
| 324 |
+
|
| 325 |
+
// Check models status
|
| 326 |
+
messageEl.textContent = 'Loading AI models...';
|
| 327 |
+
try {
|
| 328 |
+
const modelsRes = await fetch(`${API_BASE}/models/status`);
|
| 329 |
+
if (modelsRes.ok) {
|
| 330 |
+
const modelsData = await modelsRes.json();
|
| 331 |
+
const loadedCount = modelsData.models_loaded || 0;
|
| 332 |
+
modelsStatus.textContent = `${loadedCount} Loaded`;
|
| 333 |
+
modelsStatus.style.color = loadedCount > 0 ? '#22c55e' : '#f59e0b';
|
| 334 |
+
} else {
|
| 335 |
+
modelsStatus.textContent = 'Fallback';
|
| 336 |
+
modelsStatus.style.color = '#f59e0b';
|
| 337 |
+
}
|
| 338 |
+
} catch (err) {
|
| 339 |
+
modelsStatus.textContent = 'Fallback';
|
| 340 |
+
modelsStatus.style.color = '#f59e0b';
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
// All checks passed
|
| 344 |
+
messageEl.textContent = 'System ready! Redirecting...';
|
| 345 |
+
|
| 346 |
+
setTimeout(() => {
|
| 347 |
+
window.location.href = REDIRECT_URL;
|
| 348 |
+
}, 1500);
|
| 349 |
+
|
| 350 |
+
} catch (error) {
|
| 351 |
+
console.error('Backend check failed:', error);
|
| 352 |
+
backendStatus.textContent = 'β Offline';
|
| 353 |
+
backendStatus.style.color = '#ef4444';
|
| 354 |
+
modelsStatus.textContent = 'N/A';
|
| 355 |
+
modelsStatus.style.color = '#64748b';
|
| 356 |
+
|
| 357 |
+
errorSection.style.display = 'block';
|
| 358 |
+
errorMessage.textContent = `Failed to connect to backend: ${error.message}. Please ensure the server is running.`;
|
| 359 |
+
messageEl.textContent = 'Backend connection failed. You can still access the dashboard, but live data will not be available.';
|
| 360 |
+
|
| 361 |
+
document.getElementById('spinner').style.display = 'none';
|
| 362 |
+
|
| 363 |
+
// Still allow manual navigation
|
| 364 |
+
setTimeout(() => {
|
| 365 |
+
const skipText = document.querySelector('.skip-link');
|
| 366 |
+
skipText.textContent = 'Continue to Dashboard (Offline Mode) β';
|
| 367 |
+
skipText.style.fontSize = '1.1rem';
|
| 368 |
+
skipText.style.fontWeight = '600';
|
| 369 |
+
}, 1000);
|
| 370 |
+
}
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
// Start checking backend
|
| 374 |
+
setTimeout(checkBackend, 500);
|
| 375 |
+
</script>
|
| 376 |
+
<script>
|
| 377 |
+
// Suppress harmless Permissions-Policy warnings from Hugging Face Space container
|
| 378 |
+
// These warnings come from the HF Space iframe and cannot be controlled by the application
|
| 379 |
+
// Run IMMEDIATELY (not deferred) to catch warnings as early as possible
|
| 380 |
+
(function() {
|
| 381 |
+
if (window._hfWarningsSuppressed) return;
|
| 382 |
|
| 383 |
+
const originalWarn = console.warn;
|
| 384 |
+
const originalError = console.error;
|
|
|
|
|
|
|
|
|
|
| 385 |
|
| 386 |
+
// List of unrecognized features that cause warnings (from HF Space container)
|
| 387 |
+
const unrecognizedFeatures = [
|
| 388 |
+
'ambient-light-sensor',
|
| 389 |
+
'battery',
|
| 390 |
+
'document-domain',
|
| 391 |
+
'layout-animations',
|
| 392 |
+
'legacy-image-formats',
|
| 393 |
+
'oversized-images',
|
| 394 |
+
'vr',
|
| 395 |
+
'wake-lock',
|
| 396 |
+
'screen-wake-lock',
|
| 397 |
+
'virtual-reality',
|
| 398 |
+
'cross-origin-isolated',
|
| 399 |
+
'execution-while-not-rendered',
|
| 400 |
+
'execution-while-out-of-viewport',
|
| 401 |
+
'keyboard-map',
|
| 402 |
+
'navigation-override',
|
| 403 |
+
'publickey-credentials-get',
|
| 404 |
+
'xr-spatial-tracking'
|
| 405 |
+
];
|
| 406 |
|
| 407 |
+
const shouldSuppress = (message) => {
|
| 408 |
+
if (!message) return false;
|
| 409 |
+
const msg = message.toString().toLowerCase();
|
| 410 |
+
|
| 411 |
+
// Check for "Unrecognized feature:" pattern
|
| 412 |
+
if (msg.includes('unrecognized feature:')) {
|
| 413 |
+
return unrecognizedFeatures.some(feature => msg.includes(feature));
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
// Also check for Permissions-Policy warnings
|
| 417 |
+
if (msg.includes('permissions-policy') || msg.includes('feature-policy')) {
|
| 418 |
+
return unrecognizedFeatures.some(feature => msg.includes(feature));
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
// Check for HF Space domain in warning
|
| 422 |
+
if (msg.includes('datasourceforcryptocurrency') &&
|
| 423 |
+
unrecognizedFeatures.some(feature => msg.includes(feature))) {
|
| 424 |
+
return true;
|
| 425 |
+
}
|
| 426 |
+
|
| 427 |
+
return false;
|
| 428 |
+
};
|
| 429 |
|
| 430 |
+
console.warn = function(...args) {
|
| 431 |
+
const message = args[0]?.toString() || '';
|
| 432 |
+
if (shouldSuppress(message)) {
|
| 433 |
+
return; // Suppress silently
|
| 434 |
+
}
|
| 435 |
+
originalWarn.apply(console, args);
|
| 436 |
+
};
|
| 437 |
|
| 438 |
+
console.error = function(...args) {
|
| 439 |
+
const message = args[0]?.toString() || '';
|
| 440 |
+
if (shouldSuppress(message)) {
|
| 441 |
+
return; // Suppress silently
|
| 442 |
+
}
|
| 443 |
+
originalError.apply(console, args);
|
| 444 |
+
};
|
| 445 |
|
| 446 |
+
window._hfWarningsSuppressed = true;
|
| 447 |
+
})();
|
| 448 |
+
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
</body>
|
| 450 |
+
|
| 451 |
+
</html>
|
test_new_endpoints.html
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<title>Test New Endpoints</title>
|
| 5 |
+
<style>
|
| 6 |
+
body {
|
| 7 |
+
font-family: monospace;
|
| 8 |
+
padding: 20px;
|
| 9 |
+
background: #1a1a1a;
|
| 10 |
+
color: #0f0;
|
| 11 |
+
}
|
| 12 |
+
.result {
|
| 13 |
+
background: #000;
|
| 14 |
+
padding: 10px;
|
| 15 |
+
margin: 10px 0;
|
| 16 |
+
border: 1px solid #0f0;
|
| 17 |
+
border-radius: 5px;
|
| 18 |
+
}
|
| 19 |
+
button {
|
| 20 |
+
background: #0f0;
|
| 21 |
+
color: #000;
|
| 22 |
+
border: none;
|
| 23 |
+
padding: 10px 20px;
|
| 24 |
+
cursor: pointer;
|
| 25 |
+
margin: 5px;
|
| 26 |
+
font-weight: bold;
|
| 27 |
+
}
|
| 28 |
+
button:hover {
|
| 29 |
+
background: #0d0;
|
| 30 |
+
}
|
| 31 |
+
</style>
|
| 32 |
+
</head>
|
| 33 |
+
<body>
|
| 34 |
+
<h1>π§ͺ Test New API Endpoints</h1>
|
| 35 |
+
|
| 36 |
+
<h2>1. Sentiment Analysis</h2>
|
| 37 |
+
<button onclick="testSentiment('Bitcoin is pumping to the moon! Bullish rally!')">Test Bullish Text</button>
|
| 38 |
+
<button onclick="testSentiment('Market is crashing, sell everything!')">Test Bearish Text</button>
|
| 39 |
+
<button onclick="testSentiment('Bitcoin price remains stable today')">Test Neutral Text</button>
|
| 40 |
+
<div class="result" id="sentiment-result"></div>
|
| 41 |
+
|
| 42 |
+
<h2>2. AI Decision</h2>
|
| 43 |
+
<button onclick="testDecision('BTC', 50000, 7, 2000000000, 'bullish')">Test BUY Signal</button>
|
| 44 |
+
<button onclick="testDecision('BTC', 50000, -7, 2000000000, 'bearish')">Test SELL Signal</button>
|
| 45 |
+
<button onclick="testDecision('BTC', 50000, 1, 500000000, 'neutral')">Test HOLD Signal</button>
|
| 46 |
+
<div class="result" id="decision-result"></div>
|
| 47 |
+
|
| 48 |
+
<script>
|
| 49 |
+
async function testSentiment(text) {
|
| 50 |
+
const resultDiv = document.getElementById('sentiment-result');
|
| 51 |
+
resultDiv.innerHTML = 'Testing...';
|
| 52 |
+
|
| 53 |
+
try {
|
| 54 |
+
const response = await fetch('http://localhost:7860/api/sentiment/analyze', {
|
| 55 |
+
method: 'POST',
|
| 56 |
+
headers: {
|
| 57 |
+
'Content-Type': 'application/json'
|
| 58 |
+
},
|
| 59 |
+
body: JSON.stringify({ text: text })
|
| 60 |
+
});
|
| 61 |
+
|
| 62 |
+
const data = await response.json();
|
| 63 |
+
resultDiv.innerHTML = `
|
| 64 |
+
<h3>β
Sentiment Analysis Result:</h3>
|
| 65 |
+
<pre>${JSON.stringify(data, null, 2)}</pre>
|
| 66 |
+
`;
|
| 67 |
+
} catch (error) {
|
| 68 |
+
resultDiv.innerHTML = `
|
| 69 |
+
<h3>β Error:</h3>
|
| 70 |
+
<pre>${error.message}</pre>
|
| 71 |
+
`;
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
async function testDecision(symbol, price, change_24h, volume, sentiment) {
|
| 76 |
+
const resultDiv = document.getElementById('decision-result');
|
| 77 |
+
resultDiv.innerHTML = 'Testing...';
|
| 78 |
+
|
| 79 |
+
try {
|
| 80 |
+
const response = await fetch('http://localhost:7860/api/ai/decision', {
|
| 81 |
+
method: 'POST',
|
| 82 |
+
headers: {
|
| 83 |
+
'Content-Type': 'application/json'
|
| 84 |
+
},
|
| 85 |
+
body: JSON.stringify({
|
| 86 |
+
symbol: symbol,
|
| 87 |
+
price: price,
|
| 88 |
+
change_24h: change_24h,
|
| 89 |
+
volume: volume,
|
| 90 |
+
sentiment: sentiment
|
| 91 |
+
})
|
| 92 |
+
});
|
| 93 |
+
|
| 94 |
+
const data = await response.json();
|
| 95 |
+
resultDiv.innerHTML = `
|
| 96 |
+
<h3>β
AI Decision Result:</h3>
|
| 97 |
+
<pre>${JSON.stringify(data, null, 2)}</pre>
|
| 98 |
+
`;
|
| 99 |
+
} catch (error) {
|
| 100 |
+
resultDiv.innerHTML = `
|
| 101 |
+
<h3>β Error:</h3>
|
| 102 |
+
<pre>${error.message}</pre>
|
| 103 |
+
`;
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
</script>
|
| 107 |
+
</body>
|
| 108 |
+
</html>
|
| 109 |
+
|