upgraedd commited on
Commit
183b606
·
verified ·
1 Parent(s): f0a7554

Create HISTORICAL_ART_ANALYSIS_API

Browse files

This is an exploration into possible methods of proper analysis regarding multi-cultural, historic/ancient artistic means, visually.

Files changed (1) hide show
  1. HISTORICAL_ART_ANALYSIS_API +638 -0
HISTORICAL_ART_ANALYSIS_API ADDED
@@ -0,0 +1,638 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ PRODUCTION-READY TRUTH REVELATION API
4
+ Complete system with proper architecture, error handling, and scalability
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import time
10
+ from dataclasses import dataclass, asdict
11
+ from enum import Enum
12
+ from typing import Dict, List, Any, Optional, Tuple
13
+ from contextlib import asynccontextmanager
14
+ import json
15
+ import os
16
+
17
+ from fastapi import FastAPI, HTTPException, UploadFile, File, Form, Depends
18
+ from fastapi.middleware.cors import CORSMiddleware
19
+ from fastapi.responses import JSONResponse
20
+ from pydantic import BaseModel, Field
21
+ import numpy as np
22
+ from PIL import Image
23
+ import cv2
24
+ from scipy import ndimage
25
+ import torch
26
+ import torch.nn as nn
27
+ from torchvision import models, transforms
28
+ import aiofiles
29
+ from redis import asyncio as aioredis
30
+ import psutil
31
+ import prometheus_client
32
+ from prometheus_client import Counter, Histogram, Gauge
33
+
34
+ # Configuration
35
+ class Config:
36
+ REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
37
+ MODEL_CACHE_SIZE = int(os.getenv("MODEL_CACHE_SIZE", "100"))
38
+ MAX_IMAGE_SIZE = int(os.getenv("MAX_IMAGE_SIZE", "10485760")) # 10MB
39
+ REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "30"))
40
+ LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
41
+
42
+ # Analysis thresholds
43
+ HIGH_TRUTH_THRESHOLD = 0.75
44
+ MEDIUM_TRUTH_THRESHOLD = 0.6
45
+ MIN_CONFIDENCE = 0.3
46
+
47
+ # Logging setup
48
+ logging.basicConfig(
49
+ level=getattr(logging, Config.LOG_LEVEL),
50
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
51
+ )
52
+ logger = logging.getLogger("truth_revelation_api")
53
+
54
+ # Metrics
55
+ REQUEST_COUNT = Counter('request_total', 'Total requests', ['method', 'endpoint'])
56
+ REQUEST_DURATION = Histogram('request_duration_seconds', 'Request duration')
57
+ ACTIVE_REQUESTS = Gauge('active_requests', 'Active requests')
58
+ TRUTH_SCORE_DISTRIBUTION = Histogram('truth_score', 'Truth score distribution', buckets=[0.1, 0.3, 0.5, 0.7, 0.9, 1.0])
59
+
60
+ # Data Models
61
+ class AnalysisRequest(BaseModel):
62
+ text_content: Optional[str] = Field(None, description="Text content to analyze")
63
+ domain: Optional[str] = Field(None, description="Artistic domain")
64
+ context: Dict[str, Any] = Field(default_factory=dict)
65
+
66
+ class ImageAnalysisRequest(BaseModel):
67
+ description: Optional[str] = Field(None, description="Image description for context")
68
+ context: Dict[str, Any] = Field(default_factory=dict)
69
+
70
+ class AnalysisResponse(BaseModel):
71
+ request_id: str
72
+ status: str
73
+ truth_score: float
74
+ confidence: float
75
+ archetypes: List[str]
76
+ patterns: List[str]
77
+ visualization_prompt: Optional[str] = None
78
+ processing_time: float
79
+ timestamp: str
80
+
81
+ class HealthResponse(BaseModel):
82
+ status: str
83
+ version: str
84
+ redis_connected: bool
85
+ memory_usage: float
86
+ active_requests: int
87
+
88
+ # Enums
89
+ class ArtisticDomain(str, Enum):
90
+ LITERATURE = "literature"
91
+ VISUAL_ARTS = "visual_arts"
92
+ MUSIC = "music"
93
+ PERFORMING_ARTS = "performing_arts"
94
+ ARCHITECTURE = "architecture"
95
+
96
+ class TruthArchetype(str, Enum):
97
+ COSMIC_REVELATION = "cosmic_revelation"
98
+ HISTORICAL_CIPHER = "historical_cipher"
99
+ CONSCIOUSNESS_CODE = "consciousness_code"
100
+ ESOTERIC_SYMBOL = "esoteric_symbol"
101
+
102
+ # Core Analysis Engine
103
+ class ProductionImageAnalyzer:
104
+ def __init__(self):
105
+ self.model = self._load_model()
106
+ self.transform = transforms.Compose([
107
+ transforms.Resize((224, 224)),
108
+ transforms.ToTensor(),
109
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
110
+ ])
111
+
112
+ def _load_model(self):
113
+ """Load production-ready model"""
114
+ try:
115
+ model = models.resnet50(pretrained=True)
116
+ model.eval()
117
+ if torch.cuda.is_available():
118
+ model = model.cuda()
119
+ logger.info("Production model loaded successfully")
120
+ return model
121
+ except Exception as e:
122
+ logger.error(f"Failed to load model: {e}")
123
+ raise
124
+
125
+ async def analyze_image(self, image_path: str) -> Dict[str, Any]:
126
+ """Production image analysis with proper error handling"""
127
+ try:
128
+ start_time = time.time()
129
+
130
+ # Load and validate image
131
+ image = Image.open(image_path).convert('RGB')
132
+ img_array = np.array(image)
133
+
134
+ # Perform analysis
135
+ complexity = self._calculate_complexity(img_array)
136
+ symmetry = self._analyze_symmetry(img_array)
137
+ color_analysis = await self._analyze_colors(img_array)
138
+ patterns = await self._detect_patterns(img_array)
139
+ archetypes = await self._detect_archetypes(img_array)
140
+
141
+ # Calculate truth score
142
+ truth_score = self._calculate_truth_score(
143
+ complexity, symmetry, color_analysis, patterns, archetypes
144
+ )
145
+
146
+ processing_time = time.time() - start_time
147
+ logger.info(f"Image analysis completed in {processing_time:.2f}s")
148
+
149
+ return {
150
+ "truth_score": truth_score,
151
+ "complexity": complexity,
152
+ "symmetry": symmetry,
153
+ "color_analysis": color_analysis,
154
+ "patterns": patterns,
155
+ "archetypes": archetypes,
156
+ "processing_time": processing_time
157
+ }
158
+
159
+ except Exception as e:
160
+ logger.error(f"Image analysis failed: {e}")
161
+ raise
162
+
163
+ def _calculate_complexity(self, img_array: np.ndarray) -> float:
164
+ """Calculate image complexity"""
165
+ try:
166
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
167
+ edges = cv2.Canny(gray, 50, 150)
168
+ edge_density = np.sum(edges > 0) / edges.size
169
+
170
+ # Entropy calculation
171
+ hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
172
+ hist = hist / hist.sum()
173
+ entropy = -np.sum(hist * np.log2(hist + 1e-8)) / 8.0
174
+
175
+ return min(1.0, (edge_density + entropy) / 2)
176
+ except Exception as e:
177
+ logger.warning(f"Complexity calculation failed: {e}")
178
+ return 0.5
179
+
180
+ def _analyze_symmetry(self, img_array: np.ndarray) -> float:
181
+ """Analyze image symmetry"""
182
+ try:
183
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
184
+ height, width = gray.shape
185
+
186
+ # Vertical symmetry
187
+ left = gray[:, :width//2]
188
+ right = cv2.flip(gray[:, width//2:], 1)
189
+ min_height = min(left.shape[0], right.shape[0])
190
+ min_width = min(left.shape[1], right.shape[1])
191
+
192
+ vertical_sym = 1.0 - np.abs(
193
+ left[:min_height, :min_width] - right[:min_height, :min_width]
194
+ ).mean() / 255.0
195
+
196
+ return vertical_sym
197
+ except Exception as e:
198
+ logger.warning(f"Symmetry analysis failed: {e}")
199
+ return 0.5
200
+
201
+ async def _analyze_colors(self, img_array: np.ndarray) -> Dict[str, float]:
202
+ """Analyze color symbolism"""
203
+ try:
204
+ hsv = cv2.cvtColor(img_array, cv2.COLOR_RGB2HSV)
205
+
206
+ color_ranges = {
207
+ 'spiritual_gold': ([20, 100, 100], [30, 255, 255]),
208
+ 'divine_purple': ([130, 50, 50], [160, 255, 255]),
209
+ 'cosmic_blue': ([100, 50, 50], [130, 255, 255]),
210
+ }
211
+
212
+ color_presence = {}
213
+ for color_name, (lower, upper) in color_ranges.items():
214
+ mask = cv2.inRange(hsv, np.array(lower), np.array(upper))
215
+ presence = np.sum(mask > 0) / mask.size
216
+ color_presence[color_name] = min(1.0, presence * 5)
217
+
218
+ return color_presence
219
+ except Exception as e:
220
+ logger.warning(f"Color analysis failed: {e}")
221
+ return {}
222
+
223
+ async def _detect_patterns(self, img_array: np.ndarray) -> List[str]:
224
+ """Detect visual patterns"""
225
+ try:
226
+ patterns = []
227
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
228
+
229
+ # Detect circles
230
+ circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20,
231
+ param1=50, param2=30, minRadius=5, maxRadius=100)
232
+ if circles is not None and len(circles[0]) > 2:
233
+ patterns.append("sacred_geometry")
234
+
235
+ # Detect symmetry
236
+ symmetry_score = self._analyze_symmetry(img_array)
237
+ if symmetry_score > 0.7:
238
+ patterns.append("harmonic_balance")
239
+
240
+ return patterns
241
+ except Exception as e:
242
+ logger.warning(f"Pattern detection failed: {e}")
243
+ return []
244
+
245
+ async def _detect_archetypes(self, img_array: np.ndarray) -> List[str]:
246
+ """Detect truth archetypes"""
247
+ try:
248
+ archetypes = []
249
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
250
+
251
+ # Simple feature-based archetype detection
252
+ complexity = self._calculate_complexity(img_array)
253
+ if complexity > 0.7:
254
+ archetypes.append("complex_symbolism")
255
+
256
+ # Color-based archetypes
257
+ color_analysis = await self._analyze_colors(img_array)
258
+ if color_analysis.get('cosmic_blue', 0) > 0.3:
259
+ archetypes.append("cosmic_revelation")
260
+
261
+ return archetypes
262
+ except Exception as e:
263
+ logger.warning(f"Archetype detection failed: {e}")
264
+ return []
265
+
266
+ def _calculate_truth_score(self, complexity: float, symmetry: float,
267
+ color_analysis: Dict[str, float], patterns: List[str],
268
+ archetypes: List[str]) -> float:
269
+ """Calculate overall truth revelation score"""
270
+ weights = {
271
+ 'complexity': 0.25,
272
+ 'symmetry': 0.20,
273
+ 'color': 0.25,
274
+ 'patterns': 0.15,
275
+ 'archetypes': 0.15
276
+ }
277
+
278
+ color_score = np.mean(list(color_analysis.values())) if color_analysis else 0.0
279
+ pattern_score = len(patterns) * 0.1
280
+ archetype_score = len(archetypes) * 0.1
281
+
282
+ score = (complexity * weights['complexity'] +
283
+ symmetry * weights['symmetry'] +
284
+ color_score * weights['color'] +
285
+ pattern_score * weights['patterns'] +
286
+ archetype_score * weights['archetypes'])
287
+
288
+ return min(1.0, score)
289
+
290
+ class TextAnalyzer:
291
+ async def analyze_text(self, text: str, domain: Optional[str] = None) -> Dict[str, Any]:
292
+ """Production text analysis"""
293
+ try:
294
+ start_time = time.time()
295
+
296
+ # Basic text analysis
297
+ word_count = len(text.split())
298
+ symbolic_density = self._calculate_symbolic_density(text)
299
+ emotional_impact = self._assess_emotional_impact(text)
300
+ archetypes = self._detect_text_archetypes(text)
301
+
302
+ truth_score = self._calculate_text_truth_score(
303
+ symbolic_density, emotional_impact, archetypes
304
+ )
305
+
306
+ processing_time = time.time() - start_time
307
+
308
+ return {
309
+ "truth_score": truth_score,
310
+ "word_count": word_count,
311
+ "symbolic_density": symbolic_density,
312
+ "emotional_impact": emotional_impact,
313
+ "archetypes": archetypes,
314
+ "processing_time": processing_time
315
+ }
316
+
317
+ except Exception as e:
318
+ logger.error(f"Text analysis failed: {e}")
319
+ raise
320
+
321
+ def _calculate_symbolic_density(self, text: str) -> float:
322
+ """Calculate symbolic density in text"""
323
+ symbolic_terms = {
324
+ 'light', 'dark', 'water', 'fire', 'earth', 'air', 'journey',
325
+ 'transformation', 'truth', 'reality', 'consciousness', 'cosmic'
326
+ }
327
+ words = text.lower().split()
328
+ if not words:
329
+ return 0.0
330
+
331
+ matches = sum(1 for word in words if word in symbolic_terms)
332
+ return min(1.0, matches / len(words) * 5)
333
+
334
+ def _assess_emotional_impact(self, text: str) -> float:
335
+ """Assess emotional impact of text"""
336
+ emotional_words = {
337
+ 'love', 'fear', 'hope', 'despair', 'joy', 'sorrow', 'passion',
338
+ 'rage', 'ecstasy', 'terror', 'bliss', 'anguish'
339
+ }
340
+ words = text.lower().split()
341
+ if not words:
342
+ return 0.0
343
+
344
+ matches = sum(1 for word in words if word in emotional_words)
345
+ return min(1.0, matches / len(words) * 3)
346
+
347
+ def _detect_text_archetypes(self, text: str) -> List[str]:
348
+ """Detect truth archetypes in text"""
349
+ archetype_patterns = {
350
+ 'cosmic_revelation': ['cosmic', 'universe', 'galaxy', 'star', 'nebula'],
351
+ 'historical_cipher': ['ancient', 'civilization', 'lost', 'artifact'],
352
+ 'consciousness_code': ['mind', 'awareness', 'consciousness', 'dream'],
353
+ 'esoteric_symbol': ['symbol', 'sacred', 'mystery', 'hidden']
354
+ }
355
+
356
+ text_lower = text.lower()
357
+ detected = []
358
+ for archetype, patterns in archetype_patterns.items():
359
+ if any(pattern in text_lower for pattern in patterns):
360
+ detected.append(archetype)
361
+
362
+ return detected
363
+
364
+ def _calculate_text_truth_score(self, symbolic_density: float,
365
+ emotional_impact: float, archetypes: List[str]) -> float:
366
+ """Calculate text truth score"""
367
+ base_score = (symbolic_density * 0.4 + emotional_impact * 0.3)
368
+ archetype_boost = len(archetypes) * 0.1
369
+ return min(1.0, base_score + archetype_boost)
370
+
371
+ # Cache and Storage
372
+ class CacheManager:
373
+ def __init__(self):
374
+ self.redis = None
375
+
376
+ async def connect(self):
377
+ """Connect to Redis"""
378
+ try:
379
+ self.redis = await aioredis.from_url(Config.REDIS_URL, decode_responses=True)
380
+ await self.redis.ping()
381
+ logger.info("Redis connected successfully")
382
+ except Exception as e:
383
+ logger.error(f"Redis connection failed: {e}")
384
+ self.redis = None
385
+
386
+ async def get(self, key: str) -> Optional[str]:
387
+ """Get value from cache"""
388
+ if not self.redis:
389
+ return None
390
+ try:
391
+ return await self.redis.get(key)
392
+ except Exception as e:
393
+ logger.warning(f"Cache get failed: {e}")
394
+ return None
395
+
396
+ async def set(self, key: str, value: str, expire: int = 3600):
397
+ """Set value in cache"""
398
+ if not self.redis:
399
+ return
400
+ try:
401
+ await self.redis.set(key, value, ex=expire)
402
+ except Exception as e:
403
+ logger.warning(f"Cache set failed: {e}")
404
+
405
+ async def close(self):
406
+ """Close Redis connection"""
407
+ if self.redis:
408
+ await self.redis.close()
409
+
410
+ # Main Application
411
+ class TruthRevelationAPI:
412
+ def __init__(self):
413
+ self.app = FastAPI(
414
+ title="Truth Revelation API",
415
+ description="Production-ready API for artistic and visual truth analysis",
416
+ version="1.0.0"
417
+ )
418
+ self.cache = CacheManager()
419
+ self.image_analyzer = ProductionImageAnalyzer()
420
+ self.text_analyzer = TextAnalyzer()
421
+ self.setup_middleware()
422
+ self.setup_routes()
423
+
424
+ def setup_middleware(self):
425
+ """Setup application middleware"""
426
+ self.app.add_middleware(
427
+ CORSMiddleware,
428
+ allow_origins=["*"],
429
+ allow_credentials=True,
430
+ allow_methods=["*"],
431
+ allow_headers=["*"],
432
+ )
433
+
434
+ def setup_routes(self):
435
+ """Setup API routes"""
436
+
437
+ @self.app.on_event("startup")
438
+ async def startup():
439
+ await self.cache.connect()
440
+ logger.info("Truth Revelation API started")
441
+
442
+ @self.app.on_event("shutdown")
443
+ async def shutdown():
444
+ await self.cache.close()
445
+ logger.info("Truth Revelation API stopped")
446
+
447
+ @self.app.get("/health", response_model=HealthResponse)
448
+ async def health_check():
449
+ """Health check endpoint"""
450
+ redis_connected = self.cache.redis is not None
451
+ memory_usage = psutil.Process().memory_percent()
452
+
453
+ return HealthResponse(
454
+ status="healthy",
455
+ version="1.0.0",
456
+ redis_connected=redis_connected,
457
+ memory_usage=memory_usage,
458
+ active_requests=ACTIVE_REQUESTS._value.get()
459
+ )
460
+
461
+ @self.app.post("/analyze/text", response_model=AnalysisResponse)
462
+ @REQUEST_DURATION.time()
463
+ async def analyze_text(request: AnalysisRequest):
464
+ """Analyze text content for truth revelation"""
465
+ ACTIVE_REQUESTS.inc()
466
+ REQUEST_COUNT.labels(method="POST", endpoint="/analyze/text").inc()
467
+
468
+ try:
469
+ start_time = time.time()
470
+ request_id = f"text_{int(time.time())}_{hash(request.text_content or '')}"
471
+
472
+ # Check cache
473
+ cache_key = f"text_analysis:{hash(request.text_content or '')}"
474
+ cached_result = await self.cache.get(cache_key)
475
+
476
+ if cached_result:
477
+ result = json.loads(cached_result)
478
+ result['cached'] = True
479
+ logger.info(f"Serving cached text analysis for {request_id}")
480
+ else:
481
+ # Perform analysis
482
+ analysis = await self.text_analyzer.analyze_text(
483
+ request.text_content or "", request.domain
484
+ )
485
+
486
+ # Generate visualization prompt
487
+ prompt = self._generate_prompt(analysis, request.domain)
488
+
489
+ result = {
490
+ "request_id": request_id,
491
+ "status": "completed",
492
+ "truth_score": analysis["truth_score"],
493
+ "confidence": 0.8, # Based on analysis quality
494
+ "archetypes": analysis["archetypes"],
495
+ "patterns": [],
496
+ "visualization_prompt": prompt,
497
+ "processing_time": analysis["processing_time"],
498
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
499
+ "cached": False
500
+ }
501
+
502
+ # Cache result
503
+ await self.cache.set(cache_key, json.dumps(result))
504
+
505
+ TRUTH_SCORE_DISTRIBUTION.observe(result["truth_score"])
506
+ ACTIVE_REQUESTS.dec()
507
+
508
+ return AnalysisResponse(**{k: v for k, v in result.items() if k != 'cached'})
509
+
510
+ except Exception as e:
511
+ ACTIVE_REQUESTS.dec()
512
+ logger.error(f"Text analysis failed: {e}")
513
+ raise HTTPException(status_code=500, detail="Text analysis failed")
514
+
515
+ @self.app.post("/analyze/image", response_model=AnalysisResponse)
516
+ @REQUEST_DURATION.time()
517
+ async def analyze_image(
518
+ file: UploadFile = File(...),
519
+ description: Optional[str] = Form(None),
520
+ context: str = Form("{}")
521
+ ):
522
+ """Analyze image content for truth revelation"""
523
+ ACTIVE_REQUESTS.inc()
524
+ REQUEST_COUNT.labels(method="POST", endpoint="/analyze/image").inc()
525
+
526
+ try:
527
+ start_time = time.time()
528
+
529
+ # Validate file
530
+ if not file.content_type.startswith('image/'):
531
+ raise HTTPException(status_code=400, detail="Invalid image file")
532
+
533
+ # Save uploaded file
534
+ file_path = f"/tmp/{file.filename}"
535
+ async with aiofiles.open(file_path, 'wb') as f:
536
+ content = await file.read()
537
+ if len(content) > Config.MAX_IMAGE_SIZE:
538
+ raise HTTPException(status_code=400, detail="File too large")
539
+ await f.write(content)
540
+
541
+ request_id = f"image_{int(time.time())}_{hash(file.filename)}"
542
+
543
+ # Check cache
544
+ cache_key = f"image_analysis:{hash(content)}"
545
+ cached_result = await self.cache.get(cache_key)
546
+
547
+ if cached_result:
548
+ result = json.loads(cached_result)
549
+ result['cached'] = True
550
+ logger.info(f"Serving cached image analysis for {request_id}")
551
+ else:
552
+ # Perform analysis
553
+ analysis = await self.image_analyzer.analyze_image(file_path)
554
+
555
+ # Generate visualization prompt
556
+ prompt = self._generate_image_prompt(analysis, description)
557
+
558
+ result = {
559
+ "request_id": request_id,
560
+ "status": "completed",
561
+ "truth_score": analysis["truth_score"],
562
+ "confidence": 0.7, # Image analysis confidence
563
+ "archetypes": analysis["archetypes"],
564
+ "patterns": analysis["patterns"],
565
+ "visualization_prompt": prompt,
566
+ "processing_time": analysis["processing_time"],
567
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
568
+ "cached": False
569
+ }
570
+
571
+ # Cache result
572
+ await self.cache.set(cache_key, json.dumps(result))
573
+
574
+ # Cleanup
575
+ os.remove(file_path)
576
+
577
+ TRUTH_SCORE_DISTRIBUTION.observe(result["truth_score"])
578
+ ACTIVE_REQUESTS.dec()
579
+
580
+ return AnalysisResponse(**{k: v for k, v in result.items() if k != 'cached'})
581
+
582
+ except HTTPException:
583
+ ACTIVE_REQUESTS.dec()
584
+ raise
585
+ except Exception as e:
586
+ ACTIVE_REQUESTS.dec()
587
+ logger.error(f"Image analysis failed: {e}")
588
+ raise HTTPException(status_code=500, detail="Image analysis failed")
589
+
590
+ @self.app.get("/metrics")
591
+ async def metrics():
592
+ """Prometheus metrics endpoint"""
593
+ return prometheus_client.generate_latest()
594
+
595
+ def _generate_prompt(self, analysis: Dict[str, Any], domain: Optional[str]) -> str:
596
+ """Generate visualization prompt from analysis"""
597
+ components = ["middle-ages-islamic-art style"]
598
+
599
+ if domain:
600
+ components.append(f"{domain} theme")
601
+
602
+ if analysis["archetypes"]:
603
+ components.extend(analysis["archetypes"][:2])
604
+
605
+ components.extend(["intricate details", "symbolic meaning", "high resolution"])
606
+
607
+ return ", ".join(components)
608
+
609
+ def _generate_image_prompt(self, analysis: Dict[str, Any], description: Optional[str]) -> str:
610
+ """Generate image visualization prompt"""
611
+ components = ["middle-ages-islamic-art style"]
612
+
613
+ if description:
614
+ components.append(description)
615
+
616
+ if analysis["archetypes"]:
617
+ components.extend(analysis["archetypes"][:2])
618
+
619
+ if analysis["patterns"]:
620
+ components.extend(analysis["patterns"][:2])
621
+
622
+ components.extend(["detailed", "symbolic", "illuminated manuscript style"])
623
+
624
+ return ", ".join(components)
625
+
626
+ # Application instance
627
+ app = TruthRevelationAPI().app
628
+
629
+ if __name__ == "__main__":
630
+ import uvicorn
631
+ uvicorn.run(
632
+ "main:app",
633
+ host="0.0.0.0",
634
+ port=8000,
635
+ reload=False, # Disable reload in production
636
+ access_log=True,
637
+ timeout_keep_alive=30
638
+ )