Spaces:
Sleeping
Sleeping
umer6016
commited on
Commit
·
ad3d8b0
1
Parent(s):
04f25f0
Prepare for deployment: Update dependencies and add all new components
Browse files- .gitignore +9 -9
- AGENTS.md +25 -0
- README.md +3 -0
- backend/app/api/chat.py +27 -0
- backend/app/api/jobs.py +14 -0
- backend/app/main.py +0 -26
- backend/app/services/metrics_logger.py +129 -0
- backend/app/services/scrape_pipeline.py +76 -20
- deployment_guide.md +12 -3
- frontend/package-lock.json +290 -199
- frontend/package.json +7 -7
- frontend/src/App.jsx +16 -9
- requirements.txt +21 -58
- tests/test_metrics.py +176 -0
.gitignore
CHANGED
|
@@ -1,12 +1,12 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
build
|
| 4 |
-
*.log
|
| 5 |
-
__pycache__
|
| 6 |
*.pyc
|
| 7 |
.env
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
.DS_Store
|
| 9 |
-
venv
|
| 10 |
-
env
|
| 11 |
-
.vscode
|
| 12 |
-
.idea
|
|
|
|
| 1 |
+
.venv/
|
| 2 |
+
__pycache__/
|
|
|
|
|
|
|
|
|
|
| 3 |
*.pyc
|
| 4 |
.env
|
| 5 |
+
.env.local
|
| 6 |
+
knowledge_files/
|
| 7 |
+
backend/knowledge_files/
|
| 8 |
+
frontend/node_modules/
|
| 9 |
+
frontend/dist/
|
| 10 |
+
metrics_logs/
|
| 11 |
+
.vscode/
|
| 12 |
.DS_Store
|
|
|
|
|
|
|
|
|
|
|
|
AGENTS.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Codex Guidance
|
| 2 |
+
|
| 3 |
+
## Conventions
|
| 4 |
+
- Keep existing FastAPI endpoints and React flows stable; add new functionality behind flags/config toggles.
|
| 5 |
+
- Prefer small, composable helpers in `backend/app/services/` for pipeline changes.
|
| 6 |
+
- Do not commit secrets (`.env`), caches (`knowledge_files/`), build artifacts (`frontend/dist/`), or logs (`metrics_logs/`).
|
| 7 |
+
|
| 8 |
+
## Metrics
|
| 9 |
+
- New chatbot metrics live in `backend/app/services/scrape_pipeline.py`.
|
| 10 |
+
- Add supporting helpers in `backend/app/services/metrics_logger.py`.
|
| 11 |
+
- Guard all metrics code with the `ENABLE_METRICS_LOGGING` environment variable (default `false`).
|
| 12 |
+
- Surface new metrics via API stats objects without breaking existing fields.
|
| 13 |
+
|
| 14 |
+
## Commands to run after changes
|
| 15 |
+
- Backend: `pip install -r requirements.txt` (if deps change), then `pytest`.
|
| 16 |
+
- Frontend: `cd frontend && npm install` (if deps change), then `npm run build`.
|
| 17 |
+
|
| 18 |
+
## Feature flags
|
| 19 |
+
- All new features/metrics must be optional and off by default:
|
| 20 |
+
- `ENABLE_METRICS_LOGGING` controls metrics/telemetry.
|
| 21 |
+
- Wrap new logic in `if os.getenv("ENABLE_METRICS_LOGGING", "false").lower() == "true": ...`.
|
| 22 |
+
|
| 23 |
+
## Testing expectations
|
| 24 |
+
- Add/maintain unit tests for new logic; prefer fast, isolated tests with mocks over network calls.
|
| 25 |
+
- If modifying the pipeline, ensure cache behavior and new stats remain backward compatible.
|
README.md
CHANGED
|
@@ -80,6 +80,9 @@ npm install
|
|
| 80 |
npm run dev # opens on http://localhost:5173
|
| 81 |
```
|
| 82 |
|
|
|
|
|
|
|
|
|
|
| 83 |
### Usage
|
| 84 |
- Sign up (first/last/email/password) → OTP → auto-login.
|
| 85 |
- Generate chatbot: paste URL, optional Force refresh → Run. A brief summary (pages scraped, web searches) shows, then the chatbot appears.
|
|
|
|
| 80 |
npm run dev # opens on http://localhost:5173
|
| 81 |
```
|
| 82 |
|
| 83 |
+
### Optional metrics (feature-flagged)
|
| 84 |
+
- Set `ENABLE_METRICS_LOGGING=true` in your environment to capture Time-to-Chatbot-Ready (TCR), cache hit flags, and chat Q/A JSONL logs (`metrics_logs/chat_answers.jsonl`). Disabled by default to avoid any impact on existing flows.
|
| 85 |
+
|
| 86 |
### Usage
|
| 87 |
- Sign up (first/last/email/password) → OTP → auto-login.
|
| 88 |
- Generate chatbot: paste URL, optional Force refresh → Run. A brief summary (pages scraped, web searches) shows, then the chatbot appears.
|
backend/app/api/chat.py
CHANGED
|
@@ -2,6 +2,11 @@ from fastapi import APIRouter, HTTPException
|
|
| 2 |
from openai import OpenAI
|
| 3 |
|
| 4 |
from ..models.chat import ChatRequest, ChatResponse, ChatMessage
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
router = APIRouter()
|
| 7 |
|
|
@@ -22,6 +27,28 @@ async def chat(req: ChatRequest):
|
|
| 22 |
messages=messages,
|
| 23 |
)
|
| 24 |
answer = resp.choices[0].message.content or ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
return ChatResponse(message=ChatMessage(role="assistant", content=answer))
|
| 26 |
except Exception as exc:
|
| 27 |
raise HTTPException(status_code=500, detail=str(exc))
|
|
|
|
| 2 |
from openai import OpenAI
|
| 3 |
|
| 4 |
from ..models.chat import ChatRequest, ChatResponse, ChatMessage
|
| 5 |
+
from ..services.metrics_logger import (
|
| 6 |
+
log_chat_answer,
|
| 7 |
+
save_chat_answer_to_supabase,
|
| 8 |
+
metrics_enabled,
|
| 9 |
+
)
|
| 10 |
|
| 11 |
router = APIRouter()
|
| 12 |
|
|
|
|
| 27 |
messages=messages,
|
| 28 |
)
|
| 29 |
answer = resp.choices[0].message.content or ""
|
| 30 |
+
|
| 31 |
+
if metrics_enabled():
|
| 32 |
+
provenance = "primary_plus_secondary" if "SECONDARY SOURCE" in req.system_prompt else "primary_only"
|
| 33 |
+
try:
|
| 34 |
+
# Best-effort file log
|
| 35 |
+
log_chat_answer(
|
| 36 |
+
question=req.messages[-1].content if req.messages else "",
|
| 37 |
+
answer=answer,
|
| 38 |
+
provenance=provenance,
|
| 39 |
+
user=None,
|
| 40 |
+
)
|
| 41 |
+
# Best-effort Supabase log
|
| 42 |
+
save_chat_answer_to_supabase(
|
| 43 |
+
question=req.messages[-1].content if req.messages else "",
|
| 44 |
+
answer=answer,
|
| 45 |
+
system_prompt=req.system_prompt,
|
| 46 |
+
user_id=None,
|
| 47 |
+
url=None,
|
| 48 |
+
)
|
| 49 |
+
except Exception as log_exc:
|
| 50 |
+
print(f"⚠️ Metrics logging skipped: {log_exc}")
|
| 51 |
+
|
| 52 |
return ChatResponse(message=ChatMessage(role="assistant", content=answer))
|
| 53 |
except Exception as exc:
|
| 54 |
raise HTTPException(status_code=500, detail=str(exc))
|
backend/app/api/jobs.py
CHANGED
|
@@ -4,6 +4,8 @@ import asyncio
|
|
| 4 |
|
| 5 |
from ..models.jobs import JobCreate, JobStatus
|
| 6 |
from ..services import scrape_pipeline
|
|
|
|
|
|
|
| 7 |
|
| 8 |
router = APIRouter()
|
| 9 |
|
|
@@ -26,7 +28,19 @@ async def run_job(body: JobCreate) -> JobStatus:
|
|
| 26 |
"searches_run": stats.get("searches_run", 0),
|
| 27 |
"pages_scraped": stats.get("pages_scraped", 0),
|
| 28 |
"gaps_found": stats.get("gaps_found", 0),
|
|
|
|
|
|
|
| 29 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
return JobStatus(
|
| 31 |
job_id="dev-inline",
|
| 32 |
status="completed",
|
|
|
|
| 4 |
|
| 5 |
from ..models.jobs import JobCreate, JobStatus
|
| 6 |
from ..services import scrape_pipeline
|
| 7 |
+
from ..services.metrics_logger import save_job_metrics_to_supabase
|
| 8 |
+
from ..services.scrape_pipeline import ENABLE_METRICS
|
| 9 |
|
| 10 |
router = APIRouter()
|
| 11 |
|
|
|
|
| 28 |
"searches_run": stats.get("searches_run", 0),
|
| 29 |
"pages_scraped": stats.get("pages_scraped", 0),
|
| 30 |
"gaps_found": stats.get("gaps_found", 0),
|
| 31 |
+
"tcr_seconds": stats.get("tcr_seconds", 0.0),
|
| 32 |
+
"cache_hit": bool(stats.get("cache_hit", False)),
|
| 33 |
}
|
| 34 |
+
if ENABLE_METRICS:
|
| 35 |
+
try:
|
| 36 |
+
save_job_metrics_to_supabase(
|
| 37 |
+
url=str(body.url),
|
| 38 |
+
stats=stats,
|
| 39 |
+
user_id=None, # user id not available in this dev endpoint
|
| 40 |
+
)
|
| 41 |
+
except Exception as exc:
|
| 42 |
+
print(f"⚠️ Metrics logging skipped: {exc}")
|
| 43 |
+
|
| 44 |
return JobStatus(
|
| 45 |
job_id="dev-inline",
|
| 46 |
status="completed",
|
backend/app/main.py
CHANGED
|
@@ -3,11 +3,6 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
| 3 |
from .api.router import api_router
|
| 4 |
|
| 5 |
|
| 6 |
-
from fastapi.staticfiles import StaticFiles
|
| 7 |
-
from fastapi.responses import FileResponse
|
| 8 |
-
import os
|
| 9 |
-
|
| 10 |
-
|
| 11 |
def get_application() -> FastAPI:
|
| 12 |
app = FastAPI(
|
| 13 |
title="ChatSMITH Backend",
|
|
@@ -29,27 +24,6 @@ def get_application() -> FastAPI:
|
|
| 29 |
)
|
| 30 |
|
| 31 |
app.include_router(api_router, prefix="/api")
|
| 32 |
-
|
| 33 |
-
# Serve static files from the frontend build directory
|
| 34 |
-
# The Dockerfile copies frontend/dist to /app/frontend/dist
|
| 35 |
-
# In local dev, we might verify this path or stick to running frontend separately.
|
| 36 |
-
# We use a relative path assuming we run from /app root in Docker.
|
| 37 |
-
static_dir = os.path.join(os.path.dirname(__file__), "../../frontend/dist")
|
| 38 |
-
|
| 39 |
-
if os.path.isdir(static_dir):
|
| 40 |
-
app.mount("/assets", StaticFiles(directory=os.path.join(static_dir, "assets")), name="assets")
|
| 41 |
-
|
| 42 |
-
# Catch-all route for SPA client-side routing
|
| 43 |
-
@app.get("/{full_path:path}")
|
| 44 |
-
async def serve_app(full_path: str):
|
| 45 |
-
# Check if file exists in static dir (e.g. favicon.ico)
|
| 46 |
-
file_path = os.path.join(static_dir, full_path)
|
| 47 |
-
if os.path.isfile(file_path):
|
| 48 |
-
return FileResponse(file_path)
|
| 49 |
-
|
| 50 |
-
# Otherwise return index.html
|
| 51 |
-
return FileResponse(os.path.join(static_dir, "index.html"))
|
| 52 |
-
|
| 53 |
return app
|
| 54 |
|
| 55 |
|
|
|
|
| 3 |
from .api.router import api_router
|
| 4 |
|
| 5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
def get_application() -> FastAPI:
|
| 7 |
app = FastAPI(
|
| 8 |
title="ChatSMITH Backend",
|
|
|
|
| 24 |
)
|
| 25 |
|
| 26 |
app.include_router(api_router, prefix="/api")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
return app
|
| 28 |
|
| 29 |
|
backend/app/services/metrics_logger.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
from datetime import datetime, timezone
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from typing import Optional
|
| 6 |
+
|
| 7 |
+
from supabase import Client, create_client
|
| 8 |
+
|
| 9 |
+
# Default path can be overridden via METRICS_LOG_PATH; caller should guard with ENABLE_METRICS_LOGGING
|
| 10 |
+
LOG_PATH = Path(os.getenv("METRICS_LOG_PATH", "metrics_logs/chat_answers.jsonl"))
|
| 11 |
+
|
| 12 |
+
# Read Supabase settings (support common alt casing to avoid env mismatches)
|
| 13 |
+
SUPABASE_URL = (os.getenv("SUPABASE_URL") or os.getenv("supabase_url") or "").strip() or None
|
| 14 |
+
# Prefer service role key; fall back to anon for dev. Accept lowercase variants too.
|
| 15 |
+
SUPABASE_KEY = (
|
| 16 |
+
os.getenv("SUPABASE_SERVICE_ROLE_KEY")
|
| 17 |
+
or os.getenv("supabase_service_role_key")
|
| 18 |
+
or os.getenv("SUPABASE_ANON_KEY")
|
| 19 |
+
or os.getenv("supabase_anon_key")
|
| 20 |
+
or ""
|
| 21 |
+
).strip() or None
|
| 22 |
+
_supabase_client: Optional[Client] = None
|
| 23 |
+
_warned_no_supabase = False
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def _metrics_enabled() -> bool:
|
| 27 |
+
return (os.getenv("ENABLE_METRICS_LOGGING", "false") or "").strip().lower() == "true"
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def metrics_enabled() -> bool:
|
| 31 |
+
"""Public helper to check if metrics are enabled."""
|
| 32 |
+
return _metrics_enabled()
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def get_supabase_client() -> Optional[Client]:
|
| 36 |
+
"""
|
| 37 |
+
Return a Supabase client or None if not configured/initialization fails.
|
| 38 |
+
"""
|
| 39 |
+
global _supabase_client
|
| 40 |
+
global _warned_no_supabase
|
| 41 |
+
if _supabase_client:
|
| 42 |
+
return _supabase_client
|
| 43 |
+
if not SUPABASE_URL or not SUPABASE_KEY:
|
| 44 |
+
if _metrics_enabled() and not _warned_no_supabase:
|
| 45 |
+
print("⚠️ Metrics Supabase not configured: missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY/ANON.")
|
| 46 |
+
_warned_no_supabase = True
|
| 47 |
+
return None
|
| 48 |
+
try:
|
| 49 |
+
_supabase_client = create_client(SUPABASE_URL, SUPABASE_KEY)
|
| 50 |
+
except Exception as exc:
|
| 51 |
+
print(f"⚠️ Metrics Supabase init failed: {exc}")
|
| 52 |
+
_supabase_client = None
|
| 53 |
+
return _supabase_client
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def log_chat_answer(
|
| 57 |
+
question: str,
|
| 58 |
+
answer: str,
|
| 59 |
+
provenance: str,
|
| 60 |
+
user: Optional[str] = None,
|
| 61 |
+
log_path: Path = LOG_PATH,
|
| 62 |
+
) -> None:
|
| 63 |
+
"""
|
| 64 |
+
Append a single chat Q/A record as JSONL for downstream accuracy sampling.
|
| 65 |
+
"""
|
| 66 |
+
try:
|
| 67 |
+
log_path = Path(log_path)
|
| 68 |
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
| 69 |
+
record = {
|
| 70 |
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 71 |
+
"question": question,
|
| 72 |
+
"answer": answer,
|
| 73 |
+
"provenance": provenance,
|
| 74 |
+
"user": user,
|
| 75 |
+
}
|
| 76 |
+
with log_path.open("a", encoding="utf-8") as f:
|
| 77 |
+
f.write(json.dumps(record, ensure_ascii=False) + "\n")
|
| 78 |
+
except Exception as exc:
|
| 79 |
+
# Best-effort logging; never break the request path
|
| 80 |
+
print(f"⚠️ Metrics logging failed: {exc}")
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def save_job_metrics_to_supabase(url: str, stats: dict, user_id: Optional[str] = None) -> None:
|
| 84 |
+
"""
|
| 85 |
+
Insert a row into metrics_job_runs. Best-effort; quietly return on failure.
|
| 86 |
+
"""
|
| 87 |
+
client = get_supabase_client()
|
| 88 |
+
if not client:
|
| 89 |
+
return
|
| 90 |
+
try:
|
| 91 |
+
payload = {
|
| 92 |
+
"url": url,
|
| 93 |
+
"cache_hit": bool(stats.get("cache_hit", False)),
|
| 94 |
+
"tcr_seconds": float(stats.get("tcr_seconds", 0.0) or 0.0),
|
| 95 |
+
"searches_run": int(stats.get("searches_run", 0) or 0),
|
| 96 |
+
"pages_scraped": int(stats.get("pages_scraped", 0) or 0),
|
| 97 |
+
"gaps_found": int(stats.get("gaps_found", 0) or 0),
|
| 98 |
+
"user_id": user_id,
|
| 99 |
+
}
|
| 100 |
+
client.table("metrics_job_runs").insert(payload).execute()
|
| 101 |
+
except Exception as exc:
|
| 102 |
+
print(f"⚠️ Supabase job metrics insert failed: {exc}")
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def save_chat_answer_to_supabase(
|
| 106 |
+
question: str,
|
| 107 |
+
answer: str,
|
| 108 |
+
system_prompt: str,
|
| 109 |
+
user_id: Optional[str] = None,
|
| 110 |
+
url: Optional[str] = None,
|
| 111 |
+
) -> None:
|
| 112 |
+
"""
|
| 113 |
+
Insert a chat answer row into metrics_chat_answers. Best-effort.
|
| 114 |
+
"""
|
| 115 |
+
client = get_supabase_client()
|
| 116 |
+
if not client:
|
| 117 |
+
return
|
| 118 |
+
try:
|
| 119 |
+
provenance = "primary_plus_secondary" if "SECONDARY SOURCE" in (system_prompt or "") else "primary_only"
|
| 120 |
+
payload = {
|
| 121 |
+
"user_id": user_id,
|
| 122 |
+
"url": url,
|
| 123 |
+
"question": question,
|
| 124 |
+
"answer": answer,
|
| 125 |
+
"provenance": provenance,
|
| 126 |
+
}
|
| 127 |
+
client.table("metrics_chat_answers").insert(payload).execute()
|
| 128 |
+
except Exception as exc:
|
| 129 |
+
print(f"⚠️ Supabase chat metrics insert failed: {exc}")
|
backend/app/services/scrape_pipeline.py
CHANGED
|
@@ -4,6 +4,8 @@ import json
|
|
| 4 |
import hashlib
|
| 5 |
import re
|
| 6 |
import ssl
|
|
|
|
|
|
|
| 7 |
from datetime import datetime
|
| 8 |
from typing import List, Dict, Tuple
|
| 9 |
from urllib.parse import urljoin, urlparse
|
|
@@ -18,17 +20,24 @@ from pydantic import BaseModel, Field
|
|
| 18 |
from supabase import Client, create_client
|
| 19 |
from agents import Agent, WebSearchTool, Runner
|
| 20 |
from agents.model_settings import ModelSettings
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
# Initialize
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
load_dotenv(override=True)
|
| 24 |
client = OpenAI()
|
|
|
|
| 25 |
|
| 26 |
# Create SSL context with certifi certificates (matches notebook behavior)
|
| 27 |
SSL_CONTEXT = ssl.create_default_context(cafile=certifi.where())
|
| 28 |
|
| 29 |
-
# Create knowledge_files directory if it doesn't exist
|
| 30 |
-
os.makedirs("knowledge_files", exist_ok=True)
|
| 31 |
-
|
| 32 |
print("✅ Imports loaded")
|
| 33 |
|
| 34 |
# Supabase auth setup
|
|
@@ -410,6 +419,15 @@ async def scrape_website(url: str) -> Dict:
|
|
| 410 |
# Step 1: Fetch homepage with retry
|
| 411 |
print(" 📄 Fetching homepage...")
|
| 412 |
_, homepage_html, homepage_error = await fetch_page_with_retry(session, url)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
|
| 414 |
if not homepage_html:
|
| 415 |
error_msg = f"Failed to fetch homepage: {homepage_error}"
|
|
@@ -719,27 +737,34 @@ def get_cache_path(url: str) -> str:
|
|
| 719 |
"""Get the cache file path for a given URL."""
|
| 720 |
url_hash = hashlib.md5(url.encode()).hexdigest()[:12]
|
| 721 |
domain = urlparse(url).netloc.replace("www.", "").replace(".", "_")
|
| 722 |
-
return f"
|
| 723 |
|
| 724 |
|
| 725 |
def is_cached(url: str) -> bool:
|
| 726 |
"""Check if knowledge for a URL is already cached."""
|
| 727 |
-
cache_path = get_cache_path(url)
|
| 728 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 729 |
|
| 730 |
|
| 731 |
def get_cached_knowledge(url: str) -> Dict | None:
|
| 732 |
"""Load cached knowledge if available. Returns None if not cached."""
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
|
|
|
|
|
|
|
|
|
| 743 |
return None
|
| 744 |
|
| 745 |
|
|
@@ -778,13 +803,14 @@ def create_knowledge_json(url: str, scraped_data: Dict, web_search_results: List
|
|
| 778 |
|
| 779 |
def save_knowledge_json(knowledge: Dict, url: str) -> str:
|
| 780 |
"""Save knowledge JSON to file. Returns filepath."""
|
| 781 |
-
filepath = get_cache_path(url)
|
|
|
|
| 782 |
|
| 783 |
with open(filepath, 'w', encoding='utf-8') as f:
|
| 784 |
json.dump(knowledge, f, indent=2, ensure_ascii=False)
|
| 785 |
|
| 786 |
print(f"💾 Knowledge saved to: {filepath}")
|
| 787 |
-
return filepath
|
| 788 |
|
| 789 |
|
| 790 |
def load_knowledge_json(filepath: str) -> Dict:
|
|
@@ -1275,7 +1301,8 @@ async def run_full_research_new(url: str, force_refresh: bool = False, progress=
|
|
| 1275 |
NEW workflow: Scrape first, then fill gaps with targeted searches.
|
| 1276 |
With caching support and improved error handling (Phase 3).
|
| 1277 |
"""
|
| 1278 |
-
stats = {"pages_scraped": 0, "searches_run": 0, "gaps_found": 0}
|
|
|
|
| 1279 |
errors = [] # Track errors for UI feedback
|
| 1280 |
|
| 1281 |
# ===== Check Cache First =====
|
|
@@ -1284,6 +1311,7 @@ async def run_full_research_new(url: str, force_refresh: bool = False, progress=
|
|
| 1284 |
|
| 1285 |
cached_knowledge = get_cached_knowledge(url)
|
| 1286 |
if cached_knowledge:
|
|
|
|
| 1287 |
progress(0.9, desc="Preparing chatbot from cache...")
|
| 1288 |
|
| 1289 |
# Extract name from cached data
|
|
@@ -1308,6 +1336,8 @@ RULES:
|
|
| 1308 |
|
| 1309 |
=== END ===
|
| 1310 |
"""
|
|
|
|
|
|
|
| 1311 |
progress(1.0, desc="Done (from cache)!")
|
| 1312 |
status_text = build_status_new(100, current_step=4, selected_name=raw_name,
|
| 1313 |
finished=True, stats=stats, from_cache=True)
|
|
@@ -1315,7 +1345,7 @@ RULES:
|
|
| 1315 |
msg_update = gr.update(interactive=True, placeholder="Ask anything about the website...")
|
| 1316 |
send_btn_update = gr.update(interactive=True)
|
| 1317 |
|
| 1318 |
-
return status_text, system_prompt, raw_name, [], msg_update, send_btn_update
|
| 1319 |
|
| 1320 |
# ===== Step 1: Scrape Website (PRIMARY SOURCE) =====
|
| 1321 |
progress(0.05, desc="Scraping website...")
|
|
@@ -1454,6 +1484,8 @@ RULES:
|
|
| 1454 |
"""
|
| 1455 |
|
| 1456 |
progress(1.0, desc="Done!")
|
|
|
|
|
|
|
| 1457 |
status_text = build_status_new(100, current_step=4, selected_name=raw_name,
|
| 1458 |
finished=True, stats=stats, errors=errors)
|
| 1459 |
|
|
@@ -1535,6 +1567,30 @@ def chat_fn(message, history, system_prompt, name, user=None):
|
|
| 1535 |
print(f"❌ Chat error: {e}")
|
| 1536 |
answer = f"⚠️ Sorry, there was an error generating a response. Please try again.\n\nError: {str(e)[:100]}"
|
| 1537 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1538 |
# Return in Gradio 6.x format
|
| 1539 |
return "", history + [
|
| 1540 |
{"role": "user", "content": message},
|
|
|
|
| 4 |
import hashlib
|
| 5 |
import re
|
| 6 |
import ssl
|
| 7 |
+
import time
|
| 8 |
+
from pathlib import Path
|
| 9 |
from datetime import datetime
|
| 10 |
from typing import List, Dict, Tuple
|
| 11 |
from urllib.parse import urljoin, urlparse
|
|
|
|
| 20 |
from supabase import Client, create_client
|
| 21 |
from agents import Agent, WebSearchTool, Runner
|
| 22 |
from agents.model_settings import ModelSettings
|
| 23 |
+
from .metrics_logger import (
|
| 24 |
+
log_chat_answer,
|
| 25 |
+
save_chat_answer_to_supabase,
|
| 26 |
+
)
|
| 27 |
|
| 28 |
# Initialize
|
| 29 |
+
# Knowledge base directory (consistent absolute path to avoid cwd issues)
|
| 30 |
+
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
| 31 |
+
KNOWLEDGE_DIR = PROJECT_ROOT / "knowledge_files"
|
| 32 |
+
KNOWLEDGE_DIR.mkdir(parents=True, exist_ok=True)
|
| 33 |
+
|
| 34 |
load_dotenv(override=True)
|
| 35 |
client = OpenAI()
|
| 36 |
+
ENABLE_METRICS = (os.getenv("ENABLE_METRICS_LOGGING", "false") or "").strip().lower() == "true"
|
| 37 |
|
| 38 |
# Create SSL context with certifi certificates (matches notebook behavior)
|
| 39 |
SSL_CONTEXT = ssl.create_default_context(cafile=certifi.where())
|
| 40 |
|
|
|
|
|
|
|
|
|
|
| 41 |
print("✅ Imports loaded")
|
| 42 |
|
| 43 |
# Supabase auth setup
|
|
|
|
| 419 |
# Step 1: Fetch homepage with retry
|
| 420 |
print(" 📄 Fetching homepage...")
|
| 421 |
_, homepage_html, homepage_error = await fetch_page_with_retry(session, url)
|
| 422 |
+
|
| 423 |
+
# Fallback: if HTTPS failed, try HTTP (some sites block/redirect HTTPS)
|
| 424 |
+
if not homepage_html and original_url.startswith("https://"):
|
| 425 |
+
fallback_url = "http://" + original_url[len("https://"):]
|
| 426 |
+
print(f" 🔁 HTTPS fetch failed, retrying with HTTP: {fallback_url}")
|
| 427 |
+
_, homepage_html, homepage_error = await fetch_page_with_retry(session, fallback_url)
|
| 428 |
+
if homepage_html:
|
| 429 |
+
url = fallback_url.rstrip('/')
|
| 430 |
+
results["source_url"] = url
|
| 431 |
|
| 432 |
if not homepage_html:
|
| 433 |
error_msg = f"Failed to fetch homepage: {homepage_error}"
|
|
|
|
| 737 |
"""Get the cache file path for a given URL."""
|
| 738 |
url_hash = hashlib.md5(url.encode()).hexdigest()[:12]
|
| 739 |
domain = urlparse(url).netloc.replace("www.", "").replace(".", "_")
|
| 740 |
+
return str(KNOWLEDGE_DIR / f"{domain}_{url_hash}.json")
|
| 741 |
|
| 742 |
|
| 743 |
def is_cached(url: str) -> bool:
|
| 744 |
"""Check if knowledge for a URL is already cached."""
|
| 745 |
+
cache_path = Path(get_cache_path(url))
|
| 746 |
+
if cache_path.exists():
|
| 747 |
+
return True
|
| 748 |
+
# Backward compatibility: check legacy relative path if different
|
| 749 |
+
legacy = Path("knowledge_files") / cache_path.name
|
| 750 |
+
return legacy.exists()
|
| 751 |
|
| 752 |
|
| 753 |
def get_cached_knowledge(url: str) -> Dict | None:
|
| 754 |
"""Load cached knowledge if available. Returns None if not cached."""
|
| 755 |
+
paths = [Path(get_cache_path(url))]
|
| 756 |
+
# Add legacy relative path as fallback
|
| 757 |
+
paths.append(Path("knowledge_files") / paths[0].name)
|
| 758 |
+
for cache_path in paths:
|
| 759 |
+
if cache_path.exists():
|
| 760 |
+
try:
|
| 761 |
+
with open(cache_path, 'r', encoding='utf-8') as f:
|
| 762 |
+
knowledge = json.load(f)
|
| 763 |
+
print(f"📂 Loaded from cache: {cache_path}")
|
| 764 |
+
return knowledge
|
| 765 |
+
except Exception as e:
|
| 766 |
+
print(f"⚠️ Cache read error ({cache_path}): {e}")
|
| 767 |
+
continue
|
| 768 |
return None
|
| 769 |
|
| 770 |
|
|
|
|
| 803 |
|
| 804 |
def save_knowledge_json(knowledge: Dict, url: str) -> str:
|
| 805 |
"""Save knowledge JSON to file. Returns filepath."""
|
| 806 |
+
filepath = Path(get_cache_path(url))
|
| 807 |
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
| 808 |
|
| 809 |
with open(filepath, 'w', encoding='utf-8') as f:
|
| 810 |
json.dump(knowledge, f, indent=2, ensure_ascii=False)
|
| 811 |
|
| 812 |
print(f"💾 Knowledge saved to: {filepath}")
|
| 813 |
+
return str(filepath)
|
| 814 |
|
| 815 |
|
| 816 |
def load_knowledge_json(filepath: str) -> Dict:
|
|
|
|
| 1301 |
NEW workflow: Scrape first, then fill gaps with targeted searches.
|
| 1302 |
With caching support and improved error handling (Phase 3).
|
| 1303 |
"""
|
| 1304 |
+
stats = {"pages_scraped": 0, "searches_run": 0, "gaps_found": 0, "cache_hit": False}
|
| 1305 |
+
start_time = time.time() if ENABLE_METRICS else None
|
| 1306 |
errors = [] # Track errors for UI feedback
|
| 1307 |
|
| 1308 |
# ===== Check Cache First =====
|
|
|
|
| 1311 |
|
| 1312 |
cached_knowledge = get_cached_knowledge(url)
|
| 1313 |
if cached_knowledge:
|
| 1314 |
+
stats["cache_hit"] = True
|
| 1315 |
progress(0.9, desc="Preparing chatbot from cache...")
|
| 1316 |
|
| 1317 |
# Extract name from cached data
|
|
|
|
| 1336 |
|
| 1337 |
=== END ===
|
| 1338 |
"""
|
| 1339 |
+
if start_time is not None:
|
| 1340 |
+
stats["tcr_seconds"] = time.time() - start_time
|
| 1341 |
progress(1.0, desc="Done (from cache)!")
|
| 1342 |
status_text = build_status_new(100, current_step=4, selected_name=raw_name,
|
| 1343 |
finished=True, stats=stats, from_cache=True)
|
|
|
|
| 1345 |
msg_update = gr.update(interactive=True, placeholder="Ask anything about the website...")
|
| 1346 |
send_btn_update = gr.update(interactive=True)
|
| 1347 |
|
| 1348 |
+
return status_text, system_prompt, raw_name, [], msg_update, send_btn_update, stats
|
| 1349 |
|
| 1350 |
# ===== Step 1: Scrape Website (PRIMARY SOURCE) =====
|
| 1351 |
progress(0.05, desc="Scraping website...")
|
|
|
|
| 1484 |
"""
|
| 1485 |
|
| 1486 |
progress(1.0, desc="Done!")
|
| 1487 |
+
if start_time is not None:
|
| 1488 |
+
stats["tcr_seconds"] = time.time() - start_time
|
| 1489 |
status_text = build_status_new(100, current_step=4, selected_name=raw_name,
|
| 1490 |
finished=True, stats=stats, errors=errors)
|
| 1491 |
|
|
|
|
| 1567 |
print(f"❌ Chat error: {e}")
|
| 1568 |
answer = f"⚠️ Sorry, there was an error generating a response. Please try again.\n\nError: {str(e)[:100]}"
|
| 1569 |
|
| 1570 |
+
if ENABLE_METRICS:
|
| 1571 |
+
provenance = "primary_plus_secondary" if system_prompt and "SECONDARY SOURCE" in system_prompt else "primary_only"
|
| 1572 |
+
user_email = None
|
| 1573 |
+
if isinstance(user, dict):
|
| 1574 |
+
user_email = user.get("email")
|
| 1575 |
+
else:
|
| 1576 |
+
user_email = getattr(user, "email", None)
|
| 1577 |
+
log_chat_answer(
|
| 1578 |
+
question=message,
|
| 1579 |
+
answer=answer,
|
| 1580 |
+
provenance=provenance,
|
| 1581 |
+
user=user_email,
|
| 1582 |
+
)
|
| 1583 |
+
try:
|
| 1584 |
+
save_chat_answer_to_supabase(
|
| 1585 |
+
question=message,
|
| 1586 |
+
answer=answer,
|
| 1587 |
+
system_prompt=system_prompt,
|
| 1588 |
+
user_id=user_email,
|
| 1589 |
+
url=None, # URL not available in this handler
|
| 1590 |
+
)
|
| 1591 |
+
except Exception as exc:
|
| 1592 |
+
print(f"⚠️ Supabase chat metrics skipped: {exc}")
|
| 1593 |
+
|
| 1594 |
# Return in Gradio 6.x format
|
| 1595 |
return "", history + [
|
| 1596 |
{"role": "user", "content": message},
|
deployment_guide.md
CHANGED
|
@@ -31,9 +31,12 @@ You can upload files via the web interface or use Git. Since you have the code l
|
|
| 31 |
#### Option A: Drag and Drop (Easiest for one-off)
|
| 32 |
1. Go to the **Files** tab of your Space.
|
| 33 |
2. Click **Add file** -> **Upload files**.
|
| 34 |
-
3.
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
#### Option B: Git (Recommended)
|
| 39 |
1. In your local terminal, initialize git if not already:
|
|
@@ -50,6 +53,12 @@ You can upload files via the web interface or use Git. Since you have the code l
|
|
| 50 |
```bash
|
| 51 |
git push --force space master:main
|
| 52 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
### 4. Wait for Build
|
| 55 |
- Once files are uploaded, Hugging Face will automatically detect the `Dockerfile` and start building.
|
|
|
|
| 31 |
#### Option A: Drag and Drop (Easiest for one-off)
|
| 32 |
1. Go to the **Files** tab of your Space.
|
| 33 |
2. Click **Add file** -> **Upload files**.
|
| 34 |
+
3. Open your local `chatsmith-main` folder.
|
| 35 |
+
4. Select **all files inside** (backend, frontend, Dockerfile, etc.).
|
| 36 |
+
5. Drag and drop them into the web interface.
|
| 37 |
+
- **Important**: Do NOT drag the `chatsmith-main` folder itself. Drag the *contents*.
|
| 38 |
+
- *Tip: You can skip the `node_modules` folder if it exists, as it will be recreated by Docker.*
|
| 39 |
+
6. Commit the changes.
|
| 40 |
|
| 41 |
#### Option B: Git (Recommended)
|
| 42 |
1. In your local terminal, initialize git if not already:
|
|
|
|
| 53 |
```bash
|
| 54 |
git push --force space master:main
|
| 55 |
```
|
| 56 |
+
*Note: If asked for a password, you must use a **Hugging Face Access Token** with `write` permissions (Settings -> Access Tokens), NOT your account password.*
|
| 57 |
+
|
| 58 |
+
**Authentication Helper (if needed):**
|
| 59 |
+
```bash
|
| 60 |
+
git config --global credential.helper store
|
| 61 |
+
```
|
| 62 |
|
| 63 |
### 4. Wait for Build
|
| 64 |
- Once files are uploaded, Hugging Face will automatically detect the `Dockerfile` and start building.
|
frontend/package-lock.json
CHANGED
|
@@ -8,15 +8,15 @@
|
|
| 8 |
"name": "chatsmith-frontend",
|
| 9 |
"version": "0.1.0",
|
| 10 |
"dependencies": {
|
| 11 |
-
"@supabase/supabase-js": "^2.
|
| 12 |
-
"react": "^
|
| 13 |
-
"react-dom": "^
|
| 14 |
},
|
| 15 |
"devDependencies": {
|
| 16 |
-
"@types/react": "^
|
| 17 |
-
"@types/react-dom": "^
|
| 18 |
-
"@vitejs/plugin-react": "^
|
| 19 |
-
"vite": "^
|
| 20 |
}
|
| 21 |
},
|
| 22 |
"node_modules/@babel/code-frame": {
|
|
@@ -302,9 +302,9 @@
|
|
| 302 |
}
|
| 303 |
},
|
| 304 |
"node_modules/@esbuild/aix-ppc64": {
|
| 305 |
-
"version": "0.
|
| 306 |
-
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.
|
| 307 |
-
"integrity": "sha512-
|
| 308 |
"cpu": [
|
| 309 |
"ppc64"
|
| 310 |
],
|
|
@@ -315,13 +315,13 @@
|
|
| 315 |
"aix"
|
| 316 |
],
|
| 317 |
"engines": {
|
| 318 |
-
"node": ">=
|
| 319 |
}
|
| 320 |
},
|
| 321 |
"node_modules/@esbuild/android-arm": {
|
| 322 |
-
"version": "0.
|
| 323 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.
|
| 324 |
-
"integrity": "sha512-
|
| 325 |
"cpu": [
|
| 326 |
"arm"
|
| 327 |
],
|
|
@@ -332,13 +332,13 @@
|
|
| 332 |
"android"
|
| 333 |
],
|
| 334 |
"engines": {
|
| 335 |
-
"node": ">=
|
| 336 |
}
|
| 337 |
},
|
| 338 |
"node_modules/@esbuild/android-arm64": {
|
| 339 |
-
"version": "0.
|
| 340 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.
|
| 341 |
-
"integrity": "sha512-
|
| 342 |
"cpu": [
|
| 343 |
"arm64"
|
| 344 |
],
|
|
@@ -349,13 +349,13 @@
|
|
| 349 |
"android"
|
| 350 |
],
|
| 351 |
"engines": {
|
| 352 |
-
"node": ">=
|
| 353 |
}
|
| 354 |
},
|
| 355 |
"node_modules/@esbuild/android-x64": {
|
| 356 |
-
"version": "0.
|
| 357 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.
|
| 358 |
-
"integrity": "sha512-
|
| 359 |
"cpu": [
|
| 360 |
"x64"
|
| 361 |
],
|
|
@@ -366,13 +366,13 @@
|
|
| 366 |
"android"
|
| 367 |
],
|
| 368 |
"engines": {
|
| 369 |
-
"node": ">=
|
| 370 |
}
|
| 371 |
},
|
| 372 |
"node_modules/@esbuild/darwin-arm64": {
|
| 373 |
-
"version": "0.
|
| 374 |
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.
|
| 375 |
-
"integrity": "sha512-
|
| 376 |
"cpu": [
|
| 377 |
"arm64"
|
| 378 |
],
|
|
@@ -383,13 +383,13 @@
|
|
| 383 |
"darwin"
|
| 384 |
],
|
| 385 |
"engines": {
|
| 386 |
-
"node": ">=
|
| 387 |
}
|
| 388 |
},
|
| 389 |
"node_modules/@esbuild/darwin-x64": {
|
| 390 |
-
"version": "0.
|
| 391 |
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.
|
| 392 |
-
"integrity": "sha512-
|
| 393 |
"cpu": [
|
| 394 |
"x64"
|
| 395 |
],
|
|
@@ -400,13 +400,13 @@
|
|
| 400 |
"darwin"
|
| 401 |
],
|
| 402 |
"engines": {
|
| 403 |
-
"node": ">=
|
| 404 |
}
|
| 405 |
},
|
| 406 |
"node_modules/@esbuild/freebsd-arm64": {
|
| 407 |
-
"version": "0.
|
| 408 |
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.
|
| 409 |
-
"integrity": "sha512-
|
| 410 |
"cpu": [
|
| 411 |
"arm64"
|
| 412 |
],
|
|
@@ -417,13 +417,13 @@
|
|
| 417 |
"freebsd"
|
| 418 |
],
|
| 419 |
"engines": {
|
| 420 |
-
"node": ">=
|
| 421 |
}
|
| 422 |
},
|
| 423 |
"node_modules/@esbuild/freebsd-x64": {
|
| 424 |
-
"version": "0.
|
| 425 |
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.
|
| 426 |
-
"integrity": "sha512-
|
| 427 |
"cpu": [
|
| 428 |
"x64"
|
| 429 |
],
|
|
@@ -434,13 +434,13 @@
|
|
| 434 |
"freebsd"
|
| 435 |
],
|
| 436 |
"engines": {
|
| 437 |
-
"node": ">=
|
| 438 |
}
|
| 439 |
},
|
| 440 |
"node_modules/@esbuild/linux-arm": {
|
| 441 |
-
"version": "0.
|
| 442 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.
|
| 443 |
-
"integrity": "sha512-
|
| 444 |
"cpu": [
|
| 445 |
"arm"
|
| 446 |
],
|
|
@@ -451,13 +451,13 @@
|
|
| 451 |
"linux"
|
| 452 |
],
|
| 453 |
"engines": {
|
| 454 |
-
"node": ">=
|
| 455 |
}
|
| 456 |
},
|
| 457 |
"node_modules/@esbuild/linux-arm64": {
|
| 458 |
-
"version": "0.
|
| 459 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.
|
| 460 |
-
"integrity": "sha512-
|
| 461 |
"cpu": [
|
| 462 |
"arm64"
|
| 463 |
],
|
|
@@ -468,13 +468,13 @@
|
|
| 468 |
"linux"
|
| 469 |
],
|
| 470 |
"engines": {
|
| 471 |
-
"node": ">=
|
| 472 |
}
|
| 473 |
},
|
| 474 |
"node_modules/@esbuild/linux-ia32": {
|
| 475 |
-
"version": "0.
|
| 476 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.
|
| 477 |
-
"integrity": "sha512-
|
| 478 |
"cpu": [
|
| 479 |
"ia32"
|
| 480 |
],
|
|
@@ -485,13 +485,13 @@
|
|
| 485 |
"linux"
|
| 486 |
],
|
| 487 |
"engines": {
|
| 488 |
-
"node": ">=
|
| 489 |
}
|
| 490 |
},
|
| 491 |
"node_modules/@esbuild/linux-loong64": {
|
| 492 |
-
"version": "0.
|
| 493 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.
|
| 494 |
-
"integrity": "sha512-
|
| 495 |
"cpu": [
|
| 496 |
"loong64"
|
| 497 |
],
|
|
@@ -502,13 +502,13 @@
|
|
| 502 |
"linux"
|
| 503 |
],
|
| 504 |
"engines": {
|
| 505 |
-
"node": ">=
|
| 506 |
}
|
| 507 |
},
|
| 508 |
"node_modules/@esbuild/linux-mips64el": {
|
| 509 |
-
"version": "0.
|
| 510 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.
|
| 511 |
-
"integrity": "sha512-
|
| 512 |
"cpu": [
|
| 513 |
"mips64el"
|
| 514 |
],
|
|
@@ -519,13 +519,13 @@
|
|
| 519 |
"linux"
|
| 520 |
],
|
| 521 |
"engines": {
|
| 522 |
-
"node": ">=
|
| 523 |
}
|
| 524 |
},
|
| 525 |
"node_modules/@esbuild/linux-ppc64": {
|
| 526 |
-
"version": "0.
|
| 527 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.
|
| 528 |
-
"integrity": "sha512-
|
| 529 |
"cpu": [
|
| 530 |
"ppc64"
|
| 531 |
],
|
|
@@ -536,13 +536,13 @@
|
|
| 536 |
"linux"
|
| 537 |
],
|
| 538 |
"engines": {
|
| 539 |
-
"node": ">=
|
| 540 |
}
|
| 541 |
},
|
| 542 |
"node_modules/@esbuild/linux-riscv64": {
|
| 543 |
-
"version": "0.
|
| 544 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.
|
| 545 |
-
"integrity": "sha512-
|
| 546 |
"cpu": [
|
| 547 |
"riscv64"
|
| 548 |
],
|
|
@@ -553,13 +553,13 @@
|
|
| 553 |
"linux"
|
| 554 |
],
|
| 555 |
"engines": {
|
| 556 |
-
"node": ">=
|
| 557 |
}
|
| 558 |
},
|
| 559 |
"node_modules/@esbuild/linux-s390x": {
|
| 560 |
-
"version": "0.
|
| 561 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.
|
| 562 |
-
"integrity": "sha512-
|
| 563 |
"cpu": [
|
| 564 |
"s390x"
|
| 565 |
],
|
|
@@ -570,13 +570,13 @@
|
|
| 570 |
"linux"
|
| 571 |
],
|
| 572 |
"engines": {
|
| 573 |
-
"node": ">=
|
| 574 |
}
|
| 575 |
},
|
| 576 |
"node_modules/@esbuild/linux-x64": {
|
| 577 |
-
"version": "0.
|
| 578 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.
|
| 579 |
-
"integrity": "sha512-
|
| 580 |
"cpu": [
|
| 581 |
"x64"
|
| 582 |
],
|
|
@@ -587,13 +587,30 @@
|
|
| 587 |
"linux"
|
| 588 |
],
|
| 589 |
"engines": {
|
| 590 |
-
"node": ">=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 591 |
}
|
| 592 |
},
|
| 593 |
"node_modules/@esbuild/netbsd-x64": {
|
| 594 |
-
"version": "0.
|
| 595 |
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.
|
| 596 |
-
"integrity": "sha512-
|
| 597 |
"cpu": [
|
| 598 |
"x64"
|
| 599 |
],
|
|
@@ -604,13 +621,30 @@
|
|
| 604 |
"netbsd"
|
| 605 |
],
|
| 606 |
"engines": {
|
| 607 |
-
"node": ">=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 608 |
}
|
| 609 |
},
|
| 610 |
"node_modules/@esbuild/openbsd-x64": {
|
| 611 |
-
"version": "0.
|
| 612 |
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.
|
| 613 |
-
"integrity": "sha512-
|
| 614 |
"cpu": [
|
| 615 |
"x64"
|
| 616 |
],
|
|
@@ -621,13 +655,30 @@
|
|
| 621 |
"openbsd"
|
| 622 |
],
|
| 623 |
"engines": {
|
| 624 |
-
"node": ">=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 625 |
}
|
| 626 |
},
|
| 627 |
"node_modules/@esbuild/sunos-x64": {
|
| 628 |
-
"version": "0.
|
| 629 |
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.
|
| 630 |
-
"integrity": "sha512-
|
| 631 |
"cpu": [
|
| 632 |
"x64"
|
| 633 |
],
|
|
@@ -638,13 +689,13 @@
|
|
| 638 |
"sunos"
|
| 639 |
],
|
| 640 |
"engines": {
|
| 641 |
-
"node": ">=
|
| 642 |
}
|
| 643 |
},
|
| 644 |
"node_modules/@esbuild/win32-arm64": {
|
| 645 |
-
"version": "0.
|
| 646 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.
|
| 647 |
-
"integrity": "sha512-
|
| 648 |
"cpu": [
|
| 649 |
"arm64"
|
| 650 |
],
|
|
@@ -655,13 +706,13 @@
|
|
| 655 |
"win32"
|
| 656 |
],
|
| 657 |
"engines": {
|
| 658 |
-
"node": ">=
|
| 659 |
}
|
| 660 |
},
|
| 661 |
"node_modules/@esbuild/win32-ia32": {
|
| 662 |
-
"version": "0.
|
| 663 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.
|
| 664 |
-
"integrity": "sha512-
|
| 665 |
"cpu": [
|
| 666 |
"ia32"
|
| 667 |
],
|
|
@@ -672,13 +723,13 @@
|
|
| 672 |
"win32"
|
| 673 |
],
|
| 674 |
"engines": {
|
| 675 |
-
"node": ">=
|
| 676 |
}
|
| 677 |
},
|
| 678 |
"node_modules/@esbuild/win32-x64": {
|
| 679 |
-
"version": "0.
|
| 680 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.
|
| 681 |
-
"integrity": "sha512-
|
| 682 |
"cpu": [
|
| 683 |
"x64"
|
| 684 |
],
|
|
@@ -689,7 +740,7 @@
|
|
| 689 |
"win32"
|
| 690 |
],
|
| 691 |
"engines": {
|
| 692 |
-
"node": ">=
|
| 693 |
}
|
| 694 |
},
|
| 695 |
"node_modules/@jridgewell/gen-mapping": {
|
|
@@ -743,9 +794,9 @@
|
|
| 743 |
}
|
| 744 |
},
|
| 745 |
"node_modules/@rolldown/pluginutils": {
|
| 746 |
-
"version": "1.0.0-beta.
|
| 747 |
-
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.
|
| 748 |
-
"integrity": "sha512
|
| 749 |
"dev": true,
|
| 750 |
"license": "MIT"
|
| 751 |
},
|
|
@@ -1204,32 +1255,24 @@
|
|
| 1204 |
"integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==",
|
| 1205 |
"license": "MIT"
|
| 1206 |
},
|
| 1207 |
-
"node_modules/@types/prop-types": {
|
| 1208 |
-
"version": "15.7.15",
|
| 1209 |
-
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
| 1210 |
-
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
|
| 1211 |
-
"dev": true,
|
| 1212 |
-
"license": "MIT"
|
| 1213 |
-
},
|
| 1214 |
"node_modules/@types/react": {
|
| 1215 |
-
"version": "
|
| 1216 |
-
"resolved": "https://registry.npmjs.org/@types/react/-/react-
|
| 1217 |
-
"integrity": "sha512-
|
| 1218 |
"dev": true,
|
| 1219 |
"license": "MIT",
|
| 1220 |
"dependencies": {
|
| 1221 |
-
"@types/prop-types": "*",
|
| 1222 |
"csstype": "^3.2.2"
|
| 1223 |
}
|
| 1224 |
},
|
| 1225 |
"node_modules/@types/react-dom": {
|
| 1226 |
-
"version": "
|
| 1227 |
-
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-
|
| 1228 |
-
"integrity": "sha512-
|
| 1229 |
"dev": true,
|
| 1230 |
"license": "MIT",
|
| 1231 |
"peerDependencies": {
|
| 1232 |
-
"@types/react": "^
|
| 1233 |
}
|
| 1234 |
},
|
| 1235 |
"node_modules/@types/ws": {
|
|
@@ -1242,21 +1285,21 @@
|
|
| 1242 |
}
|
| 1243 |
},
|
| 1244 |
"node_modules/@vitejs/plugin-react": {
|
| 1245 |
-
"version": "
|
| 1246 |
-
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-
|
| 1247 |
-
"integrity": "sha512-
|
| 1248 |
"dev": true,
|
| 1249 |
"license": "MIT",
|
| 1250 |
"dependencies": {
|
| 1251 |
-
"@babel/core": "^7.28.
|
| 1252 |
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
| 1253 |
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
| 1254 |
-
"@rolldown/pluginutils": "1.0.0-beta.
|
| 1255 |
"@types/babel__core": "^7.20.5",
|
| 1256 |
-
"react-refresh": "^0.
|
| 1257 |
},
|
| 1258 |
"engines": {
|
| 1259 |
-
"node": "^
|
| 1260 |
},
|
| 1261 |
"peerDependencies": {
|
| 1262 |
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
@@ -1367,9 +1410,9 @@
|
|
| 1367 |
"license": "ISC"
|
| 1368 |
},
|
| 1369 |
"node_modules/esbuild": {
|
| 1370 |
-
"version": "0.
|
| 1371 |
-
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.
|
| 1372 |
-
"integrity": "sha512-
|
| 1373 |
"dev": true,
|
| 1374 |
"hasInstallScript": true,
|
| 1375 |
"license": "MIT",
|
|
@@ -1377,32 +1420,35 @@
|
|
| 1377 |
"esbuild": "bin/esbuild"
|
| 1378 |
},
|
| 1379 |
"engines": {
|
| 1380 |
-
"node": ">=
|
| 1381 |
},
|
| 1382 |
"optionalDependencies": {
|
| 1383 |
-
"@esbuild/aix-ppc64": "0.
|
| 1384 |
-
"@esbuild/android-arm": "0.
|
| 1385 |
-
"@esbuild/android-arm64": "0.
|
| 1386 |
-
"@esbuild/android-x64": "0.
|
| 1387 |
-
"@esbuild/darwin-arm64": "0.
|
| 1388 |
-
"@esbuild/darwin-x64": "0.
|
| 1389 |
-
"@esbuild/freebsd-arm64": "0.
|
| 1390 |
-
"@esbuild/freebsd-x64": "0.
|
| 1391 |
-
"@esbuild/linux-arm": "0.
|
| 1392 |
-
"@esbuild/linux-arm64": "0.
|
| 1393 |
-
"@esbuild/linux-ia32": "0.
|
| 1394 |
-
"@esbuild/linux-loong64": "0.
|
| 1395 |
-
"@esbuild/linux-mips64el": "0.
|
| 1396 |
-
"@esbuild/linux-ppc64": "0.
|
| 1397 |
-
"@esbuild/linux-riscv64": "0.
|
| 1398 |
-
"@esbuild/linux-s390x": "0.
|
| 1399 |
-
"@esbuild/linux-x64": "0.
|
| 1400 |
-
"@esbuild/netbsd-
|
| 1401 |
-
"@esbuild/
|
| 1402 |
-
"@esbuild/
|
| 1403 |
-
"@esbuild/
|
| 1404 |
-
"@esbuild/
|
| 1405 |
-
"@esbuild/
|
|
|
|
|
|
|
|
|
|
| 1406 |
}
|
| 1407 |
},
|
| 1408 |
"node_modules/escalade": {
|
|
@@ -1415,6 +1461,24 @@
|
|
| 1415 |
"node": ">=6"
|
| 1416 |
}
|
| 1417 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1418 |
"node_modules/fsevents": {
|
| 1419 |
"version": "2.3.3",
|
| 1420 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
@@ -1453,6 +1517,7 @@
|
|
| 1453 |
"version": "4.0.0",
|
| 1454 |
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
| 1455 |
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
|
|
|
| 1456 |
"license": "MIT"
|
| 1457 |
},
|
| 1458 |
"node_modules/jsesc": {
|
|
@@ -1481,18 +1546,6 @@
|
|
| 1481 |
"node": ">=6"
|
| 1482 |
}
|
| 1483 |
},
|
| 1484 |
-
"node_modules/loose-envify": {
|
| 1485 |
-
"version": "1.4.0",
|
| 1486 |
-
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
| 1487 |
-
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
| 1488 |
-
"license": "MIT",
|
| 1489 |
-
"dependencies": {
|
| 1490 |
-
"js-tokens": "^3.0.0 || ^4.0.0"
|
| 1491 |
-
},
|
| 1492 |
-
"bin": {
|
| 1493 |
-
"loose-envify": "cli.js"
|
| 1494 |
-
}
|
| 1495 |
-
},
|
| 1496 |
"node_modules/lru-cache": {
|
| 1497 |
"version": "5.1.1",
|
| 1498 |
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
|
@@ -1543,6 +1596,19 @@
|
|
| 1543 |
"dev": true,
|
| 1544 |
"license": "ISC"
|
| 1545 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1546 |
"node_modules/postcss": {
|
| 1547 |
"version": "8.5.6",
|
| 1548 |
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
|
@@ -1573,34 +1639,30 @@
|
|
| 1573 |
}
|
| 1574 |
},
|
| 1575 |
"node_modules/react": {
|
| 1576 |
-
"version": "
|
| 1577 |
-
"resolved": "https://registry.npmjs.org/react/-/react-
|
| 1578 |
-
"integrity": "sha512-
|
| 1579 |
"license": "MIT",
|
| 1580 |
-
"dependencies": {
|
| 1581 |
-
"loose-envify": "^1.1.0"
|
| 1582 |
-
},
|
| 1583 |
"engines": {
|
| 1584 |
"node": ">=0.10.0"
|
| 1585 |
}
|
| 1586 |
},
|
| 1587 |
"node_modules/react-dom": {
|
| 1588 |
-
"version": "
|
| 1589 |
-
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-
|
| 1590 |
-
"integrity": "sha512-
|
| 1591 |
"license": "MIT",
|
| 1592 |
"dependencies": {
|
| 1593 |
-
"
|
| 1594 |
-
"scheduler": "^0.23.2"
|
| 1595 |
},
|
| 1596 |
"peerDependencies": {
|
| 1597 |
-
"react": "^
|
| 1598 |
}
|
| 1599 |
},
|
| 1600 |
"node_modules/react-refresh": {
|
| 1601 |
-
"version": "0.
|
| 1602 |
-
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.
|
| 1603 |
-
"integrity": "sha512-
|
| 1604 |
"dev": true,
|
| 1605 |
"license": "MIT",
|
| 1606 |
"engines": {
|
|
@@ -1650,13 +1712,10 @@
|
|
| 1650 |
}
|
| 1651 |
},
|
| 1652 |
"node_modules/scheduler": {
|
| 1653 |
-
"version": "0.
|
| 1654 |
-
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.
|
| 1655 |
-
"integrity": "sha512-
|
| 1656 |
-
"license": "MIT"
|
| 1657 |
-
"dependencies": {
|
| 1658 |
-
"loose-envify": "^1.1.0"
|
| 1659 |
-
}
|
| 1660 |
},
|
| 1661 |
"node_modules/semver": {
|
| 1662 |
"version": "6.3.1",
|
|
@@ -1678,6 +1737,23 @@
|
|
| 1678 |
"node": ">=0.10.0"
|
| 1679 |
}
|
| 1680 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1681 |
"node_modules/tslib": {
|
| 1682 |
"version": "2.8.1",
|
| 1683 |
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
@@ -1722,21 +1798,24 @@
|
|
| 1722 |
}
|
| 1723 |
},
|
| 1724 |
"node_modules/vite": {
|
| 1725 |
-
"version": "
|
| 1726 |
-
"resolved": "https://registry.npmjs.org/vite/-/vite-
|
| 1727 |
-
"integrity": "sha512-
|
| 1728 |
"dev": true,
|
| 1729 |
"license": "MIT",
|
| 1730 |
"dependencies": {
|
| 1731 |
-
"esbuild": "^0.
|
| 1732 |
-
"
|
| 1733 |
-
"
|
|
|
|
|
|
|
|
|
|
| 1734 |
},
|
| 1735 |
"bin": {
|
| 1736 |
"vite": "bin/vite.js"
|
| 1737 |
},
|
| 1738 |
"engines": {
|
| 1739 |
-
"node": "^
|
| 1740 |
},
|
| 1741 |
"funding": {
|
| 1742 |
"url": "https://github.com/vitejs/vite?sponsor=1"
|
|
@@ -1745,19 +1824,25 @@
|
|
| 1745 |
"fsevents": "~2.3.3"
|
| 1746 |
},
|
| 1747 |
"peerDependencies": {
|
| 1748 |
-
"@types/node": "^
|
| 1749 |
-
"
|
|
|
|
| 1750 |
"lightningcss": "^1.21.0",
|
| 1751 |
-
"sass": "
|
| 1752 |
-
"sass-embedded": "
|
| 1753 |
-
"stylus": "
|
| 1754 |
-
"sugarss": "
|
| 1755 |
-
"terser": "^5.
|
|
|
|
|
|
|
| 1756 |
},
|
| 1757 |
"peerDependenciesMeta": {
|
| 1758 |
"@types/node": {
|
| 1759 |
"optional": true
|
| 1760 |
},
|
|
|
|
|
|
|
|
|
|
| 1761 |
"less": {
|
| 1762 |
"optional": true
|
| 1763 |
},
|
|
@@ -1778,6 +1863,12 @@
|
|
| 1778 |
},
|
| 1779 |
"terser": {
|
| 1780 |
"optional": true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1781 |
}
|
| 1782 |
}
|
| 1783 |
},
|
|
|
|
| 8 |
"name": "chatsmith-frontend",
|
| 9 |
"version": "0.1.0",
|
| 10 |
"dependencies": {
|
| 11 |
+
"@supabase/supabase-js": "^2.86.2",
|
| 12 |
+
"react": "^19.2.1",
|
| 13 |
+
"react-dom": "^19.2.1"
|
| 14 |
},
|
| 15 |
"devDependencies": {
|
| 16 |
+
"@types/react": "^19.2.7",
|
| 17 |
+
"@types/react-dom": "^19.2.3",
|
| 18 |
+
"@vitejs/plugin-react": "^5.1.1",
|
| 19 |
+
"vite": "^7.2.6"
|
| 20 |
}
|
| 21 |
},
|
| 22 |
"node_modules/@babel/code-frame": {
|
|
|
|
| 302 |
}
|
| 303 |
},
|
| 304 |
"node_modules/@esbuild/aix-ppc64": {
|
| 305 |
+
"version": "0.25.12",
|
| 306 |
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
| 307 |
+
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
| 308 |
"cpu": [
|
| 309 |
"ppc64"
|
| 310 |
],
|
|
|
|
| 315 |
"aix"
|
| 316 |
],
|
| 317 |
"engines": {
|
| 318 |
+
"node": ">=18"
|
| 319 |
}
|
| 320 |
},
|
| 321 |
"node_modules/@esbuild/android-arm": {
|
| 322 |
+
"version": "0.25.12",
|
| 323 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
|
| 324 |
+
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
|
| 325 |
"cpu": [
|
| 326 |
"arm"
|
| 327 |
],
|
|
|
|
| 332 |
"android"
|
| 333 |
],
|
| 334 |
"engines": {
|
| 335 |
+
"node": ">=18"
|
| 336 |
}
|
| 337 |
},
|
| 338 |
"node_modules/@esbuild/android-arm64": {
|
| 339 |
+
"version": "0.25.12",
|
| 340 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
|
| 341 |
+
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
|
| 342 |
"cpu": [
|
| 343 |
"arm64"
|
| 344 |
],
|
|
|
|
| 349 |
"android"
|
| 350 |
],
|
| 351 |
"engines": {
|
| 352 |
+
"node": ">=18"
|
| 353 |
}
|
| 354 |
},
|
| 355 |
"node_modules/@esbuild/android-x64": {
|
| 356 |
+
"version": "0.25.12",
|
| 357 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
|
| 358 |
+
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
|
| 359 |
"cpu": [
|
| 360 |
"x64"
|
| 361 |
],
|
|
|
|
| 366 |
"android"
|
| 367 |
],
|
| 368 |
"engines": {
|
| 369 |
+
"node": ">=18"
|
| 370 |
}
|
| 371 |
},
|
| 372 |
"node_modules/@esbuild/darwin-arm64": {
|
| 373 |
+
"version": "0.25.12",
|
| 374 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
|
| 375 |
+
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
|
| 376 |
"cpu": [
|
| 377 |
"arm64"
|
| 378 |
],
|
|
|
|
| 383 |
"darwin"
|
| 384 |
],
|
| 385 |
"engines": {
|
| 386 |
+
"node": ">=18"
|
| 387 |
}
|
| 388 |
},
|
| 389 |
"node_modules/@esbuild/darwin-x64": {
|
| 390 |
+
"version": "0.25.12",
|
| 391 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
|
| 392 |
+
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
|
| 393 |
"cpu": [
|
| 394 |
"x64"
|
| 395 |
],
|
|
|
|
| 400 |
"darwin"
|
| 401 |
],
|
| 402 |
"engines": {
|
| 403 |
+
"node": ">=18"
|
| 404 |
}
|
| 405 |
},
|
| 406 |
"node_modules/@esbuild/freebsd-arm64": {
|
| 407 |
+
"version": "0.25.12",
|
| 408 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
|
| 409 |
+
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
|
| 410 |
"cpu": [
|
| 411 |
"arm64"
|
| 412 |
],
|
|
|
|
| 417 |
"freebsd"
|
| 418 |
],
|
| 419 |
"engines": {
|
| 420 |
+
"node": ">=18"
|
| 421 |
}
|
| 422 |
},
|
| 423 |
"node_modules/@esbuild/freebsd-x64": {
|
| 424 |
+
"version": "0.25.12",
|
| 425 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
|
| 426 |
+
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
|
| 427 |
"cpu": [
|
| 428 |
"x64"
|
| 429 |
],
|
|
|
|
| 434 |
"freebsd"
|
| 435 |
],
|
| 436 |
"engines": {
|
| 437 |
+
"node": ">=18"
|
| 438 |
}
|
| 439 |
},
|
| 440 |
"node_modules/@esbuild/linux-arm": {
|
| 441 |
+
"version": "0.25.12",
|
| 442 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
|
| 443 |
+
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
|
| 444 |
"cpu": [
|
| 445 |
"arm"
|
| 446 |
],
|
|
|
|
| 451 |
"linux"
|
| 452 |
],
|
| 453 |
"engines": {
|
| 454 |
+
"node": ">=18"
|
| 455 |
}
|
| 456 |
},
|
| 457 |
"node_modules/@esbuild/linux-arm64": {
|
| 458 |
+
"version": "0.25.12",
|
| 459 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
|
| 460 |
+
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
|
| 461 |
"cpu": [
|
| 462 |
"arm64"
|
| 463 |
],
|
|
|
|
| 468 |
"linux"
|
| 469 |
],
|
| 470 |
"engines": {
|
| 471 |
+
"node": ">=18"
|
| 472 |
}
|
| 473 |
},
|
| 474 |
"node_modules/@esbuild/linux-ia32": {
|
| 475 |
+
"version": "0.25.12",
|
| 476 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
|
| 477 |
+
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
|
| 478 |
"cpu": [
|
| 479 |
"ia32"
|
| 480 |
],
|
|
|
|
| 485 |
"linux"
|
| 486 |
],
|
| 487 |
"engines": {
|
| 488 |
+
"node": ">=18"
|
| 489 |
}
|
| 490 |
},
|
| 491 |
"node_modules/@esbuild/linux-loong64": {
|
| 492 |
+
"version": "0.25.12",
|
| 493 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
|
| 494 |
+
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
|
| 495 |
"cpu": [
|
| 496 |
"loong64"
|
| 497 |
],
|
|
|
|
| 502 |
"linux"
|
| 503 |
],
|
| 504 |
"engines": {
|
| 505 |
+
"node": ">=18"
|
| 506 |
}
|
| 507 |
},
|
| 508 |
"node_modules/@esbuild/linux-mips64el": {
|
| 509 |
+
"version": "0.25.12",
|
| 510 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
|
| 511 |
+
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
|
| 512 |
"cpu": [
|
| 513 |
"mips64el"
|
| 514 |
],
|
|
|
|
| 519 |
"linux"
|
| 520 |
],
|
| 521 |
"engines": {
|
| 522 |
+
"node": ">=18"
|
| 523 |
}
|
| 524 |
},
|
| 525 |
"node_modules/@esbuild/linux-ppc64": {
|
| 526 |
+
"version": "0.25.12",
|
| 527 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
|
| 528 |
+
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
|
| 529 |
"cpu": [
|
| 530 |
"ppc64"
|
| 531 |
],
|
|
|
|
| 536 |
"linux"
|
| 537 |
],
|
| 538 |
"engines": {
|
| 539 |
+
"node": ">=18"
|
| 540 |
}
|
| 541 |
},
|
| 542 |
"node_modules/@esbuild/linux-riscv64": {
|
| 543 |
+
"version": "0.25.12",
|
| 544 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
|
| 545 |
+
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
|
| 546 |
"cpu": [
|
| 547 |
"riscv64"
|
| 548 |
],
|
|
|
|
| 553 |
"linux"
|
| 554 |
],
|
| 555 |
"engines": {
|
| 556 |
+
"node": ">=18"
|
| 557 |
}
|
| 558 |
},
|
| 559 |
"node_modules/@esbuild/linux-s390x": {
|
| 560 |
+
"version": "0.25.12",
|
| 561 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
|
| 562 |
+
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
|
| 563 |
"cpu": [
|
| 564 |
"s390x"
|
| 565 |
],
|
|
|
|
| 570 |
"linux"
|
| 571 |
],
|
| 572 |
"engines": {
|
| 573 |
+
"node": ">=18"
|
| 574 |
}
|
| 575 |
},
|
| 576 |
"node_modules/@esbuild/linux-x64": {
|
| 577 |
+
"version": "0.25.12",
|
| 578 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
|
| 579 |
+
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
|
| 580 |
"cpu": [
|
| 581 |
"x64"
|
| 582 |
],
|
|
|
|
| 587 |
"linux"
|
| 588 |
],
|
| 589 |
"engines": {
|
| 590 |
+
"node": ">=18"
|
| 591 |
+
}
|
| 592 |
+
},
|
| 593 |
+
"node_modules/@esbuild/netbsd-arm64": {
|
| 594 |
+
"version": "0.25.12",
|
| 595 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
|
| 596 |
+
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
|
| 597 |
+
"cpu": [
|
| 598 |
+
"arm64"
|
| 599 |
+
],
|
| 600 |
+
"dev": true,
|
| 601 |
+
"license": "MIT",
|
| 602 |
+
"optional": true,
|
| 603 |
+
"os": [
|
| 604 |
+
"netbsd"
|
| 605 |
+
],
|
| 606 |
+
"engines": {
|
| 607 |
+
"node": ">=18"
|
| 608 |
}
|
| 609 |
},
|
| 610 |
"node_modules/@esbuild/netbsd-x64": {
|
| 611 |
+
"version": "0.25.12",
|
| 612 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
|
| 613 |
+
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
|
| 614 |
"cpu": [
|
| 615 |
"x64"
|
| 616 |
],
|
|
|
|
| 621 |
"netbsd"
|
| 622 |
],
|
| 623 |
"engines": {
|
| 624 |
+
"node": ">=18"
|
| 625 |
+
}
|
| 626 |
+
},
|
| 627 |
+
"node_modules/@esbuild/openbsd-arm64": {
|
| 628 |
+
"version": "0.25.12",
|
| 629 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
|
| 630 |
+
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
|
| 631 |
+
"cpu": [
|
| 632 |
+
"arm64"
|
| 633 |
+
],
|
| 634 |
+
"dev": true,
|
| 635 |
+
"license": "MIT",
|
| 636 |
+
"optional": true,
|
| 637 |
+
"os": [
|
| 638 |
+
"openbsd"
|
| 639 |
+
],
|
| 640 |
+
"engines": {
|
| 641 |
+
"node": ">=18"
|
| 642 |
}
|
| 643 |
},
|
| 644 |
"node_modules/@esbuild/openbsd-x64": {
|
| 645 |
+
"version": "0.25.12",
|
| 646 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
|
| 647 |
+
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
|
| 648 |
"cpu": [
|
| 649 |
"x64"
|
| 650 |
],
|
|
|
|
| 655 |
"openbsd"
|
| 656 |
],
|
| 657 |
"engines": {
|
| 658 |
+
"node": ">=18"
|
| 659 |
+
}
|
| 660 |
+
},
|
| 661 |
+
"node_modules/@esbuild/openharmony-arm64": {
|
| 662 |
+
"version": "0.25.12",
|
| 663 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
|
| 664 |
+
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
|
| 665 |
+
"cpu": [
|
| 666 |
+
"arm64"
|
| 667 |
+
],
|
| 668 |
+
"dev": true,
|
| 669 |
+
"license": "MIT",
|
| 670 |
+
"optional": true,
|
| 671 |
+
"os": [
|
| 672 |
+
"openharmony"
|
| 673 |
+
],
|
| 674 |
+
"engines": {
|
| 675 |
+
"node": ">=18"
|
| 676 |
}
|
| 677 |
},
|
| 678 |
"node_modules/@esbuild/sunos-x64": {
|
| 679 |
+
"version": "0.25.12",
|
| 680 |
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
|
| 681 |
+
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
|
| 682 |
"cpu": [
|
| 683 |
"x64"
|
| 684 |
],
|
|
|
|
| 689 |
"sunos"
|
| 690 |
],
|
| 691 |
"engines": {
|
| 692 |
+
"node": ">=18"
|
| 693 |
}
|
| 694 |
},
|
| 695 |
"node_modules/@esbuild/win32-arm64": {
|
| 696 |
+
"version": "0.25.12",
|
| 697 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
|
| 698 |
+
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
|
| 699 |
"cpu": [
|
| 700 |
"arm64"
|
| 701 |
],
|
|
|
|
| 706 |
"win32"
|
| 707 |
],
|
| 708 |
"engines": {
|
| 709 |
+
"node": ">=18"
|
| 710 |
}
|
| 711 |
},
|
| 712 |
"node_modules/@esbuild/win32-ia32": {
|
| 713 |
+
"version": "0.25.12",
|
| 714 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
|
| 715 |
+
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
|
| 716 |
"cpu": [
|
| 717 |
"ia32"
|
| 718 |
],
|
|
|
|
| 723 |
"win32"
|
| 724 |
],
|
| 725 |
"engines": {
|
| 726 |
+
"node": ">=18"
|
| 727 |
}
|
| 728 |
},
|
| 729 |
"node_modules/@esbuild/win32-x64": {
|
| 730 |
+
"version": "0.25.12",
|
| 731 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
|
| 732 |
+
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
|
| 733 |
"cpu": [
|
| 734 |
"x64"
|
| 735 |
],
|
|
|
|
| 740 |
"win32"
|
| 741 |
],
|
| 742 |
"engines": {
|
| 743 |
+
"node": ">=18"
|
| 744 |
}
|
| 745 |
},
|
| 746 |
"node_modules/@jridgewell/gen-mapping": {
|
|
|
|
| 794 |
}
|
| 795 |
},
|
| 796 |
"node_modules/@rolldown/pluginutils": {
|
| 797 |
+
"version": "1.0.0-beta.47",
|
| 798 |
+
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz",
|
| 799 |
+
"integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==",
|
| 800 |
"dev": true,
|
| 801 |
"license": "MIT"
|
| 802 |
},
|
|
|
|
| 1255 |
"integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==",
|
| 1256 |
"license": "MIT"
|
| 1257 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1258 |
"node_modules/@types/react": {
|
| 1259 |
+
"version": "19.2.7",
|
| 1260 |
+
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
| 1261 |
+
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
| 1262 |
"dev": true,
|
| 1263 |
"license": "MIT",
|
| 1264 |
"dependencies": {
|
|
|
|
| 1265 |
"csstype": "^3.2.2"
|
| 1266 |
}
|
| 1267 |
},
|
| 1268 |
"node_modules/@types/react-dom": {
|
| 1269 |
+
"version": "19.2.3",
|
| 1270 |
+
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
|
| 1271 |
+
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
| 1272 |
"dev": true,
|
| 1273 |
"license": "MIT",
|
| 1274 |
"peerDependencies": {
|
| 1275 |
+
"@types/react": "^19.2.0"
|
| 1276 |
}
|
| 1277 |
},
|
| 1278 |
"node_modules/@types/ws": {
|
|
|
|
| 1285 |
}
|
| 1286 |
},
|
| 1287 |
"node_modules/@vitejs/plugin-react": {
|
| 1288 |
+
"version": "5.1.1",
|
| 1289 |
+
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.1.tgz",
|
| 1290 |
+
"integrity": "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==",
|
| 1291 |
"dev": true,
|
| 1292 |
"license": "MIT",
|
| 1293 |
"dependencies": {
|
| 1294 |
+
"@babel/core": "^7.28.5",
|
| 1295 |
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
| 1296 |
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
| 1297 |
+
"@rolldown/pluginutils": "1.0.0-beta.47",
|
| 1298 |
"@types/babel__core": "^7.20.5",
|
| 1299 |
+
"react-refresh": "^0.18.0"
|
| 1300 |
},
|
| 1301 |
"engines": {
|
| 1302 |
+
"node": "^20.19.0 || >=22.12.0"
|
| 1303 |
},
|
| 1304 |
"peerDependencies": {
|
| 1305 |
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
|
|
| 1410 |
"license": "ISC"
|
| 1411 |
},
|
| 1412 |
"node_modules/esbuild": {
|
| 1413 |
+
"version": "0.25.12",
|
| 1414 |
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
| 1415 |
+
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
| 1416 |
"dev": true,
|
| 1417 |
"hasInstallScript": true,
|
| 1418 |
"license": "MIT",
|
|
|
|
| 1420 |
"esbuild": "bin/esbuild"
|
| 1421 |
},
|
| 1422 |
"engines": {
|
| 1423 |
+
"node": ">=18"
|
| 1424 |
},
|
| 1425 |
"optionalDependencies": {
|
| 1426 |
+
"@esbuild/aix-ppc64": "0.25.12",
|
| 1427 |
+
"@esbuild/android-arm": "0.25.12",
|
| 1428 |
+
"@esbuild/android-arm64": "0.25.12",
|
| 1429 |
+
"@esbuild/android-x64": "0.25.12",
|
| 1430 |
+
"@esbuild/darwin-arm64": "0.25.12",
|
| 1431 |
+
"@esbuild/darwin-x64": "0.25.12",
|
| 1432 |
+
"@esbuild/freebsd-arm64": "0.25.12",
|
| 1433 |
+
"@esbuild/freebsd-x64": "0.25.12",
|
| 1434 |
+
"@esbuild/linux-arm": "0.25.12",
|
| 1435 |
+
"@esbuild/linux-arm64": "0.25.12",
|
| 1436 |
+
"@esbuild/linux-ia32": "0.25.12",
|
| 1437 |
+
"@esbuild/linux-loong64": "0.25.12",
|
| 1438 |
+
"@esbuild/linux-mips64el": "0.25.12",
|
| 1439 |
+
"@esbuild/linux-ppc64": "0.25.12",
|
| 1440 |
+
"@esbuild/linux-riscv64": "0.25.12",
|
| 1441 |
+
"@esbuild/linux-s390x": "0.25.12",
|
| 1442 |
+
"@esbuild/linux-x64": "0.25.12",
|
| 1443 |
+
"@esbuild/netbsd-arm64": "0.25.12",
|
| 1444 |
+
"@esbuild/netbsd-x64": "0.25.12",
|
| 1445 |
+
"@esbuild/openbsd-arm64": "0.25.12",
|
| 1446 |
+
"@esbuild/openbsd-x64": "0.25.12",
|
| 1447 |
+
"@esbuild/openharmony-arm64": "0.25.12",
|
| 1448 |
+
"@esbuild/sunos-x64": "0.25.12",
|
| 1449 |
+
"@esbuild/win32-arm64": "0.25.12",
|
| 1450 |
+
"@esbuild/win32-ia32": "0.25.12",
|
| 1451 |
+
"@esbuild/win32-x64": "0.25.12"
|
| 1452 |
}
|
| 1453 |
},
|
| 1454 |
"node_modules/escalade": {
|
|
|
|
| 1461 |
"node": ">=6"
|
| 1462 |
}
|
| 1463 |
},
|
| 1464 |
+
"node_modules/fdir": {
|
| 1465 |
+
"version": "6.5.0",
|
| 1466 |
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
| 1467 |
+
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
| 1468 |
+
"dev": true,
|
| 1469 |
+
"license": "MIT",
|
| 1470 |
+
"engines": {
|
| 1471 |
+
"node": ">=12.0.0"
|
| 1472 |
+
},
|
| 1473 |
+
"peerDependencies": {
|
| 1474 |
+
"picomatch": "^3 || ^4"
|
| 1475 |
+
},
|
| 1476 |
+
"peerDependenciesMeta": {
|
| 1477 |
+
"picomatch": {
|
| 1478 |
+
"optional": true
|
| 1479 |
+
}
|
| 1480 |
+
}
|
| 1481 |
+
},
|
| 1482 |
"node_modules/fsevents": {
|
| 1483 |
"version": "2.3.3",
|
| 1484 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
|
|
| 1517 |
"version": "4.0.0",
|
| 1518 |
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
| 1519 |
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
| 1520 |
+
"dev": true,
|
| 1521 |
"license": "MIT"
|
| 1522 |
},
|
| 1523 |
"node_modules/jsesc": {
|
|
|
|
| 1546 |
"node": ">=6"
|
| 1547 |
}
|
| 1548 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1549 |
"node_modules/lru-cache": {
|
| 1550 |
"version": "5.1.1",
|
| 1551 |
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
|
|
|
| 1596 |
"dev": true,
|
| 1597 |
"license": "ISC"
|
| 1598 |
},
|
| 1599 |
+
"node_modules/picomatch": {
|
| 1600 |
+
"version": "4.0.3",
|
| 1601 |
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
| 1602 |
+
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
| 1603 |
+
"dev": true,
|
| 1604 |
+
"license": "MIT",
|
| 1605 |
+
"engines": {
|
| 1606 |
+
"node": ">=12"
|
| 1607 |
+
},
|
| 1608 |
+
"funding": {
|
| 1609 |
+
"url": "https://github.com/sponsors/jonschlinkert"
|
| 1610 |
+
}
|
| 1611 |
+
},
|
| 1612 |
"node_modules/postcss": {
|
| 1613 |
"version": "8.5.6",
|
| 1614 |
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
|
|
|
| 1639 |
}
|
| 1640 |
},
|
| 1641 |
"node_modules/react": {
|
| 1642 |
+
"version": "19.2.1",
|
| 1643 |
+
"resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
|
| 1644 |
+
"integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
|
| 1645 |
"license": "MIT",
|
|
|
|
|
|
|
|
|
|
| 1646 |
"engines": {
|
| 1647 |
"node": ">=0.10.0"
|
| 1648 |
}
|
| 1649 |
},
|
| 1650 |
"node_modules/react-dom": {
|
| 1651 |
+
"version": "19.2.1",
|
| 1652 |
+
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
|
| 1653 |
+
"integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
|
| 1654 |
"license": "MIT",
|
| 1655 |
"dependencies": {
|
| 1656 |
+
"scheduler": "^0.27.0"
|
|
|
|
| 1657 |
},
|
| 1658 |
"peerDependencies": {
|
| 1659 |
+
"react": "^19.2.1"
|
| 1660 |
}
|
| 1661 |
},
|
| 1662 |
"node_modules/react-refresh": {
|
| 1663 |
+
"version": "0.18.0",
|
| 1664 |
+
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
| 1665 |
+
"integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
|
| 1666 |
"dev": true,
|
| 1667 |
"license": "MIT",
|
| 1668 |
"engines": {
|
|
|
|
| 1712 |
}
|
| 1713 |
},
|
| 1714 |
"node_modules/scheduler": {
|
| 1715 |
+
"version": "0.27.0",
|
| 1716 |
+
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
| 1717 |
+
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
| 1718 |
+
"license": "MIT"
|
|
|
|
|
|
|
|
|
|
| 1719 |
},
|
| 1720 |
"node_modules/semver": {
|
| 1721 |
"version": "6.3.1",
|
|
|
|
| 1737 |
"node": ">=0.10.0"
|
| 1738 |
}
|
| 1739 |
},
|
| 1740 |
+
"node_modules/tinyglobby": {
|
| 1741 |
+
"version": "0.2.15",
|
| 1742 |
+
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
| 1743 |
+
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
| 1744 |
+
"dev": true,
|
| 1745 |
+
"license": "MIT",
|
| 1746 |
+
"dependencies": {
|
| 1747 |
+
"fdir": "^6.5.0",
|
| 1748 |
+
"picomatch": "^4.0.3"
|
| 1749 |
+
},
|
| 1750 |
+
"engines": {
|
| 1751 |
+
"node": ">=12.0.0"
|
| 1752 |
+
},
|
| 1753 |
+
"funding": {
|
| 1754 |
+
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 1755 |
+
}
|
| 1756 |
+
},
|
| 1757 |
"node_modules/tslib": {
|
| 1758 |
"version": "2.8.1",
|
| 1759 |
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
|
|
| 1798 |
}
|
| 1799 |
},
|
| 1800 |
"node_modules/vite": {
|
| 1801 |
+
"version": "7.2.6",
|
| 1802 |
+
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz",
|
| 1803 |
+
"integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==",
|
| 1804 |
"dev": true,
|
| 1805 |
"license": "MIT",
|
| 1806 |
"dependencies": {
|
| 1807 |
+
"esbuild": "^0.25.0",
|
| 1808 |
+
"fdir": "^6.5.0",
|
| 1809 |
+
"picomatch": "^4.0.3",
|
| 1810 |
+
"postcss": "^8.5.6",
|
| 1811 |
+
"rollup": "^4.43.0",
|
| 1812 |
+
"tinyglobby": "^0.2.15"
|
| 1813 |
},
|
| 1814 |
"bin": {
|
| 1815 |
"vite": "bin/vite.js"
|
| 1816 |
},
|
| 1817 |
"engines": {
|
| 1818 |
+
"node": "^20.19.0 || >=22.12.0"
|
| 1819 |
},
|
| 1820 |
"funding": {
|
| 1821 |
"url": "https://github.com/vitejs/vite?sponsor=1"
|
|
|
|
| 1824 |
"fsevents": "~2.3.3"
|
| 1825 |
},
|
| 1826 |
"peerDependencies": {
|
| 1827 |
+
"@types/node": "^20.19.0 || >=22.12.0",
|
| 1828 |
+
"jiti": ">=1.21.0",
|
| 1829 |
+
"less": "^4.0.0",
|
| 1830 |
"lightningcss": "^1.21.0",
|
| 1831 |
+
"sass": "^1.70.0",
|
| 1832 |
+
"sass-embedded": "^1.70.0",
|
| 1833 |
+
"stylus": ">=0.54.8",
|
| 1834 |
+
"sugarss": "^5.0.0",
|
| 1835 |
+
"terser": "^5.16.0",
|
| 1836 |
+
"tsx": "^4.8.1",
|
| 1837 |
+
"yaml": "^2.4.2"
|
| 1838 |
},
|
| 1839 |
"peerDependenciesMeta": {
|
| 1840 |
"@types/node": {
|
| 1841 |
"optional": true
|
| 1842 |
},
|
| 1843 |
+
"jiti": {
|
| 1844 |
+
"optional": true
|
| 1845 |
+
},
|
| 1846 |
"less": {
|
| 1847 |
"optional": true
|
| 1848 |
},
|
|
|
|
| 1863 |
},
|
| 1864 |
"terser": {
|
| 1865 |
"optional": true
|
| 1866 |
+
},
|
| 1867 |
+
"tsx": {
|
| 1868 |
+
"optional": true
|
| 1869 |
+
},
|
| 1870 |
+
"yaml": {
|
| 1871 |
+
"optional": true
|
| 1872 |
}
|
| 1873 |
}
|
| 1874 |
},
|
frontend/package.json
CHANGED
|
@@ -8,14 +8,14 @@
|
|
| 8 |
"preview": "vite preview"
|
| 9 |
},
|
| 10 |
"dependencies": {
|
| 11 |
-
"@supabase/supabase-js": "^2.
|
| 12 |
-
"react": "^
|
| 13 |
-
"react-dom": "^
|
| 14 |
},
|
| 15 |
"devDependencies": {
|
| 16 |
-
"@types/react": "^
|
| 17 |
-
"@types/react-dom": "^
|
| 18 |
-
"@vitejs/plugin-react": "^
|
| 19 |
-
"vite": "^
|
| 20 |
}
|
| 21 |
}
|
|
|
|
| 8 |
"preview": "vite preview"
|
| 9 |
},
|
| 10 |
"dependencies": {
|
| 11 |
+
"@supabase/supabase-js": "^2.86.2",
|
| 12 |
+
"react": "^19.2.1",
|
| 13 |
+
"react-dom": "^19.2.1"
|
| 14 |
},
|
| 15 |
"devDependencies": {
|
| 16 |
+
"@types/react": "^19.2.7",
|
| 17 |
+
"@types/react-dom": "^19.2.3",
|
| 18 |
+
"@vitejs/plugin-react": "^5.1.1",
|
| 19 |
+
"vite": "^7.2.6"
|
| 20 |
}
|
| 21 |
}
|
frontend/src/App.jsx
CHANGED
|
@@ -27,7 +27,7 @@ export default function App() {
|
|
| 27 |
const [session, setSession] = useState(null);
|
| 28 |
const [status, setStatus] = useState("");
|
| 29 |
const [forceRefresh, setForceRefresh] = useState(false);
|
| 30 |
-
const [urlValue, setUrlValue] = useState("");
|
| 31 |
const [jobResult, setJobResult] = useState(null);
|
| 32 |
const [systemPrompt, setSystemPrompt] = useState("");
|
| 33 |
const [siteName, setSiteName] = useState("Bot");
|
|
@@ -41,6 +41,7 @@ export default function App() {
|
|
| 41 |
const [resetOtpEntered, setResetOtpEntered] = useState(false);
|
| 42 |
const [resetOtpValue, setResetOtpValue] = useState("");
|
| 43 |
const [isRunning, setIsRunning] = useState(false);
|
|
|
|
| 44 |
const resetEmailRef = useRef(null);
|
| 45 |
const resetOtpRef = useRef(null);
|
| 46 |
const resetNewPassRef = useRef(null);
|
|
@@ -115,6 +116,8 @@ export default function App() {
|
|
| 115 |
};
|
| 116 |
|
| 117 |
const handleLogin = async () => {
|
|
|
|
|
|
|
| 118 |
const email = loginEmailRef.current?.value?.trim() || "";
|
| 119 |
const password = loginPassRef.current?.value || "";
|
| 120 |
setStatus("Logging in...");
|
|
@@ -124,13 +127,15 @@ export default function App() {
|
|
| 124 |
});
|
| 125 |
if (error) {
|
| 126 |
setStatus(`Login failed: ${error.message}`);
|
|
|
|
| 127 |
} else {
|
|
|
|
| 128 |
setSession(data.session);
|
| 129 |
setEmailDisplay(data.session?.user?.email || email);
|
| 130 |
const fn = data.session?.user?.user_metadata?.first_name;
|
| 131 |
setFirstNameDisplay(fn || firstNameDisplay || (email ? email.split("@")[0] : ""));
|
| 132 |
setStatus("Logged in.");
|
| 133 |
-
|
| 134 |
}
|
| 135 |
};
|
| 136 |
|
|
@@ -229,7 +234,7 @@ export default function App() {
|
|
| 229 |
};
|
| 230 |
|
| 231 |
const runJob = async () => {
|
| 232 |
-
const targetUrl = urlInputRef.current?.value
|
| 233 |
setIsRunning(true);
|
| 234 |
setStatus("Submitting job...");
|
| 235 |
setJobResult(null);
|
|
@@ -305,7 +310,9 @@ export default function App() {
|
|
| 305 |
ref={loginPassRef}
|
| 306 |
defaultValue=""
|
| 307 |
/>
|
| 308 |
-
<button onClick={handleLogin}
|
|
|
|
|
|
|
| 309 |
<p className="link" onClick={() => setView("signup")}>
|
| 310 |
Don’t have an account? Sign up
|
| 311 |
</p>
|
|
@@ -396,8 +403,12 @@ export default function App() {
|
|
| 396 |
<label className="label">Website URL</label>
|
| 397 |
<input
|
| 398 |
placeholder="https://example.com"
|
| 399 |
-
defaultValue={
|
| 400 |
ref={urlInputRef}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
/>
|
| 402 |
<label className="checkbox">
|
| 403 |
<input type="checkbox" checked={forceRefresh} onChange={(e) => setForceRefresh(e.target.checked)} />
|
|
@@ -440,10 +451,6 @@ export default function App() {
|
|
| 440 |
/>
|
| 441 |
<button onClick={sendChat}>Send</button>
|
| 442 |
<div className="status">{chatStatus}</div>
|
| 443 |
-
<details style={{ marginTop: 8 }}>
|
| 444 |
-
<summary className="muted small">View system prompt</summary>
|
| 445 |
-
<pre className="result" style={{ maxHeight: 160 }}>{systemPrompt}</pre>
|
| 446 |
-
</details>
|
| 447 |
</>
|
| 448 |
)}
|
| 449 |
</>
|
|
|
|
| 27 |
const [session, setSession] = useState(null);
|
| 28 |
const [status, setStatus] = useState("");
|
| 29 |
const [forceRefresh, setForceRefresh] = useState(false);
|
| 30 |
+
const [urlValue, setUrlValue] = useState("https://example.com");
|
| 31 |
const [jobResult, setJobResult] = useState(null);
|
| 32 |
const [systemPrompt, setSystemPrompt] = useState("");
|
| 33 |
const [siteName, setSiteName] = useState("Bot");
|
|
|
|
| 41 |
const [resetOtpEntered, setResetOtpEntered] = useState(false);
|
| 42 |
const [resetOtpValue, setResetOtpValue] = useState("");
|
| 43 |
const [isRunning, setIsRunning] = useState(false);
|
| 44 |
+
const [isAuthLoading, setIsAuthLoading] = useState(false);
|
| 45 |
const resetEmailRef = useRef(null);
|
| 46 |
const resetOtpRef = useRef(null);
|
| 47 |
const resetNewPassRef = useRef(null);
|
|
|
|
| 116 |
};
|
| 117 |
|
| 118 |
const handleLogin = async () => {
|
| 119 |
+
if (isAuthLoading) return;
|
| 120 |
+
setIsAuthLoading(true);
|
| 121 |
const email = loginEmailRef.current?.value?.trim() || "";
|
| 122 |
const password = loginPassRef.current?.value || "";
|
| 123 |
setStatus("Logging in...");
|
|
|
|
| 127 |
});
|
| 128 |
if (error) {
|
| 129 |
setStatus(`Login failed: ${error.message}`);
|
| 130 |
+
setIsAuthLoading(false);
|
| 131 |
} else {
|
| 132 |
+
setView("app"); // jump to app immediately on success
|
| 133 |
setSession(data.session);
|
| 134 |
setEmailDisplay(data.session?.user?.email || email);
|
| 135 |
const fn = data.session?.user?.user_metadata?.first_name;
|
| 136 |
setFirstNameDisplay(fn || firstNameDisplay || (email ? email.split("@")[0] : ""));
|
| 137 |
setStatus("Logged in.");
|
| 138 |
+
setIsAuthLoading(false);
|
| 139 |
}
|
| 140 |
};
|
| 141 |
|
|
|
|
| 234 |
};
|
| 235 |
|
| 236 |
const runJob = async () => {
|
| 237 |
+
const targetUrl = (urlInputRef.current?.value || "").trim() || defaultUrl;
|
| 238 |
setIsRunning(true);
|
| 239 |
setStatus("Submitting job...");
|
| 240 |
setJobResult(null);
|
|
|
|
| 310 |
ref={loginPassRef}
|
| 311 |
defaultValue=""
|
| 312 |
/>
|
| 313 |
+
<button onClick={handleLogin} disabled={isAuthLoading} className={isAuthLoading ? "loading" : ""}>
|
| 314 |
+
{isAuthLoading ? "Logging in..." : "Log In"}
|
| 315 |
+
</button>
|
| 316 |
<p className="link" onClick={() => setView("signup")}>
|
| 317 |
Don’t have an account? Sign up
|
| 318 |
</p>
|
|
|
|
| 403 |
<label className="label">Website URL</label>
|
| 404 |
<input
|
| 405 |
placeholder="https://example.com"
|
| 406 |
+
defaultValue={urlValue}
|
| 407 |
ref={urlInputRef}
|
| 408 |
+
autoCorrect="off"
|
| 409 |
+
autoCapitalize="none"
|
| 410 |
+
spellCheck={false}
|
| 411 |
+
onChange={(e) => setUrlValue(e.target.value)}
|
| 412 |
/>
|
| 413 |
<label className="checkbox">
|
| 414 |
<input type="checkbox" checked={forceRefresh} onChange={(e) => setForceRefresh(e.target.checked)} />
|
|
|
|
| 451 |
/>
|
| 452 |
<button onClick={sendChat}>Send</button>
|
| 453 |
<div className="status">{chatStatus}</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
</>
|
| 455 |
)}
|
| 456 |
</>
|
requirements.txt
CHANGED
|
@@ -1,64 +1,27 @@
|
|
| 1 |
-
# ChatSMITH -
|
| 2 |
-
#
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
# OpenAI
|
| 9 |
-
openai
|
| 10 |
-
openai-agents
|
| 11 |
-
# Alternative: Install from GitHub if PyPI version has issues:
|
| 12 |
-
# git+https://github.com/openai/openai-agents-python.git
|
| 13 |
|
| 14 |
-
#
|
| 15 |
-
|
| 16 |
|
| 17 |
-
#
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
#
|
| 21 |
-
|
| 22 |
-
# ============================================================
|
| 23 |
|
| 24 |
-
#
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
# SSL certificates (REQUIRED for Windows!)
|
| 28 |
-
certifi>=2024.0.0
|
| 29 |
-
|
| 30 |
-
# HTML parsing and content extraction
|
| 31 |
-
beautifulsoup4>=4.12.0
|
| 32 |
-
lxml>=5.0.0
|
| 33 |
-
|
| 34 |
-
# ============================================================
|
| 35 |
-
# USER INTERFACE
|
| 36 |
-
# ============================================================
|
| 37 |
-
|
| 38 |
-
# Gradio for web UI (6.x recommended)
|
| 39 |
-
gradio>=4.0.0
|
| 40 |
-
# Supabase authentication
|
| 41 |
-
supabase>=2.4.0
|
| 42 |
-
# FastAPI backend
|
| 43 |
-
fastapi>=0.115.0
|
| 44 |
-
uvicorn[standard]>=0.29.0
|
| 45 |
-
|
| 46 |
-
# ============================================================
|
| 47 |
-
# OPTIONAL DEPENDENCIES
|
| 48 |
-
# ============================================================
|
| 49 |
-
|
| 50 |
-
# Export to Word (uncomment if needed)
|
| 51 |
-
# python-docx
|
| 52 |
-
|
| 53 |
-
# Email functionality (uncomment if needed)
|
| 54 |
-
# sendgrid
|
| 55 |
-
|
| 56 |
-
# JS rendering for heavy websites (uncomment if needed)
|
| 57 |
-
# Install browsers with: playwright install
|
| 58 |
-
# playwright>=1.48
|
| 59 |
-
|
| 60 |
-
# ============================================================
|
| 61 |
-
# DEVELOPMENT & TESTING
|
| 62 |
-
# ============================================================
|
| 63 |
-
|
| 64 |
-
pytest>=8.0.0
|
|
|
|
| 1 |
+
# ChatSMITH - backend requirements (FastAPI + scraper + Supabase auth)
|
| 2 |
+
# Updated: 2025-12-06 (pinned for reproducibility with current stack)
|
| 3 |
|
| 4 |
+
fastapi==0.123.10
|
| 5 |
+
uvicorn[standard]==0.38.0
|
| 6 |
+
python-dotenv==1.2.1
|
| 7 |
+
# gradio 6.0.2 caps pydantic at 2.12.4
|
| 8 |
+
pydantic==2.12.4
|
| 9 |
|
| 10 |
+
# OpenAI + agent stack
|
| 11 |
+
openai==2.9.0
|
| 12 |
+
openai-agents==0.6.2
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
# Supabase auth
|
| 15 |
+
supabase==2.25.0
|
| 16 |
|
| 17 |
+
# Scraping & parsing
|
| 18 |
+
aiohttp==3.13.2
|
| 19 |
+
certifi==2025.11.12
|
| 20 |
+
beautifulsoup4==4.14.3
|
| 21 |
+
lxml==6.0.2
|
| 22 |
|
| 23 |
+
# UI helpers (gradio callbacks used in pipeline)
|
| 24 |
+
gradio==6.0.2
|
|
|
|
| 25 |
|
| 26 |
+
# Dev / testing
|
| 27 |
+
pytest==9.0.1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_metrics.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
from types import SimpleNamespace
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
import pytest
|
| 8 |
+
|
| 9 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 10 |
+
|
| 11 |
+
from backend.app.services import metrics_logger as ml # noqa: E402
|
| 12 |
+
from backend.app.services import scrape_pipeline as sp # noqa: E402
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class DummyProgress:
|
| 16 |
+
def __call__(self, *args, **kwargs):
|
| 17 |
+
return None
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@pytest.mark.asyncio
|
| 21 |
+
async def test_cache_hit_metrics(monkeypatch):
|
| 22 |
+
monkeypatch.setattr(sp, "ENABLE_METRICS", True)
|
| 23 |
+
monkeypatch.setattr(sp.gr, "update", lambda **kwargs: {"update": kwargs})
|
| 24 |
+
monkeypatch.setattr(sp, "is_cached", lambda url: True)
|
| 25 |
+
monkeypatch.setattr(
|
| 26 |
+
sp,
|
| 27 |
+
"get_cached_knowledge",
|
| 28 |
+
lambda url: {"metadata": {"name": "CachedSite", "url": url, "pages_scraped": 2}},
|
| 29 |
+
)
|
| 30 |
+
monkeypatch.setattr(sp, "knowledge_to_chatbot_context", lambda knowledge: "ctx")
|
| 31 |
+
monkeypatch.setattr(sp, "build_status_new", lambda *args, **kwargs: "status")
|
| 32 |
+
|
| 33 |
+
result = await sp.run_full_research_new("https://example.com", progress=DummyProgress())
|
| 34 |
+
_, _, _, _, _, _, stats = result
|
| 35 |
+
|
| 36 |
+
assert stats["cache_hit"] is True
|
| 37 |
+
assert "tcr_seconds" in stats and stats["tcr_seconds"] >= 0
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@pytest.mark.asyncio
|
| 41 |
+
async def test_tcr_metrics_non_cache(monkeypatch, tmp_path):
|
| 42 |
+
monkeypatch.setattr(sp, "ENABLE_METRICS", True)
|
| 43 |
+
monkeypatch.setattr(sp.gr, "update", lambda **kwargs: {"update": kwargs})
|
| 44 |
+
monkeypatch.setattr(sp, "is_cached", lambda url: False)
|
| 45 |
+
monkeypatch.setattr(
|
| 46 |
+
sp,
|
| 47 |
+
"scrape_website",
|
| 48 |
+
lambda url: {
|
| 49 |
+
"success": True,
|
| 50 |
+
"total_pages": 1,
|
| 51 |
+
"pages": [{"title": "Home", "description": "", "sections": [], "content": "", "url": url, "page_type": "homepage"}],
|
| 52 |
+
"errors": [],
|
| 53 |
+
},
|
| 54 |
+
)
|
| 55 |
+
monkeypatch.setattr(sp, "format_scraped_content_for_context", lambda scraped_data: "content")
|
| 56 |
+
monkeypatch.setattr(
|
| 57 |
+
sp,
|
| 58 |
+
"analyze_content_gaps",
|
| 59 |
+
lambda scraped_content, url: SimpleNamespace(has_gaps=False, gaps_found=[], confidence_score=10, recommended_searches=[]),
|
| 60 |
+
)
|
| 61 |
+
monkeypatch.setattr(sp, "knowledge_to_chatbot_context", lambda knowledge: "ctx")
|
| 62 |
+
monkeypatch.setattr(sp, "extract_name_from_text", lambda text, url: "Site")
|
| 63 |
+
monkeypatch.setattr(sp, "create_knowledge_json", lambda url, scraped_data, web_search_results, raw_name: {})
|
| 64 |
+
monkeypatch.setattr(sp, "save_knowledge_json", lambda knowledge, url: tmp_path / "stub.json")
|
| 65 |
+
monkeypatch.setattr(sp, "build_status_new", lambda *args, **kwargs: "status")
|
| 66 |
+
|
| 67 |
+
result = await sp.run_full_research_new("https://example.com", progress=DummyProgress())
|
| 68 |
+
_, _, _, _, _, _, stats = result
|
| 69 |
+
|
| 70 |
+
assert stats["cache_hit"] is False
|
| 71 |
+
assert "tcr_seconds" in stats and stats["tcr_seconds"] >= 0
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def test_log_chat_answer(tmp_path):
|
| 75 |
+
log_file = tmp_path / "chat.jsonl"
|
| 76 |
+
ml.log_chat_answer(
|
| 77 |
+
question="Q?",
|
| 78 |
+
answer="A!",
|
| 79 |
+
provenance="primary_only",
|
| 80 |
+
user="user@example.com",
|
| 81 |
+
log_path=log_file,
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
data = log_file.read_text(encoding="utf-8").strip().splitlines()
|
| 85 |
+
assert len(data) == 1
|
| 86 |
+
record = json.loads(data[0])
|
| 87 |
+
assert record["question"] == "Q?"
|
| 88 |
+
assert record["answer"] == "A!"
|
| 89 |
+
assert record["provenance"] == "primary_only"
|
| 90 |
+
assert record["user"] == "user@example.com"
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def test_save_job_metrics_no_supabase(monkeypatch):
|
| 94 |
+
monkeypatch.setattr(ml, "get_supabase_client", lambda: None)
|
| 95 |
+
ml.save_job_metrics_to_supabase("https://example.com", {"cache_hit": True})
|
| 96 |
+
# Should not raise
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def test_save_chat_answer_no_supabase(monkeypatch):
|
| 100 |
+
monkeypatch.setattr(ml, "get_supabase_client", lambda: None)
|
| 101 |
+
ml.save_chat_answer_to_supabase("q", "a", system_prompt="ctx")
|
| 102 |
+
# Should not raise
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def test_save_job_metrics_payload(monkeypatch):
|
| 106 |
+
captured = {}
|
| 107 |
+
|
| 108 |
+
class Table:
|
| 109 |
+
def __init__(self, name):
|
| 110 |
+
self.name = name
|
| 111 |
+
|
| 112 |
+
def insert(self, payload):
|
| 113 |
+
captured["table"] = self.name
|
| 114 |
+
captured["payload"] = payload
|
| 115 |
+
return self
|
| 116 |
+
|
| 117 |
+
def execute(self):
|
| 118 |
+
captured["executed"] = True
|
| 119 |
+
return True
|
| 120 |
+
|
| 121 |
+
class Client:
|
| 122 |
+
def table(self, name):
|
| 123 |
+
return Table(name)
|
| 124 |
+
|
| 125 |
+
monkeypatch.setattr(ml, "get_supabase_client", lambda: Client())
|
| 126 |
+
ml.save_job_metrics_to_supabase(
|
| 127 |
+
"https://example.com",
|
| 128 |
+
{"cache_hit": True, "tcr_seconds": 1.5, "searches_run": 2, "pages_scraped": 3, "gaps_found": 1},
|
| 129 |
+
user_id="user-1",
|
| 130 |
+
)
|
| 131 |
+
assert captured["table"] == "metrics_job_runs"
|
| 132 |
+
assert captured["payload"]["url"] == "https://example.com"
|
| 133 |
+
assert captured["payload"]["cache_hit"] is True
|
| 134 |
+
assert captured["payload"]["tcr_seconds"] == 1.5
|
| 135 |
+
assert captured["payload"]["searches_run"] == 2
|
| 136 |
+
assert captured["payload"]["pages_scraped"] == 3
|
| 137 |
+
assert captured["payload"]["gaps_found"] == 1
|
| 138 |
+
assert captured["payload"]["user_id"] == "user-1"
|
| 139 |
+
assert captured["executed"] is True
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def test_save_chat_answer_payload(monkeypatch):
|
| 143 |
+
captured = {}
|
| 144 |
+
|
| 145 |
+
class Table:
|
| 146 |
+
def __init__(self, name):
|
| 147 |
+
self.name = name
|
| 148 |
+
|
| 149 |
+
def insert(self, payload):
|
| 150 |
+
captured["table"] = self.name
|
| 151 |
+
captured["payload"] = payload
|
| 152 |
+
return self
|
| 153 |
+
|
| 154 |
+
def execute(self):
|
| 155 |
+
captured["executed"] = True
|
| 156 |
+
return True
|
| 157 |
+
|
| 158 |
+
class Client:
|
| 159 |
+
def table(self, name):
|
| 160 |
+
return Table(name)
|
| 161 |
+
|
| 162 |
+
monkeypatch.setattr(ml, "get_supabase_client", lambda: Client())
|
| 163 |
+
ml.save_chat_answer_to_supabase(
|
| 164 |
+
question="How?",
|
| 165 |
+
answer="Here",
|
| 166 |
+
system_prompt="Contains SECONDARY SOURCE",
|
| 167 |
+
user_id="user-2",
|
| 168 |
+
url="https://example.com",
|
| 169 |
+
)
|
| 170 |
+
assert captured["table"] == "metrics_chat_answers"
|
| 171 |
+
assert captured["payload"]["question"] == "How?"
|
| 172 |
+
assert captured["payload"]["answer"] == "Here"
|
| 173 |
+
assert captured["payload"]["provenance"] == "primary_plus_secondary"
|
| 174 |
+
assert captured["payload"]["url"] == "https://example.com"
|
| 175 |
+
assert captured["payload"]["user_id"] == "user-2"
|
| 176 |
+
assert captured["executed"] is True
|