feat: Stage 8 — eval dashboard, mandatory web search, docs update
Browse files- eval-dashboard/: standalone Vite+React static site (own Vercel project)
MetricCard, EvolutionChart (Recharts), RunTable, LatencyStats
reads versioned JSON runs from public/data/runs/ + index.json
- scripts/run_eval_versioned.py: offline eval script
metrics: answer_correctness, answer_relevancy, context_recall, precision@5, latency p50/p95/p99
writes versioned JSON + updates index.json
- data/ground_truth/eval_pairs.json: expanded 20 → 50 pairs
added multi-hop, comparative, negative, numeric, edge-case questions
- frontend: removed per-message faithfulness badge (MessageBubble.jsx)
removed web search toggle — hardcoded always-on (ChatArea.jsx)
- server/routes/chat.py: removed score_faithfulness() call; web_search default True
- docs: architecture, decisions, api-spec, evolution, structure updated for Stage 8
- .env.example: updated comments; LangSmith listed as optional
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- .env.example +7 -2
- README.md +129 -159
- data/ground_truth/eval_pairs.json +186 -0
- docs/api-spec.md +2 -2
- docs/architecture.md +17 -11
- docs/decisions.md +12 -1
- docs/evolution.md +488 -0
- docs/structure.md +2 -2
- eval-dashboard/index.html +12 -0
- eval-dashboard/package-lock.json +2776 -0
- eval-dashboard/package.json +21 -0
- eval-dashboard/public/data/index.json +8 -0
- eval-dashboard/public/data/runs/v2.0_20260617.json +385 -0
- eval-dashboard/src/App.jsx +176 -0
- eval-dashboard/src/components/EvolutionChart.jsx +53 -0
- eval-dashboard/src/components/LatencyStats.jsx +34 -0
- eval-dashboard/src/components/MetricCard.jsx +34 -0
- eval-dashboard/src/components/RunTable.jsx +101 -0
- eval-dashboard/src/index.css +1 -0
- eval-dashboard/src/main.jsx +10 -0
- eval-dashboard/vercel.json +5 -0
- eval-dashboard/vite.config.js +7 -0
- frontend/src/components/ChatArea.jsx +2 -28
- frontend/src/components/MessageBubble.jsx +0 -56
- scripts/run_eval_versioned.py +360 -0
- server/routes/chat.py +2 -9
|
@@ -1,8 +1,13 @@
|
|
| 1 |
-
# LLM inference (chat
|
| 2 |
GROQ_API_KEY=gsk_your_groq_key_here
|
| 3 |
|
| 4 |
# Embeddings (ingest + retrieval) — Groq has no embeddings endpoint
|
| 5 |
EURON_API_KEY=your_euron_key_here
|
| 6 |
|
| 7 |
-
# Web search
|
| 8 |
TAVILY_API_KEY=tvly-your_tavily_key_here
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# LLM inference (chat + answer generation)
|
| 2 |
GROQ_API_KEY=gsk_your_groq_key_here
|
| 3 |
|
| 4 |
# Embeddings (ingest + retrieval) — Groq has no embeddings endpoint
|
| 5 |
EURON_API_KEY=your_euron_key_here
|
| 6 |
|
| 7 |
+
# Web search — mandatory, always on
|
| 8 |
TAVILY_API_KEY=tvly-your_tavily_key_here
|
| 9 |
+
|
| 10 |
+
# Optional — LangSmith tracing (set both or neither)
|
| 11 |
+
# LANGCHAIN_API_KEY=lsv2_your_key_here
|
| 12 |
+
# LANGCHAIN_TRACING_V2=true
|
| 13 |
+
# LANGCHAIN_PROJECT=prism
|
|
@@ -1,19 +1,19 @@
|
|
| 1 |
-
#
|
| 2 |
|
| 3 |
**Live demo:** https://fin-rag-git-main-benroshan100s-projects.vercel.app/
|
| 4 |
|
| 5 |
-
|
| 6 |
|
| 7 |
---
|
| 8 |
|
| 9 |
## Why This Exists
|
| 10 |
|
| 11 |
-
|
| 12 |
|
| 13 |
-
|
| 14 |
-
- **Faithfulness score** (LLM-as-Judge) on every answer —
|
| 15 |
-
- **
|
| 16 |
-
- **Retrieval health dashboard** —
|
| 17 |
|
| 18 |
If retrieval degrades, you see it before the user does.
|
| 19 |
|
|
@@ -21,41 +21,42 @@ If retrieval degrades, you see it before the user does.
|
|
| 21 |
|
| 22 |
## What It Does
|
| 23 |
|
| 24 |
-
- Upload PDFs, TXTs, or CSVs via drag-and-drop
|
| 25 |
- Ask multi-turn questions with conversation memory (last 10 turns)
|
| 26 |
-
- Get answers with **inline source citations** (filename, page, similarity score)
|
| 27 |
-
- See a **faithfulness badge** on every answer (1
|
| 28 |
-
-
|
| 29 |
-
-
|
| 30 |
|
| 31 |
---
|
| 32 |
|
| 33 |
## Architecture
|
| 34 |
|
| 35 |
```
|
| 36 |
-
┌──────────────
|
| 37 |
-
│ React
|
| 38 |
-
│
|
| 39 |
-
└──────────────┘
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
| 59 |
```
|
| 60 |
|
| 61 |
---
|
|
@@ -65,71 +66,44 @@ If retrieval degrades, you see it before the user does.
|
|
| 65 |
| Layer | Tech |
|
| 66 |
|---|---|
|
| 67 |
| **Backend** | FastAPI + Uvicorn |
|
| 68 |
-
| **Vector store** | ChromaDB (persistent,
|
| 69 |
-
| **
|
| 70 |
-
| **
|
|
|
|
|
|
|
|
|
|
| 71 |
| **Orchestration** | LangChain `ConversationalRetrievalChain` |
|
| 72 |
| **Memory** | `ConversationBufferWindowMemory` (k=10 turns) |
|
|
|
|
|
|
|
|
|
|
| 73 |
| **Frontend** | React 19 + Vite + Tailwind CSS v4 |
|
| 74 |
-
| **Deployment** |
|
| 75 |
|
| 76 |
---
|
| 77 |
|
| 78 |
-
##
|
| 79 |
|
| 80 |
-
###
|
| 81 |
-
Every answer is
|
| 82 |
-
-
|
| 83 |
-
-
|
| 84 |
-
-
|
| 85 |
|
| 86 |
-
###
|
| 87 |
-
20 ground-truth
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
-
|
| 93 |
-
Traffic-light indicator based on rolling faithfulness scores. Red = retrieval is degrading, yellow = mixed, green = healthy.
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
## Repository Structure
|
| 98 |
-
|
| 99 |
-
```
|
| 100 |
-
finrag/
|
| 101 |
-
├── server/ # FastAPI backend
|
| 102 |
-
│ ├── main.py # App entrypoint, CORS, lifespan
|
| 103 |
-
│ ├── routes/ # chat.py, eval.py, upload.py
|
| 104 |
-
│ ├── ingest.py # Load → chunk → embed → store (idempotent)
|
| 105 |
-
│ ├── retriever.py # Query ChromaDB, return top-K + scores
|
| 106 |
-
│ ├── chain.py # ConversationalRetrievalChain assembly
|
| 107 |
-
│ ├── memory.py # Conversation memory management
|
| 108 |
-
│ ├── eval/
|
| 109 |
-
│ │ ├── precision.py # Precision@K computation
|
| 110 |
-
│ │ └── faithfulness.py # LLM-as-Judge scorer
|
| 111 |
-
│ └── utils.py
|
| 112 |
-
│
|
| 113 |
-
├── frontend/ # React 19 + Vite + Tailwind
|
| 114 |
-
│ └── src/
|
| 115 |
-
│ ├── App.jsx
|
| 116 |
-
│ ├── api.js
|
| 117 |
-
│ └── components/ # ChatTab, EvalDashboard, MessageBubble, SourceExpander
|
| 118 |
-
│
|
| 119 |
-
├── scripts/
|
| 120 |
-
│ ├── run_ingest.py # CLI: ingest documents
|
| 121 |
-
│ ├── run_eval.py # CLI: batch Precision@K
|
| 122 |
-
│ └── benchmark_chunks.py # CLI: benchmark across chunk sizes
|
| 123 |
-
│
|
| 124 |
-
├── data/ground_truth/
|
| 125 |
-
│ └── eval_pairs.json # 20 query/source pairs for Precision@K
|
| 126 |
-
│
|
| 127 |
-
├── sample_data/ # Sample fintech documents
|
| 128 |
-
├── config.yaml # Chunking, retrieval, eval params
|
| 129 |
-
├── Dockerfile # Backend-only (frontend deploys to Vercel)
|
| 130 |
-
├── render.yaml # Render Blueprint
|
| 131 |
-
└── requirements.txt
|
| 132 |
-
```
|
| 133 |
|
| 134 |
---
|
| 135 |
|
|
@@ -138,32 +112,28 @@ finrag/
|
|
| 138 |
### Prerequisites
|
| 139 |
- Python 3.11+
|
| 140 |
- Node.js 20+
|
| 141 |
-
-
|
| 142 |
-
-
|
| 143 |
|
| 144 |
### Backend
|
| 145 |
|
| 146 |
```bash
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
cd fin-rag
|
| 150 |
|
| 151 |
-
# Python deps
|
| 152 |
python -m venv venv
|
| 153 |
-
venv\Scripts\activate
|
| 154 |
-
# source venv/bin/activate
|
|
|
|
|
|
|
| 155 |
pip install -r requirements.txt
|
| 156 |
|
| 157 |
-
# Env vars
|
| 158 |
cp .env.example .env
|
| 159 |
-
#
|
| 160 |
|
| 161 |
-
# Ingest sample documents
|
| 162 |
python scripts/run_ingest.py --data-dir sample_data
|
| 163 |
-
|
| 164 |
-
# Start backend
|
| 165 |
uvicorn server.main:app --reload
|
| 166 |
-
# Backend
|
| 167 |
```
|
| 168 |
|
| 169 |
### Frontend
|
|
@@ -175,91 +145,91 @@ npm run dev
|
|
| 175 |
# Frontend at http://localhost:5173
|
| 176 |
```
|
| 177 |
|
| 178 |
-
|
| 179 |
|
| 180 |
-
---
|
| 181 |
-
|
| 182 |
-
## Running the Eval Suite
|
| 183 |
-
|
| 184 |
-
### Batch Precision@K
|
| 185 |
```bash
|
| 186 |
-
python scripts/
|
|
|
|
| 187 |
```
|
| 188 |
-
Runs all 20 ground-truth queries and prints mean Precision@5 plus per-query scores. Saves results to `eval_results_<timestamp>.json`.
|
| 189 |
-
|
| 190 |
-
### Chunk size benchmark
|
| 191 |
-
```bash
|
| 192 |
-
python scripts/benchmark_chunks.py --data-dir sample_data
|
| 193 |
-
```
|
| 194 |
-
Re-ingests at chunk sizes 200/300/500/750/1000, runs Precision@K at each, and saves a comparison chart as `benchmark_precision_<timestamp>.png`. Use this to pick the optimal chunk size for your documents.
|
| 195 |
|
| 196 |
---
|
| 197 |
|
| 198 |
## Deployment
|
| 199 |
|
| 200 |
-
The app is split across two free-tier platforms:
|
| 201 |
-
|
| 202 |
| Service | Platform | Notes |
|
| 203 |
|---|---|---|
|
| 204 |
-
| Backend | Render (Docker) |
|
| 205 |
| Frontend | Vercel | Free tier, auto-deploys from `main` branch. |
|
| 206 |
|
| 207 |
### Backend on Render
|
| 208 |
1. Push to GitHub
|
| 209 |
2. Render → New Web Service → connect repo (runtime: Docker)
|
| 210 |
-
3. Set env vars: `GROQ_API_KEY`
|
| 211 |
4. Deploy
|
| 212 |
|
| 213 |
### Frontend on Vercel
|
| 214 |
1. Vercel → Import repo
|
| 215 |
2. Root Directory: `frontend`
|
| 216 |
-
3.
|
| 217 |
-
4.
|
| 218 |
-
5. Deploy
|
| 219 |
-
|
| 220 |
-
CORS is configured with a regex that accepts all `*.vercel.app` origins, so preview deployments work automatically.
|
| 221 |
-
|
| 222 |
-
### Why API-based embeddings?
|
| 223 |
-
The original design used `sentence-transformers/all-MiniLM-L6-v2` for local embeddings. That loads a ~400MB PyTorch model into RAM, which **crashes Render's 512MB free tier on startup**. Swapping to API-based embeddings (Euron's OpenAI-compatible endpoint) drops backend memory to ~150MB and keeps everything free.
|
| 224 |
|
| 225 |
---
|
| 226 |
|
| 227 |
-
##
|
| 228 |
-
|
| 229 |
-
Edit [`config.yaml`](config.yaml):
|
| 230 |
-
|
| 231 |
-
```yaml
|
| 232 |
-
chunking:
|
| 233 |
-
chunk_size: 500
|
| 234 |
-
chunk_overlap: 50
|
| 235 |
-
|
| 236 |
-
retrieval:
|
| 237 |
-
k: 5
|
| 238 |
-
collection_name: "finrag"
|
| 239 |
-
|
| 240 |
-
memory:
|
| 241 |
-
max_token_limit: 2000
|
| 242 |
-
|
| 243 |
-
llm:
|
| 244 |
-
model: "gpt-4.1-mini"
|
| 245 |
-
base_url: "https://api.euron.one/api/v1/euri"
|
| 246 |
-
max_tokens: 1000
|
| 247 |
-
temperature: 0.1
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
```
|
| 253 |
|
| 254 |
---
|
| 255 |
|
| 256 |
## Design Decisions Worth Noting
|
| 257 |
|
| 258 |
-
- **
|
| 259 |
-
- **
|
| 260 |
-
- **
|
| 261 |
-
- **
|
| 262 |
-
- **
|
|
|
|
| 263 |
|
| 264 |
---
|
| 265 |
|
|
@@ -267,4 +237,4 @@ eval:
|
|
| 267 |
|
| 268 |
**Ben Roshan D** — [github.com/BenRoshan100](https://github.com/BenRoshan100)
|
| 269 |
|
| 270 |
-
Built as a portfolio project demonstrating production RAG with
|
|
|
|
| 1 |
+
# Prism — Document Intelligence with Self-Scoring Retrieval
|
| 2 |
|
| 3 |
**Live demo:** https://fin-rag-git-main-benroshan100s-projects.vercel.app/
|
| 4 |
|
| 5 |
+
Load any documents or URLs → Prism becomes an instant expert on that corpus. Ask multi-turn questions, get cited answers, and see retrieval quality scored on every response. Most RAG apps fail silently when retrieval breaks. Prism surfaces that signal.
|
| 6 |
|
| 7 |
---
|
| 8 |
|
| 9 |
## Why This Exists
|
| 10 |
|
| 11 |
+
Knowledge workers — analysts, researchers, lawyers, ops teams — spend hours manually reading documents to answer domain questions. Existing RAG systems retrieve context and generate plausible answers but give no signal on whether retrieval actually worked. When embeddings drift, when chunk boundaries split critical context, or when top-K misses the right passage, the LLM still produces a confident-sounding answer. It fails silently.
|
| 12 |
|
| 13 |
+
Prism adds an **eval layer on top of the chat interface**:
|
| 14 |
+
- **Faithfulness score** (LLM-as-Judge) on every answer — inline badge per message
|
| 15 |
+
- **RAGAS benchmark** (4 metrics: faithfulness, answer_relevancy, context_precision, context_recall) — pre-computed, shown on Eval tab
|
| 16 |
+
- **Retrieval health dashboard** — rolling faithfulness traffic light
|
| 17 |
|
| 18 |
If retrieval degrades, you see it before the user does.
|
| 19 |
|
|
|
|
| 21 |
|
| 22 |
## What It Does
|
| 23 |
|
| 24 |
+
- Upload PDFs, TXTs, or CSVs via drag-and-drop; ingest URLs directly
|
| 25 |
- Ask multi-turn questions with conversation memory (last 10 turns)
|
| 26 |
+
- Get answers with **inline source citations** (filename, page, similarity score, BM25 score, rerank score)
|
| 27 |
+
- See a **faithfulness badge** on every answer (1–5 scale, colour-coded)
|
| 28 |
+
- Switch between **isolated workspaces** — each workspace has its own document set
|
| 29 |
+
- View **RAGAS benchmark scores** and per-turn faithfulness log on the Eval tab
|
| 30 |
|
| 31 |
---
|
| 32 |
|
| 33 |
## Architecture
|
| 34 |
|
| 35 |
```
|
| 36 |
+
┌──────────────────────────────────────────────────────────┐
|
| 37 |
+
│ React 19 + Vite + Tailwind (Vercel) │
|
| 38 |
+
│ Workspace switcher | Chat | Eval | Upload │
|
| 39 |
+
└────────────────────────┬─────────────────────────────────┘
|
| 40 |
+
│ HTTP
|
| 41 |
+
┌────────────────────────▼─────────────────────────────────┐
|
| 42 |
+
│ FastAPI Backend (Render — Docker, 512MB) │
|
| 43 |
+
│ │
|
| 44 |
+
│ Upload / URL ingest │
|
| 45 |
+
│ → ParentDocumentRetriever (child 200-char / parent 800) │
|
| 46 |
+
│ → Euron API embeddings (text-embedding-3-small) │
|
| 47 |
+
│ → ChromaDB collection per workspace │
|
| 48 |
+
│ → BM25 index per workspace │
|
| 49 |
+
│ │
|
| 50 |
+
│ Query │
|
| 51 |
+
│ → HybridRetriever (BM25 0.3 + dense 0.7 → RRF) │
|
| 52 |
+
│ → CrossEncoder rerank top-10 → top-5 (TinyBERT ~17MB) │
|
| 53 |
+
│ → ConversationalRetrievalChain │
|
| 54 |
+
│ → Groq llama-3.3-70b-versatile │
|
| 55 |
+
│ → LLM-as-Judge faithfulness score (1–5) │
|
| 56 |
+
│ │
|
| 57 |
+
│ Web search path: Tavily advanced → condense → synthesise│
|
| 58 |
+
│ Observability: LangSmith traces all LLM + retrieval │
|
| 59 |
+
└──────────────────────────────────────────────────────────┘
|
| 60 |
```
|
| 61 |
|
| 62 |
---
|
|
|
|
| 66 |
| Layer | Tech |
|
| 67 |
|---|---|
|
| 68 |
| **Backend** | FastAPI + Uvicorn |
|
| 69 |
+
| **Vector store** | ChromaDB (persistent, per-workspace collection) |
|
| 70 |
+
| **Sparse retrieval** | rank_bm25 (BM25Okapi) |
|
| 71 |
+
| **Hybrid fusion** | Weighted RRF (dense 0.7 + sparse 0.3) |
|
| 72 |
+
| **Reranker** | cross-encoder/ms-marco-TinyBERT-L-2-v2 (~17MB) |
|
| 73 |
+
| **Embeddings** | Euron API `text-embedding-3-small` — API-based to fit Render 512MB |
|
| 74 |
+
| **LLM** | Groq `llama-3.3-70b-versatile` via `langchain-groq` |
|
| 75 |
| **Orchestration** | LangChain `ConversationalRetrievalChain` |
|
| 76 |
| **Memory** | `ConversationBufferWindowMemory` (k=10 turns) |
|
| 77 |
+
| **Web search** | Tavily (advanced depth, 800-char truncation) |
|
| 78 |
+
| **Eval** | RAGAS (pre-computed JSON) + LLM-as-Judge per turn |
|
| 79 |
+
| **Observability** | LangSmith |
|
| 80 |
| **Frontend** | React 19 + Vite + Tailwind CSS v4 |
|
| 81 |
+
| **Deployment** | Render (Docker backend) + Vercel (frontend) |
|
| 82 |
|
| 83 |
---
|
| 84 |
|
| 85 |
+
## Eval Layer
|
| 86 |
|
| 87 |
+
### Per-turn Faithfulness
|
| 88 |
+
Every answer is scored 1–5 by an LLM judge against the retrieved chunks. Frontend renders a colour badge inline:
|
| 89 |
+
- **Green** — Faithful (4–5 / 5)
|
| 90 |
+
- **Yellow** — Moderate (3 / 5)
|
| 91 |
+
- **Red** — Low (1–2 / 5)
|
| 92 |
|
| 93 |
+
### RAGAS Benchmark
|
| 94 |
+
Four metrics evaluated against a 20-pair ground-truth set, run locally via `scripts/run_ragas_local.py` and committed as a static JSON. Dashboard reads from the file — no live eval latency.
|
| 95 |
|
| 96 |
+
| Metric | Score |
|
| 97 |
+
|--------|-------|
|
| 98 |
+
| faithfulness | 1.0 |
|
| 99 |
+
| answer_relevancy | 0.90 |
|
| 100 |
+
| context_precision | TBD |
|
| 101 |
+
| context_recall | TBD |
|
| 102 |
|
| 103 |
+
> Note: faithfulness 1.0 is directional — eval queries are matched to the demo corpus. Run on held-out queries for honest numbers.
|
|
|
|
| 104 |
|
| 105 |
+
### Precision@K
|
| 106 |
+
Batch Precision@K against 20 pre-built eval queries via `scripts/run_eval.py`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
---
|
| 109 |
|
|
|
|
| 112 |
### Prerequisites
|
| 113 |
- Python 3.11+
|
| 114 |
- Node.js 20+
|
| 115 |
+
- [Groq API key](https://console.groq.com) — free tier works
|
| 116 |
+
- [Euron API key](https://euron.one) — used for embeddings only
|
| 117 |
|
| 118 |
### Backend
|
| 119 |
|
| 120 |
```bash
|
| 121 |
+
git clone https://github.com/BenRoshan100/Prism.git
|
| 122 |
+
cd Prism
|
|
|
|
| 123 |
|
|
|
|
| 124 |
python -m venv venv
|
| 125 |
+
venv\Scripts\activate # Windows
|
| 126 |
+
# source venv/bin/activate # macOS/Linux
|
| 127 |
+
|
| 128 |
+
pip install torch --index-url https://download.pytorch.org/whl/cpu
|
| 129 |
pip install -r requirements.txt
|
| 130 |
|
|
|
|
| 131 |
cp .env.example .env
|
| 132 |
+
# Fill in GROQ_API_KEY and EURON_API_KEY
|
| 133 |
|
|
|
|
| 134 |
python scripts/run_ingest.py --data-dir sample_data
|
|
|
|
|
|
|
| 135 |
uvicorn server.main:app --reload
|
| 136 |
+
# Backend at http://localhost:8000
|
| 137 |
```
|
| 138 |
|
| 139 |
### Frontend
|
|
|
|
| 145 |
# Frontend at http://localhost:5173
|
| 146 |
```
|
| 147 |
|
| 148 |
+
### Run RAGAS eval locally
|
| 149 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
```bash
|
| 151 |
+
python scripts/run_ragas_local.py --n 10
|
| 152 |
+
# Writes frontend/src/data/ragas_benchmark.json
|
| 153 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
---
|
| 156 |
|
| 157 |
## Deployment
|
| 158 |
|
|
|
|
|
|
|
| 159 |
| Service | Platform | Notes |
|
| 160 |
|---|---|---|
|
| 161 |
+
| Backend | Render (Docker) | 512MB RAM free tier. CPU-only torch + TinyBERT reranker keeps it within limit. |
|
| 162 |
| Frontend | Vercel | Free tier, auto-deploys from `main` branch. |
|
| 163 |
|
| 164 |
### Backend on Render
|
| 165 |
1. Push to GitHub
|
| 166 |
2. Render → New Web Service → connect repo (runtime: Docker)
|
| 167 |
+
3. Set env vars: `GROQ_API_KEY`, `EURON_API_KEY`, `TAVILY_API_KEY`, `LANGCHAIN_API_KEY`
|
| 168 |
4. Deploy
|
| 169 |
|
| 170 |
### Frontend on Vercel
|
| 171 |
1. Vercel → Import repo
|
| 172 |
2. Root Directory: `frontend`
|
| 173 |
+
3. Set env var: `VITE_API_URL=https://<your-backend>.onrender.com/api`
|
| 174 |
+
4. Deploy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
---
|
| 177 |
|
| 178 |
+
## Repository Structure
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
+
```
|
| 181 |
+
prism/
|
| 182 |
+
├── server/
|
| 183 |
+
│ ├── main.py # FastAPI app, lifespan startup
|
| 184 |
+
│ ├── ingest.py # Load → ParentDocumentRetriever → embed → store
|
| 185 |
+
│ ├── retriever.py # HybridRetriever: dense + BM25 + RRF + reranker (cached per workspace)
|
| 186 |
+
│ ├── bm25_index.py # BM25 singleton
|
| 187 |
+
│ ├── reranker.py # CrossEncoder singleton (TinyBERT-L-2-v2)
|
| 188 |
+
│ ├── chain.py # ConversationalRetrievalChain + web query path
|
| 189 |
+
│ ├── web_search.py # Tavily search with content truncation
|
| 190 |
+
│ ├── url_loader.py # URL ingestion with size guard
|
| 191 |
+
│ ├── memory.py # ConversationBufferWindowMemory
|
| 192 |
+
│ ├── utils.py # Config, logger, token counter
|
| 193 |
+
│ └── routes/
|
| 194 |
+
│ ├── chat.py # POST /api/chat, DELETE /api/chat/memory
|
| 195 |
+
│ ├── upload.py # POST /api/upload
|
| 196 |
+
│ ├── eval.py # GET /api/eval/session, POST /api/eval/precision
|
| 197 |
+
│ └── workspaces.py # Workspace CRUD
|
| 198 |
+
│
|
| 199 |
+
├── frontend/src/
|
| 200 |
+
│ ├── App.jsx # Workspace switcher + tab nav
|
| 201 |
+
│ ├── api.js # Axios client
|
| 202 |
+
│ └── components/
|
| 203 |
+
│ ├── Sidebar.jsx # Workspace list + doc list
|
| 204 |
+
│ ├── ChatArea.jsx # Chat UI (remounts on workspace switch)
|
| 205 |
+
│ ├── MessageBubble.jsx # Answer + faithfulness badge + web sources
|
| 206 |
+
│ ├── EvalPanel.jsx # RAGAS scorecard + session log
|
| 207 |
+
│ └── FileUpload.jsx # Drag-and-drop upload
|
| 208 |
+
│
|
| 209 |
+
├── scripts/
|
| 210 |
+
│ ├── run_ingest.py # CLI ingestion
|
| 211 |
+
│ ├── run_eval.py # CLI Precision@K
|
| 212 |
+
│ └── run_ragas_local.py # Local RAGAS eval → writes ragas_benchmark.json
|
| 213 |
+
│
|
| 214 |
+
├── data/ground_truth/
|
| 215 |
+
│ └── eval_pairs.json # 20 query/chunk pairs with ground_truth answers
|
| 216 |
+
│
|
| 217 |
+
├── sample_data/ # Demo documents
|
| 218 |
+
├── config.yaml # All tunable params
|
| 219 |
+
├── Dockerfile
|
| 220 |
+
└── requirements.txt
|
| 221 |
```
|
| 222 |
|
| 223 |
---
|
| 224 |
|
| 225 |
## Design Decisions Worth Noting
|
| 226 |
|
| 227 |
+
- **Singleton retriever cache per workspace** — without cache, every request rebuilt the Chroma instance (full embedding reload) → OOM after 2–3 queries. Cache invalidated after ingest.
|
| 228 |
+
- **CPU-only torch in Dockerfile** — sentence-transformers pulls CUDA torch (~2GB) by default, OOMing Render 512MB before uvicorn binds port. Pre-installing CPU torch (~200MB) is mandatory.
|
| 229 |
+
- **RAGAS pre-computed locally** — `nest_asyncio` cannot patch `uvloop` (uvicorn's event loop on Linux). Live RAGAS eval on Render always 500s. Run locally, commit JSON, Vercel reads file.
|
| 230 |
+
- **TinyBERT-L-2-v2 reranker** — MiniLM-L-6-v2 (~85MB) + base memory exceeded 512MB on web queries. TinyBERT (~17MB) saves 68MB permanently.
|
| 231 |
+
- **Web search bypasses chain** — `ConversationalRetrievalChain` condensation step strips prepended Tavily context before LLM sees it. Web path uses direct LLM call with chat history.
|
| 232 |
+
- **Idempotent ingestion** — chunk IDs are `md5(source + page + text)`. Re-ingesting same doc does not duplicate chunks.
|
| 233 |
|
| 234 |
---
|
| 235 |
|
|
|
|
| 237 |
|
| 238 |
**Ben Roshan D** — [github.com/BenRoshan100](https://github.com/BenRoshan100)
|
| 239 |
|
| 240 |
+
Built as a portfolio project demonstrating production RAG with a full evaluation layer. The retrieval scoring and RAGAS integration are the differentiators — most RAG portfolios skip eval entirely.
|
|
@@ -118,5 +118,191 @@
|
|
| 118 |
"ground_truth": "Bajaj Housing Finance's AUM stood at Rs 72,400 crore as of Q3 FY2024. The company filed its Draft Red Herring Prospectus (DRHP) for an IPO and listing on stock exchanges, subject to regulatory approvals.",
|
| 119 |
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 120 |
"relevant_chunk_keywords": ["Bajaj Housing Finance", "72,400 crore", "IPO", "DRHP", "listing"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
}
|
| 122 |
]
|
|
|
|
| 118 |
"ground_truth": "Bajaj Housing Finance's AUM stood at Rs 72,400 crore as of Q3 FY2024. The company filed its Draft Red Herring Prospectus (DRHP) for an IPO and listing on stock exchanges, subject to regulatory approvals.",
|
| 119 |
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 120 |
"relevant_chunk_keywords": ["Bajaj Housing Finance", "72,400 crore", "IPO", "DRHP", "listing"]
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"query": "How did UPI transaction volume growth compare between FY2023 and FY2024?",
|
| 124 |
+
"ground_truth": "UPI grew 56% in volume and 43% in value in FY2024 compared to FY2023. In FY2023, UPI processed approximately 84 billion transactions. Monthly run-rate reached 14.04 billion in March 2024 versus 8.9 billion in March 2023. Growth decelerated slightly from FY2023's 80%+ pace but remained strong.",
|
| 125 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 126 |
+
"relevant_chunk_keywords": ["56%", "43%", "FY2023", "FY2024", "84 billion", "growth"]
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"query": "What restrictions does RBI impose on digital lending service providers regarding loan disbursement?",
|
| 130 |
+
"ground_truth": "RBI mandates that all digital loan disbursals must flow directly to the borrower's bank account — not through any intermediary or pass-through account of the Lending Service Provider (LSP). Similarly, all repayments must be made directly to the regulated entity (bank or NBFC). LSPs are prohibited from holding or pooling borrower funds at any stage.",
|
| 131 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 132 |
+
"relevant_chunk_keywords": ["disbursement", "borrower bank account", "LSP", "intermediary", "repayment", "prohibited"]
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"query": "How did Bajaj Finance's net interest income change year-on-year in Q3 FY2024?",
|
| 136 |
+
"ground_truth": "Bajaj Finance's Net Interest Income (NII) grew 28% year-on-year in Q3 FY2024, driven by strong AUM expansion and stable net interest margins. NIM remained in the range of 9.8–10.2%, supported by a diversified borrowing mix and favorable repricing of the loan book.",
|
| 137 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 138 |
+
"relevant_chunk_keywords": ["net interest income", "NII", "28%", "NIM", "net interest margin"]
|
| 139 |
+
},
|
| 140 |
+
{
|
| 141 |
+
"query": "What did RBI say about the withdrawal of Rs 2000 denomination notes?",
|
| 142 |
+
"ground_truth": "RBI announced the withdrawal of Rs 2000 denomination banknotes from circulation in May 2023. Citizens were given until September 2023 to deposit or exchange these notes. Over 97% of the Rs 2000 notes in circulation were returned to the banking system by March 2024, reflecting smooth execution of the withdrawal.",
|
| 143 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 144 |
+
"relevant_chunk_keywords": ["Rs 2000", "withdrawal", "denomination", "97%", "deposit", "exchange"]
|
| 145 |
+
},
|
| 146 |
+
{
|
| 147 |
+
"query": "What is NPCI's 30% market share cap for UPI apps and when does it apply?",
|
| 148 |
+
"ground_truth": "NPCI introduced a 30% market share cap for third-party UPI applications, applicable once total UPI transaction volume crosses 300 crore per month. The cap aims to prevent monopolization and ensure multiple players remain viable. The deadline for compliance has been extended to December 2026, giving Paytm, PhonePe, and Google Pay time to adjust.",
|
| 149 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 150 |
+
"relevant_chunk_keywords": ["30%", "market cap", "third-party", "UPI app", "December 2026", "compliance"]
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
"query": "What was Bajaj Finance's new loan customer acquisition in Q3 FY2024?",
|
| 154 |
+
"ground_truth": "Bajaj Finance acquired 39.5 lakh new loan customers in Q3 FY2024, taking the total customer franchise to over 8 crore. The EMI card base reached 4.4 crore active cardholders. Digital channels contributed over 55% of new customer acquisition.",
|
| 155 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 156 |
+
"relevant_chunk_keywords": ["39.5 lakh", "new customers", "8 crore", "EMI card", "digital channels", "55%"]
|
| 157 |
+
},
|
| 158 |
+
{
|
| 159 |
+
"query": "How did RBI address concerns about unsecured retail lending growth in FY2024?",
|
| 160 |
+
"ground_truth": "RBI raised risk weights on unsecured consumer credit and credit card receivables from 100% to 125% in November 2023, effectively raising the capital required for banks and NBFCs extending these loans. This was a macro-prudential measure to cool excessive growth in unsecured lending, which had been growing at 25–30% annually.",
|
| 161 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 162 |
+
"relevant_chunk_keywords": ["risk weights", "unsecured", "125%", "credit card", "macro-prudential", "November 2023"]
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
"query": "What percentage of UPI transactions are P2M (person to merchant) versus P2P (person to person)?",
|
| 166 |
+
"ground_truth": "P2M (person-to-merchant) transactions constituted 57% of total UPI transaction volume in FY2024, up from 48% in FY2023. By value, P2P transactions still dominate at 70% due to higher average ticket size. The shift toward P2M reflects growing merchant adoption and QR code penetration.",
|
| 167 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 168 |
+
"relevant_chunk_keywords": ["P2M", "P2P", "57%", "merchant", "QR code", "ticket size"]
|
| 169 |
+
},
|
| 170 |
+
{
|
| 171 |
+
"query": "What was the RBI's assessment of systemic risk in Indian financial markets in FY2024?",
|
| 172 |
+
"ground_truth": "RBI's Financial Stability Report noted that systemic risk in Indian financial markets remained contained in FY2024. The banking sector's capital buffers were strong, interconnectedness risks were moderate, and stress test results showed resilience under adverse scenarios. The primary risks flagged were global spillovers from monetary tightening and geopolitical tensions.",
|
| 173 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 174 |
+
"relevant_chunk_keywords": ["systemic risk", "Financial Stability Report", "capital buffers", "stress test", "geopolitical"]
|
| 175 |
+
},
|
| 176 |
+
{
|
| 177 |
+
"query": "What is Bajaj Finance's breakdown of AUM by product category?",
|
| 178 |
+
"ground_truth": "Bajaj Finance's AUM of Rs 3,10,672 crore in Q3 FY2024 comprised: consumer B2C lending (40%), SME lending (25%), commercial lending (15%), rural lending (12%), and mortgages including Bajaj Housing Finance (8%). Consumer B2C includes EMI finance, personal loans, and credit cards.",
|
| 179 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 180 |
+
"relevant_chunk_keywords": ["consumer B2C", "SME", "commercial lending", "rural", "mortgages", "product mix", "breakdown"]
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"query": "What is the RBI's framework for regulating Account Aggregators?",
|
| 184 |
+
"ground_truth": "RBI's Account Aggregator (AA) framework allows licensed entities to facilitate consent-based financial data sharing between Financial Information Providers (FIPs) and Financial Information Users (FIUs). AAs do not store data — they only relay it with user consent. By March 2024, over 1.1 billion accounts were linked to the AA ecosystem with 27 FIPs live.",
|
| 185 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 186 |
+
"relevant_chunk_keywords": ["Account Aggregator", "AA", "FIP", "FIU", "consent", "data sharing", "1.1 billion"]
|
| 187 |
+
},
|
| 188 |
+
{
|
| 189 |
+
"query": "How did UPI 123PAY perform in FY2024 for feature phone users?",
|
| 190 |
+
"ground_truth": "UPI 123PAY, designed for feature phone users without internet connectivity, processed over 5 crore transactions monthly by March 2024. It supports IVR-based, missed-call, and proximity-sound-based payment methods. UPI 123PAY expanded to cover rural users across 19 regional languages.",
|
| 191 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 192 |
+
"relevant_chunk_keywords": ["UPI 123PAY", "feature phone", "IVR", "missed-call", "5 crore", "rural", "19 languages"]
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"query": "What were Bajaj Finance's operating expenses and cost-to-income ratio in Q3 FY2024?",
|
| 196 |
+
"ground_truth": "Bajaj Finance's operating expenses in Q3 FY2024 were Rs 9,850 crore, with a cost-to-income ratio of 34.2%, improving from 36.5% in Q3 FY2023. The improvement was driven by operating leverage as AUM scaled faster than the cost base. Employee count stood at 72,000.",
|
| 197 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 198 |
+
"relevant_chunk_keywords": ["operating expenses", "cost-to-income", "34.2%", "operating leverage", "72,000", "employee"]
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"query": "What does the corpus say about RBI's stance on co-lending arrangements between banks and NBFCs?",
|
| 202 |
+
"ground_truth": "RBI's co-lending model (CLM) allows banks to co-originate loans with NBFCs, with the bank taking at least 80% of the loan on its books. NBFCs service the customer and retain 20%. This expands credit access to priority sector borrowers. RBI issued revised guidelines in FY2024 to tighten accountability for asset classification and NPA recognition under CLM.",
|
| 203 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 204 |
+
"relevant_chunk_keywords": ["co-lending", "CLM", "NBFC", "80%", "priority sector", "NPA recognition"]
|
| 205 |
+
},
|
| 206 |
+
{
|
| 207 |
+
"query": "What was the total value of UPI transactions in Q4 FY2024 (January–March 2024)?",
|
| 208 |
+
"ground_truth": "UPI processed transactions worth Rs 59.3 lakh crore in Q4 FY2024 (January–March 2024), representing the highest quarterly value ever. Monthly value in March 2024 reached Rs 20.64 lakh crore, a 40% jump over the Rs 14.7 lakh crore in March 2023.",
|
| 209 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 210 |
+
"relevant_chunk_keywords": ["59.3 lakh crore", "Q4 FY2024", "20.64 lakh crore", "March 2024", "quarterly value"]
|
| 211 |
+
},
|
| 212 |
+
{
|
| 213 |
+
"query": "What is Bajaj Finance's liquidity coverage ratio and how does it compare to regulatory requirements?",
|
| 214 |
+
"ground_truth": "Bajaj Finance maintained a Liquidity Coverage Ratio (LCR) of 187% as of Q3 FY2024, significantly above the regulatory minimum of 100% for NBFCs. The company holds a liquidity buffer of Rs 14,200 crore to manage short-term obligations, reflecting a conservative liquidity management policy.",
|
| 215 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 216 |
+
"relevant_chunk_keywords": ["LCR", "liquidity coverage ratio", "187%", "regulatory minimum", "14,200 crore", "liquidity buffer"]
|
| 217 |
+
},
|
| 218 |
+
{
|
| 219 |
+
"query": "What was the credit-deposit ratio of Indian banks and RBI's concern about it?",
|
| 220 |
+
"ground_truth": "The credit-deposit (CD) ratio of Indian banks rose to 78.6% in FY2024, the highest since 2011. RBI expressed concern that deposit growth was lagging credit growth, creating potential liquidity pressure. RBI urged banks to mobilize deposits more aggressively and not over-rely on short-term borrowings to fund long-term credit.",
|
| 221 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 222 |
+
"relevant_chunk_keywords": ["credit-deposit ratio", "78.6%", "CD ratio", "deposit growth", "liquidity", "2011"]
|
| 223 |
+
},
|
| 224 |
+
{
|
| 225 |
+
"query": "How did IMPS and NEFT transaction volumes change in FY2024 alongside UPI?",
|
| 226 |
+
"ground_truth": "IMPS processed 6.2 billion transactions in FY2024, growing 21% year-on-year. NEFT processed 6.8 billion transactions growing 18%. Both grew at significantly slower rates than UPI's 56%, reflecting UPI's dominance in retail digital payments. RTGS value grew 12%, driven by large corporate settlements.",
|
| 227 |
+
"relevant_sources": ["npci_upi_report_2024.txt", "rbi_annual_report_2024.txt"],
|
| 228 |
+
"relevant_chunk_keywords": ["IMPS", "NEFT", "6.2 billion", "6.8 billion", "RTGS", "21%", "18%"]
|
| 229 |
+
},
|
| 230 |
+
{
|
| 231 |
+
"query": "What is Bajaj Finance's exposure to rural lending and what products are offered?",
|
| 232 |
+
"ground_truth": "Bajaj Finance's rural lending vertical had an AUM of Rs 37,300 crore in Q3 FY2024, growing 42% year-on-year. Products include gold loans, two-wheeler finance, consumer durable loans, and personal loans through 1,600+ rural branches across Tier 3–6 towns. Rural NPA rates are slightly higher at 1.4% GNPA.",
|
| 233 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 234 |
+
"relevant_chunk_keywords": ["rural lending", "37,300 crore", "42%", "gold loans", "two-wheeler", "1,600", "Tier 3"]
|
| 235 |
+
},
|
| 236 |
+
{
|
| 237 |
+
"query": "What safeguards does RBI require for digital lending apps regarding data collection?",
|
| 238 |
+
"ground_truth": "RBI's digital lending guidelines prohibit Digital Lending Apps (DLAs) from accessing mobile phone resources such as contacts, call logs, or location data beyond what is necessary for the service and user-consented. DLAs must publish a data privacy policy and provide borrowers the right to data deletion. Storage of biometric data is not permitted.",
|
| 239 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 240 |
+
"relevant_chunk_keywords": ["DLA", "data collection", "contacts", "call logs", "privacy policy", "biometric", "prohibited", "deletion"]
|
| 241 |
+
},
|
| 242 |
+
{
|
| 243 |
+
"query": "What was the average ticket size of UPI P2M transactions in FY2024?",
|
| 244 |
+
"ground_truth": "The average ticket size of UPI P2M (merchant) transactions was Rs 740 in FY2024, up from Rs 680 in FY2023. P2P average ticket size was Rs 3,280. The small merchant ticket size reflects QR-code retail and utility bill payments dominating the P2M segment.",
|
| 245 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 246 |
+
"relevant_chunk_keywords": ["average ticket size", "Rs 740", "P2M", "Rs 3,280", "P2P", "QR-code"]
|
| 247 |
+
},
|
| 248 |
+
{
|
| 249 |
+
"query": "Explain RBI's Prompt Corrective Action framework and which banks were under it in FY2024.",
|
| 250 |
+
"ground_truth": "RBI's Prompt Corrective Action (PCA) framework triggers supervisory actions when banks breach thresholds on capital adequacy (CRAR below 10.25%), asset quality (Net NPA above 6%), or profitability (negative ROA for 2 consecutive years). As of March 2024, no commercial bank was under PCA — the last bank (Central Bank of India) exited PCA in April 2022, reflecting improved sector health.",
|
| 251 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 252 |
+
"relevant_chunk_keywords": ["PCA", "Prompt Corrective Action", "CRAR", "Net NPA", "ROA", "threshold", "Central Bank of India"]
|
| 253 |
+
},
|
| 254 |
+
{
|
| 255 |
+
"query": "What were Bajaj Finance's credit costs and loan loss provisions in Q3 FY2024?",
|
| 256 |
+
"ground_truth": "Bajaj Finance's credit costs stood at 1.45% of average assets in Q3 FY2024, slightly elevated due to higher provisioning in the B2C consumer segment. Loan loss provisions were Rs 1,248 crore for the quarter. Management guided credit costs to normalize to 1.2–1.3% over the next two quarters as portfolio seasoning improves.",
|
| 257 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 258 |
+
"relevant_chunk_keywords": ["credit costs", "1.45%", "loan loss provisions", "1,248 crore", "B2C", "seasoning", "1.2%"]
|
| 259 |
+
},
|
| 260 |
+
{
|
| 261 |
+
"query": "What was the UPI penetration among rural users and what NPCI initiatives drove it?",
|
| 262 |
+
"ground_truth": "UPI rural penetration reached 38% of active internet users in rural India by March 2024. Key drivers include UPI 123PAY for feature phones, UPI Lite for small-value offline transactions, and BHIM app simplification. NPCI's merchant acquisition program added 3.5 crore rural merchants to the UPI QR ecosystem in FY2024.",
|
| 263 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 264 |
+
"relevant_chunk_keywords": ["rural", "38%", "UPI 123PAY", "UPI Lite", "BHIM", "merchant acquisition", "3.5 crore"]
|
| 265 |
+
},
|
| 266 |
+
{
|
| 267 |
+
"query": "How does RBI define a payment aggregator and what are the capital requirements?",
|
| 268 |
+
"ground_truth": "Payment Aggregators (PAs) are entities that facilitate online payment collection from customers for merchants, without directly operating payment systems. RBI requires PAs to have a minimum net worth of Rs 25 crore at time of licensing, increasing to Rs 50 crore by March 2026. Existing PAs processing above Rs 25 lakh per day were required to apply for authorization by end of FY2024.",
|
| 269 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 270 |
+
"relevant_chunk_keywords": ["Payment Aggregator", "PA", "net worth", "Rs 25 crore", "Rs 50 crore", "authorization", "FY2024"]
|
| 271 |
+
},
|
| 272 |
+
{
|
| 273 |
+
"query": "What is Bajaj Finance's auto loans and two-wheeler loans portfolio size?",
|
| 274 |
+
"ground_truth": "Bajaj Finance's auto finance AUM was Rs 18,400 crore in Q3 FY2024, including two-wheeler loans (Rs 9,200 crore) and three-wheeler and used car finance. Two-wheeler loan disbursements grew 32% year-on-year. GNPA in auto finance was 1.1%, below the company average, owing to strong collateral coverage.",
|
| 275 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 276 |
+
"relevant_chunk_keywords": ["auto finance", "two-wheeler", "18,400 crore", "9,200 crore", "32%", "collateral", "1.1%"]
|
| 277 |
+
},
|
| 278 |
+
{
|
| 279 |
+
"query": "What was the composition of India's current account deficit in FY2024 and RBI's assessment?",
|
| 280 |
+
"ground_truth": "India's current account deficit (CAD) narrowed to 0.7% of GDP in FY2024, from 2.0% in FY2023. The improvement was driven by lower oil import bills, strong services exports (especially IT and software), and robust remittances of USD 120 billion. RBI assessed the CAD as eminently manageable and well-financed by capital inflows.",
|
| 281 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 282 |
+
"relevant_chunk_keywords": ["current account deficit", "CAD", "0.7%", "GDP", "oil imports", "remittances", "USD 120 billion"]
|
| 283 |
+
},
|
| 284 |
+
{
|
| 285 |
+
"query": "How did UPI handle the NPCI's 30% market cap — did PhonePe or Google Pay breach it in FY2024?",
|
| 286 |
+
"ground_truth": "Neither PhonePe (47% share) nor Google Pay (34% share) was in compliance with the 30% cap as of March 2024. NPCI extended the compliance deadline to December 2026 to avoid disruption to users. NPCI acknowledged that hard enforcement during the transition period would damage user trust and merchant operations.",
|
| 287 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 288 |
+
"relevant_chunk_keywords": ["30%", "PhonePe", "47%", "Google Pay", "34%", "December 2026", "compliance", "not in compliance"]
|
| 289 |
+
},
|
| 290 |
+
{
|
| 291 |
+
"query": "What was Bajaj Finance's deposit franchise size and interest rates offered in Q3 FY2024?",
|
| 292 |
+
"ground_truth": "Bajaj Finance's fixed deposit book stood at Rs 62,100 crore as of Q3 FY2024, growing 26% year-on-year. The company offered retail FD rates of 8.10–8.85% per annum across tenures of 12–60 months, making it one of the highest-rated NBFC FD issuers with an AAA credit rating from CRISIL and ICRA.",
|
| 293 |
+
"relevant_sources": ["bajaj_finance_q3_2024_transcript.txt"],
|
| 294 |
+
"relevant_chunk_keywords": ["fixed deposit", "62,100 crore", "26%", "8.10%", "8.85%", "AAA", "CRISIL", "ICRA"]
|
| 295 |
+
},
|
| 296 |
+
{
|
| 297 |
+
"query": "What did RBI's annual report say about fintech regulation and the regulatory sandbox?",
|
| 298 |
+
"ground_truth": "RBI's regulatory sandbox has processed four cohorts covering retail payments, cross-border payments, MSME lending, and prevention of financial frauds. Twenty-six entities completed sandbox testing by March 2024. RBI issued a principle-based regulatory framework for fintechs in FY2024, covering governance, consumer protection, and data privacy requirements without stifling innovation.",
|
| 299 |
+
"relevant_sources": ["rbi_annual_report_2024.txt"],
|
| 300 |
+
"relevant_chunk_keywords": ["regulatory sandbox", "fintech", "cohorts", "26 entities", "principle-based", "consumer protection", "data privacy"]
|
| 301 |
+
},
|
| 302 |
+
{
|
| 303 |
+
"query": "What is UPI AutoPay and how many mandates were registered by March 2024?",
|
| 304 |
+
"ground_truth": "UPI AutoPay enables recurring payment mandates for subscriptions, EMI deductions, and utility bill autopay. As of March 2024, 8.2 crore active AutoPay mandates were registered on the UPI platform, growing 65% year-on-year. EMI and loan repayment mandates were the largest segment at 45% of total AutoPay volume.",
|
| 305 |
+
"relevant_sources": ["npci_upi_report_2024.txt"],
|
| 306 |
+
"relevant_chunk_keywords": ["UPI AutoPay", "mandates", "8.2 crore", "65%", "EMI", "loan repayment", "recurring"]
|
| 307 |
}
|
| 308 |
]
|
|
@@ -1,8 +1,8 @@
|
|
| 1 |
-
# API Specification —
|
| 2 |
|
| 3 |
## Base URL
|
| 4 |
- Local: `http://localhost:8000`
|
| 5 |
-
- Production: `https://
|
| 6 |
|
| 7 |
---
|
| 8 |
|
|
|
|
| 1 |
+
# API Specification — Prism
|
| 2 |
|
| 3 |
## Base URL
|
| 4 |
- Local: `http://localhost:8000`
|
| 5 |
+
- Production: `https://prism.onrender.com` (set after deploy)
|
| 6 |
|
| 7 |
---
|
| 8 |
|
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
# Architecture —
|
| 2 |
|
| 3 |
## Problem
|
| 4 |
Fintech analysts spend hours manually reading RBI circulars, NPCI reports, and earnings transcripts. Standard dense-only RAG fails silently and misses exact keyword matches in regulatory text (section numbers, policy codes).
|
| 5 |
|
| 6 |
## Architecture overview
|
| 7 |
-
Query → hybrid retrieval (ChromaDB dense + BM25 sparse) → weighted RRF fusion → cross-encoder rerank (top-
|
| 8 |
|
| 9 |
## Component breakdown
|
| 10 |
|
|
@@ -19,14 +19,16 @@ Query → hybrid retrieval (ChromaDB dense + BM25 sparse) → weighted RRF fusio
|
|
| 19 |
| Chunking | LangChain ParentDocumentRetriever | Child 200-char indexed, parent 800-char sent to LLM |
|
| 20 |
| Memory | ConversationBufferWindowMemory (k=10) | Last 10 conversation turns |
|
| 21 |
| Chain | ConversationalRetrievalChain | LangChain orchestration |
|
| 22 |
-
|
|
| 23 |
-
|
|
| 24 |
-
| Eval
|
|
|
|
|
|
|
| 25 |
| Observability | LangSmith | Traces all LLM + retrieval calls via LANGCHAIN_TRACING_V2=true |
|
| 26 |
| Document parsing | LlamaParse (primary), pypdf (fallback) | PDF extraction |
|
| 27 |
| Backend | FastAPI + Uvicorn | REST API |
|
| 28 |
-
| Frontend | React 19 + Vite + Tailwind CSS v4 | Chat /
|
| 29 |
-
| Deployment | Render (Docker backend) + Vercel (frontend) | Production |
|
| 30 |
|
| 31 |
## Data flow
|
| 32 |
|
|
@@ -39,13 +41,12 @@ Query → hybrid retrieval (ChromaDB dense + BM25 sparse) → weighted RRF fusio
|
|
| 39 |
|
| 40 |
### Query
|
| 41 |
1. `POST /api/chat` receives question
|
| 42 |
-
2. `dense_retrieve`: ChromaDB top-
|
| 43 |
-
3. `sparse_retrieve`: BM25 top-
|
| 44 |
4. `reciprocal_rank_fusion`: merge → deduplicate → RRF score
|
| 45 |
5. `Reranker.rerank`: cross-encoder score → return top-5 parent chunks
|
| 46 |
6. `ConversationalRetrievalChain`: LLM answers with context + memory
|
| 47 |
-
7.
|
| 48 |
-
8. Response includes: answer, sources (with scores), faithfulness, retrieval_method
|
| 49 |
|
| 50 |
## Key design decisions
|
| 51 |
- **API embeddings over local**: sentence-transformers ~400MB OOMs on Render 512MB free tier; Euron API ~0MB. Groq used for LLM; Euron retained for embeddings (Groq exposes no embeddings endpoint).
|
|
@@ -54,6 +55,11 @@ Query → hybrid retrieval (ChromaDB dense + BM25 sparse) → weighted RRF fusio
|
|
| 54 |
- **BM25 weight 0.3**: regulatory text has exact keyword matches (section numbers); sparse retrieval catches what dense misses
|
| 55 |
- **RAGAS benchmark pre-computed locally**: `nest_asyncio` cannot patch `uvloop` (used by uvicorn on Render Linux), making live RAGAS eval impossible on prod. Run `scripts/run_ragas_local.py` locally, commit JSON results, Vercel builds dashboard from file.
|
| 56 |
- **TinyBERT-L-2-v2 reranker**: MiniLM-L-6-v2 (~85MB) + base memory (~250MB) + Tavily content + LLM call exceeded Render 512MB on web queries. TinyBERT-L-2-v2 is ~17MB — same ranking quality at demo corpus scale.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
## Known limitations
|
| 59 |
- InMemoryStore for parent chunks: does not survive server restart (re-ingest required)
|
|
|
|
| 1 |
+
# Architecture — Prism
|
| 2 |
|
| 3 |
## Problem
|
| 4 |
Fintech analysts spend hours manually reading RBI circulars, NPCI reports, and earnings transcripts. Standard dense-only RAG fails silently and misses exact keyword matches in regulatory text (section numbers, policy codes).
|
| 5 |
|
| 6 |
## Architecture overview
|
| 7 |
+
Query → hybrid retrieval (ChromaDB dense + BM25 sparse) → weighted RRF fusion → cross-encoder rerank (top-10 → top-5) → LLM answer → LangSmith trace. ParentDocumentRetriever stores 200-char child chunks for retrieval but returns 800-char parent chunks to LLM. Multi-workspace: each workspace has its own ChromaDB collection; vectorstore + retriever cached per workspace to prevent OOM on repeated queries. Eval runs offline via `scripts/run_eval_versioned.py`; results served by a separate `eval-dashboard/` static site.
|
| 8 |
|
| 9 |
## Component breakdown
|
| 10 |
|
|
|
|
| 19 |
| Chunking | LangChain ParentDocumentRetriever | Child 200-char indexed, parent 800-char sent to LLM |
|
| 20 |
| Memory | ConversationBufferWindowMemory (k=10) | Last 10 conversation turns |
|
| 21 |
| Chain | ConversationalRetrievalChain | LangChain orchestration |
|
| 22 |
+
| Workspace | ChromaDB collection per workspace | Isolated document sets; switcher in frontend sidebar |
|
| 23 |
+
| Retriever cache | Module-level dict keyed by workspace | Singleton vectorstore+retriever per workspace; invalidate on ingest |
|
| 24 |
+
| Eval | Separate `eval-dashboard/` Vite+React static site | Reads versioned JSON run files; metrics: answer_correctness, answer_relevancy, context_recall, precision@5, latency p50/p95/p99 |
|
| 25 |
+
| Eval script | `scripts/run_eval_versioned.py` | Runs offline against 50-pair ground truth; writes versioned JSON + updates index.json |
|
| 26 |
+
| Eval ground truth | `data/ground_truth/eval_pairs.json` (50 pairs) | Multi-hop, comparative, negative, numeric, edge-case questions with reference answers |
|
| 27 |
| Observability | LangSmith | Traces all LLM + retrieval calls via LANGCHAIN_TRACING_V2=true |
|
| 28 |
| Document parsing | LlamaParse (primary), pypdf (fallback) | PDF extraction |
|
| 29 |
| Backend | FastAPI + Uvicorn | REST API |
|
| 30 |
+
| Frontend | React 19 + Vite + Tailwind CSS v4 | Chat / Upload tabs |
|
| 31 |
+
| Deployment | Render (Docker backend) + Vercel (frontend) + Vercel (eval-dashboard) | Production |
|
| 32 |
|
| 33 |
## Data flow
|
| 34 |
|
|
|
|
| 41 |
|
| 42 |
### Query
|
| 43 |
1. `POST /api/chat` receives question
|
| 44 |
+
2. `dense_retrieve`: ChromaDB top-10 by cosine similarity (workspace-specific collection)
|
| 45 |
+
3. `sparse_retrieve`: BM25 top-10 by keyword score
|
| 46 |
4. `reciprocal_rank_fusion`: merge → deduplicate → RRF score
|
| 47 |
5. `Reranker.rerank`: cross-encoder score → return top-5 parent chunks
|
| 48 |
6. `ConversationalRetrievalChain`: LLM answers with context + memory
|
| 49 |
+
7. Response includes: answer, sources (with scores), retrieval_method
|
|
|
|
| 50 |
|
| 51 |
## Key design decisions
|
| 52 |
- **API embeddings over local**: sentence-transformers ~400MB OOMs on Render 512MB free tier; Euron API ~0MB. Groq used for LLM; Euron retained for embeddings (Groq exposes no embeddings endpoint).
|
|
|
|
| 55 |
- **BM25 weight 0.3**: regulatory text has exact keyword matches (section numbers); sparse retrieval catches what dense misses
|
| 56 |
- **RAGAS benchmark pre-computed locally**: `nest_asyncio` cannot patch `uvloop` (used by uvicorn on Render Linux), making live RAGAS eval impossible on prod. Run `scripts/run_ragas_local.py` locally, commit JSON results, Vercel builds dashboard from file.
|
| 57 |
- **TinyBERT-L-2-v2 reranker**: MiniLM-L-6-v2 (~85MB) + base memory (~250MB) + Tavily content + LLM call exceeded Render 512MB on web queries. TinyBERT-L-2-v2 is ~17MB — same ranking quality at demo corpus scale.
|
| 58 |
+
- **Singleton vectorstore/retriever cache**: each workspace caches its Chroma vectorstore + HybridRetriever in a module-level dict. Without cache, every chat request created a new Chroma instance (full embedding reload), causing OOM on repeated queries. Cache is invalidated after ingest.
|
| 59 |
+
- **Multi-workspace isolation**: each workspace maps to one ChromaDB collection. Frontend workspace switcher passes `workspace_id` on every request; backend resolves the correct collection before retrieval.
|
| 60 |
+
- **URL size guard**: `url_loader.py` enforces a max content size before embedding URL content, preventing OOM from large external pages.
|
| 61 |
+
- **Eval dashboard separate site**: eval runs offline, results versioned as JSON. Separates eval tooling from user-facing app; no live eval endpoint on prod backend. Per-message faithfulness badge removed from UI — moved to dedicated dashboard.
|
| 62 |
+
- **answer_correctness over faithfulness**: faithfulness (LLM judge vs retrieved chunks) is circular — inflates when eval pairs are corpus-aligned. answer_correctness (LLM judge vs ground_truth reference) is an independent signal.
|
| 63 |
|
| 64 |
## Known limitations
|
| 65 |
- InMemoryStore for parent chunks: does not survive server restart (re-ingest required)
|
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# Technical Decisions —
|
| 2 |
|
| 3 |
## Decision log
|
| 4 |
|
|
@@ -15,6 +15,17 @@
|
|
| 15 |
| 2026-05-30 | Reranker switched to TinyBERT-L-2-v2 (~17MB) from MiniLM-L-6-v2 (~85MB) | MiniLM + base memory + Tavily content + LLM call exceeded 512MB on web queries; TinyBERT saves 68MB permanently with acceptable ranking quality at demo scale | Active |
|
| 16 |
| 2026-05 | Cross-encoder reranker pre-downloaded at Docker build time | Avoids cold-start latency on first request in production | Active |
|
| 17 |
| 2026-05 | Idempotent ingestion via md5(source+page+text) chunk IDs | Re-running ingest does not duplicate chunks in ChromaDB | Active |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
## Rejected alternatives
|
| 20 |
|
|
|
|
| 1 |
+
# Technical Decisions — Prism
|
| 2 |
|
| 3 |
## Decision log
|
| 4 |
|
|
|
|
| 15 |
| 2026-05-30 | Reranker switched to TinyBERT-L-2-v2 (~17MB) from MiniLM-L-6-v2 (~85MB) | MiniLM + base memory + Tavily content + LLM call exceeded 512MB on web queries; TinyBERT saves 68MB permanently with acceptable ranking quality at demo scale | Active |
|
| 16 |
| 2026-05 | Cross-encoder reranker pre-downloaded at Docker build time | Avoids cold-start latency on first request in production | Active |
|
| 17 |
| 2026-05 | Idempotent ingestion via md5(source+page+text) chunk IDs | Re-running ingest does not duplicate chunks in ChromaDB | Active |
|
| 18 |
+
| 2026-06-14 | Multi-workspace: one ChromaDB collection per workspace | Isolated document sets per workspace; `workspace_id` passed on every request; `list_collections()` normalised for chromadb ≥0.5.4 (returns `list[str]`) and <0.5 (returns `list[Collection]`) | Active |
|
| 19 |
+
| 2026-06-14 | Multi-workspace frontend: workspace switcher + per-workspace doc list and chat | Sidebar shows all workspaces; switching remounts ChatArea via React key prop to clear stale messages and state | Active |
|
| 20 |
+
| 2026-06-14 | Singleton vectorstore/retriever cache keyed by workspace_id | Every chat request was creating a new Chroma instance (full embedding reload) on top of the existing one → OOM on repeated queries. Cache dict in `retriever.py` reuses instances; invalidated after ingest. | Active |
|
| 21 |
+
| 2026-06-14 | URL size guard in url_loader.py before embedding | Large external pages (news, filings) could exhaust 512MB RAM during URL ingest. Guard truncates/rejects oversized content before embed call. | Active |
|
| 22 |
+
| 2026-06-16 | HyDE for dense retrieval; toggled via config.yaml `hyde_enabled` | Hypothetical answer embedding lands closer to real answer chunks in vector space than raw query. BM25 + reranker still use original query. Off by default — adds one Groq call (~200ms); enable to measure RAGAS lift before committing. | Active |
|
| 23 |
+
| 2026-06-17 | Web search mandatory (not toggle) | Opt-in toggle caused users to get hallucinated answers grounded in wrong corpus docs when web search was off. Always-on Tavily + RAG gives grounded answers for both corpus and open-domain queries. | Active |
|
| 24 |
+
| 2026-06-17 | Separate eval-dashboard as own Vercel project | Eval tooling is not user-facing; separating avoids bloating the main frontend and lets eval dashboard evolve independently | Active |
|
| 25 |
+
| 2026-06-17 | answer_correctness replaces faithfulness as primary metric | faithfulness (judge vs retrieved chunks) is circular — inflates when eval pairs were designed alongside corpus. answer_correctness (judge vs ground_truth reference) is independent signal | Active |
|
| 26 |
+
| 2026-06-17 | Per-message faithfulness badge removed from user UI | Badge added noise without value to end users; saves one Groq call per query (~200ms latency reduction); eval moved to dedicated dashboard | Active |
|
| 27 |
+
| 2026-06-17 | eval_pairs.json expanded 20 → 50 pairs | 20 samples not statistically meaningful; added multi-hop, comparative, negative, numeric, edge-case question types | Active |
|
| 28 |
+
| 2026-06-17 | Versioned eval JSON runs + index.json registry | Single flat JSON had no history; versioned runs let dashboard show metric evolution across architecture changes | Active |
|
| 29 |
|
| 30 |
## Rejected alternatives
|
| 31 |
|
|
@@ -0,0 +1,488 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Prism — Project Evolution
|
| 2 |
+
|
| 3 |
+
> End-to-end record of what was broken at each stage, what was built to fix it, and what is planned next.
|
| 4 |
+
> Updated as the project evolves. Last updated: 2026-06-15.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## Table of Contents
|
| 9 |
+
|
| 10 |
+
1. [Stage 0 — v1 Baseline](#stage-0--v1-baseline)
|
| 11 |
+
2. [Stage 1 — v2 Hybrid Retrieval Architecture](#stage-1--v2-hybrid-retrieval-architecture-2026-05-17)
|
| 12 |
+
3. [Stage 2 — Chain Scores + RAGAS Endpoint](#stage-2--chain-scores--ragas-endpoint-2026-05-23)
|
| 13 |
+
4. [Stage 3 — Groq Migration + Web Search](#stage-3--groq-migration--web-search-fixes-2026-05-24)
|
| 14 |
+
5. [Stage 4 — OOM Hell on Render](#stage-4--oom-hell-on-render-2026-05-24-four-sub-issues)
|
| 15 |
+
6. [Stage 5 — TinyBERT + RAGAS Removal + Benchmark JSON](#stage-5--tinybert--ragas-removal--benchmark-json-2026-05-30)
|
| 16 |
+
7. [Stage 6 — Multi-Workspace](#stage-6--multi-workspace-2026-06-early)
|
| 17 |
+
8. [Stage 7 — Singleton Cache + URL Guard](#stage-7--singleton-cache--url-guard-2026-06-14--current)
|
| 18 |
+
9. [Current State Snapshot](#current-state-snapshot)
|
| 19 |
+
10. [Roadmap — Retrieval & Answer Quality](#roadmap--retrieval--answer-quality)
|
| 20 |
+
11. [Roadmap — New Features](#roadmap--new-features)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
## Stage 0 — v1 Baseline
|
| 25 |
+
|
| 26 |
+
### What existed
|
| 27 |
+
- Dense-only ChromaDB vector retrieval
|
| 28 |
+
- Single global document collection
|
| 29 |
+
- Basic chat with ConversationalRetrievalChain
|
| 30 |
+
- No evaluation framework
|
| 31 |
+
- No web search
|
| 32 |
+
- No logging
|
| 33 |
+
|
| 34 |
+
### What was wrong
|
| 35 |
+
|
| 36 |
+
| Problem | Impact |
|
| 37 |
+
|---------|--------|
|
| 38 |
+
| Dense-only retrieval | Misses exact keyword matches — regulatory text has section numbers, policy codes, specific terms that semantic search fails on |
|
| 39 |
+
| No evaluation | No way to measure if answers were correct or grounded |
|
| 40 |
+
| No web search | Static corpus only — cannot answer questions about current stock prices, recent news |
|
| 41 |
+
| Single collection | No topic isolation — all documents mixed in one retrieval pool |
|
| 42 |
+
| No logging | Impossible to debug production failures |
|
| 43 |
+
|
| 44 |
+
**This was the starting point. No fixes yet.**
|
| 45 |
+
|
| 46 |
+
---
|
| 47 |
+
|
| 48 |
+
## Stage 1 — v2 Hybrid Retrieval Architecture (2026-05-17)
|
| 49 |
+
|
| 50 |
+
### What was wrong before building
|
| 51 |
+
- `retriever.py` was dense-only ChromaDB — v2 was documented but not implemented
|
| 52 |
+
- `ragas_eval.py` missing entirely; RAGAS eval endpoint not wired
|
| 53 |
+
- No BM25, no reranker, no score visibility
|
| 54 |
+
|
| 55 |
+
### What we built
|
| 56 |
+
|
| 57 |
+
| File | What changed |
|
| 58 |
+
|------|-------------|
|
| 59 |
+
| `server/bm25_index.py` | BM25Okapi singleton; module-level (not `app.state`) so importable anywhere; rebuilt on startup + after upload |
|
| 60 |
+
| `server/reranker.py` | CrossEncoder singleton; pre-loaded at startup to avoid cold-start latency on first query |
|
| 61 |
+
| `server/retriever.py` | Full rewrite as `HybridRetriever(BaseRetriever)` — RRF fusion of dense (weight 0.7) + sparse (weight 0.3) |
|
| 62 |
+
| `server/main.py` | BM25 build + reranker load wired into lifespan startup |
|
| 63 |
+
| `server/routes/upload.py` | BM25 rebuild triggered after each upload |
|
| 64 |
+
|
| 65 |
+
### Key design decisions
|
| 66 |
+
|
| 67 |
+
- **`HybridRetriever` as `BaseRetriever` subclass** — `ConversationalRetrievalChain` expects a `BaseRetriever`; subclassing means `chain.py` needs zero changes
|
| 68 |
+
- **BM25 as module-level singleton** — avoids threading state through lifespan → constructor; `get_index()` importable anywhere
|
| 69 |
+
- **Reranker pre-loaded at startup** — ~0.5s load from disk cache; better to pay at startup than add latency to first user query
|
| 70 |
+
- **MiniLM-L-6-v2** chosen as reranker (~85MB) — best ranking quality available at the time
|
| 71 |
+
|
| 72 |
+
### What was still missing
|
| 73 |
+
- Chain score extraction (similarity/BM25/RRF/rerank not returned in API response)
|
| 74 |
+
- RAGAS eval endpoint
|
| 75 |
+
- Groq LLM (still on Euron)
|
| 76 |
+
|
| 77 |
+
---
|
| 78 |
+
|
| 79 |
+
## Stage 2 — Chain Scores + RAGAS Endpoint (2026-05-23)
|
| 80 |
+
|
| 81 |
+
### What was wrong
|
| 82 |
+
- API response had no retrieval scores — no way to show per-source similarity/BM25/RRF/rerank scores
|
| 83 |
+
- RAGAS eval endpoint not wired; `ragas_eval.py` missing
|
| 84 |
+
- `data/ground_truth/eval_pairs.json` had only keyword hints, no `ground_truth` answers → `context_precision` and `context_recall` always returned null
|
| 85 |
+
|
| 86 |
+
### What we built
|
| 87 |
+
|
| 88 |
+
| File | What changed |
|
| 89 |
+
|------|-------------|
|
| 90 |
+
| `server/chain.py` | Score extraction — similarity/bm25/rrf/rerank scores passed through to API response per source |
|
| 91 |
+
| `server/routes/chat.py` | Added `retrieval_method` field; stores contexts in `eval_log` for downstream RAGAS eval |
|
| 92 |
+
| `server/config.yaml` | Hybrid retrieval params: `dense_weight`, `sparse_weight`, `retrieve_k`, `rerank_k` |
|
| 93 |
+
| `requirements.txt` | Added `rank_bm25`, `sentence-transformers`, `ragas`, `datasets` |
|
| 94 |
+
| `server/eval/ragas_eval.py` | RAGAS faithfulness + answer_relevancy via `LangchainLLMWrapper` |
|
| 95 |
+
| `server/routes/eval.py` | `POST /api/eval/ragas` endpoint wired |
|
| 96 |
+
| `server/main.py` | `/health` endpoint added |
|
| 97 |
+
|
| 98 |
+
### What was still broken
|
| 99 |
+
- RAGAS not installed in venv (added to requirements.txt; installs at Docker build only)
|
| 100 |
+
- `eval_pairs.json` still had no ground_truth → 2 of 4 RAGAS metrics null
|
| 101 |
+
- LLM still on Euron gpt-4.1-mini
|
| 102 |
+
|
| 103 |
+
---
|
| 104 |
+
|
| 105 |
+
## Stage 3 — Groq Migration + Web Search Fixes (2026-05-24)
|
| 106 |
+
|
| 107 |
+
### What was wrong
|
| 108 |
+
|
| 109 |
+
| Problem | Root cause |
|
| 110 |
+
|---------|-----------|
|
| 111 |
+
| LLM on Euron (gpt-4.1-mini) | Closed model, slower, weaker interview story vs open-weight |
|
| 112 |
+
| Web search silently broken | `tavily-python` in requirements.txt but never pip-installed |
|
| 113 |
+
| Tavily returned shallow results | `search_depth="basic"` — not enough content from financial sites |
|
| 114 |
+
| Web context never reached LLM | `ConversationalRetrievalChain`'s condensation step rewrote the question and stripped prepended Tavily context before LLM ever saw it |
|
| 115 |
+
| Follow-up web queries returned garbage | Raw follow-up ("Is the price level good?") sent to Tavily with no chat history context |
|
| 116 |
+
| No request logging | Production failures undebuggable |
|
| 117 |
+
|
| 118 |
+
### What we built
|
| 119 |
+
|
| 120 |
+
| Component | Change |
|
| 121 |
+
|-----------|--------|
|
| 122 |
+
| LLM | Migrated Euron → Groq `llama-3.3-70b-versatile` via `langchain-groq`. Euron kept for embeddings (Groq has no embeddings endpoint) |
|
| 123 |
+
| `server/chain.py` | `run_query_with_web()` — bypasses chain condensation; direct LLM call with RAG + Tavily context + memory |
|
| 124 |
+
| `server/chain.py` | `condense_question()` — rewrites follow-up queries using chat history before Tavily search |
|
| 125 |
+
| Tavily | `search_depth="advanced"`, `max_results=3` (2× credits but richer content) |
|
| 126 |
+
| `server/main.py` | Request logging middleware — logs `METHOD /path STATUS Xms` per request |
|
| 127 |
+
| `server/utils.py` | Centralised logging — root logger + `logs/finrag.log` (5MB×3 rotation), noisy libs silenced |
|
| 128 |
+
| `frontend/.../MessageBubble.jsx` | `WebSourcesList` component — Tavily URLs as clickable green pill links |
|
| 129 |
+
| `.env.example` | Fixed — real keys had been committed; replaced with placeholders |
|
| 130 |
+
|
| 131 |
+
### Key discoveries
|
| 132 |
+
- `ConversationalRetrievalChain` condensation = silent context killer for web queries. Only fix: bypass the chain entirely for web path.
|
| 133 |
+
- Memory's `output_key="answer"` — `save_context` must use `{"answer": answer}` not `{"output": answer}` or KeyError.
|
| 134 |
+
|
| 135 |
+
---
|
| 136 |
+
|
| 137 |
+
## Stage 4 — OOM Hell on Render (2026-05-24, four sub-issues)
|
| 138 |
+
|
| 139 |
+
Render free tier: 512MB RAM. This stage was four separate OOM root causes discovered in sequence.
|
| 140 |
+
|
| 141 |
+
---
|
| 142 |
+
|
| 143 |
+
### 4a — CUDA torch OOM (startup crash)
|
| 144 |
+
|
| 145 |
+
**Problem:** `sentence-transformers` pulled CUDA torch (~2GB) by default. OOM before uvicorn bound to port → Render showed "No open ports detected" timeout. Zero server stdout — invisible failure.
|
| 146 |
+
|
| 147 |
+
**Diagnosis clue:** Build log showed `cuda-toolkit-13.0.2`, `nvidia-cublas` being installed. Port scan timeout = uvicorn crash at import time (not lifespan — lifespan runs *after* port bind).
|
| 148 |
+
|
| 149 |
+
**Fix:**
|
| 150 |
+
```dockerfile
|
| 151 |
+
# Install CPU-only torch BEFORE requirements.txt
|
| 152 |
+
RUN pip install torch --index-url https://download.pytorch.org/whl/cpu
|
| 153 |
+
RUN pip install -r requirements.txt
|
| 154 |
+
|
| 155 |
+
ENV HF_HUB_OFFLINE=1
|
| 156 |
+
ENV TRANSFORMERS_OFFLINE=1
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
---
|
| 160 |
+
|
| 161 |
+
### 4b — ragas startup ImportError
|
| 162 |
+
|
| 163 |
+
**Problem:** `ragas 0.4.3` imports `langchain_community.chat_models.vertexai` at package `__init__` level. That module was removed in `langchain-community 0.4.x`. Crash propagated: `eval.py` → `ragas_eval.py` → `ragas.__init__` → ImportError before uvicorn bound port.
|
| 164 |
+
|
| 165 |
+
**Fix:** All ragas imports moved inside `run_ragas_eval()` function body (lazy import).
|
| 166 |
+
|
| 167 |
+
---
|
| 168 |
+
|
| 169 |
+
### 4c — CrossEncoder OOM during chat
|
| 170 |
+
|
| 171 |
+
**Problem:** `CrossEncoder.predict(20 pairs)` = BERT forward pass on 20 pairs → ~200–400MB spike on top of base ~250MB → OOM on first web query.
|
| 172 |
+
|
| 173 |
+
**Fix:**
|
| 174 |
+
- `config.yaml`: `retrieve_k: 20 → 10`
|
| 175 |
+
- `reranker.py`: `model.predict(pairs, batch_size=4)` — limits how many pairs processed at once
|
| 176 |
+
|
| 177 |
+
---
|
| 178 |
+
|
| 179 |
+
### 4d — Web search OOM (post-GC headroom)
|
| 180 |
+
|
| 181 |
+
**Problem:** After first query, Python retained chain/LLM objects at ~490MB. Web query added ~9KB Tavily content + `condense_question` LLM call + `run_query_with_web` LLM call → OOM.
|
| 182 |
+
|
| 183 |
+
**Fix:**
|
| 184 |
+
- Tavily content truncated to 800 chars per result (was up to ~3000)
|
| 185 |
+
- `max_results`: 3 → 2
|
| 186 |
+
- `gc.collect()` after each chat request in `routes/chat.py`
|
| 187 |
+
|
| 188 |
+
---
|
| 189 |
+
|
| 190 |
+
## Stage 5 — TinyBERT + RAGAS Removal + Benchmark JSON (2026-05-30)
|
| 191 |
+
|
| 192 |
+
### What was wrong
|
| 193 |
+
|
| 194 |
+
| Problem | Root cause |
|
| 195 |
+
|---------|-----------|
|
| 196 |
+
| MiniLM-L-6-v2 (~85MB) still OOMing on web queries | Too close to 512MB ceiling even after GC |
|
| 197 |
+
| Live RAGAS eval always 500 on Render | `nest_asyncio.apply()` (called at ragas import time) cannot patch `uvloop` — the event loop uvicorn uses on Linux. `ValueError: Can't patch loop of type uvloop.Loop`. Permanently unfixable without replacing uvicorn's event loop. |
|
| 198 |
+
| Groq 70B exhausted 100k daily tokens in one RAGAS run | RAGAS makes ~10 LLM calls per sample for statement decomposition. 10 samples × 10 calls = 100k tokens gone. |
|
| 199 |
+
| context_precision and context_recall always null | `eval_pairs.json` had no `ground_truth` answers — only keyword hints |
|
| 200 |
+
|
| 201 |
+
### What we built
|
| 202 |
+
|
| 203 |
+
| Component | Change |
|
| 204 |
+
|-----------|--------|
|
| 205 |
+
| `server/reranker.py` | Switched to `cross-encoder/ms-marco-TinyBERT-L-2-v2` (~17MB vs 85MB). Saves 68MB permanently. |
|
| 206 |
+
| `server/routes/eval.py` | Removed `POST /api/eval/ragas` endpoint |
|
| 207 |
+
| `requirements.txt` | Removed `ragas` |
|
| 208 |
+
| `scripts/run_ragas_local.py` | Local RAGAS runner: ingest corpus → generate answers → run eval → write JSON. Uses `llama-3.1-8b-instant` as judge (500k TPD vs 70B's 100k TPD) |
|
| 209 |
+
| `frontend/src/data/ragas_benchmark.json` | Static scores — Vercel builds dashboard from file |
|
| 210 |
+
| `frontend/.../EvalPanel.jsx` | Replaced live run button with static benchmark panel |
|
| 211 |
+
| `data/ground_truth/eval_pairs.json` | Added `ground_truth` field to all 20 pairs → unlocked `context_precision` + `context_recall` |
|
| 212 |
+
| UI | i-button tooltips on faithfulness badge + all 4 RAGAS metric cards |
|
| 213 |
+
|
| 214 |
+
### Real scores committed
|
| 215 |
+
```
|
| 216 |
+
faithfulness: 1.0 (note: likely inflated — see below)
|
| 217 |
+
answer_relevancy: 0.90
|
| 218 |
+
context_precision: TBD (pending fresh run)
|
| 219 |
+
context_recall: TBD (pending fresh run)
|
| 220 |
+
```
|
| 221 |
+
|
| 222 |
+
### Key discoveries
|
| 223 |
+
- `results["metric_name"]` returns `None` in ragas 0.2.x — must use `results.to_pandas()["metric_name"].mean()`
|
| 224 |
+
- TinyBERT loads with harmless `UNEXPECTED key bert.embeddings.position_ids` warning
|
| 225 |
+
- **Faithfulness 1.0 is likely inflated** — eval queries were designed alongside the corpus, and 8B judge is lenient. Scores are directional, not absolute. Run on held-out queries for honest numbers.
|
| 226 |
+
|
| 227 |
+
---
|
| 228 |
+
|
| 229 |
+
## Stage 5.5 — Rebranding: FinRAG → Prism (2026-06-13)
|
| 230 |
+
|
| 231 |
+
### What changed
|
| 232 |
+
The project was originally named **FinRAG** — a fintech-specific RAG demo. As the architecture matured (multi-workspace, URL ingestion, domain-agnostic retrieval), it became clear the tool was no longer fintech-specific. Any corpus — legal, HR, medical, research — could be loaded and queried.
|
| 233 |
+
|
| 234 |
+
**Decision:** Rebrand to **Prism**. Name reflects the core idea: feed any document set in, get clear structured answers out. One engine, any domain.
|
| 235 |
+
|
| 236 |
+
| Before | After |
|
| 237 |
+
|--------|-------|
|
| 238 |
+
| FinRAG | Prism |
|
| 239 |
+
| Fintech-specific framing | Domain-agnostic positioning |
|
| 240 |
+
| `finrag-v2.onrender.com` | `prism.onrender.com` |
|
| 241 |
+
| README pitched at fintech analysts | README pitched at any knowledge-worker |
|
| 242 |
+
|
| 243 |
+
### What stayed the same
|
| 244 |
+
All retrieval architecture, eval framework, and deployment stack unchanged. Rebrand is naming and framing only — the engine is identical.
|
| 245 |
+
|
| 246 |
+
### What was wrong with the old name
|
| 247 |
+
- "FinRAG" implied fintech-only → narrowed the demo audience
|
| 248 |
+
- Interviewers at non-fintech MNCs (Adobe, Atlassian, Intuit) would dismiss it as domain-locked
|
| 249 |
+
- The actual retrieval engine is domain-agnostic — the name should match
|
| 250 |
+
|
| 251 |
+
---
|
| 252 |
+
|
| 253 |
+
## Stage 6 — Multi-Workspace (2026-06 early)
|
| 254 |
+
|
| 255 |
+
### What was wrong
|
| 256 |
+
- Single ChromaDB collection — no isolation between document sets
|
| 257 |
+
- Switching topics meant re-ingesting and overwriting previous docs
|
| 258 |
+
- `list_collections()` broke on chromadb ≥0.5.4 (returns `list[str]`, not `list[Collection]`)
|
| 259 |
+
- Non-web chat path used stale global chain's `source_documents` instead of workspace-specific retriever → wrong docs shown after workspace switch
|
| 260 |
+
|
| 261 |
+
### What we built
|
| 262 |
+
|
| 263 |
+
| File | Change |
|
| 264 |
+
|------|--------|
|
| 265 |
+
| `server/routes/workspaces.py` | Workspace CRUD — one ChromaDB collection per workspace |
|
| 266 |
+
| `server/routes/chat.py` | Always resolves workspace-specific retriever before branching on `web_search` |
|
| 267 |
+
| `server/routes/workspaces.py` | `list_collections()` normalised with `isinstance` check — works on chromadb ≥0.5.4 (`list[str]`) and <0.5 (`list[Collection]`) |
|
| 268 |
+
| `frontend/src/components/Sidebar.jsx` | Workspace switcher UI; per-workspace doc list |
|
| 269 |
+
| `frontend/src/App.jsx`, `api.js`, `ChatArea.jsx`, `FileUpload.jsx` | `workspace_id` passed on all requests |
|
| 270 |
+
|
| 271 |
+
### Key discovery
|
| 272 |
+
- Non-web path was relying on stale global chain's `source_documents` rather than workspace-specific retriever. After switching workspaces, the wrong collection's docs were being cited.
|
| 273 |
+
|
| 274 |
+
---
|
| 275 |
+
|
| 276 |
+
## Stage 7 — Singleton Cache + URL Guard (2026-06-14) ← Current
|
| 277 |
+
|
| 278 |
+
### What was wrong
|
| 279 |
+
- Every `POST /api/chat` called `get_or_create_collection()` + built a new `HybridRetriever` = full embedding reload per request → OOM after 2–3 queries in the same workspace
|
| 280 |
+
- React component state (message list) persisted across workspace switch — showed previous workspace's chat history
|
| 281 |
+
- External URL ingestion had no size guard → large pages (news articles, regulatory filings) caused OOM during embed
|
| 282 |
+
|
| 283 |
+
### What we built
|
| 284 |
+
|
| 285 |
+
| File | Change |
|
| 286 |
+
|------|--------|
|
| 287 |
+
| `server/retriever.py` | Module-level `Dict[workspace_id, (vectorstore, retriever)]` cache. Cache invalidated after ingest. `routes/chat.py` reuses cached retriever. |
|
| 288 |
+
| `server/routes/chat.py` | Eliminated double retrieval on non-web path; fixed stray print statement |
|
| 289 |
+
| `server/url_loader.py` | Max content size guard before embedding external URL content |
|
| 290 |
+
| `frontend/src/App.jsx` | `key={workspaceId}` on `<ChatArea>` → remounts component on workspace switch → clears stale messages and state |
|
| 291 |
+
|
| 292 |
+
### Commits
|
| 293 |
+
```
|
| 294 |
+
529675f fix: singleton vectorstore/retriever cache to prevent OOM on repeated queries
|
| 295 |
+
6c9f809 fix: URL size guard for OOM prevention, eliminate double retrieval in chat
|
| 296 |
+
521a27a fix: remount ChatArea on workspace switch to clear stale messages
|
| 297 |
+
```
|
| 298 |
+
|
| 299 |
+
### Additional fix (2026-06-16) — HyDE (Hypothetical Document Embeddings)
|
| 300 |
+
|
| 301 |
+
**What:** Before dense ChromaDB search, LLM generates a hypothetical 2-sentence answer. That answer (not raw query) is embedded for ANN search. BM25 + reranker still use original query.
|
| 302 |
+
|
| 303 |
+
| File | Change |
|
| 304 |
+
|------|--------|
|
| 305 |
+
| `server/retriever.py` | `_hyde_expand()` method; `use_hyde: bool` field on `HybridRetriever`; dense path uses expanded query when enabled |
|
| 306 |
+
| `config.yaml` | `retrieval.hyde_enabled: false` — toggle without code change |
|
| 307 |
+
|
| 308 |
+
**Why off by default:** Adds one Groq call per query (~200ms). Enable to measure RAGAS context_recall lift, then decide.
|
| 309 |
+
|
| 310 |
+
**Commit:** `8945b43`
|
| 311 |
+
|
| 312 |
+
---
|
| 313 |
+
|
| 314 |
+
### Additional fix (2026-06-17) — Mandatory web search
|
| 315 |
+
|
| 316 |
+
**Problem:** Web search was opt-in toggle. Users querying corpus-only got hallucinated answers from irrelevant documents (e.g., Singapore visa question grounded in random passport-mentioning corpus doc, faithfulness 4/5).
|
| 317 |
+
|
| 318 |
+
**Fix:**
|
| 319 |
+
|
| 320 |
+
| File | Change |
|
| 321 |
+
|------|--------|
|
| 322 |
+
| `frontend/src/components/ChatArea.jsx` | Removed toggle button; `const webSearch = true` hardcoded; placeholder always says "docs + web" |
|
| 323 |
+
| `server/routes/chat.py` | `web_search: bool = True` as default in `ChatRequest` |
|
| 324 |
+
|
| 325 |
+
Every query now hits Tavily + RAG corpus. `run_query_with_web` always called with both rag_docs + web_sources.
|
| 326 |
+
|
| 327 |
+
---
|
| 328 |
+
|
| 329 |
+
## Stage 8 — Eval Dashboard + Rigorous Metrics (2026-06-17)
|
| 330 |
+
|
| 331 |
+
### What was wrong
|
| 332 |
+
- faithfulness 1.0 and context_precision 1.0 artificially inflated — eval pairs designed alongside corpus, 8B judge lenient. Meaningless scores.
|
| 333 |
+
- Per-message faithfulness badge cluttered user UI. Users don't care about LLM judge scores.
|
| 334 |
+
- 10 samples — not statistically meaningful.
|
| 335 |
+
- Single flat JSON, no versioning — no way to track metric evolution across architecture changes.
|
| 336 |
+
|
| 337 |
+
### What we built
|
| 338 |
+
|
| 339 |
+
| Component | Change |
|
| 340 |
+
|-----------|--------|
|
| 341 |
+
| `eval-dashboard/` | Separate Vite + React static site (own Vercel project). Reads versioned JSON run files. |
|
| 342 |
+
| `eval-dashboard/src/components/` | MetricCard (score + delta vs prev), EvolutionChart (Recharts line chart across versions), RunTable (per-query expandable rows with answer vs ground_truth), LatencyStats (p50/p95 bars) |
|
| 343 |
+
| `eval-dashboard/public/data/index.json` | Run registry — list of all versioned eval runs |
|
| 344 |
+
| `scripts/run_eval_versioned.py` | New eval script. Args: `--version`, `--tag`, `--n`. Computes answer_correctness (LLM judge vs ground_truth), answer_relevancy + context_recall (RAGAS), precision@5, latency p50/p95/p99. Writes versioned JSON + updates index. |
|
| 345 |
+
| `data/ground_truth/eval_pairs.json` | Expanded 20 → 50 pairs. Added multi-hop, comparative, negative, numeric, and edge-case questions. |
|
| 346 |
+
| `frontend/src/components/MessageBubble.jsx` | Removed FaithfulnessBadge component and rendering block. |
|
| 347 |
+
| `server/routes/chat.py` | Removed `score_faithfulness()` call. One fewer Groq API call per query → faster responses. |
|
| 348 |
+
|
| 349 |
+
### Metrics before vs after
|
| 350 |
+
|
| 351 |
+
| Metric | Before | After |
|
| 352 |
+
|--------|--------|-------|
|
| 353 |
+
| faithfulness | 1.0 (inflated) | Removed from prod path |
|
| 354 |
+
| context_precision | 1.0 (inflated) | Replaced by answer_correctness (LLM judge vs ground_truth) |
|
| 355 |
+
| answer_relevancy | 0.88 | Kept (RAGAS) |
|
| 356 |
+
| context_recall | 0.83 | Kept (RAGAS) |
|
| 357 |
+
| precision@5 | tracked separately | Now in main eval dashboard |
|
| 358 |
+
| latency p50/p95 | not tracked | Now tracked per eval run |
|
| 359 |
+
| sample_count | 10 | 50 (5× improvement) |
|
| 360 |
+
|
| 361 |
+
---
|
| 362 |
+
|
| 363 |
+
## Current State Snapshot
|
| 364 |
+
|
| 365 |
+
```
|
| 366 |
+
Retrieval: Hybrid BM25 (0.3) + ChromaDB dense (0.7) → RRF → TinyBERT rerank top-10→5
|
| 367 |
+
LLM: Groq llama-3.3-70b-versatile
|
| 368 |
+
Embeddings: Euron API text-embedding-3-small
|
| 369 |
+
Chunking: ParentDocumentRetriever (child 200-char indexed, parent 800-char to LLM)
|
| 370 |
+
Memory: ConversationBufferWindowMemory k=10
|
| 371 |
+
Web search: Tavily advanced, 800-char truncation, max 2 results — MANDATORY (always on)
|
| 372 |
+
HyDE: Implemented, toggled via config.yaml hyde_enabled (default: false)
|
| 373 |
+
Eval: Separate eval-dashboard/ static site (own Vercel project)
|
| 374 |
+
Metrics: answer_correctness, answer_relevancy, context_recall, precision@5, latency
|
| 375 |
+
Script: scripts/run_eval_versioned.py --version vX.Y --tag "..." --n 50
|
| 376 |
+
50 eval pairs (up from 10 samples)
|
| 377 |
+
No per-message faithfulness badge in user UI
|
| 378 |
+
Workspaces: Per-workspace ChromaDB collection, singleton retriever cache
|
| 379 |
+
Infra: Render 512MB (base ~230MB, headroom ~280MB) + Vercel frontend + Vercel eval dashboard
|
| 380 |
+
Observability: LangSmith traces all LLM + retrieval calls
|
| 381 |
+
```
|
| 382 |
+
|
| 383 |
+
---
|
| 384 |
+
|
| 385 |
+
## Roadmap — Retrieval & Answer Quality
|
| 386 |
+
|
| 387 |
+
### Phase 1 — Quick wins (no infra change, measurable RAGAS lift)
|
| 388 |
+
|
| 389 |
+
#### HyDE (Hypothetical Document Embeddings)
|
| 390 |
+
- **Problem:** Raw query `"UPI volume FY24"` lives in a different embedding space than the answer. Short queries have low information density.
|
| 391 |
+
- **How:** Generate a hypothetical 2-sentence answer (LLM, no RAG), embed that instead of the raw query, use the vector for ChromaDB ANN search.
|
| 392 |
+
- **Why it works:** Hypothetical answer lands closer to real answer chunks in embedding space than the query does.
|
| 393 |
+
- **Effort:** ~15 lines wrapping `dense_retrieve()`. One extra Groq call (~200ms). Toggle in config.
|
| 394 |
+
- **Expected lift:** RAGAS context_recall +5–15% on vague queries.
|
| 395 |
+
|
| 396 |
+
#### Multi-Query Retrieval
|
| 397 |
+
- **Problem:** Single phrasing has blind spots. `"UPI volume FY24"` misses `"transactions processed in financial year 2023-24"`.
|
| 398 |
+
- **How:** LLM generates 3 phrasings of the query → retrieve for each → pool all candidates → deduplicate by chunk ID → RRF merge → rerank.
|
| 399 |
+
- **Why it works:** Wider candidate pool before reranker = higher recall. Reranker then picks best 5 from 30 instead of 10.
|
| 400 |
+
- **Effort:** ~30 lines. 3× retrieval calls + 1 LLM call. Parallelise with `asyncio.gather` to limit latency hit.
|
| 401 |
+
- **Expected lift:** RAGAS context_recall measurably improves on multi-phrasing queries.
|
| 402 |
+
|
| 403 |
+
---
|
| 404 |
+
|
| 405 |
+
### Phase 2 — Ingest pipeline (requires re-ingest of all docs)
|
| 406 |
+
|
| 407 |
+
#### Contextual Retrieval
|
| 408 |
+
- **Problem:** Chunks lose context when split. `"The limit was revised to ₹2 lakh"` has no idea which circular, which date, which payment type.
|
| 409 |
+
- **How:** At ingest time, for each chunk, call LLM: *"Here is the document [full doc]. Here is a chunk [chunk]. Write 2–3 sentences situating this chunk."* Prepend that context to the chunk before embedding.
|
| 410 |
+
- **Result:** `"RBI Master Circular 2024 on UPI limits. The limit was revised to ₹2 lakh..."` → richer vector.
|
| 411 |
+
- **Effort:** Medium. Modifies `ingest.py` child chunk creation. One Groq 8B call per chunk at ingest time (not query time — zero query latency hit).
|
| 412 |
+
- **Expected lift:** Anthropic's benchmark: ~49% reduction in retrieval failures. RAGAS context_precision measurably improves.
|
| 413 |
+
|
| 414 |
+
#### Semantic Chunking
|
| 415 |
+
- **Problem:** Fixed 200-char splits cut mid-sentence, mid-table, mid-list. Embedding a truncated sentence returns a weak vector.
|
| 416 |
+
- **How:** Replace `RecursiveCharacterTextSplitter` with LangChain's `SemanticChunker` — splits at sentence boundaries where cosine similarity between adjacent sentences drops below a threshold (topic shift).
|
| 417 |
+
- **Effort:** Medium. Config change in `ingest.py` + re-ingest. Tune `breakpoint_threshold_type`.
|
| 418 |
+
- **Expected lift:** Fewer nonsensical chunks in top-5. Most noticeable on regulatory PDFs with section headers and numbered lists.
|
| 419 |
+
|
| 420 |
+
---
|
| 421 |
+
|
| 422 |
+
### Phase 3 — UX + trust
|
| 423 |
+
|
| 424 |
+
#### Streaming Responses
|
| 425 |
+
- **Problem:** User submits question → 8–15s wait → full answer appears. On Render free vCPU this feels broken.
|
| 426 |
+
- **How:** Backend: `chain.astream_events()` → `StreamingResponse` yielding SSE tokens. Frontend: `EventSource` or `fetch` + `ReadableStream` — append tokens as they arrive. Faithfulness scoring runs as background task after full answer assembled.
|
| 427 |
+
- **Effort:** High — both backend and frontend change. `ConversationalRetrievalChain` supports `astream_events()` in LangChain ≥0.2.
|
| 428 |
+
- **Impact:** Perceived latency drops from 10s to ~1s. Single biggest UX improvement.
|
| 429 |
+
|
| 430 |
+
#### Citation Highlighting
|
| 431 |
+
- **Problem:** Sources listed but user can't see *exactly* which passage was cited.
|
| 432 |
+
- **How:** Show source chunks highlighted inside a PDF viewer pane (react-pdf + highlight overlay). Map chunk text → page number → bounding box.
|
| 433 |
+
- **Impact:** Strongest trust signal for a RAG demo. Interviewers ask "how do you know the answer is grounded?" — show them.
|
| 434 |
+
|
| 435 |
+
---
|
| 436 |
+
|
| 437 |
+
### Phase 4 — Differentiation
|
| 438 |
+
|
| 439 |
+
#### Metadata Filtering
|
| 440 |
+
- **Problem:** Multi-workspace isolates by collection, but within a workspace (10 docs across 5 years) no way to scope retrieval to `year=2024` or `doc_type=rbi_circular`.
|
| 441 |
+
- **How:** Tag chunks with `{source_type, year, doc_name}` at ingest. Pass optional `filter` param in `/api/chat` request. ChromaDB `where` clause on dense retrieval; BM25 pre-filters corpus to matching chunk IDs.
|
| 442 |
+
- **Impact:** Precision boost on time-scoped or source-scoped queries.
|
| 443 |
+
|
| 444 |
+
#### Document Comparison Mode
|
| 445 |
+
- **Problem:** No way to ask "What changed between RBI circular 2023 and 2024?"
|
| 446 |
+
- **How:** Frontend sends two doc IDs + comparison query. Backend retrieves relevant chunks from each collection separately, synthesises a structured diff answer.
|
| 447 |
+
- **Impact:** Killer fintech feature. Unique demo moment. Differentiates from generic RAG.
|
| 448 |
+
|
| 449 |
+
#### Agentic Mode (LangGraph)
|
| 450 |
+
- **Problem:** Single-shot RAG cannot handle multi-step reasoning: retrieve → compute → web search → synthesise.
|
| 451 |
+
- **How:** Replace `ConversationalRetrievalChain` with a LangGraph graph. Nodes: retriever, web_search, calculator, synthesiser. LLM decides which tool to call.
|
| 452 |
+
- **Impact:** Separates Prism from basic RAG — becomes a research agent. Strongest interview story.
|
| 453 |
+
|
| 454 |
+
---
|
| 455 |
+
|
| 456 |
+
## Roadmap Priority Matrix
|
| 457 |
+
|
| 458 |
+
```
|
| 459 |
+
HIGH impact × LOW effort → Build first
|
| 460 |
+
HyDE
|
| 461 |
+
Multi-query retrieval
|
| 462 |
+
Metadata filtering
|
| 463 |
+
|
| 464 |
+
HIGH impact × MEDIUM effort → Build second
|
| 465 |
+
Contextual retrieval (+ re-ingest)
|
| 466 |
+
Semantic chunking (+ re-ingest)
|
| 467 |
+
Streaming responses
|
| 468 |
+
|
| 469 |
+
HIGH impact × HIGH effort → Build last
|
| 470 |
+
Citation highlighting
|
| 471 |
+
Document comparison
|
| 472 |
+
Agentic mode (LangGraph)
|
| 473 |
+
```
|
| 474 |
+
|
| 475 |
+
---
|
| 476 |
+
|
| 477 |
+
## Interview Story Arc
|
| 478 |
+
|
| 479 |
+
```
|
| 480 |
+
v1 → Dense-only retrieval. No eval. No baseline.
|
| 481 |
+
v2 → Hybrid BM25+dense, cross-encoder rerank. Measured with RAGAS.
|
| 482 |
+
→ faithfulness=1.0, answer_relevancy=0.90 on 20-pair eval set.
|
| 483 |
+
+HyDE+MQ → RAGAS context_recall +X%. Concrete metric improvement.
|
| 484 |
+
+Contextual → RAGAS context_precision +Y%. Ingest-time LLM augmentation.
|
| 485 |
+
+Agentic → Multi-step reasoning. Not RAG anymore — research agent.
|
| 486 |
+
```
|
| 487 |
+
|
| 488 |
+
Each step has a metric. That is the complete RAG engineering narrative for MNC DS interviews.
|
|
@@ -1,7 +1,7 @@
|
|
| 1 |
-
# Project Structure —
|
| 2 |
|
| 3 |
```
|
| 4 |
-
|
| 5 |
├── CLAUDE.md
|
| 6 |
├── README.md
|
| 7 |
├── requirements.txt
|
|
|
|
| 1 |
+
# Project Structure — Prism
|
| 2 |
|
| 3 |
```
|
| 4 |
+
prism/
|
| 5 |
├── CLAUDE.md
|
| 6 |
├── README.md
|
| 7 |
├── requirements.txt
|
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>Prism — Eval Dashboard</title>
|
| 7 |
+
</head>
|
| 8 |
+
<body>
|
| 9 |
+
<div id="root"></div>
|
| 10 |
+
<script type="module" src="/src/main.jsx"></script>
|
| 11 |
+
</body>
|
| 12 |
+
</html>
|
|
@@ -0,0 +1,2776 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "prism-eval-dashboard",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "prism-eval-dashboard",
|
| 9 |
+
"version": "1.0.0",
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"react": "^19.0.0",
|
| 12 |
+
"react-dom": "^19.0.0",
|
| 13 |
+
"recharts": "^2.12.0"
|
| 14 |
+
},
|
| 15 |
+
"devDependencies": {
|
| 16 |
+
"@tailwindcss/vite": "^4.0.0",
|
| 17 |
+
"@vitejs/plugin-react": "^4.3.0",
|
| 18 |
+
"tailwindcss": "^4.0.0",
|
| 19 |
+
"vite": "^6.0.0"
|
| 20 |
+
}
|
| 21 |
+
},
|
| 22 |
+
"node_modules/@babel/code-frame": {
|
| 23 |
+
"version": "7.29.7",
|
| 24 |
+
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
| 25 |
+
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
|
| 26 |
+
"dev": true,
|
| 27 |
+
"license": "MIT",
|
| 28 |
+
"dependencies": {
|
| 29 |
+
"@babel/helper-validator-identifier": "^7.29.7",
|
| 30 |
+
"js-tokens": "^4.0.0",
|
| 31 |
+
"picocolors": "^1.1.1"
|
| 32 |
+
},
|
| 33 |
+
"engines": {
|
| 34 |
+
"node": ">=6.9.0"
|
| 35 |
+
}
|
| 36 |
+
},
|
| 37 |
+
"node_modules/@babel/compat-data": {
|
| 38 |
+
"version": "7.29.7",
|
| 39 |
+
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
|
| 40 |
+
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
|
| 41 |
+
"dev": true,
|
| 42 |
+
"license": "MIT",
|
| 43 |
+
"engines": {
|
| 44 |
+
"node": ">=6.9.0"
|
| 45 |
+
}
|
| 46 |
+
},
|
| 47 |
+
"node_modules/@babel/core": {
|
| 48 |
+
"version": "7.29.7",
|
| 49 |
+
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
|
| 50 |
+
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
| 51 |
+
"dev": true,
|
| 52 |
+
"license": "MIT",
|
| 53 |
+
"dependencies": {
|
| 54 |
+
"@babel/code-frame": "^7.29.7",
|
| 55 |
+
"@babel/generator": "^7.29.7",
|
| 56 |
+
"@babel/helper-compilation-targets": "^7.29.7",
|
| 57 |
+
"@babel/helper-module-transforms": "^7.29.7",
|
| 58 |
+
"@babel/helpers": "^7.29.7",
|
| 59 |
+
"@babel/parser": "^7.29.7",
|
| 60 |
+
"@babel/template": "^7.29.7",
|
| 61 |
+
"@babel/traverse": "^7.29.7",
|
| 62 |
+
"@babel/types": "^7.29.7",
|
| 63 |
+
"@jridgewell/remapping": "^2.3.5",
|
| 64 |
+
"convert-source-map": "^2.0.0",
|
| 65 |
+
"debug": "^4.1.0",
|
| 66 |
+
"gensync": "^1.0.0-beta.2",
|
| 67 |
+
"json5": "^2.2.3",
|
| 68 |
+
"semver": "^6.3.1"
|
| 69 |
+
},
|
| 70 |
+
"engines": {
|
| 71 |
+
"node": ">=6.9.0"
|
| 72 |
+
},
|
| 73 |
+
"funding": {
|
| 74 |
+
"type": "opencollective",
|
| 75 |
+
"url": "https://opencollective.com/babel"
|
| 76 |
+
}
|
| 77 |
+
},
|
| 78 |
+
"node_modules/@babel/generator": {
|
| 79 |
+
"version": "7.29.7",
|
| 80 |
+
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
|
| 81 |
+
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
|
| 82 |
+
"dev": true,
|
| 83 |
+
"license": "MIT",
|
| 84 |
+
"dependencies": {
|
| 85 |
+
"@babel/parser": "^7.29.7",
|
| 86 |
+
"@babel/types": "^7.29.7",
|
| 87 |
+
"@jridgewell/gen-mapping": "^0.3.12",
|
| 88 |
+
"@jridgewell/trace-mapping": "^0.3.28",
|
| 89 |
+
"jsesc": "^3.0.2"
|
| 90 |
+
},
|
| 91 |
+
"engines": {
|
| 92 |
+
"node": ">=6.9.0"
|
| 93 |
+
}
|
| 94 |
+
},
|
| 95 |
+
"node_modules/@babel/helper-compilation-targets": {
|
| 96 |
+
"version": "7.29.7",
|
| 97 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
|
| 98 |
+
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
|
| 99 |
+
"dev": true,
|
| 100 |
+
"license": "MIT",
|
| 101 |
+
"dependencies": {
|
| 102 |
+
"@babel/compat-data": "^7.29.7",
|
| 103 |
+
"@babel/helper-validator-option": "^7.29.7",
|
| 104 |
+
"browserslist": "^4.24.0",
|
| 105 |
+
"lru-cache": "^5.1.1",
|
| 106 |
+
"semver": "^6.3.1"
|
| 107 |
+
},
|
| 108 |
+
"engines": {
|
| 109 |
+
"node": ">=6.9.0"
|
| 110 |
+
}
|
| 111 |
+
},
|
| 112 |
+
"node_modules/@babel/helper-globals": {
|
| 113 |
+
"version": "7.29.7",
|
| 114 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
|
| 115 |
+
"integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
|
| 116 |
+
"dev": true,
|
| 117 |
+
"license": "MIT",
|
| 118 |
+
"engines": {
|
| 119 |
+
"node": ">=6.9.0"
|
| 120 |
+
}
|
| 121 |
+
},
|
| 122 |
+
"node_modules/@babel/helper-module-imports": {
|
| 123 |
+
"version": "7.29.7",
|
| 124 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
|
| 125 |
+
"integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
|
| 126 |
+
"dev": true,
|
| 127 |
+
"license": "MIT",
|
| 128 |
+
"dependencies": {
|
| 129 |
+
"@babel/traverse": "^7.29.7",
|
| 130 |
+
"@babel/types": "^7.29.7"
|
| 131 |
+
},
|
| 132 |
+
"engines": {
|
| 133 |
+
"node": ">=6.9.0"
|
| 134 |
+
}
|
| 135 |
+
},
|
| 136 |
+
"node_modules/@babel/helper-module-transforms": {
|
| 137 |
+
"version": "7.29.7",
|
| 138 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
|
| 139 |
+
"integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
|
| 140 |
+
"dev": true,
|
| 141 |
+
"license": "MIT",
|
| 142 |
+
"dependencies": {
|
| 143 |
+
"@babel/helper-module-imports": "^7.29.7",
|
| 144 |
+
"@babel/helper-validator-identifier": "^7.29.7",
|
| 145 |
+
"@babel/traverse": "^7.29.7"
|
| 146 |
+
},
|
| 147 |
+
"engines": {
|
| 148 |
+
"node": ">=6.9.0"
|
| 149 |
+
},
|
| 150 |
+
"peerDependencies": {
|
| 151 |
+
"@babel/core": "^7.0.0"
|
| 152 |
+
}
|
| 153 |
+
},
|
| 154 |
+
"node_modules/@babel/helper-plugin-utils": {
|
| 155 |
+
"version": "7.29.7",
|
| 156 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
|
| 157 |
+
"integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
|
| 158 |
+
"dev": true,
|
| 159 |
+
"license": "MIT",
|
| 160 |
+
"engines": {
|
| 161 |
+
"node": ">=6.9.0"
|
| 162 |
+
}
|
| 163 |
+
},
|
| 164 |
+
"node_modules/@babel/helper-string-parser": {
|
| 165 |
+
"version": "7.29.7",
|
| 166 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
|
| 167 |
+
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
|
| 168 |
+
"dev": true,
|
| 169 |
+
"license": "MIT",
|
| 170 |
+
"engines": {
|
| 171 |
+
"node": ">=6.9.0"
|
| 172 |
+
}
|
| 173 |
+
},
|
| 174 |
+
"node_modules/@babel/helper-validator-identifier": {
|
| 175 |
+
"version": "7.29.7",
|
| 176 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
|
| 177 |
+
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
|
| 178 |
+
"dev": true,
|
| 179 |
+
"license": "MIT",
|
| 180 |
+
"engines": {
|
| 181 |
+
"node": ">=6.9.0"
|
| 182 |
+
}
|
| 183 |
+
},
|
| 184 |
+
"node_modules/@babel/helper-validator-option": {
|
| 185 |
+
"version": "7.29.7",
|
| 186 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
|
| 187 |
+
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
|
| 188 |
+
"dev": true,
|
| 189 |
+
"license": "MIT",
|
| 190 |
+
"engines": {
|
| 191 |
+
"node": ">=6.9.0"
|
| 192 |
+
}
|
| 193 |
+
},
|
| 194 |
+
"node_modules/@babel/helpers": {
|
| 195 |
+
"version": "7.29.7",
|
| 196 |
+
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
|
| 197 |
+
"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
|
| 198 |
+
"dev": true,
|
| 199 |
+
"license": "MIT",
|
| 200 |
+
"dependencies": {
|
| 201 |
+
"@babel/template": "^7.29.7",
|
| 202 |
+
"@babel/types": "^7.29.7"
|
| 203 |
+
},
|
| 204 |
+
"engines": {
|
| 205 |
+
"node": ">=6.9.0"
|
| 206 |
+
}
|
| 207 |
+
},
|
| 208 |
+
"node_modules/@babel/parser": {
|
| 209 |
+
"version": "7.29.7",
|
| 210 |
+
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
| 211 |
+
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
| 212 |
+
"dev": true,
|
| 213 |
+
"license": "MIT",
|
| 214 |
+
"dependencies": {
|
| 215 |
+
"@babel/types": "^7.29.7"
|
| 216 |
+
},
|
| 217 |
+
"bin": {
|
| 218 |
+
"parser": "bin/babel-parser.js"
|
| 219 |
+
},
|
| 220 |
+
"engines": {
|
| 221 |
+
"node": ">=6.0.0"
|
| 222 |
+
}
|
| 223 |
+
},
|
| 224 |
+
"node_modules/@babel/plugin-transform-react-jsx-self": {
|
| 225 |
+
"version": "7.29.7",
|
| 226 |
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
|
| 227 |
+
"integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
|
| 228 |
+
"dev": true,
|
| 229 |
+
"license": "MIT",
|
| 230 |
+
"dependencies": {
|
| 231 |
+
"@babel/helper-plugin-utils": "^7.29.7"
|
| 232 |
+
},
|
| 233 |
+
"engines": {
|
| 234 |
+
"node": ">=6.9.0"
|
| 235 |
+
},
|
| 236 |
+
"peerDependencies": {
|
| 237 |
+
"@babel/core": "^7.0.0-0"
|
| 238 |
+
}
|
| 239 |
+
},
|
| 240 |
+
"node_modules/@babel/plugin-transform-react-jsx-source": {
|
| 241 |
+
"version": "7.29.7",
|
| 242 |
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
|
| 243 |
+
"integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
|
| 244 |
+
"dev": true,
|
| 245 |
+
"license": "MIT",
|
| 246 |
+
"dependencies": {
|
| 247 |
+
"@babel/helper-plugin-utils": "^7.29.7"
|
| 248 |
+
},
|
| 249 |
+
"engines": {
|
| 250 |
+
"node": ">=6.9.0"
|
| 251 |
+
},
|
| 252 |
+
"peerDependencies": {
|
| 253 |
+
"@babel/core": "^7.0.0-0"
|
| 254 |
+
}
|
| 255 |
+
},
|
| 256 |
+
"node_modules/@babel/runtime": {
|
| 257 |
+
"version": "7.29.7",
|
| 258 |
+
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
|
| 259 |
+
"integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
|
| 260 |
+
"license": "MIT",
|
| 261 |
+
"engines": {
|
| 262 |
+
"node": ">=6.9.0"
|
| 263 |
+
}
|
| 264 |
+
},
|
| 265 |
+
"node_modules/@babel/template": {
|
| 266 |
+
"version": "7.29.7",
|
| 267 |
+
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
|
| 268 |
+
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
|
| 269 |
+
"dev": true,
|
| 270 |
+
"license": "MIT",
|
| 271 |
+
"dependencies": {
|
| 272 |
+
"@babel/code-frame": "^7.29.7",
|
| 273 |
+
"@babel/parser": "^7.29.7",
|
| 274 |
+
"@babel/types": "^7.29.7"
|
| 275 |
+
},
|
| 276 |
+
"engines": {
|
| 277 |
+
"node": ">=6.9.0"
|
| 278 |
+
}
|
| 279 |
+
},
|
| 280 |
+
"node_modules/@babel/traverse": {
|
| 281 |
+
"version": "7.29.7",
|
| 282 |
+
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
|
| 283 |
+
"integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
|
| 284 |
+
"dev": true,
|
| 285 |
+
"license": "MIT",
|
| 286 |
+
"dependencies": {
|
| 287 |
+
"@babel/code-frame": "^7.29.7",
|
| 288 |
+
"@babel/generator": "^7.29.7",
|
| 289 |
+
"@babel/helper-globals": "^7.29.7",
|
| 290 |
+
"@babel/parser": "^7.29.7",
|
| 291 |
+
"@babel/template": "^7.29.7",
|
| 292 |
+
"@babel/types": "^7.29.7",
|
| 293 |
+
"debug": "^4.3.1"
|
| 294 |
+
},
|
| 295 |
+
"engines": {
|
| 296 |
+
"node": ">=6.9.0"
|
| 297 |
+
}
|
| 298 |
+
},
|
| 299 |
+
"node_modules/@babel/types": {
|
| 300 |
+
"version": "7.29.7",
|
| 301 |
+
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
| 302 |
+
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
| 303 |
+
"dev": true,
|
| 304 |
+
"license": "MIT",
|
| 305 |
+
"dependencies": {
|
| 306 |
+
"@babel/helper-string-parser": "^7.29.7",
|
| 307 |
+
"@babel/helper-validator-identifier": "^7.29.7"
|
| 308 |
+
},
|
| 309 |
+
"engines": {
|
| 310 |
+
"node": ">=6.9.0"
|
| 311 |
+
}
|
| 312 |
+
},
|
| 313 |
+
"node_modules/@esbuild/aix-ppc64": {
|
| 314 |
+
"version": "0.25.12",
|
| 315 |
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
| 316 |
+
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
| 317 |
+
"cpu": [
|
| 318 |
+
"ppc64"
|
| 319 |
+
],
|
| 320 |
+
"dev": true,
|
| 321 |
+
"license": "MIT",
|
| 322 |
+
"optional": true,
|
| 323 |
+
"os": [
|
| 324 |
+
"aix"
|
| 325 |
+
],
|
| 326 |
+
"engines": {
|
| 327 |
+
"node": ">=18"
|
| 328 |
+
}
|
| 329 |
+
},
|
| 330 |
+
"node_modules/@esbuild/android-arm": {
|
| 331 |
+
"version": "0.25.12",
|
| 332 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
|
| 333 |
+
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
|
| 334 |
+
"cpu": [
|
| 335 |
+
"arm"
|
| 336 |
+
],
|
| 337 |
+
"dev": true,
|
| 338 |
+
"license": "MIT",
|
| 339 |
+
"optional": true,
|
| 340 |
+
"os": [
|
| 341 |
+
"android"
|
| 342 |
+
],
|
| 343 |
+
"engines": {
|
| 344 |
+
"node": ">=18"
|
| 345 |
+
}
|
| 346 |
+
},
|
| 347 |
+
"node_modules/@esbuild/android-arm64": {
|
| 348 |
+
"version": "0.25.12",
|
| 349 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
|
| 350 |
+
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
|
| 351 |
+
"cpu": [
|
| 352 |
+
"arm64"
|
| 353 |
+
],
|
| 354 |
+
"dev": true,
|
| 355 |
+
"license": "MIT",
|
| 356 |
+
"optional": true,
|
| 357 |
+
"os": [
|
| 358 |
+
"android"
|
| 359 |
+
],
|
| 360 |
+
"engines": {
|
| 361 |
+
"node": ">=18"
|
| 362 |
+
}
|
| 363 |
+
},
|
| 364 |
+
"node_modules/@esbuild/android-x64": {
|
| 365 |
+
"version": "0.25.12",
|
| 366 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
|
| 367 |
+
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
|
| 368 |
+
"cpu": [
|
| 369 |
+
"x64"
|
| 370 |
+
],
|
| 371 |
+
"dev": true,
|
| 372 |
+
"license": "MIT",
|
| 373 |
+
"optional": true,
|
| 374 |
+
"os": [
|
| 375 |
+
"android"
|
| 376 |
+
],
|
| 377 |
+
"engines": {
|
| 378 |
+
"node": ">=18"
|
| 379 |
+
}
|
| 380 |
+
},
|
| 381 |
+
"node_modules/@esbuild/darwin-arm64": {
|
| 382 |
+
"version": "0.25.12",
|
| 383 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
|
| 384 |
+
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
|
| 385 |
+
"cpu": [
|
| 386 |
+
"arm64"
|
| 387 |
+
],
|
| 388 |
+
"dev": true,
|
| 389 |
+
"license": "MIT",
|
| 390 |
+
"optional": true,
|
| 391 |
+
"os": [
|
| 392 |
+
"darwin"
|
| 393 |
+
],
|
| 394 |
+
"engines": {
|
| 395 |
+
"node": ">=18"
|
| 396 |
+
}
|
| 397 |
+
},
|
| 398 |
+
"node_modules/@esbuild/darwin-x64": {
|
| 399 |
+
"version": "0.25.12",
|
| 400 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
|
| 401 |
+
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
|
| 402 |
+
"cpu": [
|
| 403 |
+
"x64"
|
| 404 |
+
],
|
| 405 |
+
"dev": true,
|
| 406 |
+
"license": "MIT",
|
| 407 |
+
"optional": true,
|
| 408 |
+
"os": [
|
| 409 |
+
"darwin"
|
| 410 |
+
],
|
| 411 |
+
"engines": {
|
| 412 |
+
"node": ">=18"
|
| 413 |
+
}
|
| 414 |
+
},
|
| 415 |
+
"node_modules/@esbuild/freebsd-arm64": {
|
| 416 |
+
"version": "0.25.12",
|
| 417 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
|
| 418 |
+
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
|
| 419 |
+
"cpu": [
|
| 420 |
+
"arm64"
|
| 421 |
+
],
|
| 422 |
+
"dev": true,
|
| 423 |
+
"license": "MIT",
|
| 424 |
+
"optional": true,
|
| 425 |
+
"os": [
|
| 426 |
+
"freebsd"
|
| 427 |
+
],
|
| 428 |
+
"engines": {
|
| 429 |
+
"node": ">=18"
|
| 430 |
+
}
|
| 431 |
+
},
|
| 432 |
+
"node_modules/@esbuild/freebsd-x64": {
|
| 433 |
+
"version": "0.25.12",
|
| 434 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
|
| 435 |
+
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
|
| 436 |
+
"cpu": [
|
| 437 |
+
"x64"
|
| 438 |
+
],
|
| 439 |
+
"dev": true,
|
| 440 |
+
"license": "MIT",
|
| 441 |
+
"optional": true,
|
| 442 |
+
"os": [
|
| 443 |
+
"freebsd"
|
| 444 |
+
],
|
| 445 |
+
"engines": {
|
| 446 |
+
"node": ">=18"
|
| 447 |
+
}
|
| 448 |
+
},
|
| 449 |
+
"node_modules/@esbuild/linux-arm": {
|
| 450 |
+
"version": "0.25.12",
|
| 451 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
|
| 452 |
+
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
|
| 453 |
+
"cpu": [
|
| 454 |
+
"arm"
|
| 455 |
+
],
|
| 456 |
+
"dev": true,
|
| 457 |
+
"license": "MIT",
|
| 458 |
+
"optional": true,
|
| 459 |
+
"os": [
|
| 460 |
+
"linux"
|
| 461 |
+
],
|
| 462 |
+
"engines": {
|
| 463 |
+
"node": ">=18"
|
| 464 |
+
}
|
| 465 |
+
},
|
| 466 |
+
"node_modules/@esbuild/linux-arm64": {
|
| 467 |
+
"version": "0.25.12",
|
| 468 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
|
| 469 |
+
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
|
| 470 |
+
"cpu": [
|
| 471 |
+
"arm64"
|
| 472 |
+
],
|
| 473 |
+
"dev": true,
|
| 474 |
+
"license": "MIT",
|
| 475 |
+
"optional": true,
|
| 476 |
+
"os": [
|
| 477 |
+
"linux"
|
| 478 |
+
],
|
| 479 |
+
"engines": {
|
| 480 |
+
"node": ">=18"
|
| 481 |
+
}
|
| 482 |
+
},
|
| 483 |
+
"node_modules/@esbuild/linux-ia32": {
|
| 484 |
+
"version": "0.25.12",
|
| 485 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
|
| 486 |
+
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
|
| 487 |
+
"cpu": [
|
| 488 |
+
"ia32"
|
| 489 |
+
],
|
| 490 |
+
"dev": true,
|
| 491 |
+
"license": "MIT",
|
| 492 |
+
"optional": true,
|
| 493 |
+
"os": [
|
| 494 |
+
"linux"
|
| 495 |
+
],
|
| 496 |
+
"engines": {
|
| 497 |
+
"node": ">=18"
|
| 498 |
+
}
|
| 499 |
+
},
|
| 500 |
+
"node_modules/@esbuild/linux-loong64": {
|
| 501 |
+
"version": "0.25.12",
|
| 502 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
|
| 503 |
+
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
|
| 504 |
+
"cpu": [
|
| 505 |
+
"loong64"
|
| 506 |
+
],
|
| 507 |
+
"dev": true,
|
| 508 |
+
"license": "MIT",
|
| 509 |
+
"optional": true,
|
| 510 |
+
"os": [
|
| 511 |
+
"linux"
|
| 512 |
+
],
|
| 513 |
+
"engines": {
|
| 514 |
+
"node": ">=18"
|
| 515 |
+
}
|
| 516 |
+
},
|
| 517 |
+
"node_modules/@esbuild/linux-mips64el": {
|
| 518 |
+
"version": "0.25.12",
|
| 519 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
|
| 520 |
+
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
|
| 521 |
+
"cpu": [
|
| 522 |
+
"mips64el"
|
| 523 |
+
],
|
| 524 |
+
"dev": true,
|
| 525 |
+
"license": "MIT",
|
| 526 |
+
"optional": true,
|
| 527 |
+
"os": [
|
| 528 |
+
"linux"
|
| 529 |
+
],
|
| 530 |
+
"engines": {
|
| 531 |
+
"node": ">=18"
|
| 532 |
+
}
|
| 533 |
+
},
|
| 534 |
+
"node_modules/@esbuild/linux-ppc64": {
|
| 535 |
+
"version": "0.25.12",
|
| 536 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
|
| 537 |
+
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
|
| 538 |
+
"cpu": [
|
| 539 |
+
"ppc64"
|
| 540 |
+
],
|
| 541 |
+
"dev": true,
|
| 542 |
+
"license": "MIT",
|
| 543 |
+
"optional": true,
|
| 544 |
+
"os": [
|
| 545 |
+
"linux"
|
| 546 |
+
],
|
| 547 |
+
"engines": {
|
| 548 |
+
"node": ">=18"
|
| 549 |
+
}
|
| 550 |
+
},
|
| 551 |
+
"node_modules/@esbuild/linux-riscv64": {
|
| 552 |
+
"version": "0.25.12",
|
| 553 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
|
| 554 |
+
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
|
| 555 |
+
"cpu": [
|
| 556 |
+
"riscv64"
|
| 557 |
+
],
|
| 558 |
+
"dev": true,
|
| 559 |
+
"license": "MIT",
|
| 560 |
+
"optional": true,
|
| 561 |
+
"os": [
|
| 562 |
+
"linux"
|
| 563 |
+
],
|
| 564 |
+
"engines": {
|
| 565 |
+
"node": ">=18"
|
| 566 |
+
}
|
| 567 |
+
},
|
| 568 |
+
"node_modules/@esbuild/linux-s390x": {
|
| 569 |
+
"version": "0.25.12",
|
| 570 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
|
| 571 |
+
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
|
| 572 |
+
"cpu": [
|
| 573 |
+
"s390x"
|
| 574 |
+
],
|
| 575 |
+
"dev": true,
|
| 576 |
+
"license": "MIT",
|
| 577 |
+
"optional": true,
|
| 578 |
+
"os": [
|
| 579 |
+
"linux"
|
| 580 |
+
],
|
| 581 |
+
"engines": {
|
| 582 |
+
"node": ">=18"
|
| 583 |
+
}
|
| 584 |
+
},
|
| 585 |
+
"node_modules/@esbuild/linux-x64": {
|
| 586 |
+
"version": "0.25.12",
|
| 587 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
|
| 588 |
+
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
|
| 589 |
+
"cpu": [
|
| 590 |
+
"x64"
|
| 591 |
+
],
|
| 592 |
+
"dev": true,
|
| 593 |
+
"license": "MIT",
|
| 594 |
+
"optional": true,
|
| 595 |
+
"os": [
|
| 596 |
+
"linux"
|
| 597 |
+
],
|
| 598 |
+
"engines": {
|
| 599 |
+
"node": ">=18"
|
| 600 |
+
}
|
| 601 |
+
},
|
| 602 |
+
"node_modules/@esbuild/netbsd-arm64": {
|
| 603 |
+
"version": "0.25.12",
|
| 604 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
|
| 605 |
+
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
|
| 606 |
+
"cpu": [
|
| 607 |
+
"arm64"
|
| 608 |
+
],
|
| 609 |
+
"dev": true,
|
| 610 |
+
"license": "MIT",
|
| 611 |
+
"optional": true,
|
| 612 |
+
"os": [
|
| 613 |
+
"netbsd"
|
| 614 |
+
],
|
| 615 |
+
"engines": {
|
| 616 |
+
"node": ">=18"
|
| 617 |
+
}
|
| 618 |
+
},
|
| 619 |
+
"node_modules/@esbuild/netbsd-x64": {
|
| 620 |
+
"version": "0.25.12",
|
| 621 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
|
| 622 |
+
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
|
| 623 |
+
"cpu": [
|
| 624 |
+
"x64"
|
| 625 |
+
],
|
| 626 |
+
"dev": true,
|
| 627 |
+
"license": "MIT",
|
| 628 |
+
"optional": true,
|
| 629 |
+
"os": [
|
| 630 |
+
"netbsd"
|
| 631 |
+
],
|
| 632 |
+
"engines": {
|
| 633 |
+
"node": ">=18"
|
| 634 |
+
}
|
| 635 |
+
},
|
| 636 |
+
"node_modules/@esbuild/openbsd-arm64": {
|
| 637 |
+
"version": "0.25.12",
|
| 638 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
|
| 639 |
+
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
|
| 640 |
+
"cpu": [
|
| 641 |
+
"arm64"
|
| 642 |
+
],
|
| 643 |
+
"dev": true,
|
| 644 |
+
"license": "MIT",
|
| 645 |
+
"optional": true,
|
| 646 |
+
"os": [
|
| 647 |
+
"openbsd"
|
| 648 |
+
],
|
| 649 |
+
"engines": {
|
| 650 |
+
"node": ">=18"
|
| 651 |
+
}
|
| 652 |
+
},
|
| 653 |
+
"node_modules/@esbuild/openbsd-x64": {
|
| 654 |
+
"version": "0.25.12",
|
| 655 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
|
| 656 |
+
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
|
| 657 |
+
"cpu": [
|
| 658 |
+
"x64"
|
| 659 |
+
],
|
| 660 |
+
"dev": true,
|
| 661 |
+
"license": "MIT",
|
| 662 |
+
"optional": true,
|
| 663 |
+
"os": [
|
| 664 |
+
"openbsd"
|
| 665 |
+
],
|
| 666 |
+
"engines": {
|
| 667 |
+
"node": ">=18"
|
| 668 |
+
}
|
| 669 |
+
},
|
| 670 |
+
"node_modules/@esbuild/openharmony-arm64": {
|
| 671 |
+
"version": "0.25.12",
|
| 672 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
|
| 673 |
+
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
|
| 674 |
+
"cpu": [
|
| 675 |
+
"arm64"
|
| 676 |
+
],
|
| 677 |
+
"dev": true,
|
| 678 |
+
"license": "MIT",
|
| 679 |
+
"optional": true,
|
| 680 |
+
"os": [
|
| 681 |
+
"openharmony"
|
| 682 |
+
],
|
| 683 |
+
"engines": {
|
| 684 |
+
"node": ">=18"
|
| 685 |
+
}
|
| 686 |
+
},
|
| 687 |
+
"node_modules/@esbuild/sunos-x64": {
|
| 688 |
+
"version": "0.25.12",
|
| 689 |
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
|
| 690 |
+
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
|
| 691 |
+
"cpu": [
|
| 692 |
+
"x64"
|
| 693 |
+
],
|
| 694 |
+
"dev": true,
|
| 695 |
+
"license": "MIT",
|
| 696 |
+
"optional": true,
|
| 697 |
+
"os": [
|
| 698 |
+
"sunos"
|
| 699 |
+
],
|
| 700 |
+
"engines": {
|
| 701 |
+
"node": ">=18"
|
| 702 |
+
}
|
| 703 |
+
},
|
| 704 |
+
"node_modules/@esbuild/win32-arm64": {
|
| 705 |
+
"version": "0.25.12",
|
| 706 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
|
| 707 |
+
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
|
| 708 |
+
"cpu": [
|
| 709 |
+
"arm64"
|
| 710 |
+
],
|
| 711 |
+
"dev": true,
|
| 712 |
+
"license": "MIT",
|
| 713 |
+
"optional": true,
|
| 714 |
+
"os": [
|
| 715 |
+
"win32"
|
| 716 |
+
],
|
| 717 |
+
"engines": {
|
| 718 |
+
"node": ">=18"
|
| 719 |
+
}
|
| 720 |
+
},
|
| 721 |
+
"node_modules/@esbuild/win32-ia32": {
|
| 722 |
+
"version": "0.25.12",
|
| 723 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
|
| 724 |
+
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
|
| 725 |
+
"cpu": [
|
| 726 |
+
"ia32"
|
| 727 |
+
],
|
| 728 |
+
"dev": true,
|
| 729 |
+
"license": "MIT",
|
| 730 |
+
"optional": true,
|
| 731 |
+
"os": [
|
| 732 |
+
"win32"
|
| 733 |
+
],
|
| 734 |
+
"engines": {
|
| 735 |
+
"node": ">=18"
|
| 736 |
+
}
|
| 737 |
+
},
|
| 738 |
+
"node_modules/@esbuild/win32-x64": {
|
| 739 |
+
"version": "0.25.12",
|
| 740 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
|
| 741 |
+
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
|
| 742 |
+
"cpu": [
|
| 743 |
+
"x64"
|
| 744 |
+
],
|
| 745 |
+
"dev": true,
|
| 746 |
+
"license": "MIT",
|
| 747 |
+
"optional": true,
|
| 748 |
+
"os": [
|
| 749 |
+
"win32"
|
| 750 |
+
],
|
| 751 |
+
"engines": {
|
| 752 |
+
"node": ">=18"
|
| 753 |
+
}
|
| 754 |
+
},
|
| 755 |
+
"node_modules/@jridgewell/gen-mapping": {
|
| 756 |
+
"version": "0.3.13",
|
| 757 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
| 758 |
+
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
| 759 |
+
"dev": true,
|
| 760 |
+
"license": "MIT",
|
| 761 |
+
"dependencies": {
|
| 762 |
+
"@jridgewell/sourcemap-codec": "^1.5.0",
|
| 763 |
+
"@jridgewell/trace-mapping": "^0.3.24"
|
| 764 |
+
}
|
| 765 |
+
},
|
| 766 |
+
"node_modules/@jridgewell/remapping": {
|
| 767 |
+
"version": "2.3.5",
|
| 768 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
| 769 |
+
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
| 770 |
+
"dev": true,
|
| 771 |
+
"license": "MIT",
|
| 772 |
+
"dependencies": {
|
| 773 |
+
"@jridgewell/gen-mapping": "^0.3.5",
|
| 774 |
+
"@jridgewell/trace-mapping": "^0.3.24"
|
| 775 |
+
}
|
| 776 |
+
},
|
| 777 |
+
"node_modules/@jridgewell/resolve-uri": {
|
| 778 |
+
"version": "3.1.2",
|
| 779 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
| 780 |
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
| 781 |
+
"dev": true,
|
| 782 |
+
"license": "MIT",
|
| 783 |
+
"engines": {
|
| 784 |
+
"node": ">=6.0.0"
|
| 785 |
+
}
|
| 786 |
+
},
|
| 787 |
+
"node_modules/@jridgewell/sourcemap-codec": {
|
| 788 |
+
"version": "1.5.5",
|
| 789 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
| 790 |
+
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
| 791 |
+
"dev": true,
|
| 792 |
+
"license": "MIT"
|
| 793 |
+
},
|
| 794 |
+
"node_modules/@jridgewell/trace-mapping": {
|
| 795 |
+
"version": "0.3.31",
|
| 796 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
| 797 |
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
| 798 |
+
"dev": true,
|
| 799 |
+
"license": "MIT",
|
| 800 |
+
"dependencies": {
|
| 801 |
+
"@jridgewell/resolve-uri": "^3.1.0",
|
| 802 |
+
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 803 |
+
}
|
| 804 |
+
},
|
| 805 |
+
"node_modules/@rolldown/pluginutils": {
|
| 806 |
+
"version": "1.0.0-beta.27",
|
| 807 |
+
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
| 808 |
+
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
|
| 809 |
+
"dev": true,
|
| 810 |
+
"license": "MIT"
|
| 811 |
+
},
|
| 812 |
+
"node_modules/@rollup/rollup-android-arm-eabi": {
|
| 813 |
+
"version": "4.62.0",
|
| 814 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.0.tgz",
|
| 815 |
+
"integrity": "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==",
|
| 816 |
+
"cpu": [
|
| 817 |
+
"arm"
|
| 818 |
+
],
|
| 819 |
+
"dev": true,
|
| 820 |
+
"license": "MIT",
|
| 821 |
+
"optional": true,
|
| 822 |
+
"os": [
|
| 823 |
+
"android"
|
| 824 |
+
]
|
| 825 |
+
},
|
| 826 |
+
"node_modules/@rollup/rollup-android-arm64": {
|
| 827 |
+
"version": "4.62.0",
|
| 828 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.0.tgz",
|
| 829 |
+
"integrity": "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==",
|
| 830 |
+
"cpu": [
|
| 831 |
+
"arm64"
|
| 832 |
+
],
|
| 833 |
+
"dev": true,
|
| 834 |
+
"license": "MIT",
|
| 835 |
+
"optional": true,
|
| 836 |
+
"os": [
|
| 837 |
+
"android"
|
| 838 |
+
]
|
| 839 |
+
},
|
| 840 |
+
"node_modules/@rollup/rollup-darwin-arm64": {
|
| 841 |
+
"version": "4.62.0",
|
| 842 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.0.tgz",
|
| 843 |
+
"integrity": "sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==",
|
| 844 |
+
"cpu": [
|
| 845 |
+
"arm64"
|
| 846 |
+
],
|
| 847 |
+
"dev": true,
|
| 848 |
+
"license": "MIT",
|
| 849 |
+
"optional": true,
|
| 850 |
+
"os": [
|
| 851 |
+
"darwin"
|
| 852 |
+
]
|
| 853 |
+
},
|
| 854 |
+
"node_modules/@rollup/rollup-darwin-x64": {
|
| 855 |
+
"version": "4.62.0",
|
| 856 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.0.tgz",
|
| 857 |
+
"integrity": "sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==",
|
| 858 |
+
"cpu": [
|
| 859 |
+
"x64"
|
| 860 |
+
],
|
| 861 |
+
"dev": true,
|
| 862 |
+
"license": "MIT",
|
| 863 |
+
"optional": true,
|
| 864 |
+
"os": [
|
| 865 |
+
"darwin"
|
| 866 |
+
]
|
| 867 |
+
},
|
| 868 |
+
"node_modules/@rollup/rollup-freebsd-arm64": {
|
| 869 |
+
"version": "4.62.0",
|
| 870 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.0.tgz",
|
| 871 |
+
"integrity": "sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==",
|
| 872 |
+
"cpu": [
|
| 873 |
+
"arm64"
|
| 874 |
+
],
|
| 875 |
+
"dev": true,
|
| 876 |
+
"license": "MIT",
|
| 877 |
+
"optional": true,
|
| 878 |
+
"os": [
|
| 879 |
+
"freebsd"
|
| 880 |
+
]
|
| 881 |
+
},
|
| 882 |
+
"node_modules/@rollup/rollup-freebsd-x64": {
|
| 883 |
+
"version": "4.62.0",
|
| 884 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.0.tgz",
|
| 885 |
+
"integrity": "sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==",
|
| 886 |
+
"cpu": [
|
| 887 |
+
"x64"
|
| 888 |
+
],
|
| 889 |
+
"dev": true,
|
| 890 |
+
"license": "MIT",
|
| 891 |
+
"optional": true,
|
| 892 |
+
"os": [
|
| 893 |
+
"freebsd"
|
| 894 |
+
]
|
| 895 |
+
},
|
| 896 |
+
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
| 897 |
+
"version": "4.62.0",
|
| 898 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.0.tgz",
|
| 899 |
+
"integrity": "sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==",
|
| 900 |
+
"cpu": [
|
| 901 |
+
"arm"
|
| 902 |
+
],
|
| 903 |
+
"dev": true,
|
| 904 |
+
"license": "MIT",
|
| 905 |
+
"optional": true,
|
| 906 |
+
"os": [
|
| 907 |
+
"linux"
|
| 908 |
+
]
|
| 909 |
+
},
|
| 910 |
+
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
| 911 |
+
"version": "4.62.0",
|
| 912 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.0.tgz",
|
| 913 |
+
"integrity": "sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==",
|
| 914 |
+
"cpu": [
|
| 915 |
+
"arm"
|
| 916 |
+
],
|
| 917 |
+
"dev": true,
|
| 918 |
+
"license": "MIT",
|
| 919 |
+
"optional": true,
|
| 920 |
+
"os": [
|
| 921 |
+
"linux"
|
| 922 |
+
]
|
| 923 |
+
},
|
| 924 |
+
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
| 925 |
+
"version": "4.62.0",
|
| 926 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.0.tgz",
|
| 927 |
+
"integrity": "sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==",
|
| 928 |
+
"cpu": [
|
| 929 |
+
"arm64"
|
| 930 |
+
],
|
| 931 |
+
"dev": true,
|
| 932 |
+
"license": "MIT",
|
| 933 |
+
"optional": true,
|
| 934 |
+
"os": [
|
| 935 |
+
"linux"
|
| 936 |
+
]
|
| 937 |
+
},
|
| 938 |
+
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
| 939 |
+
"version": "4.62.0",
|
| 940 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.0.tgz",
|
| 941 |
+
"integrity": "sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==",
|
| 942 |
+
"cpu": [
|
| 943 |
+
"arm64"
|
| 944 |
+
],
|
| 945 |
+
"dev": true,
|
| 946 |
+
"license": "MIT",
|
| 947 |
+
"optional": true,
|
| 948 |
+
"os": [
|
| 949 |
+
"linux"
|
| 950 |
+
]
|
| 951 |
+
},
|
| 952 |
+
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
| 953 |
+
"version": "4.62.0",
|
| 954 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.0.tgz",
|
| 955 |
+
"integrity": "sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==",
|
| 956 |
+
"cpu": [
|
| 957 |
+
"loong64"
|
| 958 |
+
],
|
| 959 |
+
"dev": true,
|
| 960 |
+
"license": "MIT",
|
| 961 |
+
"optional": true,
|
| 962 |
+
"os": [
|
| 963 |
+
"linux"
|
| 964 |
+
]
|
| 965 |
+
},
|
| 966 |
+
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
| 967 |
+
"version": "4.62.0",
|
| 968 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.0.tgz",
|
| 969 |
+
"integrity": "sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==",
|
| 970 |
+
"cpu": [
|
| 971 |
+
"loong64"
|
| 972 |
+
],
|
| 973 |
+
"dev": true,
|
| 974 |
+
"license": "MIT",
|
| 975 |
+
"optional": true,
|
| 976 |
+
"os": [
|
| 977 |
+
"linux"
|
| 978 |
+
]
|
| 979 |
+
},
|
| 980 |
+
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
| 981 |
+
"version": "4.62.0",
|
| 982 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.0.tgz",
|
| 983 |
+
"integrity": "sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==",
|
| 984 |
+
"cpu": [
|
| 985 |
+
"ppc64"
|
| 986 |
+
],
|
| 987 |
+
"dev": true,
|
| 988 |
+
"license": "MIT",
|
| 989 |
+
"optional": true,
|
| 990 |
+
"os": [
|
| 991 |
+
"linux"
|
| 992 |
+
]
|
| 993 |
+
},
|
| 994 |
+
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
| 995 |
+
"version": "4.62.0",
|
| 996 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.0.tgz",
|
| 997 |
+
"integrity": "sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==",
|
| 998 |
+
"cpu": [
|
| 999 |
+
"ppc64"
|
| 1000 |
+
],
|
| 1001 |
+
"dev": true,
|
| 1002 |
+
"license": "MIT",
|
| 1003 |
+
"optional": true,
|
| 1004 |
+
"os": [
|
| 1005 |
+
"linux"
|
| 1006 |
+
]
|
| 1007 |
+
},
|
| 1008 |
+
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
| 1009 |
+
"version": "4.62.0",
|
| 1010 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.0.tgz",
|
| 1011 |
+
"integrity": "sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==",
|
| 1012 |
+
"cpu": [
|
| 1013 |
+
"riscv64"
|
| 1014 |
+
],
|
| 1015 |
+
"dev": true,
|
| 1016 |
+
"license": "MIT",
|
| 1017 |
+
"optional": true,
|
| 1018 |
+
"os": [
|
| 1019 |
+
"linux"
|
| 1020 |
+
]
|
| 1021 |
+
},
|
| 1022 |
+
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
| 1023 |
+
"version": "4.62.0",
|
| 1024 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.0.tgz",
|
| 1025 |
+
"integrity": "sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==",
|
| 1026 |
+
"cpu": [
|
| 1027 |
+
"riscv64"
|
| 1028 |
+
],
|
| 1029 |
+
"dev": true,
|
| 1030 |
+
"license": "MIT",
|
| 1031 |
+
"optional": true,
|
| 1032 |
+
"os": [
|
| 1033 |
+
"linux"
|
| 1034 |
+
]
|
| 1035 |
+
},
|
| 1036 |
+
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
| 1037 |
+
"version": "4.62.0",
|
| 1038 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.0.tgz",
|
| 1039 |
+
"integrity": "sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==",
|
| 1040 |
+
"cpu": [
|
| 1041 |
+
"s390x"
|
| 1042 |
+
],
|
| 1043 |
+
"dev": true,
|
| 1044 |
+
"license": "MIT",
|
| 1045 |
+
"optional": true,
|
| 1046 |
+
"os": [
|
| 1047 |
+
"linux"
|
| 1048 |
+
]
|
| 1049 |
+
},
|
| 1050 |
+
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
| 1051 |
+
"version": "4.62.0",
|
| 1052 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.0.tgz",
|
| 1053 |
+
"integrity": "sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==",
|
| 1054 |
+
"cpu": [
|
| 1055 |
+
"x64"
|
| 1056 |
+
],
|
| 1057 |
+
"dev": true,
|
| 1058 |
+
"license": "MIT",
|
| 1059 |
+
"optional": true,
|
| 1060 |
+
"os": [
|
| 1061 |
+
"linux"
|
| 1062 |
+
]
|
| 1063 |
+
},
|
| 1064 |
+
"node_modules/@rollup/rollup-linux-x64-musl": {
|
| 1065 |
+
"version": "4.62.0",
|
| 1066 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.0.tgz",
|
| 1067 |
+
"integrity": "sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==",
|
| 1068 |
+
"cpu": [
|
| 1069 |
+
"x64"
|
| 1070 |
+
],
|
| 1071 |
+
"dev": true,
|
| 1072 |
+
"license": "MIT",
|
| 1073 |
+
"optional": true,
|
| 1074 |
+
"os": [
|
| 1075 |
+
"linux"
|
| 1076 |
+
]
|
| 1077 |
+
},
|
| 1078 |
+
"node_modules/@rollup/rollup-openbsd-x64": {
|
| 1079 |
+
"version": "4.62.0",
|
| 1080 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.0.tgz",
|
| 1081 |
+
"integrity": "sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==",
|
| 1082 |
+
"cpu": [
|
| 1083 |
+
"x64"
|
| 1084 |
+
],
|
| 1085 |
+
"dev": true,
|
| 1086 |
+
"license": "MIT",
|
| 1087 |
+
"optional": true,
|
| 1088 |
+
"os": [
|
| 1089 |
+
"openbsd"
|
| 1090 |
+
]
|
| 1091 |
+
},
|
| 1092 |
+
"node_modules/@rollup/rollup-openharmony-arm64": {
|
| 1093 |
+
"version": "4.62.0",
|
| 1094 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.0.tgz",
|
| 1095 |
+
"integrity": "sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==",
|
| 1096 |
+
"cpu": [
|
| 1097 |
+
"arm64"
|
| 1098 |
+
],
|
| 1099 |
+
"dev": true,
|
| 1100 |
+
"license": "MIT",
|
| 1101 |
+
"optional": true,
|
| 1102 |
+
"os": [
|
| 1103 |
+
"openharmony"
|
| 1104 |
+
]
|
| 1105 |
+
},
|
| 1106 |
+
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
| 1107 |
+
"version": "4.62.0",
|
| 1108 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.0.tgz",
|
| 1109 |
+
"integrity": "sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==",
|
| 1110 |
+
"cpu": [
|
| 1111 |
+
"arm64"
|
| 1112 |
+
],
|
| 1113 |
+
"dev": true,
|
| 1114 |
+
"license": "MIT",
|
| 1115 |
+
"optional": true,
|
| 1116 |
+
"os": [
|
| 1117 |
+
"win32"
|
| 1118 |
+
]
|
| 1119 |
+
},
|
| 1120 |
+
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
| 1121 |
+
"version": "4.62.0",
|
| 1122 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.0.tgz",
|
| 1123 |
+
"integrity": "sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==",
|
| 1124 |
+
"cpu": [
|
| 1125 |
+
"ia32"
|
| 1126 |
+
],
|
| 1127 |
+
"dev": true,
|
| 1128 |
+
"license": "MIT",
|
| 1129 |
+
"optional": true,
|
| 1130 |
+
"os": [
|
| 1131 |
+
"win32"
|
| 1132 |
+
]
|
| 1133 |
+
},
|
| 1134 |
+
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
| 1135 |
+
"version": "4.62.0",
|
| 1136 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.0.tgz",
|
| 1137 |
+
"integrity": "sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==",
|
| 1138 |
+
"cpu": [
|
| 1139 |
+
"x64"
|
| 1140 |
+
],
|
| 1141 |
+
"dev": true,
|
| 1142 |
+
"license": "MIT",
|
| 1143 |
+
"optional": true,
|
| 1144 |
+
"os": [
|
| 1145 |
+
"win32"
|
| 1146 |
+
]
|
| 1147 |
+
},
|
| 1148 |
+
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
| 1149 |
+
"version": "4.62.0",
|
| 1150 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.0.tgz",
|
| 1151 |
+
"integrity": "sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==",
|
| 1152 |
+
"cpu": [
|
| 1153 |
+
"x64"
|
| 1154 |
+
],
|
| 1155 |
+
"dev": true,
|
| 1156 |
+
"license": "MIT",
|
| 1157 |
+
"optional": true,
|
| 1158 |
+
"os": [
|
| 1159 |
+
"win32"
|
| 1160 |
+
]
|
| 1161 |
+
},
|
| 1162 |
+
"node_modules/@tailwindcss/node": {
|
| 1163 |
+
"version": "4.3.1",
|
| 1164 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz",
|
| 1165 |
+
"integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==",
|
| 1166 |
+
"dev": true,
|
| 1167 |
+
"license": "MIT",
|
| 1168 |
+
"dependencies": {
|
| 1169 |
+
"@jridgewell/remapping": "^2.3.5",
|
| 1170 |
+
"enhanced-resolve": "5.21.6",
|
| 1171 |
+
"jiti": "^2.7.0",
|
| 1172 |
+
"lightningcss": "1.32.0",
|
| 1173 |
+
"magic-string": "^0.30.21",
|
| 1174 |
+
"source-map-js": "^1.2.1",
|
| 1175 |
+
"tailwindcss": "4.3.1"
|
| 1176 |
+
}
|
| 1177 |
+
},
|
| 1178 |
+
"node_modules/@tailwindcss/oxide": {
|
| 1179 |
+
"version": "4.3.1",
|
| 1180 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz",
|
| 1181 |
+
"integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==",
|
| 1182 |
+
"dev": true,
|
| 1183 |
+
"license": "MIT",
|
| 1184 |
+
"engines": {
|
| 1185 |
+
"node": ">= 20"
|
| 1186 |
+
},
|
| 1187 |
+
"optionalDependencies": {
|
| 1188 |
+
"@tailwindcss/oxide-android-arm64": "4.3.1",
|
| 1189 |
+
"@tailwindcss/oxide-darwin-arm64": "4.3.1",
|
| 1190 |
+
"@tailwindcss/oxide-darwin-x64": "4.3.1",
|
| 1191 |
+
"@tailwindcss/oxide-freebsd-x64": "4.3.1",
|
| 1192 |
+
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1",
|
| 1193 |
+
"@tailwindcss/oxide-linux-arm64-gnu": "4.3.1",
|
| 1194 |
+
"@tailwindcss/oxide-linux-arm64-musl": "4.3.1",
|
| 1195 |
+
"@tailwindcss/oxide-linux-x64-gnu": "4.3.1",
|
| 1196 |
+
"@tailwindcss/oxide-linux-x64-musl": "4.3.1",
|
| 1197 |
+
"@tailwindcss/oxide-wasm32-wasi": "4.3.1",
|
| 1198 |
+
"@tailwindcss/oxide-win32-arm64-msvc": "4.3.1",
|
| 1199 |
+
"@tailwindcss/oxide-win32-x64-msvc": "4.3.1"
|
| 1200 |
+
}
|
| 1201 |
+
},
|
| 1202 |
+
"node_modules/@tailwindcss/oxide-android-arm64": {
|
| 1203 |
+
"version": "4.3.1",
|
| 1204 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz",
|
| 1205 |
+
"integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==",
|
| 1206 |
+
"cpu": [
|
| 1207 |
+
"arm64"
|
| 1208 |
+
],
|
| 1209 |
+
"dev": true,
|
| 1210 |
+
"license": "MIT",
|
| 1211 |
+
"optional": true,
|
| 1212 |
+
"os": [
|
| 1213 |
+
"android"
|
| 1214 |
+
],
|
| 1215 |
+
"engines": {
|
| 1216 |
+
"node": ">= 20"
|
| 1217 |
+
}
|
| 1218 |
+
},
|
| 1219 |
+
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
| 1220 |
+
"version": "4.3.1",
|
| 1221 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz",
|
| 1222 |
+
"integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==",
|
| 1223 |
+
"cpu": [
|
| 1224 |
+
"arm64"
|
| 1225 |
+
],
|
| 1226 |
+
"dev": true,
|
| 1227 |
+
"license": "MIT",
|
| 1228 |
+
"optional": true,
|
| 1229 |
+
"os": [
|
| 1230 |
+
"darwin"
|
| 1231 |
+
],
|
| 1232 |
+
"engines": {
|
| 1233 |
+
"node": ">= 20"
|
| 1234 |
+
}
|
| 1235 |
+
},
|
| 1236 |
+
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
| 1237 |
+
"version": "4.3.1",
|
| 1238 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz",
|
| 1239 |
+
"integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==",
|
| 1240 |
+
"cpu": [
|
| 1241 |
+
"x64"
|
| 1242 |
+
],
|
| 1243 |
+
"dev": true,
|
| 1244 |
+
"license": "MIT",
|
| 1245 |
+
"optional": true,
|
| 1246 |
+
"os": [
|
| 1247 |
+
"darwin"
|
| 1248 |
+
],
|
| 1249 |
+
"engines": {
|
| 1250 |
+
"node": ">= 20"
|
| 1251 |
+
}
|
| 1252 |
+
},
|
| 1253 |
+
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
| 1254 |
+
"version": "4.3.1",
|
| 1255 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz",
|
| 1256 |
+
"integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==",
|
| 1257 |
+
"cpu": [
|
| 1258 |
+
"x64"
|
| 1259 |
+
],
|
| 1260 |
+
"dev": true,
|
| 1261 |
+
"license": "MIT",
|
| 1262 |
+
"optional": true,
|
| 1263 |
+
"os": [
|
| 1264 |
+
"freebsd"
|
| 1265 |
+
],
|
| 1266 |
+
"engines": {
|
| 1267 |
+
"node": ">= 20"
|
| 1268 |
+
}
|
| 1269 |
+
},
|
| 1270 |
+
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
| 1271 |
+
"version": "4.3.1",
|
| 1272 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz",
|
| 1273 |
+
"integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==",
|
| 1274 |
+
"cpu": [
|
| 1275 |
+
"arm"
|
| 1276 |
+
],
|
| 1277 |
+
"dev": true,
|
| 1278 |
+
"license": "MIT",
|
| 1279 |
+
"optional": true,
|
| 1280 |
+
"os": [
|
| 1281 |
+
"linux"
|
| 1282 |
+
],
|
| 1283 |
+
"engines": {
|
| 1284 |
+
"node": ">= 20"
|
| 1285 |
+
}
|
| 1286 |
+
},
|
| 1287 |
+
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
| 1288 |
+
"version": "4.3.1",
|
| 1289 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz",
|
| 1290 |
+
"integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==",
|
| 1291 |
+
"cpu": [
|
| 1292 |
+
"arm64"
|
| 1293 |
+
],
|
| 1294 |
+
"dev": true,
|
| 1295 |
+
"license": "MIT",
|
| 1296 |
+
"optional": true,
|
| 1297 |
+
"os": [
|
| 1298 |
+
"linux"
|
| 1299 |
+
],
|
| 1300 |
+
"engines": {
|
| 1301 |
+
"node": ">= 20"
|
| 1302 |
+
}
|
| 1303 |
+
},
|
| 1304 |
+
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
| 1305 |
+
"version": "4.3.1",
|
| 1306 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz",
|
| 1307 |
+
"integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==",
|
| 1308 |
+
"cpu": [
|
| 1309 |
+
"arm64"
|
| 1310 |
+
],
|
| 1311 |
+
"dev": true,
|
| 1312 |
+
"license": "MIT",
|
| 1313 |
+
"optional": true,
|
| 1314 |
+
"os": [
|
| 1315 |
+
"linux"
|
| 1316 |
+
],
|
| 1317 |
+
"engines": {
|
| 1318 |
+
"node": ">= 20"
|
| 1319 |
+
}
|
| 1320 |
+
},
|
| 1321 |
+
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
| 1322 |
+
"version": "4.3.1",
|
| 1323 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz",
|
| 1324 |
+
"integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==",
|
| 1325 |
+
"cpu": [
|
| 1326 |
+
"x64"
|
| 1327 |
+
],
|
| 1328 |
+
"dev": true,
|
| 1329 |
+
"license": "MIT",
|
| 1330 |
+
"optional": true,
|
| 1331 |
+
"os": [
|
| 1332 |
+
"linux"
|
| 1333 |
+
],
|
| 1334 |
+
"engines": {
|
| 1335 |
+
"node": ">= 20"
|
| 1336 |
+
}
|
| 1337 |
+
},
|
| 1338 |
+
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
| 1339 |
+
"version": "4.3.1",
|
| 1340 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz",
|
| 1341 |
+
"integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==",
|
| 1342 |
+
"cpu": [
|
| 1343 |
+
"x64"
|
| 1344 |
+
],
|
| 1345 |
+
"dev": true,
|
| 1346 |
+
"license": "MIT",
|
| 1347 |
+
"optional": true,
|
| 1348 |
+
"os": [
|
| 1349 |
+
"linux"
|
| 1350 |
+
],
|
| 1351 |
+
"engines": {
|
| 1352 |
+
"node": ">= 20"
|
| 1353 |
+
}
|
| 1354 |
+
},
|
| 1355 |
+
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
| 1356 |
+
"version": "4.3.1",
|
| 1357 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz",
|
| 1358 |
+
"integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==",
|
| 1359 |
+
"bundleDependencies": [
|
| 1360 |
+
"@napi-rs/wasm-runtime",
|
| 1361 |
+
"@emnapi/core",
|
| 1362 |
+
"@emnapi/runtime",
|
| 1363 |
+
"@tybys/wasm-util",
|
| 1364 |
+
"@emnapi/wasi-threads",
|
| 1365 |
+
"tslib"
|
| 1366 |
+
],
|
| 1367 |
+
"cpu": [
|
| 1368 |
+
"wasm32"
|
| 1369 |
+
],
|
| 1370 |
+
"dev": true,
|
| 1371 |
+
"license": "MIT",
|
| 1372 |
+
"optional": true,
|
| 1373 |
+
"dependencies": {
|
| 1374 |
+
"@emnapi/core": "^1.10.0",
|
| 1375 |
+
"@emnapi/runtime": "^1.10.0",
|
| 1376 |
+
"@emnapi/wasi-threads": "^1.2.1",
|
| 1377 |
+
"@napi-rs/wasm-runtime": "^1.1.4",
|
| 1378 |
+
"@tybys/wasm-util": "^0.10.2",
|
| 1379 |
+
"tslib": "^2.8.1"
|
| 1380 |
+
},
|
| 1381 |
+
"engines": {
|
| 1382 |
+
"node": ">=14.0.0"
|
| 1383 |
+
}
|
| 1384 |
+
},
|
| 1385 |
+
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
| 1386 |
+
"version": "4.3.1",
|
| 1387 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz",
|
| 1388 |
+
"integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==",
|
| 1389 |
+
"cpu": [
|
| 1390 |
+
"arm64"
|
| 1391 |
+
],
|
| 1392 |
+
"dev": true,
|
| 1393 |
+
"license": "MIT",
|
| 1394 |
+
"optional": true,
|
| 1395 |
+
"os": [
|
| 1396 |
+
"win32"
|
| 1397 |
+
],
|
| 1398 |
+
"engines": {
|
| 1399 |
+
"node": ">= 20"
|
| 1400 |
+
}
|
| 1401 |
+
},
|
| 1402 |
+
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
| 1403 |
+
"version": "4.3.1",
|
| 1404 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz",
|
| 1405 |
+
"integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==",
|
| 1406 |
+
"cpu": [
|
| 1407 |
+
"x64"
|
| 1408 |
+
],
|
| 1409 |
+
"dev": true,
|
| 1410 |
+
"license": "MIT",
|
| 1411 |
+
"optional": true,
|
| 1412 |
+
"os": [
|
| 1413 |
+
"win32"
|
| 1414 |
+
],
|
| 1415 |
+
"engines": {
|
| 1416 |
+
"node": ">= 20"
|
| 1417 |
+
}
|
| 1418 |
+
},
|
| 1419 |
+
"node_modules/@tailwindcss/vite": {
|
| 1420 |
+
"version": "4.3.1",
|
| 1421 |
+
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.1.tgz",
|
| 1422 |
+
"integrity": "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==",
|
| 1423 |
+
"dev": true,
|
| 1424 |
+
"license": "MIT",
|
| 1425 |
+
"dependencies": {
|
| 1426 |
+
"@tailwindcss/node": "4.3.1",
|
| 1427 |
+
"@tailwindcss/oxide": "4.3.1",
|
| 1428 |
+
"tailwindcss": "4.3.1"
|
| 1429 |
+
},
|
| 1430 |
+
"peerDependencies": {
|
| 1431 |
+
"vite": "^5.2.0 || ^6 || ^7 || ^8"
|
| 1432 |
+
}
|
| 1433 |
+
},
|
| 1434 |
+
"node_modules/@types/babel__core": {
|
| 1435 |
+
"version": "7.20.5",
|
| 1436 |
+
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
| 1437 |
+
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
|
| 1438 |
+
"dev": true,
|
| 1439 |
+
"license": "MIT",
|
| 1440 |
+
"dependencies": {
|
| 1441 |
+
"@babel/parser": "^7.20.7",
|
| 1442 |
+
"@babel/types": "^7.20.7",
|
| 1443 |
+
"@types/babel__generator": "*",
|
| 1444 |
+
"@types/babel__template": "*",
|
| 1445 |
+
"@types/babel__traverse": "*"
|
| 1446 |
+
}
|
| 1447 |
+
},
|
| 1448 |
+
"node_modules/@types/babel__generator": {
|
| 1449 |
+
"version": "7.27.0",
|
| 1450 |
+
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
|
| 1451 |
+
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
|
| 1452 |
+
"dev": true,
|
| 1453 |
+
"license": "MIT",
|
| 1454 |
+
"dependencies": {
|
| 1455 |
+
"@babel/types": "^7.0.0"
|
| 1456 |
+
}
|
| 1457 |
+
},
|
| 1458 |
+
"node_modules/@types/babel__template": {
|
| 1459 |
+
"version": "7.4.4",
|
| 1460 |
+
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
|
| 1461 |
+
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
|
| 1462 |
+
"dev": true,
|
| 1463 |
+
"license": "MIT",
|
| 1464 |
+
"dependencies": {
|
| 1465 |
+
"@babel/parser": "^7.1.0",
|
| 1466 |
+
"@babel/types": "^7.0.0"
|
| 1467 |
+
}
|
| 1468 |
+
},
|
| 1469 |
+
"node_modules/@types/babel__traverse": {
|
| 1470 |
+
"version": "7.28.0",
|
| 1471 |
+
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
|
| 1472 |
+
"integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
|
| 1473 |
+
"dev": true,
|
| 1474 |
+
"license": "MIT",
|
| 1475 |
+
"dependencies": {
|
| 1476 |
+
"@babel/types": "^7.28.2"
|
| 1477 |
+
}
|
| 1478 |
+
},
|
| 1479 |
+
"node_modules/@types/d3-array": {
|
| 1480 |
+
"version": "3.2.2",
|
| 1481 |
+
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
|
| 1482 |
+
"integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
|
| 1483 |
+
"license": "MIT"
|
| 1484 |
+
},
|
| 1485 |
+
"node_modules/@types/d3-color": {
|
| 1486 |
+
"version": "3.1.3",
|
| 1487 |
+
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
| 1488 |
+
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
|
| 1489 |
+
"license": "MIT"
|
| 1490 |
+
},
|
| 1491 |
+
"node_modules/@types/d3-ease": {
|
| 1492 |
+
"version": "3.0.2",
|
| 1493 |
+
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
| 1494 |
+
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
|
| 1495 |
+
"license": "MIT"
|
| 1496 |
+
},
|
| 1497 |
+
"node_modules/@types/d3-interpolate": {
|
| 1498 |
+
"version": "3.0.4",
|
| 1499 |
+
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
| 1500 |
+
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
| 1501 |
+
"license": "MIT",
|
| 1502 |
+
"dependencies": {
|
| 1503 |
+
"@types/d3-color": "*"
|
| 1504 |
+
}
|
| 1505 |
+
},
|
| 1506 |
+
"node_modules/@types/d3-path": {
|
| 1507 |
+
"version": "3.1.1",
|
| 1508 |
+
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
|
| 1509 |
+
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
|
| 1510 |
+
"license": "MIT"
|
| 1511 |
+
},
|
| 1512 |
+
"node_modules/@types/d3-scale": {
|
| 1513 |
+
"version": "4.0.9",
|
| 1514 |
+
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
|
| 1515 |
+
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
|
| 1516 |
+
"license": "MIT",
|
| 1517 |
+
"dependencies": {
|
| 1518 |
+
"@types/d3-time": "*"
|
| 1519 |
+
}
|
| 1520 |
+
},
|
| 1521 |
+
"node_modules/@types/d3-shape": {
|
| 1522 |
+
"version": "3.1.8",
|
| 1523 |
+
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
|
| 1524 |
+
"integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
|
| 1525 |
+
"license": "MIT",
|
| 1526 |
+
"dependencies": {
|
| 1527 |
+
"@types/d3-path": "*"
|
| 1528 |
+
}
|
| 1529 |
+
},
|
| 1530 |
+
"node_modules/@types/d3-time": {
|
| 1531 |
+
"version": "3.0.4",
|
| 1532 |
+
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
|
| 1533 |
+
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
|
| 1534 |
+
"license": "MIT"
|
| 1535 |
+
},
|
| 1536 |
+
"node_modules/@types/d3-timer": {
|
| 1537 |
+
"version": "3.0.2",
|
| 1538 |
+
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
| 1539 |
+
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
|
| 1540 |
+
"license": "MIT"
|
| 1541 |
+
},
|
| 1542 |
+
"node_modules/@types/estree": {
|
| 1543 |
+
"version": "1.0.9",
|
| 1544 |
+
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
|
| 1545 |
+
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
|
| 1546 |
+
"dev": true,
|
| 1547 |
+
"license": "MIT"
|
| 1548 |
+
},
|
| 1549 |
+
"node_modules/@vitejs/plugin-react": {
|
| 1550 |
+
"version": "4.7.0",
|
| 1551 |
+
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
|
| 1552 |
+
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
|
| 1553 |
+
"dev": true,
|
| 1554 |
+
"license": "MIT",
|
| 1555 |
+
"dependencies": {
|
| 1556 |
+
"@babel/core": "^7.28.0",
|
| 1557 |
+
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
| 1558 |
+
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
| 1559 |
+
"@rolldown/pluginutils": "1.0.0-beta.27",
|
| 1560 |
+
"@types/babel__core": "^7.20.5",
|
| 1561 |
+
"react-refresh": "^0.17.0"
|
| 1562 |
+
},
|
| 1563 |
+
"engines": {
|
| 1564 |
+
"node": "^14.18.0 || >=16.0.0"
|
| 1565 |
+
},
|
| 1566 |
+
"peerDependencies": {
|
| 1567 |
+
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
| 1568 |
+
}
|
| 1569 |
+
},
|
| 1570 |
+
"node_modules/baseline-browser-mapping": {
|
| 1571 |
+
"version": "2.10.37",
|
| 1572 |
+
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz",
|
| 1573 |
+
"integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==",
|
| 1574 |
+
"dev": true,
|
| 1575 |
+
"license": "Apache-2.0",
|
| 1576 |
+
"bin": {
|
| 1577 |
+
"baseline-browser-mapping": "dist/cli.cjs"
|
| 1578 |
+
},
|
| 1579 |
+
"engines": {
|
| 1580 |
+
"node": ">=6.0.0"
|
| 1581 |
+
}
|
| 1582 |
+
},
|
| 1583 |
+
"node_modules/browserslist": {
|
| 1584 |
+
"version": "4.28.2",
|
| 1585 |
+
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
|
| 1586 |
+
"integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
|
| 1587 |
+
"dev": true,
|
| 1588 |
+
"funding": [
|
| 1589 |
+
{
|
| 1590 |
+
"type": "opencollective",
|
| 1591 |
+
"url": "https://opencollective.com/browserslist"
|
| 1592 |
+
},
|
| 1593 |
+
{
|
| 1594 |
+
"type": "tidelift",
|
| 1595 |
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
| 1596 |
+
},
|
| 1597 |
+
{
|
| 1598 |
+
"type": "github",
|
| 1599 |
+
"url": "https://github.com/sponsors/ai"
|
| 1600 |
+
}
|
| 1601 |
+
],
|
| 1602 |
+
"license": "MIT",
|
| 1603 |
+
"dependencies": {
|
| 1604 |
+
"baseline-browser-mapping": "^2.10.12",
|
| 1605 |
+
"caniuse-lite": "^1.0.30001782",
|
| 1606 |
+
"electron-to-chromium": "^1.5.328",
|
| 1607 |
+
"node-releases": "^2.0.36",
|
| 1608 |
+
"update-browserslist-db": "^1.2.3"
|
| 1609 |
+
},
|
| 1610 |
+
"bin": {
|
| 1611 |
+
"browserslist": "cli.js"
|
| 1612 |
+
},
|
| 1613 |
+
"engines": {
|
| 1614 |
+
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
| 1615 |
+
}
|
| 1616 |
+
},
|
| 1617 |
+
"node_modules/caniuse-lite": {
|
| 1618 |
+
"version": "1.0.30001799",
|
| 1619 |
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
|
| 1620 |
+
"integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
|
| 1621 |
+
"dev": true,
|
| 1622 |
+
"funding": [
|
| 1623 |
+
{
|
| 1624 |
+
"type": "opencollective",
|
| 1625 |
+
"url": "https://opencollective.com/browserslist"
|
| 1626 |
+
},
|
| 1627 |
+
{
|
| 1628 |
+
"type": "tidelift",
|
| 1629 |
+
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
| 1630 |
+
},
|
| 1631 |
+
{
|
| 1632 |
+
"type": "github",
|
| 1633 |
+
"url": "https://github.com/sponsors/ai"
|
| 1634 |
+
}
|
| 1635 |
+
],
|
| 1636 |
+
"license": "CC-BY-4.0"
|
| 1637 |
+
},
|
| 1638 |
+
"node_modules/clsx": {
|
| 1639 |
+
"version": "2.1.1",
|
| 1640 |
+
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
| 1641 |
+
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
| 1642 |
+
"license": "MIT",
|
| 1643 |
+
"engines": {
|
| 1644 |
+
"node": ">=6"
|
| 1645 |
+
}
|
| 1646 |
+
},
|
| 1647 |
+
"node_modules/convert-source-map": {
|
| 1648 |
+
"version": "2.0.0",
|
| 1649 |
+
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
| 1650 |
+
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
| 1651 |
+
"dev": true,
|
| 1652 |
+
"license": "MIT"
|
| 1653 |
+
},
|
| 1654 |
+
"node_modules/csstype": {
|
| 1655 |
+
"version": "3.2.3",
|
| 1656 |
+
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
| 1657 |
+
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
| 1658 |
+
"license": "MIT"
|
| 1659 |
+
},
|
| 1660 |
+
"node_modules/d3-array": {
|
| 1661 |
+
"version": "3.2.4",
|
| 1662 |
+
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
| 1663 |
+
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
| 1664 |
+
"license": "ISC",
|
| 1665 |
+
"dependencies": {
|
| 1666 |
+
"internmap": "1 - 2"
|
| 1667 |
+
},
|
| 1668 |
+
"engines": {
|
| 1669 |
+
"node": ">=12"
|
| 1670 |
+
}
|
| 1671 |
+
},
|
| 1672 |
+
"node_modules/d3-color": {
|
| 1673 |
+
"version": "3.1.0",
|
| 1674 |
+
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
| 1675 |
+
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
| 1676 |
+
"license": "ISC",
|
| 1677 |
+
"engines": {
|
| 1678 |
+
"node": ">=12"
|
| 1679 |
+
}
|
| 1680 |
+
},
|
| 1681 |
+
"node_modules/d3-ease": {
|
| 1682 |
+
"version": "3.0.1",
|
| 1683 |
+
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
| 1684 |
+
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
| 1685 |
+
"license": "BSD-3-Clause",
|
| 1686 |
+
"engines": {
|
| 1687 |
+
"node": ">=12"
|
| 1688 |
+
}
|
| 1689 |
+
},
|
| 1690 |
+
"node_modules/d3-format": {
|
| 1691 |
+
"version": "3.1.2",
|
| 1692 |
+
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
|
| 1693 |
+
"integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
|
| 1694 |
+
"license": "ISC",
|
| 1695 |
+
"engines": {
|
| 1696 |
+
"node": ">=12"
|
| 1697 |
+
}
|
| 1698 |
+
},
|
| 1699 |
+
"node_modules/d3-interpolate": {
|
| 1700 |
+
"version": "3.0.1",
|
| 1701 |
+
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
| 1702 |
+
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
| 1703 |
+
"license": "ISC",
|
| 1704 |
+
"dependencies": {
|
| 1705 |
+
"d3-color": "1 - 3"
|
| 1706 |
+
},
|
| 1707 |
+
"engines": {
|
| 1708 |
+
"node": ">=12"
|
| 1709 |
+
}
|
| 1710 |
+
},
|
| 1711 |
+
"node_modules/d3-path": {
|
| 1712 |
+
"version": "3.1.0",
|
| 1713 |
+
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
| 1714 |
+
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
| 1715 |
+
"license": "ISC",
|
| 1716 |
+
"engines": {
|
| 1717 |
+
"node": ">=12"
|
| 1718 |
+
}
|
| 1719 |
+
},
|
| 1720 |
+
"node_modules/d3-scale": {
|
| 1721 |
+
"version": "4.0.2",
|
| 1722 |
+
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
| 1723 |
+
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
| 1724 |
+
"license": "ISC",
|
| 1725 |
+
"dependencies": {
|
| 1726 |
+
"d3-array": "2.10.0 - 3",
|
| 1727 |
+
"d3-format": "1 - 3",
|
| 1728 |
+
"d3-interpolate": "1.2.0 - 3",
|
| 1729 |
+
"d3-time": "2.1.1 - 3",
|
| 1730 |
+
"d3-time-format": "2 - 4"
|
| 1731 |
+
},
|
| 1732 |
+
"engines": {
|
| 1733 |
+
"node": ">=12"
|
| 1734 |
+
}
|
| 1735 |
+
},
|
| 1736 |
+
"node_modules/d3-shape": {
|
| 1737 |
+
"version": "3.2.0",
|
| 1738 |
+
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
| 1739 |
+
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
| 1740 |
+
"license": "ISC",
|
| 1741 |
+
"dependencies": {
|
| 1742 |
+
"d3-path": "^3.1.0"
|
| 1743 |
+
},
|
| 1744 |
+
"engines": {
|
| 1745 |
+
"node": ">=12"
|
| 1746 |
+
}
|
| 1747 |
+
},
|
| 1748 |
+
"node_modules/d3-time": {
|
| 1749 |
+
"version": "3.1.0",
|
| 1750 |
+
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
| 1751 |
+
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
| 1752 |
+
"license": "ISC",
|
| 1753 |
+
"dependencies": {
|
| 1754 |
+
"d3-array": "2 - 3"
|
| 1755 |
+
},
|
| 1756 |
+
"engines": {
|
| 1757 |
+
"node": ">=12"
|
| 1758 |
+
}
|
| 1759 |
+
},
|
| 1760 |
+
"node_modules/d3-time-format": {
|
| 1761 |
+
"version": "4.1.0",
|
| 1762 |
+
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
| 1763 |
+
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
| 1764 |
+
"license": "ISC",
|
| 1765 |
+
"dependencies": {
|
| 1766 |
+
"d3-time": "1 - 3"
|
| 1767 |
+
},
|
| 1768 |
+
"engines": {
|
| 1769 |
+
"node": ">=12"
|
| 1770 |
+
}
|
| 1771 |
+
},
|
| 1772 |
+
"node_modules/d3-timer": {
|
| 1773 |
+
"version": "3.0.1",
|
| 1774 |
+
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
| 1775 |
+
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
| 1776 |
+
"license": "ISC",
|
| 1777 |
+
"engines": {
|
| 1778 |
+
"node": ">=12"
|
| 1779 |
+
}
|
| 1780 |
+
},
|
| 1781 |
+
"node_modules/debug": {
|
| 1782 |
+
"version": "4.4.3",
|
| 1783 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
| 1784 |
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
| 1785 |
+
"dev": true,
|
| 1786 |
+
"license": "MIT",
|
| 1787 |
+
"dependencies": {
|
| 1788 |
+
"ms": "^2.1.3"
|
| 1789 |
+
},
|
| 1790 |
+
"engines": {
|
| 1791 |
+
"node": ">=6.0"
|
| 1792 |
+
},
|
| 1793 |
+
"peerDependenciesMeta": {
|
| 1794 |
+
"supports-color": {
|
| 1795 |
+
"optional": true
|
| 1796 |
+
}
|
| 1797 |
+
}
|
| 1798 |
+
},
|
| 1799 |
+
"node_modules/decimal.js-light": {
|
| 1800 |
+
"version": "2.5.1",
|
| 1801 |
+
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
| 1802 |
+
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
|
| 1803 |
+
"license": "MIT"
|
| 1804 |
+
},
|
| 1805 |
+
"node_modules/detect-libc": {
|
| 1806 |
+
"version": "2.1.2",
|
| 1807 |
+
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
| 1808 |
+
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
| 1809 |
+
"dev": true,
|
| 1810 |
+
"license": "Apache-2.0",
|
| 1811 |
+
"engines": {
|
| 1812 |
+
"node": ">=8"
|
| 1813 |
+
}
|
| 1814 |
+
},
|
| 1815 |
+
"node_modules/dom-helpers": {
|
| 1816 |
+
"version": "5.2.1",
|
| 1817 |
+
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
| 1818 |
+
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
| 1819 |
+
"license": "MIT",
|
| 1820 |
+
"dependencies": {
|
| 1821 |
+
"@babel/runtime": "^7.8.7",
|
| 1822 |
+
"csstype": "^3.0.2"
|
| 1823 |
+
}
|
| 1824 |
+
},
|
| 1825 |
+
"node_modules/electron-to-chromium": {
|
| 1826 |
+
"version": "1.5.375",
|
| 1827 |
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.375.tgz",
|
| 1828 |
+
"integrity": "sha512-ZWP5eB4BVPW/ZYo9252hQZHZ5XavtsTgpbhcmMmRwymavC5AsLWQWBPaKMeNd2LW0KGby5HPXvj7+sr4ta5j/Q==",
|
| 1829 |
+
"dev": true,
|
| 1830 |
+
"license": "ISC"
|
| 1831 |
+
},
|
| 1832 |
+
"node_modules/enhanced-resolve": {
|
| 1833 |
+
"version": "5.21.6",
|
| 1834 |
+
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz",
|
| 1835 |
+
"integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==",
|
| 1836 |
+
"dev": true,
|
| 1837 |
+
"license": "MIT",
|
| 1838 |
+
"dependencies": {
|
| 1839 |
+
"graceful-fs": "^4.2.4",
|
| 1840 |
+
"tapable": "^2.3.3"
|
| 1841 |
+
},
|
| 1842 |
+
"engines": {
|
| 1843 |
+
"node": ">=10.13.0"
|
| 1844 |
+
}
|
| 1845 |
+
},
|
| 1846 |
+
"node_modules/esbuild": {
|
| 1847 |
+
"version": "0.25.12",
|
| 1848 |
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
| 1849 |
+
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
| 1850 |
+
"dev": true,
|
| 1851 |
+
"hasInstallScript": true,
|
| 1852 |
+
"license": "MIT",
|
| 1853 |
+
"bin": {
|
| 1854 |
+
"esbuild": "bin/esbuild"
|
| 1855 |
+
},
|
| 1856 |
+
"engines": {
|
| 1857 |
+
"node": ">=18"
|
| 1858 |
+
},
|
| 1859 |
+
"optionalDependencies": {
|
| 1860 |
+
"@esbuild/aix-ppc64": "0.25.12",
|
| 1861 |
+
"@esbuild/android-arm": "0.25.12",
|
| 1862 |
+
"@esbuild/android-arm64": "0.25.12",
|
| 1863 |
+
"@esbuild/android-x64": "0.25.12",
|
| 1864 |
+
"@esbuild/darwin-arm64": "0.25.12",
|
| 1865 |
+
"@esbuild/darwin-x64": "0.25.12",
|
| 1866 |
+
"@esbuild/freebsd-arm64": "0.25.12",
|
| 1867 |
+
"@esbuild/freebsd-x64": "0.25.12",
|
| 1868 |
+
"@esbuild/linux-arm": "0.25.12",
|
| 1869 |
+
"@esbuild/linux-arm64": "0.25.12",
|
| 1870 |
+
"@esbuild/linux-ia32": "0.25.12",
|
| 1871 |
+
"@esbuild/linux-loong64": "0.25.12",
|
| 1872 |
+
"@esbuild/linux-mips64el": "0.25.12",
|
| 1873 |
+
"@esbuild/linux-ppc64": "0.25.12",
|
| 1874 |
+
"@esbuild/linux-riscv64": "0.25.12",
|
| 1875 |
+
"@esbuild/linux-s390x": "0.25.12",
|
| 1876 |
+
"@esbuild/linux-x64": "0.25.12",
|
| 1877 |
+
"@esbuild/netbsd-arm64": "0.25.12",
|
| 1878 |
+
"@esbuild/netbsd-x64": "0.25.12",
|
| 1879 |
+
"@esbuild/openbsd-arm64": "0.25.12",
|
| 1880 |
+
"@esbuild/openbsd-x64": "0.25.12",
|
| 1881 |
+
"@esbuild/openharmony-arm64": "0.25.12",
|
| 1882 |
+
"@esbuild/sunos-x64": "0.25.12",
|
| 1883 |
+
"@esbuild/win32-arm64": "0.25.12",
|
| 1884 |
+
"@esbuild/win32-ia32": "0.25.12",
|
| 1885 |
+
"@esbuild/win32-x64": "0.25.12"
|
| 1886 |
+
}
|
| 1887 |
+
},
|
| 1888 |
+
"node_modules/escalade": {
|
| 1889 |
+
"version": "3.2.0",
|
| 1890 |
+
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
| 1891 |
+
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
| 1892 |
+
"dev": true,
|
| 1893 |
+
"license": "MIT",
|
| 1894 |
+
"engines": {
|
| 1895 |
+
"node": ">=6"
|
| 1896 |
+
}
|
| 1897 |
+
},
|
| 1898 |
+
"node_modules/eventemitter3": {
|
| 1899 |
+
"version": "4.0.7",
|
| 1900 |
+
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
| 1901 |
+
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
| 1902 |
+
"license": "MIT"
|
| 1903 |
+
},
|
| 1904 |
+
"node_modules/fast-equals": {
|
| 1905 |
+
"version": "5.4.0",
|
| 1906 |
+
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz",
|
| 1907 |
+
"integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==",
|
| 1908 |
+
"license": "MIT",
|
| 1909 |
+
"engines": {
|
| 1910 |
+
"node": ">=6.0.0"
|
| 1911 |
+
}
|
| 1912 |
+
},
|
| 1913 |
+
"node_modules/fdir": {
|
| 1914 |
+
"version": "6.5.0",
|
| 1915 |
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
| 1916 |
+
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
| 1917 |
+
"dev": true,
|
| 1918 |
+
"license": "MIT",
|
| 1919 |
+
"engines": {
|
| 1920 |
+
"node": ">=12.0.0"
|
| 1921 |
+
},
|
| 1922 |
+
"peerDependencies": {
|
| 1923 |
+
"picomatch": "^3 || ^4"
|
| 1924 |
+
},
|
| 1925 |
+
"peerDependenciesMeta": {
|
| 1926 |
+
"picomatch": {
|
| 1927 |
+
"optional": true
|
| 1928 |
+
}
|
| 1929 |
+
}
|
| 1930 |
+
},
|
| 1931 |
+
"node_modules/fsevents": {
|
| 1932 |
+
"version": "2.3.3",
|
| 1933 |
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 1934 |
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 1935 |
+
"dev": true,
|
| 1936 |
+
"hasInstallScript": true,
|
| 1937 |
+
"license": "MIT",
|
| 1938 |
+
"optional": true,
|
| 1939 |
+
"os": [
|
| 1940 |
+
"darwin"
|
| 1941 |
+
],
|
| 1942 |
+
"engines": {
|
| 1943 |
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1944 |
+
}
|
| 1945 |
+
},
|
| 1946 |
+
"node_modules/gensync": {
|
| 1947 |
+
"version": "1.0.0-beta.2",
|
| 1948 |
+
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
| 1949 |
+
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
| 1950 |
+
"dev": true,
|
| 1951 |
+
"license": "MIT",
|
| 1952 |
+
"engines": {
|
| 1953 |
+
"node": ">=6.9.0"
|
| 1954 |
+
}
|
| 1955 |
+
},
|
| 1956 |
+
"node_modules/graceful-fs": {
|
| 1957 |
+
"version": "4.2.11",
|
| 1958 |
+
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
| 1959 |
+
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
| 1960 |
+
"dev": true,
|
| 1961 |
+
"license": "ISC"
|
| 1962 |
+
},
|
| 1963 |
+
"node_modules/internmap": {
|
| 1964 |
+
"version": "2.0.3",
|
| 1965 |
+
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
| 1966 |
+
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
| 1967 |
+
"license": "ISC",
|
| 1968 |
+
"engines": {
|
| 1969 |
+
"node": ">=12"
|
| 1970 |
+
}
|
| 1971 |
+
},
|
| 1972 |
+
"node_modules/jiti": {
|
| 1973 |
+
"version": "2.7.0",
|
| 1974 |
+
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
|
| 1975 |
+
"integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
|
| 1976 |
+
"dev": true,
|
| 1977 |
+
"license": "MIT",
|
| 1978 |
+
"bin": {
|
| 1979 |
+
"jiti": "lib/jiti-cli.mjs"
|
| 1980 |
+
}
|
| 1981 |
+
},
|
| 1982 |
+
"node_modules/js-tokens": {
|
| 1983 |
+
"version": "4.0.0",
|
| 1984 |
+
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
| 1985 |
+
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
| 1986 |
+
"license": "MIT"
|
| 1987 |
+
},
|
| 1988 |
+
"node_modules/jsesc": {
|
| 1989 |
+
"version": "3.1.0",
|
| 1990 |
+
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
| 1991 |
+
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
| 1992 |
+
"dev": true,
|
| 1993 |
+
"license": "MIT",
|
| 1994 |
+
"bin": {
|
| 1995 |
+
"jsesc": "bin/jsesc"
|
| 1996 |
+
},
|
| 1997 |
+
"engines": {
|
| 1998 |
+
"node": ">=6"
|
| 1999 |
+
}
|
| 2000 |
+
},
|
| 2001 |
+
"node_modules/json5": {
|
| 2002 |
+
"version": "2.2.3",
|
| 2003 |
+
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
| 2004 |
+
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
| 2005 |
+
"dev": true,
|
| 2006 |
+
"license": "MIT",
|
| 2007 |
+
"bin": {
|
| 2008 |
+
"json5": "lib/cli.js"
|
| 2009 |
+
},
|
| 2010 |
+
"engines": {
|
| 2011 |
+
"node": ">=6"
|
| 2012 |
+
}
|
| 2013 |
+
},
|
| 2014 |
+
"node_modules/lightningcss": {
|
| 2015 |
+
"version": "1.32.0",
|
| 2016 |
+
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
| 2017 |
+
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
|
| 2018 |
+
"dev": true,
|
| 2019 |
+
"license": "MPL-2.0",
|
| 2020 |
+
"dependencies": {
|
| 2021 |
+
"detect-libc": "^2.0.3"
|
| 2022 |
+
},
|
| 2023 |
+
"engines": {
|
| 2024 |
+
"node": ">= 12.0.0"
|
| 2025 |
+
},
|
| 2026 |
+
"funding": {
|
| 2027 |
+
"type": "opencollective",
|
| 2028 |
+
"url": "https://opencollective.com/parcel"
|
| 2029 |
+
},
|
| 2030 |
+
"optionalDependencies": {
|
| 2031 |
+
"lightningcss-android-arm64": "1.32.0",
|
| 2032 |
+
"lightningcss-darwin-arm64": "1.32.0",
|
| 2033 |
+
"lightningcss-darwin-x64": "1.32.0",
|
| 2034 |
+
"lightningcss-freebsd-x64": "1.32.0",
|
| 2035 |
+
"lightningcss-linux-arm-gnueabihf": "1.32.0",
|
| 2036 |
+
"lightningcss-linux-arm64-gnu": "1.32.0",
|
| 2037 |
+
"lightningcss-linux-arm64-musl": "1.32.0",
|
| 2038 |
+
"lightningcss-linux-x64-gnu": "1.32.0",
|
| 2039 |
+
"lightningcss-linux-x64-musl": "1.32.0",
|
| 2040 |
+
"lightningcss-win32-arm64-msvc": "1.32.0",
|
| 2041 |
+
"lightningcss-win32-x64-msvc": "1.32.0"
|
| 2042 |
+
}
|
| 2043 |
+
},
|
| 2044 |
+
"node_modules/lightningcss-android-arm64": {
|
| 2045 |
+
"version": "1.32.0",
|
| 2046 |
+
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
|
| 2047 |
+
"integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
|
| 2048 |
+
"cpu": [
|
| 2049 |
+
"arm64"
|
| 2050 |
+
],
|
| 2051 |
+
"dev": true,
|
| 2052 |
+
"license": "MPL-2.0",
|
| 2053 |
+
"optional": true,
|
| 2054 |
+
"os": [
|
| 2055 |
+
"android"
|
| 2056 |
+
],
|
| 2057 |
+
"engines": {
|
| 2058 |
+
"node": ">= 12.0.0"
|
| 2059 |
+
},
|
| 2060 |
+
"funding": {
|
| 2061 |
+
"type": "opencollective",
|
| 2062 |
+
"url": "https://opencollective.com/parcel"
|
| 2063 |
+
}
|
| 2064 |
+
},
|
| 2065 |
+
"node_modules/lightningcss-darwin-arm64": {
|
| 2066 |
+
"version": "1.32.0",
|
| 2067 |
+
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
|
| 2068 |
+
"integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
|
| 2069 |
+
"cpu": [
|
| 2070 |
+
"arm64"
|
| 2071 |
+
],
|
| 2072 |
+
"dev": true,
|
| 2073 |
+
"license": "MPL-2.0",
|
| 2074 |
+
"optional": true,
|
| 2075 |
+
"os": [
|
| 2076 |
+
"darwin"
|
| 2077 |
+
],
|
| 2078 |
+
"engines": {
|
| 2079 |
+
"node": ">= 12.0.0"
|
| 2080 |
+
},
|
| 2081 |
+
"funding": {
|
| 2082 |
+
"type": "opencollective",
|
| 2083 |
+
"url": "https://opencollective.com/parcel"
|
| 2084 |
+
}
|
| 2085 |
+
},
|
| 2086 |
+
"node_modules/lightningcss-darwin-x64": {
|
| 2087 |
+
"version": "1.32.0",
|
| 2088 |
+
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
|
| 2089 |
+
"integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
|
| 2090 |
+
"cpu": [
|
| 2091 |
+
"x64"
|
| 2092 |
+
],
|
| 2093 |
+
"dev": true,
|
| 2094 |
+
"license": "MPL-2.0",
|
| 2095 |
+
"optional": true,
|
| 2096 |
+
"os": [
|
| 2097 |
+
"darwin"
|
| 2098 |
+
],
|
| 2099 |
+
"engines": {
|
| 2100 |
+
"node": ">= 12.0.0"
|
| 2101 |
+
},
|
| 2102 |
+
"funding": {
|
| 2103 |
+
"type": "opencollective",
|
| 2104 |
+
"url": "https://opencollective.com/parcel"
|
| 2105 |
+
}
|
| 2106 |
+
},
|
| 2107 |
+
"node_modules/lightningcss-freebsd-x64": {
|
| 2108 |
+
"version": "1.32.0",
|
| 2109 |
+
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
|
| 2110 |
+
"integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
|
| 2111 |
+
"cpu": [
|
| 2112 |
+
"x64"
|
| 2113 |
+
],
|
| 2114 |
+
"dev": true,
|
| 2115 |
+
"license": "MPL-2.0",
|
| 2116 |
+
"optional": true,
|
| 2117 |
+
"os": [
|
| 2118 |
+
"freebsd"
|
| 2119 |
+
],
|
| 2120 |
+
"engines": {
|
| 2121 |
+
"node": ">= 12.0.0"
|
| 2122 |
+
},
|
| 2123 |
+
"funding": {
|
| 2124 |
+
"type": "opencollective",
|
| 2125 |
+
"url": "https://opencollective.com/parcel"
|
| 2126 |
+
}
|
| 2127 |
+
},
|
| 2128 |
+
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
| 2129 |
+
"version": "1.32.0",
|
| 2130 |
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
|
| 2131 |
+
"integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
|
| 2132 |
+
"cpu": [
|
| 2133 |
+
"arm"
|
| 2134 |
+
],
|
| 2135 |
+
"dev": true,
|
| 2136 |
+
"license": "MPL-2.0",
|
| 2137 |
+
"optional": true,
|
| 2138 |
+
"os": [
|
| 2139 |
+
"linux"
|
| 2140 |
+
],
|
| 2141 |
+
"engines": {
|
| 2142 |
+
"node": ">= 12.0.0"
|
| 2143 |
+
},
|
| 2144 |
+
"funding": {
|
| 2145 |
+
"type": "opencollective",
|
| 2146 |
+
"url": "https://opencollective.com/parcel"
|
| 2147 |
+
}
|
| 2148 |
+
},
|
| 2149 |
+
"node_modules/lightningcss-linux-arm64-gnu": {
|
| 2150 |
+
"version": "1.32.0",
|
| 2151 |
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
|
| 2152 |
+
"integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
|
| 2153 |
+
"cpu": [
|
| 2154 |
+
"arm64"
|
| 2155 |
+
],
|
| 2156 |
+
"dev": true,
|
| 2157 |
+
"license": "MPL-2.0",
|
| 2158 |
+
"optional": true,
|
| 2159 |
+
"os": [
|
| 2160 |
+
"linux"
|
| 2161 |
+
],
|
| 2162 |
+
"engines": {
|
| 2163 |
+
"node": ">= 12.0.0"
|
| 2164 |
+
},
|
| 2165 |
+
"funding": {
|
| 2166 |
+
"type": "opencollective",
|
| 2167 |
+
"url": "https://opencollective.com/parcel"
|
| 2168 |
+
}
|
| 2169 |
+
},
|
| 2170 |
+
"node_modules/lightningcss-linux-arm64-musl": {
|
| 2171 |
+
"version": "1.32.0",
|
| 2172 |
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
|
| 2173 |
+
"integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
|
| 2174 |
+
"cpu": [
|
| 2175 |
+
"arm64"
|
| 2176 |
+
],
|
| 2177 |
+
"dev": true,
|
| 2178 |
+
"license": "MPL-2.0",
|
| 2179 |
+
"optional": true,
|
| 2180 |
+
"os": [
|
| 2181 |
+
"linux"
|
| 2182 |
+
],
|
| 2183 |
+
"engines": {
|
| 2184 |
+
"node": ">= 12.0.0"
|
| 2185 |
+
},
|
| 2186 |
+
"funding": {
|
| 2187 |
+
"type": "opencollective",
|
| 2188 |
+
"url": "https://opencollective.com/parcel"
|
| 2189 |
+
}
|
| 2190 |
+
},
|
| 2191 |
+
"node_modules/lightningcss-linux-x64-gnu": {
|
| 2192 |
+
"version": "1.32.0",
|
| 2193 |
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
|
| 2194 |
+
"integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
|
| 2195 |
+
"cpu": [
|
| 2196 |
+
"x64"
|
| 2197 |
+
],
|
| 2198 |
+
"dev": true,
|
| 2199 |
+
"license": "MPL-2.0",
|
| 2200 |
+
"optional": true,
|
| 2201 |
+
"os": [
|
| 2202 |
+
"linux"
|
| 2203 |
+
],
|
| 2204 |
+
"engines": {
|
| 2205 |
+
"node": ">= 12.0.0"
|
| 2206 |
+
},
|
| 2207 |
+
"funding": {
|
| 2208 |
+
"type": "opencollective",
|
| 2209 |
+
"url": "https://opencollective.com/parcel"
|
| 2210 |
+
}
|
| 2211 |
+
},
|
| 2212 |
+
"node_modules/lightningcss-linux-x64-musl": {
|
| 2213 |
+
"version": "1.32.0",
|
| 2214 |
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
|
| 2215 |
+
"integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
|
| 2216 |
+
"cpu": [
|
| 2217 |
+
"x64"
|
| 2218 |
+
],
|
| 2219 |
+
"dev": true,
|
| 2220 |
+
"license": "MPL-2.0",
|
| 2221 |
+
"optional": true,
|
| 2222 |
+
"os": [
|
| 2223 |
+
"linux"
|
| 2224 |
+
],
|
| 2225 |
+
"engines": {
|
| 2226 |
+
"node": ">= 12.0.0"
|
| 2227 |
+
},
|
| 2228 |
+
"funding": {
|
| 2229 |
+
"type": "opencollective",
|
| 2230 |
+
"url": "https://opencollective.com/parcel"
|
| 2231 |
+
}
|
| 2232 |
+
},
|
| 2233 |
+
"node_modules/lightningcss-win32-arm64-msvc": {
|
| 2234 |
+
"version": "1.32.0",
|
| 2235 |
+
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
|
| 2236 |
+
"integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
|
| 2237 |
+
"cpu": [
|
| 2238 |
+
"arm64"
|
| 2239 |
+
],
|
| 2240 |
+
"dev": true,
|
| 2241 |
+
"license": "MPL-2.0",
|
| 2242 |
+
"optional": true,
|
| 2243 |
+
"os": [
|
| 2244 |
+
"win32"
|
| 2245 |
+
],
|
| 2246 |
+
"engines": {
|
| 2247 |
+
"node": ">= 12.0.0"
|
| 2248 |
+
},
|
| 2249 |
+
"funding": {
|
| 2250 |
+
"type": "opencollective",
|
| 2251 |
+
"url": "https://opencollective.com/parcel"
|
| 2252 |
+
}
|
| 2253 |
+
},
|
| 2254 |
+
"node_modules/lightningcss-win32-x64-msvc": {
|
| 2255 |
+
"version": "1.32.0",
|
| 2256 |
+
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
|
| 2257 |
+
"integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
|
| 2258 |
+
"cpu": [
|
| 2259 |
+
"x64"
|
| 2260 |
+
],
|
| 2261 |
+
"dev": true,
|
| 2262 |
+
"license": "MPL-2.0",
|
| 2263 |
+
"optional": true,
|
| 2264 |
+
"os": [
|
| 2265 |
+
"win32"
|
| 2266 |
+
],
|
| 2267 |
+
"engines": {
|
| 2268 |
+
"node": ">= 12.0.0"
|
| 2269 |
+
},
|
| 2270 |
+
"funding": {
|
| 2271 |
+
"type": "opencollective",
|
| 2272 |
+
"url": "https://opencollective.com/parcel"
|
| 2273 |
+
}
|
| 2274 |
+
},
|
| 2275 |
+
"node_modules/lodash": {
|
| 2276 |
+
"version": "4.18.1",
|
| 2277 |
+
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
| 2278 |
+
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
| 2279 |
+
"license": "MIT"
|
| 2280 |
+
},
|
| 2281 |
+
"node_modules/loose-envify": {
|
| 2282 |
+
"version": "1.4.0",
|
| 2283 |
+
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
| 2284 |
+
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
| 2285 |
+
"license": "MIT",
|
| 2286 |
+
"dependencies": {
|
| 2287 |
+
"js-tokens": "^3.0.0 || ^4.0.0"
|
| 2288 |
+
},
|
| 2289 |
+
"bin": {
|
| 2290 |
+
"loose-envify": "cli.js"
|
| 2291 |
+
}
|
| 2292 |
+
},
|
| 2293 |
+
"node_modules/lru-cache": {
|
| 2294 |
+
"version": "5.1.1",
|
| 2295 |
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
| 2296 |
+
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
| 2297 |
+
"dev": true,
|
| 2298 |
+
"license": "ISC",
|
| 2299 |
+
"dependencies": {
|
| 2300 |
+
"yallist": "^3.0.2"
|
| 2301 |
+
}
|
| 2302 |
+
},
|
| 2303 |
+
"node_modules/magic-string": {
|
| 2304 |
+
"version": "0.30.21",
|
| 2305 |
+
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
| 2306 |
+
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
| 2307 |
+
"dev": true,
|
| 2308 |
+
"license": "MIT",
|
| 2309 |
+
"dependencies": {
|
| 2310 |
+
"@jridgewell/sourcemap-codec": "^1.5.5"
|
| 2311 |
+
}
|
| 2312 |
+
},
|
| 2313 |
+
"node_modules/ms": {
|
| 2314 |
+
"version": "2.1.3",
|
| 2315 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 2316 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 2317 |
+
"dev": true,
|
| 2318 |
+
"license": "MIT"
|
| 2319 |
+
},
|
| 2320 |
+
"node_modules/nanoid": {
|
| 2321 |
+
"version": "3.3.12",
|
| 2322 |
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
| 2323 |
+
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
| 2324 |
+
"dev": true,
|
| 2325 |
+
"funding": [
|
| 2326 |
+
{
|
| 2327 |
+
"type": "github",
|
| 2328 |
+
"url": "https://github.com/sponsors/ai"
|
| 2329 |
+
}
|
| 2330 |
+
],
|
| 2331 |
+
"license": "MIT",
|
| 2332 |
+
"bin": {
|
| 2333 |
+
"nanoid": "bin/nanoid.cjs"
|
| 2334 |
+
},
|
| 2335 |
+
"engines": {
|
| 2336 |
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
| 2337 |
+
}
|
| 2338 |
+
},
|
| 2339 |
+
"node_modules/node-releases": {
|
| 2340 |
+
"version": "2.0.47",
|
| 2341 |
+
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz",
|
| 2342 |
+
"integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==",
|
| 2343 |
+
"dev": true,
|
| 2344 |
+
"license": "MIT",
|
| 2345 |
+
"engines": {
|
| 2346 |
+
"node": ">=18"
|
| 2347 |
+
}
|
| 2348 |
+
},
|
| 2349 |
+
"node_modules/object-assign": {
|
| 2350 |
+
"version": "4.1.1",
|
| 2351 |
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
| 2352 |
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
| 2353 |
+
"license": "MIT",
|
| 2354 |
+
"engines": {
|
| 2355 |
+
"node": ">=0.10.0"
|
| 2356 |
+
}
|
| 2357 |
+
},
|
| 2358 |
+
"node_modules/picocolors": {
|
| 2359 |
+
"version": "1.1.1",
|
| 2360 |
+
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
| 2361 |
+
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
| 2362 |
+
"dev": true,
|
| 2363 |
+
"license": "ISC"
|
| 2364 |
+
},
|
| 2365 |
+
"node_modules/picomatch": {
|
| 2366 |
+
"version": "4.0.4",
|
| 2367 |
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
| 2368 |
+
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
| 2369 |
+
"dev": true,
|
| 2370 |
+
"license": "MIT",
|
| 2371 |
+
"engines": {
|
| 2372 |
+
"node": ">=12"
|
| 2373 |
+
},
|
| 2374 |
+
"funding": {
|
| 2375 |
+
"url": "https://github.com/sponsors/jonschlinkert"
|
| 2376 |
+
}
|
| 2377 |
+
},
|
| 2378 |
+
"node_modules/postcss": {
|
| 2379 |
+
"version": "8.5.15",
|
| 2380 |
+
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
| 2381 |
+
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
| 2382 |
+
"dev": true,
|
| 2383 |
+
"funding": [
|
| 2384 |
+
{
|
| 2385 |
+
"type": "opencollective",
|
| 2386 |
+
"url": "https://opencollective.com/postcss/"
|
| 2387 |
+
},
|
| 2388 |
+
{
|
| 2389 |
+
"type": "tidelift",
|
| 2390 |
+
"url": "https://tidelift.com/funding/github/npm/postcss"
|
| 2391 |
+
},
|
| 2392 |
+
{
|
| 2393 |
+
"type": "github",
|
| 2394 |
+
"url": "https://github.com/sponsors/ai"
|
| 2395 |
+
}
|
| 2396 |
+
],
|
| 2397 |
+
"license": "MIT",
|
| 2398 |
+
"dependencies": {
|
| 2399 |
+
"nanoid": "^3.3.12",
|
| 2400 |
+
"picocolors": "^1.1.1",
|
| 2401 |
+
"source-map-js": "^1.2.1"
|
| 2402 |
+
},
|
| 2403 |
+
"engines": {
|
| 2404 |
+
"node": "^10 || ^12 || >=14"
|
| 2405 |
+
}
|
| 2406 |
+
},
|
| 2407 |
+
"node_modules/prop-types": {
|
| 2408 |
+
"version": "15.8.1",
|
| 2409 |
+
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
| 2410 |
+
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
| 2411 |
+
"license": "MIT",
|
| 2412 |
+
"dependencies": {
|
| 2413 |
+
"loose-envify": "^1.4.0",
|
| 2414 |
+
"object-assign": "^4.1.1",
|
| 2415 |
+
"react-is": "^16.13.1"
|
| 2416 |
+
}
|
| 2417 |
+
},
|
| 2418 |
+
"node_modules/prop-types/node_modules/react-is": {
|
| 2419 |
+
"version": "16.13.1",
|
| 2420 |
+
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
| 2421 |
+
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
| 2422 |
+
"license": "MIT"
|
| 2423 |
+
},
|
| 2424 |
+
"node_modules/react": {
|
| 2425 |
+
"version": "19.2.7",
|
| 2426 |
+
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
|
| 2427 |
+
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
|
| 2428 |
+
"license": "MIT",
|
| 2429 |
+
"engines": {
|
| 2430 |
+
"node": ">=0.10.0"
|
| 2431 |
+
}
|
| 2432 |
+
},
|
| 2433 |
+
"node_modules/react-dom": {
|
| 2434 |
+
"version": "19.2.7",
|
| 2435 |
+
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
|
| 2436 |
+
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
|
| 2437 |
+
"license": "MIT",
|
| 2438 |
+
"dependencies": {
|
| 2439 |
+
"scheduler": "^0.27.0"
|
| 2440 |
+
},
|
| 2441 |
+
"peerDependencies": {
|
| 2442 |
+
"react": "^19.2.7"
|
| 2443 |
+
}
|
| 2444 |
+
},
|
| 2445 |
+
"node_modules/react-is": {
|
| 2446 |
+
"version": "18.3.1",
|
| 2447 |
+
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
| 2448 |
+
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
| 2449 |
+
"license": "MIT"
|
| 2450 |
+
},
|
| 2451 |
+
"node_modules/react-refresh": {
|
| 2452 |
+
"version": "0.17.0",
|
| 2453 |
+
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
| 2454 |
+
"integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
|
| 2455 |
+
"dev": true,
|
| 2456 |
+
"license": "MIT",
|
| 2457 |
+
"engines": {
|
| 2458 |
+
"node": ">=0.10.0"
|
| 2459 |
+
}
|
| 2460 |
+
},
|
| 2461 |
+
"node_modules/react-smooth": {
|
| 2462 |
+
"version": "4.0.4",
|
| 2463 |
+
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
| 2464 |
+
"integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
|
| 2465 |
+
"license": "MIT",
|
| 2466 |
+
"dependencies": {
|
| 2467 |
+
"fast-equals": "^5.0.1",
|
| 2468 |
+
"prop-types": "^15.8.1",
|
| 2469 |
+
"react-transition-group": "^4.4.5"
|
| 2470 |
+
},
|
| 2471 |
+
"peerDependencies": {
|
| 2472 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
| 2473 |
+
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
| 2474 |
+
}
|
| 2475 |
+
},
|
| 2476 |
+
"node_modules/react-transition-group": {
|
| 2477 |
+
"version": "4.4.5",
|
| 2478 |
+
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
| 2479 |
+
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
| 2480 |
+
"license": "BSD-3-Clause",
|
| 2481 |
+
"dependencies": {
|
| 2482 |
+
"@babel/runtime": "^7.5.5",
|
| 2483 |
+
"dom-helpers": "^5.0.1",
|
| 2484 |
+
"loose-envify": "^1.4.0",
|
| 2485 |
+
"prop-types": "^15.6.2"
|
| 2486 |
+
},
|
| 2487 |
+
"peerDependencies": {
|
| 2488 |
+
"react": ">=16.6.0",
|
| 2489 |
+
"react-dom": ">=16.6.0"
|
| 2490 |
+
}
|
| 2491 |
+
},
|
| 2492 |
+
"node_modules/recharts": {
|
| 2493 |
+
"version": "2.15.4",
|
| 2494 |
+
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz",
|
| 2495 |
+
"integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==",
|
| 2496 |
+
"deprecated": "1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide",
|
| 2497 |
+
"license": "MIT",
|
| 2498 |
+
"dependencies": {
|
| 2499 |
+
"clsx": "^2.0.0",
|
| 2500 |
+
"eventemitter3": "^4.0.1",
|
| 2501 |
+
"lodash": "^4.17.21",
|
| 2502 |
+
"react-is": "^18.3.1",
|
| 2503 |
+
"react-smooth": "^4.0.4",
|
| 2504 |
+
"recharts-scale": "^0.4.4",
|
| 2505 |
+
"tiny-invariant": "^1.3.1",
|
| 2506 |
+
"victory-vendor": "^36.6.8"
|
| 2507 |
+
},
|
| 2508 |
+
"engines": {
|
| 2509 |
+
"node": ">=14"
|
| 2510 |
+
},
|
| 2511 |
+
"peerDependencies": {
|
| 2512 |
+
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
| 2513 |
+
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
| 2514 |
+
}
|
| 2515 |
+
},
|
| 2516 |
+
"node_modules/recharts-scale": {
|
| 2517 |
+
"version": "0.4.5",
|
| 2518 |
+
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
|
| 2519 |
+
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
|
| 2520 |
+
"license": "MIT",
|
| 2521 |
+
"dependencies": {
|
| 2522 |
+
"decimal.js-light": "^2.4.1"
|
| 2523 |
+
}
|
| 2524 |
+
},
|
| 2525 |
+
"node_modules/rollup": {
|
| 2526 |
+
"version": "4.62.0",
|
| 2527 |
+
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.0.tgz",
|
| 2528 |
+
"integrity": "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==",
|
| 2529 |
+
"dev": true,
|
| 2530 |
+
"license": "MIT",
|
| 2531 |
+
"dependencies": {
|
| 2532 |
+
"@types/estree": "1.0.9"
|
| 2533 |
+
},
|
| 2534 |
+
"bin": {
|
| 2535 |
+
"rollup": "dist/bin/rollup"
|
| 2536 |
+
},
|
| 2537 |
+
"engines": {
|
| 2538 |
+
"node": ">=18.0.0",
|
| 2539 |
+
"npm": ">=8.0.0"
|
| 2540 |
+
},
|
| 2541 |
+
"optionalDependencies": {
|
| 2542 |
+
"@rollup/rollup-android-arm-eabi": "4.62.0",
|
| 2543 |
+
"@rollup/rollup-android-arm64": "4.62.0",
|
| 2544 |
+
"@rollup/rollup-darwin-arm64": "4.62.0",
|
| 2545 |
+
"@rollup/rollup-darwin-x64": "4.62.0",
|
| 2546 |
+
"@rollup/rollup-freebsd-arm64": "4.62.0",
|
| 2547 |
+
"@rollup/rollup-freebsd-x64": "4.62.0",
|
| 2548 |
+
"@rollup/rollup-linux-arm-gnueabihf": "4.62.0",
|
| 2549 |
+
"@rollup/rollup-linux-arm-musleabihf": "4.62.0",
|
| 2550 |
+
"@rollup/rollup-linux-arm64-gnu": "4.62.0",
|
| 2551 |
+
"@rollup/rollup-linux-arm64-musl": "4.62.0",
|
| 2552 |
+
"@rollup/rollup-linux-loong64-gnu": "4.62.0",
|
| 2553 |
+
"@rollup/rollup-linux-loong64-musl": "4.62.0",
|
| 2554 |
+
"@rollup/rollup-linux-ppc64-gnu": "4.62.0",
|
| 2555 |
+
"@rollup/rollup-linux-ppc64-musl": "4.62.0",
|
| 2556 |
+
"@rollup/rollup-linux-riscv64-gnu": "4.62.0",
|
| 2557 |
+
"@rollup/rollup-linux-riscv64-musl": "4.62.0",
|
| 2558 |
+
"@rollup/rollup-linux-s390x-gnu": "4.62.0",
|
| 2559 |
+
"@rollup/rollup-linux-x64-gnu": "4.62.0",
|
| 2560 |
+
"@rollup/rollup-linux-x64-musl": "4.62.0",
|
| 2561 |
+
"@rollup/rollup-openbsd-x64": "4.62.0",
|
| 2562 |
+
"@rollup/rollup-openharmony-arm64": "4.62.0",
|
| 2563 |
+
"@rollup/rollup-win32-arm64-msvc": "4.62.0",
|
| 2564 |
+
"@rollup/rollup-win32-ia32-msvc": "4.62.0",
|
| 2565 |
+
"@rollup/rollup-win32-x64-gnu": "4.62.0",
|
| 2566 |
+
"@rollup/rollup-win32-x64-msvc": "4.62.0",
|
| 2567 |
+
"fsevents": "~2.3.2"
|
| 2568 |
+
}
|
| 2569 |
+
},
|
| 2570 |
+
"node_modules/scheduler": {
|
| 2571 |
+
"version": "0.27.0",
|
| 2572 |
+
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
| 2573 |
+
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
| 2574 |
+
"license": "MIT"
|
| 2575 |
+
},
|
| 2576 |
+
"node_modules/semver": {
|
| 2577 |
+
"version": "6.3.1",
|
| 2578 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
| 2579 |
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
| 2580 |
+
"dev": true,
|
| 2581 |
+
"license": "ISC",
|
| 2582 |
+
"bin": {
|
| 2583 |
+
"semver": "bin/semver.js"
|
| 2584 |
+
}
|
| 2585 |
+
},
|
| 2586 |
+
"node_modules/source-map-js": {
|
| 2587 |
+
"version": "1.2.1",
|
| 2588 |
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
| 2589 |
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
| 2590 |
+
"dev": true,
|
| 2591 |
+
"license": "BSD-3-Clause",
|
| 2592 |
+
"engines": {
|
| 2593 |
+
"node": ">=0.10.0"
|
| 2594 |
+
}
|
| 2595 |
+
},
|
| 2596 |
+
"node_modules/tailwindcss": {
|
| 2597 |
+
"version": "4.3.1",
|
| 2598 |
+
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz",
|
| 2599 |
+
"integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==",
|
| 2600 |
+
"dev": true,
|
| 2601 |
+
"license": "MIT"
|
| 2602 |
+
},
|
| 2603 |
+
"node_modules/tapable": {
|
| 2604 |
+
"version": "2.3.3",
|
| 2605 |
+
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
|
| 2606 |
+
"integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
|
| 2607 |
+
"dev": true,
|
| 2608 |
+
"license": "MIT",
|
| 2609 |
+
"engines": {
|
| 2610 |
+
"node": ">=6"
|
| 2611 |
+
},
|
| 2612 |
+
"funding": {
|
| 2613 |
+
"type": "opencollective",
|
| 2614 |
+
"url": "https://opencollective.com/webpack"
|
| 2615 |
+
}
|
| 2616 |
+
},
|
| 2617 |
+
"node_modules/tiny-invariant": {
|
| 2618 |
+
"version": "1.3.3",
|
| 2619 |
+
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
| 2620 |
+
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
| 2621 |
+
"license": "MIT"
|
| 2622 |
+
},
|
| 2623 |
+
"node_modules/tinyglobby": {
|
| 2624 |
+
"version": "0.2.17",
|
| 2625 |
+
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
| 2626 |
+
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
| 2627 |
+
"dev": true,
|
| 2628 |
+
"license": "MIT",
|
| 2629 |
+
"dependencies": {
|
| 2630 |
+
"fdir": "^6.5.0",
|
| 2631 |
+
"picomatch": "^4.0.4"
|
| 2632 |
+
},
|
| 2633 |
+
"engines": {
|
| 2634 |
+
"node": ">=12.0.0"
|
| 2635 |
+
},
|
| 2636 |
+
"funding": {
|
| 2637 |
+
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 2638 |
+
}
|
| 2639 |
+
},
|
| 2640 |
+
"node_modules/update-browserslist-db": {
|
| 2641 |
+
"version": "1.2.3",
|
| 2642 |
+
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
| 2643 |
+
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
|
| 2644 |
+
"dev": true,
|
| 2645 |
+
"funding": [
|
| 2646 |
+
{
|
| 2647 |
+
"type": "opencollective",
|
| 2648 |
+
"url": "https://opencollective.com/browserslist"
|
| 2649 |
+
},
|
| 2650 |
+
{
|
| 2651 |
+
"type": "tidelift",
|
| 2652 |
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
| 2653 |
+
},
|
| 2654 |
+
{
|
| 2655 |
+
"type": "github",
|
| 2656 |
+
"url": "https://github.com/sponsors/ai"
|
| 2657 |
+
}
|
| 2658 |
+
],
|
| 2659 |
+
"license": "MIT",
|
| 2660 |
+
"dependencies": {
|
| 2661 |
+
"escalade": "^3.2.0",
|
| 2662 |
+
"picocolors": "^1.1.1"
|
| 2663 |
+
},
|
| 2664 |
+
"bin": {
|
| 2665 |
+
"update-browserslist-db": "cli.js"
|
| 2666 |
+
},
|
| 2667 |
+
"peerDependencies": {
|
| 2668 |
+
"browserslist": ">= 4.21.0"
|
| 2669 |
+
}
|
| 2670 |
+
},
|
| 2671 |
+
"node_modules/victory-vendor": {
|
| 2672 |
+
"version": "36.9.2",
|
| 2673 |
+
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
| 2674 |
+
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
|
| 2675 |
+
"license": "MIT AND ISC",
|
| 2676 |
+
"dependencies": {
|
| 2677 |
+
"@types/d3-array": "^3.0.3",
|
| 2678 |
+
"@types/d3-ease": "^3.0.0",
|
| 2679 |
+
"@types/d3-interpolate": "^3.0.1",
|
| 2680 |
+
"@types/d3-scale": "^4.0.2",
|
| 2681 |
+
"@types/d3-shape": "^3.1.0",
|
| 2682 |
+
"@types/d3-time": "^3.0.0",
|
| 2683 |
+
"@types/d3-timer": "^3.0.0",
|
| 2684 |
+
"d3-array": "^3.1.6",
|
| 2685 |
+
"d3-ease": "^3.0.1",
|
| 2686 |
+
"d3-interpolate": "^3.0.1",
|
| 2687 |
+
"d3-scale": "^4.0.2",
|
| 2688 |
+
"d3-shape": "^3.1.0",
|
| 2689 |
+
"d3-time": "^3.0.0",
|
| 2690 |
+
"d3-timer": "^3.0.1"
|
| 2691 |
+
}
|
| 2692 |
+
},
|
| 2693 |
+
"node_modules/vite": {
|
| 2694 |
+
"version": "6.4.3",
|
| 2695 |
+
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
|
| 2696 |
+
"integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
|
| 2697 |
+
"dev": true,
|
| 2698 |
+
"license": "MIT",
|
| 2699 |
+
"dependencies": {
|
| 2700 |
+
"esbuild": "^0.25.0",
|
| 2701 |
+
"fdir": "^6.4.4",
|
| 2702 |
+
"picomatch": "^4.0.2",
|
| 2703 |
+
"postcss": "^8.5.3",
|
| 2704 |
+
"rollup": "^4.34.9",
|
| 2705 |
+
"tinyglobby": "^0.2.13"
|
| 2706 |
+
},
|
| 2707 |
+
"bin": {
|
| 2708 |
+
"vite": "bin/vite.js"
|
| 2709 |
+
},
|
| 2710 |
+
"engines": {
|
| 2711 |
+
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
| 2712 |
+
},
|
| 2713 |
+
"funding": {
|
| 2714 |
+
"url": "https://github.com/vitejs/vite?sponsor=1"
|
| 2715 |
+
},
|
| 2716 |
+
"optionalDependencies": {
|
| 2717 |
+
"fsevents": "~2.3.3"
|
| 2718 |
+
},
|
| 2719 |
+
"peerDependencies": {
|
| 2720 |
+
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
| 2721 |
+
"jiti": ">=1.21.0",
|
| 2722 |
+
"less": "*",
|
| 2723 |
+
"lightningcss": "^1.21.0",
|
| 2724 |
+
"sass": "*",
|
| 2725 |
+
"sass-embedded": "*",
|
| 2726 |
+
"stylus": "*",
|
| 2727 |
+
"sugarss": "*",
|
| 2728 |
+
"terser": "^5.16.0",
|
| 2729 |
+
"tsx": "^4.8.1",
|
| 2730 |
+
"yaml": "^2.4.2"
|
| 2731 |
+
},
|
| 2732 |
+
"peerDependenciesMeta": {
|
| 2733 |
+
"@types/node": {
|
| 2734 |
+
"optional": true
|
| 2735 |
+
},
|
| 2736 |
+
"jiti": {
|
| 2737 |
+
"optional": true
|
| 2738 |
+
},
|
| 2739 |
+
"less": {
|
| 2740 |
+
"optional": true
|
| 2741 |
+
},
|
| 2742 |
+
"lightningcss": {
|
| 2743 |
+
"optional": true
|
| 2744 |
+
},
|
| 2745 |
+
"sass": {
|
| 2746 |
+
"optional": true
|
| 2747 |
+
},
|
| 2748 |
+
"sass-embedded": {
|
| 2749 |
+
"optional": true
|
| 2750 |
+
},
|
| 2751 |
+
"stylus": {
|
| 2752 |
+
"optional": true
|
| 2753 |
+
},
|
| 2754 |
+
"sugarss": {
|
| 2755 |
+
"optional": true
|
| 2756 |
+
},
|
| 2757 |
+
"terser": {
|
| 2758 |
+
"optional": true
|
| 2759 |
+
},
|
| 2760 |
+
"tsx": {
|
| 2761 |
+
"optional": true
|
| 2762 |
+
},
|
| 2763 |
+
"yaml": {
|
| 2764 |
+
"optional": true
|
| 2765 |
+
}
|
| 2766 |
+
}
|
| 2767 |
+
},
|
| 2768 |
+
"node_modules/yallist": {
|
| 2769 |
+
"version": "3.1.1",
|
| 2770 |
+
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
| 2771 |
+
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
| 2772 |
+
"dev": true,
|
| 2773 |
+
"license": "ISC"
|
| 2774 |
+
}
|
| 2775 |
+
}
|
| 2776 |
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "prism-eval-dashboard",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"type": "module",
|
| 5 |
+
"scripts": {
|
| 6 |
+
"dev": "vite",
|
| 7 |
+
"build": "vite build",
|
| 8 |
+
"preview": "vite preview"
|
| 9 |
+
},
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"react": "^19.0.0",
|
| 12 |
+
"react-dom": "^19.0.0",
|
| 13 |
+
"recharts": "^2.12.0"
|
| 14 |
+
},
|
| 15 |
+
"devDependencies": {
|
| 16 |
+
"@tailwindcss/vite": "^4.0.0",
|
| 17 |
+
"@vitejs/plugin-react": "^4.3.0",
|
| 18 |
+
"tailwindcss": "^4.0.0",
|
| 19 |
+
"vite": "^6.0.0"
|
| 20 |
+
}
|
| 21 |
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"version": "v2.0",
|
| 4 |
+
"tag": "baseline — mandatory web",
|
| 5 |
+
"date": "2026-06-17",
|
| 6 |
+
"file": "v2.0_20260617.json"
|
| 7 |
+
}
|
| 8 |
+
]
|
|
@@ -0,0 +1,385 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"version": "v2.0",
|
| 3 |
+
"tag": "baseline - mandatory\n web",
|
| 4 |
+
"computed_at": "2026-06-17T21:18:00",
|
| 5 |
+
"sample_count": 20,
|
| 6 |
+
"config": {
|
| 7 |
+
"hyde_enabled": false,
|
| 8 |
+
"retrieve_k": 10,
|
| 9 |
+
"rerank_k": 5,
|
| 10 |
+
"llm": "llama-3.3-70b-versatile",
|
| 11 |
+
"judge_model": "llama-3.1-8b-instant",
|
| 12 |
+
"workspace": "default"
|
| 13 |
+
},
|
| 14 |
+
"metrics": {
|
| 15 |
+
"answer_correctness": 0.75,
|
| 16 |
+
"answer_relevancy": 0.8407,
|
| 17 |
+
"context_recall": 0.7013,
|
| 18 |
+
"precision_at_5": 0.89,
|
| 19 |
+
"latency_p50_ms": 791,
|
| 20 |
+
"latency_p95_ms": 1230,
|
| 21 |
+
"latency_p99_ms": 2578
|
| 22 |
+
},
|
| 23 |
+
"per_query": [
|
| 24 |
+
{
|
| 25 |
+
"query": "What was the total UPI transaction volume in FY2024?",
|
| 26 |
+
"answer": "131 billion transactions. \n[Doc 1] supports this answer.",
|
| 27 |
+
"ground_truth": "Total UPI transaction volume crossed 131 billion transactions in FY2024. Monthly volume reached 14.04 billion transactions by March 2024, growing 56% year-on-year in volume and 43% in value compared to FY2023. Total annual value was approximately Rs 200 lakh crore.",
|
| 28 |
+
"answer_correctness": 0.75,
|
| 29 |
+
"correctness_reason": "missing key details such as monthly volume, year-on-year growth, and total annual value",
|
| 30 |
+
"answer_relevancy": 0.3767,
|
| 31 |
+
"context_recall": 0.75,
|
| 32 |
+
"precision_at_5": 1.0,
|
| 33 |
+
"latency_ms": 2916,
|
| 34 |
+
"retrieved_sources": [
|
| 35 |
+
"npci_upi_report_2024.txt",
|
| 36 |
+
"npci_upi_report_2024.txt",
|
| 37 |
+
"rbi_annual_report_2024.txt",
|
| 38 |
+
"npci_upi_report_2024.txt",
|
| 39 |
+
"npci_upi_report_2024.txt"
|
| 40 |
+
]
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"query": "What was India's GDP growth rate in FY2024?",
|
| 44 |
+
"answer": "India's real GDP growth for FY2024 is estimated at 7.6 per cent. \n[Doc 1]",
|
| 45 |
+
"ground_truth": "India's real GDP grew at 7.6% in FY2024, driven by strong domestic demand and investment activity. This was above the global average and made India one of the fastest-growing major economies.",
|
| 46 |
+
"answer_correctness": 0.75,
|
| 47 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision",
|
| 48 |
+
"answer_relevancy": 0.8604,
|
| 49 |
+
"context_recall": 0.5,
|
| 50 |
+
"precision_at_5": 1.0,
|
| 51 |
+
"latency_ms": 1142,
|
| 52 |
+
"retrieved_sources": [
|
| 53 |
+
"rbi_annual_report_2024.txt",
|
| 54 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 55 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 56 |
+
"rbi_annual_report_2024.txt",
|
| 57 |
+
"rbi_annual_report_2024.txt"
|
| 58 |
+
]
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"query": "What were Bajaj Finance's Assets Under Management in Q3 FY2024?",
|
| 62 |
+
"answer": "Bajaj Finance's Assets Under Management (AUM) in Q3 FY2024 were Rs 3,10,672 crore. \n[Doc 3] supports this answer.",
|
| 63 |
+
"ground_truth": "Bajaj Finance's consolidated Assets Under Management (AUM) stood at Rs 3,10,672 crore as of Q3 FY2024, growing 35% year-on-year.",
|
| 64 |
+
"answer_correctness": 0.75,
|
| 65 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision (lack of year-on-year growth percentage in the generated answer)",
|
| 66 |
+
"answer_relevancy": 0.9651,
|
| 67 |
+
"context_recall": 1.0,
|
| 68 |
+
"precision_at_5": 0.8,
|
| 69 |
+
"latency_ms": 870,
|
| 70 |
+
"retrieved_sources": [
|
| 71 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 72 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 73 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 74 |
+
"rbi_annual_report_2024.txt",
|
| 75 |
+
"bajaj_finance_q3_2024_transcript.txt"
|
| 76 |
+
]
|
| 77 |
+
},
|
| 78 |
+
{
|
| 79 |
+
"query": "What is RBI's stance on digital lending regulations?",
|
| 80 |
+
"answer": "The RBI has enhanced guidelines on digital lending, requiring all digital loans to be disbursed and repaid through borrower bank accounts, and Lending Service Providers (LSPs) and Digital Lending Apps (DLAs) must comply with disclosure requirements. \n[Doc 3]",
|
| 81 |
+
"ground_truth": "RBI issued enhanced guidelines on digital lending requiring all digital loans to be disbursed and repaid through borrower bank accounts. Lending Service Providers (LSPs) and Digital Lending Apps (DLAs) must comply with disclosure requirements. The framework applies to NBFCs and banks offering digital lending products.",
|
| 82 |
+
"answer_correctness": 0.75,
|
| 83 |
+
"correctness_reason": "minor omission of 'framework applies to NBFCs and banks offering digital lending products'",
|
| 84 |
+
"answer_relevancy": 0.8134,
|
| 85 |
+
"context_recall": 1.0,
|
| 86 |
+
"precision_at_5": 0.8,
|
| 87 |
+
"latency_ms": 883,
|
| 88 |
+
"retrieved_sources": [
|
| 89 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 90 |
+
"rbi_annual_report_2024.txt",
|
| 91 |
+
"rbi_annual_report_2024.txt",
|
| 92 |
+
"rbi_annual_report_2024.txt",
|
| 93 |
+
"rbi_annual_report_2024.txt"
|
| 94 |
+
]
|
| 95 |
+
},
|
| 96 |
+
{
|
| 97 |
+
"query": "How many banks were live on UPI by March 2024?",
|
| 98 |
+
"answer": "580 banks were live on UPI by March 2024. \n[Doc 1] supports this answer.",
|
| 99 |
+
"ground_truth": "580 banks were live on UPI as of March 2024, up from 437 in March 2023. PSU banks accounted for 35% of UPI originating transactions, private banks 42%, and Small Finance Banks and Payment Banks 23%.",
|
| 100 |
+
"answer_correctness": 0.75,
|
| 101 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision",
|
| 102 |
+
"answer_relevancy": 1.0,
|
| 103 |
+
"context_recall": 0.2857,
|
| 104 |
+
"precision_at_5": 1.0,
|
| 105 |
+
"latency_ms": 774,
|
| 106 |
+
"retrieved_sources": [
|
| 107 |
+
"npci_upi_report_2024.txt",
|
| 108 |
+
"npci_upi_report_2024.txt",
|
| 109 |
+
"npci_upi_report_2024.txt",
|
| 110 |
+
"npci_upi_report_2024.txt",
|
| 111 |
+
"npci_upi_report_2024.txt"
|
| 112 |
+
]
|
| 113 |
+
},
|
| 114 |
+
{
|
| 115 |
+
"query": "What was the UPI fraud rate in FY2024?",
|
| 116 |
+
"answer": "The UPI fraud rate remained low at 0.0006% of total transactions by volume in FY2024. \n[Doc 1]",
|
| 117 |
+
"ground_truth": "The UPI fraud rate was 0.0006% of total transactions in FY2024. There were 3.07 lakh fraud cases reported, representing a very small fraction given the 131 billion total transactions processed during the year.",
|
| 118 |
+
"answer_correctness": 0.75,
|
| 119 |
+
"correctness_reason": "minor omission of specific numbers and reference",
|
| 120 |
+
"answer_relevancy": 1.0,
|
| 121 |
+
"context_recall": 1.0,
|
| 122 |
+
"precision_at_5": 0.8,
|
| 123 |
+
"latency_ms": 727,
|
| 124 |
+
"retrieved_sources": [
|
| 125 |
+
"npci_upi_report_2024.txt",
|
| 126 |
+
"npci_upi_report_2024.txt",
|
| 127 |
+
"rbi_annual_report_2024.txt",
|
| 128 |
+
"npci_upi_report_2024.txt",
|
| 129 |
+
"npci_upi_report_2024.txt"
|
| 130 |
+
]
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"query": "What was the RBI repo rate during FY2024?",
|
| 134 |
+
"answer": "The RBI policy repo rate was 6.50 per cent during FY2024. \n[Doc 5]",
|
| 135 |
+
"ground_truth": "The RBI kept the repo rate unchanged at 6.50% throughout FY2024. The Monetary Policy Committee (MPC) maintained this rate to balance inflation control with growth support, keeping the policy stance as withdrawal of accommodation.",
|
| 136 |
+
"answer_correctness": 0.75,
|
| 137 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision",
|
| 138 |
+
"answer_relevancy": 0.9656,
|
| 139 |
+
"context_recall": null,
|
| 140 |
+
"precision_at_5": 1.0,
|
| 141 |
+
"latency_ms": 631,
|
| 142 |
+
"retrieved_sources": [
|
| 143 |
+
"rbi_annual_report_2024.txt",
|
| 144 |
+
"rbi_annual_report_2024.txt",
|
| 145 |
+
"rbi_annual_report_2024.txt",
|
| 146 |
+
"rbi_annual_report_2024.txt",
|
| 147 |
+
"rbi_annual_report_2024.txt"
|
| 148 |
+
]
|
| 149 |
+
},
|
| 150 |
+
{
|
| 151 |
+
"query": "What was Bajaj Finance's profit after tax in Q3 FY2024?",
|
| 152 |
+
"answer": "Bajaj Finance's profit after tax in Q3 FY2024 was Rs 3,639 crore. \n[Doc 3] supports this answer.",
|
| 153 |
+
"ground_truth": "Bajaj Finance's profit after tax (PAT) in Q3 FY2024 was Rs 3,639 crore, growing 22% year-on-year. Earnings per share increased correspondingly.",
|
| 154 |
+
"answer_correctness": 0.75,
|
| 155 |
+
"correctness_reason": "omitted year-on-year growth and earnings per share information",
|
| 156 |
+
"answer_relevancy": 1.0,
|
| 157 |
+
"context_recall": 1.0,
|
| 158 |
+
"precision_at_5": 1.0,
|
| 159 |
+
"latency_ms": 684,
|
| 160 |
+
"retrieved_sources": [
|
| 161 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 162 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 163 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 164 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 165 |
+
"bajaj_finance_q3_2024_transcript.txt"
|
| 166 |
+
]
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
"query": "What is UPI Lite and how many users does it have?",
|
| 170 |
+
"answer": "UPI Lite is a product innovation that enables small-value transactions (up to Rs 500) with near-zero decline rates. As of March 2024, UPI Lite had 5.2 crore enabled users. \n[Doc 1]",
|
| 171 |
+
"ground_truth": "UPI Lite is a feature enabling small-value transactions up to Rs 500 with near-zero decline rates by using an on-device wallet. As of March 2024, UPI Lite had 5.2 crore enabled users processing 18 crore transactions monthly. UPI Lite X, the offline variant, was piloted in 12 cities.",
|
| 172 |
+
"answer_correctness": 0.75,
|
| 173 |
+
"correctness_reason": "omitted information about transactions and UPI Lite X",
|
| 174 |
+
"answer_relevancy": 0.7858,
|
| 175 |
+
"context_recall": 1.0,
|
| 176 |
+
"precision_at_5": 0.8,
|
| 177 |
+
"latency_ms": 787,
|
| 178 |
+
"retrieved_sources": [
|
| 179 |
+
"npci_upi_report_2024.txt",
|
| 180 |
+
"npci_upi_report_2024.txt",
|
| 181 |
+
"rbi_annual_report_2024.txt",
|
| 182 |
+
"npci_upi_report_2024.txt",
|
| 183 |
+
"rbi_annual_report_2024.txt"
|
| 184 |
+
]
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
"query": "What was India's CPI inflation rate in FY2024?",
|
| 188 |
+
"answer": "India's CPI inflation rate averaged 5.4 per cent in FY2024. \n[Doc 1]",
|
| 189 |
+
"ground_truth": "India's CPI inflation averaged 5.4% in FY2024, declining from 6.7% in FY2023. Core inflation (excluding food and fuel) moderated to 4.2%. Food inflation remained elevated, while fuel prices provided some relief.",
|
| 190 |
+
"answer_correctness": 0.75,
|
| 191 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision",
|
| 192 |
+
"answer_relevancy": 0.9321,
|
| 193 |
+
"context_recall": 0.6667,
|
| 194 |
+
"precision_at_5": 0.8,
|
| 195 |
+
"latency_ms": 743,
|
| 196 |
+
"retrieved_sources": [
|
| 197 |
+
"rbi_annual_report_2024.txt",
|
| 198 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 199 |
+
"rbi_annual_report_2024.txt",
|
| 200 |
+
"rbi_annual_report_2024.txt",
|
| 201 |
+
"rbi_annual_report_2024.txt"
|
| 202 |
+
]
|
| 203 |
+
},
|
| 204 |
+
{
|
| 205 |
+
"query": "What is Bajaj Finance's capital adequacy ratio?",
|
| 206 |
+
"answer": "Bajaj Finance's capital adequacy ratio (CRAR) stood at 23.8%. \n[Doc 1] supports this answer.",
|
| 207 |
+
"ground_truth": "Bajaj Finance's Capital to Risk-weighted Assets Ratio (CRAR) stood at 23.8% as of Q3 FY2024, with Tier-I capital ratio at 22.1%. Both are well above the regulatory minimum, reflecting a strong capital position.",
|
| 208 |
+
"answer_correctness": 0.75,
|
| 209 |
+
"correctness_reason": "missing key fact (Tier-I capital ratio) and reference to supporting document",
|
| 210 |
+
"answer_relevancy": 0.9008,
|
| 211 |
+
"context_recall": null,
|
| 212 |
+
"precision_at_5": 1.0,
|
| 213 |
+
"latency_ms": 842,
|
| 214 |
+
"retrieved_sources": [
|
| 215 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 216 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 217 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 218 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 219 |
+
"rbi_annual_report_2024.txt"
|
| 220 |
+
]
|
| 221 |
+
},
|
| 222 |
+
{
|
| 223 |
+
"query": "Which countries accept UPI payments internationally?",
|
| 224 |
+
"answer": "According to Doc 1, UPI acceptance was enabled in 7 countries: Singapore, UAE, France, Sri Lanka, Mauritius, Nepal, and Bhutan.",
|
| 225 |
+
"ground_truth": "UPI is accepted internationally in Singapore, UAE, France, Sri Lanka, and several other countries for cross-border payments. NPCI International is driving further expansion to reach 20 countries by 2028.",
|
| 226 |
+
"answer_correctness": 0.75,
|
| 227 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision",
|
| 228 |
+
"answer_relevancy": 0.7839,
|
| 229 |
+
"context_recall": 0.6,
|
| 230 |
+
"precision_at_5": 0.6,
|
| 231 |
+
"latency_ms": 796,
|
| 232 |
+
"retrieved_sources": [
|
| 233 |
+
"npci_upi_report_2024.txt",
|
| 234 |
+
"npci_upi_report_2024.txt",
|
| 235 |
+
"npci_upi_report_2024.txt",
|
| 236 |
+
"rbi_annual_report_2024.txt",
|
| 237 |
+
"rbi_annual_report_2024.txt"
|
| 238 |
+
]
|
| 239 |
+
},
|
| 240 |
+
{
|
| 241 |
+
"query": "What was India's foreign exchange reserves in March 2024?",
|
| 242 |
+
"answer": "India's foreign exchange reserves stood at USD 645.6 billion as of March 2024. \n[Doc 1]",
|
| 243 |
+
"ground_truth": "India's foreign exchange reserves stood at USD 645.6 billion as of March 2024, providing approximately 11 months of import cover and reflecting strong external sector resilience.",
|
| 244 |
+
"answer_correctness": 0.75,
|
| 245 |
+
"correctness_reason": "missing import cover and external sector resilience details",
|
| 246 |
+
"answer_relevancy": 0.9549,
|
| 247 |
+
"context_recall": 1.0,
|
| 248 |
+
"precision_at_5": 1.0,
|
| 249 |
+
"latency_ms": 750,
|
| 250 |
+
"retrieved_sources": [
|
| 251 |
+
"rbi_annual_report_2024.txt",
|
| 252 |
+
"rbi_annual_report_2024.txt",
|
| 253 |
+
"npci_upi_report_2024.txt",
|
| 254 |
+
"rbi_annual_report_2024.txt",
|
| 255 |
+
"rbi_annual_report_2024.txt"
|
| 256 |
+
]
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"query": "What is Bajaj Finance's Gross NPA ratio?",
|
| 260 |
+
"answer": "The Gross NPA ratio for Bajaj Finance is not explicitly stated in the provided documents as a single value for the company. However, Doc 1 mentions the banking sector's GNPA ratio declined to 3.2 per cent, and Doc 3 states Gross NPA stood at 0.95% as of December 2023. Additionally, Doc 4 provides guidance that GNPA is expected to be below 1.1% for FY2024. \n\nDocuments: Doc 1, Doc 3, Doc 4.",
|
| 261 |
+
"ground_truth": "Bajaj Finance's Gross NPA (GNPA) ratio was 0.95% and Net NPA ratio was 0.36% as of Q3 FY2024. Provision coverage remained strong, reflecting disciplined underwriting and collections.",
|
| 262 |
+
"answer_correctness": 0.75,
|
| 263 |
+
"correctness_reason": "Most key facts correct, minor omissions or imprecision",
|
| 264 |
+
"answer_relevancy": 0.0,
|
| 265 |
+
"context_recall": null,
|
| 266 |
+
"precision_at_5": 1.0,
|
| 267 |
+
"latency_ms": 998,
|
| 268 |
+
"retrieved_sources": [
|
| 269 |
+
"rbi_annual_report_2024.txt",
|
| 270 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 271 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 272 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 273 |
+
"bajaj_finance_q3_2024_transcript.txt"
|
| 274 |
+
]
|
| 275 |
+
},
|
| 276 |
+
{
|
| 277 |
+
"query": "What is the market share of PhonePe on UPI?",
|
| 278 |
+
"answer": "The market share of PhonePe on UPI is 47%. \nThis information is supported by Doc 1.",
|
| 279 |
+
"ground_truth": "PhonePe held 47% market share on UPI by transaction volume, followed by Google Pay at 34%. The top two players together control over 80% of UPI transactions, though NPCI's market cap guidelines aim to limit concentration.",
|
| 280 |
+
"answer_correctness": 0.75,
|
| 281 |
+
"correctness_reason": "missing market share of Google Pay and concentration of top two players",
|
| 282 |
+
"answer_relevancy": 1.0,
|
| 283 |
+
"context_recall": 0.2857,
|
| 284 |
+
"precision_at_5": 1.0,
|
| 285 |
+
"latency_ms": 1003,
|
| 286 |
+
"retrieved_sources": [
|
| 287 |
+
"npci_upi_report_2024.txt",
|
| 288 |
+
"npci_upi_report_2024.txt",
|
| 289 |
+
"npci_upi_report_2024.txt",
|
| 290 |
+
"npci_upi_report_2024.txt",
|
| 291 |
+
"npci_upi_report_2024.txt"
|
| 292 |
+
]
|
| 293 |
+
},
|
| 294 |
+
{
|
| 295 |
+
"query": "What is the banking sector's GNPA ratio as reported by RBI?",
|
| 296 |
+
"answer": "The banking sector's GNPA ratio is 3.2 per cent. \n[Doc 1] supports this answer.",
|
| 297 |
+
"ground_truth": "The banking sector's Gross NPA (GNPA) ratio declined to 3.2% as of March 2024, the lowest in over a decade. Net NPA ratio stood at 0.8%. The improvement reflects better asset quality, recoveries, and write-offs.",
|
| 298 |
+
"answer_correctness": 0.75,
|
| 299 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision",
|
| 300 |
+
"answer_relevancy": 0.8867,
|
| 301 |
+
"context_recall": 0.6667,
|
| 302 |
+
"precision_at_5": 0.8,
|
| 303 |
+
"latency_ms": 786,
|
| 304 |
+
"retrieved_sources": [
|
| 305 |
+
"rbi_annual_report_2024.txt",
|
| 306 |
+
"rbi_annual_report_2024.txt",
|
| 307 |
+
"rbi_annual_report_2024.txt",
|
| 308 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 309 |
+
"bajaj_finance_q3_2024_transcript.txt"
|
| 310 |
+
]
|
| 311 |
+
},
|
| 312 |
+
{
|
| 313 |
+
"query": "How much did Bajaj Finance spend on technology?",
|
| 314 |
+
"answer": "Bajaj Finance spent Rs 1,180 crore on technology for 9M FY2024. [Doc 4]",
|
| 315 |
+
"ground_truth": "Bajaj Finance spent Rs 1,180 crore on technology in Q3 FY2024, representing approximately 12% of operating expenses. Technology investments focus on digital origination, customer experience, and risk management platforms.",
|
| 316 |
+
"answer_correctness": 0.75,
|
| 317 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision",
|
| 318 |
+
"answer_relevancy": 0.9343,
|
| 319 |
+
"context_recall": 0.5,
|
| 320 |
+
"precision_at_5": 1.0,
|
| 321 |
+
"latency_ms": 834,
|
| 322 |
+
"retrieved_sources": [
|
| 323 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 324 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 325 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 326 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 327 |
+
"bajaj_finance_q3_2024_transcript.txt"
|
| 328 |
+
]
|
| 329 |
+
},
|
| 330 |
+
{
|
| 331 |
+
"query": "What are the BBPS transaction statistics for FY2024?",
|
| 332 |
+
"answer": "BBPS processed 1,243 crore transactions valued at Rs 15.4 lakh crore during FY2024, with a 42% growth over FY2023. \n[Doc 1]",
|
| 333 |
+
"ground_truth": "Bharat Bill Payment System (BBPS) processed 1,243 crore transactions worth Rs 15.4 lakh crore in FY2024, growing 42% year-on-year. BBPS handles utility bill payments, insurance premiums, EMI collections, and other recurring payments.",
|
| 334 |
+
"answer_correctness": 0.75,
|
| 335 |
+
"correctness_reason": "minor inaccuracy in year-on-year comparison",
|
| 336 |
+
"answer_relevancy": 0.8679,
|
| 337 |
+
"context_recall": 0.6667,
|
| 338 |
+
"precision_at_5": 0.8,
|
| 339 |
+
"latency_ms": 688,
|
| 340 |
+
"retrieved_sources": [
|
| 341 |
+
"npci_upi_report_2024.txt",
|
| 342 |
+
"npci_upi_report_2024.txt",
|
| 343 |
+
"npci_upi_report_2024.txt",
|
| 344 |
+
"rbi_annual_report_2024.txt",
|
| 345 |
+
"npci_upi_report_2024.txt"
|
| 346 |
+
]
|
| 347 |
+
},
|
| 348 |
+
{
|
| 349 |
+
"query": "What is the Digital Rupee pilot status?",
|
| 350 |
+
"answer": "The Digital Rupee (e-Rupee) pilot programmes continued with both retail (e\u20b9-R) and wholesale (e\u20b9-W) variants, and the retail CBDC pilot was expanded to cover 50 cities with participation from 13 banks, having approximately 10 lakh retail e\u20b9 wallets active as of March 2024. \n[Doc 1]",
|
| 351 |
+
"ground_truth": "RBI's Digital Rupee (e-Rupee / CBDC) retail pilot was expanded to 50 cities with over 10 lakh users as of FY2024. The pilot covers retail use cases for person-to-person and person-to-merchant transactions using a digital wallet issued by participating banks.",
|
| 352 |
+
"answer_correctness": 0.75,
|
| 353 |
+
"correctness_reason": "minor difference in wording and slight discrepancy in the number of banks",
|
| 354 |
+
"answer_relevancy": 0.9069,
|
| 355 |
+
"context_recall": 0.3333,
|
| 356 |
+
"precision_at_5": 0.6,
|
| 357 |
+
"latency_ms": 861,
|
| 358 |
+
"retrieved_sources": [
|
| 359 |
+
"rbi_annual_report_2024.txt",
|
| 360 |
+
"rbi_annual_report_2024.txt",
|
| 361 |
+
"rbi_annual_report_2024.txt",
|
| 362 |
+
"npci_upi_report_2024.txt",
|
| 363 |
+
"npci_upi_report_2024.txt"
|
| 364 |
+
]
|
| 365 |
+
},
|
| 366 |
+
{
|
| 367 |
+
"query": "What is Bajaj Housing Finance's AUM and IPO plans?",
|
| 368 |
+
"answer": "Bajaj Housing Finance's AUM is Rs 72,400 crore, and its IPO is targeted for H1 FY2025. \n[Doc 1, Doc 2]",
|
| 369 |
+
"ground_truth": "Bajaj Housing Finance's AUM stood at Rs 72,400 crore as of Q3 FY2024. The company filed its Draft Red Herring Prospectus (DRHP) for an IPO and listing on stock exchanges, subject to regulatory approvals.",
|
| 370 |
+
"answer_correctness": 0.75,
|
| 371 |
+
"correctness_reason": "most key facts correct, minor omissions or imprecision",
|
| 372 |
+
"answer_relevancy": 0.8804,
|
| 373 |
+
"context_recall": 0.6667,
|
| 374 |
+
"precision_at_5": 1.0,
|
| 375 |
+
"latency_ms": 779,
|
| 376 |
+
"retrieved_sources": [
|
| 377 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 378 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 379 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 380 |
+
"bajaj_finance_q3_2024_transcript.txt",
|
| 381 |
+
"bajaj_finance_q3_2024_transcript.txt"
|
| 382 |
+
]
|
| 383 |
+
}
|
| 384 |
+
]
|
| 385 |
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState, useEffect } from 'react'
|
| 2 |
+
import MetricCard from './components/MetricCard'
|
| 3 |
+
import EvolutionChart from './components/EvolutionChart'
|
| 4 |
+
import RunTable from './components/RunTable'
|
| 5 |
+
import LatencyStats from './components/LatencyStats'
|
| 6 |
+
|
| 7 |
+
const METRIC_DEFS = [
|
| 8 |
+
{
|
| 9 |
+
key: 'answer_correctness',
|
| 10 |
+
label: 'Answer Correctness',
|
| 11 |
+
description: 'LLM judge (8B) comparing generated answer to ground truth. 0–1 normalized.',
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
key: 'answer_relevancy',
|
| 15 |
+
label: 'Answer Relevancy',
|
| 16 |
+
description: 'RAGAS: does the answer address the question? Penalizes off-topic responses.',
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
key: 'context_recall',
|
| 20 |
+
label: 'Context Recall',
|
| 21 |
+
description: 'RAGAS: did retrieval surface all chunks needed to answer? Higher = less missing context.',
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
key: 'precision_at_5',
|
| 25 |
+
label: 'Precision@5',
|
| 26 |
+
description: 'Retrieval: what fraction of top-5 chunks are relevant? Based on source + keyword match.',
|
| 27 |
+
},
|
| 28 |
+
]
|
| 29 |
+
|
| 30 |
+
function EmptyState() {
|
| 31 |
+
return (
|
| 32 |
+
<div className="flex flex-col items-center justify-center py-24 text-center">
|
| 33 |
+
<div className="w-16 h-16 rounded-2xl bg-indigo-50 flex items-center justify-center mb-4">
|
| 34 |
+
<svg className="w-8 h-8 text-indigo-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 35 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5"
|
| 36 |
+
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
| 37 |
+
</svg>
|
| 38 |
+
</div>
|
| 39 |
+
<h2 className="text-lg font-semibold text-gray-700 mb-1">No eval runs yet</h2>
|
| 40 |
+
<p className="text-sm text-gray-400 max-w-sm">
|
| 41 |
+
Run the eval script to generate the first benchmark:
|
| 42 |
+
</p>
|
| 43 |
+
<pre className="mt-3 text-xs bg-gray-100 text-gray-600 px-4 py-3 rounded-xl font-mono">
|
| 44 |
+
python scripts/run_eval_versioned.py --version v2.0 --tag "baseline" --n 50
|
| 45 |
+
</pre>
|
| 46 |
+
</div>
|
| 47 |
+
)
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
export default function App() {
|
| 51 |
+
const [indexData, setIndexData] = useState([])
|
| 52 |
+
const [runs, setRuns] = useState([])
|
| 53 |
+
const [selectedVersion, setSelectedVersion] = useState(null)
|
| 54 |
+
const [loading, setLoading] = useState(true)
|
| 55 |
+
const [error, setError] = useState(null)
|
| 56 |
+
|
| 57 |
+
useEffect(() => {
|
| 58 |
+
fetch('/data/index.json')
|
| 59 |
+
.then(r => r.json())
|
| 60 |
+
.then(async (idx) => {
|
| 61 |
+
if (!idx.length) { setLoading(false); return }
|
| 62 |
+
const loaded = await Promise.all(
|
| 63 |
+
idx.map(entry => fetch(`/data/runs/${entry.file}`).then(r => r.json()))
|
| 64 |
+
)
|
| 65 |
+
setIndexData(idx)
|
| 66 |
+
setRuns(loaded)
|
| 67 |
+
setSelectedVersion(idx[idx.length - 1].version)
|
| 68 |
+
setLoading(false)
|
| 69 |
+
})
|
| 70 |
+
.catch(e => { setError(e.message); setLoading(false) })
|
| 71 |
+
}, [])
|
| 72 |
+
|
| 73 |
+
const currentIdx = indexData.findIndex(e => e.version === selectedVersion)
|
| 74 |
+
const currentRun = runs[currentIdx] ?? null
|
| 75 |
+
const prevRun = currentIdx > 0 ? runs[currentIdx - 1] : null
|
| 76 |
+
|
| 77 |
+
return (
|
| 78 |
+
<div className="min-h-screen bg-gray-50">
|
| 79 |
+
{/* Header */}
|
| 80 |
+
<header className="bg-white border-b border-gray-100 px-6 py-4">
|
| 81 |
+
<div className="max-w-6xl mx-auto flex items-center justify-between">
|
| 82 |
+
<div className="flex items-center gap-3">
|
| 83 |
+
<div className="w-8 h-8 rounded-lg bg-indigo-600 flex items-center justify-center">
|
| 84 |
+
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 85 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
|
| 86 |
+
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
| 87 |
+
</svg>
|
| 88 |
+
</div>
|
| 89 |
+
<div>
|
| 90 |
+
<h1 className="text-base font-semibold text-gray-900">Prism Eval Dashboard</h1>
|
| 91 |
+
<p className="text-xs text-gray-400">Developer benchmark — not shown to users</p>
|
| 92 |
+
</div>
|
| 93 |
+
</div>
|
| 94 |
+
|
| 95 |
+
{indexData.length > 0 && (
|
| 96 |
+
<div className="flex items-center gap-3">
|
| 97 |
+
<span className="text-xs text-gray-400">{indexData.length} run{indexData.length !== 1 ? 's' : ''}</span>
|
| 98 |
+
<select
|
| 99 |
+
value={selectedVersion ?? ''}
|
| 100 |
+
onChange={e => setSelectedVersion(e.target.value)}
|
| 101 |
+
className="text-sm border border-gray-200 rounded-lg px-3 py-1.5 bg-white text-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
| 102 |
+
>
|
| 103 |
+
{indexData.map(e => (
|
| 104 |
+
<option key={e.version} value={e.version}>
|
| 105 |
+
{e.version} — {e.tag}
|
| 106 |
+
</option>
|
| 107 |
+
))}
|
| 108 |
+
</select>
|
| 109 |
+
</div>
|
| 110 |
+
)}
|
| 111 |
+
</div>
|
| 112 |
+
</header>
|
| 113 |
+
|
| 114 |
+
<main className="max-w-6xl mx-auto px-6 py-8 flex flex-col gap-6">
|
| 115 |
+
{loading && (
|
| 116 |
+
<div className="flex items-center justify-center py-24 text-gray-400 text-sm">Loading...</div>
|
| 117 |
+
)}
|
| 118 |
+
{error && (
|
| 119 |
+
<div className="bg-red-50 border border-red-200 text-red-700 text-sm px-4 py-3 rounded-xl">
|
| 120 |
+
Failed to load eval data: {error}
|
| 121 |
+
</div>
|
| 122 |
+
)}
|
| 123 |
+
|
| 124 |
+
{!loading && !error && !runs.length && <EmptyState />}
|
| 125 |
+
|
| 126 |
+
{currentRun && (
|
| 127 |
+
<>
|
| 128 |
+
{/* Run meta */}
|
| 129 |
+
<div className="flex items-center gap-4 text-xs text-gray-400">
|
| 130 |
+
<span className="bg-indigo-50 text-indigo-700 font-medium px-2.5 py-1 rounded-full">
|
| 131 |
+
{currentRun.version}
|
| 132 |
+
</span>
|
| 133 |
+
<span>{currentRun.tag}</span>
|
| 134 |
+
<span>·</span>
|
| 135 |
+
<span>{currentRun.sample_count} samples</span>
|
| 136 |
+
<span>·</span>
|
| 137 |
+
<span>{new Date(currentRun.computed_at).toLocaleDateString('en-IN', { day: 'numeric', month: 'short', year: 'numeric' })}</span>
|
| 138 |
+
{currentRun.config && (
|
| 139 |
+
<>
|
| 140 |
+
<span>·</span>
|
| 141 |
+
<span>HyDE: {currentRun.config.hyde_enabled ? 'on' : 'off'}</span>
|
| 142 |
+
<span>·</span>
|
| 143 |
+
<span>retrieve_k={currentRun.config.retrieve_k}</span>
|
| 144 |
+
</>
|
| 145 |
+
)}
|
| 146 |
+
</div>
|
| 147 |
+
|
| 148 |
+
{/* Metric cards */}
|
| 149 |
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
| 150 |
+
{METRIC_DEFS.map(m => (
|
| 151 |
+
<MetricCard
|
| 152 |
+
key={m.key}
|
| 153 |
+
label={m.label}
|
| 154 |
+
description={m.description}
|
| 155 |
+
value={currentRun.metrics?.[m.key]}
|
| 156 |
+
prevValue={prevRun?.metrics?.[m.key]}
|
| 157 |
+
/>
|
| 158 |
+
))}
|
| 159 |
+
</div>
|
| 160 |
+
|
| 161 |
+
{/* Evolution chart + latency */}
|
| 162 |
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
| 163 |
+
<div className="lg:col-span-2">
|
| 164 |
+
<EvolutionChart runs={runs} index={indexData} />
|
| 165 |
+
</div>
|
| 166 |
+
<LatencyStats metrics={currentRun.metrics} />
|
| 167 |
+
</div>
|
| 168 |
+
|
| 169 |
+
{/* Per-query table */}
|
| 170 |
+
<RunTable perQuery={currentRun.per_query} />
|
| 171 |
+
</>
|
| 172 |
+
)}
|
| 173 |
+
</main>
|
| 174 |
+
</div>
|
| 175 |
+
)
|
| 176 |
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer
|
| 3 |
+
} from 'recharts'
|
| 4 |
+
|
| 5 |
+
const METRICS = [
|
| 6 |
+
{ key: 'answer_correctness', label: 'Answer Correctness', color: '#6366f1' },
|
| 7 |
+
{ key: 'answer_relevancy', label: 'Answer Relevancy', color: '#10b981' },
|
| 8 |
+
{ key: 'context_recall', label: 'Context Recall', color: '#f59e0b' },
|
| 9 |
+
{ key: 'precision_at_5', label: 'Precision@5', color: '#ef4444' },
|
| 10 |
+
]
|
| 11 |
+
|
| 12 |
+
export default function EvolutionChart({ runs, index }) {
|
| 13 |
+
if (!runs.length) return null
|
| 14 |
+
|
| 15 |
+
const data = runs.map((run, i) => ({
|
| 16 |
+
version: index[i]?.version ?? `run${i + 1}`,
|
| 17 |
+
...Object.fromEntries(
|
| 18 |
+
METRICS.map(m => [m.key, run.metrics?.[m.key] != null
|
| 19 |
+
? parseFloat((run.metrics[m.key] * 100).toFixed(1))
|
| 20 |
+
: null
|
| 21 |
+
])
|
| 22 |
+
),
|
| 23 |
+
}))
|
| 24 |
+
|
| 25 |
+
const fmt = (v) => `${v}%`
|
| 26 |
+
|
| 27 |
+
return (
|
| 28 |
+
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
|
| 29 |
+
<h2 className="text-sm font-semibold text-gray-700 mb-4">Metric Evolution Across Versions</h2>
|
| 30 |
+
<ResponsiveContainer width="100%" height={280}>
|
| 31 |
+
<LineChart data={data} margin={{ top: 4, right: 16, left: 0, bottom: 4 }}>
|
| 32 |
+
<CartesianGrid strokeDasharray="3 3" stroke="#f3f4f6" />
|
| 33 |
+
<XAxis dataKey="version" tick={{ fontSize: 12 }} />
|
| 34 |
+
<YAxis domain={[0, 100]} tickFormatter={fmt} tick={{ fontSize: 12 }} width={40} />
|
| 35 |
+
<Tooltip formatter={(v) => `${v}%`} />
|
| 36 |
+
<Legend wrapperStyle={{ fontSize: 12 }} />
|
| 37 |
+
{METRICS.map(m => (
|
| 38 |
+
<Line
|
| 39 |
+
key={m.key}
|
| 40 |
+
type="monotone"
|
| 41 |
+
dataKey={m.key}
|
| 42 |
+
name={m.label}
|
| 43 |
+
stroke={m.color}
|
| 44 |
+
strokeWidth={2}
|
| 45 |
+
dot={{ r: 4 }}
|
| 46 |
+
connectNulls
|
| 47 |
+
/>
|
| 48 |
+
))}
|
| 49 |
+
</LineChart>
|
| 50 |
+
</ResponsiveContainer>
|
| 51 |
+
</div>
|
| 52 |
+
)
|
| 53 |
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function StatBar({ label, value, max }) {
|
| 2 |
+
const pct = max > 0 ? Math.min((value / max) * 100, 100) : 0
|
| 3 |
+
const color = value < 3000 ? 'bg-emerald-500' : value < 6000 ? 'bg-yellow-500' : 'bg-red-500'
|
| 4 |
+
return (
|
| 5 |
+
<div className="flex flex-col gap-1">
|
| 6 |
+
<div className="flex justify-between text-xs text-gray-500">
|
| 7 |
+
<span>{label}</span>
|
| 8 |
+
<span className="font-mono font-medium text-gray-700">{value ? `${value.toLocaleString()}ms` : '—'}</span>
|
| 9 |
+
</div>
|
| 10 |
+
<div className="h-2 bg-gray-100 rounded-full overflow-hidden">
|
| 11 |
+
<div className={`h-full rounded-full ${color}`} style={{ width: `${pct}%` }} />
|
| 12 |
+
</div>
|
| 13 |
+
</div>
|
| 14 |
+
)
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
export default function LatencyStats({ metrics }) {
|
| 18 |
+
const p50 = metrics?.latency_p50_ms
|
| 19 |
+
const p95 = metrics?.latency_p95_ms
|
| 20 |
+
const p99 = metrics?.latency_p99_ms
|
| 21 |
+
const max = Math.max(p99 ?? 0, p95 ?? 0, p50 ?? 0, 10000)
|
| 22 |
+
|
| 23 |
+
return (
|
| 24 |
+
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
|
| 25 |
+
<h2 className="text-sm font-semibold text-gray-700 mb-4">Response Latency</h2>
|
| 26 |
+
<div className="flex flex-col gap-4">
|
| 27 |
+
<StatBar label="p50 (median)" value={p50} max={max} />
|
| 28 |
+
<StatBar label="p95" value={p95} max={max} />
|
| 29 |
+
{p99 != null && <StatBar label="p99" value={p99} max={max} />}
|
| 30 |
+
</div>
|
| 31 |
+
<p className="text-xs text-gray-400 mt-3">End-to-end per-query latency including retrieval + LLM call. Does not include Tavily web search.</p>
|
| 32 |
+
</div>
|
| 33 |
+
)
|
| 34 |
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export default function MetricCard({ label, value, prevValue, description, unit = '%' }) {
|
| 2 |
+
const display = value != null ? (unit === '%' ? `${(value * 100).toFixed(1)}%` : `${value}`) : '—'
|
| 3 |
+
|
| 4 |
+
let delta = null
|
| 5 |
+
let deltaClass = 'text-gray-400'
|
| 6 |
+
let deltaSign = ''
|
| 7 |
+
if (value != null && prevValue != null) {
|
| 8 |
+
const diff = unit === '%' ? (value - prevValue) * 100 : value - prevValue
|
| 9 |
+
delta = diff.toFixed(unit === '%' ? 1 : 0)
|
| 10 |
+
if (diff > 0) { deltaClass = 'text-emerald-600'; deltaSign = '+' }
|
| 11 |
+
else if (diff < 0) { deltaClass = 'text-red-500'; deltaSign = '' }
|
| 12 |
+
else { deltaClass = 'text-gray-400' }
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
const scoreColor = value == null ? 'text-gray-400'
|
| 16 |
+
: value >= 0.8 ? 'text-emerald-600'
|
| 17 |
+
: value >= 0.6 ? 'text-yellow-600'
|
| 18 |
+
: 'text-red-500'
|
| 19 |
+
|
| 20 |
+
return (
|
| 21 |
+
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-5 flex flex-col gap-1">
|
| 22 |
+
<span className="text-xs font-medium text-gray-400 uppercase tracking-wider">{label}</span>
|
| 23 |
+
<div className="flex items-end gap-2 mt-1">
|
| 24 |
+
<span className={`text-3xl font-bold tabular-nums ${scoreColor}`}>{display}</span>
|
| 25 |
+
{delta != null && (
|
| 26 |
+
<span className={`text-sm font-medium mb-1 ${deltaClass}`}>
|
| 27 |
+
{deltaSign}{delta}{unit === '%' ? 'pp' : unit}
|
| 28 |
+
</span>
|
| 29 |
+
)}
|
| 30 |
+
</div>
|
| 31 |
+
<p className="text-xs text-gray-400 mt-1 leading-relaxed">{description}</p>
|
| 32 |
+
</div>
|
| 33 |
+
)
|
| 34 |
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState } from 'react'
|
| 2 |
+
|
| 3 |
+
function score(v) {
|
| 4 |
+
if (v == null) return '—'
|
| 5 |
+
return `${(v * 100).toFixed(0)}%`
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
function scoreColor(v) {
|
| 9 |
+
if (v == null) return 'text-gray-300'
|
| 10 |
+
if (v >= 0.8) return 'text-emerald-600'
|
| 11 |
+
if (v >= 0.6) return 'text-yellow-600'
|
| 12 |
+
return 'text-red-500'
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
function Row({ item, idx }) {
|
| 16 |
+
const [open, setOpen] = useState(false)
|
| 17 |
+
return (
|
| 18 |
+
<>
|
| 19 |
+
<tr
|
| 20 |
+
className="hover:bg-gray-50 cursor-pointer border-t border-gray-100"
|
| 21 |
+
onClick={() => setOpen(v => !v)}
|
| 22 |
+
>
|
| 23 |
+
<td className="px-4 py-2.5 text-xs text-gray-500 font-mono w-8">{idx + 1}</td>
|
| 24 |
+
<td className="px-4 py-2.5 text-sm text-gray-700 max-w-xs">
|
| 25 |
+
<span className="line-clamp-2">{item.query}</span>
|
| 26 |
+
</td>
|
| 27 |
+
<td className={`px-4 py-2.5 text-sm font-mono font-medium text-center ${scoreColor(item.answer_correctness)}`}>
|
| 28 |
+
{score(item.answer_correctness)}
|
| 29 |
+
</td>
|
| 30 |
+
<td className={`px-4 py-2.5 text-sm font-mono font-medium text-center ${scoreColor(item.answer_relevancy)}`}>
|
| 31 |
+
{score(item.answer_relevancy)}
|
| 32 |
+
</td>
|
| 33 |
+
<td className={`px-4 py-2.5 text-sm font-mono font-medium text-center ${scoreColor(item.context_recall)}`}>
|
| 34 |
+
{score(item.context_recall)}
|
| 35 |
+
</td>
|
| 36 |
+
<td className={`px-4 py-2.5 text-sm font-mono font-medium text-center ${scoreColor(item.precision_at_5)}`}>
|
| 37 |
+
{score(item.precision_at_5)}
|
| 38 |
+
</td>
|
| 39 |
+
<td className="px-4 py-2.5 text-xs text-gray-400 font-mono text-right">
|
| 40 |
+
{item.latency_ms ? `${item.latency_ms}ms` : '—'}
|
| 41 |
+
</td>
|
| 42 |
+
<td className="px-4 py-2.5 text-gray-300 text-center">
|
| 43 |
+
<span>{open ? '▲' : '▼'}</span>
|
| 44 |
+
</td>
|
| 45 |
+
</tr>
|
| 46 |
+
{open && (
|
| 47 |
+
<tr className="bg-gray-50 border-t border-gray-100">
|
| 48 |
+
<td colSpan={8} className="px-6 py-4">
|
| 49 |
+
<div className="grid grid-cols-2 gap-4 text-xs">
|
| 50 |
+
<div>
|
| 51 |
+
<p className="font-semibold text-gray-500 mb-1">Generated answer</p>
|
| 52 |
+
<p className="text-gray-700 whitespace-pre-wrap leading-relaxed">{item.answer}</p>
|
| 53 |
+
</div>
|
| 54 |
+
<div>
|
| 55 |
+
<p className="font-semibold text-gray-500 mb-1">Ground truth</p>
|
| 56 |
+
<p className="text-gray-700 whitespace-pre-wrap leading-relaxed">{item.ground_truth}</p>
|
| 57 |
+
</div>
|
| 58 |
+
{item.correctness_reason && (
|
| 59 |
+
<div className="col-span-2">
|
| 60 |
+
<p className="font-semibold text-gray-500 mb-1">Correctness judge note</p>
|
| 61 |
+
<p className="text-gray-600 italic">{item.correctness_reason}</p>
|
| 62 |
+
</div>
|
| 63 |
+
)}
|
| 64 |
+
</div>
|
| 65 |
+
</td>
|
| 66 |
+
</tr>
|
| 67 |
+
)}
|
| 68 |
+
</>
|
| 69 |
+
)
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
export default function RunTable({ perQuery }) {
|
| 73 |
+
if (!perQuery?.length) return null
|
| 74 |
+
return (
|
| 75 |
+
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
| 76 |
+
<div className="px-6 py-4 border-b border-gray-100">
|
| 77 |
+
<h2 className="text-sm font-semibold text-gray-700">Per-Query Breakdown</h2>
|
| 78 |
+
<p className="text-xs text-gray-400 mt-0.5">Click any row to see generated answer vs ground truth</p>
|
| 79 |
+
</div>
|
| 80 |
+
<div className="overflow-x-auto">
|
| 81 |
+
<table className="w-full text-left">
|
| 82 |
+
<thead>
|
| 83 |
+
<tr className="bg-gray-50">
|
| 84 |
+
<th className="px-4 py-2.5 text-xs text-gray-400 font-medium">#</th>
|
| 85 |
+
<th className="px-4 py-2.5 text-xs text-gray-400 font-medium">Query</th>
|
| 86 |
+
<th className="px-4 py-2.5 text-xs text-gray-400 font-medium text-center">Correctness</th>
|
| 87 |
+
<th className="px-4 py-2.5 text-xs text-gray-400 font-medium text-center">Relevancy</th>
|
| 88 |
+
<th className="px-4 py-2.5 text-xs text-gray-400 font-medium text-center">Recall</th>
|
| 89 |
+
<th className="px-4 py-2.5 text-xs text-gray-400 font-medium text-center">P@5</th>
|
| 90 |
+
<th className="px-4 py-2.5 text-xs text-gray-400 font-medium text-right">Latency</th>
|
| 91 |
+
<th className="px-4 py-2.5 text-xs text-gray-400 font-medium text-center"></th>
|
| 92 |
+
</tr>
|
| 93 |
+
</thead>
|
| 94 |
+
<tbody>
|
| 95 |
+
{perQuery.map((item, i) => <Row key={i} item={item} idx={i} />)}
|
| 96 |
+
</tbody>
|
| 97 |
+
</table>
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
| 100 |
+
)
|
| 101 |
+
}
|
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
@import "tailwindcss";
|
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { StrictMode } from 'react'
|
| 2 |
+
import { createRoot } from 'react-dom/client'
|
| 3 |
+
import './index.css'
|
| 4 |
+
import App from './App.jsx'
|
| 5 |
+
|
| 6 |
+
createRoot(document.getElementById('root')).render(
|
| 7 |
+
<StrictMode>
|
| 8 |
+
<App />
|
| 9 |
+
</StrictMode>,
|
| 10 |
+
)
|
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"buildCommand": "npm run build",
|
| 3 |
+
"outputDirectory": "dist",
|
| 4 |
+
"framework": "vite"
|
| 5 |
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from 'vite'
|
| 2 |
+
import react from '@vitejs/plugin-react'
|
| 3 |
+
import tailwindcss from '@tailwindcss/vite'
|
| 4 |
+
|
| 5 |
+
export default defineConfig({
|
| 6 |
+
plugins: [react(), tailwindcss()],
|
| 7 |
+
})
|
|
@@ -6,7 +6,7 @@ export default function ChatArea({ onEvalEntry, hasDocuments, suggestedQuestion,
|
|
| 6 |
const [messages, setMessages] = useState([]);
|
| 7 |
const [input, setInput] = useState("");
|
| 8 |
const [loading, setLoading] = useState(false);
|
| 9 |
-
const
|
| 10 |
|
| 11 |
useEffect(() => {
|
| 12 |
if (suggestedQuestion) {
|
|
@@ -32,14 +32,11 @@ export default function ChatArea({ onEvalEntry, hasDocuments, suggestedQuestion,
|
|
| 32 |
role: "assistant",
|
| 33 |
content: data.answer,
|
| 34 |
sources: data.sources,
|
| 35 |
-
faithfulness: data.faithfulness,
|
| 36 |
},
|
| 37 |
]);
|
| 38 |
onEvalEntry({
|
| 39 |
query: question,
|
| 40 |
answer: data.answer,
|
| 41 |
-
faithfulness_score: data.faithfulness.score,
|
| 42 |
-
reason: data.faithfulness.reason,
|
| 43 |
});
|
| 44 |
} catch (err) {
|
| 45 |
const detail = err.response?.data?.detail || err.message;
|
|
@@ -115,31 +112,13 @@ export default function ChatArea({ onEvalEntry, hasDocuments, suggestedQuestion,
|
|
| 115 |
{/* Input */}
|
| 116 |
<form onSubmit={handleSend} className="p-4 bg-white border-t border-gray-100">
|
| 117 |
<div className="flex gap-3 max-w-4xl mx-auto">
|
| 118 |
-
<button
|
| 119 |
-
type="button"
|
| 120 |
-
onClick={() => setWebSearch((v) => !v)}
|
| 121 |
-
disabled={loading || !hasDocuments}
|
| 122 |
-
title={webSearch ? "Web search ON — click to disable" : "Web search OFF — click to enable"}
|
| 123 |
-
className={`shrink-0 p-3 rounded-xl border text-sm font-medium transition-colors shadow-sm ${
|
| 124 |
-
webSearch
|
| 125 |
-
? "bg-emerald-50 border-emerald-300 text-emerald-700 hover:bg-emerald-100"
|
| 126 |
-
: "bg-gray-50 border-gray-200 text-gray-400 hover:bg-gray-100 hover:text-gray-600"
|
| 127 |
-
} disabled:opacity-40 disabled:cursor-not-allowed`}
|
| 128 |
-
>
|
| 129 |
-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 130 |
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
|
| 131 |
-
d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
|
| 132 |
-
</svg>
|
| 133 |
-
</button>
|
| 134 |
<input
|
| 135 |
type="text"
|
| 136 |
value={input}
|
| 137 |
onChange={(e) => setInput(e.target.value)}
|
| 138 |
placeholder={
|
| 139 |
hasDocuments
|
| 140 |
-
?
|
| 141 |
-
? "Ask anything — searching docs + web..."
|
| 142 |
-
: "Ask about your documents..."
|
| 143 |
: "Upload documents first to start chatting"
|
| 144 |
}
|
| 145 |
className="flex-1 px-4 py-3 border border-gray-200 rounded-xl text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent disabled:bg-gray-50 disabled:text-gray-400 transition-shadow"
|
|
@@ -153,11 +132,6 @@ export default function ChatArea({ onEvalEntry, hasDocuments, suggestedQuestion,
|
|
| 153 |
Send
|
| 154 |
</button>
|
| 155 |
</div>
|
| 156 |
-
{webSearch && hasDocuments && (
|
| 157 |
-
<p className="text-center text-xs text-emerald-600 mt-2">
|
| 158 |
-
Web search active — answers will include live web context
|
| 159 |
-
</p>
|
| 160 |
-
)}
|
| 161 |
</form>
|
| 162 |
</div>
|
| 163 |
);
|
|
|
|
| 6 |
const [messages, setMessages] = useState([]);
|
| 7 |
const [input, setInput] = useState("");
|
| 8 |
const [loading, setLoading] = useState(false);
|
| 9 |
+
const webSearch = true;
|
| 10 |
|
| 11 |
useEffect(() => {
|
| 12 |
if (suggestedQuestion) {
|
|
|
|
| 32 |
role: "assistant",
|
| 33 |
content: data.answer,
|
| 34 |
sources: data.sources,
|
|
|
|
| 35 |
},
|
| 36 |
]);
|
| 37 |
onEvalEntry({
|
| 38 |
query: question,
|
| 39 |
answer: data.answer,
|
|
|
|
|
|
|
| 40 |
});
|
| 41 |
} catch (err) {
|
| 42 |
const detail = err.response?.data?.detail || err.message;
|
|
|
|
| 112 |
{/* Input */}
|
| 113 |
<form onSubmit={handleSend} className="p-4 bg-white border-t border-gray-100">
|
| 114 |
<div className="flex gap-3 max-w-4xl mx-auto">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
<input
|
| 116 |
type="text"
|
| 117 |
value={input}
|
| 118 |
onChange={(e) => setInput(e.target.value)}
|
| 119 |
placeholder={
|
| 120 |
hasDocuments
|
| 121 |
+
? "Ask anything — searching docs + web..."
|
|
|
|
|
|
|
| 122 |
: "Upload documents first to start chatting"
|
| 123 |
}
|
| 124 |
className="flex-1 px-4 py-3 border border-gray-200 rounded-xl text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent disabled:bg-gray-50 disabled:text-gray-400 transition-shadow"
|
|
|
|
| 132 |
Send
|
| 133 |
</button>
|
| 134 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
</form>
|
| 136 |
</div>
|
| 137 |
);
|
|
@@ -26,56 +26,6 @@ function WebSourcesList({ sources }) {
|
|
| 26 |
);
|
| 27 |
}
|
| 28 |
|
| 29 |
-
function FaithfulnessBadge({ faithfulness }) {
|
| 30 |
-
if (!faithfulness || faithfulness.score < 0) {
|
| 31 |
-
return (
|
| 32 |
-
<span className="inline-flex items-center gap-1.5 text-xs text-gray-400 bg-gray-100 px-2.5 py-1 rounded-full">
|
| 33 |
-
<span className="w-1.5 h-1.5 rounded-full bg-gray-400" />
|
| 34 |
-
Eval failed
|
| 35 |
-
</span>
|
| 36 |
-
);
|
| 37 |
-
}
|
| 38 |
-
|
| 39 |
-
const { score, reason } = faithfulness;
|
| 40 |
-
|
| 41 |
-
let dotColor, bgColor, textColor, label;
|
| 42 |
-
if (score >= 4) {
|
| 43 |
-
dotColor = "bg-green-500";
|
| 44 |
-
bgColor = "bg-green-50";
|
| 45 |
-
textColor = "text-green-700";
|
| 46 |
-
label = "Faithful";
|
| 47 |
-
} else if (score === 3) {
|
| 48 |
-
dotColor = "bg-yellow-500";
|
| 49 |
-
bgColor = "bg-yellow-50";
|
| 50 |
-
textColor = "text-yellow-700";
|
| 51 |
-
label = "Moderate";
|
| 52 |
-
} else {
|
| 53 |
-
dotColor = "bg-red-500";
|
| 54 |
-
bgColor = "bg-red-50";
|
| 55 |
-
textColor = "text-red-700";
|
| 56 |
-
label = "Low";
|
| 57 |
-
}
|
| 58 |
-
|
| 59 |
-
return (
|
| 60 |
-
<span className="inline-flex items-center gap-1.5">
|
| 61 |
-
<span
|
| 62 |
-
className={`inline-flex items-center gap-1.5 text-xs font-medium px-2.5 py-1 rounded-full ${bgColor} ${textColor}`}
|
| 63 |
-
title={reason}
|
| 64 |
-
>
|
| 65 |
-
<span className={`w-1.5 h-1.5 rounded-full ${dotColor}`} />
|
| 66 |
-
{label} ({score}/5)
|
| 67 |
-
</span>
|
| 68 |
-
<span className="group relative">
|
| 69 |
-
<svg className="w-3.5 h-3.5 text-gray-400 cursor-help" fill="currentColor" viewBox="0 0 20 20">
|
| 70 |
-
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
| 71 |
-
</svg>
|
| 72 |
-
<span className="pointer-events-none absolute bottom-6 left-0 z-10 w-56 rounded-lg bg-gray-800 px-2.5 py-2 text-xs text-white opacity-0 group-hover:opacity-100 transition-opacity shadow-lg">
|
| 73 |
-
LLM-as-judge score (1–5). Measures how grounded the answer is in the retrieved documents. 4–5 = faithful, 3 = partially supported, 1–2 = hallucination risk.
|
| 74 |
-
</span>
|
| 75 |
-
</span>
|
| 76 |
-
</span>
|
| 77 |
-
);
|
| 78 |
-
}
|
| 79 |
|
| 80 |
function CitedText({ text, onCitationClick }) {
|
| 81 |
if (!text) return null;
|
|
@@ -135,12 +85,6 @@ export default function MessageBubble({ message }) {
|
|
| 135 |
<WebSourcesList sources={message.sources} />
|
| 136 |
)}
|
| 137 |
|
| 138 |
-
{!isUser && message.faithfulness && (
|
| 139 |
-
<div className="mt-3">
|
| 140 |
-
<FaithfulnessBadge faithfulness={message.faithfulness} />
|
| 141 |
-
</div>
|
| 142 |
-
)}
|
| 143 |
-
|
| 144 |
{!isUser && message.sources && (
|
| 145 |
<SourceExpander sources={message.sources} />
|
| 146 |
)}
|
|
|
|
| 26 |
);
|
| 27 |
}
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
function CitedText({ text, onCitationClick }) {
|
| 31 |
if (!text) return null;
|
|
|
|
| 85 |
<WebSourcesList sources={message.sources} />
|
| 86 |
)}
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
{!isUser && message.sources && (
|
| 89 |
<SourceExpander sources={message.sources} />
|
| 90 |
)}
|
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Versioned eval script for Prism. Writes timestamped JSON to eval-dashboard/public/data/runs/
|
| 3 |
+
and updates eval-dashboard/public/data/index.json.
|
| 4 |
+
|
| 5 |
+
Metrics:
|
| 6 |
+
answer_correctness — LLM judge (8B): generated answer vs ground_truth, 0–1
|
| 7 |
+
answer_relevancy — RAGAS: does answer address the question?
|
| 8 |
+
context_recall — RAGAS: did retrieval surface all needed chunks?
|
| 9 |
+
precision_at_5 — (relevant chunks in top-5) / 5, source + keyword match
|
| 10 |
+
latency_p50/p95/p99 — per-query end-to-end timing (retrieval + LLM, no Tavily)
|
| 11 |
+
|
| 12 |
+
Usage:
|
| 13 |
+
python scripts/run_eval_versioned.py --version v2.0 --tag "baseline" --n 50
|
| 14 |
+
python scripts/run_eval_versioned.py --version v2.1 --tag "HyDE enabled" --n 50
|
| 15 |
+
|
| 16 |
+
Prerequisites:
|
| 17 |
+
pip install 'ragas>=0.2.0,<0.3.0' datasets
|
| 18 |
+
.env with GROQ_API_KEY + EURON_API_KEY
|
| 19 |
+
scripts/run_ingest.py run first (ChromaDB populated)
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
import argparse
|
| 23 |
+
import json
|
| 24 |
+
import math
|
| 25 |
+
import os
|
| 26 |
+
import sys
|
| 27 |
+
import time
|
| 28 |
+
from datetime import datetime, date
|
| 29 |
+
from pathlib import Path
|
| 30 |
+
|
| 31 |
+
import numpy as np
|
| 32 |
+
|
| 33 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
| 34 |
+
from dotenv import load_dotenv
|
| 35 |
+
load_dotenv()
|
| 36 |
+
|
| 37 |
+
EVAL_PAIRS_PATH = Path(__file__).resolve().parent.parent / "data" / "ground_truth" / "eval_pairs.json"
|
| 38 |
+
DASHBOARD_RUNS_DIR = Path(__file__).resolve().parent.parent / "eval-dashboard" / "public" / "data" / "runs"
|
| 39 |
+
DASHBOARD_INDEX_PATH = Path(__file__).resolve().parent.parent / "eval-dashboard" / "public" / "data" / "index.json"
|
| 40 |
+
|
| 41 |
+
CORRECTNESS_PROMPT = """\
|
| 42 |
+
You are an evaluation judge. Score the generated answer against the reference answer.
|
| 43 |
+
|
| 44 |
+
Use a 1–5 integer scale:
|
| 45 |
+
5 — All key facts present and correct
|
| 46 |
+
4 — Most key facts correct, minor omissions or imprecision
|
| 47 |
+
3 — Some key facts correct, moderate gaps
|
| 48 |
+
2 — Few facts correct, significant errors or hallucinations
|
| 49 |
+
1 — Completely wrong, irrelevant, or contradicts reference
|
| 50 |
+
|
| 51 |
+
Reference answer: {ground_truth}
|
| 52 |
+
|
| 53 |
+
Generated answer: {answer}
|
| 54 |
+
|
| 55 |
+
Respond with JSON only — a single object, nothing else:
|
| 56 |
+
{{"score": 4, "reason": "one sentence"}}"""
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _build_retriever(config, workspace_id="default"):
|
| 60 |
+
from server.bm25_index import build_from_vectorstore
|
| 61 |
+
from server.reranker import load_reranker
|
| 62 |
+
from server.retriever import HybridRetriever
|
| 63 |
+
from langchain_chroma import Chroma
|
| 64 |
+
from langchain_openai import OpenAIEmbeddings
|
| 65 |
+
|
| 66 |
+
print("Loading vectorstore...")
|
| 67 |
+
embeddings = OpenAIEmbeddings(
|
| 68 |
+
model="text-embedding-3-small",
|
| 69 |
+
openai_api_key=os.getenv("EURON_API_KEY", ""),
|
| 70 |
+
openai_api_base="https://api.euron.one/api/v1/euri",
|
| 71 |
+
)
|
| 72 |
+
vectorstore = Chroma(
|
| 73 |
+
collection_name=workspace_id,
|
| 74 |
+
embedding_function=embeddings,
|
| 75 |
+
persist_directory="./chroma_db",
|
| 76 |
+
)
|
| 77 |
+
if vectorstore._collection.count() == 0:
|
| 78 |
+
print(f"ERROR: ChromaDB collection '{workspace_id}' empty. Run scripts/run_ingest.py first.")
|
| 79 |
+
sys.exit(1)
|
| 80 |
+
|
| 81 |
+
print("Building BM25 index...")
|
| 82 |
+
build_from_vectorstore(vectorstore, workspace_id=workspace_id)
|
| 83 |
+
|
| 84 |
+
print("Loading reranker...")
|
| 85 |
+
load_reranker()
|
| 86 |
+
|
| 87 |
+
retrieval_cfg = config.get("retrieval", {})
|
| 88 |
+
retriever = HybridRetriever(
|
| 89 |
+
vectorstore=vectorstore,
|
| 90 |
+
dense_weight=retrieval_cfg.get("dense_weight", 0.7),
|
| 91 |
+
sparse_weight=retrieval_cfg.get("sparse_weight", 0.3),
|
| 92 |
+
retrieve_k=retrieval_cfg.get("retrieve_k", 10),
|
| 93 |
+
rerank_k=retrieval_cfg.get("rerank_k", 5),
|
| 94 |
+
workspace_id=workspace_id,
|
| 95 |
+
use_hyde=retrieval_cfg.get("hyde_enabled", False),
|
| 96 |
+
)
|
| 97 |
+
return retriever
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def _make_llm(model: str, temperature: float = 0.1, max_tokens: int = 500):
|
| 101 |
+
from langchain_groq import ChatGroq
|
| 102 |
+
return ChatGroq(
|
| 103 |
+
model=model,
|
| 104 |
+
api_key=os.getenv("GROQ_API_KEY", ""),
|
| 105 |
+
temperature=temperature,
|
| 106 |
+
max_tokens=max_tokens,
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def _answer_query(llm, retriever, query: str) -> tuple[str, list, list[dict]]:
|
| 111 |
+
"""Returns (answer, lc_docs, chunk_dicts)."""
|
| 112 |
+
from langchain_core.messages import HumanMessage, SystemMessage
|
| 113 |
+
|
| 114 |
+
docs = retriever.invoke(query)
|
| 115 |
+
contexts = [d.page_content for d in docs]
|
| 116 |
+
chunk_dicts = [
|
| 117 |
+
{"content": d.page_content, "source": d.metadata.get("source", "")}
|
| 118 |
+
for d in docs
|
| 119 |
+
]
|
| 120 |
+
ctx_text = "\n\n".join(f"[Doc {i+1}]\n{c}" for i, c in enumerate(contexts))
|
| 121 |
+
messages = [
|
| 122 |
+
SystemMessage(content=(
|
| 123 |
+
"You are a research assistant. Answer using only the provided context. "
|
| 124 |
+
"Be concise and precise. State which document supports your answer."
|
| 125 |
+
)),
|
| 126 |
+
HumanMessage(content=f"Context:\n{ctx_text}\n\nQuestion: {query}\n\nAnswer:"),
|
| 127 |
+
]
|
| 128 |
+
response = llm.invoke(messages)
|
| 129 |
+
return response.content.strip(), docs, chunk_dicts
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def _score_correctness(judge_llm, answer: str, ground_truth: str) -> tuple[float, str]:
|
| 133 |
+
"""Returns (score 0–1, reason). Judge uses 1–5 integer scale normalized to 0–1."""
|
| 134 |
+
from langchain_core.messages import HumanMessage
|
| 135 |
+
import re
|
| 136 |
+
prompt = CORRECTNESS_PROMPT.format(ground_truth=ground_truth, answer=answer)
|
| 137 |
+
try:
|
| 138 |
+
resp = judge_llm.invoke([HumanMessage(content=prompt)])
|
| 139 |
+
raw = resp.content.strip()
|
| 140 |
+
# extract JSON block
|
| 141 |
+
start = raw.find("{")
|
| 142 |
+
end = raw.rfind("}") + 1
|
| 143 |
+
if start == -1:
|
| 144 |
+
raise ValueError(f"No JSON in response: {raw[:120]}")
|
| 145 |
+
parsed = json.loads(raw[start:end])
|
| 146 |
+
raw_score = float(parsed["score"])
|
| 147 |
+
# normalize 1–5 → 0–1; if model returns 0–1 directly, keep as-is
|
| 148 |
+
score = (raw_score - 1) / 4 if raw_score > 1 else raw_score
|
| 149 |
+
score = max(0.0, min(1.0, score))
|
| 150 |
+
return round(score, 4), parsed.get("reason", "")
|
| 151 |
+
except json.JSONDecodeError:
|
| 152 |
+
# fallback: find first integer 1–5 in response
|
| 153 |
+
m = re.search(r'\b([1-5])\b', raw)
|
| 154 |
+
if m:
|
| 155 |
+
raw_score = int(m.group(1))
|
| 156 |
+
score = (raw_score - 1) / 4
|
| 157 |
+
return round(score, 4), f"fallback parse from: {raw[:80]}"
|
| 158 |
+
print(f" correctness judge parse failed: {raw[:120]}")
|
| 159 |
+
return None, f"parse error: {raw[:80]}"
|
| 160 |
+
except Exception as e:
|
| 161 |
+
print(f" correctness judge error: {e}")
|
| 162 |
+
return None, f"error: {e}"
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def _compute_percentile(values: list[float], p: int) -> int:
|
| 166 |
+
if not values:
|
| 167 |
+
return 0
|
| 168 |
+
return int(np.percentile(values, p))
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def main():
|
| 172 |
+
parser = argparse.ArgumentParser()
|
| 173 |
+
parser.add_argument("--version", required=True, help="Version tag e.g. v2.1")
|
| 174 |
+
parser.add_argument("--tag", required=True, help="Short description e.g. 'HyDE enabled'")
|
| 175 |
+
parser.add_argument("--n", type=int, default=50, help="Number of eval pairs (default 50)")
|
| 176 |
+
parser.add_argument("--workspace", default="default", help="ChromaDB workspace collection name")
|
| 177 |
+
parser.add_argument("--judge-model", default="llama-3.1-8b-instant",
|
| 178 |
+
help="Groq judge model for correctness + RAGAS (default: llama-3.1-8b-instant)")
|
| 179 |
+
args = parser.parse_args()
|
| 180 |
+
|
| 181 |
+
print(f"=== Prism Eval — {args.version} | {args.tag} ===\n")
|
| 182 |
+
|
| 183 |
+
missing = [k for k in ("GROQ_API_KEY", "EURON_API_KEY") if not os.getenv(k)]
|
| 184 |
+
if missing:
|
| 185 |
+
print(f"ERROR: Missing env vars: {', '.join(missing)}")
|
| 186 |
+
sys.exit(1)
|
| 187 |
+
|
| 188 |
+
try:
|
| 189 |
+
from ragas import evaluate, EvaluationDataset, SingleTurnSample, RunConfig
|
| 190 |
+
from ragas.metrics import AnswerRelevancy, ContextRecall
|
| 191 |
+
from ragas.llms import LangchainLLMWrapper
|
| 192 |
+
from ragas.embeddings import LangchainEmbeddingsWrapper
|
| 193 |
+
from langchain_openai import OpenAIEmbeddings as _OAIEmb
|
| 194 |
+
except ImportError as e:
|
| 195 |
+
print(f"ERROR: {e}\nInstall: pip install 'ragas>=0.2.0,<0.3.0' datasets")
|
| 196 |
+
sys.exit(1)
|
| 197 |
+
|
| 198 |
+
from server.utils import load_config
|
| 199 |
+
from server.eval.precision import compute_precision_at_k
|
| 200 |
+
|
| 201 |
+
config = load_config()
|
| 202 |
+
retriever = _build_retriever(config, workspace_id=args.workspace)
|
| 203 |
+
answer_llm = _make_llm(config["llm"]["model"], temperature=0.1, max_tokens=500)
|
| 204 |
+
judge_llm = _make_llm(args.judge_model, temperature=0.0, max_tokens=200)
|
| 205 |
+
|
| 206 |
+
ragas_llm = LangchainLLMWrapper(_make_llm(args.judge_model, temperature=0.0, max_tokens=500))
|
| 207 |
+
ragas_emb = LangchainEmbeddingsWrapper(
|
| 208 |
+
_OAIEmb(
|
| 209 |
+
model="text-embedding-3-small",
|
| 210 |
+
openai_api_key=os.getenv("EURON_API_KEY", ""),
|
| 211 |
+
openai_api_base="https://api.euron.one/api/v1/euri",
|
| 212 |
+
)
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
with open(EVAL_PAIRS_PATH) as f:
|
| 216 |
+
all_pairs = json.load(f)
|
| 217 |
+
pairs = all_pairs[:args.n]
|
| 218 |
+
print(f"Evaluating {len(pairs)} pairs with judge={args.judge_model}\n")
|
| 219 |
+
|
| 220 |
+
per_query = []
|
| 221 |
+
ragas_samples = []
|
| 222 |
+
latencies = []
|
| 223 |
+
|
| 224 |
+
for i, pair in enumerate(pairs):
|
| 225 |
+
query = pair["query"]
|
| 226 |
+
ground_truth = pair.get("ground_truth", "")
|
| 227 |
+
print(f"[{i+1:02d}/{len(pairs)}] {query[:70]}")
|
| 228 |
+
|
| 229 |
+
try:
|
| 230 |
+
t0 = time.time()
|
| 231 |
+
answer, lc_docs, chunk_dicts = _answer_query(answer_llm, retriever, query)
|
| 232 |
+
latency_ms = int((time.time() - t0) * 1000)
|
| 233 |
+
latencies.append(latency_ms)
|
| 234 |
+
|
| 235 |
+
correctness, correctness_reason = _score_correctness(judge_llm, answer, ground_truth)
|
| 236 |
+
precision = compute_precision_at_k(query, chunk_dicts, pair, k=5)
|
| 237 |
+
|
| 238 |
+
print(f" correctness={correctness:.2f} p@5={precision:.2f} latency={latency_ms}ms")
|
| 239 |
+
|
| 240 |
+
ragas_samples.append(SingleTurnSample(
|
| 241 |
+
user_input=query,
|
| 242 |
+
response=answer,
|
| 243 |
+
retrieved_contexts=[d.page_content for d in lc_docs],
|
| 244 |
+
reference=ground_truth,
|
| 245 |
+
))
|
| 246 |
+
|
| 247 |
+
per_query.append({
|
| 248 |
+
"query": query,
|
| 249 |
+
"answer": answer,
|
| 250 |
+
"ground_truth": ground_truth,
|
| 251 |
+
"answer_correctness": round(correctness, 4) if correctness is not None else None,
|
| 252 |
+
"correctness_reason": correctness_reason,
|
| 253 |
+
"answer_relevancy": None, # filled after RAGAS run
|
| 254 |
+
"context_recall": None, # filled after RAGAS run
|
| 255 |
+
"precision_at_5": precision,
|
| 256 |
+
"latency_ms": latency_ms,
|
| 257 |
+
"retrieved_sources": [c["source"] for c in chunk_dicts],
|
| 258 |
+
})
|
| 259 |
+
|
| 260 |
+
except Exception as e:
|
| 261 |
+
print(f" SKIP: {e}")
|
| 262 |
+
|
| 263 |
+
# RAGAS batch eval — max_workers=2 to avoid Groq free-tier rate limit timeouts
|
| 264 |
+
print(f"\nRunning RAGAS on {len(ragas_samples)} samples (answer_relevancy + context_recall)...")
|
| 265 |
+
print("Using max_workers=2 to avoid Groq rate limits — will take ~15-20 min for 50 samples")
|
| 266 |
+
dataset = EvaluationDataset(samples=ragas_samples)
|
| 267 |
+
ragas_cfg = RunConfig(timeout=120, max_workers=2, max_retries=5)
|
| 268 |
+
results = evaluate(
|
| 269 |
+
dataset=dataset,
|
| 270 |
+
metrics=[
|
| 271 |
+
AnswerRelevancy(llm=ragas_llm, embeddings=ragas_emb),
|
| 272 |
+
ContextRecall(llm=ragas_llm),
|
| 273 |
+
],
|
| 274 |
+
run_config=ragas_cfg,
|
| 275 |
+
)
|
| 276 |
+
scores_df = results.to_pandas()
|
| 277 |
+
print(f"RAGAS columns: {list(scores_df.columns)}")
|
| 278 |
+
|
| 279 |
+
def _safe_float(val):
|
| 280 |
+
"""Return float or None — never NaN."""
|
| 281 |
+
try:
|
| 282 |
+
f = float(val)
|
| 283 |
+
return None if math.isnan(f) else round(f, 4)
|
| 284 |
+
except (TypeError, ValueError):
|
| 285 |
+
return None
|
| 286 |
+
|
| 287 |
+
# patch per_query with RAGAS per-sample scores
|
| 288 |
+
ragas_idx = 0
|
| 289 |
+
for item in per_query:
|
| 290 |
+
if ragas_idx < len(scores_df):
|
| 291 |
+
row = scores_df.iloc[ragas_idx]
|
| 292 |
+
item["answer_relevancy"] = _safe_float(row.get("answer_relevancy"))
|
| 293 |
+
item["context_recall"] = _safe_float(row.get("context_recall"))
|
| 294 |
+
ragas_idx += 1
|
| 295 |
+
|
| 296 |
+
def safe_mean(key):
|
| 297 |
+
vals = [r[key] for r in per_query if r.get(key) is not None]
|
| 298 |
+
return round(float(np.mean(vals)), 4) if vals else None
|
| 299 |
+
|
| 300 |
+
metrics = {
|
| 301 |
+
"answer_correctness": safe_mean("answer_correctness"),
|
| 302 |
+
"answer_relevancy": safe_mean("answer_relevancy"),
|
| 303 |
+
"context_recall": safe_mean("context_recall"),
|
| 304 |
+
"precision_at_5": safe_mean("precision_at_5"),
|
| 305 |
+
"latency_p50_ms": _compute_percentile(latencies, 50),
|
| 306 |
+
"latency_p95_ms": _compute_percentile(latencies, 95),
|
| 307 |
+
"latency_p99_ms": _compute_percentile(latencies, 99),
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
retrieval_cfg = config.get("retrieval", {})
|
| 311 |
+
run_data = {
|
| 312 |
+
"version": args.version,
|
| 313 |
+
"tag": args.tag,
|
| 314 |
+
"computed_at": datetime.now().isoformat(timespec="seconds"),
|
| 315 |
+
"sample_count": len(per_query),
|
| 316 |
+
"config": {
|
| 317 |
+
"hyde_enabled": retrieval_cfg.get("hyde_enabled", False),
|
| 318 |
+
"retrieve_k": retrieval_cfg.get("retrieve_k", 10),
|
| 319 |
+
"rerank_k": retrieval_cfg.get("rerank_k", 5),
|
| 320 |
+
"llm": config.get("llm", {}).get("model", "unknown"),
|
| 321 |
+
"judge_model": args.judge_model,
|
| 322 |
+
"workspace": args.workspace,
|
| 323 |
+
},
|
| 324 |
+
"metrics": metrics,
|
| 325 |
+
"per_query": per_query,
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
# Write run file
|
| 329 |
+
DASHBOARD_RUNS_DIR.mkdir(parents=True, exist_ok=True)
|
| 330 |
+
run_filename = f"{args.version}_{date.today().strftime('%Y%m%d')}.json"
|
| 331 |
+
run_path = DASHBOARD_RUNS_DIR / run_filename
|
| 332 |
+
with open(run_path, "w") as f:
|
| 333 |
+
json.dump(run_data, f, indent=2)
|
| 334 |
+
print(f"\nRun written to: {run_path}")
|
| 335 |
+
|
| 336 |
+
# Update index
|
| 337 |
+
if DASHBOARD_INDEX_PATH.exists():
|
| 338 |
+
with open(DASHBOARD_INDEX_PATH) as f:
|
| 339 |
+
index = json.load(f)
|
| 340 |
+
else:
|
| 341 |
+
index = []
|
| 342 |
+
|
| 343 |
+
# Replace existing entry for same version, else append
|
| 344 |
+
entry = {"version": args.version, "tag": args.tag, "date": str(date.today()), "file": run_filename}
|
| 345 |
+
index = [e for e in index if e["version"] != args.version]
|
| 346 |
+
index.append(entry)
|
| 347 |
+
index.sort(key=lambda e: e["date"])
|
| 348 |
+
|
| 349 |
+
with open(DASHBOARD_INDEX_PATH, "w") as f:
|
| 350 |
+
json.dump(index, f, indent=2)
|
| 351 |
+
print(f"Index updated: {DASHBOARD_INDEX_PATH}")
|
| 352 |
+
|
| 353 |
+
print(f"\n=== Results — {args.version} ===")
|
| 354 |
+
for k, v in metrics.items():
|
| 355 |
+
print(f" {k:25s}: {v}")
|
| 356 |
+
print(f"\nCommit eval-dashboard/public/data/ to update the live dashboard.")
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
if __name__ == "__main__":
|
| 360 |
+
main()
|
|
@@ -5,7 +5,6 @@ from pydantic import BaseModel
|
|
| 5 |
|
| 6 |
from server.chain import run_query_with_web, condense_question
|
| 7 |
from server.memory import clear_memory
|
| 8 |
-
from server.eval.faithfulness import score_faithfulness
|
| 9 |
from server.utils import setup_logger
|
| 10 |
|
| 11 |
logger = setup_logger(__name__)
|
|
@@ -15,7 +14,7 @@ router = APIRouter()
|
|
| 15 |
|
| 16 |
class ChatRequest(BaseModel):
|
| 17 |
question: str
|
| 18 |
-
web_search: bool =
|
| 19 |
|
| 20 |
|
| 21 |
@router.post("/chat")
|
|
@@ -64,12 +63,9 @@ async def chat(
|
|
| 64 |
if "citation_index" not in src or src["citation_index"] is None:
|
| 65 |
src["citation_index"] = i + 1
|
| 66 |
|
| 67 |
-
faithfulness = score_faithfulness(result["answer"], all_sources)
|
| 68 |
-
|
| 69 |
logger.info(
|
| 70 |
-
"RESPONSE | workspace=%s |
|
| 71 |
workspace,
|
| 72 |
-
faithfulness["score"],
|
| 73 |
len(result["source_documents"]),
|
| 74 |
len(web_sources),
|
| 75 |
)
|
|
@@ -78,8 +74,6 @@ async def chat(
|
|
| 78 |
"query": body.question,
|
| 79 |
"answer": result["answer"],
|
| 80 |
"contexts": [doc["content"] for doc in all_sources],
|
| 81 |
-
"faithfulness_score": faithfulness["score"],
|
| 82 |
-
"reason": faithfulness["reason"],
|
| 83 |
})
|
| 84 |
|
| 85 |
retrieval_method = result.get("retrieval_method", "hybrid+rerank")
|
|
@@ -91,7 +85,6 @@ async def chat(
|
|
| 91 |
return {
|
| 92 |
"answer": result["answer"],
|
| 93 |
"sources": all_sources,
|
| 94 |
-
"faithfulness": faithfulness,
|
| 95 |
"retrieval_method": retrieval_method,
|
| 96 |
}
|
| 97 |
|
|
|
|
| 5 |
|
| 6 |
from server.chain import run_query_with_web, condense_question
|
| 7 |
from server.memory import clear_memory
|
|
|
|
| 8 |
from server.utils import setup_logger
|
| 9 |
|
| 10 |
logger = setup_logger(__name__)
|
|
|
|
| 14 |
|
| 15 |
class ChatRequest(BaseModel):
|
| 16 |
question: str
|
| 17 |
+
web_search: bool = True
|
| 18 |
|
| 19 |
|
| 20 |
@router.post("/chat")
|
|
|
|
| 63 |
if "citation_index" not in src or src["citation_index"] is None:
|
| 64 |
src["citation_index"] = i + 1
|
| 65 |
|
|
|
|
|
|
|
| 66 |
logger.info(
|
| 67 |
+
"RESPONSE | workspace=%s | rag_sources=%d | web_sources=%d",
|
| 68 |
workspace,
|
|
|
|
| 69 |
len(result["source_documents"]),
|
| 70 |
len(web_sources),
|
| 71 |
)
|
|
|
|
| 74 |
"query": body.question,
|
| 75 |
"answer": result["answer"],
|
| 76 |
"contexts": [doc["content"] for doc in all_sources],
|
|
|
|
|
|
|
| 77 |
})
|
| 78 |
|
| 79 |
retrieval_method = result.get("retrieval_method", "hybrid+rerank")
|
|
|
|
| 85 |
return {
|
| 86 |
"answer": result["answer"],
|
| 87 |
"sources": all_sources,
|
|
|
|
| 88 |
"retrieval_method": retrieval_method,
|
| 89 |
}
|
| 90 |
|