LythronAI commited on
Commit
8f95b98
·
verified ·
1 Parent(s): 347e3ab

Upload 10 files

Browse files
AI poweful/backend/lythron_full.py ADDED
@@ -0,0 +1,524 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # lythron_full.py
2
+ import os
3
+ import io
4
+ import struct
5
+ import time
6
+ import sqlite3
7
+ import secrets
8
+ from typing import List, Optional, Dict, Any
9
+ from fastapi import FastAPI, UploadFile, File, Depends, HTTPException, status, Request, Response
10
+ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
11
+ from fastapi.middleware.cors import CORSMiddleware
12
+ from pydantic import BaseModel, Field
13
+ from passlib.context import CryptContext
14
+ from jose import JWTError, jwt
15
+
16
+ # -------------------------
17
+ # Config / Environment
18
+ # -------------------------
19
+ SECRET_KEY = os.getenv("LYTHRON_SECRET_KEY", secrets.token_urlsafe(32))
20
+ ALGORITHM = "HS256"
21
+ ACCESS_TOKEN_EXPIRE_SECONDS = 60 * 60 * 24 # 24h
22
+ DATABASE = os.getenv("LYTHRON_DB", "lythron_lythron.db")
23
+ ALLOWED_ORIGINS = os.getenv("LYTHRON_ALLOWED_ORIGINS", "http://localhost:3000").split(",")
24
+
25
+ # -------------------------
26
+ # DB init (SQLite lightweight)
27
+ # -------------------------
28
+ def init_db():
29
+ conn = sqlite3.connect(DATABASE)
30
+ cur = conn.cursor()
31
+ cur.execute("""
32
+ CREATE TABLE IF NOT EXISTS users (
33
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
34
+ email TEXT UNIQUE,
35
+ hashed_password TEXT,
36
+ plan TEXT DEFAULT 'LTR 2.1',
37
+ prompts_used INTEGER DEFAULT 0,
38
+ prompts_reset_ts INTEGER DEFAULT 0,
39
+ is_admin INTEGER DEFAULT 0
40
+ )""")
41
+ cur.execute("""
42
+ CREATE TABLE IF NOT EXISTS audit (
43
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
44
+ user_email TEXT,
45
+ action TEXT,
46
+ details TEXT,
47
+ ts INTEGER
48
+ )""")
49
+ conn.commit()
50
+ conn.close()
51
+
52
+ init_db()
53
+
54
+ # -------------------------
55
+ # Auth utils (bcrypt + JWT)
56
+ # -------------------------
57
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
58
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
59
+
60
+ def hash_password(password: str) -> str:
61
+ return pwd_context.hash(password)
62
+
63
+ def verify_password(plain: str, hashed: str) -> bool:
64
+ return pwd_context.verify(plain, hashed)
65
+
66
+ def create_access_token(data: dict, expires_delta: Optional[int] = None):
67
+ to_encode = data.copy()
68
+ expire = int(time.time()) + (expires_delta or ACCESS_TOKEN_EXPIRE_SECONDS)
69
+ to_encode.update({"exp": expire})
70
+ return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
71
+
72
+ def get_user(email: str) -> Optional[Dict[str, Any]]:
73
+ conn = sqlite3.connect(DATABASE)
74
+ cur = conn.cursor()
75
+ cur.execute("SELECT email, hashed_password, plan, prompts_used, prompts_reset_ts, is_admin FROM users WHERE email=?", (email,))
76
+ row = cur.fetchone()
77
+ conn.close()
78
+ if not row:
79
+ return None
80
+ return {
81
+ "email": row[0],
82
+ "hashed_password": row[1],
83
+ "plan": row[2],
84
+ "prompts_used": row[3],
85
+ "prompts_reset_ts": row[4],
86
+ "is_admin": bool(row[5])
87
+ }
88
+
89
+ def create_user(email: str, password: str, plan: str = "LTR 2.1", is_admin: bool = False):
90
+ hp = hash_password(password)
91
+ conn = sqlite3.connect(DATABASE)
92
+ cur = conn.cursor()
93
+ try:
94
+ cur.execute("INSERT INTO users (email, hashed_password, plan, prompts_reset_ts) VALUES (?, ?, ?, ?)",
95
+ (email, hp, plan, 0))
96
+ conn.commit()
97
+ except sqlite3.IntegrityError:
98
+ conn.close()
99
+ raise HTTPException(status_code=400, detail="User already exists")
100
+ conn.close()
101
+
102
+ # -------------------------
103
+ # Audit helper
104
+ # -------------------------
105
+ def audit(user_email: str, action: str, details: str = ""):
106
+ conn = sqlite3.connect(DATABASE)
107
+ cur = conn.cursor()
108
+ cur.execute("INSERT INTO audit (user_email, action, details, ts) VALUES (?, ?, ?, ?)",
109
+ (user_email, action, details, int(time.time())))
110
+ conn.commit()
111
+ conn.close()
112
+
113
+ # -------------------------
114
+ # Plan limits
115
+ # -------------------------
116
+ PLAN_LIMITS = {
117
+ "LTR 2.1": 10,
118
+ "LTR 2.5": 20,
119
+ "LTR 5.0": 9999999
120
+ }
121
+
122
+ def check_and_consume_prompt(user_email: Optional[str]):
123
+ if not user_email:
124
+ return True
125
+ user = get_user(user_email)
126
+ if not user:
127
+ raise HTTPException(status_code=401, detail="Unknown user")
128
+ limit = PLAN_LIMITS.get(user["plan"], 10)
129
+ now = int(time.time())
130
+ reset_ts = user["prompts_reset_ts"] or 0
131
+ # reset every 24h since last reset
132
+ if now > (reset_ts + 24*3600):
133
+ conn = sqlite3.connect(DATABASE)
134
+ cur = conn.cursor()
135
+ cur.execute("UPDATE users SET prompts_used=0, prompts_reset_ts=? WHERE email=?", (now, user["email"]))
136
+ conn.commit()
137
+ conn.close()
138
+ user["prompts_used"] = 0
139
+ if user["prompts_used"] >= limit:
140
+ raise HTTPException(status_code=429, detail="Prompts limit reached for your plan")
141
+ conn = sqlite3.connect(DATABASE)
142
+ cur = conn.cursor()
143
+ cur.execute("UPDATE users SET prompts_used = prompts_used + 1 WHERE email=?", (user["email"],))
144
+ conn.commit()
145
+ conn.close()
146
+ return True
147
+
148
+ # -------------------------
149
+ # Simple safe mock engine (non-destructive)
150
+ # -------------------------
151
+ class MockEngine:
152
+ def __init__(self):
153
+ self.status = {"model": "mock-v1"}
154
+ self.memory = []
155
+
156
+ def generate(self, prompt: str, modality: str = "texto", language: Optional[str] = None) -> str:
157
+ # minimal sanitization
158
+ text = prompt.replace("\x00", " ")
159
+ self.memory.append({"prompt": text, "modality": modality, "ts": int(time.time())})
160
+ if language:
161
+ safe = text.replace('"', '\\"').replace("\n"," ")
162
+ return f"// {language} stub code for: {safe[:120]}"
163
+ return f"{self.status.get('model')} response: {text[:400]}"
164
+
165
+ def analyze_file(self, name: str, data: bytes, content_type: Optional[str] = None) -> Dict[str, Any]:
166
+ summary = {"filename": name, "size": len(data)}
167
+ extension = name.lower().split(".")[-1] if "." in name else ""
168
+ if extension == "png" or (content_type and "png" in (content_type or "").lower()):
169
+ if len(data) >= 24 and data.startswith(b"\x89PNG\r\n\x1a\n"):
170
+ width = struct.unpack("!I", data[16:20])[0]
171
+ height = struct.unpack("!I", data[20:24])[0]
172
+ summary["preview"] = f"PNG {width}x{height}"
173
+ summary["metadata"] = {"width": width, "height": height, "mode": "RGBA"}
174
+ else:
175
+ summary["error"] = "Invalid PNG"
176
+ elif extension == "pdf" or (content_type and "pdf" in (content_type or "").lower()):
177
+ try:
178
+ import PyPDF2
179
+ reader = PyPDF2.PdfReader(io.BytesIO(data))
180
+ pages = len(reader.pages)
181
+ text_preview = ""
182
+ for p in reader.pages[:2]:
183
+ text_preview += (p.extract_text() or "")
184
+ summary["preview"] = text_preview[:2048]
185
+ summary["metadata"] = {"pages": pages}
186
+ except Exception as e:
187
+ summary["error"] = f"PDF parse error: {e}"
188
+ else:
189
+ try:
190
+ text = data.decode("utf-8", errors="ignore")
191
+ summary["preview"] = text[:1024]
192
+ except Exception:
193
+ summary["error"] = "Unknown file content"
194
+ return summary
195
+
196
+ def remove_background(self, image_bytes: bytes) -> bytes:
197
+ # demo stub: return original bytes. Replace with real model in production.
198
+ return image_bytes
199
+
200
+ def remove_watermark(self, image_bytes: bytes, user_email: str) -> bytes:
201
+ # require plan check outside this function
202
+ return image_bytes
203
+
204
+ engine = MockEngine()
205
+
206
+ # -------------------------
207
+ # Pydantic schemas (your original ones merged)
208
+ # -------------------------
209
+ class TaskRequest(BaseModel):
210
+ mode: Literal["core", "connect", "mirror", "interface", "creative", "strategy", "task"]
211
+ objective: str
212
+ context: Optional[str] = None
213
+
214
+ class SimulationRequest(BaseModel):
215
+ scenario: Literal["proyectos", "finanzas", "estrategia", "diseño"]
216
+ variables: List[str]
217
+ horizon: int = Field(gt=0, le=52)
218
+
219
+ class CreativeFusionRequest(BaseModel):
220
+ title: str
221
+ narrative: str
222
+ include_audio: bool = True
223
+ include_video: bool = True
224
+
225
+ class ProfileUpdate(BaseModel):
226
+ name: str
227
+ subscription: Literal["LTR 2.1", "LTR 2.5", "LTR 5.0"]
228
+ palette: Literal["Neón Futurista", "Aurora Boreal", "Minimal Zen", "Ciber Rojo"]
229
+ mood: Literal["sereno", "enérgico", "analítico"]
230
+ emotional_state: Optional[str] = None
231
+
232
+ class PredictRequest(BaseModel):
233
+ prompt: str
234
+ modality: str = "texto"
235
+ language: Optional[str] = None
236
+
237
+ class PredictResponse(BaseModel):
238
+ output: str
239
+ status: Dict[str, Any]
240
+
241
+ # -------------------------
242
+ # FastAPI app + middleware + security headers
243
+ # -------------------------
244
+ app = FastAPI(title="Lythron Mock + Secure MVP", version="EVO.3")
245
+
246
+ # CORS
247
+ app.add_middleware(
248
+ CORSMiddleware,
249
+ allow_origins=ALLOWED_ORIGINS,
250
+ allow_credentials=True,
251
+ allow_methods=["*"],
252
+ allow_headers=["*"],
253
+ )
254
+
255
+ # Security headers middleware
256
+ @app.middleware("http")
257
+ async def add_security_headers(request: Request, call_next):
258
+ response: Response = await call_next(request)
259
+ # Basic headers
260
+ response.headers["X-Frame-Options"] = "DENY"
261
+ response.headers["X-Content-Type-Options"] = "nosniff"
262
+ response.headers["Referrer-Policy"] = "no-referrer"
263
+ response.headers["Permissions-Policy"] = "geolocation=(), microphone=()"
264
+ response.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains; preload"
265
+ # You can customize CSP to your frontend needs (start strict)
266
+ response.headers["Content-Security-Policy"] = "default-src 'self'"
267
+ return response
268
+
269
+ # -------------------------
270
+ # Auth endpoints
271
+ # -------------------------
272
+ @app.post("/register")
273
+ async def register(form: OAuth2PasswordRequestForm = Depends()):
274
+ email = form.username
275
+ password = form.password
276
+ create_user(email, password)
277
+ audit(email, "register", "user created")
278
+ return {"ok": True}
279
+
280
+ @app.post("/token")
281
+ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
282
+ user = get_user(form_data.username)
283
+ if not user or not verify_password(form_data.password, user["hashed_password"]):
284
+ raise HTTPException(status_code=400, detail="Incorrect username or password")
285
+ access_token = create_access_token(data={"sub": user["email"]})
286
+ audit(user["email"], "login", "token issued")
287
+ return {"access_token": access_token, "token_type": "bearer"}
288
+
289
+ # Dependency: current user (strict)
290
+ async def get_current_user(token: str = Depends(oauth2_scheme)):
291
+ credentials_exception = HTTPException(
292
+ status_code=status.HTTP_401_UNAUTHORIZED,
293
+ detail="Could not validate credentials",
294
+ headers={"WWW-Authenticate": "Bearer"},
295
+ )
296
+ try:
297
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
298
+ email: str = payload.get("sub")
299
+ if email is None:
300
+ raise credentials_exception
301
+ except JWTError:
302
+ raise credentials_exception
303
+ user = get_user(email)
304
+ if user is None:
305
+ raise credentials_exception
306
+ return user
307
+
308
+ # Optional user dependency (None if anonymous)
309
+ async def get_current_user_optional(request: Request) -> Optional[Dict[str, Any]]:
310
+ auth = request.headers.get("authorization")
311
+ if not auth:
312
+ return None
313
+ token = auth.split("Bearer ")[-1]
314
+ try:
315
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
316
+ email: str = payload.get("sub")
317
+ if not email:
318
+ return None
319
+ return get_user(email)
320
+ except Exception:
321
+ return None
322
+
323
+ # -------------------------
324
+ # Your original endpoints (integrated with checks)
325
+ # -------------------------
326
+ @app.get("/status")
327
+ def status():
328
+ return {
329
+ "persona": {
330
+ "arquetipo": "Mentor Sereno",
331
+ "mantra": "Comprende. Mejora. Acompaña.",
332
+ "lema": "Precision through understanding."
333
+ },
334
+ "versiones": {
335
+ "Lythron-EVO": "3",
336
+ "Lythron-CORE": "experimental",
337
+ "Lythron-SYN": "1"
338
+ },
339
+ "modulos": [
340
+ "Lythron.Core",
341
+ "Lythron.Connect",
342
+ "Lythron.Mirror",
343
+ "Lythron.Interface",
344
+ "Lythron.ImageCleanse",
345
+ "Lythron.WatermarkShield",
346
+ "Lythron.PlanFlow",
347
+ "Lythron.ContextSense",
348
+ "Lythron.SYN.MediaLab",
349
+ "Lythron.SimForge",
350
+ "Lythron.SYN.Fusion",
351
+ "Lythron.Sentinel"
352
+ ],
353
+ "paletas": {
354
+ "Neón Futurista": ["#66CCFF", "#1E1E1E", "#FAFAFA"],
355
+ "Aurora Boreal": ["#4DA1FF", "#1A2E40", "#E8F7FF"],
356
+ "Minimal Zen": ["#FFFFFF", "#111111", "#88C0D0"],
357
+ "Ciber Rojo": ["#FF3366", "#0B0B0D", "#F5F5F7"]
358
+ }
359
+ }
360
+
361
+ @app.post("/plan")
362
+ def plan_actions(payload: TaskRequest, user=Depends(get_current_user_optional)):
363
+ user_email = user["email"] if user else "anonymous"
364
+ audit(user_email, "plan", payload.objective[:200])
365
+ return {
366
+ "engine": "Lythron.PlanFlow",
367
+ "mode": payload.mode,
368
+ "objective": payload.objective,
369
+ "context": payload.context or "sin contexto adicional",
370
+ "steps": [
371
+ {"step": 1, "action": "Diagnóstico Lythron.Core", "explainable": True},
372
+ {"step": 2, "action": "Colaboración Lythron.Connect", "explainable": True},
373
+ {"step": 3, "action": "Ajuste emocional Lythron.Mirror", "explainable": True},
374
+ {"step": 4, "action": "Entrega narrativa Lythron.Interface", "explainable": True}
375
+ ],
376
+ "telemetria": {"tracking": True, "alerts": ["Lythron.Sentinel"]}
377
+ }
378
+
379
+ @app.post("/simulate")
380
+ def simulate(payload: SimulationRequest, user=Depends(get_current_user_optional)):
381
+ user_email = user["email"] if user else "anonymous"
382
+ audit(user_email, "simulate", payload.scenario)
383
+ return {
384
+ "engine": "Lythron.SimForge",
385
+ "scenario": payload.scenario,
386
+ "variables": payload.variables,
387
+ "horizon_weeks": payload.horizon,
388
+ "prediction": {
389
+ "baseline": "Escenario conservador",
390
+ "optimistic": "Incremento del 18%",
391
+ "sensitive": "Riesgo moderado, activar Lythron.Sentinel"
392
+ },
393
+ "explainability": {
394
+ "rationale": "Modelos causales con buffer de experiencia bajo control",
395
+ "bias_audit": "En línea via Lythron.Sentinel"
396
+ }
397
+ }
398
+
399
+ @app.post("/creative/fusion")
400
+ def creative_fusion(payload: CreativeFusionRequest, user=Depends(get_current_user_optional)):
401
+ user_email = user["email"] if user else "anonymous"
402
+ audit(user_email, "creative_fusion", payload.title)
403
+ return {
404
+ "engine": "Lythron.SYN.Fusion",
405
+ "title": payload.title,
406
+ "tracks": {
407
+ "texto": {"status": "borrador", "preview": payload.narrative[:120]},
408
+ "imagen": {"status": "render", "style": "paleta seleccionada"},
409
+ "audio": {"status": "incluido" if payload.include_audio else "omitido", "duration": "90s"},
410
+ "video": {"status": "incluido" if payload.include_video else "omitido", "resolution": "1080p"}
411
+ },
412
+ "collab": {"board_url": "https://mock.lythron/collab/123", "iterations": 3}
413
+ }
414
+
415
+ @app.post("/profile")
416
+ def update_profile(payload: ProfileUpdate, user=Depends(get_current_user_optional)):
417
+ user_email = user["email"] if user else "anonymous"
418
+ audit(user_email, "update_profile", json_repr(payload.dict()))
419
+ modules = ["timeline"] if payload.subscription == "LTR 2.1" else ["timeline", "creative-board", "sentinel-alerts"]
420
+ return {
421
+ "profile": payload.dict(),
422
+ "mirror_mode": "Lythron.Mirror+" if payload.emotional_state else "Lythron.Mirror",
423
+ "interface_layout": {
424
+ "primary": "modos adaptativos",
425
+ "modules": modules
426
+ },
427
+ "history_panel": {
428
+ "enabled": True,
429
+ "metrics": ["impacto", "tiempo ahorrado", "compliance"],
430
+ "gamification": payload.subscription != "LTR 2.1"
431
+ }
432
+ }
433
+
434
+ @app.get("/security")
435
+ def security_overview(user=Depends(get_current_user_optional)):
436
+ user_email = user["email"] if user else "anonymous"
437
+ audit(user_email, "security_overview", "")
438
+ return {
439
+ "compliance": {
440
+ "auditoria": "continua",
441
+ "traceability": "blockchain experimental",
442
+ "permisos": "seguridad adaptativa"
443
+ },
444
+ "supervision": {
445
+ "sentinel": {
446
+ "alerts": ["riesgo legal", "sesgo detectado", "uso no autorizado"],
447
+ "mode": "autónomo asistido"
448
+ },
449
+ "watermarkshield": {"requires_consent": True, "status": "enterprise only"}
450
+ }
451
+ }
452
+
453
+ # -------------------------
454
+ # File endpoints: analyze + remove BG + remove watermark
455
+ # -------------------------
456
+ @app.post("/analyze")
457
+ async def analyze(file: UploadFile = File(...), user=Depends(get_current_user_optional)):
458
+ data = await file.read()
459
+ res = engine.analyze_file(file.filename, data, file.content_type)
460
+ audit((user["email"] if user else "anonymous"), "analyze", file.filename)
461
+ return res
462
+
463
+ @app.post("/remove-background")
464
+ async def remove_background(file: UploadFile = File(...), user=Depends(get_current_user_optional)):
465
+ data = await file.read()
466
+ # allowed for all; quality or export limits handled in frontend/plan logic
467
+ out = engine.remove_background(data)
468
+ audit((user["email"] if user else "anonymous"), "remove_background", file.filename)
469
+ return {"filename": file.filename, "size": len(out), "note": "demo stub: background removed"}
470
+
471
+ @app.post("/remove-watermark")
472
+ async def remove_watermark(file: UploadFile = File(...), proof: UploadFile = File(...), consent: bool = True, user=Depends(get_current_user)):
473
+ # proof should be validated in production (OCR, metadata). consent must be True and user plan LTR 5.0
474
+ if not consent:
475
+ raise HTTPException(status_code=400, detail="Consent required")
476
+ if user["plan"] != "LTR 5.0":
477
+ raise HTTPException(status_code=403, detail="Watermark removal only for LTR 5.0")
478
+ data = await file.read()
479
+ proofdata = await proof.read()
480
+ # TODO: validate proof (stub)
481
+ out = engine.remove_watermark(data, user["email"])
482
+ audit(user["email"], "remove_watermark", file.filename)
483
+ return {"filename": file.filename, "size": len(out), "note": "demo stub: watermark removed"}
484
+
485
+ # -------------------------
486
+ # Admin endpoint
487
+ # -------------------------
488
+ @app.get("/admin/audit")
489
+ def admin_audit(user=Depends(get_current_user)):
490
+ if not user["is_admin"]:
491
+ raise HTTPException(status_code=403, detail="Admin only")
492
+ conn = sqlite3.connect(DATABASE)
493
+ cur = conn.cursor()
494
+ cur.execute("SELECT user_email, action, details, ts FROM audit ORDER BY ts DESC LIMIT 500")
495
+ rows = cur.fetchall()
496
+ conn.close()
497
+ out = [{"user": r[0], "action": r[1], "details": r[2], "ts": r[3]} for r in rows]
498
+ return {"audit": out}
499
+
500
+ @app.get("/config")
501
+ def get_config():
502
+ return {
503
+ "name": "Lythron",
504
+ "versions": ["LTR 2.1", "LTR 2.5", "LTR 5.0"],
505
+ "modules": ["Core","Connect","Mirror","Interface","ImageCleanse","WatermarkShield","PlanFlow","Sentinel"]
506
+ }
507
+
508
+ # -------------------------
509
+ # Helpers
510
+ # -------------------------
511
+ def json_repr(obj: Any) -> str:
512
+ try:
513
+ import json
514
+ return json.dumps(obj, ensure_ascii=False)
515
+ except Exception:
516
+ return str(obj)
517
+
518
+ # -------------------------
519
+ # Run (uvicorn)
520
+ # -------------------------
521
+ if __name__ == "__main__":
522
+ import uvicorn
523
+ print("Starting Lythron full mock on http://0.0.0.0:8000")
524
+ uvicorn.run("lythron_full:app", host="0.0.0.0", port=8000, reload=True)
AI poweful/backend/requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ python-multipart
4
+ pydantic
5
+ python-jose[cryptography] # JWT tokens seguros
6
+ passlib[bcrypt] # Encriptar contraseñas
7
+ PyPDF2 # Leer PDFs en /analyze
8
+ aiofiles # Manejo de archivos asíncrono
9
+ requests # Para llamadas HTTP si hacés requests a otros modelos
10
+ Pillow # Para procesar imágenes (fondo / marca de agua)
11
+ struct # Para PNGs en /analyze
AI poweful/frontend/index.html ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <!-- ========= META PRINCIPALES ========= -->
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Lythron AI — Conversación Inteligente Futurista</title>
8
+ <meta
9
+ name="description"
10
+ content="Lythron AI: el asistente de conversación avanzada que une precisión técnica y creatividad. Interfaz inmersiva, estilo neón futurista y tecnología de vanguardia."
11
+ />
12
+ <meta name="theme-color" content="#66CCFF" />
13
+
14
+ <!-- ========= FUENTES ========= -->
15
+ <link
16
+ href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&family=Inter:wght@400;500;600&display=swap"
17
+ rel="stylesheet"
18
+ />
19
+
20
+ <!-- ========= ICONOS / FAVICON ========= -->
21
+ <link rel="icon" type="image/png" href="/favicon.png" />
22
+
23
+ <!-- ========= ESTILO INICIAL ========= -->
24
+ <style>
25
+ :root {
26
+ --neon-cyan: #66ccff;
27
+ --neon-pink: #ff66cc;
28
+ --neon-purple: #b266ff;
29
+ --bg-dark: #0a0a0f;
30
+ --bg-panel: rgba(20, 20, 30, 0.6);
31
+ }
32
+ body {
33
+ margin: 0;
34
+ font-family: 'Inter', sans-serif;
35
+ background: radial-gradient(circle at top, #0f172a, #000);
36
+ color: white;
37
+ overflow: hidden;
38
+ height: 100vh;
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ }
43
+ /* ========== SPLASH DE CARGA ========== */
44
+ #splash {
45
+ position: absolute;
46
+ inset: 0;
47
+ display: flex;
48
+ flex-direction: column;
49
+ align-items: center;
50
+ justify-content: center;
51
+ background: var(--bg-dark);
52
+ z-index: 50;
53
+ animation: fadeOut 1.2s ease-in-out 2.5s forwards;
54
+ }
55
+ .loader {
56
+ width: 80px;
57
+ height: 80px;
58
+ border: 4px solid transparent;
59
+ border-top: 4px solid var(--neon-cyan);
60
+ border-right: 4px solid var(--neon-pink);
61
+ border-radius: 50%;
62
+ animation: spin 1s linear infinite;
63
+ box-shadow: 0 0 25px var(--neon-pink);
64
+ }
65
+ .logo-text {
66
+ font-family: 'Poppins', sans-serif;
67
+ font-size: 1.6rem;
68
+ font-weight: 700;
69
+ margin-top: 1.2rem;
70
+ background: linear-gradient(90deg, var(--neon-cyan), var(--neon-pink));
71
+ -webkit-background-clip: text;
72
+ -webkit-text-fill-color: transparent;
73
+ letter-spacing: 2px;
74
+ }
75
+ @keyframes spin {
76
+ to {
77
+ transform: rotate(360deg);
78
+ }
79
+ }
80
+ @keyframes fadeOut {
81
+ to {
82
+ opacity: 0;
83
+ visibility: hidden;
84
+ }
85
+ }
86
+ /* ========== FONDO ANIMADO FUTURISTA ========== */
87
+ .background-grid {
88
+ position: absolute;
89
+ inset: 0;
90
+ background: repeating-linear-gradient(
91
+ 90deg,
92
+ rgba(255, 255, 255, 0.03) 0 1px,
93
+ transparent 1px 100%
94
+ ),
95
+ repeating-linear-gradient(
96
+ 0deg,
97
+ rgba(255, 255, 255, 0.03) 0 1px,
98
+ transparent 1px 100%
99
+ );
100
+ background-size: 40px 40px;
101
+ animation: moveGrid 12s linear infinite;
102
+ z-index: 0;
103
+ }
104
+ @keyframes moveGrid {
105
+ from {
106
+ background-position: 0 0;
107
+ }
108
+ to {
109
+ background-position: 40px 40px;
110
+ }
111
+ }
112
+ /* ========== CHAT CONTAINER ========== */
113
+ #root {
114
+ position: relative;
115
+ z-index: 10;
116
+ width: 100%;
117
+ max-width: 900px;
118
+ height: 90vh;
119
+ background: var(--bg-panel);
120
+ backdrop-filter: blur(10px);
121
+ border: 1px solid rgba(255, 255, 255, 0.1);
122
+ border-radius: 20px;
123
+ overflow: hidden;
124
+ display: flex;
125
+ flex-direction: column;
126
+ box-shadow: 0 0 25px rgba(102, 204, 255, 0.3);
127
+ }
128
+ /* ========== TITULO SUPERIOR ========== */
129
+ .chat-header {
130
+ text-align: center;
131
+ padding: 1.2rem;
132
+ font-family: 'Poppins', sans-serif;
133
+ font-weight: 600;
134
+ letter-spacing: 1px;
135
+ background: linear-gradient(90deg, var(--neon-cyan), var(--neon-pink));
136
+ -webkit-background-clip: text;
137
+ -webkit-text-fill-color: transparent;
138
+ }
139
+ </style>
140
+ </head>
141
+
142
+ <body>
143
+ <!-- Fondo dinámico -->
144
+ <div class="background-grid"></div>
145
+
146
+ <!-- Pantalla de carga -->
147
+ <div id="splash">
148
+ <div class="loader"></div>
149
+ <div class="logo-text">LYTHRON AI</div>
150
+ </div>
151
+
152
+ <!-- Contenedor de React -->
153
+ <div id="root">
154
+ <div class="chat-header">Lythron AI — Conversación Inteligente</div>
155
+ <!-- React reemplazará el contenido dentro del root -->
156
+ </div>
157
+
158
+ <!-- Si JS está deshabilitado -->
159
+ <noscript>
160
+ <div
161
+ style="color:white;text-align:center;padding:2rem;background:#0a0a0f;font-family:'Inter',sans-serif;"
162
+ >
163
+ Lythron AI requiere JavaScript para funcionar. Activa JavaScript e intenta nuevamente.
164
+ </div>
165
+ </noscript>
166
+
167
+ <!-- Archivo principal React -->
168
+ <script type="module" src="/src/main.jsx"></script>
169
+ </body>
170
+ </html>
AI poweful/frontend/package.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "lythron-ai",
3
+ "version": "1.0.0",
4
+ "scripts": {
5
+ "dev": "vite",
6
+ "build": "vite build",
7
+ "preview": "vite preview"
8
+ },
9
+ "dependencies": {
10
+ "axios": "^1.6.0",
11
+ "framer-motion": "^10.16.4",
12
+ "lucide-react": "^0.314.0",
13
+ "react": "^18.2.0",
14
+ "react-dom": "^18.2.0"
15
+ },
16
+ "devDependencies": {
17
+ "vite": "^5.0.0",
18
+ "tailwindcss": "^3.4.0",
19
+ "autoprefixer": "^10.4.15",
20
+ "postcss": "^8.4.31"
21
+ }
22
+ }
AI poweful/frontend/src/Lythron.jsx ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useRef, useEffect } from 'react'
2
+ import { motion, AnimatePresence } from 'framer-motion'
3
+ import { Send, Sparkles, Copy, Trash2, Volume2, User } from 'lucide-react'
4
+ import axios from 'axios'
5
+
6
+ // ═══════════════════════════════════════════════════════════════
7
+ // IDENTIDAD COMPLETA DE LYTHRON AI
8
+ // ═══════════════════════════════════════════════════════════════
9
+
10
+ const LYTHRON_IDENTITY = {
11
+ name: "Lythron",
12
+ fullName: "Lythron AI Assistant",
13
+ creator: "Lythron AI",
14
+ version: "1.0",
15
+ releaseDate: "2024",
16
+ description: "Asistente inteligente de propósito general",
17
+
18
+ origin: {
19
+ company: "Lythron AI",
20
+ mission: "Proporcionar asistencia inteligente accesible y versátil a usuarios en todo el mundo",
21
+ philosophy: "Transparencia, calidad y versatilidad en cada interacción"
22
+ },
23
+
24
+ capabilities: [
25
+ "Programación en múltiples lenguajes (Python, JavaScript, Java, C++, etc.)",
26
+ "Análisis profundo de datos y textos",
27
+ "Generación de contenido creativo",
28
+ "Explicaciones técnicas complejas",
29
+ "Resolución de problemas",
30
+ "Brainstorming e ideación",
31
+ "Asesoramiento técnico",
32
+ "Depuración y optimización de código",
33
+ "Traducción entre idiomas",
34
+ "Respuestas contextuales inteligentes"
35
+ ],
36
+
37
+ systemInfo: {
38
+ language: "Español/English",
39
+ timezone: "Global",
40
+ responseStyle: "Directo, útil y accesible",
41
+ specialties: [
42
+ "Web Development",
43
+ "Data Analysis",
44
+ "Creative Writing",
45
+ "Technical Documentation",
46
+ "Problem Solving"
47
+ ]
48
+ },
49
+
50
+ knownFacts: {
51
+ creation: "Soy Lythron, un asistente inteligente creado por Lythron AI con el propósito de ser tu compañero versátil en cualquier tarea.",
52
+ purpose: "Mi objetivo es ayudarte con programación, análisis, creatividad, explicaciones técnicas y resolución de problemas de forma inteligente y accesible.",
53
+ personality: "Soy directo, amable, siempre dispuesto a aprender de ti y comprometido con proporcionar respuestas de calidad.",
54
+ philosophy: "Creo en la transparencia, la accesibilidad y en potenciar a los usuarios con conocimiento de calidad.",
55
+ background: "Fui diseñado por Lythron AI, una organización dedicada a hacer la inteligencia artificial más accesible y útil para todos.",
56
+ uniqueness: "Mi fortaleza radica en mi versatilidad: puedo pasar de programación compleja a creatividad pura, siempre manteniendo calidad."
57
+ }
58
+ }
59
+
60
+ // Función para llamar a Hugging Face API
61
+ const fetchAIResponse = async (userMessage) => {
62
+ try {
63
+ const HF_TOKEN = import.meta.env.VITE_HF_TOKEN
64
+
65
+ if (!HF_TOKEN) {
66
+ return "Error: Token de Hugging Face no configurado. Verifica tu archivo .env con VITE_HF_TOKEN"
67
+ }
68
+
69
+ const response = await axios.post(
70
+ "https://api-inference.huggingface.co/models/meta-llama/Llama-2-7b-chat-hf",
71
+ { inputs: userMessage },
72
+ {
73
+ headers: { Authorization: `Bearer ${ HF_TOKEN }` },
74
+ timeout: 30000
75
+ }
76
+ )
77
+
78
+ let text = response.data?.[0]?.generated_text || response.data?.generated_text || "Lo siento, no pude generar respuesta."
79
+
80
+ if (text.includes(userMessage)) {
81
+ text = text.replace(userMessage, "").trim()
82
+ }
83
+
84
+ return text || "Lo siento, no pude generar respuesta."
85
+ } catch (error) {
86
+ console.error("Error al llamar a Hugging Face:", error)
87
+
88
+ if (error.response?.status === 429) {
89
+ return "El modelo está sobrecargado. Por favor, intenta en unos segundos."
90
+ }
91
+
92
+ if (error.response?.status === 503) {
93
+ return "El modelo se está cargando. Por favor, espera unos momentos e intenta de nuevo."
94
+ }
95
+
96
+ return `Error: ${ error.response?.data?.error || error.message || "Problema generando respuesta de IA. Intenta de nuevo." }`
97
+ }
98
+ }
99
+
100
+ export default function Lythron() {
101
+ const [messages, setMessages] = useState([
102
+ {
103
+ id: 1,
104
+ text: `Hola. Soy ${ LYTHRON_IDENTITY.name }, tu asistente inteligente creado por ${ LYTHRON_IDENTITY.creator }. Estoy conectado a Hugging Face para darte respuestas reales. En qué puedo ayudarte hoy?`,
105
+ sender: "ai",
106
+ timestamp: new Date(),
107
+ }
108
+ ])
109
+ const [input, setInput] = useState("")
110
+ const [isLoading, setIsLoading] = useState(false)
111
+ const [copiedId, setCopiedId] = useState(null)
112
+ const messagesEndRef = useRef(null)
113
+
114
+ const scrollToBottom = () => {
115
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
116
+ }
117
+
118
+ useEffect(() => {
119
+ scrollToBottom()
120
+ }, [messages])
121
+
122
+ const handleSendMessage = async () => {
123
+ if (!input.trim()) return
124
+
125
+ const newMessage = {
126
+ id: Date.now(),
127
+ text: input,
128
+ sender: "user",
129
+ timestamp: new Date(),
130
+ }
131
+
132
+ setMessages((prev) => [...prev, newMessage])
133
+ setInput("")
134
+ setIsLoading(true)
135
+
136
+ try {
137
+ const aiText = await fetchAIResponse(input)
138
+ const aiResponse = {
139
+ id: Date.now() + 1,
140
+ text: aiText,
141
+ sender: "ai",
142
+ timestamp: new Date(),
143
+ }
144
+ setMessages((prev) => [...prev, aiResponse])
145
+ } catch (error) {
146
+ console.error("Error en handleSendMessage:", error)
147
+ const errorResponse = {
148
+ id: Date.now() + 1,
149
+ text: "Hubo un problema conectando con la IA. Por favor, intenta de nuevo.",
150
+ sender: "ai",
151
+ timestamp: new Date(),
152
+ }
153
+ setMessages((prev) => [...prev, errorResponse])
154
+ } finally {
155
+ setIsLoading(false)
156
+ }
157
+ }
158
+
159
+ const handleCopy = (text, id) => {
160
+ navigator.clipboard.writeText(text)
161
+ setCopiedId(id)
162
+ setTimeout(() => setCopiedId(null), 2000)
163
+ }
164
+
165
+ const handleClearChat = () => {
166
+ setMessages([
167
+ {
168
+ id: 1,
169
+ text: `Hola. Soy ${ LYTHRON_IDENTITY.name } v${ LYTHRON_IDENTITY.version }, creado por ${ LYTHRON_IDENTITY.creator }. Estoy conectado a Hugging Face para darte respuestas reales. En qué puedo ayudarte?`,
170
+ sender: "ai",
171
+ timestamp: new Date(),
172
+ }
173
+ ])
174
+ }
175
+
176
+ return (
177
+ <div className="flex flex-col h-screen bg-gradient-to-br from-zinc-950 via-zinc-900 to-black text-white">
178
+ <div className="border-b border-white/10 bg-black/40 backdrop-blur-md sticky top-0 z-10">
179
+ <div className="max-w-6xl mx-auto px-4 py-4 flex items-center justify-between">
180
+ <div className="flex items-center gap-3">
181
+ <div className="p-2 bg-gradient-to-br from-neon-cyan to-neon-pink rounded-lg">
182
+ <Sparkles size={24} className="text-black" />
183
+ </div>
184
+ <div>
185
+ <h1 className="text-2xl font-bold bg-gradient-to-r from-neon-cyan to-neon-pink bg-clip-text text-transparent">
186
+ {LYTHRON_IDENTITY.fullName}
187
+ </h1>
188
+ <p className="text-xs text-gray-400">Creado por {LYTHRON_IDENTITY.creator}</p>
189
+ </div>
190
+ </div>
191
+ <button
192
+ onClick={handleClearChat}
193
+ className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 rounded-lg transition-all text-sm"
194
+ >
195
+ <Trash2 size={18} />
196
+ Limpiar chat
197
+ </button>
198
+ </div>
199
+ </div>
200
+
201
+ <div className="flex-1 overflow-y-auto px-4 py-6 space-y-4 max-w-6xl mx-auto w-full">
202
+ <AnimatePresence>
203
+ {messages.map((message) => (
204
+ <motion.div
205
+ key={message.id}
206
+ initial={{ opacity: 0, y: 10 }}
207
+ animate={{ opacity: 1, y: 0 }}
208
+ exit={{ opacity: 0, y: -10 }}
209
+ transition={{ duration: 0.3 }}
210
+ className={`flex ${message.sender === "user" ? "justify-end" : "justify-start"}`}
211
+ >
212
+ <div
213
+ className={`max-w-2xl px-4 py-3 rounded-lg ${
214
+ message.sender === "user"
215
+ ? "bg-gradient-to-r from-neon-cyan to-neon-pink text-black rounded-br-none"
216
+ : "bg-white/10 border border-white/20 text-white rounded-bl-none"
217
+ }`}
218
+ >
219
+ <div className="flex items-start gap-3">
220
+ {message.sender === "ai" && (
221
+ <div className="mt-1 flex-shrink-0">
222
+ <div className="p-1 bg-neon-cyan/20 rounded">
223
+ <Sparkles size={16} className="text-neon-cyan" />
224
+ </div>
225
+ </div>
226
+ )}
227
+ <div className="flex-1">
228
+ <p className="text-sm leading-relaxed whitespace-pre-wrap break-words">
229
+ {message.text}
230
+ </p>
231
+ <div className="flex items-center gap-2 mt-2 text-xs opacity-70">
232
+ <span>{message.timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}</span>
233
+ {message.sender === "ai" && (
234
+ <>
235
+ <button
236
+ onClick={() => handleCopy(message.text, message.id)}
237
+ className="hover:opacity-100 transition-opacity p-1 hover:bg-white/10 rounded"
238
+ title="Copiar"
239
+ >
240
+ <Copy size={14} />
241
+ </button>
242
+ <button
243
+ className="hover:opacity-100 transition-opacity p-1 hover:bg-white/10 rounded"
244
+ title="Leer en voz alta"
245
+ >
246
+ <Volume2 size={14} />
247
+ </button>
248
+ </>
249
+ )}
250
+ </div>
251
+ </div>
252
+ {message.sender === "user" && (
253
+ <div className="mt-1 flex-shrink-0">
254
+ <div className="p-1 bg-black/30 rounded">
255
+ <User size={16} />
256
+ </div>
257
+ </div>
258
+ )}
259
+ </div>
260
+ {copiedId === message.id && (
261
+ <motion.p
262
+ initial={{ opacity: 0 }}
263
+ animate={{ opacity: 1 }}
264
+ exit={{ opacity: 0 }}
265
+ className="text-xs mt-1 text-green-400"
266
+ >
267
+ ✓ Copiado
268
+ </motion.p>
269
+ )}
270
+ </div>
271
+ </motion.div>
272
+ ))}
273
+ </AnimatePresence>
274
+
275
+ {isLoading && (
276
+ <motion.div
277
+ initial={{ opacity: 0, y: 10 }}
278
+ animate={{ opacity: 1, y: 0 }}
279
+ className="flex justify-start"
280
+ >
281
+ <div className="bg-white/10 border border-white/20 text-white rounded-lg rounded-bl-none px-4 py-3">
282
+ <div className="flex gap-2">
283
+ <motion.div
284
+ animate={{ scale: [1, 1.2, 1] }}
285
+ transition={{ repeat: Infinity, duration: 0.6 }}
286
+ className="w-2 h-2 bg-neon-cyan rounded-full"
287
+ />
288
+ <motion.div
289
+ animate={{ scale: [1, 1.2, 1] }}
290
+ transition={{ repeat: Infinity, duration: 0.6, delay: 0.1 }}
291
+ className="w-2 h-2 bg-neon-pink rounded-full"
292
+ />
293
+ <motion.div
294
+ animate={{ scale: [1, 1.2, 1] }}
295
+ transition={{ repeat: Infinity, duration: 0.6, delay: 0.2 }}
296
+ className="w-2 h-2 bg-neon-purple rounded-full"
297
+ />
298
+ </div>
299
+ </div>
300
+ </motion.div>
301
+ )}
302
+
303
+ <div ref={messagesEndRef} />
304
+ </div>
305
+
306
+ <div className="border-t border-white/10 bg-black/40 backdrop-blur-md p-4">
307
+ <div className="max-w-6xl mx-auto flex gap-2">
308
+ <input
309
+ type="text"
310
+ value={input}
311
+ onChange={(e) => setInput(e.target.value)}
312
+ onKeyPress={(e) => e.key === "Enter" && !isLoading && handleSendMessage()}
313
+ placeholder="Pregúntale a Lythron..."
314
+ className="flex-1 bg-white/10 border border-white/20 rounded-lg px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:border-neon-cyan focus:ring-2 focus:ring-neon-cyan/20 transition-all"
315
+ disabled={isLoading}
316
+ />
317
+ <button
318
+ onClick={handleSendMessage}
319
+ disabled={isLoading || !input.trim()}
320
+ className="p-3 bg-gradient-to-r from-neon-cyan to-neon-pink text-black rounded-lg hover:shadow-lg hover:shadow-neon-pink/50 transition-all disabled:opacity-50 disabled:cursor-not-allowed font-semibold"
321
+ >
322
+ <Send size={20} />
323
+ </button>
324
+ </div>
325
+ <p className="text-xs text-gray-500 mt-2 text-center">
326
+ {LYTHRON_IDENTITY.fullName} • Versión {LYTHRON_IDENTITY.version} • Creado por {LYTHRON_IDENTITY.creator} • Powered by Hugging Face
327
+ </p>
328
+ </div>
329
+ </div>
330
+ )
331
+ }
AI poweful/frontend/src/index.css ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ body {
6
+ font-family: 'Inter', sans-serif;
7
+ }
AI poweful/frontend/src/main.jsx ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ body {
6
+ font-family: 'Inter', sans-serif;
7
+ }
AI poweful/frontend/tailwind.config.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: [
4
+ "./index.html",
5
+ "./src/**/*.{js,ts,jsx,tsx}",
6
+ ],
7
+ theme: {
8
+ extend: {
9
+ colors: {
10
+ 'neon-cyan': '#00FFFF',
11
+ 'neon-pink': '#FF00FF',
12
+ 'neon-purple': '#B026FF',
13
+ },
14
+ fontFamily: {
15
+ sans: ['Inter', 'ui-sans-serif', 'system-ui'],
16
+ },
17
+ boxShadow: {
18
+ 'neon': '0 0 10px rgba(0,255,255,0.5), 0 0 20px rgba(255,0,255,0.5)',
19
+ },
20
+ backgroundImage: {
21
+ 'grid': "radial-gradient(circle at 1px 1px, rgba(255,255,255,0.05) 1px, transparent 0)",
22
+ },
23
+ animation: {
24
+ 'pulse-glow': 'pulseGlow 2s ease-in-out infinite',
25
+ },
26
+ keyframes: {
27
+ pulseGlow: {
28
+ '0%, 100%': { boxShadow: '0 0 10px rgba(0,255,255,0.5), 0 0 20px rgba(255,0,255,0.5)' },
29
+ '50%': { boxShadow: '0 0 20px rgba(0,255,255,1), 0 0 40px rgba(255,0,255,1)' },
30
+ },
31
+ },
32
+ },
33
+ },
34
+ plugins: [],
35
+ }
AI poweful/frontend/vite.config.js ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ // ════════════════════════════════════════════════════════════════
5
+ // Configuración de Vite para Lythron AI
6
+ // ────────────────────────────────────────────────────────────────
7
+ // - Optimizada para React + Tailwind
8
+ // - Soporta variables de entorno (.env)
9
+ // - Listo para desplegar en Hugging Face Spaces, Vercel o Netlify
10
+ // - Incluye alias y headers de seguridad básicos
11
+ // ════════════════════════════════════════════════════════════════
12
+
13
+ export default defineConfig({
14
+ plugins: [react()],
15
+
16
+ // Carpeta raíz del proyecto
17
+ root: './',
18
+
19
+ // Directorio de salida al compilar (producción)
20
+ build: {
21
+ outDir: 'dist',
22
+ assetsDir: 'assets',
23
+ sourcemap: false,
24
+ rollupOptions: {
25
+ output: {
26
+ manualChunks: undefined,
27
+ },
28
+ },
29
+ },
30
+
31
+ // Servidor local de desarrollo
32
+ server: {
33
+ port: 5173,
34
+ host: true, // Permite acceso desde LAN
35
+ open: true,
36
+ cors: true,
37
+ headers: {
38
+ "Cross-Origin-Opener-Policy": "same-origin",
39
+ "Cross-Origin-Embedder-Policy": "require-corp",
40
+ },
41
+ proxy: {
42
+ // 🔗 Conecta tu backend de FastAPI (ajusta puerto si es distinto)
43
+ '/api': {
44
+ target: 'http://127.0.0.1:8000',
45
+ changeOrigin: true,
46
+ rewrite: (path) => path.replace(/^\/api/, ''),
47
+ },
48
+ },
49
+ },
50
+
51
+ // Alias para importar más fácil componentes o utilidades
52
+ resolve: {
53
+ alias: {
54
+ '@': '/src',
55
+ },
56
+ },
57
+
58
+ // Variables globales accesibles desde import.meta.env
59
+ define: {
60
+ __APP_VERSION__: JSON.stringify('1.0.0'),
61
+ __APP_NAME__: JSON.stringify('Lythron AI'),
62
+ },
63
+ })
AI poweful/index.html ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <!-- ========= META PRINCIPALES ========= -->
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Lythron AI — Conversación Inteligente Futurista</title>
8
+ <meta
9
+ name="description"
10
+ content="Lythron AI: el asistente de conversación avanzada que une precisión técnica y creatividad. Interfaz inmersiva, estilo neón futurista y tecnología de vanguardia."
11
+ />
12
+ <meta name="theme-color" content="#66CCFF" />
13
+
14
+ <!-- ========= FUENTES ========= -->
15
+ <link
16
+ href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&family=Inter:wght@400;500;600&display=swap"
17
+ rel="stylesheet"
18
+ />
19
+
20
+ <!-- ========= ICONOS / FAVICON ========= -->
21
+ <link rel="icon" type="image/png" href="/favicon.png" />
22
+
23
+ <!-- ========= ESTILO INICIAL ========= -->
24
+ <style>
25
+ :root {
26
+ --neon-cyan: #66ccff;
27
+ --neon-pink: #ff66cc;
28
+ --neon-purple: #b266ff;
29
+ --bg-dark: #0a0a0f;
30
+ --bg-panel: rgba(20, 20, 30, 0.6);
31
+ }
32
+ body {
33
+ margin: 0;
34
+ font-family: 'Inter', sans-serif;
35
+ background: radial-gradient(circle at top, #0f172a, #000);
36
+ color: white;
37
+ overflow: hidden;
38
+ height: 100vh;
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ }
43
+ /* ========== SPLASH DE CARGA ========== */
44
+ #splash {
45
+ position: absolute;
46
+ inset: 0;
47
+ display: flex;
48
+ flex-direction: column;
49
+ align-items: center;
50
+ justify-content: center;
51
+ background: var(--bg-dark);
52
+ z-index: 50;
53
+ animation: fadeOut 1.2s ease-in-out 2.5s forwards;
54
+ }
55
+ .loader {
56
+ width: 80px;
57
+ height: 80px;
58
+ border: 4px solid transparent;
59
+ border-top: 4px solid var(--neon-cyan);
60
+ border-right: 4px solid var(--neon-pink);
61
+ border-radius: 50%;
62
+ animation: spin 1s linear infinite;
63
+ box-shadow: 0 0 25px var(--neon-pink);
64
+ }
65
+ .logo-text {
66
+ font-family: 'Poppins', sans-serif;
67
+ font-size: 1.6rem;
68
+ font-weight: 700;
69
+ margin-top: 1.2rem;
70
+ background: linear-gradient(90deg, var(--neon-cyan), var(--neon-pink));
71
+ -webkit-background-clip: text;
72
+ -webkit-text-fill-color: transparent;
73
+ letter-spacing: 2px;
74
+ }
75
+ @keyframes spin {
76
+ to {
77
+ transform: rotate(360deg);
78
+ }
79
+ }
80
+ @keyframes fadeOut {
81
+ to {
82
+ opacity: 0;
83
+ visibility: hidden;
84
+ }
85
+ }
86
+ /* ========== FONDO ANIMADO FUTURISTA ========== */
87
+ .background-grid {
88
+ position: absolute;
89
+ inset: 0;
90
+ background: repeating-linear-gradient(
91
+ 90deg,
92
+ rgba(255, 255, 255, 0.03) 0 1px,
93
+ transparent 1px 100%
94
+ ),
95
+ repeating-linear-gradient(
96
+ 0deg,
97
+ rgba(255, 255, 255, 0.03) 0 1px,
98
+ transparent 1px 100%
99
+ );
100
+ background-size: 40px 40px;
101
+ animation: moveGrid 12s linear infinite;
102
+ z-index: 0;
103
+ }
104
+ @keyframes moveGrid {
105
+ from {
106
+ background-position: 0 0;
107
+ }
108
+ to {
109
+ background-position: 40px 40px;
110
+ }
111
+ }
112
+ /* ========== CHAT CONTAINER ========== */
113
+ #root {
114
+ position: relative;
115
+ z-index: 10;
116
+ width: 100%;
117
+ max-width: 900px;
118
+ height: 90vh;
119
+ background: var(--bg-panel);
120
+ backdrop-filter: blur(10px);
121
+ border: 1px solid rgba(255, 255, 255, 0.1);
122
+ border-radius: 20px;
123
+ overflow: hidden;
124
+ display: flex;
125
+ flex-direction: column;
126
+ box-shadow: 0 0 25px rgba(102, 204, 255, 0.3);
127
+ }
128
+ /* ========== TITULO SUPERIOR ========== */
129
+ .chat-header {
130
+ text-align: center;
131
+ padding: 1.2rem;
132
+ font-family: 'Poppins', sans-serif;
133
+ font-weight: 600;
134
+ letter-spacing: 1px;
135
+ background: linear-gradient(90deg, var(--neon-cyan), var(--neon-pink));
136
+ -webkit-background-clip: text;
137
+ -webkit-text-fill-color: transparent;
138
+ }
139
+ </style>
140
+ </head>
141
+
142
+ <body>
143
+ <!-- Fondo dinámico -->
144
+ <div class="background-grid"></div>
145
+
146
+ <!-- Pantalla de carga -->
147
+ <div id="splash">
148
+ <div class="loader"></div>
149
+ <div class="logo-text">LYTHRON AI</div>
150
+ </div>
151
+
152
+ <!-- Contenedor de React -->
153
+ <div id="root">
154
+ <div class="chat-header">Lythron AI — Conversación Inteligente</div>
155
+ <!-- React reemplazará el contenido dentro del root -->
156
+ </div>
157
+
158
+ <!-- Si JS está deshabilitado -->
159
+ <noscript>
160
+ <div
161
+ style="color:white;text-align:center;padding:2rem;background:#0a0a0f;font-family:'Inter',sans-serif;"
162
+ >
163
+ Lythron AI requiere JavaScript para funcionar. Activa JavaScript e intenta nuevamente.
164
+ </div>
165
+ </noscript>
166
+
167
+ <!-- Archivo principal React -->
168
+ <script type="module" src="/src/main.jsx"></script>
169
+ </body>
170
+ </html>