From 6e7dcfb5a325a0df009424cf71bf67a3606e055a Mon Sep 17 00:00:00 2001 From: Arshdeep54 Date: Fri, 17 Apr 2026 10:54:06 +0530 Subject: [PATCH 1/6] add document rag demo Signed-off-by: Arshdeep54 --- crates/http/src/lib.rs | 2 + demo/document-rag/.env.example | 8 + demo/document-rag/README.md | 83 + demo/document-rag/backend/Dockerfile | 16 + demo/document-rag/backend/requirements.txt | 9 + demo/document-rag/backend/src/chunker.py | 34 + demo/document-rag/backend/src/config.py | 15 + demo/document-rag/backend/src/embedder.py | 27 + demo/document-rag/backend/src/extractor.py | 55 + demo/document-rag/backend/src/generator.py | 53 + demo/document-rag/backend/src/main.py | 171 ++ demo/document-rag/backend/src/vectorstore.py | 73 + demo/document-rag/docker-compose.yml | 35 + demo/document-rag/frontend/.env.production | 1 + demo/document-rag/frontend/.gitignore | 5 + demo/document-rag/frontend/Dockerfile | 5 + demo/document-rag/frontend/index.html | 12 + demo/document-rag/frontend/nginx.conf | 16 + demo/document-rag/frontend/package-lock.json | 1677 ++++++++++++++++++ demo/document-rag/frontend/package.json | 19 + demo/document-rag/frontend/src/App.css | 592 +++++++ demo/document-rag/frontend/src/App.jsx | 410 +++++ demo/document-rag/frontend/src/main.jsx | 9 + demo/document-rag/frontend/vite.config.js | 14 + 24 files changed, 3341 insertions(+) create mode 100644 demo/document-rag/.env.example create mode 100644 demo/document-rag/README.md create mode 100644 demo/document-rag/backend/Dockerfile create mode 100644 demo/document-rag/backend/requirements.txt create mode 100644 demo/document-rag/backend/src/chunker.py create mode 100644 demo/document-rag/backend/src/config.py create mode 100644 demo/document-rag/backend/src/embedder.py create mode 100644 demo/document-rag/backend/src/extractor.py create mode 100644 demo/document-rag/backend/src/generator.py create mode 100644 demo/document-rag/backend/src/main.py create mode 100644 demo/document-rag/backend/src/vectorstore.py create mode 100644 demo/document-rag/docker-compose.yml create mode 100644 demo/document-rag/frontend/.env.production create mode 100644 demo/document-rag/frontend/.gitignore create mode 100644 demo/document-rag/frontend/Dockerfile create mode 100644 demo/document-rag/frontend/index.html create mode 100644 demo/document-rag/frontend/nginx.conf create mode 100644 demo/document-rag/frontend/package-lock.json create mode 100644 demo/document-rag/frontend/package.json create mode 100644 demo/document-rag/frontend/src/App.css create mode 100644 demo/document-rag/frontend/src/App.jsx create mode 100644 demo/document-rag/frontend/src/main.jsx create mode 100644 demo/document-rag/frontend/vite.config.js diff --git a/crates/http/src/lib.rs b/crates/http/src/lib.rs index 7843aff..69146d5 100644 --- a/crates/http/src/lib.rs +++ b/crates/http/src/lib.rs @@ -3,6 +3,7 @@ pub mod handler; use api::VectorDb; use axum::{ Router, + extract::DefaultBodyLimit, routing::{get, post}, }; use defs::BoxError; @@ -36,6 +37,7 @@ pub fn create_router(db: Arc) -> Router { .route("/points/batch", post(batch_insert_handler)) .route("/points/search/batch", post(batch_search_handler)) .with_state(app_state) + .layer(DefaultBodyLimit::max(50 * 1024 * 1024)) // 50MB limit } /// Runs the HTTP server on the specified address. diff --git a/demo/document-rag/.env.example b/demo/document-rag/.env.example new file mode 100644 index 0000000..9248cb0 --- /dev/null +++ b/demo/document-rag/.env.example @@ -0,0 +1,8 @@ +OPENAI_API_KEY=sk-your-api-key-here +VORTEXDB_HOST=vortexdb +VORTEXDB_PORT=3034 +EMBEDDING_MODEL=text-embedding-3-small +LLM_MODEL=gpt-4o-mini +CHUNK_SIZE=512 +CHUNK_OVERLAP=50 +TOP_K=5 diff --git a/demo/document-rag/README.md b/demo/document-rag/README.md new file mode 100644 index 0000000..c0a1f70 --- /dev/null +++ b/demo/document-rag/README.md @@ -0,0 +1,83 @@ +# VectorDB RAG Demo + +A fully containerized Document RAG demo with a dark-themed web UI. Upload documents, chat with your knowledge base. + +## Quick Start + +```bash +# 1. Fill in your API key +cp .env.example .env +# Edit .env and set OPENAI_API_KEY=sk-your-key-here + +# 2. Build and start everything +docker compose up -d --build + +# 3. Open browser +open http://localhost:3035 +``` + +That's it! No other setup required. + +## Features + +- **File Upload** - Drag & drop or browse documents (PDF, TXT, MD, DOCX, CSV) +- **Chat Interface** - Ask questions, get AI-powered answers +- **Fully Containerized** - VortexDB + Backend + Frontend in Docker + +## Architecture + +``` +Browser (localhost:3035) → Frontend (nginx) + ↓ + Backend API (port 8000) + ↓ + ┌───────────────┴───────────────┐ + ↓ ↓ + OpenAI API VortexDB + (embeddings + LLM) (HTTP port 3000) +``` + +## Configuration + +### .env file + +```env +OPENAI_API_KEY=sk-your-api-key-here +VORTEXDB_HOST=vortexdb +VORTEXDB_PORT=3000 +EMBEDDING_MODEL=text-embedding-3-small +LLM_MODEL=gpt-4o-mini +CHUNK_SIZE=512 +CHUNK_OVERLAP=50 +TOP_K=5 +``` + +## Project Structure + +``` +demo/document-rag/ +├── docker-compose.yml +├── .env.example +├── README.md +├── backend/ +│ ├── src/ +│ │ ├── main.py # FastAPI app +│ │ ├── config.py # Config from env +│ │ ├── chunker.py # Text chunking +│ │ ├── embedder.py # OpenAI embeddings +│ │ ├── generator.py # Chat completion +│ │ ├── extractor.py # Document parsing +│ │ └── vectorstore.py # VortexDB HTTP client +│ ├── requirements.txt +│ └── Dockerfile +└── frontend/ + ├── src/ + │ ├── App.jsx # Main React component + │ ├── App.css # Styles + │ └── main.jsx # Entry point + ├── index.html + ├── package.json + ├── vite.config.js + ├── nginx.conf + └── Dockerfile +``` diff --git a/demo/document-rag/backend/Dockerfile b/demo/document-rag/backend/Dockerfile new file mode 100644 index 0000000..9d63df5 --- /dev/null +++ b/demo/document-rag/backend/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY src/ ./src/ + +RUN mkdir -p /app/uploads + +ENV PYTHONPATH=/app + +EXPOSE 8000 + +CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/demo/document-rag/backend/requirements.txt b/demo/document-rag/backend/requirements.txt new file mode 100644 index 0000000..521531f --- /dev/null +++ b/demo/document-rag/backend/requirements.txt @@ -0,0 +1,9 @@ +fastapi==0.109.2 +uvicorn[standard]==0.27.1 +python-multipart==0.0.9 +openai==1.12.0 +httpx==0.27.0 +pypdf2==3.0.1 +python-docx==1.1.0 +pydantic==2.6.1 +python-dotenv==1.0.1 diff --git a/demo/document-rag/backend/src/chunker.py b/demo/document-rag/backend/src/chunker.py new file mode 100644 index 0000000..d380b46 --- /dev/null +++ b/demo/document-rag/backend/src/chunker.py @@ -0,0 +1,34 @@ +import re +from typing import List + + +def chunk_text(text: str, chunk_size: int = 512, chunk_overlap: int = 50) -> List[str]: + """ + Split text into overlapping chunks. + """ + if not text or not text.strip(): + return [] + + text = re.sub(r'\s+', ' ', text).strip() + + chunks = [] + start = 0 + text_len = len(text) + + while start < text_len: + end = start + chunk_size + chunk = text[start:end] + + if end < text_len: + last_period = chunk.rfind('. ') + last_newline = chunk.rfind('\n') + split_pos = max(last_period, last_newline) + + if split_pos > chunk_size // 2: + chunk = chunk[:split_pos + 1] + end = start + split_pos + 1 + + chunks.append(chunk.strip()) + start = end - chunk_overlap if end < text_len else text_len + + return [c for c in chunks if c] diff --git a/demo/document-rag/backend/src/config.py b/demo/document-rag/backend/src/config.py new file mode 100644 index 0000000..fd6a995 --- /dev/null +++ b/demo/document-rag/backend/src/config.py @@ -0,0 +1,15 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + +class Config: + OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "") + VORTEXDB_HOST: str = os.getenv("VORTEXDB_HOST", "localhost") + VORTEXDB_PORT: int = int(os.getenv("VORTEXDB_PORT", "3034")) + EMBEDDING_MODEL: str = os.getenv("EMBEDDING_MODEL", "text-embedding-3-small") + LLM_MODEL: str = os.getenv("LLM_MODEL", "gpt-4o-mini") + CHUNK_SIZE: int = int(os.getenv("CHUNK_SIZE", "512")) + CHUNK_OVERLAP: int = int(os.getenv("CHUNK_OVERLAP", "50")) + TOP_K: int = int(os.getenv("TOP_K", "5")) + VECTOR_SIZE: int = 1536 diff --git a/demo/document-rag/backend/src/embedder.py b/demo/document-rag/backend/src/embedder.py new file mode 100644 index 0000000..f9c7d4a --- /dev/null +++ b/demo/document-rag/backend/src/embedder.py @@ -0,0 +1,27 @@ +import openai +from openai import OpenAI +from typing import List +from src.config import Config + + +class Embedder: + def __init__(self, api_key: str): + self.client = OpenAI(api_key=api_key) + self.model = Config.EMBEDDING_MODEL + + def embed(self, texts: List[str]) -> List[List[float]]: + """Generate embeddings for a list of texts.""" + if not texts: + return [] + + response = self.client.embeddings.create( + model=self.model, + input=texts + ) + + return [item.embedding for item in response.data] + + def embed_single(self, text: str) -> List[float]: + """Generate embedding for a single text.""" + embeddings = self.embed([text]) + return embeddings[0] if embeddings else [] diff --git a/demo/document-rag/backend/src/extractor.py b/demo/document-rag/backend/src/extractor.py new file mode 100644 index 0000000..9568710 --- /dev/null +++ b/demo/document-rag/backend/src/extractor.py @@ -0,0 +1,55 @@ +from pathlib import Path +from PyPDF2 import PdfReader +import docx + + +SUPPORTED_EXTENSIONS = {'.txt', '.md', '.pdf', '.docx', '.csv'} + + +def extract_text(file_path: str) -> str: + path = Path(file_path) + ext = path.suffix.lower() + + if ext not in SUPPORTED_EXTENSIONS: + raise ValueError(f"Unsupported format: {ext}") + + extractors = { + '.txt': extract_txt, + '.md': extract_markdown, + '.pdf': extract_pdf, + '.docx': extract_docx, + '.csv': extract_csv, + } + + return extractors[ext](file_path) + + +def extract_txt(file_path: str) -> str: + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: + return f.read() + + +def extract_markdown(file_path: str) -> str: + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: + return f.read() + + +def extract_pdf(file_path: str) -> str: + reader = PdfReader(file_path) + text_parts = [] + for page in reader.pages: + text = page.extract_text() + if text: + text_parts.append(text) + return "\n\n".join(text_parts) + + +def extract_docx(file_path: str) -> str: + doc = docx.Document(file_path) + paragraphs = [p.text for p in doc.paragraphs if p.text.strip()] + return "\n\n".join(paragraphs) + + +def extract_csv(file_path: str) -> str: + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: + return f.read() diff --git a/demo/document-rag/backend/src/generator.py b/demo/document-rag/backend/src/generator.py new file mode 100644 index 0000000..0a55a07 --- /dev/null +++ b/demo/document-rag/backend/src/generator.py @@ -0,0 +1,53 @@ +import openai +from openai import OpenAI +from typing import List, Dict +from src.config import Config + + +class Generator: + def __init__(self, api_key: str): + self.client = OpenAI(api_key=api_key) + self.model = Config.LLM_MODEL + + def generate( + self, + question: str, + context_chunks: List[Dict[str, any]] + ) -> str: + """ + Generate answer using RAG prompt. + """ + if not context_chunks: + return "No relevant documents found. Please upload a document first." + + context_text = "\n\n".join([ + f"[Document {i+1}]\n{chunk['text']}" + for i, chunk in enumerate(context_chunks) + ]) + + prompt = f"""You are a helpful assistant answering questions based on provided documents. + +Context from documents: +{context_text} + +Question: {question} + +Instructions: +- Answer based ONLY on the context provided above +- If the answer is not in the context, say "I couldn't find this information in the uploaded documents." +- Be concise and helpful +- Cite which document(s) you're using when relevant + +Answer:""" + + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": "You are a helpful assistant that answers questions based on provided documents."}, + {"role": "user", "content": prompt} + ], + temperature=0.3, + max_tokens=1000 + ) + + return response.choices[0].message.content diff --git a/demo/document-rag/backend/src/main.py b/demo/document-rag/backend/src/main.py new file mode 100644 index 0000000..36aa8f6 --- /dev/null +++ b/demo/document-rag/backend/src/main.py @@ -0,0 +1,171 @@ +from fastapi import FastAPI, UploadFile, File, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse +from contextlib import asynccontextmanager +from typing import Dict +import tempfile +import os + +from src.config import Config +from src.extractor import extract_text, SUPPORTED_EXTENSIONS +from src.chunker import chunk_text +from src.embedder import Embedder +from src.generator import Generator +from src.vectorstore import VectorStore + + +vector_store: VectorStore = None +embedder: Embedder = None +generator: Generator = None + + +@asynccontextmanager +async def lifespan(app: FastAPI): + global vector_store, embedder, generator + + if not Config.OPENAI_API_KEY or Config.OPENAI_API_KEY == "sk-your-api-key-here": + print("Warning: OPENAI_API_KEY not set. Set it in .env file.") + else: + embedder = Embedder(Config.OPENAI_API_KEY) + generator = Generator(Config.OPENAI_API_KEY) + vector_store = VectorStore(Config.VORTEXDB_HOST, Config.VORTEXDB_PORT) + print(f"Connected to VortexDB at {Config.VORTEXDB_HOST}:{Config.VORTEXDB_PORT}") + + yield + + +app = FastAPI(title="Document RAG API", lifespan=lifespan) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@app.get("/") +async def root(): + return {"status": "ok", "message": "Document RAG API"} + + +@app.get("/health") +async def health(): + if not embedder or not vector_store: + return JSONResponse( + status_code=503, + content={"status": "error", "message": "Service not ready. Check API key."} + ) + return {"status": "ok"} + + +@app.post("/upload") +async def upload_document(file: UploadFile = File(...)): + global embedder, vector_store + + if not embedder or not vector_store: + raise HTTPException(status_code=503, detail="Service not ready. Set OPENAI_API_KEY in .env") + + ext = os.path.splitext(file.filename)[1].lower() + if ext not in SUPPORTED_EXTENSIONS: + raise HTTPException( + status_code=400, + detail=f"Unsupported format: {ext}. Supported: {', '.join(SUPPORTED_EXTENSIONS)}" + ) + + if ext in ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp']: + raise HTTPException( + status_code=400, + detail="Image files are not supported. Please upload a text document (PDF, TXT, MD, DOCX, CSV)." + ) + + with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as tmp: + content = await file.read() + tmp.write(content) + tmp_path = tmp.name + + try: + text = extract_text(tmp_path) + + if not text or not text.strip(): + raise HTTPException(status_code=400, detail="Document appears to be empty or no text could be extracted.") + + chunks = chunk_text(text, Config.CHUNK_SIZE, Config.CHUNK_OVERLAP) + + if not chunks: + raise HTTPException(status_code=400, detail="Could not chunk document") + + embeddings = embedder.embed(chunks) + + points_inserted = vector_store.insert_batch(embeddings, chunks, file.filename) + + return { + "success": True, + "filename": file.filename, + "chunks": points_inserted, + "message": f"Document indexed successfully" + } + + except HTTPException: + raise + except Exception as e: + error_msg = str(e) + if "quota" in error_msg.lower() or "429" in error_msg: + raise HTTPException(status_code=429, detail="OpenAI API quota exceeded. Please add billing or wait for quota reset.") + if "clipboard" in error_msg.lower() or "image" in error_msg.lower(): + raise HTTPException(status_code=400, detail="This PDF contains images. Please upload a text-based PDF.") + raise HTTPException(status_code=500, detail=f"Error processing document: {error_msg}") + finally: + os.unlink(tmp_path) + + +@app.post("/chat") +async def chat(question: str = None, body: Dict = None): + global embedder, generator, vector_store + + if not embedder or not generator or not vector_store: + raise HTTPException(status_code=503, detail="Service not ready. Set OPENAI_API_KEY in .env") + + if body: + question = body.get("question", question) + + if not question: + raise HTTPException(status_code=400, detail="Question is required") + + query_embedding = embedder.embed_single(question) + + results = vector_store.search(query_embedding, Config.TOP_K) + + answer = generator.generate(question, results) + + return { + "answer": answer, + "sources": [ + {"text": r["text"][:200] + "..." if len(r["text"]) > 200 else r["text"], + "filename": r["filename"], + "score": round(r["score"], 3)} + for r in results + ] + } + + +@app.delete("/clear") +async def clear(): + global vector_store + + if not vector_store: + raise HTTPException(status_code=503, detail="Service not ready") + + vector_store.clear() + return {"success": True, "message": "All documents cleared"} + + +@app.get("/stats") +async def stats(): + global vector_store + + if not vector_store: + return {"points_count": 0} + + return vector_store.get_info() diff --git a/demo/document-rag/backend/src/vectorstore.py b/demo/document-rag/backend/src/vectorstore.py new file mode 100644 index 0000000..3af8dd6 --- /dev/null +++ b/demo/document-rag/backend/src/vectorstore.py @@ -0,0 +1,73 @@ +import httpx +from typing import List, Dict +from src.config import Config + + +class VectorStore: + def __init__(self, host: str, port: int): + self.base_url = f"http://{host}:{port}" + self.vector_size = Config.VECTOR_SIZE + + def _get_client(self) -> httpx.Client: + return httpx.Client(base_url=self.base_url, timeout=60.0) + + def insert_batch(self, vectors: List[List[float]], texts: List[str], filename: str) -> int: + """Batch insert vectors using VortexDB's batch insert endpoint.""" + client = self._get_client() + + points = [] + for i, (vector, text) in enumerate(zip(vectors, texts)): + points.append({ + "vector": vector, + "payload": { + "content_type": "Text", + "content": text + } + }) + + response = client.post("/points/batch", json={"points": points}) + + if response.status_code != 200: + raise Exception(f"Batch insert failed: {response.text}") + + data = response.json() + return data.get("inserted", len(points)) + + def search(self, query_vector: List[float], top_k: int = 5) -> List[Dict]: + """Search for similar vectors.""" + client = self._get_client() + + response = client.post("/points/search", json={ + "vector": query_vector, + "similarity": "Cosine", + "limit": top_k + }) + + if response.status_code != 200: + return [] + + data = response.json() + results = [] + + for point_id in data.get("results", []): + point_response = client.get(f"/points/{point_id}") + if point_response.status_code == 200: + point = point_response.json() + payload = point.get("payload", {}) + results.append({ + "id": point_id, + "text": payload.get("content", ""), + "filename": "", + "score": 1.0 + }) + + return results + + def get_point_count(self) -> int: + return 0 + + def clear(self): + pass + + def get_info(self) -> Dict: + return {"points_count": 0, "status": "ok"} diff --git a/demo/document-rag/docker-compose.yml b/demo/document-rag/docker-compose.yml new file mode 100644 index 0000000..a37c0f1 --- /dev/null +++ b/demo/document-rag/docker-compose.yml @@ -0,0 +1,35 @@ +services: + vortexdb: + build: ../../ + image: vortexdb:latest + container_name: vortexdb + environment: + HTTP_HOST: "0.0.0.0" + HTTP_PORT: "3000" + STORAGE_TYPE: rocksdb + INDEX_TYPE: hnsw + DIMENSION: 1536 + SIMILARITY: cosine + LOGGING: "true" + GRPC_ROOT_PASSWORD: vortexdb-secret + DISABLE_HTTP: "false" + ports: + - "3034:3000" + + backend: + build: ./backend + env_file: + - .env + environment: + VORTEXDB_HOST: vortexdb + VORTEXDB_PORT: 3000 + OPENAI_API_KEY: ${OPENAI_API_KEY} + depends_on: + - vortexdb + + frontend: + build: ./frontend + ports: + - "3035:80" + depends_on: + - backend diff --git a/demo/document-rag/frontend/.env.production b/demo/document-rag/frontend/.env.production new file mode 100644 index 0000000..e82c617 --- /dev/null +++ b/demo/document-rag/frontend/.env.production @@ -0,0 +1 @@ +VITE_API_URL=/api diff --git a/demo/document-rag/frontend/.gitignore b/demo/document-rag/frontend/.gitignore new file mode 100644 index 0000000..4274b51 --- /dev/null +++ b/demo/document-rag/frontend/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +.env +*.log +.DS_Store diff --git a/demo/document-rag/frontend/Dockerfile b/demo/document-rag/frontend/Dockerfile new file mode 100644 index 0000000..de385bc --- /dev/null +++ b/demo/document-rag/frontend/Dockerfile @@ -0,0 +1,5 @@ +FROM nginx:alpine +COPY dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/demo/document-rag/frontend/index.html b/demo/document-rag/frontend/index.html new file mode 100644 index 0000000..70397cf --- /dev/null +++ b/demo/document-rag/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + VortexDB RAG + + +
+ + + diff --git a/demo/document-rag/frontend/nginx.conf b/demo/document-rag/frontend/nginx.conf new file mode 100644 index 0000000..f2f694c --- /dev/null +++ b/demo/document-rag/frontend/nginx.conf @@ -0,0 +1,16 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/ { + proxy_pass http://backend:8000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } +} diff --git a/demo/document-rag/frontend/package-lock.json b/demo/document-rag/frontend/package-lock.json new file mode 100644 index 0000000..5f31018 --- /dev/null +++ b/demo/document-rag/frontend/package-lock.json @@ -0,0 +1,1677 @@ +{ + "name": "vortexdb-rag-frontend", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vortexdb-rag-frontend", + "version": "0.0.1", + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.2.1", + "vite": "^5.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.19.tgz", + "integrity": "sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001788", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", + "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.339", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.339.tgz", + "integrity": "sha512-Is+0BBHJ4NrdpAYiperrmp53pLywG/yV/6lIMTAnhxvzj/Cmn5Q/ogSHC6AKe7X+8kPLxxFk0cs5oc/3j/fxIg==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/demo/document-rag/frontend/package.json b/demo/document-rag/frontend/package.json new file mode 100644 index 0000000..d0c6096 --- /dev/null +++ b/demo/document-rag/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "vortexdb-rag-frontend", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.2.1", + "vite": "^5.1.0" + } +} diff --git a/demo/document-rag/frontend/src/App.css b/demo/document-rag/frontend/src/App.css new file mode 100644 index 0000000..2cafbeb --- /dev/null +++ b/demo/document-rag/frontend/src/App.css @@ -0,0 +1,592 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --bg-primary: #0d0d0d; + --bg-secondary: #171717; + --bg-tertiary: #1a1a1a; + --bg-hover: #262626; + --bg-input: #262626; + --border-color: #333333; + --border-subtle: #2a2a2a; + --text-primary: #e5e5e5; + --text-secondary: #a0a0a0; + --text-tertiary: #666666; + --accent: #19c37d; + --accent-hover: #15a868; + --error: #ef4444; + --user-bubble: #19c37d; + --shadow: rgba(0, 0, 0, 0.3); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +.app { + display: flex; + height: 100vh; + overflow: hidden; +} + +/* Sidebar */ +.sidebar { + width: 280px; + background: var(--bg-secondary); + border-right: 1px solid var(--border-color); + display: flex; + flex-direction: column; + flex-shrink: 0; +} + +.sidebar-header { + padding: 20px; + border-bottom: 1px solid var(--border-subtle); +} + +.sidebar-header h1 { + font-size: 18px; + font-weight: 600; + color: var(--text-primary); + letter-spacing: -0.02em; +} + +.new-chat-btn { + display: flex; + align-items: center; + gap: 10px; + width: calc(100% - 32px); + margin: 16px; + padding: 12px 16px; + background: transparent; + border: 1px solid var(--border-color); + border-radius: 8px; + color: var(--text-primary); + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; +} + +.new-chat-btn:hover { + background: var(--bg-hover); + border-color: var(--text-tertiary); +} + +.upload-zone { + margin: 0 16px 16px; + padding: 24px; + border: 2px dashed var(--border-color); + border-radius: 12px; + cursor: pointer; + transition: all 0.2s; +} + +.upload-zone:hover, +.upload-zone.dragging { + border-color: var(--accent); + background: rgba(25, 195, 125, 0.05); +} + +.upload-zone input { + display: none; +} + +.upload-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + color: var(--text-secondary); +} + +.upload-content svg { + color: var(--text-tertiary); +} + +.upload-formats { + font-size: 11px; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.documents-list { + flex: 1; + padding: 0 16px; + overflow-y: auto; +} + +.documents-list h3 { + font-size: 12px; + font-weight: 600; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 12px; +} + +.no-docs { + font-size: 13px; + color: var(--text-tertiary); + font-style: italic; +} + +.document-item { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + margin-bottom: 4px; + border-radius: 6px; + font-size: 13px; + color: var(--text-secondary); + cursor: default; +} + +.document-item:hover { + background: var(--bg-hover); +} + +.document-item svg { + flex-shrink: 0; + color: var(--text-tertiary); +} + +.clear-btn { + margin: 16px; + padding: 10px; + background: transparent; + border: 1px solid var(--error); + border-radius: 6px; + color: var(--error); + font-size: 13px; + cursor: pointer; + transition: all 0.2s; +} + +.clear-btn:hover { + background: var(--error); + color: white; +} + +/* Chat Area */ +.chat-area { + flex: 1; + display: flex; + flex-direction: column; + background: var(--bg-primary); + overflow: hidden; +} + +.messages { + flex: 1; + overflow-y: auto; + padding: 24px; +} + +.welcome { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + text-align: center; +} + +.welcome h2 { + font-size: 28px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 12px; + letter-spacing: -0.02em; +} + +.welcome p { + font-size: 15px; + color: var(--text-secondary); +} + +.message { + display: flex; + gap: 16px; + max-width: 800px; + margin: 0 auto 24px; +} + +.message-user { + flex-direction: row-reverse; +} + +.message-avatar { + width: 36px; + height: 36px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.message-user .message-avatar { + background: var(--accent); + color: white; +} + +.message-assistant .message-avatar { + background: var(--bg-tertiary); + color: var(--text-secondary); +} + +.message-error .message-avatar { + background: rgba(239, 68, 68, 0.2); + color: var(--error); +} + +.message-content { + flex: 1; + min-width: 0; +} + +.message-user .message-content { + text-align: right; +} + +.message-text { + padding: 12px 16px; + border-radius: 12px; + font-size: 14px; + line-height: 1.6; + word-wrap: break-word; +} + +.message-user .message-text { + background: var(--user-bubble); + color: white; + border-bottom-right-radius: 4px; +} + +.message-assistant .message-text { + background: var(--bg-secondary); + color: var(--text-primary); + border: 1px solid var(--border-subtle); + border-bottom-left-radius: 4px; +} + +.message-error .message-text { + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + color: var(--error); +} + +.sources { + margin-top: 16px; + padding: 12px; + background: var(--bg-secondary); + border: 1px solid var(--border-subtle); + border-radius: 8px; +} + +.sources h4 { + font-size: 12px; + font-weight: 600; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 10px; +} + +.source-item { + display: flex; + gap: 8px; + padding: 8px 0; + border-bottom: 1px solid var(--border-subtle); + font-size: 13px; +} + +.source-item:last-child { + border-bottom: none; + padding-bottom: 0; +} + +.source-score { + color: var(--accent); + font-weight: 500; + flex-shrink: 0; +} + +.source-text { + color: var(--text-secondary); + word-break: break-word; +} + +/* Typing indicator */ +.typing { + display: flex; + gap: 4px; + padding: 16px 20px; +} + +.typing .dot { + width: 8px; + height: 8px; + background: var(--text-tertiary); + border-radius: 50%; + animation: typing 1.4s infinite; +} + +.typing .dot:nth-child(2) { + animation-delay: 0.2s; +} + +.typing .dot:nth-child(3) { + animation-delay: 0.4s; +} + +@keyframes typing { + 0%, 60%, 100% { + transform: translateY(0); + opacity: 0.4; + } + 30% { + transform: translateY(-4px); + opacity: 1; + } +} + +/* Input Area */ +.input-area { + padding: 16px 24px 24px; + background: var(--bg-primary); +} + +.input-container { + display: flex; + align-items: center; + gap: 12px; + max-width: 800px; + margin: 0 auto; + padding: 12px 16px; + background: var(--bg-input); + border: 1px solid var(--border-color); + border-radius: 12px; + transition: all 0.2s; +} + +.input-container:focus-within { + border-color: var(--accent); + box-shadow: 0 0 0 2px rgba(25, 195, 125, 0.1); +} + +.input-container.dragging { + border-color: var(--accent); + background: rgba(25, 195, 125, 0.05); +} + +.input-container input { + flex: 1; + background: transparent; + border: none; + outline: none; + color: var(--text-primary); + font-size: 14px; + font-family: inherit; +} + +.input-container input::placeholder { + color: var(--text-tertiary); +} + +.input-container button { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + background: var(--accent); + border: none; + border-radius: 8px; + color: white; + cursor: pointer; + transition: all 0.2s; +} + +.input-container button:hover:not(:disabled) { + background: var(--accent-hover); +} + +.input-container button:disabled { + background: var(--bg-hover); + color: var(--text-tertiary); + cursor: not-allowed; +} + +/* Scrollbar */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--border-color); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-tertiary); +} + +/* Responsive */ +@media (max-width: 768px) { + .sidebar { + display: none; + } +} + +/* Modal */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.8); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + backdrop-filter: blur(4px); +} + +.modal { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 16px; + padding: 32px; + min-width: 360px; + max-width: 420px; + text-align: center; +} + +.modal-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +.modal-spinner { + display: flex; + align-items: center; + justify-content: center; +} + +.spinner { + width: 48px; + height: 48px; + border: 3px solid var(--border-color); + border-top-color: var(--accent); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.modal-icon { + display: flex; + align-items: center; + justify-content: center; + width: 64px; + height: 64px; + border-radius: 50%; +} + +.modal-icon.success { + background: rgba(25, 195, 125, 0.15); + color: var(--accent); +} + +.modal-icon.error { + background: rgba(239, 68, 68, 0.15); + color: var(--error); +} + +.modal h3 { + font-size: 20px; + font-weight: 600; + color: var(--text-primary); +} + +.modal-filename { + font-size: 13px; + color: var(--text-secondary); + word-break: break-all; + max-width: 100%; +} + +.modal-detail { + font-size: 14px; + color: var(--text-tertiary); +} + +.modal-progress { + width: 100%; + display: flex; + align-items: center; + gap: 12px; +} + +.progress-bar { + flex: 1; + height: 6px; + background: var(--bg-hover); + border-radius: 3px; + overflow: hidden; +} + +.progress-fill { + height: 100%; + background: var(--accent); + border-radius: 3px; + transition: width 0.3s ease; +} + +.progress-text { + font-size: 13px; + font-weight: 600; + color: var(--text-secondary); + min-width: 40px; + text-align: right; +} + +.modal-close-btn { + margin-top: 8px; + padding: 10px 24px; + background: var(--bg-hover); + border: 1px solid var(--border-color); + border-radius: 8px; + color: var(--text-primary); + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; +} + +.modal-close-btn:hover { + background: var(--error); + border-color: var(--error); +} diff --git a/demo/document-rag/frontend/src/App.jsx b/demo/document-rag/frontend/src/App.jsx new file mode 100644 index 0000000..0ab1726 --- /dev/null +++ b/demo/document-rag/frontend/src/App.jsx @@ -0,0 +1,410 @@ +import { useState, useRef, useEffect } from 'react'; +import './App.css'; + +const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8002'; + +const SUPPORTED_FORMATS = ['.pdf', '.txt', '.md', '.docx', '.csv']; +const IMAGE_FORMATS = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp']; + +function App() { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [uploadedFiles, setUploadedFiles] = useState(new Set()); + const [isDragging, setIsDragging] = useState(false); + const [uploadModal, setUploadModal] = useState(null); + const messagesEndRef = useRef(null); + const fileInputRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); + + const updateModal = (status, progress = null, detail = null) => { + setUploadModal(prev => ({ + ...prev, + status, + progress, + detail, + timestamp: Date.now() + })); + }; + + const handleFileSelect = async (file) => { + const ext = '.' + file.name.split('.').pop().toLowerCase(); + + if (IMAGE_FORMATS.includes(ext)) { + setUploadModal({ + status: 'error', + fileName: file.name, + detail: `Image files are not supported. This model does not support image input. Please upload a text document (${SUPPORTED_FORMATS.join(', ')}).` + }); + return; + } + + if (!SUPPORTED_FORMATS.includes(ext)) { + setUploadModal({ + status: 'error', + fileName: file.name, + detail: `Unsupported format: ${ext}. Please upload ${SUPPORTED_FORMATS.join(', ')} files.` + }); + return; + } + + setUploadModal({ + status: 'uploading', + fileName: file.name, + progress: 0, + detail: 'Reading file...' + }); + + const formData = new FormData(); + formData.append('file', file); + + try { + updateModal('uploading', 10, 'Uploading to server...'); + + const res = await fetch(`${API_URL}/upload`, { + method: 'POST', + body: formData, + }); + + updateModal('processing', 50, 'Processing document...'); + + const data = await res.json(); + + if (!res.ok) { + throw new Error(data.detail || 'Upload failed'); + } + + updateModal('vectors', 75, 'Creating embeddings...'); + + await new Promise(resolve => setTimeout(resolve, 500)); + + updateModal('indexing', 90, 'Indexing vectors...'); + + await new Promise(resolve => setTimeout(resolve, 300)); + + updateModal('complete', 100, `Indexed ${data.chunks} chunks successfully!`); + + setUploadedFiles(prev => new Set([...prev, file.name])); + + setTimeout(() => { + setUploadModal(null); + addMessage('system', `📄 Uploaded: ${file.name} (${data.chunks} chunks indexed)`); + }, 1500); + + } catch (e) { + setUploadModal({ + status: 'error', + fileName: file.name, + detail: e.message + }); + } + }; + + const closeModal = () => { + if (uploadModal?.status !== 'uploading' && uploadModal?.status !== 'processing') { + setUploadModal(null); + } + }; + + const handleDrop = (e) => { + e.preventDefault(); + setIsDragging(false); + const file = e.dataTransfer.files[0]; + if (file) handleFileSelect(file); + }; + + const handleDragOver = (e) => { + e.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = () => { + setIsDragging(false); + }; + + const addMessage = (role, content, sources = []) => { + setMessages(prev => [...prev, { role, content, sources, id: Date.now() }]); + }; + + const addError = (content) => { + setMessages(prev => [...prev, { role: 'error', content, id: Date.now() }]); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + if (!input.trim() || isLoading) return; + + const question = input.trim(); + setInput(''); + addMessage('user', question); + setIsLoading(true); + + try { + const res = await fetch(`${API_URL}/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ question }), + }); + + const data = await res.json(); + + if (!res.ok) { + throw new Error(data.detail || 'Request failed'); + } + + addMessage('assistant', data.answer, data.sources || []); + } catch (e) { + addError(e.message); + } finally { + setIsLoading(false); + } + }; + + const clearChat = () => { + setMessages([]); + }; + + const clearAll = async () => { + if (!confirm('Clear all documents and chat?')) return; + + try { + await fetch(`${API_URL}/clear`, { method: 'DELETE' }); + setUploadedFiles(new Set()); + setMessages([]); + } catch (e) { + addError('Failed to clear documents'); + } + }; + + const getStatusIcon = () => { + if (!uploadModal) return null; + + switch (uploadModal.status) { + case 'uploading': + case 'processing': + case 'vectors': + case 'indexing': + return ( +
+
+
+ ); + case 'complete': + return ( +
+ + + + +
+ ); + case 'error': + return ( +
+ + + + + +
+ ); + default: + return null; + } + }; + + const getStatusText = () => { + if (!uploadModal) return ''; + + switch (uploadModal.status) { + case 'uploading': return 'Uploading'; + case 'processing': return 'Processing'; + case 'vectors': return 'Creating Embeddings'; + case 'indexing': return 'Indexing'; + case 'complete': return 'Complete'; + case 'error': return 'Error'; + default: return ''; + } + }; + + return ( +
+ {uploadModal && ( +
+
e.stopPropagation()}> +
+ {getStatusIcon()} +

{getStatusText()}

+

{uploadModal.fileName}

+ + {(uploadModal.status === 'uploading' || uploadModal.status === 'processing' || + uploadModal.status === 'vectors' || uploadModal.status === 'indexing') && ( +
+
+
+
+ {uploadModal.progress}% +
+ )} + +

{uploadModal.detail}

+ + {uploadModal.status === 'error' && ( + + )} +
+
+
+ )} + + + +
+
+ {messages.length === 0 && ( +
+

VortexDB RAG Demo

+

Upload documents and ask questions about them

+
+ )} + + {messages.map((msg, i) => ( +
+
+ {msg.role === 'user' ? ( + + + + ) : msg.role === 'error' ? ( + + + + ) : ( + + + + )} +
+
+
{msg.content}
+ {msg.sources && msg.sources.length > 0 && ( +
+

Sources:

+ {msg.sources.map((s, j) => ( +
+ [{s.score}] + {s.text} +
+ ))} +
+ )} +
+
+ ))} + + {isLoading && ( +
+
+ + + +
+
+
+ + + +
+
+
+ )} +
+
+ +
+
+ setInput(e.target.value)} + placeholder="Ask a question about your documents..." + disabled={isLoading} + /> + +
+
+
+
+ ); +} + +export default App; diff --git a/demo/document-rag/frontend/src/main.jsx b/demo/document-rag/frontend/src/main.jsx new file mode 100644 index 0000000..3d9da8a --- /dev/null +++ b/demo/document-rag/frontend/src/main.jsx @@ -0,0 +1,9 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.jsx' + +createRoot(document.getElementById('root')).render( + + + , +) diff --git a/demo/document-rag/frontend/vite.config.js b/demo/document-rag/frontend/vite.config.js new file mode 100644 index 0000000..9d1dd06 --- /dev/null +++ b/demo/document-rag/frontend/vite.config.js @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + base: '/', + server: { + port: 5173, + host: true, + }, + build: { + outDir: 'dist', + }, +}) From a7f247769353e4008c4fa51ace5b251572cc9a6b Mon Sep 17 00:00:00 2001 From: ishaan Date: Tue, 18 Aug 2026 00:59:19 +0530 Subject: [PATCH 2/6] feat(demo): update document-rag demo with pre-ingestion and ratelimiting support --- demo/document-rag/.env.example | 13 +- demo/document-rag/README.md | 131 +++-- demo/document-rag/backend/Dockerfile | 3 +- demo/document-rag/backend/requirements.txt | 16 +- demo/document-rag/backend/src/config.py | 14 +- demo/document-rag/backend/src/embedder.py | 31 +- demo/document-rag/backend/src/generator.py | 51 +- demo/document-rag/backend/src/ingestion.py | 87 ++++ demo/document-rag/backend/src/main.py | 210 ++++---- demo/document-rag/backend/src/vectorstore.py | 82 ++- demo/document-rag/docker-compose.yml | 52 +- .../documents/chunking-config.csv | 5 + .../documents/cloudflare-models.txt | 7 + .../documents/ingestion-pipeline.md | 5 + demo/document-rag/documents/rag-api.txt | 7 + .../documents/vortexdb-overview.txt | 5 + demo/document-rag/frontend/.env.development | 1 + demo/document-rag/frontend/Dockerfile | 16 +- demo/document-rag/frontend/nginx.conf | 1 + demo/document-rag/frontend/src/App.css | 307 ++++-------- demo/document-rag/frontend/src/App.jsx | 468 ++++++++---------- demo/document-rag/frontend/src/api.js | 36 ++ demo/document-rag/frontend/vite.config.js | 7 + 23 files changed, 823 insertions(+), 732 deletions(-) create mode 100644 demo/document-rag/backend/src/ingestion.py create mode 100644 demo/document-rag/documents/chunking-config.csv create mode 100644 demo/document-rag/documents/cloudflare-models.txt create mode 100644 demo/document-rag/documents/ingestion-pipeline.md create mode 100644 demo/document-rag/documents/rag-api.txt create mode 100644 demo/document-rag/documents/vortexdb-overview.txt create mode 100644 demo/document-rag/frontend/.env.development create mode 100644 demo/document-rag/frontend/src/api.js diff --git a/demo/document-rag/.env.example b/demo/document-rag/.env.example index 9248cb0..c898e33 100644 --- a/demo/document-rag/.env.example +++ b/demo/document-rag/.env.example @@ -1,8 +1,15 @@ -OPENAI_API_KEY=sk-your-api-key-here +OPENAI_API_KEY=your-api-token +ACCOUNT_ID=your-account-id + +EMBEDDING_MODEL=@cf/qwen/qwen3-embedding-0.6b +LLM_MODEL=@cf/openai/gpt-oss-20b +VECTOR_SIZE=1024 + VORTEXDB_HOST=vortexdb VORTEXDB_PORT=3034 -EMBEDDING_MODEL=text-embedding-3-small -LLM_MODEL=gpt-4o-mini CHUNK_SIZE=512 CHUNK_OVERLAP=50 TOP_K=5 +EMBEDDING_BATCH_SIZE=64 +DOCS_DIRECTORY=./documents + diff --git a/demo/document-rag/README.md b/demo/document-rag/README.md index c0a1f70..0772c93 100644 --- a/demo/document-rag/README.md +++ b/demo/document-rag/README.md @@ -1,55 +1,111 @@ -# VectorDB RAG Demo +# VortexDB RAG Demo -A fully containerized Document RAG demo with a dark-themed web UI. Upload documents, chat with your knowledge base. +A containerized document RAG demo: offline ingest into VortexDB, query via a FastAPI backend, and chat through a React frontend. Embeddings and LLM calls use [Cloudflare Workers AI](https://developers.cloudflare.com/workers-ai/) through the OpenAI-compatible API. ## Quick Start ```bash -# 1. Fill in your API key +# 1. Configure credentials cp .env.example .env -# Edit .env and set OPENAI_API_KEY=sk-your-key-here +# Set OPENAI_API_KEY (Cloudflare API token) and ACCOUNT_ID -# 2. Build and start everything +# 2. Add documents to ingest +cp /path/to/your/files/* documents/ + +# 3. Start the stack (builds images, ingests docs, starts API + UI) docker compose up -d --build -# 3. Open browser +# 4. Open the UI open http://localhost:3035 ``` -That's it! No other setup required. +On startup, Docker will: + +1. Start VortexDB and wait until healthy +2. Run ingest on `./documents` (TXT, MD, PDF, DOCX, CSV) +3. Start the backend API +4. Start the frontend + +To re-ingest after adding or changing documents: + +```bash +docker compose up ingest --force-recreate +docker compose up -d backend +``` -## Features +## Ports -- **File Upload** - Drag & drop or browse documents (PDF, TXT, MD, DOCX, CSV) -- **Chat Interface** - Ask questions, get AI-powered answers -- **Fully Containerized** - VortexDB + Backend + Frontend in Docker +| Service | URL | +|-----------|--------------------------| +| Frontend | http://localhost:3035 | +| Backend | http://localhost:8000 | +| VortexDB | http://localhost:3034 | ## Architecture ``` -Browser (localhost:3035) → Frontend (nginx) +documents/ → ingest (one-shot) → VortexDB + ↑ +Browser (3035) → nginx → backend (8000) ─┘ ↓ - Backend API (port 8000) - ↓ - ┌───────────────┴───────────────┐ - ↓ ↓ - OpenAI API VortexDB - (embeddings + LLM) (HTTP port 3000) + Cloudflare Workers AI + (embeddings + LLM) +``` + +## API + +| Method | Path | Body | Description | +|--------|----------|-------------------------|--------------------------| +| GET | `/health`| — | Health check | +| POST | `/chat` | `{"query": "..."}` | RAG answer + sources | +| POST | `/query` | `{"query": "..."}` | Retrieval only (no LLM) | + +Example: + +```bash +curl -X POST http://localhost:8000/chat \ + -H "Content-Type: application/json" \ + -d '{"query": "What embedding model is used?"}' ``` ## Configuration -### .env file +All settings live in `.env`. See `.env.example` for the full list. ```env -OPENAI_API_KEY=sk-your-api-key-here -VORTEXDB_HOST=vortexdb -VORTEXDB_PORT=3000 -EMBEDDING_MODEL=text-embedding-3-small -LLM_MODEL=gpt-4o-mini +OPENAI_API_KEY=your-cloudflare-api-token +ACCOUNT_ID=your-account-id + +EMBEDDING_MODEL=@cf/qwen/qwen3-embedding-0.6b +LLM_MODEL=@cf/openai/gpt-oss-20b +VECTOR_SIZE=1024 + CHUNK_SIZE=512 CHUNK_OVERLAP=50 TOP_K=5 +EMBEDDING_BATCH_SIZE=64 +DOCS_DIRECTORY=./documents +``` + +Get credentials from the [Cloudflare dashboard](https://dash.cloudflare.com/) → Workers AI → **Use REST API**. The API token needs Workers AI read access. `VECTOR_SIZE` must match VortexDB's `DIMENSION` in `docker-compose.yml` (default `1024`). + +Inside Docker Compose, `VORTEXDB_HOST` and `VORTEXDB_PORT` are overridden to `vortexdb:3000` automatically. + +## Local Development (without Docker) + +```bash +# Terminal 1 — VortexDB (from repo root) +HTTP_HOST=0.0.0.0 HTTP_PORT=3034 STORAGE_TYPE=inmemory INDEX_TYPE=flat \ + DIMENSION=1024 cargo run --release --bin server + +# Terminal 2 — ingest +cd backend +pip install -r requirements.txt +cp ../.env.example ../.env # fill in credentials +python -m src.ingestion + +# Terminal 3 — API +uvicorn src.main:app --reload --port 8000 ``` ## Project Structure @@ -58,26 +114,21 @@ TOP_K=5 demo/document-rag/ ├── docker-compose.yml ├── .env.example -├── README.md +├── documents/ # Drop files here for ingest ├── backend/ │ ├── src/ -│ │ ├── main.py # FastAPI app -│ │ ├── config.py # Config from env -│ │ ├── chunker.py # Text chunking -│ │ ├── embedder.py # OpenAI embeddings -│ │ ├── generator.py # Chat completion -│ │ ├── extractor.py # Document parsing -│ │ └── vectorstore.py # VortexDB HTTP client +│ │ ├── main.py # FastAPI app +│ │ ├── ingestion.py # Offline ingest CLI +│ │ ├── config.py # Env-based config +│ │ ├── chunker.py +│ │ ├── embedder.py # Cloudflare embeddings +│ │ ├── generator.py # Cloudflare chat completions +│ │ ├── extractor.py +│ │ └── vectorstore.py # VortexDB HTTP client │ ├── requirements.txt │ └── Dockerfile └── frontend/ - ├── src/ - │ ├── App.jsx # Main React component - │ ├── App.css # Styles - │ └── main.jsx # Entry point - ├── index.html - ├── package.json - ├── vite.config.js - ├── nginx.conf + ├── src/App.jsx + ├── nginx.conf # Proxies /api → backend:8000 └── Dockerfile ``` diff --git a/demo/document-rag/backend/Dockerfile b/demo/document-rag/backend/Dockerfile index 9d63df5..4800b59 100644 --- a/demo/document-rag/backend/Dockerfile +++ b/demo/document-rag/backend/Dockerfile @@ -7,9 +7,10 @@ RUN pip install --no-cache-dir -r requirements.txt COPY src/ ./src/ -RUN mkdir -p /app/uploads +RUN mkdir -p /app/documents ENV PYTHONPATH=/app +ENV DOCS_DIRECTORY=/app/documents EXPOSE 8000 diff --git a/demo/document-rag/backend/requirements.txt b/demo/document-rag/backend/requirements.txt index 521531f..14d0639 100644 --- a/demo/document-rag/backend/requirements.txt +++ b/demo/document-rag/backend/requirements.txt @@ -1,9 +1,9 @@ -fastapi==0.109.2 -uvicorn[standard]==0.27.1 -python-multipart==0.0.9 -openai==1.12.0 -httpx==0.27.0 -pypdf2==3.0.1 -python-docx==1.1.0 -pydantic==2.6.1 +fastapi==0.124.4 +uvicorn[standard]==0.41.0 +httpx==0.28.1 +openai==2.30.0 python-dotenv==1.0.1 +slowapi==0.1.10 +python-multipart==0.0.22 +PyPDF2==3.0.1 +python-docx==1.1.2 diff --git a/demo/document-rag/backend/src/config.py b/demo/document-rag/backend/src/config.py index fd6a995..10e9bd6 100644 --- a/demo/document-rag/backend/src/config.py +++ b/demo/document-rag/backend/src/config.py @@ -3,13 +3,21 @@ load_dotenv() +_account_id = os.getenv("ACCOUNT_ID", "") + + class Config: OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "") + OPENAI_BASE_URL: str = ( + f"https://api.cloudflare.com/client/v4/accounts/{_account_id}/ai/v1" + ) VORTEXDB_HOST: str = os.getenv("VORTEXDB_HOST", "localhost") VORTEXDB_PORT: int = int(os.getenv("VORTEXDB_PORT", "3034")) - EMBEDDING_MODEL: str = os.getenv("EMBEDDING_MODEL", "text-embedding-3-small") - LLM_MODEL: str = os.getenv("LLM_MODEL", "gpt-4o-mini") + EMBEDDING_MODEL: str = os.getenv("EMBEDDING_MODEL", "") + LLM_MODEL: str = os.getenv("LLM_MODEL", "") CHUNK_SIZE: int = int(os.getenv("CHUNK_SIZE", "512")) CHUNK_OVERLAP: int = int(os.getenv("CHUNK_OVERLAP", "50")) TOP_K: int = int(os.getenv("TOP_K", "5")) - VECTOR_SIZE: int = 1536 + VECTOR_SIZE: int = int(os.getenv("VECTOR_SIZE", "")) + DOCS_DIRECTORY: str = os.getenv("DOCS_DIRECTORY", "./documents") + EMBEDDING_BATCH_SIZE: int = int(os.getenv("EMBEDDING_BATCH_SIZE", "64")) diff --git a/demo/document-rag/backend/src/embedder.py b/demo/document-rag/backend/src/embedder.py index f9c7d4a..57f29b6 100644 --- a/demo/document-rag/backend/src/embedder.py +++ b/demo/document-rag/backend/src/embedder.py @@ -1,26 +1,31 @@ -import openai -from openai import OpenAI from typing import List + +from openai import OpenAI + from src.config import Config class Embedder: - def __init__(self, api_key: str): - self.client = OpenAI(api_key=api_key) + def __init__(self): + self.client = OpenAI( + api_key=Config.OPENAI_API_KEY, + base_url=Config.OPENAI_BASE_URL, + ) self.model = Config.EMBEDDING_MODEL - + def embed(self, texts: List[str]) -> List[List[float]]: """Generate embeddings for a list of texts.""" if not texts: return [] - - response = self.client.embeddings.create( - model=self.model, - input=texts - ) - - return [item.embedding for item in response.data] - + + embeddings = [] + for i in range(0, len(texts), Config.EMBEDDING_BATCH_SIZE): + batch = texts[i : i + Config.EMBEDDING_BATCH_SIZE] + response = self.client.embeddings.create(model=self.model, input=batch) + embeddings.extend(item.embedding for item in response.data) + + return embeddings + def embed_single(self, text: str) -> List[float]: """Generate embedding for a single text.""" embeddings = self.embed([text]) diff --git a/demo/document-rag/backend/src/generator.py b/demo/document-rag/backend/src/generator.py index 0a55a07..3200f9e 100644 --- a/demo/document-rag/backend/src/generator.py +++ b/demo/document-rag/backend/src/generator.py @@ -1,30 +1,30 @@ -import openai +from typing import Dict, List + from openai import OpenAI -from typing import List, Dict + from src.config import Config class Generator: - def __init__(self, api_key: str): - self.client = OpenAI(api_key=api_key) + def __init__(self): + self.client = OpenAI( + api_key=Config.OPENAI_API_KEY, + base_url=Config.OPENAI_BASE_URL, + ) self.model = Config.LLM_MODEL - - def generate( - self, - question: str, - context_chunks: List[Dict[str, any]] - ) -> str: - """ - Generate answer using RAG prompt. - """ + + def generate(self, question: str, context_chunks: List[Dict[str, any]]) -> str: + """Generate answer using RAG prompt.""" if not context_chunks: return "No relevant documents found. Please upload a document first." - - context_text = "\n\n".join([ - f"[Document {i+1}]\n{chunk['text']}" - for i, chunk in enumerate(context_chunks) - ]) - + + context_text = "\n\n".join( + [ + f"[Document {i + 1}]\n{chunk['text']}" + for i, chunk in enumerate(context_chunks) + ] + ) + prompt = f"""You are a helpful assistant answering questions based on provided documents. Context from documents: @@ -39,15 +39,18 @@ def generate( - Cite which document(s) you're using when relevant Answer:""" - + response = self.client.chat.completions.create( model=self.model, messages=[ - {"role": "system", "content": "You are a helpful assistant that answers questions based on provided documents."}, - {"role": "user", "content": prompt} + { + "role": "system", + "content": "You are a helpful assistant that answers questions based on provided documents.", + }, + {"role": "user", "content": prompt}, ], temperature=0.3, - max_tokens=1000 + max_tokens=1000, ) - + return response.choices[0].message.content diff --git a/demo/document-rag/backend/src/ingestion.py b/demo/document-rag/backend/src/ingestion.py new file mode 100644 index 0000000..c1918d3 --- /dev/null +++ b/demo/document-rag/backend/src/ingestion.py @@ -0,0 +1,87 @@ +import os +import sys +from pathlib import Path + +from src.chunker import chunk_text +from src.config import Config +from src.embedder import Embedder +from src.extractor import extract_text +from src.vectorstore import VectorStore + +SUPPORTED_EXTENSIONS = {".txt", ".md", ".pdf", ".docx", ".csv"} + + +def keep_supported_files(directory: str) -> list[Path]: + file_paths = [] + for root, _, files in os.walk(directory): + for file in files: + file_path = Path(root) / file + if file_path.suffix.lower() in SUPPORTED_EXTENSIONS: + file_paths.append(file_path) + return file_paths + + +def parse_files(directory: str) -> list[dict]: + parsed_files = [] + for file_path in keep_supported_files(directory): + extracted_text = extract_text(file_path) + chunks = chunk_text( + extracted_text, + chunk_size=Config.CHUNK_SIZE, + chunk_overlap=Config.CHUNK_OVERLAP, + ) + parsed_files.append({"file_path": str(file_path), "chunks": chunks}) + return parsed_files + + +def embed_chunks(parsed_files: list[dict]) -> list[dict]: + embedder = Embedder() + embedded_files = [] + for parsed_file in parsed_files: + embedded_files.append( + { + "file_path": parsed_file["file_path"], + "chunks": parsed_file["chunks"], + "embeddings": embedder.embed(parsed_file["chunks"]), + } + ) + return embedded_files + + +def ingest_documents() -> None: + directory = os.path.expanduser(Config.DOCS_DIRECTORY) + + if not Config.OPENAI_API_KEY: + print("Error: OPENAI_API_KEY not set.") + sys.exit(1) + + if not os.path.isdir(directory): + print(f"Error: Documents directory not found: {directory}") + sys.exit(1) + + parsed_files = parse_files(directory) + if not parsed_files: + print(f"No supported documents found in {directory}. Skipping ingest.") + return + + embedded_files = embed_chunks(parsed_files) + vector_store = VectorStore(Config.VORTEXDB_HOST, Config.VORTEXDB_PORT) + + total_inserted = 0 + for embedded_file in embedded_files: + inserted = vector_store.insert_batch( + embedded_file["embeddings"], + embedded_file["chunks"], + embedded_file["file_path"], + ) + total_inserted += inserted + print(f"Ingested {inserted} chunks from {embedded_file['file_path']}") + + print( + f"Done. Ingested {total_inserted} chunks from {len(embedded_files)} files " + f"into VortexDB at {Config.VORTEXDB_HOST}:{Config.VORTEXDB_PORT}" + ) + + +if __name__ == "__main__": + ingest_documents() diff --git a/demo/document-rag/backend/src/main.py b/demo/document-rag/backend/src/main.py index 36aa8f6..018ac4d 100644 --- a/demo/document-rag/backend/src/main.py +++ b/demo/document-rag/backend/src/main.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI, UploadFile, File, HTTPException +from fastapi import FastAPI, Request, UploadFile, File, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from contextlib import asynccontextmanager @@ -6,166 +6,160 @@ import tempfile import os +from slowapi import Limiter, _rate_limit_exceeded_handler +from slowapi.util import get_remote_address +from slowapi.errors import RateLimitExceeded from src.config import Config from src.extractor import extract_text, SUPPORTED_EXTENSIONS from src.chunker import chunk_text from src.embedder import Embedder from src.generator import Generator from src.vectorstore import VectorStore - +from pydantic import BaseModel vector_store: VectorStore = None + embedder: Embedder = None generator: Generator = None +def get_ip(request: Request) -> str: + forwared_request_headers = request.headers.get("X-Forwarded-For") + if forwared_request_headers: + return forwared_request_headers.split(",")[0].strip() + else: + return request.client.host or request.headers.get("X-Real-IP") + + +limiter = Limiter(key_func=get_ip) + + +class user_query(BaseModel): + query: str + + @asynccontextmanager async def lifespan(app: FastAPI): global vector_store, embedder, generator - - if not Config.OPENAI_API_KEY or Config.OPENAI_API_KEY == "sk-your-api-key-here": - print("Warning: OPENAI_API_KEY not set. Set it in .env file.") + + vector_store = VectorStore(Config.VORTEXDB_HOST, Config.VORTEXDB_PORT) + try: + vector_store.health_check() + except Exception as e: + raise RuntimeError( + f"Failed to connect to VortexDB at " + f"{Config.VORTEXDB_HOST}:{Config.VORTEXDB_PORT}: {e}" + ) from e + + print(f"Connected to VortexDB at {Config.VORTEXDB_HOST}:{Config.VORTEXDB_PORT}") + + if not Config.OPENAI_API_KEY: + print("Warning: OPENAI_API_KEY not set.") else: - embedder = Embedder(Config.OPENAI_API_KEY) - generator = Generator(Config.OPENAI_API_KEY) - vector_store = VectorStore(Config.VORTEXDB_HOST, Config.VORTEXDB_PORT) - print(f"Connected to VortexDB at {Config.VORTEXDB_HOST}:{Config.VORTEXDB_PORT}") - + embedder = Embedder() + generator = Generator() + yield app = FastAPI(title="Document RAG API", lifespan=lifespan) - +app.state.limiter = limiter +app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) app.add_middleware( CORSMiddleware, allow_origins=["*"], - allow_credentials=True, + allow_credentials=False, allow_methods=["*"], allow_headers=["*"], ) @app.get("/") -async def root(): +@limiter.limit("5/minute") +async def root(request: Request): + return {"status": "ok", "message": "Document RAG API"} @app.get("/health") -async def health(): +async def health(request: Request): if not embedder or not vector_store: return JSONResponse( status_code=503, - content={"status": "error", "message": "Service not ready. Check API key."} - ) - return {"status": "ok"} - - -@app.post("/upload") -async def upload_document(file: UploadFile = File(...)): - global embedder, vector_store - - if not embedder or not vector_store: - raise HTTPException(status_code=503, detail="Service not ready. Set OPENAI_API_KEY in .env") - - ext = os.path.splitext(file.filename)[1].lower() - if ext not in SUPPORTED_EXTENSIONS: - raise HTTPException( - status_code=400, - detail=f"Unsupported format: {ext}. Supported: {', '.join(SUPPORTED_EXTENSIONS)}" - ) - - if ext in ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp']: - raise HTTPException( - status_code=400, - detail="Image files are not supported. Please upload a text document (PDF, TXT, MD, DOCX, CSV)." + content={ + "status": "error", + "message": "Service not ready. Check AI provider config.", + }, ) - - with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as tmp: - content = await file.read() - tmp.write(content) - tmp_path = tmp.name - try: - text = extract_text(tmp_path) - - if not text or not text.strip(): - raise HTTPException(status_code=400, detail="Document appears to be empty or no text could be extracted.") - - chunks = chunk_text(text, Config.CHUNK_SIZE, Config.CHUNK_OVERLAP) - - if not chunks: - raise HTTPException(status_code=400, detail="Could not chunk document") - - embeddings = embedder.embed(chunks) - - points_inserted = vector_store.insert_batch(embeddings, chunks, file.filename) - - return { - "success": True, - "filename": file.filename, - "chunks": points_inserted, - "message": f"Document indexed successfully" - } - - except HTTPException: - raise + vector_store.health_check() except Exception as e: - error_msg = str(e) - if "quota" in error_msg.lower() or "429" in error_msg: - raise HTTPException(status_code=429, detail="OpenAI API quota exceeded. Please add billing or wait for quota reset.") - if "clipboard" in error_msg.lower() or "image" in error_msg.lower(): - raise HTTPException(status_code=400, detail="This PDF contains images. Please upload a text-based PDF.") - raise HTTPException(status_code=500, detail=f"Error processing document: {error_msg}") - finally: - os.unlink(tmp_path) + return JSONResponse( + status_code=503, + content={"status": "error", "message": f"VortexDB unreachable: {e}"}, + ) + return {"status": "ok"} @app.post("/chat") -async def chat(question: str = None, body: Dict = None): +@limiter.limit("2/minute") +async def chat(request: Request, user_query: user_query): global embedder, generator, vector_store - + if not embedder or not generator or not vector_store: - raise HTTPException(status_code=503, detail="Service not ready. Set OPENAI_API_KEY in .env") - - if body: - question = body.get("question", question) - + raise HTTPException( + status_code=503, + detail="Service not ready. Configure OpenAI or Cloudflare AI credentials.", + ) + + question = user_query.query + if len(question) > 10000: + raise HTTPException(status_code=400, detail="Question is too long") if not question: raise HTTPException(status_code=400, detail="Question is required") - + query_embedding = embedder.embed_single(question) - + results = vector_store.search(query_embedding, Config.TOP_K) - + answer = generator.generate(question, results) - + return { "answer": answer, "sources": [ - {"text": r["text"][:200] + "..." if len(r["text"]) > 200 else r["text"], - "filename": r["filename"], - "score": round(r["score"], 3)} + { + "text": r["text"][:200] + "..." if len(r["text"]) > 200 else r["text"], + } for r in results - ] + ], } -@app.delete("/clear") -async def clear(): - global vector_store - - if not vector_store: - raise HTTPException(status_code=503, detail="Service not ready") - - vector_store.clear() - return {"success": True, "message": "All documents cleared"} - - -@app.get("/stats") -async def stats(): - global vector_store - - if not vector_store: - return {"points_count": 0} - - return vector_store.get_info() +@app.post("/query") +@limiter.limit("2/minute") +async def query_raw(request: Request, user_query: user_query): + global embedder, generator, vector_store + + if not embedder or not generator or not vector_store: + raise HTTPException( + status_code=503, + detail="Service not ready. Configure OpenAI or Cloudflare AI credentials.", + ) + + question = user_query.query + + if not question: + raise HTTPException(status_code=400, detail="Question is required") + + query_embedding = embedder.embed_single(question) + + results = vector_store.search(query_embedding, Config.TOP_K) + + return { + "sources": [ + { + "text": r["text"][:200] + "..." if len(r["text"]) > 200 else r["text"], + } + for r in results + ], + } diff --git a/demo/document-rag/backend/src/vectorstore.py b/demo/document-rag/backend/src/vectorstore.py index 3af8dd6..3c15662 100644 --- a/demo/document-rag/backend/src/vectorstore.py +++ b/demo/document-rag/backend/src/vectorstore.py @@ -7,67 +7,65 @@ class VectorStore: def __init__(self, host: str, port: int): self.base_url = f"http://{host}:{port}" self.vector_size = Config.VECTOR_SIZE - + def _get_client(self) -> httpx.Client: return httpx.Client(base_url=self.base_url, timeout=60.0) - - def insert_batch(self, vectors: List[List[float]], texts: List[str], filename: str) -> int: + + def health_check(self) -> None: + client = self._get_client() + response = client.get("/health") + if response.status_code != 200: + raise Exception(f"VortexDB health check failed: {response.text}") + + def insert_batch( + self, vectors: List[List[float]], texts: List[str], filename: str + ) -> int: """Batch insert vectors using VortexDB's batch insert endpoint.""" client = self._get_client() - + points = [] for i, (vector, text) in enumerate(zip(vectors, texts)): - points.append({ - "vector": vector, - "payload": { - "content_type": "Text", - "content": text - } - }) - + points.append( + {"vector": vector, "payload": {"content_type": "Text", "content": text}} + ) + response = client.post("/points/batch", json={"points": points}) - + if response.status_code != 200: raise Exception(f"Batch insert failed: {response.text}") - + data = response.json() return data.get("inserted", len(points)) - + def search(self, query_vector: List[float], top_k: int = 5) -> List[Dict]: """Search for similar vectors.""" client = self._get_client() - - response = client.post("/points/search", json={ - "vector": query_vector, - "similarity": "Cosine", - "limit": top_k - }) - + + response = client.post( + "/points/search", + json={"vector": query_vector, "similarity": "Cosine", "limit": top_k}, + ) + if response.status_code != 200: - return [] - + raise Exception(f"Search failed") + data = response.json() results = [] - + for point_id in data.get("results", []): point_response = client.get(f"/points/{point_id}") - if point_response.status_code == 200: - point = point_response.json() - payload = point.get("payload", {}) - results.append({ + if point_response.status_code != 200: + raise Exception( + f"Failed to fetch point {point_id}: {point_response.text}" + ) + point = point_response.json() + payload = point.get("payload", {}) + results.append( + { "id": point_id, "text": payload.get("content", ""), - "filename": "", - "score": 1.0 - }) - + } + ) + return results - - def get_point_count(self) -> int: - return 0 - - def clear(self): - pass - - def get_info(self) -> Dict: - return {"points_count": 0, "status": "ok"} + diff --git a/demo/document-rag/docker-compose.yml b/demo/document-rag/docker-compose.yml index a37c0f1..6780b85 100644 --- a/demo/document-rag/docker-compose.yml +++ b/demo/document-rag/docker-compose.yml @@ -8,13 +8,35 @@ services: HTTP_PORT: "3000" STORAGE_TYPE: rocksdb INDEX_TYPE: hnsw - DIMENSION: 1536 + DIMENSION: 1024 SIMILARITY: cosine LOGGING: "true" GRPC_ROOT_PASSWORD: vortexdb-secret DISABLE_HTTP: "false" ports: - "3034:3000" + healthcheck: + test: ["CMD-SHELL", "bash -c 'exec 3<>/dev/tcp/127.0.0.1/3000'"] + interval: 2s + timeout: 2s + retries: 30 + start_period: 5s + + ingest: + build: ./backend + env_file: + - .env + environment: + VORTEXDB_HOST: vortexdb + VORTEXDB_PORT: "3000" + DOCS_DIRECTORY: /app/documents + volumes: + - ./documents:/app/documents:ro + depends_on: + vortexdb: + condition: service_healthy + command: python -m src.ingestion + restart: "no" backend: build: ./backend @@ -22,14 +44,34 @@ services: - .env environment: VORTEXDB_HOST: vortexdb - VORTEXDB_PORT: 3000 - OPENAI_API_KEY: ${OPENAI_API_KEY} + VORTEXDB_PORT: "3000" + DOCS_DIRECTORY: /app/documents + ports: + - "8000:8000" + volumes: + - ./documents:/app/documents:ro depends_on: - - vortexdb + vortexdb: + condition: service_healthy + ingest: + condition: service_completed_successfully + healthcheck: + test: + [ + "CMD", + "python", + "-c", + "import urllib.request; urllib.request.urlopen('http://localhost:8000/')", + ] + interval: 5s + timeout: 3s + retries: 12 + start_period: 10s frontend: build: ./frontend ports: - "3035:80" depends_on: - - backend + backend: + condition: service_healthy diff --git a/demo/document-rag/documents/chunking-config.csv b/demo/document-rag/documents/chunking-config.csv new file mode 100644 index 0000000..7ce72b3 --- /dev/null +++ b/demo/document-rag/documents/chunking-config.csv @@ -0,0 +1,5 @@ +setting,value,description +CHUNK_SIZE,512,Maximum characters per text chunk +CHUNK_OVERLAP,50,Overlap between consecutive chunks +TOP_K,5,Number of chunks retrieved per query +EMBEDDING_BATCH_SIZE,64,Chunks embedded per API request diff --git a/demo/document-rag/documents/cloudflare-models.txt b/demo/document-rag/documents/cloudflare-models.txt new file mode 100644 index 0000000..6587926 --- /dev/null +++ b/demo/document-rag/documents/cloudflare-models.txt @@ -0,0 +1,7 @@ +Cloudflare Models Used + +Embedding model: @cf/qwen/qwen3-embedding-0.6b +Embedding dimension: 1024 + +LLM model: @cf/openai/gpt-oss-20b +Used for answering questions based on retrieved document context. diff --git a/demo/document-rag/documents/ingestion-pipeline.md b/demo/document-rag/documents/ingestion-pipeline.md new file mode 100644 index 0000000..fa5c7ac --- /dev/null +++ b/demo/document-rag/documents/ingestion-pipeline.md @@ -0,0 +1,5 @@ +# Ingestion Pipeline + +The document ingestion pipeline scans a directory for supported files: TXT, MD, PDF, DOCX, and CSV. +Each file is extracted to plain text, split into overlapping chunks, and embedded using a Cloudflare Workers AI embedding model. +Vectors are stored in VortexDB with a text payload for retrieval during chat. diff --git a/demo/document-rag/documents/rag-api.txt b/demo/document-rag/documents/rag-api.txt new file mode 100644 index 0000000..42990be --- /dev/null +++ b/demo/document-rag/documents/rag-api.txt @@ -0,0 +1,7 @@ +RAG API Endpoints + +GET /health - checks VortexDB and AI provider readiness +POST /chat - accepts {"query": "..."} and returns an answer with source snippets +POST /query - returns raw retrieval sources without LLM generation + +The chat endpoint embeds the user question, searches VortexDB for top-k similar chunks, then calls the LLM with that context. diff --git a/demo/document-rag/documents/vortexdb-overview.txt b/demo/document-rag/documents/vortexdb-overview.txt new file mode 100644 index 0000000..945ea95 --- /dev/null +++ b/demo/document-rag/documents/vortexdb-overview.txt @@ -0,0 +1,5 @@ +VortexDB Overview + +VortexDB is a vector database designed for semantic search and retrieval-augmented generation (RAG). +It stores document chunks as dense vectors and supports cosine similarity search. +The HTTP API exposes endpoints for batch insert, search, and point retrieval. diff --git a/demo/document-rag/frontend/.env.development b/demo/document-rag/frontend/.env.development new file mode 100644 index 0000000..e82c617 --- /dev/null +++ b/demo/document-rag/frontend/.env.development @@ -0,0 +1 @@ +VITE_API_URL=/api diff --git a/demo/document-rag/frontend/Dockerfile b/demo/document-rag/frontend/Dockerfile index de385bc..07d6c31 100644 --- a/demo/document-rag/frontend/Dockerfile +++ b/demo/document-rag/frontend/Dockerfile @@ -1,5 +1,17 @@ +FROM node:20-alpine AS build + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . +ENV VITE_API_URL=/api +RUN npm run build + FROM nginx:alpine -COPY dist /usr/share/nginx/html + +COPY --from=build /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf + EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] diff --git a/demo/document-rag/frontend/nginx.conf b/demo/document-rag/frontend/nginx.conf index f2f694c..5aaa3f5 100644 --- a/demo/document-rag/frontend/nginx.conf +++ b/demo/document-rag/frontend/nginx.conf @@ -12,5 +12,6 @@ server { proxy_pass http://backend:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } diff --git a/demo/document-rag/frontend/src/App.css b/demo/document-rag/frontend/src/App.css index 2cafbeb..f34c279 100644 --- a/demo/document-rag/frontend/src/App.css +++ b/demo/document-rag/frontend/src/App.css @@ -56,6 +56,44 @@ body { font-weight: 600; color: var(--text-primary); letter-spacing: -0.02em; + margin-bottom: 8px; +} + +.backend-status { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: var(--text-secondary); +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--text-tertiary); +} + +.status-checking .status-dot { + background: var(--text-tertiary); + animation: pulse 1.5s infinite; +} + +.status-ready .status-dot { + background: var(--accent); +} + +.status-degraded .status-dot { + background: #f59e0b; +} + +.status-offline .status-dot { + background: var(--error); +} + +@keyframes pulse { + 0%, 100% { opacity: 0.4; } + 50% { opacity: 1; } } .new-chat-btn { @@ -80,101 +118,95 @@ body { border-color: var(--text-tertiary); } -.upload-zone { - margin: 0 16px 16px; - padding: 24px; - border: 2px dashed var(--border-color); - border-radius: 12px; - cursor: pointer; - transition: all 0.2s; -} - -.upload-zone:hover, -.upload-zone.dragging { - border-color: var(--accent); - background: rgba(25, 195, 125, 0.05); -} - -.upload-zone input { - display: none; -} - -.upload-content { +.chat-history { + flex: 1; + overflow-y: auto; + padding: 8px 12px; display: flex; flex-direction: column; - align-items: center; - gap: 8px; - color: var(--text-secondary); } -.upload-content svg { - color: var(--text-tertiary); -} - -.upload-formats { +.chat-history-header { font-size: 11px; - color: var(--text-tertiary); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.documents-list { - flex: 1; - padding: 0 16px; - overflow-y: auto; -} - -.documents-list h3 { - font-size: 12px; font-weight: 600; - color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.5px; - margin-bottom: 12px; + color: var(--text-tertiary); + padding: 8px 8px 6px; } -.no-docs { - font-size: 13px; - color: var(--text-tertiary); - font-style: italic; +.chat-list { + display: flex; + flex-direction: column; + gap: 4px; } -.document-item { +.chat-list-item { display: flex; align-items: center; gap: 10px; - padding: 10px 12px; - margin-bottom: 4px; - border-radius: 6px; + padding: 9px 12px; + border-radius: 8px; font-size: 13px; color: var(--text-secondary); - cursor: default; + cursor: pointer; + transition: all 0.2s ease; + user-select: none; } -.document-item:hover { +.chat-list-item:hover { background: var(--bg-hover); + color: var(--text-primary); +} + +.chat-list-item.active { + background: var(--bg-tertiary); + color: var(--text-primary); + border: 1px solid var(--border-subtle); } -.document-item svg { +.chat-list-item svg { flex-shrink: 0; color: var(--text-tertiary); } -.clear-btn { - margin: 16px; - padding: 10px; +.chat-list-item:hover svg, +.chat-list-item.active svg { + color: var(--accent); +} + +.chat-title { + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.delete-chat-btn { + opacity: 0; background: transparent; - border: 1px solid var(--error); - border-radius: 6px; - color: var(--error); - font-size: 13px; + border: none; + color: var(--text-tertiary); cursor: pointer; - transition: all 0.2s; + padding: 4px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; } -.clear-btn:hover { - background: var(--error); - color: white; +.chat-list-item:hover .delete-chat-btn { + opacity: 1; +} + +.delete-chat-btn:hover { + color: var(--error); + background: rgba(239, 68, 68, 0.15); +} + +.delete-chat-btn:hover svg { + color: var(--error) !important; } /* Chat Area */ @@ -316,7 +348,7 @@ body { padding-bottom: 0; } -.source-score { +.source-index { color: var(--accent); font-weight: 500; flex-shrink: 0; @@ -385,11 +417,6 @@ body { box-shadow: 0 0 0 2px rgba(25, 195, 125, 0.1); } -.input-container.dragging { - border-color: var(--accent); - background: rgba(25, 195, 125, 0.05); -} - .input-container input { flex: 1; background: transparent; @@ -452,141 +479,3 @@ body { display: none; } } - -/* Modal */ -.modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.8); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; - backdrop-filter: blur(4px); -} - -.modal { - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 16px; - padding: 32px; - min-width: 360px; - max-width: 420px; - text-align: center; -} - -.modal-content { - display: flex; - flex-direction: column; - align-items: center; - gap: 16px; -} - -.modal-spinner { - display: flex; - align-items: center; - justify-content: center; -} - -.spinner { - width: 48px; - height: 48px; - border: 3px solid var(--border-color); - border-top-color: var(--accent); - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -.modal-icon { - display: flex; - align-items: center; - justify-content: center; - width: 64px; - height: 64px; - border-radius: 50%; -} - -.modal-icon.success { - background: rgba(25, 195, 125, 0.15); - color: var(--accent); -} - -.modal-icon.error { - background: rgba(239, 68, 68, 0.15); - color: var(--error); -} - -.modal h3 { - font-size: 20px; - font-weight: 600; - color: var(--text-primary); -} - -.modal-filename { - font-size: 13px; - color: var(--text-secondary); - word-break: break-all; - max-width: 100%; -} - -.modal-detail { - font-size: 14px; - color: var(--text-tertiary); -} - -.modal-progress { - width: 100%; - display: flex; - align-items: center; - gap: 12px; -} - -.progress-bar { - flex: 1; - height: 6px; - background: var(--bg-hover); - border-radius: 3px; - overflow: hidden; -} - -.progress-fill { - height: 100%; - background: var(--accent); - border-radius: 3px; - transition: width 0.3s ease; -} - -.progress-text { - font-size: 13px; - font-weight: 600; - color: var(--text-secondary); - min-width: 40px; - text-align: right; -} - -.modal-close-btn { - margin-top: 8px; - padding: 10px 24px; - background: var(--bg-hover); - border: 1px solid var(--border-color); - border-radius: 8px; - color: var(--text-primary); - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; -} - -.modal-close-btn:hover { - background: var(--error); - border-color: var(--error); -} diff --git a/demo/document-rag/frontend/src/App.jsx b/demo/document-rag/frontend/src/App.jsx index 0ab1726..f8e8554 100644 --- a/demo/document-rag/frontend/src/App.jsx +++ b/demo/document-rag/frontend/src/App.jsx @@ -1,140 +1,122 @@ import { useState, useRef, useEffect } from 'react'; import './App.css'; +import { chat, getHealth } from './api'; -const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8002'; +const STORAGE_KEY_CHATS = 'vortexdb_rag_chats'; +const STORAGE_KEY_ACTIVE = 'vortexdb_rag_active_chat_id'; -const SUPPORTED_FORMATS = ['.pdf', '.txt', '.md', '.docx', '.csv']; -const IMAGE_FORMATS = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp']; +const createSession = () => ({ + id: `chat_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`, + title: 'New Chat', + createdAt: Date.now(), + messages: [], +}); function App() { - const [messages, setMessages] = useState([]); + const [chats, setChats] = useState(() => { + try { + const saved = localStorage.getItem(STORAGE_KEY_CHATS); + if (saved) { + const parsed = JSON.parse(saved); + if (Array.isArray(parsed) && parsed.length > 0) { + return parsed; + } + } + } catch (e) { + console.error('Failed to load chats from localStorage', e); + } + return [createSession()]; + }); + + const [activeChatId, setActiveChatId] = useState(() => { + try { + const savedId = localStorage.getItem(STORAGE_KEY_ACTIVE); + if (savedId) return savedId; + } catch (e) { + console.error(e); + } + return chats[0]?.id || ''; + }); + const [input, setInput] = useState(''); const [isLoading, setIsLoading] = useState(false); - const [uploadedFiles, setUploadedFiles] = useState(new Set()); - const [isDragging, setIsDragging] = useState(false); - const [uploadModal, setUploadModal] = useState(null); + const [backendStatus, setBackendStatus] = useState('checking'); const messagesEndRef = useRef(null); - const fileInputRef = useRef(null); - - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }; + // Sync activeChatId if invalid useEffect(() => { - scrollToBottom(); - }, [messages]); - - const updateModal = (status, progress = null, detail = null) => { - setUploadModal(prev => ({ - ...prev, - status, - progress, - detail, - timestamp: Date.now() - })); - }; - - const handleFileSelect = async (file) => { - const ext = '.' + file.name.split('.').pop().toLowerCase(); - - if (IMAGE_FORMATS.includes(ext)) { - setUploadModal({ - status: 'error', - fileName: file.name, - detail: `Image files are not supported. This model does not support image input. Please upload a text document (${SUPPORTED_FORMATS.join(', ')}).` - }); - return; + if (!chats.some((c) => c.id === activeChatId) && chats.length > 0) { + setActiveChatId(chats[0].id); } + }, [chats, activeChatId]); - if (!SUPPORTED_FORMATS.includes(ext)) { - setUploadModal({ - status: 'error', - fileName: file.name, - detail: `Unsupported format: ${ext}. Please upload ${SUPPORTED_FORMATS.join(', ')} files.` - }); - return; + // Persist chats and activeChatId + useEffect(() => { + try { + localStorage.setItem(STORAGE_KEY_CHATS, JSON.stringify(chats)); + } catch (e) { + console.error('Failed to save chats to localStorage', e); } + }, [chats]); - setUploadModal({ - status: 'uploading', - fileName: file.name, - progress: 0, - detail: 'Reading file...' - }); - - const formData = new FormData(); - formData.append('file', file); - + useEffect(() => { try { - updateModal('uploading', 10, 'Uploading to server...'); - - const res = await fetch(`${API_URL}/upload`, { - method: 'POST', - body: formData, - }); - - updateModal('processing', 50, 'Processing document...'); - - const data = await res.json(); - - if (!res.ok) { - throw new Error(data.detail || 'Upload failed'); - } - - updateModal('vectors', 75, 'Creating embeddings...'); - - await new Promise(resolve => setTimeout(resolve, 500)); - - updateModal('indexing', 90, 'Indexing vectors...'); - - await new Promise(resolve => setTimeout(resolve, 300)); - - updateModal('complete', 100, `Indexed ${data.chunks} chunks successfully!`); - - setUploadedFiles(prev => new Set([...prev, file.name])); - - setTimeout(() => { - setUploadModal(null); - addMessage('system', `📄 Uploaded: ${file.name} (${data.chunks} chunks indexed)`); - }, 1500); - + localStorage.setItem(STORAGE_KEY_ACTIVE, activeChatId); } catch (e) { - setUploadModal({ - status: 'error', - fileName: file.name, - detail: e.message - }); + console.error('Failed to save active chat ID', e); } - }; + }, [activeChatId]); - const closeModal = () => { - if (uploadModal?.status !== 'uploading' && uploadModal?.status !== 'processing') { - setUploadModal(null); - } - }; + const activeChat = chats.find((c) => c.id === activeChatId) || chats[0] || createSession(); + const messages = activeChat.messages || []; - const handleDrop = (e) => { - e.preventDefault(); - setIsDragging(false); - const file = e.dataTransfer.files[0]; - if (file) handleFileSelect(file); + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; - const handleDragOver = (e) => { - e.preventDefault(); - setIsDragging(true); - }; + useEffect(() => { + scrollToBottom(); + }, [messages, isLoading]); - const handleDragLeave = () => { - setIsDragging(false); + useEffect(() => { + getHealth() + .then((data) => setBackendStatus(data.status === 'ok' ? 'ready' : 'degraded')) + .catch(() => setBackendStatus('offline')); + }, []); + + const updateActiveChatMessages = (updater, newTitle = null) => { + setChats((prevChats) => + prevChats.map((c) => { + if (c.id === activeChatId) { + const updatedMessages = typeof updater === 'function' ? updater(c.messages) : updater; + return { + ...c, + title: newTitle || c.title, + messages: updatedMessages, + }; + } + return c; + }) + ); }; const addMessage = (role, content, sources = []) => { - setMessages(prev => [...prev, { role, content, sources, id: Date.now() }]); + const newMsg = { + role, + content, + sources, + id: `${role}_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`, + }; + updateActiveChatMessages((prev) => [...prev, newMsg]); }; const addError = (content) => { - setMessages(prev => [...prev, { role: 'error', content, id: Date.now() }]); + const errorMsg = { + role: 'error', + content, + id: `err_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`, + }; + updateActiveChatMessages((prev) => [...prev, errorMsg]); }; const handleSubmit = async (e) => { @@ -143,184 +125,122 @@ function App() { const question = input.trim(); setInput(''); - addMessage('user', question); - setIsLoading(true); - try { - const res = await fetch(`${API_URL}/chat`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ question }), - }); + // Generate title from first message if title is 'New Chat' + let updatedTitle = null; + if (activeChat.messages.length === 0 && activeChat.title === 'New Chat') { + updatedTitle = question.length > 32 ? question.slice(0, 32).trim() + '...' : question; + } - const data = await res.json(); + const userMsg = { + role: 'user', + content: question, + sources: [], + id: `user_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`, + }; - if (!res.ok) { - throw new Error(data.detail || 'Request failed'); - } + updateActiveChatMessages((prev) => [...prev, userMsg], updatedTitle); + setIsLoading(true); - addMessage('assistant', data.answer, data.sources || []); - } catch (e) { - addError(e.message); + try { + const data = await chat(question); + const assistantMsg = { + role: 'assistant', + content: data.answer, + sources: data.sources || [], + id: `asst_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`, + }; + updateActiveChatMessages((prev) => [...prev, assistantMsg]); + } catch (err) { + addError(err.message); } finally { setIsLoading(false); } }; - const clearChat = () => { - setMessages([]); - }; - - const clearAll = async () => { - if (!confirm('Clear all documents and chat?')) return; - - try { - await fetch(`${API_URL}/clear`, { method: 'DELETE' }); - setUploadedFiles(new Set()); - setMessages([]); - } catch (e) { - addError('Failed to clear documents'); + const startNewChat = () => { + // If current chat is already empty, just stay on it + if (activeChat && activeChat.messages.length === 0) { + return; } - }; - const getStatusIcon = () => { - if (!uploadModal) return null; - - switch (uploadModal.status) { - case 'uploading': - case 'processing': - case 'vectors': - case 'indexing': - return ( -
-
-
- ); - case 'complete': - return ( -
- - - - -
- ); - case 'error': - return ( -
- - - - - -
- ); - default: - return null; - } + const newChat = createSession(); + setChats((prev) => [newChat, ...prev]); + setActiveChatId(newChat.id); }; - const getStatusText = () => { - if (!uploadModal) return ''; - - switch (uploadModal.status) { - case 'uploading': return 'Uploading'; - case 'processing': return 'Processing'; - case 'vectors': return 'Creating Embeddings'; - case 'indexing': return 'Indexing'; - case 'complete': return 'Complete'; - case 'error': return 'Error'; - default: return ''; - } + const deleteChat = (e, chatIdToDelete) => { + e.stopPropagation(); + setChats((prev) => { + const filtered = prev.filter((c) => c.id !== chatIdToDelete); + if (filtered.length === 0) { + const fresh = createSession(); + setActiveChatId(fresh.id); + return [fresh]; + } + if (activeChatId === chatIdToDelete) { + setActiveChatId(filtered[0].id); + } + return filtered; + }); }; + const statusLabel = { + checking: 'Checking...', + ready: 'Ready', + degraded: 'Degraded', + offline: 'Offline', + }[backendStatus]; + return (
- {uploadModal && ( -
-
e.stopPropagation()}> -
- {getStatusIcon()} -

{getStatusText()}

-

{uploadModal.fileName}

- - {(uploadModal.status === 'uploading' || uploadModal.status === 'processing' || - uploadModal.status === 'vectors' || uploadModal.status === 'indexing') && ( -
-
-
-
- {uploadModal.progress}% -
- )} - -

{uploadModal.detail}

- - {uploadModal.status === 'error' && ( - - )} -
-
-
- )} -
@@ -328,24 +248,24 @@ function App() { {messages.length === 0 && (

VortexDB RAG Demo

-

Upload documents and ask questions about them

+

Ask questions about the pre-ingested documents

)} - + {messages.map((msg, i) => (
{msg.role === 'user' ? ( - + ) : msg.role === 'error' ? ( - + ) : ( - + )}
@@ -356,7 +276,7 @@ function App() {

Sources:

{msg.sources.map((s, j) => (
- [{s.score}] + [{j + 1}] {s.text}
))} @@ -365,19 +285,19 @@ function App() {
))} - + {isLoading && (
- +
- - - + + +
@@ -386,18 +306,18 @@ function App() {
-
+
setInput(e.target.value)} placeholder="Ask a question about your documents..." - disabled={isLoading} + disabled={isLoading || backendStatus === 'offline'} /> -
diff --git a/demo/document-rag/frontend/src/api.js b/demo/document-rag/frontend/src/api.js new file mode 100644 index 0000000..d3fa8f7 --- /dev/null +++ b/demo/document-rag/frontend/src/api.js @@ -0,0 +1,36 @@ +const API_URL = import.meta.env.VITE_API_URL || '/api'; + +export function parseApiError(data, status) { + if (status === 429) { + return data.error || 'Rate limit exceeded. Try again in a minute.'; + } + if (typeof data.detail === 'string') { + return data.detail; + } + if (Array.isArray(data.detail)) { + return data.detail.map((e) => e.msg).join(', '); + } + return 'Request failed'; +} + +export async function chat(query) { + const res = await fetch(`${API_URL}/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query }), + }); + const data = await res.json(); + if (!res.ok) { + throw new Error(parseApiError(data, res.status)); + } + return data; +} + +export async function getHealth() { + const res = await fetch(`${API_URL}/health`); + const data = await res.json(); + if (!res.ok) { + throw new Error(data.message || 'Health check failed'); + } + return data; +} diff --git a/demo/document-rag/frontend/vite.config.js b/demo/document-rag/frontend/vite.config.js index 9d1dd06..fb80d0e 100644 --- a/demo/document-rag/frontend/vite.config.js +++ b/demo/document-rag/frontend/vite.config.js @@ -7,6 +7,13 @@ export default defineConfig({ server: { port: 5173, host: true, + proxy: { + '/api': { + target: 'http://localhost:8000', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), + }, + }, }, build: { outDir: 'dist', From 7950de3373e47e8c0e2cfbab4c285aebe5702ba5 Mon Sep 17 00:00:00 2001 From: ishaan Date: Fri, 21 Aug 2026 00:50:17 +0530 Subject: [PATCH 3/6] feat(demo): update document-rag demo and seperated it from the previous branch --- .../documents/api-integration-guide.md | 26 +++++++++++++++++++ .../documents/chunking-config.csv | 5 ---- .../documents/cloudflare-models.txt | 7 ----- .../documents/company-overview.md | 22 ++++++++++++++++ .../documents/employee-onboarding.md | 20 ++++++++++++++ .../engineering-decision-record-042.md | 18 +++++++++++++ .../documents/incident-2026-05-northstar.md | 19 ++++++++++++++ .../documents/ingestion-pipeline.md | 5 ---- .../monthly-operations-report-june-2026.md | 19 ++++++++++++++ .../documents/platform-architecture.md | 21 +++++++++++++++ .../documents/pricing-and-plans.md | 20 ++++++++++++++ .../documents/product-guide-aster-edge.md | 22 ++++++++++++++++ .../documents/product-roadmap-2026-h2.md | 23 ++++++++++++++++ .../documents/q2-customer-success-notes.md | 17 ++++++++++++ demo/document-rag/documents/rag-api.txt | 7 ----- .../sales-proposal-northstar-draft.md | 10 +++++++ .../documents/security-and-access-policy.md | 20 ++++++++++++++ .../documents/support-ticket-1842.md | 11 ++++++++ .../documents/support-ticket-1907.md | 11 ++++++++ .../documents/vortexdb-overview.txt | 5 ---- 20 files changed, 279 insertions(+), 29 deletions(-) create mode 100644 demo/document-rag/documents/api-integration-guide.md delete mode 100644 demo/document-rag/documents/chunking-config.csv delete mode 100644 demo/document-rag/documents/cloudflare-models.txt create mode 100644 demo/document-rag/documents/company-overview.md create mode 100644 demo/document-rag/documents/employee-onboarding.md create mode 100644 demo/document-rag/documents/engineering-decision-record-042.md create mode 100644 demo/document-rag/documents/incident-2026-05-northstar.md delete mode 100644 demo/document-rag/documents/ingestion-pipeline.md create mode 100644 demo/document-rag/documents/monthly-operations-report-june-2026.md create mode 100644 demo/document-rag/documents/platform-architecture.md create mode 100644 demo/document-rag/documents/pricing-and-plans.md create mode 100644 demo/document-rag/documents/product-guide-aster-edge.md create mode 100644 demo/document-rag/documents/product-roadmap-2026-h2.md create mode 100644 demo/document-rag/documents/q2-customer-success-notes.md delete mode 100644 demo/document-rag/documents/rag-api.txt create mode 100644 demo/document-rag/documents/sales-proposal-northstar-draft.md create mode 100644 demo/document-rag/documents/security-and-access-policy.md create mode 100644 demo/document-rag/documents/support-ticket-1842.md create mode 100644 demo/document-rag/documents/support-ticket-1907.md delete mode 100644 demo/document-rag/documents/vortexdb-overview.txt diff --git a/demo/document-rag/documents/api-integration-guide.md b/demo/document-rag/documents/api-integration-guide.md new file mode 100644 index 0000000..ec0b0a7 --- /dev/null +++ b/demo/document-rag/documents/api-integration-guide.md @@ -0,0 +1,26 @@ +# Vortex Cloud API Integration Guide + +**API version:** v2 +**Updated:** 2026-05-18 + +The Vortex Cloud API lets customers read site telemetry, acknowledge alarms, and export reports. API access is available on Growth and Enterprise plans. + +## Authentication + +Use an organization-scoped service account with a short-lived OAuth token. Tokens expire after one hour. Service accounts receive only the scopes explicitly assigned to them; use `telemetry:read`, `alarms:write`, and `reports:read` rather than a broad administrative scope. + +## Key endpoints + +`GET /v2/sites/{site_id}/telemetry?from=&to=` returns normalized readings. The maximum query window is 31 days. + +`GET /v2/alarms` accepts filters for status, severity, site, and tag. Alarm timestamps are in UTC. + +`POST /v2/alarms/{alarm_id}/acknowledgements` records the named operator, timestamp, and optional note. It does not resolve the alarm; resolution requires the `alarms:resolve` scope. + +`POST /v2/exports` creates an asynchronous CSV or Parquet export. Exports are retained for 24 hours and are listed in the audit log. + +## Rate limits and backfill + +The default limit is 120 requests per minute per service account. During an Aster connectivity recovery, telemetry can arrive up to 72 hours late; clients should order records by `observed_at`, not API receipt time. + +Never embed service-account secrets in Aster configuration files or source repositories. diff --git a/demo/document-rag/documents/chunking-config.csv b/demo/document-rag/documents/chunking-config.csv deleted file mode 100644 index 7ce72b3..0000000 --- a/demo/document-rag/documents/chunking-config.csv +++ /dev/null @@ -1,5 +0,0 @@ -setting,value,description -CHUNK_SIZE,512,Maximum characters per text chunk -CHUNK_OVERLAP,50,Overlap between consecutive chunks -TOP_K,5,Number of chunks retrieved per query -EMBEDDING_BATCH_SIZE,64,Chunks embedded per API request diff --git a/demo/document-rag/documents/cloudflare-models.txt b/demo/document-rag/documents/cloudflare-models.txt deleted file mode 100644 index 6587926..0000000 --- a/demo/document-rag/documents/cloudflare-models.txt +++ /dev/null @@ -1,7 +0,0 @@ -Cloudflare Models Used - -Embedding model: @cf/qwen/qwen3-embedding-0.6b -Embedding dimension: 1024 - -LLM model: @cf/openai/gpt-oss-20b -Used for answering questions based on retrieved document context. diff --git a/demo/document-rag/documents/company-overview.md b/demo/document-rag/documents/company-overview.md new file mode 100644 index 0000000..2add43b --- /dev/null +++ b/demo/document-rag/documents/company-overview.md @@ -0,0 +1,22 @@ +# Vortex Lab: Company Overview + +**Updated:** 2026-06-12 +**Owner:** Maya Chen, Chief Executive Officer + +Vortex Lab builds software for teams operating distributed energy equipment: solar farms, battery sites, and microgrids. Its platform combines rugged edge gateways with a cloud control plane that turns noisy device readings into actionable operating signals. + +## Products + +- **Aster Edge** collects telemetry from site controllers, normalizes common industrial protocols, and can keep operating while disconnected. +- **Vortex Cloud** stores, visualizes, and routes telemetry, alarms, maintenance notes, and reports. +- **Pulse** is the anomaly-detection service used to prioritize alerts. + +## Customers and operating model + +Our customers are asset owners and service providers with 20 to 2,000 sites. They typically use Vortex during daily operations, incident response, and monthly performance reviews. Customer Success owns adoption; Reliability Engineering owns the shared cloud platform; Support owns first response for production incidents. + +Vortex Lab’s stated operating principle is: **an alert should lead to a clear next action, not merely more data**. + +## Current priorities + +For the second half of 2026, the company is focused on reducing false-positive alerts, expanding offline workflows in Aster Edge, and improving export controls for enterprise customers. The public product roadmap is in `product-roadmap-2026-h2.md`. diff --git a/demo/document-rag/documents/employee-onboarding.md b/demo/document-rag/documents/employee-onboarding.md new file mode 100644 index 0000000..9664e96 --- /dev/null +++ b/demo/document-rag/documents/employee-onboarding.md @@ -0,0 +1,20 @@ +# Employee Onboarding: Operations and Engineering + +**Updated:** 2026-06-08 + +Welcome to Vortex Lab. During your first week, complete security training, request the least-privilege role for your work, and join your team’s service rotation shadow session. + +## Required setup + +1. Enroll a hardware security key in SSO. +2. Activate the password manager and store no customer credentials elsewhere. +3. Read the Security and Access Policy and acknowledge the acceptable-use statement. +4. Request staging access through Access Hub; do not request production access until your manager identifies a business need. + +## Working with customer information + +Use a customer’s organization ID, not its name, in internal engineering logs whenever practical. Do not download raw telemetry to personal machines. Support attachments must be placed in the case workspace, which applies access controls and retention rules. + +## Escalation + +For a production issue, page the on-call reliability engineer. For a suspected security event, follow the one-hour reporting requirement in `security-and-access-policy.md`. diff --git a/demo/document-rag/documents/engineering-decision-record-042.md b/demo/document-rag/documents/engineering-decision-record-042.md new file mode 100644 index 0000000..7c135e9 --- /dev/null +++ b/demo/document-rag/documents/engineering-decision-record-042.md @@ -0,0 +1,18 @@ +# ADR-042: Keep a 10-Minute Alert Watermark + +**Date:** 2026-05-28 +**Status:** Accepted + +## Context + +Pulse must handle delayed telemetry from Aster Edge without causing duplicate or misleading pages. A May incident demonstrated that a globally expanded 60-minute watermark made operational alerts unacceptably late for battery customers. + +## Decision + +The default alert watermark remains 10 minutes. Any exception longer than 15 minutes must be organization-scoped, approved by Reliability Engineering, have an expiry date, and be covered by a replay test using delayed events. + +Events received after the watermark are marked as backfill. They update historical charts and reports but do not reopen a resolved alarm automatically. Operators can manually review a backfill event from the alarm timeline. + +## Consequences + +Some intermittently connected sites may continue to create duplicate candidate alerts, which Pulse deduplicates using rule and event identity. This is preferable to silently delaying high-severity alerts across unrelated customers. diff --git a/demo/document-rag/documents/incident-2026-05-northstar.md b/demo/document-rag/documents/incident-2026-05-northstar.md new file mode 100644 index 0000000..4ab1f08 --- /dev/null +++ b/demo/document-rag/documents/incident-2026-05-northstar.md @@ -0,0 +1,19 @@ +# Incident Report: Northstar Alert Delay + +**Date:** 2026-05-14 +**Severity:** SEV-2 +**Status:** Closed + +## Summary + +Northstar Energy received delayed high-temperature alerts for three battery sites between 09:12 and 10:03 UTC. No equipment damage occurred. Operators identified the condition through their local SCADA system and placed the affected units in a safe operating mode. + +## Cause + +On May 12, an alert-processing configuration change increased Pulse’s late-event watermark from 10 to 60 minutes for all organizations. The change was intended to reduce duplicate pages caused by one intermittent site. Aster Edge gateways at Northstar backfilled valid events after brief cellular outages; Pulse withheld their alert evaluation until the expanded watermark elapsed. + +## Resolution and follow-up + +The watermark was restored to 10 minutes at 10:03 UTC. We added organization-scoped configuration validation, a test fixture for delayed Aster events, and a dashboard showing alert-evaluation lag. The previous customer-specific workaround was removed. + +Northstar received a written incident summary, a 30-day alerting credit, and weekly progress updates until the follow-up items were complete. See `q2-customer-success-notes.md` for the commercial commitments. diff --git a/demo/document-rag/documents/ingestion-pipeline.md b/demo/document-rag/documents/ingestion-pipeline.md deleted file mode 100644 index fa5c7ac..0000000 --- a/demo/document-rag/documents/ingestion-pipeline.md +++ /dev/null @@ -1,5 +0,0 @@ -# Ingestion Pipeline - -The document ingestion pipeline scans a directory for supported files: TXT, MD, PDF, DOCX, and CSV. -Each file is extracted to plain text, split into overlapping chunks, and embedded using a Cloudflare Workers AI embedding model. -Vectors are stored in VortexDB with a text payload for retrieval during chat. diff --git a/demo/document-rag/documents/monthly-operations-report-june-2026.md b/demo/document-rag/documents/monthly-operations-report-june-2026.md new file mode 100644 index 0000000..0297d78 --- /dev/null +++ b/demo/document-rag/documents/monthly-operations-report-june-2026.md @@ -0,0 +1,19 @@ +# Monthly Operations Report — June 2026 + +**Prepared:** 2026-07-05 + +## Reliability snapshot + +Vortex Cloud availability was 99.96% in June. Median telemetry ingestion latency was 4.2 seconds for connected sites. 94.1% of delayed Aster uploads completed within 12 minutes after connectivity was restored. + +## Alert quality + +High-severity alert median evaluation time was 7.8 minutes, down from 18.4 minutes in May. The improvement followed the restoration of the 10-minute Pulse watermark and the addition of evaluation-lag monitoring. The Northstar pilot reported two actionable backfill labels and no delayed high-temperature pages during the month. + +## Open work + +Engineering is completing API support for `is_backfill`; this remains targeted for July. Customer Success is validating the weekly alert-quality report before offering it to other battery customers. + +## Retention reminder + +Raw telemetry retention follows each customer’s plan or contract. Growth includes 90 days under the April 2026 plan update; data exports are available for 24 hours after creation. diff --git a/demo/document-rag/documents/platform-architecture.md b/demo/document-rag/documents/platform-architecture.md new file mode 100644 index 0000000..b2e2d5a --- /dev/null +++ b/demo/document-rag/documents/platform-architecture.md @@ -0,0 +1,21 @@ +# Platform Architecture + +**Updated:** 2026-06-10 +**Audience:** Engineering and Security + +Vortex Cloud separates ingestion, operational storage, alert evaluation, and customer-facing applications. + +1. **Ingress** validates device identity, schema, and message signatures. +2. **Stream processing** enriches events with site metadata and writes immutable raw telemetry. +3. **Pulse** evaluates anomaly models and deterministic alert rules. +4. **Operations API** serves the web application, reports, and customer integrations. + +Raw telemetry is encrypted at rest and logically partitioned by organization ID. The web application uses the Operations API; it does not directly query the telemetry store. Customer data is replicated within the selected hosting region for durability. + +## Resilience + +Ingress accepts delayed events from Aster Edge and deduplicates them using gateway ID, sequence number, and observed timestamp. Pulse uses a 10-minute watermark for normal alert evaluation; events older than the watermark are marked as backfill and may update reports, but do not automatically reopen a resolved incident. + +## Known trade-off + +The watermark avoids duplicate pages during connectivity recovery, but it can delay alert classification for intermittently connected sites. The Northstar incident in May 2026 exposed a configuration error in this boundary; details are in `incident-2026-05-northstar.md`. diff --git a/demo/document-rag/documents/pricing-and-plans.md b/demo/document-rag/documents/pricing-and-plans.md new file mode 100644 index 0000000..16dd682 --- /dev/null +++ b/demo/document-rag/documents/pricing-and-plans.md @@ -0,0 +1,20 @@ +# Pricing and Plans + +**Effective:** 2026-04-01 +**Owner:** Revenue Operations + +| Plan | Monthly platform fee | Included sites | Raw telemetry retention | API access | +| --- | ---: | ---: | --- | --- | +| Starter | $1,200 | 10 | 30 days | No | +| Growth | $3,500 | 50 | 90 days | Yes | +| Enterprise | Custom | 51+ | 365 days | Yes | + +Additional sites are billed annually. Aster Edge hardware is quoted separately. Enterprise includes regional hosting selection, SAML SSO, quarterly security reviews, and a named Customer Success Manager. + +## Alerting and support + +All plans include email and in-app alarms. SMS and webhook delivery are available on Growth and Enterprise. Starter support responds during business hours; Growth receives 8x5 support with a four-business-hour target; Enterprise receives 24x7 severity-one response. + +## April 2026 change + +Before 2026-04-01, Growth plans included 60 days of raw telemetry. New and renewing Growth agreements now include 90 days. Contract terms take precedence if a signed agreement specifies a different retention period. diff --git a/demo/document-rag/documents/product-guide-aster-edge.md b/demo/document-rag/documents/product-guide-aster-edge.md new file mode 100644 index 0000000..873810a --- /dev/null +++ b/demo/document-rag/documents/product-guide-aster-edge.md @@ -0,0 +1,22 @@ +# Aster Edge Product Guide + +**Version:** 3.4 +**Updated:** 2026-06-01 + +Aster Edge is a site-installed gateway that reads device telemetry, applies local rules, and securely synchronizes with Vortex Cloud. + +## Core behavior + +Aster polls supported devices every 15 seconds by default. It signs and batches observations before upload. If a site loses internet access, Aster writes events and commands to an encrypted local queue. On reconnect, it uploads queued telemetry in chronological order and reports a `backfill_complete` event to Vortex Cloud. + +The local queue is capped at 72 hours of standard telemetry. If the queue is full, Aster preserves alarm and command-audit events and begins sampling ordinary telemetry at five-minute intervals. This does not affect local safety interlocks, which remain on the controller. + +## Local rules + +Operators may deploy approved threshold rules from Vortex Cloud. A local rule can create an alarm, attach a recommended runbook, or hold a non-safety command for operator review. Aster never autonomously changes inverter set points unless the site has the optional Closed Loop Automation entitlement and an approved site policy. + +## Installation notes + +Installers register each gateway against a single customer organization and site. The registration token expires after 30 minutes. Aster requires outbound HTTPS access to `ingest.vortexlab.example` and time synchronization via NTP. + +See `api-integration-guide.md` for data payloads and `security-and-access-policy.md` for credential handling. diff --git a/demo/document-rag/documents/product-roadmap-2026-h2.md b/demo/document-rag/documents/product-roadmap-2026-h2.md new file mode 100644 index 0000000..5c46194 --- /dev/null +++ b/demo/document-rag/documents/product-roadmap-2026-h2.md @@ -0,0 +1,23 @@ +# Product Roadmap: H2 2026 + +**Published:** 2026-06-30 +**Status:** Directional; dates may change + +## July–August + +- Add `is_backfill` and `ingested_at` to the v2 telemetry API. +- Release alert-evaluation lag indicators in the operator console. +- Pilot weekly alert-quality reports with Northstar Energy. + +## September–October + +- Expand Aster Edge offline queue observability, including local storage pressure warnings. +- Introduce organization-scoped Pulse configuration guardrails. +- Launch self-service webhook signing-key rotation for Growth and Enterprise customers. + +## November–December + +- Limited beta for Closed Loop Automation policy templates. +- Enterprise export controls: approval workflows and region-aware export storage. + +The roadmap intentionally does not promise delivery dates to customers. Customer commitments recorded in signed agreements or success plans take priority over this document. diff --git a/demo/document-rag/documents/q2-customer-success-notes.md b/demo/document-rag/documents/q2-customer-success-notes.md new file mode 100644 index 0000000..7d70915 --- /dev/null +++ b/demo/document-rag/documents/q2-customer-success-notes.md @@ -0,0 +1,17 @@ +# Q2 Customer Success Notes: Northstar Energy + +**Meeting date:** 2026-05-20 +**Participants:** Elena Ruiz (Northstar), Priya Nair (Vortex), Omar Bell (Vortex) + +## Customer feedback + +Northstar values Aster’s offline telemetry recovery but wants a clear indication that an alarm is based on delayed data. Its operations team also asked for a site-level weekly alert-quality report and an API field that identifies backfilled events. + +## Vortex commitments + +- Provide a written root-cause analysis by May 22 and weekly follow-ups through June. +- Deliver an `is_backfill` field in the v2 telemetry API by July 15. +- Pilot the weekly alert-quality report for Northstar’s three battery sites in June. +- Apply a 30-day alerting credit to the June invoice. + +Northstar will evaluate a Growth-to-Enterprise upgrade in August if the pilot demonstrates fewer false-positive escalations. Pricing questions should reference the current plan sheet, not the pre-April proposal. diff --git a/demo/document-rag/documents/rag-api.txt b/demo/document-rag/documents/rag-api.txt deleted file mode 100644 index 42990be..0000000 --- a/demo/document-rag/documents/rag-api.txt +++ /dev/null @@ -1,7 +0,0 @@ -RAG API Endpoints - -GET /health - checks VortexDB and AI provider readiness -POST /chat - accepts {"query": "..."} and returns an answer with source snippets -POST /query - returns raw retrieval sources without LLM generation - -The chat endpoint embeds the user question, searches VortexDB for top-k similar chunks, then calls the LLM with that context. diff --git a/demo/document-rag/documents/sales-proposal-northstar-draft.md b/demo/document-rag/documents/sales-proposal-northstar-draft.md new file mode 100644 index 0000000..12fa530 --- /dev/null +++ b/demo/document-rag/documents/sales-proposal-northstar-draft.md @@ -0,0 +1,10 @@ +# Draft Proposal: Northstar Energy Enterprise Upgrade + +**Drafted:** 2026-01-20 +**Status:** Superseded — do not use for current pricing + +This draft proposed an Enterprise upgrade for Northstar’s 42 sites. It described a 60-day raw telemetry retention period for Growth and listed webhook delivery as a paid add-on. + +The proposal was never signed. The plan catalogue changed on 2026-04-01: Growth now includes 90 days of raw telemetry, while webhooks are included for Growth and Enterprise. For current terms, use `pricing-and-plans.md` and the customer’s signed agreement. + +This document is retained only to demonstrate how retrieval systems should recognize stale commercial information. diff --git a/demo/document-rag/documents/security-and-access-policy.md b/demo/document-rag/documents/security-and-access-policy.md new file mode 100644 index 0000000..3c67e0e --- /dev/null +++ b/demo/document-rag/documents/security-and-access-policy.md @@ -0,0 +1,20 @@ +# Security and Access Policy + +**Effective:** 2026-06-15 +**Policy owner:** Security Engineering + +## Access principles + +Vortex uses least privilege, organization isolation, and time-bounded elevated access. Employees authenticate with SSO and phishing-resistant MFA. Production access is granted through named roles and logged. + +Contractors may access staging systems and sanitized support reproductions. They may not access production telemetry, production databases, customer exports, or incident channels containing customer data unless the Chief Information Security Officer grants a documented, time-limited exception. + +## Customer data retention + +Raw telemetry retention is controlled by the customer plan: Starter retains 30 days, Growth retains 90 days, and Enterprise retains 365 days by default. Enterprise customers can purchase an archival extension of up to seven years. Aggregated monthly metrics are retained for the life of an active account plus 12 months. + +This policy supersedes the draft retention language in the January planning notes. Legal holds suspend deletion for the data in scope. + +## Incident handling + +Suspected unauthorized access must be reported to `security@vortexlab.example` and the on-call reliability engineer within one hour. Security Engineering coordinates investigation, customer notification, and required regulatory reporting. diff --git a/demo/document-rag/documents/support-ticket-1842.md b/demo/document-rag/documents/support-ticket-1842.md new file mode 100644 index 0000000..fa1c634 --- /dev/null +++ b/demo/document-rag/documents/support-ticket-1842.md @@ -0,0 +1,11 @@ +# Support Ticket 1842: Missing Telemetry After Storm + +**Customer:** Rivermark Solar +**Opened:** 2026-06-03 +**Status:** Resolved + +Rivermark reported a gap in inverter telemetry after a regional storm. Support verified that the Aster gateway remained powered but had no cellular route from 02:18 to 06:47 local time. + +After connectivity returned, Aster uploaded the queued readings. The customer dashboard showed a temporary gap because its selected view was sorted by ingestion time; engineering confirmed the records existed when queried by `observed_at`. + +Support advised Rivermark to use the “event time” option in reporting and linked the API integration guide. No data was lost. The case also prompted a UI improvement request to label backfilled data more clearly. diff --git a/demo/document-rag/documents/support-ticket-1907.md b/demo/document-rag/documents/support-ticket-1907.md new file mode 100644 index 0000000..c0ec3a3 --- /dev/null +++ b/demo/document-rag/documents/support-ticket-1907.md @@ -0,0 +1,11 @@ +# Support Ticket 1907: Request for Contractor Access + +**Customer:** Orion Field Services +**Opened:** 2026-06-18 +**Status:** Closed — guidance provided + +Orion asked whether a third-party maintenance contractor could receive a Vortex login to investigate an active inverter alarm. + +Support explained that customer-managed users can be invited with the Site Technician role, limited to selected sites and alarm acknowledgement. The customer remains responsible for approving and removing that user. Vortex Lab contractors cannot receive production telemetry access under the standard Security and Access Policy. + +For a Vortex-assisted investigation, Support can create a sanitized reproduction or request a documented, time-limited security exception. Orion elected to invite its own technician and requested an audit-log export after the work was completed. diff --git a/demo/document-rag/documents/vortexdb-overview.txt b/demo/document-rag/documents/vortexdb-overview.txt deleted file mode 100644 index 945ea95..0000000 --- a/demo/document-rag/documents/vortexdb-overview.txt +++ /dev/null @@ -1,5 +0,0 @@ -VortexDB Overview - -VortexDB is a vector database designed for semantic search and retrieval-augmented generation (RAG). -It stores document chunks as dense vectors and supports cosine similarity search. -The HTTP API exposes endpoints for batch insert, search, and point retrieval. From f8a3b6d5f3a134e24fe8878c9a29aec00f32a754 Mon Sep 17 00:00:00 2001 From: ishaan Date: Fri, 21 Aug 2026 02:12:06 +0530 Subject: [PATCH 4/6] Revert "Merge branch 'refactor' into documet-rag-refactor" This reverts commit 6ec0d7d68eec1ebaf2ee96808360754549fc05dd, reversing changes made to 7950de3373e47e8c0e2cfbab4c285aebe5702ba5. --- .env.example | 6 +- .gitignore | 1 - README.md | 13 +- client/python/USAGE.md | 2 +- crates/api/src/lib.rs | 456 +++++++++++++++++++++- crates/api/src/tests.rs | 451 --------------------- crates/defs/src/auth.rs | 62 --- crates/defs/src/lib.rs | 2 - crates/grpc/src/interceptors.rs | 58 +-- crates/grpc/src/lib.rs | 12 +- crates/grpc/src/service.rs | 7 +- crates/grpc/src/tests.rs | 64 +-- crates/http/src/auth.rs | 59 --- crates/http/src/constants.rs | 1 - crates/http/src/lib.rs | 112 ++++-- crates/http/src/tests.rs | 171 -------- crates/index/src/flat/constants.rs | 3 - crates/index/src/flat/mod.rs | 5 +- crates/index/src/hnsw/constants.rs | 3 - crates/index/src/hnsw/mod.rs | 5 +- crates/index/src/kd_tree/constants.rs | 8 - crates/index/src/kd_tree/mod.rs | 5 +- crates/index/src/kd_tree/serialize.rs | 6 +- crates/server/src/config.rs | 132 +++---- crates/server/src/constants.rs | 28 -- crates/server/src/error.rs | 43 -- crates/server/src/main.rs | 11 +- crates/snapshot/src/constants.rs | 2 - crates/snapshot/src/lib.rs | 7 +- crates/snapshot/src/metadata.rs | 12 +- crates/snapshot/src/registry/constants.rs | 2 - crates/snapshot/src/registry/mod.rs | 3 +- crates/storage/src/in_memory.rs | 193 ++++++++- crates/storage/src/in_memory/constants.rs | 4 - crates/storage/src/in_memory/tests.rs | 182 --------- crates/storage/src/rocks_db.rs | 175 ++++++++- crates/storage/src/rocks_db/constants.rs | 2 - crates/storage/src/rocks_db/tests.rs | 161 -------- crates/tui/src/app/embeddings.rs | 5 +- crates/tui/src/app/events.rs | 4 +- crates/tui/src/constants.rs | 17 - crates/tui/src/main.rs | 3 +- crates/tui/src/ui/db.rs | 3 +- crates/tui/src/ui/vector_operations.rs | 9 +- docker-compose.yml | 3 +- docs/api-reference/grpc.mdx | 2 +- docs/api-reference/overview.mdx | 15 +- docs/concepts/architecture.mdx | 2 +- docs/getting-started/installation.mdx | 2 +- keys.example.json | 6 - 50 files changed, 1036 insertions(+), 1504 deletions(-) delete mode 100644 crates/api/src/tests.rs delete mode 100644 crates/defs/src/auth.rs delete mode 100644 crates/http/src/auth.rs delete mode 100644 crates/http/src/constants.rs delete mode 100644 crates/http/src/tests.rs delete mode 100644 crates/index/src/flat/constants.rs delete mode 100644 crates/index/src/hnsw/constants.rs delete mode 100644 crates/index/src/kd_tree/constants.rs delete mode 100644 crates/server/src/constants.rs delete mode 100644 crates/server/src/error.rs delete mode 100644 crates/storage/src/in_memory/constants.rs delete mode 100644 crates/storage/src/in_memory/tests.rs delete mode 100644 crates/storage/src/rocks_db/constants.rs delete mode 100644 crates/storage/src/rocks_db/tests.rs delete mode 100644 crates/tui/src/constants.rs delete mode 100644 keys.example.json diff --git a/.env.example b/.env.example index 96d5bcd..e92e8cb 100644 --- a/.env.example +++ b/.env.example @@ -5,10 +5,8 @@ HTTP_PORT=3000 # gRPC Server GRPC_HOST=127.0.0.1 GRPC_PORT=50051 - -# API keys used by both the HTTP and gRPC servers (required) -# See keys.example.json for the file format -VORTEXDB_KEYS_FILE=./keys.json +# required +GRPC_ROOT_PASSWORD=your-secure-password # Database Configuration # Storage: inmemory, rocksdb diff --git a/.gitignore b/.gitignore index 1fb9f85..b40848c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ .TODO /databases .env -/keys.json __pycache__/ build/ .venv/ diff --git a/README.md b/README.md index c6094ec..783316f 100644 --- a/README.md +++ b/README.md @@ -67,23 +67,12 @@ cp .env.example .env ``` The following env vars are important, and are required to be set by the user: -`VORTEXDB_KEYS_FILE` +`GRPC_ROOT_PASSWORD` `DIMENSION` `DATA_PATH` **NOTE**: `DATA_PATH` is the directory within the container where persistent data is stored -**NOTE**: `VORTEXDB_KEYS_FILE` points to a JSON file of API keys shared by both the HTTP and gRPC servers, e.g.: -```json -{ - "keys": [ - { "name": "admin", "role": "readwrite", "key": "some-random-secret" }, - { "name": "search-service", "role": "readonly", "key": "another-random-secret" } - ] -} -``` -Requests authenticate with an `api-key: ` header over HTTP, or an `authorization: Bearer ` header over gRPC. `readonly` keys can read/search but not insert, batch-insert, or delete; `readwrite` keys can do all of the above. See `keys.example.json`. - Setting of the following env vars is optional, as they fallback to safe defaults, but recommended: | .env Var | Function | Safe Default | diff --git a/client/python/USAGE.md b/client/python/USAGE.md index fbe22a0..be2e5d5 100644 --- a/client/python/USAGE.md +++ b/client/python/USAGE.md @@ -18,7 +18,7 @@ pip install -e . The client communicates with VortexDB over gRPC and requires: - gRPC endpoint (host:port) -- API key (must match a `key` entry in the server's `VORTEXDB_KEYS_FILE`) +- API key (maps to `GRPC_ROOT_PASSWORD` on the server) These can be provided either: diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 741efb2..08f89ea 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -281,4 +281,458 @@ pub fn init_api(config: DbConfig) -> Result { } #[cfg(test)] -mod tests; +mod tests { + + // TODO: Add more exhaustive tests + + use std::sync::Mutex; + + use super::*; + use defs::ContentType; + use snapshot::{engine::SnapshotEngine, registry::local::LocalRegistry}; + use tempfile::{TempDir, tempdir}; + + // Helper function to create a test database + fn create_test_db() -> (VectorDb, TempDir) { + create_test_db_with_storage(StorageType::RocksDb) + } + + fn create_test_db_with_storage(storage_type: StorageType) -> (VectorDb, TempDir) { + let temp_dir = tempdir().unwrap(); + let config = DbConfig { + storage_type, + index_type: IndexType::Flat, + data_path: temp_dir.path().to_path_buf(), + dimension: 3, + similarity: Similarity::Cosine, + hnsw_config: HnswConfig::default(), + kd_tree_config: KDTreeConfig::default(), + }; + (init_api(config).unwrap(), temp_dir) + } + + fn test_payload(content: &str) -> Payload { + Payload { + content_type: ContentType::Text, + content: content.to_string(), + } + } + + #[test] + fn test_insert_and_get() { + let (db, _temp_dir) = create_test_db(); + let vector = vec![1.0, 2.0, 3.0]; + let payload = Payload { + content_type: ContentType::Text, + content: "Test content".to_string(), + }; + + // Test insert + let id = db.insert(vector.clone(), payload.clone()).unwrap(); + assert!(id != Uuid::nil()); + + // Test get + let point = db.get(id).unwrap().unwrap(); + assert_eq!(point.id, id); + assert_eq!(point.vector.as_ref().unwrap(), &vector); + assert_eq!(point.payload.as_ref().unwrap(), &payload); + assert_eq!( + point.payload.as_ref().unwrap().content_type, + ContentType::Text + ); + assert_eq!(point.payload.as_ref().unwrap().content, "Test content"); + } + + #[test] + fn test_insert_and_get_with_in_memory_storage() { + let (db, _temp_dir) = create_test_db_with_storage(StorageType::InMemory); + let vector = vec![1.0, 2.0, 3.0]; + let payload = test_payload("Test content"); + + let id = db.insert(vector.clone(), payload.clone()).unwrap(); + let point = db.get(id).unwrap().unwrap(); + + assert_eq!(point.id, id); + assert_eq!(point.vector, Some(vector)); + assert_eq!(point.payload, Some(payload)); + } + + #[test] + fn test_dimension_mismatch() { + let (db, _temp_dir) = create_test_db(); + let v1 = vec![1.0, 2.0, 3.0]; + let v2 = vec![1.0, 2.0]; + let payload = defs::Payload { + content_type: ContentType::Text, + content: "tester".to_string(), + }; + + let res1 = db.insert(v1, payload.clone()); + assert!(res1.is_ok()); + + // Insert vector of dimension 2 != 3 + let res2 = db.insert(v2, payload); + assert!(res2.is_err()); + match res2.unwrap_err() { + ApiError::DimensionMismatch { expected, got } => { + assert_eq!(expected, 3); + assert_eq!(got, 2); + } + other => panic!("Expected DimensionMismatch, got: {:?}", other), + } + } + + #[test] + fn test_delete() { + let (db, _temp_dir) = create_test_db(); + let vector = vec![1.0, 2.0, 3.0]; + let payload = Payload { + content_type: ContentType::Text, + content: "Test content".to_string(), + }; + + // Insert a point + let id = db.insert(vector, payload).unwrap(); + + // try deleting a point that does not exist + let found = db.delete(Uuid::new_v4()); + assert!(found.is_ok()); + assert!(!found.unwrap()); + + // delete the point + assert!(db.get(id).unwrap().is_some()); + db.delete(id).unwrap(); + assert!(db.get(id).unwrap().is_none()); + } + + #[test] + fn test_search() { + let (db, _temp_dir) = create_test_db(); + + // Insert some points + let vectors = vec![ + vec![1.0, 0.0, 0.0], + vec![0.0, 1.0, 0.0], + vec![0.0, 0.0, 1.0], + ]; + + let mut ids = Vec::new(); + for vector in vectors { + let payload = Payload { + content_type: ContentType::Text, + content: format!("Test content {vector:?}"), + }; + let id = db.insert(vector, payload).unwrap(); + ids.push(id); + } + + // Search for the closest vector to [1.0, 0.1, 0.1] + let query = vec![1.0, 0.1, 0.1]; + let results = db + .search(SearchQueryInput { + vector: query, + similarity: Similarity::Cosine, + limit: 1, + ef: None, + }) + .unwrap(); + + assert_eq!(results.len(), 1); + assert_eq!(results[0], ids[0]); // The first vector should be closest + } + + #[test] + fn test_search_limit() { + let (db, _temp_dir) = create_test_db(); + + // Insert 5 points + let mut ids = Vec::new(); + for i in 0..5 { + let vector = vec![i as f32, 0.0, 0.0]; + let id = db + .insert( + vector, + Payload { + content_type: ContentType::Text, + content: format!("Test content {i}"), + }, + ) + .unwrap(); + ids.push(id); + } + + // Search with limit 3 + let query = vec![0.0, 0.0, 0.0]; + let results = db + .search(SearchQueryInput { + vector: query, + similarity: Similarity::Euclidean, + limit: 3, + ef: None, + }) + .unwrap(); + + assert_eq!(results.len(), 3); + } + + #[test] + fn test_search_zero_limit() { + let (db, _temp_dir) = create_test_db(); + + let query = vec![1.0, 2.0, 3.0]; + let result = db.search(SearchQueryInput { + vector: query, + similarity: Similarity::Cosine, + limit: 0, + ef: None, + }); + + assert!(result.is_err()); + match result.unwrap_err() { + ApiError::InvalidSearchLimit { limit } => { + assert_eq!(limit, 0); + } + other => panic!("Expected InvalidSearchLimit, got: {:?}", other), + } + } + + #[test] + fn test_empty_database() { + let (db, _temp_dir) = create_test_db(); + + // Get non-existent point + assert!(db.get(Uuid::new_v4()).unwrap().is_none()); + + let query = vec![1.0, 2.0, 3.0]; + let results = db + .search(SearchQueryInput { + vector: query, + similarity: Similarity::Cosine, + limit: 10, + ef: None, + }) + .unwrap(); + assert_eq!(results.len(), 0); + } + + #[test] + fn test_list_vectors() { + let (db, _temp_dir) = create_test_db(); + // insert some points + let mut ids = Vec::new(); + for i in 0..10 { + let i = i as f32; + let vector = vec![i, i + 1.0, i + 2.0]; + let id = db + .insert( + vector, + Payload { + content_type: ContentType::Text, + content: format!("Test content {i}"), + }, + ) + .unwrap(); + ids.push(id); + } + + // list vectors with limit 5 + // list the values as well as their length + let (vectors, next_offset) = db.list(Uuid::nil(), 5).unwrap().unwrap(); + assert_eq!(vectors.len(), 5); + + // list next set of vectors + // list the values as well as their length + let (next_vectors, _) = db.list(next_offset, 5).unwrap().unwrap(); + assert_eq!(next_vectors.len(), 5); + } + + #[test] + fn test_build_index() { + let (db, _temp_dir) = create_test_db(); + + // insert some points + for i in 0..10 { + let i = i as f32; + let vector = vec![i, i + 1.0, i + 2.0]; + db.insert( + vector, + Payload { + content_type: ContentType::Text, + content: format!("Test content {i}"), + }, + ) + .unwrap(); + } + + // rebuild the index + let inserted = db.build_index().unwrap(); + assert_eq!(inserted, 10); + } + + #[test] + fn test_create_and_load_snapshot() { + let (old_db, temp_dir) = create_test_db(); + + let v1 = vec![0.0, 1.0, 2.0]; + let v2 = vec![3.0, 4.0, 5.0]; + let v3 = vec![6.0, 7.0, 8.0]; + + let id1 = old_db + .insert( + v1.clone(), + Payload { + content_type: ContentType::Text, + content: "test".to_string(), + }, + ) + .unwrap(); + + let id2 = old_db + .insert( + v2.clone(), + Payload { + content_type: ContentType::Text, + content: "test".to_string(), + }, + ) + .unwrap(); + + let temp_snapshot_dir = tempdir().unwrap(); + let snapshot_path = old_db.create_snapshot(temp_snapshot_dir.path()).unwrap(); + + // insert v3 after snapshot + let id3 = old_db + .insert( + v3.clone(), + Payload { + content_type: ContentType::Text, + content: "test".to_string(), + }, + ) + .unwrap(); + + let reload_config = DbRestoreConfig { + data_path: temp_dir.path().to_path_buf(), + snapshot_path, + }; + + std::mem::drop(old_db); + let loaded_db = restore_from_snapshot(&reload_config).unwrap(); + + assert!(loaded_db.get(id1).unwrap_or(None).is_some()); + assert!(loaded_db.get(id2).unwrap_or(None).is_some()); + assert!(loaded_db.get(id3).unwrap_or(None).is_none()); // v3 was inserted after snapshot was taken + + // vector restore check + assert!(loaded_db.get(id1).unwrap().unwrap().vector.unwrap() == v1); + assert!(loaded_db.get(id2).unwrap().unwrap().vector.unwrap() == v2); + } + + #[test] + fn test_create_and_load_snapshot_with_in_memory_storage() { + let (old_db, temp_dir) = create_test_db_with_storage(StorageType::InMemory); + + let v1 = vec![0.0, 1.0, 2.0]; + let v2 = vec![3.0, 4.0, 5.0]; + let v3 = vec![6.0, 7.0, 8.0]; + + let id1 = old_db.insert(v1.clone(), test_payload("one")).unwrap(); + let id2 = old_db.insert(v2.clone(), test_payload("two")).unwrap(); + + let temp_snapshot_dir = tempdir().unwrap(); + let snapshot_path = old_db.create_snapshot(temp_snapshot_dir.path()).unwrap(); + + let id3 = old_db.insert(v3, test_payload("three")).unwrap(); + + let reload_config = DbRestoreConfig { + data_path: temp_dir.path().to_path_buf(), + snapshot_path, + }; + + let loaded_db = restore_from_snapshot(&reload_config).unwrap(); + + assert_eq!(loaded_db.get(id1).unwrap().unwrap().vector, Some(v1)); + assert_eq!(loaded_db.get(id2).unwrap().unwrap().vector, Some(v2)); + assert!(loaded_db.get(id3).unwrap().is_none()); + } + + #[test] + fn test_snapshot_engine() { + let (_db, _temp_dir) = create_test_db(); + let db = Arc::new(Mutex::new(_db)); + + let registry_tempdir = tempdir().unwrap(); + + let registry = Arc::new(Mutex::new( + LocalRegistry::new(registry_tempdir.path()).unwrap(), + )); + + let last_k = 4; + let mut se = SnapshotEngine::new(last_k, db.clone(), registry.clone()); + + let v1 = vec![0.0, 1.0, 2.0]; + let v2 = vec![3.0, 4.0, 5.0]; + let v3 = vec![6.0, 7.0, 8.0]; + + let test_vectors = vec![v1.clone(), v2.clone(), v3.clone()]; + let mut inserted_ids = Vec::new(); + + for (i, vector) in test_vectors.clone().into_iter().enumerate() { + se.snapshot().unwrap(); + let id = db + .lock() + .unwrap() + .insert( + vector.clone(), + Payload { + content_type: ContentType::Text, + content: format!("{}", i), + }, + ) + .unwrap(); + inserted_ids.push(id); + } + se.snapshot().unwrap(); + let snapshots = se.list_alive_snapshots().unwrap(); + + // asserting these cases: + // snapshot 0 : no vectors + // snapshot 1 : v1 + // snapshot 2 : v1, v2 + // snapshot 3 : v1, v2, v3 + + std::mem::drop(db); + std::mem::drop(se); + + for (i, snapshot) in snapshots.iter().enumerate() { + let temp_dir = tempdir().unwrap(); + let db = restore_from_snapshot(&DbRestoreConfig { + data_path: temp_dir.path().to_path_buf(), + snapshot_path: snapshot.path.clone(), + }) + .unwrap(); + for j in 0..i { + // test if point is present + assert!(db.get(inserted_ids[j]).unwrap_or(None).is_some()); + // test vector restore + assert!( + db.get(inserted_ids[j]).unwrap().unwrap().vector.unwrap() == test_vectors[j] + ); + // test payload restore + assert!( + db.get(inserted_ids[j]) + .unwrap() + .unwrap() + .payload + .unwrap() + .content + == format!("{}", j) + ); + } + for absent_id in inserted_ids.iter().skip(i) { + assert!(db.get(*absent_id).unwrap_or(None).is_none()); + } + std::mem::drop(db); + } + } +} diff --git a/crates/api/src/tests.rs b/crates/api/src/tests.rs deleted file mode 100644 index ea9504b..0000000 --- a/crates/api/src/tests.rs +++ /dev/null @@ -1,451 +0,0 @@ -use super::*; - -// TODO: Add more exhaustive tests - -use std::sync::Mutex; - -use defs::ContentType; -use snapshot::{engine::SnapshotEngine, registry::local::LocalRegistry}; -use tempfile::{TempDir, tempdir}; - -// Helper function to create a test database -fn create_test_db() -> (VectorDb, TempDir) { - create_test_db_with_storage(StorageType::RocksDb) -} - -fn create_test_db_with_storage(storage_type: StorageType) -> (VectorDb, TempDir) { - let temp_dir = tempdir().unwrap(); - let config = DbConfig { - storage_type, - index_type: IndexType::Flat, - data_path: temp_dir.path().to_path_buf(), - dimension: 3, - similarity: Similarity::Cosine, - hnsw_config: HnswConfig::default(), - kd_tree_config: KDTreeConfig::default(), - }; - (init_api(config).unwrap(), temp_dir) -} - -fn test_payload(content: &str) -> Payload { - Payload { - content_type: ContentType::Text, - content: content.to_string(), - } -} - -#[test] -fn test_insert_and_get() { - let (db, _temp_dir) = create_test_db(); - let vector = vec![1.0, 2.0, 3.0]; - let payload = Payload { - content_type: ContentType::Text, - content: "Test content".to_string(), - }; - - // Test insert - let id = db.insert(vector.clone(), payload.clone()).unwrap(); - assert!(id != Uuid::nil()); - - // Test get - let point = db.get(id).unwrap().unwrap(); - assert_eq!(point.id, id); - assert_eq!(point.vector.as_ref().unwrap(), &vector); - assert_eq!(point.payload.as_ref().unwrap(), &payload); - assert_eq!( - point.payload.as_ref().unwrap().content_type, - ContentType::Text - ); - assert_eq!(point.payload.as_ref().unwrap().content, "Test content"); -} - -#[test] -fn test_insert_and_get_with_in_memory_storage() { - let (db, _temp_dir) = create_test_db_with_storage(StorageType::InMemory); - let vector = vec![1.0, 2.0, 3.0]; - let payload = test_payload("Test content"); - - let id = db.insert(vector.clone(), payload.clone()).unwrap(); - let point = db.get(id).unwrap().unwrap(); - - assert_eq!(point.id, id); - assert_eq!(point.vector, Some(vector)); - assert_eq!(point.payload, Some(payload)); -} - -#[test] -fn test_dimension_mismatch() { - let (db, _temp_dir) = create_test_db(); - let v1 = vec![1.0, 2.0, 3.0]; - let v2 = vec![1.0, 2.0]; - let payload = defs::Payload { - content_type: ContentType::Text, - content: "tester".to_string(), - }; - - let res1 = db.insert(v1, payload.clone()); - assert!(res1.is_ok()); - - // Insert vector of dimension 2 != 3 - let res2 = db.insert(v2, payload); - assert!(res2.is_err()); - match res2.unwrap_err() { - ApiError::DimensionMismatch { expected, got } => { - assert_eq!(expected, 3); - assert_eq!(got, 2); - } - other => panic!("Expected DimensionMismatch, got: {:?}", other), - } -} - -#[test] -fn test_delete() { - let (db, _temp_dir) = create_test_db(); - let vector = vec![1.0, 2.0, 3.0]; - let payload = Payload { - content_type: ContentType::Text, - content: "Test content".to_string(), - }; - - // Insert a point - let id = db.insert(vector, payload).unwrap(); - - // try deleting a point that does not exist - let found = db.delete(Uuid::new_v4()); - assert!(found.is_ok()); - assert!(!found.unwrap()); - - // delete the point - assert!(db.get(id).unwrap().is_some()); - db.delete(id).unwrap(); - assert!(db.get(id).unwrap().is_none()); -} - -#[test] -fn test_search() { - let (db, _temp_dir) = create_test_db(); - - // Insert some points - let vectors = vec![ - vec![1.0, 0.0, 0.0], - vec![0.0, 1.0, 0.0], - vec![0.0, 0.0, 1.0], - ]; - - let mut ids = Vec::new(); - for vector in vectors { - let payload = Payload { - content_type: ContentType::Text, - content: format!("Test content {vector:?}"), - }; - let id = db.insert(vector, payload).unwrap(); - ids.push(id); - } - - // Search for the closest vector to [1.0, 0.1, 0.1] - let query = vec![1.0, 0.1, 0.1]; - let results = db - .search(SearchQueryInput { - vector: query, - similarity: Similarity::Cosine, - limit: 1, - ef: None, - }) - .unwrap(); - - assert_eq!(results.len(), 1); - assert_eq!(results[0], ids[0]); // The first vector should be closest -} - -#[test] -fn test_search_limit() { - let (db, _temp_dir) = create_test_db(); - - // Insert 5 points - let mut ids = Vec::new(); - for i in 0..5 { - let vector = vec![i as f32, 0.0, 0.0]; - let id = db - .insert( - vector, - Payload { - content_type: ContentType::Text, - content: format!("Test content {i}"), - }, - ) - .unwrap(); - ids.push(id); - } - - // Search with limit 3 - let query = vec![0.0, 0.0, 0.0]; - let results = db - .search(SearchQueryInput { - vector: query, - similarity: Similarity::Euclidean, - limit: 3, - ef: None, - }) - .unwrap(); - - assert_eq!(results.len(), 3); -} - -#[test] -fn test_search_zero_limit() { - let (db, _temp_dir) = create_test_db(); - - let query = vec![1.0, 2.0, 3.0]; - let result = db.search(SearchQueryInput { - vector: query, - similarity: Similarity::Cosine, - limit: 0, - ef: None, - }); - - assert!(result.is_err()); - match result.unwrap_err() { - ApiError::InvalidSearchLimit { limit } => { - assert_eq!(limit, 0); - } - other => panic!("Expected InvalidSearchLimit, got: {:?}", other), - } -} - -#[test] -fn test_empty_database() { - let (db, _temp_dir) = create_test_db(); - - // Get non-existent point - assert!(db.get(Uuid::new_v4()).unwrap().is_none()); - - let query = vec![1.0, 2.0, 3.0]; - let results = db - .search(SearchQueryInput { - vector: query, - similarity: Similarity::Cosine, - limit: 10, - ef: None, - }) - .unwrap(); - assert_eq!(results.len(), 0); -} - -#[test] -fn test_list_vectors() { - let (db, _temp_dir) = create_test_db(); - // insert some points - let mut ids = Vec::new(); - for i in 0..10 { - let i = i as f32; - let vector = vec![i, i + 1.0, i + 2.0]; - let id = db - .insert( - vector, - Payload { - content_type: ContentType::Text, - content: format!("Test content {i}"), - }, - ) - .unwrap(); - ids.push(id); - } - - // list vectors with limit 5 - // list the values as well as their length - let (vectors, next_offset) = db.list(Uuid::nil(), 5).unwrap().unwrap(); - assert_eq!(vectors.len(), 5); - - // list next set of vectors - // list the values as well as their length - let (next_vectors, _) = db.list(next_offset, 5).unwrap().unwrap(); - assert_eq!(next_vectors.len(), 5); -} - -#[test] -fn test_build_index() { - let (db, _temp_dir) = create_test_db(); - - // insert some points - for i in 0..10 { - let i = i as f32; - let vector = vec![i, i + 1.0, i + 2.0]; - db.insert( - vector, - Payload { - content_type: ContentType::Text, - content: format!("Test content {i}"), - }, - ) - .unwrap(); - } - - // rebuild the index - let inserted = db.build_index().unwrap(); - assert_eq!(inserted, 10); -} - -#[test] -fn test_create_and_load_snapshot() { - let (old_db, temp_dir) = create_test_db(); - - let v1 = vec![0.0, 1.0, 2.0]; - let v2 = vec![3.0, 4.0, 5.0]; - let v3 = vec![6.0, 7.0, 8.0]; - - let id1 = old_db - .insert( - v1.clone(), - Payload { - content_type: ContentType::Text, - content: "test".to_string(), - }, - ) - .unwrap(); - - let id2 = old_db - .insert( - v2.clone(), - Payload { - content_type: ContentType::Text, - content: "test".to_string(), - }, - ) - .unwrap(); - - let temp_snapshot_dir = tempdir().unwrap(); - let snapshot_path = old_db.create_snapshot(temp_snapshot_dir.path()).unwrap(); - - // insert v3 after snapshot - let id3 = old_db - .insert( - v3.clone(), - Payload { - content_type: ContentType::Text, - content: "test".to_string(), - }, - ) - .unwrap(); - - let reload_config = DbRestoreConfig { - data_path: temp_dir.path().to_path_buf(), - snapshot_path, - }; - - std::mem::drop(old_db); - let loaded_db = restore_from_snapshot(&reload_config).unwrap(); - - assert!(loaded_db.get(id1).unwrap_or(None).is_some()); - assert!(loaded_db.get(id2).unwrap_or(None).is_some()); - assert!(loaded_db.get(id3).unwrap_or(None).is_none()); // v3 was inserted after snapshot was taken - - // vector restore check - assert!(loaded_db.get(id1).unwrap().unwrap().vector.unwrap() == v1); - assert!(loaded_db.get(id2).unwrap().unwrap().vector.unwrap() == v2); -} - -#[test] -fn test_create_and_load_snapshot_with_in_memory_storage() { - let (old_db, temp_dir) = create_test_db_with_storage(StorageType::InMemory); - - let v1 = vec![0.0, 1.0, 2.0]; - let v2 = vec![3.0, 4.0, 5.0]; - let v3 = vec![6.0, 7.0, 8.0]; - - let id1 = old_db.insert(v1.clone(), test_payload("one")).unwrap(); - let id2 = old_db.insert(v2.clone(), test_payload("two")).unwrap(); - - let temp_snapshot_dir = tempdir().unwrap(); - let snapshot_path = old_db.create_snapshot(temp_snapshot_dir.path()).unwrap(); - - let id3 = old_db.insert(v3, test_payload("three")).unwrap(); - - let reload_config = DbRestoreConfig { - data_path: temp_dir.path().to_path_buf(), - snapshot_path, - }; - - let loaded_db = restore_from_snapshot(&reload_config).unwrap(); - - assert_eq!(loaded_db.get(id1).unwrap().unwrap().vector, Some(v1)); - assert_eq!(loaded_db.get(id2).unwrap().unwrap().vector, Some(v2)); - assert!(loaded_db.get(id3).unwrap().is_none()); -} - -#[test] -fn test_snapshot_engine() { - let (_db, _temp_dir) = create_test_db(); - let db = Arc::new(Mutex::new(_db)); - - let registry_tempdir = tempdir().unwrap(); - - let registry = Arc::new(Mutex::new( - LocalRegistry::new(registry_tempdir.path()).unwrap(), - )); - - let last_k = 4; - let mut se = SnapshotEngine::new(last_k, db.clone(), registry.clone()); - - let v1 = vec![0.0, 1.0, 2.0]; - let v2 = vec![3.0, 4.0, 5.0]; - let v3 = vec![6.0, 7.0, 8.0]; - - let test_vectors = vec![v1.clone(), v2.clone(), v3.clone()]; - let mut inserted_ids = Vec::new(); - - for (i, vector) in test_vectors.clone().into_iter().enumerate() { - se.snapshot().unwrap(); - let id = db - .lock() - .unwrap() - .insert( - vector.clone(), - Payload { - content_type: ContentType::Text, - content: format!("{}", i), - }, - ) - .unwrap(); - inserted_ids.push(id); - } - se.snapshot().unwrap(); - let snapshots = se.list_alive_snapshots().unwrap(); - - // asserting these cases: - // snapshot 0 : no vectors - // snapshot 1 : v1 - // snapshot 2 : v1, v2 - // snapshot 3 : v1, v2, v3 - - std::mem::drop(db); - std::mem::drop(se); - - for (i, snapshot) in snapshots.iter().enumerate() { - let temp_dir = tempdir().unwrap(); - let db = restore_from_snapshot(&DbRestoreConfig { - data_path: temp_dir.path().to_path_buf(), - snapshot_path: snapshot.path.clone(), - }) - .unwrap(); - for j in 0..i { - // test if point is present - assert!(db.get(inserted_ids[j]).unwrap_or(None).is_some()); - // test vector restore - assert!(db.get(inserted_ids[j]).unwrap().unwrap().vector.unwrap() == test_vectors[j]); - // test payload restore - assert!( - db.get(inserted_ids[j]) - .unwrap() - .unwrap() - .payload - .unwrap() - .content - == format!("{}", j) - ); - } - for absent_id in inserted_ids.iter().skip(i) { - assert!(db.get(*absent_id).unwrap_or(None).is_none()); - } - std::mem::drop(db); - } -} diff --git a/crates/defs/src/auth.rs b/crates/defs/src/auth.rs deleted file mode 100644 index 793055d..0000000 --- a/crates/defs/src/auth.rs +++ /dev/null @@ -1,62 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum ApiKeyRole { - ReadOnly, - ReadWrite, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ApiKeyEntry { - pub name: String, - pub role: ApiKeyRole, - pub key: String, -} - -#[derive(Debug, Clone, Default)] -pub struct ApiKeyStore { - keys: Vec, -} - -fn constant_time_eq(a: &[u8], b: &[u8]) -> bool { - if a.len() != b.len() { - return false; - } - let mut diff = 0u8; - for (x, y) in a.iter().zip(b.iter()) { - diff |= x ^ y; - } - diff == 0 -} - -impl ApiKeyStore { - pub fn new(keys: Vec) -> Self { - Self { keys } - } - - pub fn find(&self, presented: &str) -> Option<&ApiKeyEntry> { - let mut matched = None; - for entry in &self.keys { - if constant_time_eq(entry.key.as_bytes(), presented.as_bytes()) { - matched = Some(entry); - } - } - matched - } - - pub fn is_empty(&self) -> bool { - self.keys.is_empty() - } - - pub fn duplicate_key(&self) -> Option<&str> { - for (i, entry) in self.keys.iter().enumerate() { - for other in &self.keys[i + 1..] { - if constant_time_eq(entry.key.as_bytes(), other.key.as_bytes()) { - return Some(&entry.key); - } - } - } - None - } -} diff --git a/crates/defs/src/lib.rs b/crates/defs/src/lib.rs index fd55902..cf3b3f3 100644 --- a/crates/defs/src/lib.rs +++ b/crates/defs/src/lib.rs @@ -1,9 +1,7 @@ -pub mod auth; pub mod error; pub mod types; // Without re-exports, users would need to write defs::types::SomeType instead of just defs::SomeType. Re-exports simplify the API by flattening the module hierarchy. The * means "everything public" from that module. -pub use auth::*; pub use error::*; use std::path::{Path, PathBuf}; pub use types::*; diff --git a/crates/grpc/src/interceptors.rs b/crates/grpc/src/interceptors.rs index 5b149d4..0b760f6 100644 --- a/crates/grpc/src/interceptors.rs +++ b/crates/grpc/src/interceptors.rs @@ -1,6 +1,3 @@ -use std::sync::Arc; - -use defs::{ApiKeyEntry, ApiKeyRole, ApiKeyStore}; use tonic::{Status, service::Interceptor}; use tracing::{Level, event}; @@ -8,49 +5,32 @@ use crate::constants::AUTHORIZATION_HEADER_KEY; #[derive(Clone)] pub struct AuthInterceptor { - keys: Arc, -} - -fn extract_bearer_token(req: &tonic::Request) -> Option<&str> { - req.metadata() - .get(AUTHORIZATION_HEADER_KEY)? - .to_str() - .ok()? - .strip_prefix("Bearer ") - .filter(|token| !token.is_empty()) -} - -pub fn require_write_role(req: &tonic::Request) -> Result<(), Status> { - match req.extensions().get::() { - Some(entry) if entry.role == ApiKeyRole::ReadWrite => Ok(()), - Some(_) => Err(Status::permission_denied( - "This api key does not have write access", - )), - None => Err(Status::unauthenticated("Invalid credentials")), - } + root_password: String, } impl Interceptor for AuthInterceptor { - fn call(&mut self, mut req: tonic::Request<()>) -> Result, Status> { - let matched = extract_bearer_token(&req) - .and_then(|token| self.keys.find(token)) - .cloned(); - - match matched { - Some(entry) => { - req.extensions_mut().insert(entry); - Ok(req) - } - None => { - event!(Level::WARN, "Unauthorized Request"); - Err(Status::unauthenticated("Invalid credentials")) - } + fn call(&mut self, req: tonic::Request<()>) -> Result, Status> { + let auth_token = match req.metadata().get(AUTHORIZATION_HEADER_KEY) { + Some(t) => t, + None => return Err(Status::unauthenticated("Invalid credentials")), + }; + if auth_token + .to_str() + .unwrap_or_default() + .strip_prefix("Bearer ") + .unwrap_or_default() + == self.root_password + { + Ok(req) + } else { + event!(Level::WARN, "Unauthorized Request"); + Err(Status::unauthenticated("Invalid credentials")) } } } impl AuthInterceptor { - pub fn new(keys: Arc) -> AuthInterceptor { - AuthInterceptor { keys } + pub fn new(root_password: String) -> AuthInterceptor { + AuthInterceptor { root_password } } } diff --git a/crates/grpc/src/lib.rs b/crates/grpc/src/lib.rs index fc6d5ab..a8f98a4 100644 --- a/crates/grpc/src/lib.rs +++ b/crates/grpc/src/lib.rs @@ -15,13 +15,17 @@ use utils::ServerEndpoint; pub async fn run_grpc_server( db: Arc, addr: SocketAddr, - keys: Arc, + root_password: String, logging: bool, ) -> Result<(), BoxError> { let vector_db_service = VectorDBService::new(db, logging); - run_server(vector_db_service, ServerEndpoint::Address(addr), keys) - .await - .map_err(|e| -> BoxError { e.to_string().into() }) + run_server( + vector_db_service, + ServerEndpoint::Address(addr), + root_password, + ) + .await + .map_err(|e| -> BoxError { e.to_string().into() }) } #[cfg(test)] diff --git a/crates/grpc/src/service.rs b/crates/grpc/src/service.rs index 9225077..6e0d2c1 100644 --- a/crates/grpc/src/service.rs +++ b/crates/grpc/src/service.rs @@ -38,7 +38,6 @@ impl VectorDb for VectorDBService { request: Request, ) -> Result, Status> { log_rpc("insert_vector", self.logging); - interceptors::require_write_role(&request)?; let inner_request = request.into_inner(); @@ -158,7 +157,6 @@ impl VectorDb for VectorDBService { async fn delete_point(&self, request: Request) -> Result, Status> { log_rpc("delete_point", self.logging); - interceptors::require_write_role(&request)?; let point_id = request.into_inner().id.unwrap_or_default().value; @@ -181,7 +179,6 @@ impl VectorDb for VectorDBService { &self, request: tonic::Request, ) -> Result, tonic::Status> { - interceptors::require_write_role(&request)?; let req = request.into_inner(); let mut ids = Vec::with_capacity(req.vectors.len()); @@ -257,11 +254,11 @@ impl VectorDb for VectorDBService { pub async fn run_server( vector_db_service: VectorDBService, endpoint: ServerEndpoint, - keys: Arc, + root_password: String, ) -> Result<(), Box> { event!(Level::INFO, "Starting gRPC server at: {:?}", endpoint); - let auth_interceptor = interceptors::AuthInterceptor::new(keys); + let auth_interceptor = interceptors::AuthInterceptor::new(root_password); let router = Server::builder() .layer(InterceptorLayer::new(auth_interceptor)) diff --git a/crates/grpc/src/tests.rs b/crates/grpc/src/tests.rs index e623e13..704dd8f 100644 --- a/crates/grpc/src/tests.rs +++ b/crates/grpc/src/tests.rs @@ -6,7 +6,7 @@ use crate::service::vectordb::{ use crate::service::{VectorDBService, run_server}; use crate::utils::ServerEndpoint; use api::DbConfig; -use defs::{ApiKeyEntry, ApiKeyRole, ApiKeyStore, Similarity}; +use defs::Similarity; use index::{IndexType, hnsw::HnswConfig, kd_tree::KDTreeConfig}; use std::net::SocketAddr; use std::sync::Arc; @@ -17,7 +17,6 @@ use tonic::transport::Channel; // Inspired from https://github.com/hyperium/tonic/discussions/924#discussioncomment-9854088 const TEST_AUTH_BEARER_TOKEN: &str = "123"; -const TEST_READONLY_BEARER_TOKEN: &str = "456"; fn append_test_auth_header(request: &mut tonic::Request, token: &str) { let auth_value = format!("Bearer {}", token); @@ -42,28 +41,19 @@ async fn start_test_server() -> Result<(SocketAddr, TempDir), Box Option<&str> { - req.headers().get(API_KEY_HEADER)?.to_str().ok() -} - -fn unauthorized() -> Response { - ( - StatusCode::UNAUTHORIZED, - Json(json!({ "error": "Missing or invalid api-key header" })), - ) - .into_response() -} - -fn forbidden() -> Response { - ( - StatusCode::FORBIDDEN, - Json(json!({ "error": "This api-key does not have write access" })), - ) - .into_response() -} - -pub async fn require_write_key( - State(state): State, - req: Request, - next: Next, -) -> Response { - let Some(key) = extract_key(&req) else { - return unauthorized(); - }; - - match state.keys.find(key) { - Some(entry) if entry.role == ApiKeyRole::ReadWrite => next.run(req).await, - Some(_) => forbidden(), - None => unauthorized(), - } -} - -pub async fn require_read_key(State(state): State, req: Request, next: Next) -> Response { - let Some(key) = extract_key(&req) else { - return unauthorized(); - }; - - match state.keys.find(key) { - Some(_) => next.run(req).await, - None => unauthorized(), - } -} diff --git a/crates/http/src/constants.rs b/crates/http/src/constants.rs deleted file mode 100644 index 8b9329c..0000000 --- a/crates/http/src/constants.rs +++ /dev/null @@ -1 +0,0 @@ -pub const API_KEY_HEADER: &str = "api-key"; diff --git a/crates/http/src/lib.rs b/crates/http/src/lib.rs index 1232995..69146d5 100644 --- a/crates/http/src/lib.rs +++ b/crates/http/src/lib.rs @@ -1,5 +1,3 @@ -pub mod auth; -pub mod constants; pub mod handler; use api::VectorDb; @@ -7,8 +5,6 @@ use axum::{ Router, extract::DefaultBodyLimit, routing::{get, post}, - Router, middleware, - routing::{delete, get, post}, }; use defs::BoxError; use std::net::SocketAddr; @@ -24,48 +20,29 @@ use handler::{ #[derive(Clone)] pub struct AppState { pub db: Arc, - pub keys: Arc, } -pub fn create_router(db: Arc, keys: Arc) -> Router { - let app_state = AppState { db, keys }; - - let public_routes = Router::new() +/// Creates the HTTP router with all VectorDB routes. +pub fn create_router(db: Arc) -> Router { + let app_state = AppState { db }; + Router::new() .route("/", get(root_handler)) - .route("/health", get(health_handler)); - - let write_routes = Router::new() + .route("/health", get(health_handler)) .route("/points", post(insert_point_handler)) - .route("/points/{id}", delete(delete_point_handler)) - .route("/points/batch", post(batch_insert_handler)) - .route_layer(middleware::from_fn_with_state( - app_state.clone(), - auth::require_write_key, - )); - - let read_routes = Router::new() - .route("/points/{id}", get(get_point_handler)) + .route( + "/points/{id}", + get(get_point_handler).delete(delete_point_handler), + ) .route("/points/search", post(search_points_handler)) + .route("/points/batch", post(batch_insert_handler)) .route("/points/search/batch", post(batch_search_handler)) - .route_layer(middleware::from_fn_with_state( - app_state.clone(), - auth::require_read_key, - )); - - public_routes - .merge(write_routes) - .merge(read_routes) .with_state(app_state) .layer(DefaultBodyLimit::max(50 * 1024 * 1024)) // 50MB limit } /// Runs the HTTP server on the specified address. -pub async fn run_http_server( - db: Arc, - addr: SocketAddr, - keys: Arc, -) -> Result<(), BoxError> { - let app = create_router(db, keys); +pub async fn run_http_server(db: Arc, addr: SocketAddr) -> Result<(), BoxError> { + let app = create_router(db); let listener = TcpListener::bind(addr).await?; info!("HTTP server listening on http://{}", addr); axum::serve(listener, app.into_make_service()).await?; @@ -73,4 +50,67 @@ pub async fn run_http_server( } #[cfg(test)] -mod tests; +mod tests { + use super::*; + use api::DbConfig; + use axum::http::StatusCode; + use axum_test::TestServer; + use defs::Similarity; + use index::{IndexType, hnsw::HnswConfig, kd_tree::KDTreeConfig}; + use serde_json::json; + use storage::StorageType; + + #[tokio::test] + async fn in_memory_storage_http_smoke_test() { + let temp_dir = tempfile::tempdir().unwrap(); + let db = api::init_api(DbConfig { + storage_type: StorageType::InMemory, + index_type: IndexType::Flat, + data_path: temp_dir.path().to_path_buf(), + dimension: 3, + similarity: Similarity::Cosine, + hnsw_config: HnswConfig::default(), + kd_tree_config: KDTreeConfig::default(), + }) + .unwrap(); + let server = TestServer::new(create_router(Arc::new(db))).unwrap(); + + let insert_response = server + .post("/points") + .json(&json!({ + "vector": [1.0, 0.0, 0.0], + "payload": { + "content_type": "Text", + "content": "smoke-test" + } + })) + .await; + insert_response.assert_status(StatusCode::CREATED); + let insert_body: serde_json::Value = insert_response.json(); + let point_id = insert_body["point_id"].as_str().unwrap(); + + let get_response = server.get(&format!("/points/{point_id}")).await; + get_response.assert_status_ok(); + let point_body: serde_json::Value = get_response.json(); + assert_eq!(point_body["payload"]["content"], "smoke-test"); + assert_eq!(point_body["vector"], json!([1.0, 0.0, 0.0])); + + let search_response = server + .post("/points/search") + .json(&json!({ + "vector": [1.0, 0.0, 0.0], + "similarity": "Cosine", + "limit": 1 + })) + .await; + search_response.assert_status_ok(); + let search_body: serde_json::Value = search_response.json(); + assert_eq!(search_body["results"], json!([point_id])); + + let delete_response = server.delete(&format!("/points/{point_id}")).await; + delete_response.assert_status(StatusCode::NO_CONTENT); + + let missing_response = server.get(&format!("/points/{point_id}")).await; + missing_response.assert_status(StatusCode::NOT_FOUND); + } +} diff --git a/crates/http/src/tests.rs b/crates/http/src/tests.rs deleted file mode 100644 index b201716..0000000 --- a/crates/http/src/tests.rs +++ /dev/null @@ -1,171 +0,0 @@ -use super::*; -use api::DbConfig; -use axum::http::StatusCode; -use axum_test::TestServer; -use defs::Similarity; -use index::{IndexType, hnsw::HnswConfig, kd_tree::KDTreeConfig}; -use serde_json::json; -use storage::StorageType; - -use defs::{ApiKeyEntry, ApiKeyRole, ApiKeyStore}; - -const API_KEY: &str = "full-access-key"; -const READONLY_API_KEY: &str = "read-only-key"; - -fn test_db() -> Arc { - let temp_dir = tempfile::tempdir().unwrap(); - let db = api::init_api(DbConfig { - storage_type: StorageType::InMemory, - index_type: IndexType::Flat, - data_path: temp_dir.path().to_path_buf(), - dimension: 3, - similarity: Similarity::Cosine, - hnsw_config: HnswConfig::default(), - kd_tree_config: KDTreeConfig::default(), - }) - .unwrap(); - Arc::new(db) -} - -fn test_server() -> TestServer { - let keys = ApiKeyStore::new(vec![ - ApiKeyEntry { - name: "full".to_string(), - role: ApiKeyRole::ReadWrite, - key: API_KEY.to_string(), - }, - ApiKeyEntry { - name: "readonly".to_string(), - role: ApiKeyRole::ReadOnly, - key: READONLY_API_KEY.to_string(), - }, - ]); - let router = create_router(test_db(), Arc::new(keys)); - TestServer::new(router).unwrap() -} - -#[tokio::test] -async fn in_memory_storage_http_smoke_test() { - let server = test_server(); - - let insert_response = server - .post("/points") - .add_header("api-key", API_KEY) - .json(&json!({ - "vector": [1.0, 0.0, 0.0], - "payload": { - "content_type": "Text", - "content": "smoke-test" - } - })) - .await; - insert_response.assert_status(StatusCode::CREATED); - let insert_body: serde_json::Value = insert_response.json(); - let point_id = insert_body["point_id"].as_str().unwrap(); - - let get_response = server - .get(&format!("/points/{point_id}")) - .add_header("api-key", API_KEY) - .await; - get_response.assert_status_ok(); - let point_body: serde_json::Value = get_response.json(); - assert_eq!(point_body["payload"]["content"], "smoke-test"); - assert_eq!(point_body["vector"], json!([1.0, 0.0, 0.0])); - - let search_response = server - .post("/points/search") - .add_header("api-key", API_KEY) - .json(&json!({ - "vector": [1.0, 0.0, 0.0], - "similarity": "Cosine", - "limit": 1 - })) - .await; - search_response.assert_status_ok(); - let search_body: serde_json::Value = search_response.json(); - assert_eq!(search_body["results"], json!([point_id])); - - let delete_response = server - .delete(&format!("/points/{point_id}")) - .add_header("api-key", API_KEY) - .await; - delete_response.assert_status(StatusCode::NO_CONTENT); - - let missing_response = server - .get(&format!("/points/{point_id}")) - .add_header("api-key", API_KEY) - .await; - missing_response.assert_status(StatusCode::NOT_FOUND); -} - -#[tokio::test] -async fn public_routes_require_no_key() { - let server = test_server(); - server.get("/").await.assert_status_ok(); - server.get("/health").await.assert_status_ok(); -} - -#[tokio::test] -async fn protected_routes_reject_missing_key() { - let server = test_server(); - server - .get("/points/00000000-0000-0000-0000-000000000000") - .await - .assert_status(StatusCode::UNAUTHORIZED); - server - .post("/points") - .json(&json!({"vector": [1.0, 0.0, 0.0], "payload": {"content_type": "Text", "content": "x"}})) - .await - .assert_status(StatusCode::UNAUTHORIZED); -} - -#[tokio::test] -async fn protected_routes_reject_wrong_key() { - let server = test_server(); - server - .get("/points/00000000-0000-0000-0000-000000000000") - .add_header("api-key", "not-the-right-key") - .await - .assert_status(StatusCode::UNAUTHORIZED); -} - -#[tokio::test] -async fn readonly_key_can_read_but_not_write() { - let server = test_server(); - - let insert_response = server - .post("/points") - .add_header("api-key", API_KEY) - .json(&json!({"vector": [1.0, 0.0, 0.0], "payload": {"content_type": "Text", "content": "x"}})) - .await; - let point_id = insert_response.json::()["point_id"] - .as_str() - .unwrap() - .to_string(); - - server - .get(&format!("/points/{point_id}")) - .add_header("api-key", READONLY_API_KEY) - .await - .assert_status_ok(); - - server - .post("/points/search") - .add_header("api-key", READONLY_API_KEY) - .json(&json!({"vector": [1.0, 0.0, 0.0], "similarity": "Cosine", "limit": 1})) - .await - .assert_status_ok(); - - server - .delete(&format!("/points/{point_id}")) - .add_header("api-key", READONLY_API_KEY) - .await - .assert_status(StatusCode::FORBIDDEN); - - server - .post("/points") - .add_header("api-key", READONLY_API_KEY) - .json(&json!({"vector": [1.0, 0.0, 0.0], "payload": {"content_type": "Text", "content": "y"}})) - .await - .assert_status(StatusCode::FORBIDDEN); -} diff --git a/crates/index/src/flat/constants.rs b/crates/index/src/flat/constants.rs deleted file mode 100644 index 8df4ff2..0000000 --- a/crates/index/src/flat/constants.rs +++ /dev/null @@ -1,3 +0,0 @@ -use defs::Magic; - -pub const FLAT_MAGIC_BYTES: Magic = [0x00, 0x00, 0x00, 0x01]; diff --git a/crates/index/src/flat/mod.rs b/crates/index/src/flat/mod.rs index 81015b5..5e3f726 100644 --- a/crates/index/src/flat/mod.rs +++ b/crates/index/src/flat/mod.rs @@ -1,8 +1,9 @@ -pub mod constants; +use defs::Magic; + pub mod index; mod serialize; #[cfg(test)] mod tests; -pub use constants::FLAT_MAGIC_BYTES; +pub const FLAT_MAGIC_BYTES: Magic = [0x00, 0x00, 0x00, 0x01]; diff --git a/crates/index/src/hnsw/constants.rs b/crates/index/src/hnsw/constants.rs deleted file mode 100644 index f6877fa..0000000 --- a/crates/index/src/hnsw/constants.rs +++ /dev/null @@ -1,3 +0,0 @@ -use defs::Magic; - -pub const HNSW_MAGIC_BYTES: Magic = [0x02, 0x01, 0x03, 0x00]; diff --git a/crates/index/src/hnsw/mod.rs b/crates/index/src/hnsw/mod.rs index c8f306a..129985a 100644 --- a/crates/index/src/hnsw/mod.rs +++ b/crates/index/src/hnsw/mod.rs @@ -1,13 +1,14 @@ // Referenced from HNSW (Malkov & Yashunin, 2018) // https://arxiv.org/abs/1603.09320 -pub mod constants; pub mod index; pub mod search; pub mod serialize; pub mod types; -pub use constants::HNSW_MAGIC_BYTES; +use defs::Magic; pub use index::{HnswConfig, HnswIndex}; +pub const HNSW_MAGIC_BYTES: Magic = [0x02, 0x01, 0x03, 0x00]; + #[cfg(test)] mod tests; diff --git a/crates/index/src/kd_tree/constants.rs b/crates/index/src/kd_tree/constants.rs deleted file mode 100644 index b80fdf3..0000000 --- a/crates/index/src/kd_tree/constants.rs +++ /dev/null @@ -1,8 +0,0 @@ -use defs::Magic; - -pub const KD_TREE_MAGIC_BYTES: Magic = [0x00, 0x01, 0x02, 0x00]; - -pub(super) const NODE_MARKER_BYTE: u8 = 1u8; -pub(super) const SKIP_MARKER_BYTE: u8 = 0u8; - -pub(super) const DELETED_MASK: u8 = 2u8; diff --git a/crates/index/src/kd_tree/mod.rs b/crates/index/src/kd_tree/mod.rs index ae4fdbb..61233f4 100644 --- a/crates/index/src/kd_tree/mod.rs +++ b/crates/index/src/kd_tree/mod.rs @@ -1,4 +1,5 @@ -pub mod constants; +use defs::Magic; + pub mod helpers; pub mod index; mod serialize; @@ -7,6 +8,6 @@ pub mod types; #[cfg(test)] mod tests; -pub use constants::KD_TREE_MAGIC_BYTES; +pub const KD_TREE_MAGIC_BYTES: Magic = [0x00, 0x01, 0x02, 0x00]; pub use index::{KDTree, KDTreeConfig}; diff --git a/crates/index/src/kd_tree/serialize.rs b/crates/index/src/kd_tree/serialize.rs index 7831fc7..397d629 100644 --- a/crates/index/src/kd_tree/serialize.rs +++ b/crates/index/src/kd_tree/serialize.rs @@ -2,7 +2,6 @@ use std::collections::HashSet; use std::io::{Cursor, Read, Write}; use super::KD_TREE_MAGIC_BYTES; -use super::constants::{DELETED_MASK, NODE_MARKER_BYTE, SKIP_MARKER_BYTE}; use super::index::KDTree; use super::types::KDTreeNode; use crate::{IndexSnapshot, IndexType, SerializableIndex}; @@ -58,6 +57,11 @@ impl SerializableIndex for KDTree { } } +const NODE_MARKER_BYTE: u8 = 1u8; +const SKIP_MARKER_BYTE: u8 = 0u8; + +const DELETED_MASK: u8 = 2u8; + impl KDTree { pub fn deserialize( IndexSnapshot { diff --git a/crates/server/src/config.rs b/crates/server/src/config.rs index 7360b53..dfcd0f4 100644 --- a/crates/server/src/config.rs +++ b/crates/server/src/config.rs @@ -1,42 +1,61 @@ use api::DbConfig; -use defs::{ApiKeyEntry, ApiKeyStore, Similarity}; +use defs::Similarity; use dotenv::dotenv; use index::{IndexType, hnsw::HnswConfig, kd_tree::KDTreeConfig}; +use snafu::prelude::*; use std::env; use std::fs; use std::net::SocketAddr; use std::path::PathBuf; -use std::sync::Arc; use storage::StorageType; use tracing::{Level, event}; -use crate::constants::{ - DEFAULT_GRPC_PORT, DEFAULT_HNSW_EF, DEFAULT_HNSW_EF_CONSTRUCTION, DEFAULT_HNSW_M, - DEFAULT_HNSW_MAX_LAYER, DEFAULT_HTTP_PORT, DEFAULT_KD_TREE_BALANCE_THRESHOLD, - DEFAULT_KD_TREE_DELETE_REBUILD_RATIO, ENV_DATA_PATH, ENV_DIMENSION, ENV_DISABLE_HTTP, - ENV_GRPC_HOST, ENV_GRPC_PORT, ENV_HNSW_EF, ENV_HNSW_EF_CONSTRUCTION, ENV_HNSW_M, ENV_HNSW_M0, - ENV_HNSW_MAX_LAYER, ENV_HTTP_HOST, ENV_HTTP_PORT, ENV_INDEX_TYPE, - ENV_KD_TREE_BALANCE_THRESHOLD, ENV_KD_TREE_DELETE_REBUILD_RATIO, ENV_KEYS_FILE, ENV_LOGGING, - ENV_SIMILARITY, ENV_STORAGE_TYPE, -}; -pub use crate::error::{ConfigError, Result}; +const DEFAULT_HTTP_PORT: &str = "3000"; +const DEFAULT_GRPC_PORT: &str = "50051"; +const DEFAULT_HNSW_M: usize = 16; +const DEFAULT_HNSW_MAX_LAYER: usize = 16; +const DEFAULT_HNSW_EF_CONSTRUCTION: usize = 200; +const DEFAULT_HNSW_EF: usize = 100; +const DEFAULT_KD_TREE_BALANCE_THRESHOLD: f32 = 0.7; +const DEFAULT_KD_TREE_DELETE_REBUILD_RATIO: f32 = 0.25; #[derive(Debug)] pub struct ServerConfig { pub http_addr: SocketAddr, pub grpc_addr: SocketAddr, - pub api_keys: Arc, + pub grpc_root_password: String, pub db_config: DbConfig, pub logging: bool, pub disable_http: bool, } +#[derive(Debug, Snafu)] +#[snafu(visibility(pub))] +pub enum ConfigError { + #[snafu(display("Missing required environment variable: {var}"))] + MissingRequiredEnvVar { var: String }, + + #[snafu(display("Invalid dimension value"))] + InvalidDimension, + + #[snafu(display("Invalid data path: {source}"))] + InvalidDataPath { source: std::io::Error }, + + #[snafu(display("IO error: {source}"))] + IoError { source: std::io::Error }, + + #[snafu(display("Invalid address: {addr}"))] + InvalidAddress { addr: String }, +} + +pub type Result = std::result::Result; + impl ServerConfig { pub fn load_config() -> Result { dotenv().ok(); // HTTP server configuration - let http_host = env::var(ENV_HTTP_HOST) + let http_host = env::var("HTTP_HOST") .inspect_err(|_| { event!( Level::WARN, @@ -45,7 +64,7 @@ impl ServerConfig { }) .unwrap_or_else(|_| "127.0.0.1".to_string()); - let http_port = env::var(ENV_HTTP_PORT) + let http_port = env::var("HTTP_PORT") .inspect_err(|_| { event!( Level::WARN, @@ -63,7 +82,7 @@ impl ServerConfig { })?; // gRPC server configuration - let grpc_host = env::var(ENV_GRPC_HOST) + let grpc_host = env::var("GRPC_HOST") .inspect_err(|_| { event!( Level::WARN, @@ -72,7 +91,7 @@ impl ServerConfig { }) .unwrap_or_else(|_| "127.0.0.1".to_string()); - let grpc_port = env::var(ENV_GRPC_PORT) + let grpc_port = env::var("GRPC_PORT") .inspect_err(|_| { event!( Level::WARN, @@ -89,14 +108,14 @@ impl ServerConfig { addr: format!("{}:{}", grpc_host, grpc_port), })?; - let keys_file_path = - env::var(ENV_KEYS_FILE).map_err(|_| ConfigError::MissingRequiredEnvVar { - var: ENV_KEYS_FILE.to_string(), + // gRPC root password (required) + let grpc_root_password = + env::var("GRPC_ROOT_PASSWORD").map_err(|_| ConfigError::MissingRequiredEnvVar { + var: "GRPC_ROOT_PASSWORD".to_string(), })?; - let api_keys = Arc::new(load_keys_file(&keys_file_path)?); // Storage type - let storage_type_str = env::var(ENV_STORAGE_TYPE) + let storage_type_str = env::var("STORAGE_TYPE") .inspect_err(|_| { event!( Level::WARN, @@ -112,7 +131,7 @@ impl ServerConfig { }; // Index type - let index_type_str = env::var(ENV_INDEX_TYPE) + let index_type_str = env::var("INDEX_TYPE") .inspect_err(|_| { event!(Level::WARN, "INDEX_TYPE not defined, defaulting to flat"); }) @@ -127,15 +146,15 @@ impl ServerConfig { }; // Dimension (required) - let dimension: usize = env::var(ENV_DIMENSION) + let dimension: usize = env::var("DIMENSION") .map_err(|_| ConfigError::MissingRequiredEnvVar { - var: ENV_DIMENSION.to_string(), + var: "DIMENSION".to_string(), })? .parse() .map_err(|_| ConfigError::InvalidDimension)?; // Data path - let data_path: PathBuf = if let Ok(data_path_str) = env::var(ENV_DATA_PATH) { + let data_path: PathBuf = if let Ok(data_path_str) = env::var("DATA_PATH") { let path = PathBuf::from(data_path_str); fs::create_dir_all(&path).map_err(|e| ConfigError::InvalidDataPath { source: e })?; path @@ -151,19 +170,19 @@ impl ServerConfig { }; // Logging - let logging = env::var(ENV_LOGGING) + let logging = env::var("LOGGING") .unwrap_or_else(|_| "true".to_string()) .parse() .unwrap_or(true); // HTTP server disable flag (default to false, set to true to run only gRPC) - let disable_http = env::var(ENV_DISABLE_HTTP) + let disable_http = env::var("DISABLE_HTTP") .unwrap_or_else(|_| "false".to_string()) .parse() .unwrap_or(false); // Similarity metric - let similarity: Similarity = match env::var(ENV_SIMILARITY) { + let similarity: Similarity = match env::var("SIMILARITY") { Ok(val) => match val.to_lowercase().as_str() { "cosine" => Similarity::Cosine, "euclidean" => Similarity::Euclidean, @@ -184,21 +203,21 @@ impl ServerConfig { } }; - let hnsw_m = load_usize_env(ENV_HNSW_M, DEFAULT_HNSW_M); + let hnsw_m = load_usize_env("HNSW_M", DEFAULT_HNSW_M); let hnsw_config = HnswConfig { max_connections: hnsw_m, - max_connections_0: load_usize_env(ENV_HNSW_M0, 2 * hnsw_m), - max_layer: load_usize_env(ENV_HNSW_MAX_LAYER, DEFAULT_HNSW_MAX_LAYER), - ef_construction: load_usize_env(ENV_HNSW_EF_CONSTRUCTION, DEFAULT_HNSW_EF_CONSTRUCTION), - ef: load_usize_env(ENV_HNSW_EF, DEFAULT_HNSW_EF), + max_connections_0: load_usize_env("HNSW_M0", 2 * hnsw_m), + max_layer: load_usize_env("HNSW_MAX_LAYER", DEFAULT_HNSW_MAX_LAYER), + ef_construction: load_usize_env("HNSW_EF_CONSTRUCTION", DEFAULT_HNSW_EF_CONSTRUCTION), + ef: load_usize_env("HNSW_EF", DEFAULT_HNSW_EF), }; let kd_tree_config = KDTreeConfig { balance_threshold: load_f32_env( - ENV_KD_TREE_BALANCE_THRESHOLD, + "KD_TREE_BALANCE_THRESHOLD", DEFAULT_KD_TREE_BALANCE_THRESHOLD, ), delete_rebuild_ratio: load_f32_env( - ENV_KD_TREE_DELETE_REBUILD_RATIO, + "KD_TREE_DELETE_REBUILD_RATIO", DEFAULT_KD_TREE_DELETE_REBUILD_RATIO, ), }; @@ -216,7 +235,7 @@ impl ServerConfig { Ok(ServerConfig { http_addr, grpc_addr, - api_keys, + grpc_root_password, db_config, logging, disable_http, @@ -224,45 +243,6 @@ impl ServerConfig { } } -#[derive(serde::Deserialize)] -struct KeysFile { - keys: Vec, -} - -fn load_keys_file(path: &str) -> Result { - let contents = fs::read_to_string(path).map_err(|source| ConfigError::KeysFileRead { - path: path.to_string(), - source, - })?; - let parsed: KeysFile = - serde_json::from_str(&contents).map_err(|source| ConfigError::KeysFileParse { - path: path.to_string(), - source, - })?; - - if parsed.keys.is_empty() { - return Err(ConfigError::KeysFileEmpty { - path: path.to_string(), - }); - } - - if parsed.keys.iter().any(|entry| entry.key.is_empty()) { - return Err(ConfigError::EmptyApiKey { - path: path.to_string(), - }); - } - - let store = ApiKeyStore::new(parsed.keys); - if let Some(key) = store.duplicate_key() { - return Err(ConfigError::DuplicateApiKey { - path: path.to_string(), - key: key.to_string(), - }); - } - - Ok(store) -} - fn load_usize_env(name: &str, default: usize) -> usize { match env::var(name) { Ok(value) => value.parse().unwrap_or_else(|_| { diff --git a/crates/server/src/constants.rs b/crates/server/src/constants.rs deleted file mode 100644 index aca8e8b..0000000 --- a/crates/server/src/constants.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub const ENV_HTTP_HOST: &str = "HTTP_HOST"; -pub const ENV_HTTP_PORT: &str = "HTTP_PORT"; -pub const ENV_GRPC_HOST: &str = "GRPC_HOST"; -pub const ENV_GRPC_PORT: &str = "GRPC_PORT"; -pub const ENV_KEYS_FILE: &str = "VORTEXDB_KEYS_FILE"; -pub const ENV_STORAGE_TYPE: &str = "STORAGE_TYPE"; -pub const ENV_INDEX_TYPE: &str = "INDEX_TYPE"; -pub const ENV_DIMENSION: &str = "DIMENSION"; -pub const ENV_DATA_PATH: &str = "DATA_PATH"; -pub const ENV_LOGGING: &str = "LOGGING"; -pub const ENV_DISABLE_HTTP: &str = "DISABLE_HTTP"; -pub const ENV_SIMILARITY: &str = "SIMILARITY"; -pub const ENV_HNSW_M: &str = "HNSW_M"; -pub const ENV_HNSW_M0: &str = "HNSW_M0"; -pub const ENV_HNSW_MAX_LAYER: &str = "HNSW_MAX_LAYER"; -pub const ENV_HNSW_EF_CONSTRUCTION: &str = "HNSW_EF_CONSTRUCTION"; -pub const ENV_HNSW_EF: &str = "HNSW_EF"; -pub const ENV_KD_TREE_BALANCE_THRESHOLD: &str = "KD_TREE_BALANCE_THRESHOLD"; -pub const ENV_KD_TREE_DELETE_REBUILD_RATIO: &str = "KD_TREE_DELETE_REBUILD_RATIO"; - -pub const DEFAULT_HTTP_PORT: &str = "3000"; -pub const DEFAULT_GRPC_PORT: &str = "50051"; -pub const DEFAULT_HNSW_M: usize = 16; -pub const DEFAULT_HNSW_MAX_LAYER: usize = 16; -pub const DEFAULT_HNSW_EF_CONSTRUCTION: usize = 200; -pub const DEFAULT_HNSW_EF: usize = 100; -pub const DEFAULT_KD_TREE_BALANCE_THRESHOLD: f32 = 0.7; -pub const DEFAULT_KD_TREE_DELETE_REBUILD_RATIO: f32 = 0.25; diff --git a/crates/server/src/error.rs b/crates/server/src/error.rs deleted file mode 100644 index 5be8b5e..0000000 --- a/crates/server/src/error.rs +++ /dev/null @@ -1,43 +0,0 @@ -use snafu::prelude::*; - -#[derive(Debug, Snafu)] -#[snafu(visibility(pub))] -pub enum ConfigError { - #[snafu(display("Missing required environment variable: {var}"))] - MissingRequiredEnvVar { var: String }, - - #[snafu(display("Invalid dimension value"))] - InvalidDimension, - - #[snafu(display("Invalid data path: {source}"))] - InvalidDataPath { source: std::io::Error }, - - #[snafu(display("IO error: {source}"))] - IoError { source: std::io::Error }, - - #[snafu(display("Invalid address: {addr}"))] - InvalidAddress { addr: String }, - - #[snafu(display("Failed to read keys file {path}: {source}"))] - KeysFileRead { - path: String, - source: std::io::Error, - }, - - #[snafu(display("Failed to parse keys file {path}: {source}"))] - KeysFileParse { - path: String, - source: serde_json::Error, - }, - - #[snafu(display("Keys file {path} contains no keys"))] - KeysFileEmpty { path: String }, - - #[snafu(display("Keys file {path} contains an entry with an empty key value"))] - EmptyApiKey { path: String }, - - #[snafu(display("Keys file {path} has two entries with the same key value: {key}"))] - DuplicateApiKey { path: String, key: String }, -} - -pub type Result = std::result::Result; diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 3df8119..e10ba39 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -1,6 +1,4 @@ mod config; -mod constants; -mod error; use std::sync::Arc; @@ -36,10 +34,7 @@ async fn main() -> Result<(), BoxError> { let http_handle = if !config.disable_http { let db = Arc::clone(&shared_db); let addr = config.http_addr; - let keys = Arc::clone(&config.api_keys); - Some(tokio::spawn(async move { - run_http_server(db, addr, keys).await - })) + Some(tokio::spawn(async move { run_http_server(db, addr).await })) } else { info!("HTTP server is disabled"); None @@ -49,9 +44,9 @@ async fn main() -> Result<(), BoxError> { let grpc_handle = { let db = Arc::clone(&shared_db); let addr = config.grpc_addr; - let keys = Arc::clone(&config.api_keys); + let password = config.grpc_root_password; let logging = config.logging; - tokio::spawn(async move { run_grpc_server(db, addr, keys, logging).await }) + tokio::spawn(async move { run_grpc_server(db, addr, password, logging).await }) }; if let Some(http) = http_handle { diff --git a/crates/snapshot/src/constants.rs b/crates/snapshot/src/constants.rs index 2cd8e2e..3dd46d3 100644 --- a/crates/snapshot/src/constants.rs +++ b/crates/snapshot/src/constants.rs @@ -3,5 +3,3 @@ use semver::Version; pub const SNAPSHOT_PARSER_VER: Version = Version::new(0, 1, 0); pub const SMALL_ID_LEN: usize = 8; pub const MANIFEST_FILE: &str = "manifest.json"; -pub const FILENAME_METADATA_SEPARATOR: &str = "-x"; -pub const SNAPSHOT_FILE_EXTENSION: &str = ".tar.gz"; diff --git a/crates/snapshot/src/lib.rs b/crates/snapshot/src/lib.rs index 496a9a7..7324a75 100644 --- a/crates/snapshot/src/lib.rs +++ b/crates/snapshot/src/lib.rs @@ -6,7 +6,7 @@ pub mod registry; mod util; use crate::{ - constants::{MANIFEST_FILE, SNAPSHOT_FILE_EXTENSION, SNAPSHOT_PARSER_VER}, + constants::{MANIFEST_FILE, SNAPSHOT_PARSER_VER}, manifest::Manifest, util::{compress_archive, save_index_metadata, save_topology}, }; @@ -131,14 +131,13 @@ impl Snapshot { .map_err(|e| DbError::SnapshotError(e.to_string()))?; let tar_filename = format!( - "{}{}", + "{}.tar.gz", metadata::Metadata::new( self.id, self.date, index_metadata_path.clone(), constants::SNAPSHOT_PARSER_VER - ), - SNAPSHOT_FILE_EXTENSION + ) ); let tar_gz_path = dir_path.join(tar_filename); diff --git a/crates/snapshot/src/metadata.rs b/crates/snapshot/src/metadata.rs index 457e024..cd73185 100644 --- a/crates/snapshot/src/metadata.rs +++ b/crates/snapshot/src/metadata.rs @@ -1,4 +1,4 @@ -use crate::constants::{FILENAME_METADATA_SEPARATOR, SMALL_ID_LEN, SNAPSHOT_FILE_EXTENSION}; +use crate::constants::SMALL_ID_LEN; use chrono::DateTime; use chrono::Local; use defs::DbError; @@ -18,6 +18,8 @@ pub struct Metadata { pub sem_ver: Version, } +const FILENAME_METADATA_SEPARATOR: &str = "-x"; + impl Metadata { pub fn new(id: Uuid, date: SystemTime, path: PathBuf, sem_ver: Version) -> Self { Metadata { @@ -39,10 +41,10 @@ impl Metadata { .ok_or(DbError::SnapshotError( "Invalid UTF-8 in filename".to_string(), ))? - .strip_suffix(SNAPSHOT_FILE_EXTENSION) - .ok_or(DbError::SnapshotError(format!( - "Snapshot filename doesnt end with {SNAPSHOT_FILE_EXTENSION}" - )))?; + .strip_suffix(".tar.gz") + .ok_or(DbError::SnapshotError( + "Snapshot filename doesnt end with .tar.gz".to_string(), + ))?; let parts = filename .split(FILENAME_METADATA_SEPARATOR) diff --git a/crates/snapshot/src/registry/constants.rs b/crates/snapshot/src/registry/constants.rs index ecfb9cf..9138454 100644 --- a/crates/snapshot/src/registry/constants.rs +++ b/crates/snapshot/src/registry/constants.rs @@ -1,3 +1 @@ pub const LOCAL_REGISTRY_LOCKFILE: &str = "LOCKFILE"; -pub const INFINITY_LIMIT: usize = 100000; -pub const NO_OFFSET: usize = 0; diff --git a/crates/snapshot/src/registry/mod.rs b/crates/snapshot/src/registry/mod.rs index c532007..6513d97 100644 --- a/crates/snapshot/src/registry/mod.rs +++ b/crates/snapshot/src/registry/mod.rs @@ -7,7 +7,8 @@ use crate::{VectorDbRestore, metadata::Metadata}; pub type SnapshotMetaPage = Vec; -pub use constants::{INFINITY_LIMIT, NO_OFFSET}; +pub const INFINITY_LIMIT: usize = 100000; +pub const NO_OFFSET: usize = 0; pub trait SnapshotRegistry: Send + Sync { fn add_snapshot(&mut self, snapshot_path: &Path) -> Result; diff --git a/crates/storage/src/in_memory.rs b/crates/storage/src/in_memory.rs index 3ee86fb..1e8ca60 100644 --- a/crates/storage/src/in_memory.rs +++ b/crates/storage/src/in_memory.rs @@ -10,11 +10,10 @@ use std::ops::Bound::{Excluded, Unbounded}; use std::path::Path; use std::sync::RwLock; -mod constants; -pub use constants::INMEMORY_CHECKPOINT_FILENAME_MARKER; -use constants::{ - INMEMORY_CHECKPOINT_EXTENSION, INMEMORY_CHECKPOINT_MAGIC, INMEMORY_CHECKPOINT_VERSION, -}; +pub const INMEMORY_CHECKPOINT_FILENAME_MARKER: &str = "inmemory"; +const INMEMORY_CHECKPOINT_EXTENSION: &str = "bin"; +const INMEMORY_CHECKPOINT_MAGIC: &[u8; 8] = b"VDBIMCP\0"; +const INMEMORY_CHECKPOINT_VERSION: u16 = 1; pub struct MemoryStorage { points: RwLock>, @@ -252,4 +251,186 @@ impl StorageEngine for MemoryStorage { } #[cfg(test)] -mod tests; +mod tests { + use super::*; + use defs::ContentType; + use std::io::{Read, Write}; + use tempfile::{TempDir, tempdir}; + use uuid::Uuid; + + fn create_test_storage() -> MemoryStorage { + MemoryStorage::new() + } + + fn test_payload(content: &str) -> Payload { + Payload { + content_type: ContentType::Text, + content: content.to_string(), + } + } + + #[test] + fn test_insert_and_get_vector() { + let storage = create_test_storage(); + let id = Uuid::new_v4(); + let vector = Some(vec![0.1, 0.2, 0.3]); + let payload = Some(test_payload("Test")); + + storage.insert_point(id, vector.clone(), payload).unwrap(); + + assert_eq!(storage.get_vector(id).unwrap(), vector); + } + + #[test] + fn test_insert_and_get_payload() { + let storage = create_test_storage(); + let id = Uuid::new_v4(); + let payload = Some(test_payload("Test")); + + storage.insert_point(id, None, payload.clone()).unwrap(); + + assert_eq!(storage.get_payload(id).unwrap(), payload); + } + + #[test] + fn test_contains_and_delete_point() { + let storage = create_test_storage(); + let id = Uuid::new_v4(); + + assert!(!storage.contains_point(id).unwrap()); + + storage + .insert_point(id, Some(vec![0.4, 0.5, 0.6]), Some(test_payload("Test"))) + .unwrap(); + assert!(storage.contains_point(id).unwrap()); + + storage.delete_point(id).unwrap(); + assert!(!storage.contains_point(id).unwrap()); + assert_eq!(storage.get_vector(id).unwrap(), None); + assert_eq!(storage.get_payload(id).unwrap(), None); + } + + #[test] + fn test_list_vectors_respects_offset_limit_and_skips_payload_only_points() { + let storage = create_test_storage(); + let ids = [ + Uuid::from_u128(1), + Uuid::from_u128(2), + Uuid::from_u128(3), + Uuid::from_u128(4), + ]; + + storage + .insert_point(ids[0], Some(vec![1.0, 1.1]), Some(test_payload("one"))) + .unwrap(); + storage + .insert_point(ids[1], None, Some(test_payload("payload-only"))) + .unwrap(); + storage + .insert_point(ids[2], Some(vec![3.0, 3.1]), Some(test_payload("three"))) + .unwrap(); + storage + .insert_point(ids[3], Some(vec![4.0, 4.1]), Some(test_payload("four"))) + .unwrap(); + + let (first_page, next_offset) = storage.list_vectors(Uuid::nil(), 2).unwrap().unwrap(); + assert_eq!( + first_page, + vec![(ids[0], vec![1.0, 1.1]), (ids[2], vec![3.0, 3.1])] + ); + assert_eq!(next_offset, ids[2]); + + let (second_page, next_offset) = storage.list_vectors(next_offset, 2).unwrap().unwrap(); + assert_eq!(second_page, vec![(ids[3], vec![4.0, 4.1])]); + assert_eq!(next_offset, ids[3]); + } + + #[test] + fn test_list_vectors_with_zero_limit_returns_none() { + let storage = create_test_storage(); + + assert_eq!(storage.list_vectors(Uuid::nil(), 0).unwrap(), None); + } + + #[test] + fn test_create_and_restore_checkpoint() { + let mut storage = create_test_storage(); + let temp_dir: TempDir = tempdir().unwrap(); + let id_before_checkpoint = Uuid::new_v4(); + let id_after_checkpoint = Uuid::new_v4(); + + storage + .insert_point( + id_before_checkpoint, + Some(vec![0.1, 0.2, 0.3]), + Some(test_payload("before")), + ) + .unwrap(); + let checkpoint = storage.checkpoint_at(temp_dir.path()).unwrap(); + + storage + .insert_point( + id_after_checkpoint, + Some(vec![0.4, 0.5, 0.6]), + Some(test_payload("after")), + ) + .unwrap(); + + storage.restore_checkpoint(&checkpoint).unwrap(); + + assert!(storage.contains_point(id_before_checkpoint).unwrap()); + assert!(!storage.contains_point(id_after_checkpoint).unwrap()); + assert_eq!( + storage.get_payload(id_before_checkpoint).unwrap(), + Some(test_payload("before")) + ); + } + + #[test] + fn test_checkpoint_writes_header() { + let storage = create_test_storage(); + let temp_dir = tempdir().unwrap(); + let id = Uuid::new_v4(); + + storage + .insert_point(id, Some(vec![0.1, 0.2, 0.3]), Some(test_payload("point"))) + .unwrap(); + let checkpoint = storage.checkpoint_at(temp_dir.path()).unwrap(); + + let mut file = File::open(checkpoint.path).unwrap(); + let mut magic = [0u8; INMEMORY_CHECKPOINT_MAGIC.len()]; + file.read_exact(&mut magic).unwrap(); + assert_eq!(&magic, INMEMORY_CHECKPOINT_MAGIC); + + let mut version_bytes = [0u8; size_of::()]; + file.read_exact(&mut version_bytes).unwrap(); + assert_eq!( + u16::from_le_bytes(version_bytes), + INMEMORY_CHECKPOINT_VERSION + ); + + let mut count_bytes = [0u8; size_of::()]; + file.read_exact(&mut count_bytes).unwrap(); + assert_eq!(u64::from_le_bytes(count_bytes), 1); + } + + #[test] + fn test_restore_rejects_invalid_checkpoint_magic() { + let mut storage = create_test_storage(); + let temp_dir = tempdir().unwrap(); + let checkpoint_path = temp_dir.path().join("inmemory-invalid.bin"); + let mut file = File::create(&checkpoint_path).unwrap(); + file.write_all(b"BADMAGIC").unwrap(); + file.write_all(&INMEMORY_CHECKPOINT_VERSION.to_le_bytes()) + .unwrap(); + file.write_all(&0u64.to_le_bytes()).unwrap(); + + let checkpoint = StorageCheckpoint { + path: checkpoint_path, + storage_type: StorageType::InMemory, + }; + + let error = storage.restore_checkpoint(&checkpoint).unwrap_err(); + assert!(matches!(error, StorageError::InMemoryCheckpoint { .. })); + } +} diff --git a/crates/storage/src/in_memory/constants.rs b/crates/storage/src/in_memory/constants.rs deleted file mode 100644 index 5974b25..0000000 --- a/crates/storage/src/in_memory/constants.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub const INMEMORY_CHECKPOINT_FILENAME_MARKER: &str = "inmemory"; -pub const INMEMORY_CHECKPOINT_EXTENSION: &str = "bin"; -pub const INMEMORY_CHECKPOINT_MAGIC: &[u8; 8] = b"VDBIMCP\0"; -pub const INMEMORY_CHECKPOINT_VERSION: u16 = 1; diff --git a/crates/storage/src/in_memory/tests.rs b/crates/storage/src/in_memory/tests.rs deleted file mode 100644 index 9d8510e..0000000 --- a/crates/storage/src/in_memory/tests.rs +++ /dev/null @@ -1,182 +0,0 @@ -use super::*; - -use defs::ContentType; -use std::io::{Read, Write}; -use tempfile::{TempDir, tempdir}; -use uuid::Uuid; - -fn create_test_storage() -> MemoryStorage { - MemoryStorage::new() -} - -fn test_payload(content: &str) -> Payload { - Payload { - content_type: ContentType::Text, - content: content.to_string(), - } -} - -#[test] -fn test_insert_and_get_vector() { - let storage = create_test_storage(); - let id = Uuid::new_v4(); - let vector = Some(vec![0.1, 0.2, 0.3]); - let payload = Some(test_payload("Test")); - - storage.insert_point(id, vector.clone(), payload).unwrap(); - - assert_eq!(storage.get_vector(id).unwrap(), vector); -} - -#[test] -fn test_insert_and_get_payload() { - let storage = create_test_storage(); - let id = Uuid::new_v4(); - let payload = Some(test_payload("Test")); - - storage.insert_point(id, None, payload.clone()).unwrap(); - - assert_eq!(storage.get_payload(id).unwrap(), payload); -} - -#[test] -fn test_contains_and_delete_point() { - let storage = create_test_storage(); - let id = Uuid::new_v4(); - - assert!(!storage.contains_point(id).unwrap()); - - storage - .insert_point(id, Some(vec![0.4, 0.5, 0.6]), Some(test_payload("Test"))) - .unwrap(); - assert!(storage.contains_point(id).unwrap()); - - storage.delete_point(id).unwrap(); - assert!(!storage.contains_point(id).unwrap()); - assert_eq!(storage.get_vector(id).unwrap(), None); - assert_eq!(storage.get_payload(id).unwrap(), None); -} - -#[test] -fn test_list_vectors_respects_offset_limit_and_skips_payload_only_points() { - let storage = create_test_storage(); - let ids = [ - Uuid::from_u128(1), - Uuid::from_u128(2), - Uuid::from_u128(3), - Uuid::from_u128(4), - ]; - - storage - .insert_point(ids[0], Some(vec![1.0, 1.1]), Some(test_payload("one"))) - .unwrap(); - storage - .insert_point(ids[1], None, Some(test_payload("payload-only"))) - .unwrap(); - storage - .insert_point(ids[2], Some(vec![3.0, 3.1]), Some(test_payload("three"))) - .unwrap(); - storage - .insert_point(ids[3], Some(vec![4.0, 4.1]), Some(test_payload("four"))) - .unwrap(); - - let (first_page, next_offset) = storage.list_vectors(Uuid::nil(), 2).unwrap().unwrap(); - assert_eq!( - first_page, - vec![(ids[0], vec![1.0, 1.1]), (ids[2], vec![3.0, 3.1])] - ); - assert_eq!(next_offset, ids[2]); - - let (second_page, next_offset) = storage.list_vectors(next_offset, 2).unwrap().unwrap(); - assert_eq!(second_page, vec![(ids[3], vec![4.0, 4.1])]); - assert_eq!(next_offset, ids[3]); -} - -#[test] -fn test_list_vectors_with_zero_limit_returns_none() { - let storage = create_test_storage(); - - assert_eq!(storage.list_vectors(Uuid::nil(), 0).unwrap(), None); -} - -#[test] -fn test_create_and_restore_checkpoint() { - let mut storage = create_test_storage(); - let temp_dir: TempDir = tempdir().unwrap(); - let id_before_checkpoint = Uuid::new_v4(); - let id_after_checkpoint = Uuid::new_v4(); - - storage - .insert_point( - id_before_checkpoint, - Some(vec![0.1, 0.2, 0.3]), - Some(test_payload("before")), - ) - .unwrap(); - let checkpoint = storage.checkpoint_at(temp_dir.path()).unwrap(); - - storage - .insert_point( - id_after_checkpoint, - Some(vec![0.4, 0.5, 0.6]), - Some(test_payload("after")), - ) - .unwrap(); - - storage.restore_checkpoint(&checkpoint).unwrap(); - - assert!(storage.contains_point(id_before_checkpoint).unwrap()); - assert!(!storage.contains_point(id_after_checkpoint).unwrap()); - assert_eq!( - storage.get_payload(id_before_checkpoint).unwrap(), - Some(test_payload("before")) - ); -} - -#[test] -fn test_checkpoint_writes_header() { - let storage = create_test_storage(); - let temp_dir = tempdir().unwrap(); - let id = Uuid::new_v4(); - - storage - .insert_point(id, Some(vec![0.1, 0.2, 0.3]), Some(test_payload("point"))) - .unwrap(); - let checkpoint = storage.checkpoint_at(temp_dir.path()).unwrap(); - - let mut file = File::open(checkpoint.path).unwrap(); - let mut magic = [0u8; INMEMORY_CHECKPOINT_MAGIC.len()]; - file.read_exact(&mut magic).unwrap(); - assert_eq!(&magic, INMEMORY_CHECKPOINT_MAGIC); - - let mut version_bytes = [0u8; size_of::()]; - file.read_exact(&mut version_bytes).unwrap(); - assert_eq!( - u16::from_le_bytes(version_bytes), - INMEMORY_CHECKPOINT_VERSION - ); - - let mut count_bytes = [0u8; size_of::()]; - file.read_exact(&mut count_bytes).unwrap(); - assert_eq!(u64::from_le_bytes(count_bytes), 1); -} - -#[test] -fn test_restore_rejects_invalid_checkpoint_magic() { - let mut storage = create_test_storage(); - let temp_dir = tempdir().unwrap(); - let checkpoint_path = temp_dir.path().join("inmemory-invalid.bin"); - let mut file = File::create(&checkpoint_path).unwrap(); - file.write_all(b"BADMAGIC").unwrap(); - file.write_all(&INMEMORY_CHECKPOINT_VERSION.to_le_bytes()) - .unwrap(); - file.write_all(&0u64.to_le_bytes()).unwrap(); - - let checkpoint = StorageCheckpoint { - path: checkpoint_path, - storage_type: StorageType::InMemory, - }; - - let error = storage.restore_checkpoint(&checkpoint).unwrap_err(); - assert!(matches!(error, StorageError::InMemoryCheckpoint { .. })); -} diff --git a/crates/storage/src/rocks_db.rs b/crates/storage/src/rocks_db.rs index 3fd2828..166aefc 100644 --- a/crates/storage/src/rocks_db.rs +++ b/crates/storage/src/rocks_db.rs @@ -27,9 +27,7 @@ pub enum RocksDBStorageError { RocksDBError(Error), } -mod constants; -use constants::ROCKSDB_CHECKPOINT_EXTENSION; -pub use constants::ROCKSDB_CHECKPOINT_FILENAME_MARKER; +pub const ROCKSDB_CHECKPOINT_FILENAME_MARKER: &str = "rocksdb"; impl RocksDbStorage { // Creates new db or switches to existing db @@ -208,11 +206,11 @@ impl StorageEngine for RocksDbStorage { .flush() .context(RocksDbFlushSnafu)?; + // filename is rocksdb-{uuid}.tar.gz let checkpoint_filename = format!( - "{}-{}.{}", + "{}-{}.tar.gz", ROCKSDB_CHECKPOINT_FILENAME_MARKER, - uuid::Uuid::new_v4(), - ROCKSDB_CHECKPOINT_EXTENSION + uuid::Uuid::new_v4() ); let checkpoint_path = path.join(checkpoint_filename); @@ -278,7 +276,7 @@ impl StorageEngine for RocksDbStorage { .ok_or_else(|| StorageError::RocksDbCheckpointMsg { msg: "Checkpoint filename is not valid UTF-8".to_string(), })?; - if !checkpoint_filename.ends_with(ROCKSDB_CHECKPOINT_EXTENSION) + if !checkpoint_filename.ends_with(".tar.gz") || !checkpoint_filename.starts_with(ROCKSDB_CHECKPOINT_FILENAME_MARKER) { return RocksDbCheckpointMsgSnafu { @@ -324,4 +322,165 @@ impl StorageEngine for RocksDbStorage { } #[cfg(test)] -mod tests; +mod tests { + use super::*; + use defs::ContentType; + use uuid::Uuid; + + use tempfile::{TempDir, tempdir}; + + fn create_test_db() -> (RocksDbStorage, TempDir) { + let temp_dir = tempdir().unwrap(); + + let db = RocksDbStorage::new(temp_dir.path()).expect("Failed to create RocksDB"); + (db, temp_dir) + } + + #[test] + fn test_new_rocksdb_storage() { + let (db, temp_dir) = create_test_db(); + assert_eq!(db.get_current_path(), temp_dir.path()); + } + + #[test] + fn test_insert_and_get_vector() { + let (db, _temp_dir) = create_test_db(); + let id = Uuid::new_v4(); + let vector = Some(vec![0.1, 0.2, 0.3]); + let payload = Some(Payload { + content_type: ContentType::Text, + content: "Test".to_string(), + }); + + assert!(db.insert_point(id, vector.clone(), payload).is_ok()); + let result = db.get_vector(id).unwrap(); + assert_eq!(result, vector); + } + + #[test] + fn test_insert_and_get_payload() { + let (db, _temp_dir) = create_test_db(); + let id = Uuid::new_v4(); + let payload = Some(Payload { + content_type: ContentType::Text, + content: "Test".to_string(), + }); + let vector = None; + + // Move payload into insert_point and recreate expected for comparison + assert!(db.insert_point(id, vector, payload).is_ok()); + let result = db.get_payload(id).unwrap(); + let expected = Some(Payload { + content_type: ContentType::Text, + content: "Test".to_string(), + }); + assert_eq!(result, expected); + } + + #[test] + fn test_contains_point() { + let (db, _temp_dir) = create_test_db(); + let id = Uuid::new_v4(); + let payload = Some(Payload { + content_type: ContentType::Text, + content: "Test".to_string(), + }); + + assert!(!db.contains_point(id).unwrap()); + + let vector = Some(vec![0.4, 0.5, 0.6]); + db.insert_point(id, vector, payload).unwrap(); + + assert!(db.contains_point(id).unwrap()); + } + + #[test] + fn test_delete_point() { + let (db, _temp_dir) = create_test_db(); + let id = Uuid::new_v4(); + let payload = Some(Payload { + content_type: ContentType::Text, + content: "Test".to_string(), + }); + + let vector = vec![0.7, 0.8, 0.9]; + + db.insert_point(id, Some(vector), payload).unwrap(); + + assert!(db.contains_point(id).unwrap()); + + db.delete_point(id).unwrap(); + + assert!(!db.contains_point(id).unwrap()); + assert_eq!(db.get_vector(id).unwrap(), None); + assert_eq!(db.get_payload(id).unwrap(), None); + } + + #[test] + fn test_get_nonexistent_vector() { + let (db, _temp_dir) = create_test_db(); + let id = Uuid::new_v4(); + + assert_eq!(db.get_vector(id).unwrap(), None); + } + + #[test] + fn test_get_nonexistent_payload() { + let (db, _temp_dir) = create_test_db(); + let id = Uuid::new_v4(); + + assert_eq!(db.get_payload(id).unwrap(), None); + } + #[test] + fn test_error_context_preservation() { + // Test that the error chain is preserved + let result = RocksDbStorage::new("/proc/invalid-path"); + + if let Err(err) = result { + // The Display implementation should show both the context and source + let err_string = format!("{}", err); + println!("Full error message: {}", err_string); + + // Should contain our custom context + assert!(err_string.contains("Failed to open RocksDB")); + assert!(err_string.contains("/proc/invalid-path")); + + // The error should also be debuggable + let debug_string = format!("{:?}", err); + println!("Debug format: {}", debug_string); + } + } + + #[test] + fn test_create_and_load_checkpoint() { + let (mut db, temp_dir) = create_test_db(); + + let id1 = Uuid::new_v4(); + let id2 = Uuid::new_v4(); + + let vector = Some(vec![0.1, 0.2, 0.3]); + let payload = Some(Payload { + content_type: ContentType::Text, + content: "Test".to_string(), + }); + + assert!( + db.insert_point(id1, vector.clone(), payload.clone()) + .is_ok() + ); + + let checkpoint = db + .checkpoint_at(temp_dir.path()) + .expect("Failed to create checkpoint"); + + assert!( + db.insert_point(id2, vector.clone(), payload.clone()) + .is_ok() + ); + + db.restore_checkpoint(&checkpoint).unwrap(); + + assert!(db.contains_point(id1).unwrap()); + assert!(!db.contains_point(id2).unwrap()); + } +} diff --git a/crates/storage/src/rocks_db/constants.rs b/crates/storage/src/rocks_db/constants.rs deleted file mode 100644 index 7cf88aa..0000000 --- a/crates/storage/src/rocks_db/constants.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub const ROCKSDB_CHECKPOINT_FILENAME_MARKER: &str = "rocksdb"; -pub const ROCKSDB_CHECKPOINT_EXTENSION: &str = "tar.gz"; diff --git a/crates/storage/src/rocks_db/tests.rs b/crates/storage/src/rocks_db/tests.rs deleted file mode 100644 index 4673f2f..0000000 --- a/crates/storage/src/rocks_db/tests.rs +++ /dev/null @@ -1,161 +0,0 @@ -use super::*; - -use defs::ContentType; -use uuid::Uuid; - -use tempfile::{TempDir, tempdir}; - -fn create_test_db() -> (RocksDbStorage, TempDir) { - let temp_dir = tempdir().unwrap(); - - let db = RocksDbStorage::new(temp_dir.path()).expect("Failed to create RocksDB"); - (db, temp_dir) -} - -#[test] -fn test_new_rocksdb_storage() { - let (db, temp_dir) = create_test_db(); - assert_eq!(db.get_current_path(), temp_dir.path()); -} - -#[test] -fn test_insert_and_get_vector() { - let (db, _temp_dir) = create_test_db(); - let id = Uuid::new_v4(); - let vector = Some(vec![0.1, 0.2, 0.3]); - let payload = Some(Payload { - content_type: ContentType::Text, - content: "Test".to_string(), - }); - - assert!(db.insert_point(id, vector.clone(), payload).is_ok()); - let result = db.get_vector(id).unwrap(); - assert_eq!(result, vector); -} - -#[test] -fn test_insert_and_get_payload() { - let (db, _temp_dir) = create_test_db(); - let id = Uuid::new_v4(); - let payload = Some(Payload { - content_type: ContentType::Text, - content: "Test".to_string(), - }); - let vector = None; - - // Move payload into insert_point and recreate expected for comparison - assert!(db.insert_point(id, vector, payload).is_ok()); - let result = db.get_payload(id).unwrap(); - let expected = Some(Payload { - content_type: ContentType::Text, - content: "Test".to_string(), - }); - assert_eq!(result, expected); -} - -#[test] -fn test_contains_point() { - let (db, _temp_dir) = create_test_db(); - let id = Uuid::new_v4(); - let payload = Some(Payload { - content_type: ContentType::Text, - content: "Test".to_string(), - }); - - assert!(!db.contains_point(id).unwrap()); - - let vector = Some(vec![0.4, 0.5, 0.6]); - db.insert_point(id, vector, payload).unwrap(); - - assert!(db.contains_point(id).unwrap()); -} - -#[test] -fn test_delete_point() { - let (db, _temp_dir) = create_test_db(); - let id = Uuid::new_v4(); - let payload = Some(Payload { - content_type: ContentType::Text, - content: "Test".to_string(), - }); - - let vector = vec![0.7, 0.8, 0.9]; - - db.insert_point(id, Some(vector), payload).unwrap(); - - assert!(db.contains_point(id).unwrap()); - - db.delete_point(id).unwrap(); - - assert!(!db.contains_point(id).unwrap()); - assert_eq!(db.get_vector(id).unwrap(), None); - assert_eq!(db.get_payload(id).unwrap(), None); -} - -#[test] -fn test_get_nonexistent_vector() { - let (db, _temp_dir) = create_test_db(); - let id = Uuid::new_v4(); - - assert_eq!(db.get_vector(id).unwrap(), None); -} - -#[test] -fn test_get_nonexistent_payload() { - let (db, _temp_dir) = create_test_db(); - let id = Uuid::new_v4(); - - assert_eq!(db.get_payload(id).unwrap(), None); -} -#[test] -fn test_error_context_preservation() { - // Test that the error chain is preserved - let result = RocksDbStorage::new("/proc/invalid-path"); - - if let Err(err) = result { - // The Display implementation should show both the context and source - let err_string = format!("{}", err); - println!("Full error message: {}", err_string); - - // Should contain our custom context - assert!(err_string.contains("Failed to open RocksDB")); - assert!(err_string.contains("/proc/invalid-path")); - - // The error should also be debuggable - let debug_string = format!("{:?}", err); - println!("Debug format: {}", debug_string); - } -} - -#[test] -fn test_create_and_load_checkpoint() { - let (mut db, temp_dir) = create_test_db(); - - let id1 = Uuid::new_v4(); - let id2 = Uuid::new_v4(); - - let vector = Some(vec![0.1, 0.2, 0.3]); - let payload = Some(Payload { - content_type: ContentType::Text, - content: "Test".to_string(), - }); - - assert!( - db.insert_point(id1, vector.clone(), payload.clone()) - .is_ok() - ); - - let checkpoint = db - .checkpoint_at(temp_dir.path()) - .expect("Failed to create checkpoint"); - - assert!( - db.insert_point(id2, vector.clone(), payload.clone()) - .is_ok() - ); - - db.restore_checkpoint(&checkpoint).unwrap(); - - assert!(db.contains_point(id1).unwrap()); - assert!(!db.contains_point(id2).unwrap()); -} diff --git a/crates/tui/src/app/embeddings.rs b/crates/tui/src/app/embeddings.rs index 238817c..343b422 100644 --- a/crates/tui/src/app/embeddings.rs +++ b/crates/tui/src/app/embeddings.rs @@ -1,4 +1,3 @@ -use crate::constants::{ENV_IMAGE_EMBEDDING_URL, ENV_TEXT_EMBEDDING_URL}; use reqwest::StatusCode; use reqwest::blocking::{Client, Response, multipart}; use serde::Deserialize; @@ -62,8 +61,8 @@ impl EmbeddingClient { .timeout(Duration::from_secs(15)) .build() .unwrap_or_else(|_| Client::new()); - let text_url = env::var(ENV_TEXT_EMBEDDING_URL).expect("TEXT_EMBEDDING_URL must be set"); - let image_url = env::var(ENV_IMAGE_EMBEDDING_URL).expect("IMAGE_EMBEDDING_URL must be set"); + let text_url = env::var("TEXT_EMBEDDING_URL").expect("TEXT_EMBEDDING_URL must be set"); + let image_url = env::var("IMAGE_EMBEDDING_URL").expect("IMAGE_EMBEDDING_URL must be set"); Self { client, diff --git a/crates/tui/src/app/events.rs b/crates/tui/src/app/events.rs index e7d90a9..5f4668f 100644 --- a/crates/tui/src/app/events.rs +++ b/crates/tui/src/app/events.rs @@ -1,11 +1,13 @@ use super::{App, AppState, ModalType, VectorListItem}; -use crate::constants::VECTOR_LIST_LIMIT; use crossterm::event::{Event, KeyCode, KeyEvent}; use defs::{ContentType, Payload, SearchQueryInput, Similarity}; use std::io; use std::path::PathBuf; use uuid::Uuid; +// Set how many vectors to fetch per function call in list_vectors +const VECTOR_LIST_LIMIT: usize = 50; + pub fn handle_event(app: &mut App, event: Event) -> io::Result<()> { if let Event::Key(key) = event { handle_key_event(app, key)?; diff --git a/crates/tui/src/constants.rs b/crates/tui/src/constants.rs deleted file mode 100644 index 00bdeb1..0000000 --- a/crates/tui/src/constants.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub const POLL_DURATION: std::time::Duration = std::time::Duration::from_millis(50); - -// Set how many vectors to fetch per function call in list_vectors -pub const VECTOR_LIST_LIMIT: usize = 50; - -pub const VECTOR_OPERATIONS: &[&str] = &[ - "List All Vectors", - "Delete Vector", - "Search Similar Vectors", - "Insert Text Embedding", - "Insert Image Embedding", -]; - -pub const DB_OPERATIONS: &[&str] = &["Create New Database", "Select Database", "Delete Database"]; - -pub const ENV_TEXT_EMBEDDING_URL: &str = "TEXT_EMBEDDING_URL"; -pub const ENV_IMAGE_EMBEDDING_URL: &str = "IMAGE_EMBEDDING_URL"; diff --git a/crates/tui/src/main.rs b/crates/tui/src/main.rs index 64df5e1..ebd543f 100644 --- a/crates/tui/src/main.rs +++ b/crates/tui/src/main.rs @@ -1,5 +1,4 @@ mod app; -mod constants; mod ui; use app::App; @@ -16,7 +15,7 @@ use ui::{ vector_operations::render_vector_operations, }; -use constants::POLL_DURATION; +const POLL_DURATION: std::time::Duration = std::time::Duration::from_millis(50); fn main() -> Result<()> { color_eyre::install()?; diff --git a/crates/tui/src/ui/db.rs b/crates/tui/src/ui/db.rs index c4fb40c..3f36b88 100644 --- a/crates/tui/src/ui/db.rs +++ b/crates/tui/src/ui/db.rs @@ -7,7 +7,8 @@ use ratatui::{ use super::components::{OperationsList, PageTitle, common_instructions, create_instructions}; use crate::app::App; -use crate::constants::DB_OPERATIONS; + +const DB_OPERATIONS: &[&str] = &["Create New Database", "Select Database", "Delete Database"]; fn get_db_items() -> Vec> { DB_OPERATIONS.iter().map(|&op| ListItem::new(op)).collect() diff --git a/crates/tui/src/ui/vector_operations.rs b/crates/tui/src/ui/vector_operations.rs index 28596ce..02bc061 100644 --- a/crates/tui/src/ui/vector_operations.rs +++ b/crates/tui/src/ui/vector_operations.rs @@ -8,7 +8,14 @@ use ratatui::{ use super::components::{OperationsList, PageTitle, common_instructions, create_instructions}; use crate::app::App; -use crate::constants::VECTOR_OPERATIONS; + +const VECTOR_OPERATIONS: &[&str] = &[ + "List All Vectors", + "Delete Vector", + "Search Similar Vectors", + "Insert Text Embedding", + "Insert Image Embedding", +]; fn get_vector_items() -> Vec> { VECTOR_OPERATIONS diff --git a/docker-compose.yml b/docker-compose.yml index 8608435..bf06ca1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,7 @@ services: DISABLE_HTTP: ${DISABLE_HTTP:-false} # Required - VORTEXDB_KEYS_FILE: /run/secrets/keys.json + GRPC_ROOT_PASSWORD: ${GRPC_ROOT_PASSWORD} DIMENSION: ${DIMENSION} DATA_PATH: /data @@ -31,7 +31,6 @@ services: volumes: - vortexdb_data:/data - - ./keys.json:/run/secrets/keys.json:ro volumes: vortexdb_data: diff --git a/docs/api-reference/grpc.mdx b/docs/api-reference/grpc.mdx index bb544b2..9e8297e 100644 --- a/docs/api-reference/grpc.mdx +++ b/docs/api-reference/grpc.mdx @@ -28,7 +28,7 @@ All gRPC calls require the `authorization` header with your API key: -H "authorization: your-api-key" ``` -Valid keys come from the JSON file pointed to by the `VORTEXDB_KEYS_FILE` environment variable, shared with the HTTP server. `readonly` keys can call `GetPoint`, `SearchPoints`, and `SearchPointsBatch`; `readwrite` keys can additionally call `InsertVector`, `InsertVectorsBatch`, and `DeletePoint`. A `readonly` key calling a write RPC gets a `PERMISSION_DENIED` status. +The API key is set via the `GRPC_ROOT_PASSWORD` environment variable. --- diff --git a/docs/api-reference/overview.mdx b/docs/api-reference/overview.mdx index 57d29a7..b06f3b9 100644 --- a/docs/api-reference/overview.mdx +++ b/docs/api-reference/overview.mdx @@ -24,7 +24,7 @@ VortexDB provides two complementary APIs for different use cases: |---------|------|------| | **Protocol** | HTTP/2 + Protobuf | HTTP/1.1 + JSON | | **Default Port** | 50051 | 3000 | -| **Authentication** | API key required | API key required | +| **Authentication** | API key required | None | | **Performance** | Higher throughput | Lower latency for simple requests | | **Client Libraries** | Auto-generated | Any HTTP client | | **Best For** | Production, SDKs | Debugging, curl | @@ -52,16 +52,11 @@ db = VortexDB( ### HTTP -The HTTP API requires an `api-key` header on every request under `/points`. `/` and `/health` remain open for health checks. +The HTTP API does not require authentication by default. It's designed for trusted internal networks and development. -```bash -curl -X POST "http://localhost:3000/points/search" \ - -H "api-key: your-api-key" \ - -H "Content-Type: application/json" \ - -d '{"vector": [0.1, 0.2, 0.3], "similarity": "Cosine", "limit": 5}' -``` - -Keys come from the same `VORTEXDB_KEYS_FILE` used by gRPC (see [gRPC API](/api-reference/grpc)), but the HTTP API additionally enforces each key's role: `readonly` keys can fetch and search points; `readwrite` keys can also insert, batch-insert, and delete. A `readonly` key used against a write route gets `403 Forbidden`; a missing or unrecognized key gets `401 Unauthorized`. + +In production, always deploy the HTTP API behind a reverse proxy with authentication, or disable it entirely using `DISABLE_HTTP=true`. + ## Common Operations diff --git a/docs/concepts/architecture.mdx b/docs/concepts/architecture.mdx index 5d31837..13d5307 100644 --- a/docs/concepts/architecture.mdx +++ b/docs/concepts/architecture.mdx @@ -147,7 +147,7 @@ VortexDB is configured via environment variables: | Variable | Required | Default | Description | |----------|----------|---------|-------------| -| `VORTEXDB_KEYS_FILE` | Yes | - | Path to a JSON file of API keys shared by the HTTP and gRPC servers | +| `GRPC_ROOT_PASSWORD` | Yes | - | Authentication password for gRPC | | `DIMENSION` | Yes | - | Vector dimensionality | | `DATA_PATH` | No | system temp dir | Directory for persistent storage | | `HTTP_HOST` | No | `127.0.0.1` | HTTP server bind address | diff --git a/docs/getting-started/installation.mdx b/docs/getting-started/installation.mdx index 802a683..5cb05e7 100644 --- a/docs/getting-started/installation.mdx +++ b/docs/getting-started/installation.mdx @@ -37,7 +37,7 @@ Edit the `.env` file with your settings: ```bash # Required settings -VORTEXDB_KEYS_FILE=./keys.json # see keys.example.json for the format +GRPC_ROOT_PASSWORD=your-secure-password DIMENSION=384 # Vector dimension (e.g., 384 for MiniLM embeddings) DATA_PATH=/data diff --git a/keys.example.json b/keys.example.json deleted file mode 100644 index ec75451..0000000 --- a/keys.example.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "keys": [ - { "name": "admin", "role": "readwrite", "key": "replace-with-a-random-secret" }, - { "name": "search-service", "role": "readonly", "key": "replace-with-a-different-random-secret" } - ] -} From 9a92a1ca5af75e0fa1a1fefafa4b101035576096 Mon Sep 17 00:00:00 2001 From: Shashank Date: Sat, 22 Aug 2026 23:28:54 +0530 Subject: [PATCH 5/6] fix(Frontend): Add markdown rendering. author: tcan Signed-off-by: Shashank --- demo/document-rag/frontend/package-lock.json | 1587 +++++++++++++++++- demo/document-rag/frontend/package.json | 4 +- demo/document-rag/frontend/src/App.css | 145 ++ demo/document-rag/frontend/src/App.jsx | 19 +- 4 files changed, 1701 insertions(+), 54 deletions(-) diff --git a/demo/document-rag/frontend/package-lock.json b/demo/document-rag/frontend/package-lock.json index 5f31018..37030a0 100644 --- a/demo/document-rag/frontend/package-lock.json +++ b/demo/document-rag/frontend/package-lock.json @@ -9,7 +9,9 @@ "version": "0.0.1", "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1" }, "devDependencies": { "@vitejs/plugin-react": "^4.2.1", @@ -1141,13 +1143,76 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "license": "ISC" + }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", @@ -1169,6 +1234,16 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.10.19", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.19.tgz", @@ -1237,6 +1312,66 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1244,11 +1379,17 @@ "dev": true, "license": "MIT" }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT", + "peer": true + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1262,6 +1403,41 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.339", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.339.tgz", @@ -1318,6 +1494,34 @@ "node": ">=6" } }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1329,79 +1533,1043 @@ "os": [ "darwin" ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=6.9.0" + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT" }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" + "dependencies": { + "micromark-util-types": "^2.0.0" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "yallist": "^3.0.2" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -1430,6 +2598,31 @@ "dev": true, "license": "MIT" }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1466,6 +2659,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -1491,6 +2694,33 @@ "react": "^18.3.1" } }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -1501,6 +2731,72 @@ "node": ">=0.10.0" } }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/rollup": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", @@ -1575,6 +2871,155 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -1606,6 +3051,34 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "5.4.21", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", @@ -1672,6 +3145,16 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/demo/document-rag/frontend/package.json b/demo/document-rag/frontend/package.json index d0c6096..e4b8d35 100644 --- a/demo/document-rag/frontend/package.json +++ b/demo/document-rag/frontend/package.json @@ -10,7 +10,9 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1" }, "devDependencies": { "@vitejs/plugin-react": "^4.2.1", diff --git a/demo/document-rag/frontend/src/App.css b/demo/document-rag/frontend/src/App.css index f34c279..53b3616 100644 --- a/demo/document-rag/frontend/src/App.css +++ b/demo/document-rag/frontend/src/App.css @@ -303,6 +303,7 @@ body { background: var(--user-bubble); color: white; border-bottom-right-radius: 4px; + white-space: pre-wrap; } .message-assistant .message-text { @@ -312,10 +313,154 @@ body { border-bottom-left-radius: 4px; } +.message-assistant .message-text p { + margin-bottom: 0.75em; +} + +.message-assistant .message-text p:last-child { + margin-bottom: 0; +} + +.message-assistant .message-text h1, +.message-assistant .message-text h2, +.message-assistant .message-text h3, +.message-assistant .message-text h4, +.message-assistant .message-text h5, +.message-assistant .message-text h6 { + color: var(--text-primary); + font-weight: 600; + margin-top: 1em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +.message-assistant .message-text h1:first-child, +.message-assistant .message-text h2:first-child, +.message-assistant .message-text h3:first-child, +.message-assistant .message-text h4:first-child { + margin-top: 0; +} + +.message-assistant .message-text h1 { font-size: 1.4em; } +.message-assistant .message-text h2 { font-size: 1.25em; } +.message-assistant .message-text h3 { font-size: 1.1em; } +.message-assistant .message-text h4 { font-size: 1em; } + +.message-assistant .message-text ul, +.message-assistant .message-text ol { + padding-left: 24px; + margin-top: 0.5em; + margin-bottom: 0.75em; +} + +.message-assistant .message-text ul { + list-style-type: disc; +} + +.message-assistant .message-text ol { + list-style-type: decimal; +} + +.message-assistant .message-text li { + margin-bottom: 0.25em; +} + +.message-assistant .message-text li:last-child { + margin-bottom: 0; +} + +.message-assistant .message-text li > ul, +.message-assistant .message-text li > ol { + margin-top: 0.25em; + margin-bottom: 0.25em; +} + +.message-assistant .message-text code { + background: rgba(255, 255, 255, 0.1); + padding: 2px 6px; + border-radius: 4px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.88em; + color: #e2e8f0; +} + +.message-assistant .message-text pre { + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 12px 14px; + margin: 0.75em 0; + overflow-x: auto; +} + +.message-assistant .message-text pre code { + background: transparent; + padding: 0; + border-radius: 0; + font-size: 13px; + line-height: 1.5; + color: inherit; +} + +.message-assistant .message-text blockquote { + border-left: 3px solid var(--accent); + padding-left: 12px; + margin: 0.75em 0; + color: var(--text-secondary); +} + +.message-assistant .message-text table { + width: 100%; + border-collapse: collapse; + margin: 0.75em 0; + font-size: 13px; + display: block; + overflow-x: auto; +} + +.message-assistant .message-text th, +.message-assistant .message-text td { + border: 1px solid var(--border-color); + padding: 8px 12px; + text-align: left; +} + +.message-assistant .message-text th { + background: var(--bg-tertiary); + font-weight: 600; + color: var(--text-primary); +} + +.message-assistant .message-text tr:nth-child(even) { + background: rgba(255, 255, 255, 0.02); +} + +.message-assistant .message-text a { + color: var(--accent); + text-decoration: underline; + text-underline-offset: 2px; +} + +.message-assistant .message-text a:hover { + color: var(--accent-hover); +} + +.message-assistant .message-text hr { + border: none; + border-top: 1px solid var(--border-color); + margin: 1em 0; +} + +.message-assistant .message-text strong { + font-weight: 600; + color: var(--text-primary); +} + .message-error .message-text { background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); color: var(--error); + white-space: pre-wrap; } .sources { diff --git a/demo/document-rag/frontend/src/App.jsx b/demo/document-rag/frontend/src/App.jsx index f8e8554..ecc430a 100644 --- a/demo/document-rag/frontend/src/App.jsx +++ b/demo/document-rag/frontend/src/App.jsx @@ -1,4 +1,6 @@ import { useState, useRef, useEffect } from 'react'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; import './App.css'; import { chat, getHealth } from './api'; @@ -270,7 +272,22 @@ function App() { )}
-
{msg.content}
+ {msg.sources && msg.sources.length > 0 && (

Sources:

From bf1e0b9a811445e7c3b54ec1ff2e0084ebb5a76f Mon Sep 17 00:00:00 2001 From: ishaan Date: Sun, 23 Aug 2026 00:41:36 +0530 Subject: [PATCH 6/6] chore(demo): updated the demo documents to include the documentation of labs projects --- demo/document-rag/documents/Gasper_README.md | 190 +++ .../documents/Gasper_content_concepts.md | 91 ++ .../Gasper_content_configurations_admin.md | 17 + .../Gasper_content_configurations_appmaker.md | 35 + ...asper_content_configurations_cloudflare.md | 38 + .../Gasper_content_configurations_dbmaker.md | 92 ++ ...er_content_configurations_docker-images.md | 27 + .../Gasper_content_configurations_gendns.md | 36 + .../Gasper_content_configurations_genproxy.md | 49 + .../Gasper_content_configurations_genssh.md | 41 + .../Gasper_content_configurations_global.md | 151 ++ .../Gasper_content_configurations_jwt.md | 29 + .../Gasper_content_configurations_master.md | 46 + .../Gasper_content_configurations_mongodb.md | 19 + .../Gasper_content_configurations_overview.md | 314 ++++ .../Gasper_content_configurations_redis.md | 21 + ...tent_examples_applications_advanced-php.md | 136 ++ ...er_content_examples_applications_golang.md | 136 ++ ...nt_examples_applications_nodejs-with-db.md | 166 +++ ...er_content_examples_applications_nodejs.md | 140 ++ ...ent_examples_applications_python-django.md | 144 ++ ...tent_examples_applications_python-flask.md | 143 ++ ...ent_examples_applications_ruby-on-rails.md | 156 ++ ...sper_content_examples_applications_rust.md | 155 ++ ...ontent_examples_applications_simple-php.md | 68 + ...er_content_examples_applications_static.md | 67 + ...sper_content_examples_databases_mongodb.md | 31 + ...Gasper_content_examples_databases_mysql.md | 31 + ...r_content_examples_databases_postgresql.md | 32 + ...Gasper_content_examples_databases_redis.md | 32 + .../Gasper_content_examples_login.md | 27 + .../documents/Gasper_content_features.md | 18 + .../documents/Gasper_content_index.md | 24 + .../documents/Gasper_content_quick-start.md | 127 ++ demo/document-rag/documents/README.md | 72 + demo/document-rag/documents/Rootex_README.md | 88 ++ .../documents/Rootex_api_rootex.md | 1278 +++++++++++++++++ .../documents/Rootex_engine_architecture.md | 23 + .../documents/Rootex_engine_audio.md | 8 + .../documents/Rootex_engine_events.md | 15 + .../documents/Rootex_engine_framework.md | 33 + .../documents/Rootex_engine_inputs.md | 39 + .../documents/Rootex_engine_multithreading.md | 10 + .../documents/Rootex_engine_physics.md | 9 + .../documents/Rootex_engine_rendering.md | 11 + .../documents/Rootex_engine_resources.md | 15 + .../documents/Rootex_engine_scripting.md | 52 + .../documents/Rootex_guides_editor_layout.md | 54 + .../documents/Rootex_guides_getting_help.md | 8 + .../Rootex_guides_getting_started.md | 50 + .../Rootex_guides_graphics_tutorial.md | 111 ++ .../documents/Rootex_guides_hud_tutorial.md | 143 ++ .../Rootex_guides_particle_effects.md | 39 + .../Rootex_guides_running_the_editor.md | 16 + .../Rootex_guides_transform_animation.md | 76 + demo/document-rag/documents/Rootex_index.md | 42 + .../VortexDB_docs_api-reference_grpc.md | 474 ++++++ .../VortexDB_docs_api-reference_http.md | 404 ++++++ .../VortexDB_docs_api-reference_overview.md | 180 +++ .../VortexDB_docs_concepts_architecture.md | 184 +++ .../VortexDB_docs_concepts_indexers.md | 147 ++ .../VortexDB_docs_concepts_snapshots.md | 196 +++ ...texDB_docs_getting-started_installation.md | 156 ++ ...ortexDB_docs_getting-started_quickstart.md | 225 +++ .../documents/VortexDB_docs_sdk_examples.md | 144 ++ .../documents/VortexDB_docs_sdk_reference.md | 626 ++++++++ .../document-rag/documents/Watchdog_README.md | 145 ++ .../documents/api-integration-guide.md | 26 - .../documents/company-overview.md | 22 - .../documents/employee-onboarding.md | 20 - .../engineering-decision-record-042.md | 18 - .../documents/incident-2026-05-northstar.md | 19 - .../monthly-operations-report-june-2026.md | 19 - .../documents/platform-architecture.md | 21 - .../documents/pricing-and-plans.md | 20 - .../documents/product-guide-aster-edge.md | 22 - .../documents/product-roadmap-2026-h2.md | 23 - .../documents/q2-customer-success-notes.md | 17 - .../sales-proposal-northstar-draft.md | 10 - .../documents/security-and-access-policy.md | 20 - .../documents/support-ticket-1842.md | 11 - .../documents/support-ticket-1907.md | 11 - 82 files changed, 7902 insertions(+), 279 deletions(-) create mode 100644 demo/document-rag/documents/Gasper_README.md create mode 100644 demo/document-rag/documents/Gasper_content_concepts.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_admin.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_appmaker.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_cloudflare.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_dbmaker.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_docker-images.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_gendns.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_genproxy.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_genssh.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_global.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_jwt.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_master.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_mongodb.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_overview.md create mode 100644 demo/document-rag/documents/Gasper_content_configurations_redis.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_advanced-php.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_golang.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_nodejs-with-db.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_nodejs.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_python-django.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_python-flask.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_ruby-on-rails.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_rust.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_simple-php.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_applications_static.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_databases_mongodb.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_databases_mysql.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_databases_postgresql.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_databases_redis.md create mode 100644 demo/document-rag/documents/Gasper_content_examples_login.md create mode 100644 demo/document-rag/documents/Gasper_content_features.md create mode 100644 demo/document-rag/documents/Gasper_content_index.md create mode 100644 demo/document-rag/documents/Gasper_content_quick-start.md create mode 100644 demo/document-rag/documents/README.md create mode 100644 demo/document-rag/documents/Rootex_README.md create mode 100644 demo/document-rag/documents/Rootex_api_rootex.md create mode 100644 demo/document-rag/documents/Rootex_engine_architecture.md create mode 100644 demo/document-rag/documents/Rootex_engine_audio.md create mode 100644 demo/document-rag/documents/Rootex_engine_events.md create mode 100644 demo/document-rag/documents/Rootex_engine_framework.md create mode 100644 demo/document-rag/documents/Rootex_engine_inputs.md create mode 100644 demo/document-rag/documents/Rootex_engine_multithreading.md create mode 100644 demo/document-rag/documents/Rootex_engine_physics.md create mode 100644 demo/document-rag/documents/Rootex_engine_rendering.md create mode 100644 demo/document-rag/documents/Rootex_engine_resources.md create mode 100644 demo/document-rag/documents/Rootex_engine_scripting.md create mode 100644 demo/document-rag/documents/Rootex_guides_editor_layout.md create mode 100644 demo/document-rag/documents/Rootex_guides_getting_help.md create mode 100644 demo/document-rag/documents/Rootex_guides_getting_started.md create mode 100644 demo/document-rag/documents/Rootex_guides_graphics_tutorial.md create mode 100644 demo/document-rag/documents/Rootex_guides_hud_tutorial.md create mode 100644 demo/document-rag/documents/Rootex_guides_particle_effects.md create mode 100644 demo/document-rag/documents/Rootex_guides_running_the_editor.md create mode 100644 demo/document-rag/documents/Rootex_guides_transform_animation.md create mode 100644 demo/document-rag/documents/Rootex_index.md create mode 100644 demo/document-rag/documents/VortexDB_docs_api-reference_grpc.md create mode 100644 demo/document-rag/documents/VortexDB_docs_api-reference_http.md create mode 100644 demo/document-rag/documents/VortexDB_docs_api-reference_overview.md create mode 100644 demo/document-rag/documents/VortexDB_docs_concepts_architecture.md create mode 100644 demo/document-rag/documents/VortexDB_docs_concepts_indexers.md create mode 100644 demo/document-rag/documents/VortexDB_docs_concepts_snapshots.md create mode 100644 demo/document-rag/documents/VortexDB_docs_getting-started_installation.md create mode 100644 demo/document-rag/documents/VortexDB_docs_getting-started_quickstart.md create mode 100644 demo/document-rag/documents/VortexDB_docs_sdk_examples.md create mode 100644 demo/document-rag/documents/VortexDB_docs_sdk_reference.md create mode 100644 demo/document-rag/documents/Watchdog_README.md delete mode 100644 demo/document-rag/documents/api-integration-guide.md delete mode 100644 demo/document-rag/documents/company-overview.md delete mode 100644 demo/document-rag/documents/employee-onboarding.md delete mode 100644 demo/document-rag/documents/engineering-decision-record-042.md delete mode 100644 demo/document-rag/documents/incident-2026-05-northstar.md delete mode 100644 demo/document-rag/documents/monthly-operations-report-june-2026.md delete mode 100644 demo/document-rag/documents/platform-architecture.md delete mode 100644 demo/document-rag/documents/pricing-and-plans.md delete mode 100644 demo/document-rag/documents/product-guide-aster-edge.md delete mode 100644 demo/document-rag/documents/product-roadmap-2026-h2.md delete mode 100644 demo/document-rag/documents/q2-customer-success-notes.md delete mode 100644 demo/document-rag/documents/sales-proposal-northstar-draft.md delete mode 100644 demo/document-rag/documents/security-and-access-policy.md delete mode 100644 demo/document-rag/documents/support-ticket-1842.md delete mode 100644 demo/document-rag/documents/support-ticket-1907.md diff --git a/demo/document-rag/documents/Gasper_README.md b/demo/document-rag/documents/Gasper_README.md new file mode 100644 index 0000000..a474a6d --- /dev/null +++ b/demo/document-rag/documents/Gasper_README.md @@ -0,0 +1,190 @@ +# Gasper + +> Your Cloud in a Binary + + + +[![Build Status](https://travis-ci.org/sdslabs/gasper.svg?branch=develop)](https://travis-ci.org/sdslabs/gasper) +[![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://gasper-docs.netlify.com/) +[![Go Report Card](https://goreportcard.com/badge/github.com/sdslabs/gasper)](https://goreportcard.com/report/github.com/sdslabs/gasper) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/sdslabs/gasper/blob/develop/LICENSE.md) + +Gasper is an intelligent Platform as a Service (PaaS) used for deploying and managing applications and databases in any cloud topology. + +## Contents + +* [Overview](#overview) +* [Features](#features) +* [Supported Languages](#supported-languages) +* [Supported Databases](#supported-databases) +* [Documentation](#documentation) +* [Dependencies](#dependencies) +* [Download](#download) +* [Development](#development) +* [Contributing](#contributing) +* [Meet the A-Team](#meet-the-a-team) +* [Contact](#contact) + +## Overview + +### The Dilemma +Imagine you have a couple of *Bare Metal Servers* and/or *Virtual Machines* (collectively called nodes) at your disposal. Now you want to deploy a couple of applications/services to these nodes in such a manner so as to not put too much load on a single node. + +### Naive Approach +Your 1st option is to manually decide which application goes to which node, then use ssh/telnet to manually +setup all of your applications in each node one by one. + +### A Wise Choice +But you are smarter than that, hence you go for the 2nd option which is [Kubernetes](https://kubernetes.io/). You setup Kubernetes in all of your +nodes which forms a cluster, and now you can deploy your applications without worrying about load distribution. But +Kubernetes requires a lot of configuration for each application(deployments, services, stateful-sets etc) not to mention +pipelines for creating the corresponding docker image. + +### The Ultimatum +Here comes (🥁drumroll please 🥁) **Gasper**, your 3rd option!
+Gasper builds and runs applications in docker containers **directly from source code**. You no longer need to create application specific docker images and build pipelines, let Gasper do the heavylifting for you 😊. +Gasper requires minimal parameters for deploying an application, so minimal that you can count them on fingers in one hand 🤚. Same goes for Gasper provisioned databases. Gone are the days of hard labour (writing configurations). + +## Features + +Fear not because the reduction in complexity doesn't imply the reduction in features. You can rest assured because Gasper has:- + +* Worker services for creating/managing databases and applications +* Master service for:- + * Checking the status of worker services + * Intelligently distributing applications/databases among them + * Transferring applications from one worker node to another in case of node failure + * Removing dead worker nodes from the cloud +* REST API interface for the entire ecosystem +* Reverse-proxy service with HTTPS, HTTP/2, Websocket and gRPC support for accessing deployed applications +* DNS service which automatically creates DNS entries for all applications which in turn are resolved inside containers +* SSH service for providing ssh access directly to an application's docker container +* Virtual terminal for interacting with your application's docker container from your browser +* Dynamic addition/removal of nodes and services without configuration changes or restarts +* Compatibility with Linux, Windows, MacOS, FreeBSD and OpenBSD +* All of the above packaged with ❤️ in a **single binary** + +## Supported Languages + +Gasper currently supports applications of the following types:- + +* Static web pages +* PHP +* Python 2 +* Python 3 +* Node.js +* Golang +* Ruby +* Rust + +It ain't much but it's honest work 🥳 + +## Supported Databases + +The following databases are supported by Gasper:- + +* MySQL +* MongoDB +* PostgreSQL +* Redis + +It ain't.... (complete the rest yourself) + +## Documentation + +You can find the complete documentation of Gasper at [https://gasper-docs.netlify.app/](https://gasper-docs.netlify.app/) + +## Dependencies + +The only thing you need for running Gasper is [Docker](https://www.docker.com/). Here are the installation guides for:- + +* [Linux](https://runnable.com/docker/install-docker-on-linux) +* [MacOS](https://docs.docker.com/docker-for-mac/install/) +* [Windows](https://docs.docker.com/docker-for-windows/install/) + +If you perhaps need a higher degree of control over your entire cloud then you may setup [MongoDB](https://www.mongodb.com/download-center/community) and [Redis](https://redis.io/download) separately within your infrastructure and make the necessary changes in the `mongo` and `redis` sections of `config.toml`. + +## Download + +Assuming you have the [dependencies](#dependencies) installed, head over to Gasper's [releases](https://github.com/sdslabs/gasper/releases) page and grab the latest binary according to your operating system and system architecture + +Run the downloaded binary with the [sample configuration file](./config.sample.toml) + +```bash +$ ./gasper --conf ./config.toml +``` + +## Development + +You need to have [Golang 1.13.x](https://golang.org/dl/) or higher installed along with the mentioned [dependencies](#dependencies) + +Open your favourite terminal and perform the following tasks:- + +1. Cross-check your golang version. + + ```bash + $ go version + go version go1.13.5 darwin/amd64 + ``` + +2. Clone this repository. + + ```bash + $ git clone https://github.com/sdslabs/gasper + ``` + +3. Go inside the cloned directory and list available *makefile* commands. + + ```bash + $ cd gasper && make help + + Gasper: Your cloud in a binary + + install Install missing dependencies + build Build the project binary + tools Install development tools + release Build release binaries + start Start in development mode with hot-reload enabled + clean Clean build files + fmt Format entire codebase + vet Vet entire codebase + lint Check codebase for style mistakes + test Run tests + help Display this help + ``` + +4. Setup project configuration and make changes if required. The configuration file is well-documented so you +won't have a hard time looking around. + + ```bash + $ cp config.sample.toml config.toml + ``` + +5. Start the development server. + + ```bash + $ make start + ``` + +## Contributing + +If you'd like to contribute to this project, refer to the [contributing documentation](./CONTRIBUTING.md). + +## Meet the A-Team + +* Anish Mukherjee [@alphadose](https://github.com/alphadose) +* Vaibhav [@vrongmeal](https://github.com/vrongmeal) +* Supratik Das [@supra08](https://github.com/supra08) +* Karanpreet Singh [@karan0299](https://github.com/karan0299) +* Mohit Sharma [@Scar26](https://github.com/Scar26) + +Gasper Logo: Leshna Balara [@leshnabalara](https://github.com/leshnabalara) + +You can find the entire list of contributors [here](https://github.com/sdslabs/gasper/graphs/contributors) + +Created with 💖 by [SDSLabs](https://github.com/sdslabs) + +## Contact + +If you have a query regarding the product or just want to say hello then feel free to visit +[chat.sdslabs.co](http://chat.sdslabs.co/) or drop a mail at [contact@sdslabs.co.in](mailto:contact@sdslabs.co.in) diff --git a/demo/document-rag/documents/Gasper_content_concepts.md b/demo/document-rag/documents/Gasper_content_concepts.md new file mode 100644 index 0000000..7831084 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_concepts.md @@ -0,0 +1,91 @@ +# Concepts + +## Terminology + +* **Node**: A Virtual Machine or a Bare-Metal Server +* **Worker Node**: A node which handles creation/management of applications and databases +* **Support Node**: A node which supports the functioning of applications and databases in worker nodes +* **Master Node**: A node which manages all other nodes and keeps them in check + +## Components + +Gasper is divided into number of components each playing a specific role in the ecosystem. Lets have a look. + +### AppMaker 💧 + +AppMaker service deals with creating and managing applications and their life-cycles. + +It currently supports applications of the following types + +* Static web pages +* PHP +* Python 2 +* Python 3 +* Node.js +* Golang +* Ruby + +It ain't much but it's honest work 🥳 + +!!!info + A node with **AppMaker** deployed is a Worker Node + +### DbMaker 🔥 + +DbMaker service deals with creating and managing databases and their life-cycles + +It currently supports databases of the following types + +* MySQL +* MongoDB +* PostgreSQL +* Redis + +It ain't.... (complete the rest yourself) + +!!!info + A node with **DbMaker** deployed is a Worker Node + +### GenProxy ⚡ + +GenProxy service deals with reverse-proxying HTTP, HTTPS, HTTP/2, Websocket and gRPC requests to the desired application's IPv4 address and port based on the hostname. + +!!!info + A node with **GenProxy** deployed is a Support Node + +### GenDNS 💡 + +GenDNS service deals with creating and managing DNS records of all deployed applications. All DNS records point to the IPv4 addresses of GenProxy ⚡ instances which in turn reverse-proxies the request to the desired application's IPv4 address and port. + +!!!note + **GenDNS** stores DNS records in such a manner that all requests are equally distributed among all available **GenProxy** instances. The records dynamically change with the addition/deletion of **GenProxy** instances. + +!!!info + A node with **GenDNS** deployed is a Support Node + +### GenSSH 🗿 + +GenSSH service provides [SSH](https://www.ssh.com/ssh/protocol/) access directly to an application's docker container to the end user. +The SSH command will be automatically returned to the user on application creation provided the node where the application is deployed has the GenSSH service deployed. + +!!!info + A node with **GenSSH** deployed is a Support Node + +### Master 🌪 + +Master is the master of the entire Gasper ecosystem which performs the following tasks + +* Equal distribution of applications and databases among worker nodes +* User Authentication based on JWT (JSON Web Token) +* User API for performing operations on any application/database in any node (Identity Access Management is handled with JWT) +* Admin API for fetching and managing information of all nodes, applications, databases and users +* Removal of inactive nodes from the cloud ecosystem +* Re-scheduling of applications in case of node failure + +Master API docs are available [here](/api) + +!!!info + You can interact with the entire Gasper ecosystem (example:- create/manage applications or databases) only through the REST API provided by **Master** + +!!!info + A node with **Master** deployed is a Master Node diff --git a/demo/document-rag/documents/Gasper_content_configurations_admin.md b/demo/document-rag/documents/Gasper_content_configurations_admin.md new file mode 100644 index 0000000..dacace3 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_admin.md @@ -0,0 +1,17 @@ +# Admin Configuration + +An admin user is created every time a Gasper instance is spawned in any node + +The credentials of the default admin user are defined in the **admin** section of the configuration file + +```toml +########################### +# Admin Configuration # +########################### + +# Default admin credentials for the Gasper ecosystem. +[admin] +email = "anish.mukherjee1996@gmail.com" +username = "alphadose" +password = "alphadose" +``` diff --git a/demo/document-rag/documents/Gasper_content_configurations_appmaker.md b/demo/document-rag/documents/Gasper_content_configurations_appmaker.md new file mode 100644 index 0000000..28a6b05 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_appmaker.md @@ -0,0 +1,35 @@ +# AppMaker Configuration + +AppMaker deals with creating and managing applications and their life-cycles + +The following section deals with the configuration of AppMaker + +```toml +############################## +# AppMaker Configuration # +############################## + +[services.appmaker] +deploy = true # Deploy AppMaker? +port = 4000 + +# Time Interval (in seconds) in which metrics of all application containers +# running in the current node are collected and stored in the central mongoDB database +metrics_interval = 600 + +# Time Interval (in seconds) in which health is checked of all application containers and if unhealthy, they are restarted +health_interval = 300 + +# Hard Limits the total number of app instances that can be deployed by an user +# Set app_limit = -1 if no hard limit is to be imposed +app_limit = 10 + +# Specifies the maximum CPU allocation for a container created by a non admin user +max_container_cpu = 0.25 + +# Specifies the maximum memory allocation for a container created by a non admin user +max_container_memory = 0.5 +``` + +!!!warning + The node where **AppMaker** is to be deployed should have **Docker** installed and running diff --git a/demo/document-rag/documents/Gasper_content_configurations_cloudflare.md b/demo/document-rag/documents/Gasper_content_configurations_cloudflare.md new file mode 100644 index 0000000..b5965e8 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_cloudflare.md @@ -0,0 +1,38 @@ +# Cloudflare Configuration + +Gasper has in-built support for using [cloudflare](https://www.cloudflare.com/) services + +If Gasper's cloudflare plugin is enabled then whenever an application is created, its corresponding DNS entry will be automatically created in cloudflare + +The DNS entry created will be according to the [domain](/configurations/global/#domain) parameter in the configuration file + +???example + If the domain parameter's value is `sdslabs.co` and you have created an application named **foo**, then an entry will be created in cloudflare (if plugin enabled) with the domain name `foo-app-gasper.sdslabs.co` + +!!!warning + The domain name set in the [domain](/configurations/global/#domain) parameter should be managed by cloudflare in order for this plugin to work + +The following section deals with configurations related to Cloudflare + +```toml +################################ +# CloudFlare Configuration # +################################ + +[cloudflare] +# API Token used for creating/updating Cloudflare's DNS records. +# This token must have the scopes ZONE:ZONE:READ, ZONE:ZONE:EDIT, ZONE:DNS:EDIT and ACCOUNT:ACCOUNT SETTINGS:READ. +api_token = "" +plugin = false # Use Cloudflare Plugin? +public_ip = "" # IPv4 address for Cloudflare's DNS records to point to. +``` + +You can generate a *Cloudflare API Token* from [here](https://dash.cloudflare.com/profile/api-tokens) and fill that value in the **api_token** field in the above configuration + +!!!warning + The generated token must have the permissions **ZONE:ZONE:READ**, **ZONE:ZONE:EDIT**, **ZONE:DNS:EDIT** and **ACCOUNT:ACCOUNT SETTINGS:READ** in order for this plugin to work + +The **public_ip** field in the above configuration should hold the public IPv4 address of an **GenProxy ⚡** instance or a **load balancer** pointing to multiple **GenProxy ⚡** instances + +!!!warning + If you wish to use the Cloudflare plugin in your cloud ecosystem then make sure that the above configuration is **same** across all **nodes** where **AppMaker 💧** is deployed diff --git a/demo/document-rag/documents/Gasper_content_configurations_dbmaker.md b/demo/document-rag/documents/Gasper_content_configurations_dbmaker.md new file mode 100644 index 0000000..b9971f2 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_dbmaker.md @@ -0,0 +1,92 @@ +# DbMaker Configuration + +DbMaker deals with creating and managing databases and their life-cycles + +The following section deals with the configuration of DbMaker + +```toml +############################# +# DbMaker Configuration # +############################# + +[services.dbmaker] +deploy = false # Deploy DbMaker? +port = 9000 + +# Hard Limits the total number of db instances that can be deployed by an user +# Set db_limit = -1 if no hard limit is to be imposed +db_limit= 10 +``` + +!!!warning + The node where **DbMaker** is to be deployed should have **Docker** installed and running + +## MySQL Configuration + +This section deals with the MySQL server configuration managed by DbMaker + +```toml +# Configuration for MySQL database server managed by `DbMaker` +[services.dbmaker.mysql] +plugin = false # Deploy MySQL server and let `DbMaker` manage it? +container_port = 33061 # Port on which the MySQL server container will run + +# Environment variables for MySQL docker container. +[services.dbmaker.mysql.env] +MYSQL_ROOT_PASSWORD = "YOUR_MYSQL_PASSWORD" # Root password of MySQL server inside the container +``` + +!!!info + The username of the deployed MySQL server will be **root** and the password will be the value of the variable **MYSQL_ROOT_PASSWORD** + +## MongoDB Configuration + +This section deals with the MongoDB server configuration managed by DbMaker + +```toml +# Configuration for MongoDB database server managed by `DbMaker` +[services.dbmaker.mongodb] +plugin = false # Deploy MongoDB server and let `DbMaker` manage it? +container_port = 27018 # Port on which the MongoDB server container will run + +# Environment variables for MongoDB docker container. +[services.dbmaker.mongodb.env] +MONGO_INITDB_ROOT_USERNAME = "YOUR_ROOT_NAME" # Root user of MongoDB server inside the container +MONGO_INITDB_ROOT_PASSWORD = "YOUR_ROOT_PASSWORD" # Root password of MongoDB server inside the container +``` + +!!!info + The username of the deployed MongoDB server will be the value of the variable **MONGO_INITDB_ROOT_USERNAME** and the password will be the value of the variable **MONGO_INITDB_ROOT_PASSWORD** + +## PostgreSQL Configuration + +This section deals with the PostgreSQL server configuration managed by DbMaker + +```toml +# Configuration for PostgreSQL database server managed by `DbMaker` +[services.dbmaker.postgresql] +plugin = false # Deploy PostgreSQL server and let `DbMaker` manage it? +container_port = 29121 # Port on which the PostgreSQL server container will run + +# Environment variables for PostgreSQL docker container. +[services.dbmaker.postgresql.env] +POSTGRES_USER = "YOUR_ROOT_NAME" # Root user of PostgreSQL server inside the container +POSTGRES_PASSWORD = "YOUR_ROOT_PASSWORD" # Root password of PostgreSQL server inside the container +``` + +!!!info + The username of the deployed PostgreSQL server will be the value of the variable **POSTGRES_USER** and the password will be the value of the variable **POSTGRES_PASSWORD** + +## Redis Configuration + +This section deals with the Redis server configuration managed by DbMaker + +```toml +# Configuration for Redis database server managed by `DbMaker` +[services.dbmaker.redis] +plugin = false # Deploy RedisDB server and let `DbMaker` manage it +``` + +!!!info + * For Redis due to the lack of namespaces a new container is created per user unlike others where one database is created per user in a single container + * The container name of the deployed Redis server will be the value of the variable **username** and the password will be the value of the variable **password** both of which are retrieved from the API request to the master service diff --git a/demo/document-rag/documents/Gasper_content_configurations_docker-images.md b/demo/document-rag/documents/Gasper_content_configurations_docker-images.md new file mode 100644 index 0000000..8fc6107 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_docker-images.md @@ -0,0 +1,27 @@ +# Docker Images Configuration + +The docker images used by Gasper for creating application containers and databases are defined in this section + +```toml +################################### +# Docker Images Configuration # +################################### + +[images] +static = "docker.io/sdslabs/static:latest" +php = "docker.io/sdslabs/php:latest" +nodejs = "docker.io/sdslabs/node:latest" +python2 = "docker.io/sdslabs/python2:latest" +python3 = "docker.io/sdslabs/python3:latest" +golang = "docker.io/sdslabs/golang:latest" +ruby = "docker.io/sdslabs/ruby:latest" +rust = "docker.io/sdslabs/rust:latest" +mysql = "docker.io/mysql:latest" +mongodb = "docker.io/sdslabs/alpine-mongo:latest" +postgresql = "docker.io/postgres:latest" +redis = "docker.io/redis:6.0-rc3-alpine3.11" +``` + +You can replace the above default images and plug in your own docker images but make sure that each image has a **blocking CMD call** at the end of its corresponding dockerfile such as **CMD tail -f /dev/null** + +For reference, you can check out the [dockerfiles](https://github.com/sdslabs/gasper-dockerfiles) for the default images used by Gasper diff --git a/demo/document-rag/documents/Gasper_content_configurations_gendns.md b/demo/document-rag/documents/Gasper_content_configurations_gendns.md new file mode 100644 index 0000000..88c8b2a --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_gendns.md @@ -0,0 +1,36 @@ +# GenDNS Configuration + +GenDNS deals with creating and managing DNS records of all deployed applications and databases + +All application DNS records point to the IPv4 addresses of GenProxy ⚡ instances which in turn reverse-proxies the request to the desired application's IPv4 address and port + +All database DNS records point to the IPv4 address of the node where the database's server is deployed + +!!!info + **GenDNS 💡** automatically creates a DNS entry for **Master 🌪** (if deployed) pointing to an **GenProxy ⚡** instance which will be further load-balanced among all available **Master 🌪** instances + + The created DNS entry will be based on the [domain](/configurations/global/#domain) parameter + + !!!example + If the [domain](/configurations/global/#domain) parameter is set to `sdslabs.co` then the corresponding DNS entry `master.sdslabs.co` will be created by **GenDNS 💡** + +The following section deals with the configuration of GenDNS + +```toml +############################ +# GenDNS Configuration # +############################ + +[services.gendns] +# Time Interval (in seconds) in which `GenDNS` updates its +# `DNS Record Storage` by polling the central registry-server. +record_update_interval = 15 +deploy = false # Deploy GenDNS? +port = 53 +``` + +!!!tip + You can reduce the value of **record_update_interval** parameter in the above configuration if you need changes in your ecosystem to propagate faster but this will in turn increase the load on the Redis central registry server so *choose wisely* + +!!!warning + **GenDNS** usually runs on port 53, hence the Gasper binary must be executed with **root** privileges in Linux systems diff --git a/demo/document-rag/documents/Gasper_content_configurations_genproxy.md b/demo/document-rag/documents/Gasper_content_configurations_genproxy.md new file mode 100644 index 0000000..139600f --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_genproxy.md @@ -0,0 +1,49 @@ +# GenProxy Configuration + +GenProxy service deals with reverse-proxying HTTP, HTTPS, HTTP/2, Websocket and gRPC requests to the desired application's IPv4 address and port based on the hostname + +!!!info + **GenProxy ⚡** automatically creates a reverse-proxy entry for **Master 🌪** (if deployed) pointing to its IPv4 address and port + +## Default +The following section deals with the configuration of GenProxy + +```toml +############################## +# GenProxy Configuration # +############################## + +[services.genproxy] +# Time Interval (in seconds) in which `GenProxy` updates its +# `Reverse-Proxy Record Storage` by polling the central registry-server. +record_update_interval = 15 +deploy = false # Deploy GenProxy? +port = 80 +``` + +!!!tip + You can reduce the value of **record_update_interval** parameter in the above configuration if you need changes in your ecosystem to propagate faster but this will in turn increase the load on the Redis central registry server so *choose wisely* + +!!!warning + **GenProxy** usually runs on port 80, hence the Gasper binary must be executed with **root** privileges in Linux systems + +## GenProxy with SSL + +The following section deals with configuring GenProxy with SSL support for HTTPS + +```toml +# Configuration for using SSL with `GenProxy`. +[services.genproxy.ssl] +plugin = false # Use SSL with GenProxy? +port = 443 +certificate = "/home/user/fullchain.pem" # Certificate Location +private_key = "/home/user/privkey.pem" # Private Key Location +``` + +The **certificate** and **private key** in the above configuration should be configured for all sub-domains based on the [domain parameter](/configurations/global/#domain) in the configuration file + +!!!example "Configuration Example" + If the [domain](/configurations/global/#domain) parameter is `sdslabs.co` then the certificate and private key should be configured for the following subdomains `*.sdslabs.co` and `*.*.sdslabs.co` + +!!!warning + **GenProxy with SSL** usually runs on port 443, hence the Gasper binary must be executed with **root** privileges in Linux systems diff --git a/demo/document-rag/documents/Gasper_content_configurations_genssh.md b/demo/document-rag/documents/Gasper_content_configurations_genssh.md new file mode 100644 index 0000000..59da40a --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_genssh.md @@ -0,0 +1,41 @@ +# GenSSH Configuration + +GenSSH service provides [SSH](https://www.ssh.com/ssh/protocol/) access directly to an application's docker container to the end user + +The SSH command will be automatically returned to the user on application creation provided the node where the application is deployed has the GenSSH service deployed + +The following section deals with the configuration of GenSSH + +```toml +############################ +# GenSSH Configuration # +############################ + +[services.genssh] +deploy = false # Deploy GenSSH? +port = 2222 + +# Location of Private Key for creating the SSH Signer. +host_signers = ["/home/user/.ssh/id_rsa"] +using_passphrase = false # Private Key is passphrase protected? +passphrase = "" # Passphrase (if any) for decrypting the Private Key + +# IP address to establish a SSH connection to. +# Equal to the current node's IP address if left blank. +# This field is only for information of the client who will create applications +# and this field's value will not affect GenSSH's functioning in any manner. +# To be used when the current node is only accessible by a jump host or +# behind some network forwarding rule or proxy setup. +entrypoint_ip = "" +``` + +The **host_signers** field stores the location of your private key + +!!!note + If your private key is passphrase protected then set the **using_passphrase** field to `true` and insert your passphrase as the value of the **passphrase** field + +!!!info + The password required for SSH access is provided by the user during application creation + +!!!bug "Compatibility Issues" + **GenSSH 🗿** is not compatible with [Windows](https://www.microsoft.com/en-in/windows), hence its deployment will be skipped on Windows systems \ No newline at end of file diff --git a/demo/document-rag/documents/Gasper_content_configurations_global.md b/demo/document-rag/documents/Gasper_content_configurations_global.md new file mode 100644 index 0000000..0499b50 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_global.md @@ -0,0 +1,151 @@ +# Global Configuration + +This section of Gasper's configuration file deals with global settings + +Here is the entire section of the configuration file dealing with global settings and we will go through each of them one by one + +```toml +############################ +# Global Configuration # +############################ + +# Run Gasper in Debug mode ? +# Set this value to `false` in Production. +debug = true + +# Root domain for all deployed applications and databases. +domain = "sdslabs.co" + +# Secret Key used for internal communication in the Gasper ecosystem. +secret = "YOUR_SECRET_KEY" + +# Root of the deployed application in the docker container's filesystem. +project_root = "/gasper" + +# Name of the file used for building and running applications. +# This file is application specific and must be present in an application's git repository's root. +# The contents of the file must be linux shell commands separated by newlines. +rc_file = "Gasperfile.txt" + +# Run Gasper in Offline mode. +# For Development purposes only. +offline_mode = false + +# DNS nameservers used by docker containers created by Gasper. +dns_servers = [ + "8.8.8.8", + "8.8.4.4", +] +``` + +## Debug Mode + +```toml +# Run Gasper in Debug mode ? +# Set this value to `false` in Production. +debug = true +``` + +The variable **debug** determines whether to run Gasper in debug mode or not +In debug mode, internal server error messages are returned to the end user as JSON responses + +!!!tip + Set **debug** to `false` in Production + +## Domain + +```toml +# Root domain for all deployed applications and databases. +domain = "sdslabs.co" +``` + +This section determines the root domain of all deployed applications and databases + +The corresponding DNS entries for applications and databases will be automatically created by **GenDNS 💡** + +!!! example "DNS entry example for an application" + If you create an application named **foo** then a DNS entry of `foo-app-gasper.sdslabs.co` will be created (based on the above root domain setting) pointing to the IPv4 address of an **GenProxy ⚡** instance which in turn will reverse-proxy the request to the application's IPv4 address and port + +!!! example "DNS entry example for a database" + If you create a database named **bar** then a DNS entry of `bar-db-gasper.sdslabs.co` will be created (based on the above root domain setting) pointing to the IPv4 address of the node where the database's server is deployed + +## Secret Key + +```toml +# Secret Key used for internal communication in the Gasper ecosystem. +secret = "YOUR_SECRET_KEY" +``` + +Secret Key is used to encrypt the internal communications between **Master** 🌪 , **AppMaker** 💧 and **DbMaker** 🔥 + +!!!tip + We recommend setting a strong **secret key** for securing your cloud ecosystem + +!!!warning + Make sure that the **secret key** is the same across all Gasper instances in your entire cloud ecosystem + +## Project Root + +```toml +# Root of the deployed application in the docker container's filesystem. +project_root = "/gasper" +``` + +All applications deployed by Gasper run within docker containers + +**project_root** variable defines the root directory in the docker container's filesystem inside which the application's directory will be placed + +## Run Commands File + +```toml +# Name of the file used for building and running applications. +# This file is application specific and must be present in an application's git repository's root. +# The contents of the file must be linux shell commands separated by newlines. +rc_file = "Gasperfile.txt" +``` + +The Run Commands File or **rc_file** is the name of the file containing linux shell commands for building and running an application + +This file must be present in an application's git repository's root directory + +!!!info + A user can deploy an application by either supplying the `build and run commands` in the request payload or by using this Run Commands File + +???example "Usage" + If the above **rc_file** parameter changes from `Gasperfile.txt` to `Alphadose`, then Gasper will look for a file named `Alphadose` in the application's git repository root during its deployment and will execute all commands present inside it + +???example "Sample Run Commands File" + For a [sample nodejs application](https://github.com/sdslabs/node), here is the corresponding run commands file [https://github.com/sdslabs/node/blob/master/Gasperfile.txt](https://github.com/sdslabs/node/blob/master/Gasperfile.txt) + + +## Offline Mode + +```toml +# Run Gasper in Offline mode. +# For Development purposes only. +offline_mode = false +``` + +Gasper requires network connectivity for booting but with this parameter Gasper can run without it + +Used for development purposes when the developer doesn't have an internet connectivity + +!!!danger + This functionality should be used strictly for development purposes + +## DNS Nameservers + +```toml +# DNS nameservers used by docker containers created by Gasper. +dns_servers = [ + "8.8.8.8", + "8.8.4.4", +] +``` + +This field defines the DNS Nameservers that would be used inside all deployed application's docker containers for domain name resolution + +Change it according to your network infrastructure if required + +!!!info + By default Google's nameservers are used diff --git a/demo/document-rag/documents/Gasper_content_configurations_jwt.md b/demo/document-rag/documents/Gasper_content_configurations_jwt.md new file mode 100644 index 0000000..50659a2 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_jwt.md @@ -0,0 +1,29 @@ +# JWT Configuration + +This is used to configure the timeout and refresh time for the authentication token + +Both values `timeout` and `max_refresh` are defined in the **jwt** section of the configuration file + +`timeout` is the interval after which the client needs to request for a new token which can be done by either logging in again or obtaining the token through refresh route (`GET /auth/refresh`) + +`max_refresh` is the time interval after which user is logged out and the token can only be obtained by logging in again + +```toml +######################### +# JWT Configuration # +######################### + +# Configuration for the JSON Web Token (JWT) authentication mechanism. +[jwt] + +# timeout refers to the duration in which the JWT is valid. +# max_refresh refers to the duration in which the JWT can be refreshed after its expiry. + +# Both timeout and max_refresh are in seconds +# Total refresh time = max_refresh + timeout +timeout = 3600 # 1 hour +max_refresh = 2419200 # 28 days +``` + +!!!info + The above section only needs to be configured for the nodes where **Master 🌪** is to be deployed diff --git a/demo/document-rag/documents/Gasper_content_configurations_master.md b/demo/document-rag/documents/Gasper_content_configurations_master.md new file mode 100644 index 0000000..af80c60 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_master.md @@ -0,0 +1,46 @@ +# Master Configuration + +Master is the master of the entire Gasper ecosystem which performs the following tasks + +* Equal distribution of applications and databases among worker nodes +* User Authentication based on JWT (JSON Web Token) +* User API for performing operations on any application/database in any node (Identity Access Management is handled with JWT) +* Admin API for fetching and managing information of all nodes, applications, databases and users +* Removal of inactive nodes from the cloud ecosystem +* Re-scheduling of applications in case of node failure + +Master API docs are available [here](/api) + +The following section deals with the configuration of Master + +```toml +############################ +# Master Configuration # +############################ + +[services.master] +# Time Interval (in seconds) in which `Master` sends health-check probes +# to all worker nodes and removes inactive nodes from the central registry-server. +cleanup_interval = 600 +deploy = true # Deploy Master? +port = 3000 + +# Configuration for the MongoDB service container required by all deployed services. +[services.master.mongodb] +plugin = true # Deploy MongoDB server and let `Master` manage it? +container_port = 27019 # Port on which the MongoDB server container will run + +# Environment variables for MongoDB docker container. +[services.master.mongodb.env] +MONGO_INITDB_ROOT_USERNAME = "alphadose" # Root user of MongoDB server inside the container +MONGO_INITDB_ROOT_PASSWORD = "alphadose" # Root password of MongoDB server inside the container + +# Configuration for the Redis service container required by all deployed services. +[services.master.redis] +plugin = true # Deploy Redis server and let `Master` manage it? +container_port = 6380 # Port on which the Redis server container will run +password = "alphadose" +``` + +!!!tip + You can reduce the value of **cleanup_interval** parameter in the above configuration if you need changes in your ecosystem to propagate faster but this will in turn increase the load on the Redis central registry server so *choose wisely* diff --git a/demo/document-rag/documents/Gasper_content_configurations_mongodb.md b/demo/document-rag/documents/Gasper_content_configurations_mongodb.md new file mode 100644 index 0000000..136a660 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_mongodb.md @@ -0,0 +1,19 @@ +# MongoDB Configuration + +Gasper uses [MongoDB](https://www.mongodb.com/) for storing data pertaining to users, applications and databases + +The following section in the configuration file deals with MongoDB + +```toml +############################# +# MongoDB Configuration # +############################# + +[mongo] +# For databases with authentication +# use the following URL format `mongodb://username:password@host:port`. +url = "mongodb://alphadose:alphadose@localhost:27019/?authSource=admin" +``` + +!!!warning + There should be only a single instance of MongoDB running in your entire cloud ecosystem and all instances of Gasper should connect only to that single MongoDB instance i.e the above configuration must be **same** across all Gasper instances in all nodes diff --git a/demo/document-rag/documents/Gasper_content_configurations_overview.md b/demo/document-rag/documents/Gasper_content_configurations_overview.md new file mode 100644 index 0000000..4af6de5 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_overview.md @@ -0,0 +1,314 @@ +# Overview + +This section of documentation deals with configuring Gasper to suit your needs + +All configurations are present in a file named **config.toml** which is packaged with every [release](https://github.com/sdslabs/gasper/releases) + +## Sample +Here is a [sample config.toml](https://github.com/sdslabs/gasper/blob/develop/config.sample.toml) and we are going to discuss each and every section of this file in detail in the next parts + +```toml +# Configuration sample for Gasper + +############################ +# Global Configuration # +############################ + +# Run Gasper in Debug mode ? +# Set this value to `false` in Production. +debug = true + +# Root domain for all deployed applications and databases. +domain = "sdslabs.co" + +# Secret Key used for internal communication in the Gasper ecosystem. +secret = "YOUR_SECRET_KEY" + +# Root of the deployed application in the docker container's filesystem. +project_root = "/gasper" + +# Name of the file used for building and running applications. +# This file is application specific and must be present in an application's git repository's root. +# The contents of the file must be linux shell commands separated by newlines. +rc_file = "Gasperfile.txt" + +# Run Gasper in Offline mode. +# For Development purposes only. +offline_mode = false + +# DNS nameservers used by docker containers created by Gasper. +dns_servers = [ + "8.8.8.8", + "8.8.4.4", +] + + +########################### +# Admin Configuration # +########################### + +# Default admin credentials for the Gasper ecosystem. +[admin] +email = "anish.mukherjee1996@gmail.com" +username = "alphadose" +password = "alphadose" + + +############################# +# MongoDB Configuration # +############################# + +[mongo] +# For databases with authentication +# use the following URL format `mongodb://username:password@host:port/?authSource=admin`. +url = "mongodb://alphadose:alphadose@localhost:27019/?authSource=admin" + + +########################### +# Redis Configuration # +########################### + +# Acts as a central-registry for the Gasper ecosystem. +[redis] +host = "localhost" +port = 6380 +password = "alphadose" +db = 0 + + +######################### +# JWT Configuration # +######################### + +# Configuration for the JSON Web Token (JWT) authentication mechanism. +[jwt] + +# timeout refers to the duration in which the JWT is valid. +# max_refresh refers to the duration in which the JWT can be refreshed after its expiry. + +# Both timeout and max_refresh are in seconds +# Total refresh time = max_refresh + timeout +timeout = 3600 # 1 hour +max_refresh = 2419200 # 28 days + + +################################ +# CloudFlare Configuration # +################################ + +[cloudflare] +# API Token used for creating/updating Cloudflare's DNS records. +# This token must have the scopes ZONE:ZONE:READ, ZONE:ZONE:EDIT, ZONE:DNS:EDIT and ACCOUNT:ACCOUNT SETTINGS:READ. +api_token = "" +plugin = false # Use Cloudflare Plugin? +public_ip = "" # IPv4 address for Cloudflare's DNS records to point to. + + +################################### +# Docker Images Configuration # +################################### + +[images] +static = "docker.io/sdslabs/static:latest" +php = "docker.io/sdslabs/php:latest" +nodejs = "docker.io/sdslabs/node:latest" +python2 = "docker.io/sdslabs/python2:latest" +python3 = "docker.io/sdslabs/python3:latest" +golang = "docker.io/sdslabs/golang:latest" +ruby = "docker.io/sdslabs/ruby:latest" +rust = "docker.io/sdslabs/rust:latest" +mysql = "docker.io/mysql:latest" +mongodb = "docker.io/sdslabs/alpine-mongo:latest" +postgresql = "docker.io/postgres:latest" +redis = "docker.io/redis:6.0-rc3-alpine3.11" + + +############################## +# Services Configuration # +############################## + +# Configuration for the various microservices comprising the Gasper ecosystem. +[services] + +# Time Interval (in seconds) in which the current node updates +# the central registry-server with the status of its microservices. +exposure_interval = 30 + +# Rate limit of deploying number of app/DB per unit time +# Set rate_limit = -1 if no rate limit is to be imposed +rate_limit = 2 + +# Time interval (in hours) for rate limiting for App/DB creation +rate_interval = 24 + + +############################ +# Master Configuration # +############################ + +[services.master] +# Time Interval (in seconds) in which `Master` sends health-check probes +# to all worker nodes and removes inactive nodes from the central registry-server. +cleanup_interval = 600 +deploy = true # Deploy Master? +port = 3000 + +# Configuration for the MongoDB service container required by all deployed services. +[services.master.mongodb] +plugin = true # Deploy MongoDB server and let `Master` manage it? +container_port = 27019 # Port on which the MongoDB server container will run + +# Environment variables for MongoDB docker container. +[services.master.mongodb.env] +MONGO_INITDB_ROOT_USERNAME = "alphadose" # Root user of MongoDB server inside the container +MONGO_INITDB_ROOT_PASSWORD = "alphadose" # Root password of MongoDB server inside the container + +# Configuration for the Redis service container required by all deployed services. +[services.master.redis] +plugin = true # Deploy Redis server and let `Master` manage it? +container_port = 6380 # Port on which the Redis server container will run +password = "alphadose" + + +############################## +# GenProxy Configuration # +############################## + +[services.genproxy] +# Time Interval (in seconds) in which `GenProxy` updates its +# `Reverse-Proxy Record Storage` by polling the central registry-server. +record_update_interval = 15 +deploy = false # Deploy GenProxy? +port = 80 + +# Configuration for using SSL with `GenProxy`. +[services.genproxy.ssl] +plugin = false # Use SSL with GenProxy? +port = 443 +certificate = "/home/user/fullchain.pem" # Certificate Location +private_key = "/home/user/privkey.pem" # Private Key Location + + +############################## +# AppMaker Configuration # +############################## + +[services.appmaker] +deploy = true # Deploy AppMaker? +port = 4000 + +# Time Interval (in seconds) in which metrics of all application containers +# running in the current node are collected and stored in the central mongoDB database +metrics_interval = 600 + +# Time Interval (in seconds) in which health is checked of all application containers and if unhealthy, they are restarted +health_interval = 300 + +# Hard Limits the total number of app instances that can be deployed by an user +# Set app_limit = -1 if no hard limit is to be imposed +app_limit = 10 + +# Specifies the maximum CPU allocation for a container created by a non admin user +max_container_cpu = 0.25 + +# Specifies the maximum memory allocation for a container created by a non admin user +max_container_memory = 0.5 + + +############################# +# DbMaker Configuration # +############################# + +[services.dbmaker] +deploy = false # Deploy DbMaker? +port = 9000 + +# Hard Limits the total number of db instances that can be deployed by an user +# Set db_limit = -1 if no hard limit is to be imposed +db_limit= 10 + +# Configuration for MySQL database server managed by `DbMaker` +[services.dbmaker.mysql] +plugin = false # Deploy MySQL server and let `DbMaker` manage it? +container_port = 33061 # Port on which the MySQL server container will run + +# Environment variables for MySQL docker container. +[services.dbmaker.mysql.env] +MYSQL_ROOT_PASSWORD = "YOUR_MYSQL_PASSWORD" # Root password of MySQL server inside the container + +# Configuration for PostgreSQL database server managed by `DbMaker` +[services.dbmaker.postgresql] +plugin = false # Deploy PostgreSQL server and let `DbMaker` manage it? +container_port = 29121 # Port on which the PostgreSQL server container will run + +# Environment variables for PostgreSQL docker container. +[services.dbmaker.postgresql.env] +POSTGRES_USER = "YOUR_ROOT_NAME" # Root user of PostgreSQL server inside the container +POSTGRES_PASSWORD = "YOUR_ROOT_PASSWORD" # Root password of PostgreSQL server inside the container + +# Configuration for MongoDB database server managed by `DbMaker` +[services.dbmaker.mongodb] +plugin = false # Deploy MongoDB server and let `DbMaker` manage it +container_port = 27018 # Port on which the MongoDB server container will run + +# Environment variables for MongoDB docker container. +[services.dbmaker.mongodb.env] +MONGO_INITDB_ROOT_USERNAME = "YOUR_ROOT_NAME" # Root user of MongoDB server inside the container +MONGO_INITDB_ROOT_PASSWORD = "YOUR_ROOT_PASSWORD" # Root password of MongoDB server inside the container + +# Configuration for Redis database server managed by `DbMaker` +[services.dbmaker.redis] +plugin = false # Deploy RedisDB server and let `DbMaker` manage it + + +############################ +# GenDNS Configuration # +############################ + +[services.gendns] +# Time Interval (in seconds) in which `GenDNS` updates its +# `DNS Record Storage` by polling the central registry-server. +record_update_interval = 15 +deploy = false # Deploy GenDNS? +port = 53 + + +############################ +# GenSSH Configuration # +############################ + +[services.genssh] +deploy = false # Deploy GenSSH? +port = 2222 + +# Location of Private Key for creating the SSH Signer. +host_signers = ["/home/user/.ssh/id_rsa"] +using_passphrase = false # Private Key is passphrase protected? +passphrase = "" # Passphrase (if any) for decrypting the Private Key + +# IP address to establish a SSH connection to. +# Equal to the current node's IP address if left blank. +# This field is only for information of the client who will create applications +# and this field's value will not affect GenSSH's functioning in any manner. +# To be used when the current node is only accessible by a jump host or +# behind some network forwarding rule or proxy setup. +entrypoint_ip = "" + + +########################### +# Jikan Configuration # +########################### + +[services.jikan] +deploy = false # Deploy Jikan? +port = 3333 + +############################ +# Github Configuration # +############################ + +[github] +username = "gasper-github-username" +email = "gasper-mail-id" +pat = "personal-access-token" +``` diff --git a/demo/document-rag/documents/Gasper_content_configurations_redis.md b/demo/document-rag/documents/Gasper_content_configurations_redis.md new file mode 100644 index 0000000..b7a7407 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_configurations_redis.md @@ -0,0 +1,21 @@ +# Redis Configuration + +Gasper uses [Redis](https://redis.io/) as a central registry server + +It is used for storing the addresses of all active components, nodes, applications and databases + +```toml +########################### +# Redis Configuration # +########################### + +# Acts as a central-registry for the Gasper ecosystem +[redis] +host = "localhost" +port = 6379 +password = "alphadose" +db = 0 +``` + +!!!warning + There should be only a single instance of Redis running in your entire cloud ecosystem and all instances of Gasper should connect only to that single Redis instance i.e the above configuration must be **same** across all Gasper instances in all nodes diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_advanced-php.md b/demo/document-rag/documents/Gasper_content_examples_applications_advanced-php.md new file mode 100644 index 0000000..9937c7a --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_advanced-php.md @@ -0,0 +1,136 @@ +# Deploying an Advanced PHP Application + +This example shows how to deploy a PHP application which uses [composer](https://getcomposer.org/) for managing dependencies + +Lets use an [advanced PHP application](https://github.com/alphadose/MVC-Project) for demonstration + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + + +## Deploy using Build and Run Commands + +```bash +$ curl -X POST \ + http://localhost:3000/apps/php \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"advancedphp", +"password":"advancedphp", +"git": { + "repo_url": "https://github.com/alphadose/MVC-Project", + "branch":"master" +}, +"context":{ + "index":"public/index.php", + "build": ["composer install"], + "port": 80 +} +}' + +{ + "name": "advancedphp", + "password": "advancedphp", + "git": { + "repo_url": "https://github.com/alphadose/MVC-Project", + "branch": "master" + }, + "context": { + "index": "public/index.php", + "port": 80, + "rc_file": false, + "build": [ + "composer install" + ] + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/php:latest", + "container_id": "f37749b727988833dda70714539ee1ce7f167abe66d78300553f6843a8af39e2", + "container_port": 50475, + "language": "php", + "instance_type": "application", + "app_url": "advancedphp-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 advancedphp@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:50475` + +## Deploy using [Run Commands File](/configurations/global/#run-commands-file) + +Have a look at the [run commands file](https://github.com/alphadose/MVC-Project/blob/master/Gasperfile.txt) for the above [sample application](https://github.com/alphadose/MVC-Project) + +```bash +$ curl -X POST \ + http://localhost:3000/apps/php \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"advancedphp", +"password":"advancedphp", +"git": { + "repo_url": "https://github.com/alphadose/MVC-Project", + "branch":"master" +}, +"context":{ + "index":"public/index.php", + "rc_file": true +} +}' + +{ + "name": "advancedphp", + "password": "advancedphp", + "git": { + "repo_url": "https://github.com/alphadose/MVC-Project", + "branch": "master" + }, + "context": { + "index": "public/index.php", + "port": 80, + "rc_file": true + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/php:latest", + "container_id": "d4a54b0800eb8e8bbcea007275746180e5c193b23fc0e1f4f184abf9b984165b", + "container_port": 51223, + "language": "php", + "instance_type": "application", + "app_url": "advancedphp-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 advancedphp@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:51223` diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_golang.md b/demo/document-rag/documents/Gasper_content_examples_applications_golang.md new file mode 100644 index 0000000..29bd88a --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_golang.md @@ -0,0 +1,136 @@ +# Deploying a Golang Application + +This example shows how to deploy a [golang](https://golang.org/) application + +Lets use a [sample application](https://github.com/sdslabs/gasper-sample-golang) for demonstration which runs on **port 8000** + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + + +## Deploy using Build and Run Commands + +```bash +$ curl -X POST \ + http://localhost:3000/apps/golang \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"samplego", +"password":"samplego", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-golang", + "branch":"master" +}, +"context":{ + "index":"main.go", + "port": 8000, + "run": ["go run main.go"] +} +}' + +{ + "name": "samplego", + "password": "samplego", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-golang" + }, + "context": { + "index": "main.go", + "port": 8000, + "rc_file": false, + "run": [ + "go run main.go" + ] + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/golang:latest", + "container_id": "e0d2b784cab9c6cc4c360c81953502d757447a70a7d84bc944cc05819d2ee818", + "container_port": 55147, + "language": "golang", + "instance_type": "application", + "app_url": "samplego-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 samplego@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:55147` + +## Deploy using [Run Commands File](/configurations/global/#run-commands-file) + +Have a look at the [run commands file](https://github.com/sdslabs/gasper-sample-golang/blob/master/Gasperfile.txt) for the above [sample application](https://github.com/sdslabs/gasper-sample-golang) + +```bash +$ curl -X POST \ + http://localhost:3000/apps/golang \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"samplego", +"password":"samplego", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-golang", + "branch":"master" +}, +"context":{ + "index":"main.go", + "port": 8000, + "rc_file": true +} +}' + +{ + "name": "samplego", + "password": "samplego", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-golang", + "branch": "master" + }, + "context": { + "index": "main.go", + "port": 8000, + "rc_file": true + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/golang:latest", + "container_id": "0c4b1ec05fe65fcb0b3ef168244d38ae9fab4d0bc22e2e0d5a39badeedce31e7", + "container_port": 55229, + "language": "golang", + "instance_type": "application", + "app_url": "samplego-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 samplego@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:55229` diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_nodejs-with-db.md b/demo/document-rag/documents/Gasper_content_examples_applications_nodejs-with-db.md new file mode 100644 index 0000000..8579aca --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_nodejs-with-db.md @@ -0,0 +1,166 @@ +# Deploying a Node.js Application with MySQL database + +This example shows how to deploy a [node.js](https://nodejs.org/en/) application [MySQL](https://www.mysql.com/) database via Gasper + +Lets use a [sample application](https://github.com/sdslabs/gasper-sample-nodejs-db) for demonstration which runs on **port 3005** + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/), [AppMaker](/configurations/appmaker/) and [DbMaker](/configurations/dbmaker/) up and running + * You have [DbMaker MySQL Plugin](/configurations/dbmaker/#mysql-configuration) enabled + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + + +## Create a MySQL database via Gasper + +```bash +$ curl -X POST \ + http://localhost:3000/dbs/mysql \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "nodetestapp", + "password": "nodetestapp" +}' + +{ + "name": "nodetestapp", + "password": "nodetestapp", + "user": "nodetestapp", + "instance_type": "database", + "language": "mysql", + "host_ip": "10.43.3.24", + "port": 33061, + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Now, change the host and port in config file of + +## Deploy using Build and Run Commands + +Note: Here host and port should be the host ip and port of the db you just hosted. + +```bash +$ curl -X POST \ + http://localhost:3000/apps/nodejs \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"samplenode", +"password":"samplenode", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-nodejs-db" +}, +"context":{ + "index":"index.js", + "port": 3005, + "build": ["npm install"], + "run": ["node index.js"] +}, +"env": ["DB_HOST":"10.43.3.24", "DB_PORT":"33061"] +}' + +{ + "name": "samplenode", + "password": "samplenode", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-nodejs-db" + }, + "context": { + "index": "index.js", + "port": 3005, + "rc_file": false, + "build": [ + "npm install" + ], + "run": [ + "node index.js" + ] + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdsws/node:2.1", + "container_id": "dc04aea7dbef287b5bfa597120773c4ff5b5309d3a39235055ff80e9ffbee00f", + "container_port": 51952, + "language": "nodejs", + "instance_type": "application", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 samplenode@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:51952` + +## Deploy using [Run Commands File](/configurations/global/#run-commands-file) + +Have a look at the [run commands file](https://github.com/sdslabs/gasper-sample-nodejs/blob/master/Gasperfile.txt) for the above [sample application](https://github.com/sdslabs/gasper-sample-nodejs) + +```bash +$ curl -X POST \ + http://localhost:3000/apps/nodejs \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"samplenode", +"password":"samplenode", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-nodejs-db" +}, +"context":{ + "index":"index.js", + "port": 3005, + "rc_file": true +} +}' + +{ + "name": "samplenode", + "password": "samplenode", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-nodejs-db" + }, + "context": { + "index": "index.js", + "port": 3005, + "rc_file": true + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdsws/node:2.1", + "container_id": "5e025f17c9d5c11f93609f7d019b3efbdc44ccef598a6e6564973da895e5e366", + "container_port": 51720, + "language": "nodejs", + "instance_type": "application", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 samplenode1@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:51720` diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_nodejs.md b/demo/document-rag/documents/Gasper_content_examples_applications_nodejs.md new file mode 100644 index 0000000..6cedc61 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_nodejs.md @@ -0,0 +1,140 @@ +# Deploying a Node.js Application + +This example shows how to deploy a [node.js](https://nodejs.org/en/) application + +Lets use a [sample application](https://github.com/sdslabs/gasper-sample-nodejs) for demonstration which runs on **port 3000** + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + + +## Deploy using Build and Run Commands + +```bash +$ curl -X POST \ + http://localhost:3000/apps/nodejs \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"samplenode", +"password":"samplenode", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-nodejs", + "branch":"master" +}, +"context":{ + "index":"main.js", + "port": 3000, + "build": ["npm install"], + "run": ["node main.js"] +} +}' + +{ + "name": "samplenode", + "password": "samplenode", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-nodejs" + }, + "context": { + "index": "main.js", + "port": 3000, + "rc_file": false, + "build": [ + "npm install" + ], + "run": [ + "node main.js" + ] + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/node:latest", + "container_id": "dc04aea7dbef287b5bfa597120773c4ff5b5309d3a39235055ff80e9ffbee00f", + "container_port": 51952, + "language": "nodejs", + "instance_type": "application", + "app_url": "samplenode-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 samplenode@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:51952` + +## Deploy using [Run Commands File](/configurations/global/#run-commands-file) + +Have a look at the [run commands file](https://github.com/sdslabs/gasper-sample-nodejs/blob/master/Gasperfile.txt) for the above [sample application](https://github.com/sdslabs/gasper-sample-nodejs) + +```bash +$ curl -X POST \ + http://localhost:3000/apps/nodejs \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"samplenode", +"password":"samplenode", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-nodejs", + "branch":"master" +}, +"context":{ + "index":"main.js", + "port": 3000, + "rc_file": true +} +}' + +{ + "name": "samplenode", + "password": "samplenode", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-nodejs", + "branch": "master" + }, + "context": { + "index": "main.js", + "port": 3000, + "rc_file": true + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/node:latest", + "container_id": "5e025f17c9d5c11f93609f7d019b3efbdc44ccef598a6e6564973da895e5e366", + "container_port": 51720, + "language": "nodejs", + "instance_type": "application", + "app_url": "samplenode-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 samplenode1@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:51720` diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_python-django.md b/demo/document-rag/documents/Gasper_content_examples_applications_python-django.md new file mode 100644 index 0000000..14223b6 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_python-django.md @@ -0,0 +1,144 @@ +# Deploying a Python Django Application + +This example shows how to deploy a [python django](https://www.djangoproject.com/) application + +Lets use a [sample application](https://github.com/sdslabs/gasper-sample-django) for demonstration which runs on **port 8000** + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + + +## Deploy using Build and Run Commands + +```bash +$ curl -X POST \ + http://localhost:3000/apps/python3 \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"sampledjango", +"password":"sampledjango", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-django", + "branch":"master" +}, +"context":{ + "index":"todo/manage.py", + "port": 8000, + "build": ["pip install -r requirements.txt", "python todo/manage.py migrate"], + "run": ["python todo/manage.py runserver 0.0.0.0:8000"] +} +}' + +{ + "name": "sampledjango", + "password": "sampledjango", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-django" + }, + "context": { + "index": "todo/manage.py", + "port": 8000, + "rc_file": false, + "build": [ + "pip install -r requirements.txt", + "python todo/manage.py migrate" + ], + "run": [ + "python todo/manage.py runserver 0.0.0.0:8000" + ] + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/python3:latest", + "container_id": "8f2a04bb54f0b90a911b05d3fb1ae73ff240c2e5a5093609d393f7c426de4755", + "container_port": 53358, + "language": "python3", + "instance_type": "application", + "app_url": "sampledjango-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 sampledjango@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:53358` + +## Deploy using [Run Commands File](/configurations/global/#run-commands-file) + +Have a look at the [run commands file](https://github.com/sdslabs/gasper-sample-django/blob/master/Gasperfile.txt) for the above [sample application](https://github.com/sdslabs/gasper-sample-django) + +```bash +$ curl -X POST \ + http://localhost:3000/apps/python3 \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"sampledjango2", +"password":"sampledjango2", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-django", + "branch":"master" +}, +"context":{ + "index":"todo/manage.py", + "port": 8000, + "rc_file": true +} +}' + +{ + "name": "sampledjango", + "password": "sampledjango", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-django", + "branch": "master" + }, + "context": { + "index": "todo/manage.py", + "port": 8000, + "rc_file": true + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/python3:latest", + "container_id": "48ada540a6296b184470eb192e4b543f195ab5f67615ec12318d3e8d01e05edf", + "container_port": 53672, + "language": "python3", + "instance_type": "application", + "app_url": "sampledjango-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 sampledjango@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:53672` + +!!!info + HTTP request to the above URL endpoint `localhost:3000/apps/python3` runs the application in a **Python 3** environment. If you want to run your application in a **Python 2** environment then change the URL endpoint to `localhost:3000/apps/python2` diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_python-flask.md b/demo/document-rag/documents/Gasper_content_examples_applications_python-flask.md new file mode 100644 index 0000000..832d166 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_python-flask.md @@ -0,0 +1,143 @@ +# Deploying a Python Flask Application + +This example shows how to deploy a [python flask](https://www.palletsprojects.com/p/flask/) application + +Lets use a [sample application](https://github.com/sdslabs/gasper-sample-flask) for demonstration which runs on **port 5000** + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + + +## Deploy using Build and Run Commands + +```bash +$ curl -X POST \ + http://localhost:3000/apps/python3 \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"sampleflask", +"password":"sampleflask", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-flask", + "branch":"master" +}, +"context":{ + "index":"run.py", + "port": 5000, + "build": ["pip install -r requirements.txt"], + "run": ["flask run --host=0.0.0.0 --port=5000"] +} +}' + +{ + "name": "sampleflask", + "password": "sampleflask", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-flask" + }, + "context": { + "index": "run.py", + "port": 5000, + "rc_file": false, + "build": [ + "pip install -r requirements.txt" + ], + "run": [ + "flask run --host=0.0.0.0 --port=5000" + ] + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/python3:latest", + "container_id": "b9521abaa377f5cdf525eb3e3fbe083719f8bee7f8500863b079310f69f4a413", + "container_port": 52687, + "language": "python3", + "instance_type": "application", + "app_url": "sampleflask-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 sampleflask@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:52687` + +## Deploy using [Run Commands File](/configurations/global/#run-commands-file) + +Have a look at the [run commands file](https://github.com/sdslabs/gasper-sample-flask/blob/master/Gasperfile.txt) for the above [sample application](https://github.com/sdslabs/gasper-sample-flask) + +```bash +$ curl -X POST \ + http://localhost:3000/apps/python3 \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"sampleflask", +"password":"sampleflask", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-flask", + "branch":"master" +}, +"context":{ + "index":"run.py", + "port": 5000, + "rc_file": true +} +}' + +{ + "name": "sampleflask", + "password": "sampleflask", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-flask", + "branch": "master" + }, + "context": { + "index": "run.py", + "port": 5000, + "rc_file": true + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/python3:latest", + "container_id": "574c8b5d8c9e8a14baa10f207723c2083ff28d008b9302a6bb3a6662cb7b06a8", + "container_port": 52811, + "language": "python3", + "instance_type": "application", + "app_url": "sampleflask-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 sampleflask@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:52811` + +!!!info + HTTP request to the above URL endpoint `localhost:3000/apps/python3` runs the application in a **Python 3** environment. If you want to run your application in a **Python 2** environment then change the URL endpoint to `localhost:3000/apps/python2` diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_ruby-on-rails.md b/demo/document-rag/documents/Gasper_content_examples_applications_ruby-on-rails.md new file mode 100644 index 0000000..df478f3 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_ruby-on-rails.md @@ -0,0 +1,156 @@ +# Deploying a Ruby on Rails Application + +This example shows how to deploy a [ruby on rails](https://rubyonrails.org/) application + +Lets use a [sample application](https://github.com/sdslabs/gasper-sample-ruby-on-rails) for demonstration which runs on **port 3000** + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + + +## Deploy using Build and Run Commands + +```bash +$ curl -X POST \ + http://localhost:3000/apps/ruby \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"sampleruby", +"password":"sampleruby", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-ruby-on-rails", + "branch":"master" +}, +"context":{ + "index":"bin/rails", + "port": 3000, + "build": ["bundle install --without production", "rails db:migrate"], + "run": ["rails server"] +}, +"resources": { + "memory": 4, + "cpu": 4 +} +}' + +{ + "name": "sampleruby", + "password": "sampleruby", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-ruby-on-rails", + "branch": "master" + }, + "context": { + "index": "bin/rails", + "port": 3000, + "rc_file": false, + "build": [ + "bundle install --without production", + "rails db:migrate" + ], + "run": [ + "rails server" + ] + }, + "resources": { + "memory": 4, + "cpu": 4 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/ruby:latest", + "container_id": "dd4d4199b81120abe58fb80dca355eba639e1caf8fb37ade02c9a53ee40634a0", + "container_port": 55673, + "language": "ruby", + "instance_type": "application", + "app_url": "sampleruby-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 sampleruby@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:55673` + +!!!warning + The above [sample application](https://github.com/sdslabs/gasper-sample-ruby-on-rails) takes around 6 minutes to start hence you need to wait for that duration before hitting the URL in your browser + +## Deploy using [Run Commands File](/configurations/global/#run-commands-file) + +Have a look at the [run commands file](https://github.com/sdslabs/gasper-sample-ruby-on-rails/blob/master/Gasperfile.txt) for the above [sample application](https://github.com/sdslabs/gasper-sample-ruby-on-rails) + +```bash +$ curl -X POST \ + http://localhost:3000/apps/ruby \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"sampleruby", +"password":"sampleruby", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-ruby-on-rails", + "branch":"master" +}, +"context":{ + "index":"bin/rails", + "port": 3000, + "rc_file": true +}, +"resources": { + "memory": 4, + "cpu": 4 +} +}' + +{ + "name": "sampleruby", + "password": "sampleruby", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-ruby-on-rails", + "branch": "master" + }, + "context": { + "index": "bin/rails", + "port": 3000, + "rc_file": true + }, + "resources": { + "memory": 4, + "cpu": 4 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/ruby:latest", + "container_id": "2e1b2165f93836d8021465802857692f37b51515361e78d13d201fde645d753f", + "container_port": 56041, + "language": "ruby", + "instance_type": "application", + "app_url": "sampleruby-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 sampleruby@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:56041` + +!!!warning + The above [sample application](https://github.com/sdslabs/gasper-sample-ruby-on-rails) takes around 6 minutes to start hence you need to wait for that duration before hitting the URL in your browser diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_rust.md b/demo/document-rag/documents/Gasper_content_examples_applications_rust.md new file mode 100644 index 0000000..e1f2570 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_rust.md @@ -0,0 +1,155 @@ +# Deploying a Rust Application + +This example shows how to deploy a [rust](https://www.rust-lang.org/) application + +Lets use a [sample application](https://github.com/sdslabs/gasper-sample-rust) for demonstration which runs on **port 3000** + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + + +## Deploy using Build and Run Commands + +```bash +$ curl -X POST \ + http://localhost:3000/apps/rust \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"samplerust", +"password":"samplerust", +"git": { + "repo_url":"https://github.com/sdslabs/gasper-sample-rust", + "branch":"master" +}, +"context":{ + "index": "src/main.rs", + "port": 3000, + "build" : ["cargo build --release"], + "run": ["./target/release/gasper-sample-rust"] +} +}, +"resources": { + "memory": 4, + "cpu": 4 +} +}' + +{ + "name": "samplerust", + "password": "samplerust", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-rust", + "branch": "master" + }, + "context": { + "index": "src/main.rs", + "port": 3000, + "rc_file": false, + "build": [ + "cargo build --release" + ], + "run": [ + "./target/release/gasper-sample-rust" + ] + }, + "resources": { + "memory": 4, + "cpu": 4 + }, + "name_servers": [ + "8.8.8.8", + "8.8.4.4" + ], + "docker_image": "sdslabs/rust:latest", + "container_id": "2b9b1f772259c4c6e81aebc3d0e5aca941695bb923ef55652eb73a6b45765c61", + "container_port": 53341, + "language": "rust", + "instance_type": "application", + "app_url": "samplerust-app-gasper.sdslabs.co", + "host_ip": "192.168.29.250", + "ssh_cmd": "ssh -p 2222 samplerust@192.168.29.250", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `192.168.29.250:53341` + +!!!warning + The above [sample application](https://github.com/sdslabs/gasper-sample-rust) takes around 3 minutes to build and start hence you need to wait for that duration before hitting the URL in your browser + +## Deploy using [Run Commands File](/configurations/global/#run-commands-file) + +Have a look at the [run commands file](https://github.com/sdslabs/gasper-sample-rust/blob/master/Gasperfile.txt) for the above [sample application](https://github.com/sdslabs/gasper-sample-rust) + +```bash +$ curl -X POST \ + http://localhost:3000/apps/rust \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"samplerust", +"password":"samplerust", +"git": { + "repo_url":"https://github.com/sdslabs/gasper-sample-rust", + "branch":"master" +}, +"context":{ + "index": "src/main.rs", + "port": 3000, + "rc_file": true +} +}, +"resources": { + "memory": 4, + "cpu": 4 +} +}' + +{ + "name": "samplerust", + "password": "samplerust", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-rust", + "branch": "master" + }, + "context": { + "index": "src/main.rs", + "port": 3000, + "rc_file": true + }, + "resources": { + "memory": 4, + "cpu": 4 + }, + "name_servers": [ + "8.8.8.8", + "8.8.4.4" + ], + "docker_image": "sdslabs/rust:latest", + "container_id": "917423498cc1d1d344a00069da6d453fdcdd7848d502a43063f852a4bd8afb94", + "container_port": 53473, + "language": "rust", + "instance_type": "application", + "app_url": "samplerust-app-gasper.sdslabs.co", + "host_ip": "192.168.29.250", + "ssh_cmd": "ssh -p 2222 samplerust@192.168.29.250", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `192.168.29.250:53473` + +!!!warning + The above [sample application](https://github.com/sdslabs/gasper-sample-rust) takes around 3 minutes to build and start hence you need to wait for that duration before hitting the URL in your browser diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_simple-php.md b/demo/document-rag/documents/Gasper_content_examples_applications_simple-php.md new file mode 100644 index 0000000..afecd72 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_simple-php.md @@ -0,0 +1,68 @@ +# Deploying a Simple PHP Application + +This example shows how to deploy a simple PHP application + +Lets use a [sample PHP application](https://github.com/sdslabs/gasper-sample-php) for demonstration + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + +```bash +$ curl -X POST \ + http://localhost:3000/apps/php \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"simplephp", +"password":"simplephp", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-php", + "branch":"master" +}, +"context":{ + "index":"index.php", + "port":80 +} +}' + +{ + "name": "simplephp", + "password": "simplephp", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-php", + "branch": "master" + }, + "context": { + "index": "index.php", + "port": 80, + "rc_file": false + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/php:latest", + "container_id": "c447c03399e5b23b860c6bfd932fa6a7f93e9ff6d7001d0cd4064f1554752cc3", + "container_port": 49599, + "language": "php", + "instance_type": "application", + "app_url": "simplephp-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 simplephp@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:49599` + diff --git a/demo/document-rag/documents/Gasper_content_examples_applications_static.md b/demo/document-rag/documents/Gasper_content_examples_applications_static.md new file mode 100644 index 0000000..0a5aad6 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_applications_static.md @@ -0,0 +1,67 @@ +# Deploying a Static Website + +This example shows how to deploy a static website + +Lets use the [hangman game](https://github.com/sdslabs/hangman-js-game) for demonstration + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [AppMaker](/configurations/appmaker/) up and running + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + +```bash +$ curl -X POST \ + http://localhost:3000/apps/static \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"static", +"password":"static", +"git": { + "repo_url": "https://github.com/sdslabs/hangman-js-game", + "branch":"master" +}, +"context":{ + "index":"hangman.html", + "port":80 +} +}' + +{ + "name": "samplestatic", + "password": "samplestatic", + "git": { + "repo_url": "https://github.com/sdslabs/hangman-js-game", + "branch": "master" + }, + "context": { + "index": "hangman.html", + "port": 80, + "rc_file": false + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/static:latest", + "container_id": "a05900527ad4b7175be438d8d28707cda39df3b94806d35f92949fd0b3d134db", + "container_port": 65499, + "language": "static", + "instance_type": "application", + "app_url": "samplestatic-app-gasper.sdslabs.co", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 static@10.43.3.24", + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:65499` diff --git a/demo/document-rag/documents/Gasper_content_examples_databases_mongodb.md b/demo/document-rag/documents/Gasper_content_examples_databases_mongodb.md new file mode 100644 index 0000000..59e96ca --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_databases_mongodb.md @@ -0,0 +1,31 @@ +# Creating a MongoDB Database + +This example shows how to deploy a [MongoDB](https://www.mongodb.com/) database via Gasper + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [DbMaker](/configurations/dbmaker/) up and running + * You have [DbMaker MongoDB Plugin](/configurations/dbmaker/#mongodb-configuration) enabled + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + +```bash +$ curl -X POST \ + http://localhost:3000/dbs/mongodb \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "alphamongo", + "password": "alphamongo" +}' + +{ + "name": "alphamongo", + "password": "alphamongo", + "user": "alphamongo", + "instance_type": "database", + "language": "mongodb", + "host_ip": "10.43.3.24", + "port": 27018, + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` diff --git a/demo/document-rag/documents/Gasper_content_examples_databases_mysql.md b/demo/document-rag/documents/Gasper_content_examples_databases_mysql.md new file mode 100644 index 0000000..5cf7545 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_databases_mysql.md @@ -0,0 +1,31 @@ +# Creating a MySQL Database + +This example shows how to deploy a [MySQL](https://www.mysql.com/) database via Gasper + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [DbMaker](/configurations/dbmaker/) up and running + * You have [DbMaker MySQL Plugin](/configurations/dbmaker/#mysql-configuration) enabled + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + +```bash +$ curl -X POST \ + http://localhost:3000/dbs/mysql \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "alphamysql", + "password": "alphamysql" +}' + +{ + "name": "alphamysql", + "password": "alphamysql", + "user": "alphamysql", + "instance_type": "database", + "language": "mysql", + "host_ip": "10.43.3.24", + "port": 33061, + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` diff --git a/demo/document-rag/documents/Gasper_content_examples_databases_postgresql.md b/demo/document-rag/documents/Gasper_content_examples_databases_postgresql.md new file mode 100644 index 0000000..5ef282f --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_databases_postgresql.md @@ -0,0 +1,32 @@ +# Creating a PostgreSQL Database + +This example shows how to deploy a [PostgreSQL](https://www.postgresql.org/) database via Gasper + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [DbMaker](/configurations/dbmaker/) up and running + * You have [DbMaker PostgreSQL Plugin](/configurations/dbmaker/#postgresql-configuration) enabled + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + +```bash +$ curl -X POST \ + http://localhost:3000/dbs/postgresql \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "alphapostgresql", + "password": "alphapostgresql" +}' + +{ + "name": "alphapostgresql", + "password": "alphapostgresql", + "user": "alphapostgresql", + "instance_type": "database", + "language": "postgresql", + "db_url": "alphapostgresql.db.sdslabs.co", + "host_ip": "192.168.225.90", + "port": 29121, + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` diff --git a/demo/document-rag/documents/Gasper_content_examples_databases_redis.md b/demo/document-rag/documents/Gasper_content_examples_databases_redis.md new file mode 100644 index 0000000..634b23a --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_databases_redis.md @@ -0,0 +1,32 @@ +# Creating a Redis Database + +This example shows how to deploy a [Redis](https://redis.io/) database via Gasper + +!!!warning "Prerequisites" + * You have [Master](/configurations/master/) and [DbMaker](/configurations/dbmaker/) up and running + * You have [DbMaker Redis Plugin](/configurations/dbmaker/#redis-configuration) enabled + * You have already [logged in](/examples/login/) and obtained a JSON Web Token + +```bash +$ curl -X POST \ + http://localhost:3000/dbs/redis \ + -H 'Authorization: Bearer {{token}}' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "alpha", + "password": "alpha" +}' + +{ + "name": "alpha", + "password": "alpha", + "user": "alpha", + "instance_type": "database", + "language": "redis", + "db_url": "alpha.db.sdslabs.co", + "host_ip": "192.168.43.46", + "port": 45861, + "owner": "anish.mukherjee1996@gmail.com", + "success": true +} +``` diff --git a/demo/document-rag/documents/Gasper_content_examples_login.md b/demo/document-rag/documents/Gasper_content_examples_login.md new file mode 100644 index 0000000..4ea68b9 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_examples_login.md @@ -0,0 +1,27 @@ +# Login + +This example shows how to login into the Gasper ecosystem and obtain a JSON Web Token + +!!!info + The JSON Web Token obtained will be used in other requests + +!!!warning "Prerequisites" + You have [Master](/configurations/master/) up and running + +```bash +$ curl -X POST \ + http://localhost:3000/auth/login \ + -H 'Content-Type: application/json' \ + -d '{ + "email": "anish.mukherjee1996@gmail.com", + "password": "alphadose" + }' + +{ + "code": 200, + "expire": "2019-12-04T22:05:41+05:30", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZW1haWwiOiJhbHBoYWRvc2VAZ21haWwuY29tIiwiZXhwIjoxNTc1NDc3MzQxLCJvcmlnX2lhdCI6MTU3NTQ3Mzc0MSwidXNlcm5hbWUiOiJhbHBoYWRvc2UifQ.Io0txryVH8zR6JfZ0iey86474oZl8gNwo4HjKgZl2s8" +} +``` + +The **token** field in the above response holds the required JSON Web Token diff --git a/demo/document-rag/documents/Gasper_content_features.md b/demo/document-rag/documents/Gasper_content_features.md new file mode 100644 index 0000000..77f6622 --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_features.md @@ -0,0 +1,18 @@ +# Features + +The following functionalities are provided by the Gasper Ecosystem + +* Worker services for creating/managing databases and applications +* Master service for:- + * Checking the status of worker services + * Intelligently distributing applications/databases among them + * Transferring applications from one worker node to another in case of node failure + * Removing dead worker nodes from the cloud +* REST API interface for the entire ecosystem +* Reverse-proxy service with HTTPS, HTTP/2, Websocket and gRPC support for accessing deployed applications +* DNS service which automatically creates DNS entries for all applications which in turn are resolved inside containers +* SSH service for providing ssh access directly to an application's docker container +* Virtual terminal for interacting with your application's docker container from your browser +* Dynamic addition/removal of nodes and services without configuration changes or restarts +* Compatibility with Linux, Windows, MacOS, FreeBSD and OpenBSD +* All of the above packaged with ❤️ in a **single binary** diff --git a/demo/document-rag/documents/Gasper_content_index.md b/demo/document-rag/documents/Gasper_content_index.md new file mode 100644 index 0000000..14a3b7a --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_index.md @@ -0,0 +1,24 @@ +# Welcome + + + +Gasper is an intelligent Platform as a Service (PaaS) used for deploying and managing +applications and databases in any cloud topology. + +## The Dilemma +Imagine you have a couple of *Bare Metal Servers* and/or *Virtual Machines* (collectively called nodes) at your disposal. Now you want to deploy a couple of applications/services to these nodes in such a manner so as to not put too much load on a single node. + +## Naive Approach +Your 1st option is to manually decide which application goes to which node, then use ssh/telnet to manually +setup all of your applications in each node one by one. + +## A Wise Choice +But you are smarter than that, hence you go for the 2nd option which is [Kubernetes](https://kubernetes.io/). You setup Kubernetes in all of your nodes which forms a cluster, and now you can deploy your applications without worrying about load distribution. But Kubernetes requires a lot of configuration for each application(deployments, services, stateful-sets etc) not to mention pipelines for creating the corresponding docker image.
+ +## The Ultimatum +Here comes (🥁drumroll please 🥁) **Gasper**, your 3rd option!
+Gasper builds and runs applications in docker containers **directly from source code** instead of docker images. +It requires minimal parameters for deploying an application, so minimal that you can count them on fingers in one hand 🤚. Same goes for Gasper provisioned databases. Gone are the days of hard labour (writing configurations). + +!!!question "What is Gasper in a nutshell ?" + Your Cloud in a Binary :) diff --git a/demo/document-rag/documents/Gasper_content_quick-start.md b/demo/document-rag/documents/Gasper_content_quick-start.md new file mode 100644 index 0000000..dd079bb --- /dev/null +++ b/demo/document-rag/documents/Gasper_content_quick-start.md @@ -0,0 +1,127 @@ +# Quick Start + +## Dependencies +The only thing you need for running Gasper is [Docker](https://www.docker.com/). Here are the installation guides for:- + +* [Linux](https://runnable.com/docker/install-docker-on-linux) +* [MacOS](https://docs.docker.com/docker-for-mac/install/) +* [Windows](https://docs.docker.com/docker-for-windows/install/) + +## Grab the latest binary +Assuming you have the [dependencies](#dependencies) installed, head over to Gasper's [releases](https://github.com/sdslabs/gasper/releases) page and grab the latest binary according to your operating system and system architecture. + +## Extract the downloaded content +After downloading, unzip the tar file + +```bash +$ tar -xf gasper_version_platform_arch.tar.gz +``` + +After extraction, the extracted directory should have the `gasper binary` and `config.toml`, the configuration file + +```bash +$ cd gasper_version_platform_arch +$ ls +gasper +config.toml +``` + +## Run Gasper +Run Gasper by executing the binary with the provided configuration +```bash +$ ./gasper --conf ./config.toml +``` + +!!!warning + Make sure that Docker is running on your system before executing the above command + +## Login and Token Retrieval +After Gasper is up and successfully running, lets deploy a sample application using [curl](https://curl.haxx.se/) + +To do that first we need to login and obtain a [JWT](https://jwt.io/) (JSON Web Token) + +```bash +$ curl -X POST \ + http://localhost:3000/auth/login \ + -H 'Content-Type: application/json' \ + -d '{ + "email": "anish.mukherjee1996@gmail.com", + "password": "alphadose" + }' + +{ + "code": 200, + "expire": "2019-12-04T22:05:41+05:30", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZW1haWwiOiJhbHBoYWRvc2VAZ21haWwuY29tIiwiZXhwIjoxNTc1NDc3MzQxLCJvcmlnX2lhdCI6MTU3NTQ3Mzc0MSwidXNlcm5hbWUiOiJhbHBoYWRvc2UifQ.Io0txryVH8zR6JfZ0iey86474oZl8gNwo4HjKgZl2s8" +} +``` + +!!!note + If you have made any changes in the [admin section](https://github.com/sdslabs/gasper/blob/develop/config.sample.toml#L36) of `config.toml` then change the payload (email and password) of the above request accordingly + +## Application Deployment +The **token** obtained from the above JSON response is our required JWT + +We will now use that **token** in the **Authorization Header** to deploy a [Sample PHP application](https://github.com/sdslabs/gasper-sample-php) +The format for using the token in the request header is `Authorization: Bearer {{token}}` + +```bash +$ curl -X POST \ + http://localhost:3000/apps/php \ + -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZW1haWwiOiJhbHBoYWRvc2VAZ21haWwuY29tIiwiZXhwIjoxNTc1NDc4MTc5LCJvcmlnX2lhdCI6MTU3NTQ3NDU3OSwidXNlcm5hbWUiOiJhbHBoYWRvc2UifQ.XKxKmC5mrSwHq3RGmTGqiAcQreVQjd9S-DMxw8ZN1k0' \ + -H 'Content-Type: application/json' \ + -d '{ +"name":"test", +"password":"test", +"git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-php",, + "branch":"master" +}, +"context":{ + "index":"index.php" +} +}' + +{ + "name": "test", + "password": "test", + "git": { + "repo_url": "https://github.com/sdslabs/gasper-sample-php", + "branch": "master" + }, + "context": { + "index": "index.php", + "port": 80, + "rc_file": false + }, + "resources": { + "memory": 0.5, + "cpu": 0.25 + }, + "name_servers": [ + "192.168.108.121", + "192.168.108.122", + "10.43.3.24" + ], + "docker_image": "sdslabs/php:latest", + "container_id": "fe04f8d7cbbdfa100ac9f03c8bdcec7b3d3246aa189dc0264c7d2af1cb92308b", + "container_port": 64128, + "language": "php", + "instance_type": "application", + "host_ip": "10.43.3.24", + "ssh_cmd": "ssh -p 2222 test@10.43.3.24", + "owner": "alphadose@gmail.com", + "success": true +} +``` + +Note the **host_ip** and **container_port** fields in the above JSON response + +You can now access the deployed application by hitting the URL **host_ip:container_port** from your browser + +For the above case it will be `10.43.3.24:64128` + +You should get the message `Hello World` in your browser marking the end of this tutorial + +!!!question "Where to go next?" + You can either have a look at more [examples](/examples/login) or how to [configure and setup](/configurations/overview) Gasper to your liking diff --git a/demo/document-rag/documents/README.md b/demo/document-rag/documents/README.md new file mode 100644 index 0000000..be952df --- /dev/null +++ b/demo/document-rag/documents/README.md @@ -0,0 +1,72 @@ +# SDSLabs documentation + +All documentation is flattened into this directory. + +## Files + +- [Gasper_README.md](Gasper_README.md) +- [Gasper_content_concepts.md](Gasper_content_concepts.md) +- [Gasper_content_configurations_admin.md](Gasper_content_configurations_admin.md) +- [Gasper_content_configurations_appmaker.md](Gasper_content_configurations_appmaker.md) +- [Gasper_content_configurations_cloudflare.md](Gasper_content_configurations_cloudflare.md) +- [Gasper_content_configurations_dbmaker.md](Gasper_content_configurations_dbmaker.md) +- [Gasper_content_configurations_docker-images.md](Gasper_content_configurations_docker-images.md) +- [Gasper_content_configurations_gendns.md](Gasper_content_configurations_gendns.md) +- [Gasper_content_configurations_genproxy.md](Gasper_content_configurations_genproxy.md) +- [Gasper_content_configurations_genssh.md](Gasper_content_configurations_genssh.md) +- [Gasper_content_configurations_global.md](Gasper_content_configurations_global.md) +- [Gasper_content_configurations_jwt.md](Gasper_content_configurations_jwt.md) +- [Gasper_content_configurations_master.md](Gasper_content_configurations_master.md) +- [Gasper_content_configurations_mongodb.md](Gasper_content_configurations_mongodb.md) +- [Gasper_content_configurations_overview.md](Gasper_content_configurations_overview.md) +- [Gasper_content_configurations_redis.md](Gasper_content_configurations_redis.md) +- [Gasper_content_examples_applications_advanced-php.md](Gasper_content_examples_applications_advanced-php.md) +- [Gasper_content_examples_applications_golang.md](Gasper_content_examples_applications_golang.md) +- [Gasper_content_examples_applications_nodejs-with-db.md](Gasper_content_examples_applications_nodejs-with-db.md) +- [Gasper_content_examples_applications_nodejs.md](Gasper_content_examples_applications_nodejs.md) +- [Gasper_content_examples_applications_python-django.md](Gasper_content_examples_applications_python-django.md) +- [Gasper_content_examples_applications_python-flask.md](Gasper_content_examples_applications_python-flask.md) +- [Gasper_content_examples_applications_ruby-on-rails.md](Gasper_content_examples_applications_ruby-on-rails.md) +- [Gasper_content_examples_applications_rust.md](Gasper_content_examples_applications_rust.md) +- [Gasper_content_examples_applications_simple-php.md](Gasper_content_examples_applications_simple-php.md) +- [Gasper_content_examples_applications_static.md](Gasper_content_examples_applications_static.md) +- [Gasper_content_examples_databases_mongodb.md](Gasper_content_examples_databases_mongodb.md) +- [Gasper_content_examples_databases_mysql.md](Gasper_content_examples_databases_mysql.md) +- [Gasper_content_examples_databases_postgresql.md](Gasper_content_examples_databases_postgresql.md) +- [Gasper_content_examples_databases_redis.md](Gasper_content_examples_databases_redis.md) +- [Gasper_content_examples_login.md](Gasper_content_examples_login.md) +- [Gasper_content_features.md](Gasper_content_features.md) +- [Gasper_content_index.md](Gasper_content_index.md) +- [Gasper_content_quick-start.md](Gasper_content_quick-start.md) +- [Rootex_README.md](Rootex_README.md) +- [Rootex_api_rootex.md](Rootex_api_rootex.md) +- [Rootex_engine_architecture.md](Rootex_engine_architecture.md) +- [Rootex_engine_audio.md](Rootex_engine_audio.md) +- [Rootex_engine_events.md](Rootex_engine_events.md) +- [Rootex_engine_framework.md](Rootex_engine_framework.md) +- [Rootex_engine_inputs.md](Rootex_engine_inputs.md) +- [Rootex_engine_multithreading.md](Rootex_engine_multithreading.md) +- [Rootex_engine_physics.md](Rootex_engine_physics.md) +- [Rootex_engine_rendering.md](Rootex_engine_rendering.md) +- [Rootex_engine_resources.md](Rootex_engine_resources.md) +- [Rootex_engine_scripting.md](Rootex_engine_scripting.md) +- [Rootex_guides_editor_layout.md](Rootex_guides_editor_layout.md) +- [Rootex_guides_getting_help.md](Rootex_guides_getting_help.md) +- [Rootex_guides_getting_started.md](Rootex_guides_getting_started.md) +- [Rootex_guides_graphics_tutorial.md](Rootex_guides_graphics_tutorial.md) +- [Rootex_guides_hud_tutorial.md](Rootex_guides_hud_tutorial.md) +- [Rootex_guides_particle_effects.md](Rootex_guides_particle_effects.md) +- [Rootex_guides_running_the_editor.md](Rootex_guides_running_the_editor.md) +- [Rootex_guides_transform_animation.md](Rootex_guides_transform_animation.md) +- [Rootex_index.md](Rootex_index.md) +- [VortexDB_docs_api-reference_grpc.md](VortexDB_docs_api-reference_grpc.md) +- [VortexDB_docs_api-reference_http.md](VortexDB_docs_api-reference_http.md) +- [VortexDB_docs_api-reference_overview.md](VortexDB_docs_api-reference_overview.md) +- [VortexDB_docs_concepts_architecture.md](VortexDB_docs_concepts_architecture.md) +- [VortexDB_docs_concepts_indexers.md](VortexDB_docs_concepts_indexers.md) +- [VortexDB_docs_concepts_snapshots.md](VortexDB_docs_concepts_snapshots.md) +- [VortexDB_docs_getting-started_installation.md](VortexDB_docs_getting-started_installation.md) +- [VortexDB_docs_getting-started_quickstart.md](VortexDB_docs_getting-started_quickstart.md) +- [VortexDB_docs_sdk_examples.md](VortexDB_docs_sdk_examples.md) +- [VortexDB_docs_sdk_reference.md](VortexDB_docs_sdk_reference.md) +- [Watchdog_README.md](Watchdog_README.md) diff --git a/demo/document-rag/documents/Rootex_README.md b/demo/document-rag/documents/Rootex_README.md new file mode 100644 index 0000000..3593f1b --- /dev/null +++ b/demo/document-rag/documents/Rootex_README.md @@ -0,0 +1,88 @@ +# Rootex + + + +[![MIT License](https://img.shields.io/apm/l/atomic-design-ui.svg?)](https://github.com/tterb/atomic-design-ui/blob/master/LICENSEs) +![C/C++ CI](https://github.com/sdslabs/Rootex/workflows/C/C++%20CI/badge.svg) +[![Documentation Status](https://readthedocs.org/projects/rootex/badge/?version=latest)](https://rootex.readthedocs.io/en/latest/?badge=latest) +[![Discord](https://discordapp.com/api/guilds/758961084337618944/embed.png)](https://discord.gg/dXkVEgTPu9) + +## Contents + +* [What is Rootex?](#what) +* [Why is Rootex?](#why) +* [How do I use Rootex?](#setup) +* [How can I contribute?](#how) +* [License](#license) + +##
What is Rootex? + +Rootex is an advanced C++ 3D game engine powering an in-production game yet to be announced. The game will finally ship on Windows and use DirectX 11. + +Find the upcoming features and the game's [public Trello board here!](https://trello.com/b/ES4oR0Gs/rootex-game) + + + +### Features + +* Based on the popular Entity-Component-System architecture + * Allows performance benefits due to better CPU cache usage. However over-optimizations are not the goal + * Uses an impure ECS where components can have functions +* Full editor GUI implemented in ImGui + * Similar interface as other popular game engine editors for added familiarity and ease of use + * Readable asset files (100% JSON) +* 3D DirectX 11 renderer + * Dynamic and static Phong-model lighting + * High resolution textures for Diffuse, Normal, Specular and Light mapping + * Post processing effects like gaussian blur, bloom, Adaptive SSAO, FXAA, including ad-hoc sepia, black-and-white, tonemapping + * Mildly configurable CPU based particle effects + * Effekseer Particle effects integration available for high quality VFX + * Environment effects like Sky sphere, sky reflections, refractions and depth fog + * Supports basic transform and skeletal animations + * Automatic LOD (level-of-detail) generation for 3D models and animations + * Custom materials using custom HLSL shaders + * And few more things... +* RmlUi integration which allows writing/debugging UI overlays in an HTML/CSS-like language and Lua +* Physics engine powered by Bullet Physics 3, allows an easy-to-work-with interface for making physics simulations +* OpenAL-Soft based audio engine supporting 3D attenuation and stereo sound +* Lua scripting engine with functionality exposed for easy game curation + * Uses an object-oriented approach with Lua scripting for making intuitive scope declarations and reducing garbage generation + * Lua debugger integration + * Equipped with a tweening API provided by [flux](https://github.com/rxi/flux) +* Applies the event-based programming paradigm for better maitainability of game code +* Being developed for an actual game + * Some game-specific features are also present like the Inky [Lua runtime integration](https://github.com/astrochili/narrator/) which allows writing dialogue in the [Ink language](https://www.inklestudios.com/ink/) and running them inside Rootex. +* Documentation + +## Why is Rootex? + +Rootex is the direct successor of [Rubeus, our 2D Game Engine](https://github.com/sdslabs/Rubeus). Rubeus Engine is not being maintained and is being preserved. Newer features will only be planned for Rootex. The game being developed will be announced soon as well. + +## How do I use Rootex? + +Rootex runs only on Windows and there are no plans to port it to other platforms. + +1. Install [Visual Studio 2022 or Visual Studio 2019](https://visualstudio.microsoft.com/vs/), [CMake build system](https://cmake.org/download/). +2. Install Visual Studio Desktop C++ development pack (or anything similar, since C++ is no longer a default language since at least Visual Studio 19) +3. Run `generate_cache.bat /22` for VS 2022 or `generate_cache.bat /19` for VS 2019. +4. Use `build.bat` to build Rootex. + +Assets Workflow : + +1. Assets are stored in separate repositories and added as git submodules. The testing assets are stored at https://gitlab.com/sdslabs/rootex-assets while the assets for the game are stored at https://gitlab.com/sdslabs/rootex-game. +2. The `assets.bat` script has two subcommands to make switching of the submodules between the testing and game repository easier. Use `assets.bat assets-test` to switch to the test repository and `assets.bat assets-game` to switch to the game repository. The submodule will have two remote urls set. The `origin` points to the HTTPS url while `upstream` points to the SSH url. +3. You can also add your own repository as an assets submodule. Use `assets.bat assets-custom ` for that. If setting a custom url, use an HTTPS url here and then set an SSH remote afterwards. + +**__WARNING__** : Running `assets.bat` will delete the `game/assets/` folder. Be sure to backup any unsaved progress before running it. + +Now you can start reading the [documentation](https://rootex.readthedocs.io/) and build games on Rootex! + +> **_NOTE:_** If you get the error `dxgidebug.dll not loaded` while opening the Rootex Editor, install *Graphics Tools* by following this [guide](https://docs.microsoft.com/en-us/windows/uwp/gaming/use-the-directx-runtime-and-visual-studio-graphics-diagnostic-features). + +## How can I contribute? + +Read [here](CONTRIBUTING.md) to know our contribution guidelines. Join our [Discord server](https://discord.gg/dXkVEgTPu9) or optionally ping us at chat.sdslabs.co to get guidance. You can start with setting up Rootex on your Windows machine and try solving a few bugs listed here: https://github.com/sdslabs/Rootex/issues + +## License + +This project is under the MIT license. See `THIRDPARTY.md` for thirdparty license notices. diff --git a/demo/document-rag/documents/Rootex_api_rootex.md b/demo/document-rag/documents/Rootex_api_rootex.md new file mode 100644 index 0000000..8475304 --- /dev/null +++ b/demo/document-rag/documents/Rootex_api_rootex.md @@ -0,0 +1,1278 @@ + + +Rootex — Rootex documentation +- Rootex +# Rootex +## Full API +### Namespaces +- [Namespace ECSFactory](https://rootex.readthedocs.io/en/latest/api/namespace_ECSFactory.html) +- [Functions](https://rootex.readthedocs.io/en/latest/api/namespace_ECSFactory.html#functions) +- [Variables](https://rootex.readthedocs.io/en/latest/api/namespace_ECSFactory.html#variables) +- [Namespace nlohmann](https://rootex.readthedocs.io/en/latest/api/namespace_nlohmann.html) +- [Classes](https://rootex.readthedocs.io/en/latest/api/namespace_nlohmann.html#classes) +### Classes and Structs +- [Struct AnimatedVertexData](https://rootex.readthedocs.io/en/latest/api/struct_animated_vertex_data.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/struct_animated_vertex_data.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/struct_animated_vertex_data.html#base-type) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_animated_vertex_data.html#struct-documentation) +- [Struct BasicMaterialData](https://rootex.readthedocs.io/en/latest/api/struct_basic_material_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_basic_material_data.html#struct-documentation) +- [Struct Component::Category](https://rootex.readthedocs.io/en/latest/api/struct_component_1_1_category.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_component_1_1_category.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_component_1_1_category.html#struct-documentation) +- [Struct ContentBrowser::ContentBrowserSettings](https://rootex.readthedocs.io/en/latest/api/struct_content_browser_1_1_content_browser_settings.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_content_browser_1_1_content_browser_settings.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_content_browser_1_1_content_browser_settings.html#struct-documentation) +- [Struct CPUParticlesComponent::Particle](https://rootex.readthedocs.io/en/latest/api/struct_c_p_u_particles_component_1_1_particle.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_c_p_u_particles_component_1_1_particle.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_c_p_u_particles_component_1_1_particle.html#struct-documentation) +- [Struct CustomMaterialData](https://rootex.readthedocs.io/en/latest/api/struct_custom_material_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_custom_material_data.html#struct-documentation) +- [Struct CustomRenderInterface::GeometryData](https://rootex.readthedocs.io/en/latest/api/struct_custom_render_interface_1_1_geometry_data.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_custom_render_interface_1_1_geometry_data.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_custom_render_interface_1_1_geometry_data.html#struct-documentation) +- [Struct DecalMaterialData](https://rootex.readthedocs.io/en/latest/api/struct_decal_material_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_decal_material_data.html#struct-documentation) +- [Struct DirectionalLight](https://rootex.readthedocs.io/en/latest/api/struct_directional_light.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_directional_light.html#struct-documentation) +- [Struct DirectionalLightInfo](https://rootex.readthedocs.io/en/latest/api/struct_directional_light_info.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_directional_light_info.html#struct-documentation) +- [Struct EditorEvents](https://rootex.readthedocs.io/en/latest/api/struct_editor_events.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_editor_events.html#struct-documentation) +- [Struct EditorSystem::Icons](https://rootex.readthedocs.io/en/latest/api/struct_editor_system_1_1_icons.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_editor_system_1_1_icons.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_editor_system_1_1_icons.html#struct-documentation) +- [Struct FlipbookDecorator::FlipbookElementData](https://rootex.readthedocs.io/en/latest/api/struct_flipbook_decorator_1_1_flipbook_element_data.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_flipbook_decorator_1_1_flipbook_element_data.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_flipbook_decorator_1_1_flipbook_element_data.html#struct-documentation) +- [Struct FXAAData](https://rootex.readthedocs.io/en/latest/api/struct_f_x_a_a_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_f_x_a_a_data.html#struct-documentation) +- [Struct GodRaysData](https://rootex.readthedocs.io/en/latest/api/struct_god_rays_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_god_rays_data.html#struct-documentation) +- [Struct Hit](https://rootex.readthedocs.io/en/latest/api/struct_hit.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_hit.html#struct-documentation) +- [Template Struct IndexTriangleList](https://rootex.readthedocs.io/en/latest/api/struct_index_triangle_list.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_index_triangle_list.html#struct-documentation) +- [Struct InputDescription](https://rootex.readthedocs.io/en/latest/api/struct_input_description.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_input_description.html#struct-documentation) +- [Struct InputScheme](https://rootex.readthedocs.io/en/latest/api/struct_input_scheme.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_input_scheme.html#struct-documentation) +- [Struct InspectorDock::InspectorSettings](https://rootex.readthedocs.io/en/latest/api/struct_inspector_dock_1_1_inspector_settings.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_inspector_dock_1_1_inspector_settings.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_inspector_dock_1_1_inspector_settings.html#struct-documentation) +- [Struct InstanceData](https://rootex.readthedocs.io/en/latest/api/struct_instance_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_instance_data.html#struct-documentation) +- [Struct LightsInfo](https://rootex.readthedocs.io/en/latest/api/struct_lights_info.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_lights_info.html#struct-documentation) +- [Struct MasterThread](https://rootex.readthedocs.io/en/latest/api/struct_master_thread.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_master_thread.html#struct-documentation) +- [Struct Mesh](https://rootex.readthedocs.io/en/latest/api/struct_mesh.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_mesh.html#struct-documentation) +- [Template Struct adl_serializer< BoundingBox >](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_bounding_box_01_4.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_bounding_box_01_4.html#struct-documentation) +- [Template Struct adl_serializer< Color >](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_color_01_4.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_color_01_4.html#struct-documentation) +- [Template Struct adl_serializer< Matrix >](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_matrix_01_4.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_matrix_01_4.html#struct-documentation) +- [Template Struct adl_serializer< Quaternion >](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_quaternion_01_4.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_quaternion_01_4.html#struct-documentation) +- [Template Struct adl_serializer< Vector2 >](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_vector2_01_4.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_vector2_01_4.html#struct-documentation) +- [Template Struct adl_serializer< Vector3 >](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_vector3_01_4.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_vector3_01_4.html#struct-documentation) +- [Template Struct adl_serializer< Vector4 >](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_vector4_01_4.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/structnlohmann_1_1adl__serializer_3_01_vector4_01_4.html#struct-documentation) +- [Struct OutputDock::OutputDockSettings](https://rootex.readthedocs.io/en/latest/api/struct_output_dock_1_1_output_dock_settings.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_output_dock_1_1_output_dock_settings.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_output_dock_1_1_output_dock_settings.html#struct-documentation) +- [Struct ParticleTemplate](https://rootex.readthedocs.io/en/latest/api/struct_particle_template.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_particle_template.html#struct-documentation) +- [Struct PerCameraChangePSCB](https://rootex.readthedocs.io/en/latest/api/struct_per_camera_change_p_s_c_b.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_camera_change_p_s_c_b.html#struct-documentation) +- [Struct PerDecalPSCB](https://rootex.readthedocs.io/en/latest/api/struct_per_decal_p_s_c_b.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_decal_p_s_c_b.html#struct-documentation) +- [Struct PerFrameCustomPSCBData](https://rootex.readthedocs.io/en/latest/api/struct_per_frame_custom_p_s_c_b_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_frame_custom_p_s_c_b_data.html#struct-documentation) +- [Struct PerFramePSCB](https://rootex.readthedocs.io/en/latest/api/struct_per_frame_p_s_c_b.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_frame_p_s_c_b.html#struct-documentation) +- [Struct PerFrameVSCB](https://rootex.readthedocs.io/en/latest/api/struct_per_frame_v_s_c_b.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_frame_v_s_c_b.html#struct-documentation) +- [Struct PerModelAnimationVSCBData](https://rootex.readthedocs.io/en/latest/api/struct_per_model_animation_v_s_c_b_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_model_animation_v_s_c_b_data.html#struct-documentation) +- [Struct PerModelDecalPSCBData](https://rootex.readthedocs.io/en/latest/api/struct_per_model_decal_p_s_c_b_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_model_decal_p_s_c_b_data.html#struct-documentation) +- [Struct PerModelPSCB](https://rootex.readthedocs.io/en/latest/api/struct_per_model_p_s_c_b.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_model_p_s_c_b.html#struct-documentation) +- [Struct PerModelPSCBData](https://rootex.readthedocs.io/en/latest/api/struct_per_model_p_s_c_b_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_model_p_s_c_b_data.html#struct-documentation) +- [Struct PerModelVSCBData](https://rootex.readthedocs.io/en/latest/api/struct_per_model_v_s_c_b_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_model_v_s_c_b_data.html#struct-documentation) +- [Struct PerScenePSCB](https://rootex.readthedocs.io/en/latest/api/struct_per_scene_p_s_c_b.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_per_scene_p_s_c_b.html#struct-documentation) +- [Struct PhysicsMaterialData](https://rootex.readthedocs.io/en/latest/api/struct_physics_material_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_physics_material_data.html#struct-documentation) +- [Struct PointLight](https://rootex.readthedocs.io/en/latest/api/struct_point_light.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_point_light.html#struct-documentation) +- [Struct PointLightInfo](https://rootex.readthedocs.io/en/latest/api/struct_point_light_info.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_point_light_info.html#struct-documentation) +- [Struct PostProcessingDetails](https://rootex.readthedocs.io/en/latest/api/struct_post_processing_details.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_post_processing_details.html#struct-documentation) +- [Struct PSFXAACB](https://rootex.readthedocs.io/en/latest/api/struct_p_s_f_x_a_a_c_b.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_p_s_f_x_a_a_c_b.html#struct-documentation) +- [Struct PSGodRaysCB](https://rootex.readthedocs.io/en/latest/api/struct_p_s_god_rays_c_b.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_p_s_god_rays_c_b.html#struct-documentation) +- [Struct RenderSystem::LineRequests](https://rootex.readthedocs.io/en/latest/api/struct_render_system_1_1_line_requests.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_render_system_1_1_line_requests.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_render_system_1_1_line_requests.html#struct-documentation) +- [Struct RootexEvents](https://rootex.readthedocs.io/en/latest/api/struct_rootex_events.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_rootex_events.html#struct-documentation) +- [Struct RotationKeyframe](https://rootex.readthedocs.io/en/latest/api/struct_rotation_keyframe.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_rotation_keyframe.html#struct-documentation) +- [Struct ScalingKeyframe](https://rootex.readthedocs.io/en/latest/api/struct_scaling_keyframe.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_scaling_keyframe.html#struct-documentation) +- [Struct SceneDock::SceneDockSettings](https://rootex.readthedocs.io/en/latest/api/struct_scene_dock_1_1_scene_dock_settings.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_scene_dock_1_1_scene_dock_settings.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_scene_dock_1_1_scene_dock_settings.html#struct-documentation) +- [Struct SceneSettings](https://rootex.readthedocs.io/en/latest/api/struct_scene_settings.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_scene_settings.html#struct-documentation) +- [Struct SkeletonNode](https://rootex.readthedocs.io/en/latest/api/struct_skeleton_node.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_skeleton_node.html#struct-documentation) +- [Struct SkyMaterialData](https://rootex.readthedocs.io/en/latest/api/struct_sky_material_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_sky_material_data.html#struct-documentation) +- [Struct SpotLight](https://rootex.readthedocs.io/en/latest/api/struct_spot_light.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_spot_light.html#struct-documentation) +- [Struct SpotLightInfo](https://rootex.readthedocs.io/en/latest/api/struct_spot_light_info.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_spot_light_info.html#struct-documentation) +- [Struct StaticLightID](https://rootex.readthedocs.io/en/latest/api/struct_static_light_i_d.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_static_light_i_d.html#struct-documentation) +- [Struct StaticPointLightsInfo](https://rootex.readthedocs.io/en/latest/api/struct_static_point_lights_info.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_static_point_lights_info.html#struct-documentation) +- [Struct TaskComplete](https://rootex.readthedocs.io/en/latest/api/struct_task_complete.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_task_complete.html#struct-documentation) +- [Struct TaskQueue](https://rootex.readthedocs.io/en/latest/api/struct_task_queue.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_task_queue.html#struct-documentation) +- [Struct TaskReady](https://rootex.readthedocs.io/en/latest/api/struct_task_ready.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_task_ready.html#struct-documentation) +- [Struct ToolbarDock::ToolbarDockSettings](https://rootex.readthedocs.io/en/latest/api/struct_toolbar_dock_1_1_toolbar_dock_settings.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_toolbar_dock_1_1_toolbar_dock_settings.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_toolbar_dock_1_1_toolbar_dock_settings.html#struct-documentation) +- [Struct TransformAnimationComponent::Keyframe](https://rootex.readthedocs.io/en/latest/api/struct_transform_animation_component_1_1_keyframe.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_transform_animation_component_1_1_keyframe.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_transform_animation_component_1_1_keyframe.html#struct-documentation) +- [Struct TransformComponent::TransformBuffer](https://rootex.readthedocs.io/en/latest/api/struct_transform_component_1_1_transform_buffer.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_transform_component_1_1_transform_buffer.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_transform_component_1_1_transform_buffer.html#struct-documentation) +- [Struct TranslationKeyframe](https://rootex.readthedocs.io/en/latest/api/struct_translation_keyframe.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_translation_keyframe.html#struct-documentation) +- [Struct UIVertexData](https://rootex.readthedocs.io/en/latest/api/struct_u_i_vertex_data.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_u_i_vertex_data.html#struct-documentation) +- [Struct VertexBufferElement](https://rootex.readthedocs.io/en/latest/api/struct_vertex_buffer_element.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_vertex_buffer_element.html#struct-documentation) +- [Struct VertexData](https://rootex.readthedocs.io/en/latest/api/struct_vertex_data.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/struct_vertex_data.html#inheritance-relationships) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/struct_vertex_data.html#derived-type) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_vertex_data.html#struct-documentation) +- [Struct ViewportDock::ViewportDockSettings](https://rootex.readthedocs.io/en/latest/api/struct_viewport_dock_1_1_viewport_dock_settings.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/struct_viewport_dock_1_1_viewport_dock_settings.html#nested-relationships) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_viewport_dock_1_1_viewport_dock_settings.html#struct-documentation) +- [Struct WorkerParameters](https://rootex.readthedocs.io/en/latest/api/struct_worker_parameters.html) +- [Struct Documentation](https://rootex.readthedocs.io/en/latest/api/struct_worker_parameters.html#struct-documentation) +- [Class AnimatedBasicMaterialResourceFile](https://rootex.readthedocs.io/en/latest/api/class_animated_basic_material_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_animated_basic_material_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_animated_basic_material_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_animated_basic_material_resource_file.html#class-documentation) +- [Class AnimatedModelComponent](https://rootex.readthedocs.io/en/latest/api/class_animated_model_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_animated_model_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_animated_model_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_animated_model_component.html#class-documentation) +- [Class AnimatedModelResourceFile](https://rootex.readthedocs.io/en/latest/api/class_animated_model_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_animated_model_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_animated_model_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_animated_model_resource_file.html#class-documentation) +- [Class AnimationSystem](https://rootex.readthedocs.io/en/latest/api/class_animation_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_animation_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_animation_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_animation_system.html#class-documentation) +- [Class Application](https://rootex.readthedocs.io/en/latest/api/class_application.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_application.html#inheritance-relationships) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_application.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_application.html#class-documentation) +- [Class ApplicationSettings](https://rootex.readthedocs.io/en/latest/api/class_application_settings.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_application_settings.html#class-documentation) +- [Class AudioBuffer](https://rootex.readthedocs.io/en/latest/api/class_audio_buffer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_audio_buffer.html#inheritance-relationships) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_audio_buffer.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_audio_buffer.html#class-documentation) +- [Class AudioComponent](https://rootex.readthedocs.io/en/latest/api/class_audio_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_audio_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_audio_component.html#base-type) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_audio_component.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_audio_component.html#class-documentation) +- [Class AudioListenerComponent](https://rootex.readthedocs.io/en/latest/api/class_audio_listener_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_audio_listener_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_audio_listener_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_audio_listener_component.html#class-documentation) +- [Class AudioPlayer](https://rootex.readthedocs.io/en/latest/api/class_audio_player.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_audio_player.html#class-documentation) +- [Class AudioResourceFile](https://rootex.readthedocs.io/en/latest/api/class_audio_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_audio_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_audio_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_audio_resource_file.html#class-documentation) +- [Class AudioSource](https://rootex.readthedocs.io/en/latest/api/class_audio_source.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_audio_source.html#inheritance-relationships) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_audio_source.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_audio_source.html#class-documentation) +- [Class AudioSystem](https://rootex.readthedocs.io/en/latest/api/class_audio_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_audio_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_audio_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_audio_system.html#class-documentation) +- [Class BaseComponentSet](https://rootex.readthedocs.io/en/latest/api/class_base_component_set.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_base_component_set.html#inheritance-relationships) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/class_base_component_set.html#derived-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_base_component_set.html#class-documentation) +- [Class BasicMaterialResourceFile](https://rootex.readthedocs.io/en/latest/api/class_basic_material_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_basic_material_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_basic_material_resource_file.html#base-type) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_basic_material_resource_file.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_basic_material_resource_file.html#class-documentation) +- [Class BoneAnimation](https://rootex.readthedocs.io/en/latest/api/class_bone_animation.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_bone_animation.html#class-documentation) +- [Class BoxColliderComponent](https://rootex.readthedocs.io/en/latest/api/class_box_collider_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_box_collider_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_box_collider_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_box_collider_component.html#class-documentation) +- [Class BufferFormat](https://rootex.readthedocs.io/en/latest/api/class_buffer_format.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_buffer_format.html#class-documentation) +- [Class CameraComponent](https://rootex.readthedocs.io/en/latest/api/class_camera_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_camera_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_camera_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_camera_component.html#class-documentation) +- [Class CapsuleColliderComponent](https://rootex.readthedocs.io/en/latest/api/class_capsule_collider_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_capsule_collider_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_capsule_collider_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_capsule_collider_component.html#class-documentation) +- [Class CollisionComponent](https://rootex.readthedocs.io/en/latest/api/class_collision_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_collision_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_collision_component.html#base-type) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_collision_component.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_collision_component.html#class-documentation) +- [Class CollisionModelResourceFile](https://rootex.readthedocs.io/en/latest/api/class_collision_model_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_collision_model_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_collision_model_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_collision_model_resource_file.html#class-documentation) +- [Class Component](https://rootex.readthedocs.io/en/latest/api/class_component.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_component.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_component.html#nested-types) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_component.html#inheritance-relationships) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_component.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_component.html#class-documentation) +- [Template Class ComponentArray](https://rootex.readthedocs.io/en/latest/api/class_component_array.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_component_array.html#class-documentation) +- [Template Class ComponentArrayIterator](https://rootex.readthedocs.io/en/latest/api/class_component_array_iterator.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_component_array_iterator.html#class-documentation) +- [Template Class ComponentSet](https://rootex.readthedocs.io/en/latest/api/class_component_set.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_component_set.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_component_set.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_component_set.html#class-documentation) +- [Class ContentBrowser](https://rootex.readthedocs.io/en/latest/api/class_content_browser.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_content_browser.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_content_browser.html#nested-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_content_browser.html#class-documentation) +- [Class CPUParticlesComponent](https://rootex.readthedocs.io/en/latest/api/class_c_p_u_particles_component.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_c_p_u_particles_component.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_c_p_u_particles_component.html#nested-types) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_c_p_u_particles_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_c_p_u_particles_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_c_p_u_particles_component.html#class-documentation) +- [Class CPUTexture](https://rootex.readthedocs.io/en/latest/api/class_c_p_u_texture.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_c_p_u_texture.html#class-documentation) +- [Class CustomMaterialResourceFile](https://rootex.readthedocs.io/en/latest/api/class_custom_material_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_custom_material_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_custom_material_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_custom_material_resource_file.html#class-documentation) +- [Class CustomPostProcess](https://rootex.readthedocs.io/en/latest/api/class_custom_post_process.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_custom_post_process.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_custom_post_process.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_custom_post_process.html#class-documentation) +- [Class CustomRenderInterface](https://rootex.readthedocs.io/en/latest/api/class_custom_render_interface.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_custom_render_interface.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_custom_render_interface.html#nested-types) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_custom_render_interface.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_custom_render_interface.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_custom_render_interface.html#class-documentation) +- [Class CustomSystemInterface](https://rootex.readthedocs.io/en/latest/api/class_custom_system_interface.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_custom_system_interface.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_custom_system_interface.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_custom_system_interface.html#class-documentation) +- [Class DebugDrawer](https://rootex.readthedocs.io/en/latest/api/class_debug_drawer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_debug_drawer.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_debug_drawer.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_debug_drawer.html#class-documentation) +- [Class DebugSystem](https://rootex.readthedocs.io/en/latest/api/class_debug_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_debug_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_debug_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_debug_system.html#class-documentation) +- [Class DecalComponent](https://rootex.readthedocs.io/en/latest/api/class_decal_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_decal_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_decal_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_decal_component.html#class-documentation) +- [Class DecalMaterialResourceFile](https://rootex.readthedocs.io/en/latest/api/class_decal_material_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_decal_material_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_decal_material_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_decal_material_resource_file.html#class-documentation) +- [Class Dependable](https://rootex.readthedocs.io/en/latest/api/class_dependable.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_dependable.html#inheritance-relationships) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/class_dependable.html#derived-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_dependable.html#class-documentation) +- [Template Class Dependency](https://rootex.readthedocs.io/en/latest/api/class_dependency.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_dependency.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_dependency.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_dependency.html#class-documentation) +- [Class DirectionalLightComponent](https://rootex.readthedocs.io/en/latest/api/class_directional_light_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_directional_light_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_directional_light_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_directional_light_component.html#class-documentation) +- [Class DxgiDebugInterface](https://rootex.readthedocs.io/en/latest/api/class_dxgi_debug_interface.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_dxgi_debug_interface.html#class-documentation) +- [Class EditorApplication](https://rootex.readthedocs.io/en/latest/api/class_editor_application.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_editor_application.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_editor_application.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_editor_application.html#class-documentation) +- [Class EditorSystem](https://rootex.readthedocs.io/en/latest/api/class_editor_system.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_editor_system.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_editor_system.html#nested-types) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_editor_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_editor_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_editor_system.html#class-documentation) +- [Class Entity](https://rootex.readthedocs.io/en/latest/api/class_entity.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_entity.html#class-documentation) +- [Class Event](https://rootex.readthedocs.io/en/latest/api/class_event.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_event.html#class-documentation) +- [Template Class EventBinder](https://rootex.readthedocs.io/en/latest/api/class_event_binder.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_event_binder.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_event_binder.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_event_binder.html#class-documentation) +- [Class EventBinderBase](https://rootex.readthedocs.io/en/latest/api/class_event_binder_base.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_event_binder_base.html#inheritance-relationships) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_event_binder_base.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_event_binder_base.html#class-documentation) +- [Class EventManager](https://rootex.readthedocs.io/en/latest/api/class_event_manager.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_event_manager.html#class-documentation) +- [Class FileEditor](https://rootex.readthedocs.io/en/latest/api/class_file_editor.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_file_editor.html#class-documentation) +- [Class FileViewer](https://rootex.readthedocs.io/en/latest/api/class_file_viewer.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_file_viewer.html#class-documentation) +- [Class FlipbookDecorator](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator.html#nested-types) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator.html#class-documentation) +- [Class FlipbookDecoratorInstancer](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator_instancer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator_instancer.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator_instancer.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_flipbook_decorator_instancer.html#class-documentation) +- [Class FogComponent](https://rootex.readthedocs.io/en/latest/api/class_fog_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_fog_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_fog_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_fog_component.html#class-documentation) +- [Class FontResourceFile](https://rootex.readthedocs.io/en/latest/api/class_font_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_font_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_font_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_font_resource_file.html#class-documentation) +- [Class FrameTimer](https://rootex.readthedocs.io/en/latest/api/class_frame_timer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_frame_timer.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_frame_timer.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_frame_timer.html#class-documentation) +- [Class GameApplication](https://rootex.readthedocs.io/en/latest/api/class_game_application.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_game_application.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_game_application.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_game_application.html#class-documentation) +- [Class GameRenderSystem](https://rootex.readthedocs.io/en/latest/api/class_game_render_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_game_render_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_game_render_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_game_render_system.html#class-documentation) +- [Class GPUTexture](https://rootex.readthedocs.io/en/latest/api/class_g_p_u_texture.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_g_p_u_texture.html#class-documentation) +- [Class GridModelComponent](https://rootex.readthedocs.io/en/latest/api/class_grid_model_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_grid_model_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_grid_model_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_grid_model_component.html#class-documentation) +- [Class ImageCubeResourceFile](https://rootex.readthedocs.io/en/latest/api/class_image_cube_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_image_cube_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_image_cube_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_image_cube_resource_file.html#class-documentation) +- [Class ImageResourceFile](https://rootex.readthedocs.io/en/latest/api/class_image_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_image_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_image_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_image_resource_file.html#class-documentation) +- [Class ImageViewer](https://rootex.readthedocs.io/en/latest/api/class_image_viewer.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_image_viewer.html#class-documentation) +- [Class IndexBuffer](https://rootex.readthedocs.io/en/latest/api/class_index_buffer.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_index_buffer.html#class-documentation) +- [Class InputInterface](https://rootex.readthedocs.io/en/latest/api/class_input_interface.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_input_interface.html#class-documentation) +- [Class InputListener](https://rootex.readthedocs.io/en/latest/api/class_input_listener.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_input_listener.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_input_listener.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_input_listener.html#class-documentation) +- [Class InputManager](https://rootex.readthedocs.io/en/latest/api/class_input_manager.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_input_manager.html#class-documentation) +- [Class InputSystem](https://rootex.readthedocs.io/en/latest/api/class_input_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_input_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_input_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_input_system.html#class-documentation) +- [Class InspectorDock](https://rootex.readthedocs.io/en/latest/api/class_inspector_dock.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_inspector_dock.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_inspector_dock.html#nested-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_inspector_dock.html#class-documentation) +- [Class InstancingBasicMaterialResourceFile](https://rootex.readthedocs.io/en/latest/api/class_instancing_basic_material_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_instancing_basic_material_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_instancing_basic_material_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_instancing_basic_material_resource_file.html#class-documentation) +- [Class LightSystem](https://rootex.readthedocs.io/en/latest/api/class_light_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_light_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_light_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_light_system.html#class-documentation) +- [Class Locale](https://rootex.readthedocs.io/en/latest/api/class_locale.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_locale.html#class-documentation) +- [Class LoggingScopeTimer](https://rootex.readthedocs.io/en/latest/api/class_logging_scope_timer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_logging_scope_timer.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_logging_scope_timer.html#base-type) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/class_logging_scope_timer.html#derived-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_logging_scope_timer.html#class-documentation) +- [Class LuaInterpreter](https://rootex.readthedocs.io/en/latest/api/class_lua_interpreter.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_lua_interpreter.html#class-documentation) +- [Class LuaTextResourceFile](https://rootex.readthedocs.io/en/latest/api/class_lua_text_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_lua_text_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_lua_text_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_lua_text_resource_file.html#class-documentation) +- [Class MaterialResourceFile](https://rootex.readthedocs.io/en/latest/api/class_material_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_material_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_material_resource_file.html#base-type) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_material_resource_file.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_material_resource_file.html#class-documentation) +- [Class MaterialViewer](https://rootex.readthedocs.io/en/latest/api/class_material_viewer.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_material_viewer.html#class-documentation) +- [Class ModelComponent](https://rootex.readthedocs.io/en/latest/api/class_model_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_model_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_model_component.html#base-type) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_model_component.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_model_component.html#class-documentation) +- [Class ModelResourceFile](https://rootex.readthedocs.io/en/latest/api/class_model_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_model_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_model_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_model_resource_file.html#class-documentation) +- [Class MusicComponent](https://rootex.readthedocs.io/en/latest/api/class_music_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_music_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_music_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_music_component.html#class-documentation) +- [Class OS](https://rootex.readthedocs.io/en/latest/api/class_o_s.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_o_s.html#class-documentation) +- [Class OutputDock](https://rootex.readthedocs.io/en/latest/api/class_output_dock.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_output_dock.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_output_dock.html#nested-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_output_dock.html#class-documentation) +- [Class ParticleEffectComponent](https://rootex.readthedocs.io/en/latest/api/class_particle_effect_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_particle_effect_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_particle_effect_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_particle_effect_component.html#class-documentation) +- [Class ParticleEffectResourceFile](https://rootex.readthedocs.io/en/latest/api/class_particle_effect_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_particle_effect_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_particle_effect_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_particle_effect_resource_file.html#class-documentation) +- [Class ParticleSystem](https://rootex.readthedocs.io/en/latest/api/class_particle_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_particle_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_particle_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_particle_system.html#class-documentation) +- [Class PauseSystem](https://rootex.readthedocs.io/en/latest/api/class_pause_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_pause_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_pause_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_pause_system.html#class-documentation) +- [Class PhysicsSystem](https://rootex.readthedocs.io/en/latest/api/class_physics_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_physics_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_physics_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_physics_system.html#class-documentation) +- [Class PlayerController](https://rootex.readthedocs.io/en/latest/api/class_player_controller.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_player_controller.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_player_controller.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_player_controller.html#class-documentation) +- [Class PlayerSystem](https://rootex.readthedocs.io/en/latest/api/class_player_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_player_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_player_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_player_system.html#class-documentation) +- [Class PointLightComponent](https://rootex.readthedocs.io/en/latest/api/class_point_light_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_point_light_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_point_light_component.html#base-type) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/class_point_light_component.html#derived-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_point_light_component.html#class-documentation) +- [Class PostProcess](https://rootex.readthedocs.io/en/latest/api/class_post_process.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_post_process.html#inheritance-relationships) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/class_post_process.html#derived-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_post_process.html#class-documentation) +- [Class PostProcessor](https://rootex.readthedocs.io/en/latest/api/class_post_processor.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_post_processor.html#class-documentation) +- [Class PostProcessSystem](https://rootex.readthedocs.io/en/latest/api/class_post_process_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_post_process_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_post_process_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_post_process_system.html#class-documentation) +- [Class Random](https://rootex.readthedocs.io/en/latest/api/class_random.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_random.html#class-documentation) +- [Class RenderableComponent](https://rootex.readthedocs.io/en/latest/api/class_renderable_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_renderable_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_renderable_component.html#base-type) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_renderable_component.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_renderable_component.html#class-documentation) +- [Class Renderer](https://rootex.readthedocs.io/en/latest/api/class_renderer.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_renderer.html#class-documentation) +- [Class RenderingDevice](https://rootex.readthedocs.io/en/latest/api/class_rendering_device.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_rendering_device.html#class-documentation) +- [Class RenderSystem](https://rootex.readthedocs.io/en/latest/api/class_render_system.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_render_system.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_render_system.html#nested-types) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_render_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_render_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_render_system.html#class-documentation) +- [Class RenderUIComponent](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_component.html#base-type) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_component.html#derived-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_component.html#class-documentation) +- [Class RenderUISystem](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_render_u_i_system.html#class-documentation) +- [Class ResourceFile](https://rootex.readthedocs.io/en/latest/api/class_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_resource_file.html#inheritance-relationships) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_resource_file.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_resource_file.html#class-documentation) +- [Class ResourceLoader](https://rootex.readthedocs.io/en/latest/api/class_resource_loader.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_resource_loader.html#class-documentation) +- [Class RigidBodyComponent](https://rootex.readthedocs.io/en/latest/api/class_rigid_body_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_rigid_body_component.html#inheritance-relationships) +- [Base Types](https://rootex.readthedocs.io/en/latest/api/class_rigid_body_component.html#base-types) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_rigid_body_component.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_rigid_body_component.html#class-documentation) +- [Class RootexDecorator](https://rootex.readthedocs.io/en/latest/api/class_rootex_decorator.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_rootex_decorator.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_rootex_decorator.html#base-type) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/class_rootex_decorator.html#derived-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_rootex_decorator.html#class-documentation) +- [Class Scene](https://rootex.readthedocs.io/en/latest/api/class_scene.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_scene.html#class-documentation) +- [Class SceneDock](https://rootex.readthedocs.io/en/latest/api/class_scene_dock.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_scene_dock.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_scene_dock.html#nested-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_scene_dock.html#class-documentation) +- [Class SceneLoader](https://rootex.readthedocs.io/en/latest/api/class_scene_loader.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_scene_loader.html#class-documentation) +- [Class Script](https://rootex.readthedocs.io/en/latest/api/class_script.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_script.html#class-documentation) +- [Class ScriptSystem](https://rootex.readthedocs.io/en/latest/api/class_script_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_script_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_script_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_script_system.html#class-documentation) +- [Class Shader](https://rootex.readthedocs.io/en/latest/api/class_shader.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_shader.html#class-documentation) +- [Class ShortMusicComponent](https://rootex.readthedocs.io/en/latest/api/class_short_music_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_short_music_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_short_music_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_short_music_component.html#class-documentation) +- [Class SkeletalAnimation](https://rootex.readthedocs.io/en/latest/api/class_skeletal_animation.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_skeletal_animation.html#class-documentation) +- [Class SkyComponent](https://rootex.readthedocs.io/en/latest/api/class_sky_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_sky_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_sky_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_sky_component.html#class-documentation) +- [Class SkyMaterialResourceFile](https://rootex.readthedocs.io/en/latest/api/class_sky_material_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_sky_material_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_sky_material_resource_file.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_sky_material_resource_file.html#class-documentation) +- [Class SphereColliderComponent](https://rootex.readthedocs.io/en/latest/api/class_sphere_collider_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_sphere_collider_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_sphere_collider_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_sphere_collider_component.html#class-documentation) +- [Class SplashWindow](https://rootex.readthedocs.io/en/latest/api/class_splash_window.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_splash_window.html#class-documentation) +- [Class SpotLightComponent](https://rootex.readthedocs.io/en/latest/api/class_spot_light_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_spot_light_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_spot_light_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_spot_light_component.html#class-documentation) +- [Class SpriteComponent](https://rootex.readthedocs.io/en/latest/api/class_sprite_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_sprite_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_sprite_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_sprite_component.html#class-documentation) +- [Class State](https://rootex.readthedocs.io/en/latest/api/class_state.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_state.html#class-documentation) +- [Class StateManager](https://rootex.readthedocs.io/en/latest/api/class_state_manager.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_state_manager.html#class-documentation) +- [Class StaticAudioBuffer](https://rootex.readthedocs.io/en/latest/api/class_static_audio_buffer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_static_audio_buffer.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_static_audio_buffer.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_static_audio_buffer.html#class-documentation) +- [Class StaticAudioSource](https://rootex.readthedocs.io/en/latest/api/class_static_audio_source.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_static_audio_source.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_static_audio_source.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_static_audio_source.html#class-documentation) +- [Class StaticMeshColliderComponent](https://rootex.readthedocs.io/en/latest/api/class_static_mesh_collider_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_static_mesh_collider_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_static_mesh_collider_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_static_mesh_collider_component.html#class-documentation) +- [Class StaticPointLightComponent](https://rootex.readthedocs.io/en/latest/api/class_static_point_light_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_static_point_light_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_static_point_light_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_static_point_light_component.html#class-documentation) +- [Class StopTimer](https://rootex.readthedocs.io/en/latest/api/class_stop_timer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_stop_timer.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_stop_timer.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_stop_timer.html#class-documentation) +- [Class StreamingAudioBuffer](https://rootex.readthedocs.io/en/latest/api/class_streaming_audio_buffer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_streaming_audio_buffer.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_streaming_audio_buffer.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_streaming_audio_buffer.html#class-documentation) +- [Class StreamingAudioSource](https://rootex.readthedocs.io/en/latest/api/class_streaming_audio_source.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_streaming_audio_source.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_streaming_audio_source.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_streaming_audio_source.html#class-documentation) +- [Class System](https://rootex.readthedocs.io/en/latest/api/class_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_system.html#inheritance-relationships) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_system.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_system.html#class-documentation) +- [Class Task](https://rootex.readthedocs.io/en/latest/api/class_task.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_task.html#class-documentation) +- [Class TextResourceFile](https://rootex.readthedocs.io/en/latest/api/class_text_resource_file.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_text_resource_file.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_text_resource_file.html#base-type) +- [Derived Type](https://rootex.readthedocs.io/en/latest/api/class_text_resource_file.html#derived-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_text_resource_file.html#class-documentation) +- [Class TextUIComponent](https://rootex.readthedocs.io/en/latest/api/class_text_u_i_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_text_u_i_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_text_u_i_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_text_u_i_component.html#class-documentation) +- [Class TextureCube](https://rootex.readthedocs.io/en/latest/api/class_texture_cube.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_texture_cube.html#class-documentation) +- [Class TextViewer](https://rootex.readthedocs.io/en/latest/api/class_text_viewer.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_text_viewer.html#class-documentation) +- [Class ThreadPool](https://rootex.readthedocs.io/en/latest/api/class_thread_pool.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_thread_pool.html#class-documentation) +- [Class Timer](https://rootex.readthedocs.io/en/latest/api/class_timer.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_timer.html#inheritance-relationships) +- [Derived Types](https://rootex.readthedocs.io/en/latest/api/class_timer.html#derived-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_timer.html#class-documentation) +- [Class ToolbarDock](https://rootex.readthedocs.io/en/latest/api/class_toolbar_dock.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_toolbar_dock.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_toolbar_dock.html#nested-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_toolbar_dock.html#class-documentation) +- [Class TransformAnimationComponent](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_component.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_component.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_component.html#nested-types) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_component.html#class-documentation) +- [Class TransformAnimationSystem](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_transform_animation_system.html#class-documentation) +- [Class TransformComponent](https://rootex.readthedocs.io/en/latest/api/class_transform_component.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_transform_component.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_transform_component.html#nested-types) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_transform_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_transform_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_transform_component.html#class-documentation) +- [Class TransformSystem](https://rootex.readthedocs.io/en/latest/api/class_transform_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_transform_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_transform_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_transform_system.html#class-documentation) +- [Class TriggerComponent](https://rootex.readthedocs.io/en/latest/api/class_trigger_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_trigger_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_trigger_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_trigger_component.html#class-documentation) +- [Class TriggerSystem](https://rootex.readthedocs.io/en/latest/api/class_trigger_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_trigger_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_trigger_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_trigger_system.html#class-documentation) +- [Class UIComponent](https://rootex.readthedocs.io/en/latest/api/class_u_i_component.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_u_i_component.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_u_i_component.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_u_i_component.html#class-documentation) +- [Class UISystem](https://rootex.readthedocs.io/en/latest/api/class_u_i_system.html) +- [Inheritance Relationships](https://rootex.readthedocs.io/en/latest/api/class_u_i_system.html#inheritance-relationships) +- [Base Type](https://rootex.readthedocs.io/en/latest/api/class_u_i_system.html#base-type) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_u_i_system.html#class-documentation) +- [Class VertexBuffer](https://rootex.readthedocs.io/en/latest/api/class_vertex_buffer.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_vertex_buffer.html#class-documentation) +- [Class Viewport](https://rootex.readthedocs.io/en/latest/api/class_viewport.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_viewport.html#class-documentation) +- [Class ViewportDock](https://rootex.readthedocs.io/en/latest/api/class_viewport_dock.html) +- [Nested Relationships](https://rootex.readthedocs.io/en/latest/api/class_viewport_dock.html#nested-relationships) +- [Nested Types](https://rootex.readthedocs.io/en/latest/api/class_viewport_dock.html#nested-types) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_viewport_dock.html#class-documentation) +- [Class Window](https://rootex.readthedocs.io/en/latest/api/class_window.html) +- [Class Documentation](https://rootex.readthedocs.io/en/latest/api/class_window.html#class-documentation) +### Enums +- [Enum CollisionMask](https://rootex.readthedocs.io/en/latest/api/enum_collision__component_8h_1afebb47eb1c7cee166bbed331f3d23588.html) +- [Enum Documentation](https://rootex.readthedocs.io/en/latest/api/enum_collision__component_8h_1afebb47eb1c7cee166bbed331f3d23588.html#enum-documentation) +- [Enum ComponentIDs](https://rootex.readthedocs.io/en/latest/api/enum_component__ids_8h_1a0cc1c991ee9657e70f2c740e6bfc09e6.html) +- [Enum Documentation](https://rootex.readthedocs.io/en/latest/api/enum_component__ids_8h_1a0cc1c991ee9657e70f2c740e6bfc09e6.html#enum-documentation) +- [Enum Device](https://rootex.readthedocs.io/en/latest/api/enum_input__manager_8h_1adb53a8cc97236ca207c035241a5b7fb8.html) +- [Enum Documentation](https://rootex.readthedocs.io/en/latest/api/enum_input__manager_8h_1adb53a8cc97236ca207c035241a5b7fb8.html#enum-documentation) +- [Enum PhysicsMaterial](https://rootex.readthedocs.io/en/latest/api/enum_physics__system_8h_1a5547629cc4d910b0015ea5dd23e820f2.html) +- [Enum Documentation](https://rootex.readthedocs.io/en/latest/api/enum_physics__system_8h_1a5547629cc4d910b0015ea5dd23e820f2.html#enum-documentation) +- [Enum RenderPass](https://rootex.readthedocs.io/en/latest/api/enum_render__pass_8h_1a4f9eee39dfc89a120ad908b7849762f3.html) +- [Enum Documentation](https://rootex.readthedocs.io/en/latest/api/enum_render__pass_8h_1a4f9eee39dfc89a120ad908b7849762f3.html#enum-documentation) +- [Enum RootExclusion](https://rootex.readthedocs.io/en/latest/api/enum_animated__model__resource__file_8h_1a1f76e3b3e13fa00198a1b3d6e09760d4.html) +- [Enum Documentation](https://rootex.readthedocs.io/en/latest/api/enum_animated__model__resource__file_8h_1a1f76e3b3e13fa00198a1b3d6e09760d4.html#enum-documentation) +- [Enum TransformPassDown](https://rootex.readthedocs.io/en/latest/api/enum_transform__component_8h_1a20cbd51bf00e7e3d2be74d436f0827e2.html) +- [Enum Documentation](https://rootex.readthedocs.io/en/latest/api/enum_transform__component_8h_1a20cbd51bf00e7e3d2be74d436f0827e2.html#enum-documentation) +- [Enum TYPES_OF_BUFFERS](https://rootex.readthedocs.io/en/latest/api/enum_material__resource__file_8h_1ad648c4cfe32b921ab9bb94c1abfd428f.html) +- [Enum Documentation](https://rootex.readthedocs.io/en/latest/api/enum_material__resource__file_8h_1ad648c4cfe32b921ab9bb94c1abfd428f.html#enum-documentation) +### Functions +- [Function BtTransformToMat](https://rootex.readthedocs.io/en/latest/api/function_bullet__conversions_8h_1ae8b063bba8387667803cf34ffe21926f.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_bullet__conversions_8h_1ae8b063bba8387667803cf34ffe21926f.html#function-documentation) +- [Function BtVector3ToVec](https://rootex.readthedocs.io/en/latest/api/function_bullet__conversions_8h_1ab2a4c0811cf474c0b29fb679f89c775a.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_bullet__conversions_8h_1ab2a4c0811cf474c0b29fb679f89c775a.html#function-documentation) +- [Function ColorToImColor](https://rootex.readthedocs.io/en/latest/api/function_editor__system_8h_1aea82603faf8e57873c9e50a9d53307fe.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_editor__system_8h_1aea82603faf8e57873c9e50a9d53307fe.html#function-documentation) +- [Function CompareMaterials](https://rootex.readthedocs.io/en/latest/api/function_model__component_8h_1a750beb9eed94a497b9477af044fdbcb4.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_model__component_8h_1a750beb9eed94a497b9477af044fdbcb4.html#function-documentation) +- [Function CreateRootexApplication](https://rootex.readthedocs.io/en/latest/api/function_application_8h_1a883d10f0382522e2e7a1af253903d346.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_application_8h_1a883d10f0382522e2e7a1af253903d346.html#function-documentation) +- [Function DECLARE_COMPONENT(AnimatedModelComponent)](https://rootex.readthedocs.io/en/latest/api/function_animated__model__component_8h_1ac67f70946df9d79fd91e8c29100c2fd1.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_animated__model__component_8h_1ac67f70946df9d79fd91e8c29100c2fd1.html#function-documentation) +- [Function DECLARE_COMPONENT(AudioListenerComponent)](https://rootex.readthedocs.io/en/latest/api/function_audio__listener__component_8h_1a6335de4c768ae21522d5a80f3d1932dc.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_audio__listener__component_8h_1a6335de4c768ae21522d5a80f3d1932dc.html#function-documentation) +- [Function DECLARE_COMPONENT(BoxColliderComponent)](https://rootex.readthedocs.io/en/latest/api/function_box__collider__component_8h_1ad847b2d1125a4043f49e6eb07125ea5c.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_box__collider__component_8h_1ad847b2d1125a4043f49e6eb07125ea5c.html#function-documentation) +- [Function DECLARE_COMPONENT(CameraComponent)](https://rootex.readthedocs.io/en/latest/api/function_camera__component_8h_1acec16a2c16b7aa4ee08f5f931dbaf495.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_camera__component_8h_1acec16a2c16b7aa4ee08f5f931dbaf495.html#function-documentation) +- [Function DECLARE_COMPONENT(CapsuleColliderComponent)](https://rootex.readthedocs.io/en/latest/api/function_capsule__collider__component_8h_1a875ac9627309ee4a2162ee8900f08de4.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_capsule__collider__component_8h_1a875ac9627309ee4a2162ee8900f08de4.html#function-documentation) +- [Function DECLARE_COMPONENT(CPUParticlesComponent)](https://rootex.readthedocs.io/en/latest/api/function_cpu__particles__component_8h_1a887ff8363cc8e2016ebb85bec33a5bc6.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_cpu__particles__component_8h_1a887ff8363cc8e2016ebb85bec33a5bc6.html#function-documentation) +- [Function DECLARE_COMPONENT(DecalComponent)](https://rootex.readthedocs.io/en/latest/api/function_decal__component_8h_1ad8dfc046d1f1118fdefbc07b14e23321.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_decal__component_8h_1ad8dfc046d1f1118fdefbc07b14e23321.html#function-documentation) +- [Function DECLARE_COMPONENT(DirectionalLightComponent)](https://rootex.readthedocs.io/en/latest/api/function_directional__light__component_8h_1a195cdd5745f4d385b6026c3b9f0386d7.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_directional__light__component_8h_1a195cdd5745f4d385b6026c3b9f0386d7.html#function-documentation) +- [Function DECLARE_COMPONENT(FogComponent)](https://rootex.readthedocs.io/en/latest/api/function_fog__component_8h_1a43e98247d44760c30af6b5a04dfb587b.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_fog__component_8h_1a43e98247d44760c30af6b5a04dfb587b.html#function-documentation) +- [Function DECLARE_COMPONENT(GridModelComponent)](https://rootex.readthedocs.io/en/latest/api/function_grid__model__component_8h_1a5c5ce2ccfc6a5d0e457adfab16fd365b.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_grid__model__component_8h_1a5c5ce2ccfc6a5d0e457adfab16fd365b.html#function-documentation) +- [Function DECLARE_COMPONENT(ModelComponent)](https://rootex.readthedocs.io/en/latest/api/function_model__component_8h_1a04fbf04635ca5d8c20947666b3b189ef.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_model__component_8h_1a04fbf04635ca5d8c20947666b3b189ef.html#function-documentation) +- [Function DECLARE_COMPONENT(MusicComponent)](https://rootex.readthedocs.io/en/latest/api/function_music__component_8h_1aa6413ef0f05096ad0f7a4208cce88a42.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_music__component_8h_1aa6413ef0f05096ad0f7a4208cce88a42.html#function-documentation) +- [Function DECLARE_COMPONENT(ParticleEffectComponent)](https://rootex.readthedocs.io/en/latest/api/function_particle__effect__component_8h_1ad2f6f8c8938a38bc15fe082bd47bad10.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_particle__effect__component_8h_1ad2f6f8c8938a38bc15fe082bd47bad10.html#function-documentation) +- [Function DECLARE_COMPONENT(PlayerController)](https://rootex.readthedocs.io/en/latest/api/function_player__controller_8h_1aaa35a2d227bec5f20d0bc4b821c82071.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_player__controller_8h_1aaa35a2d227bec5f20d0bc4b821c82071.html#function-documentation) +- [Function DECLARE_COMPONENT(PointLightComponent)](https://rootex.readthedocs.io/en/latest/api/function_point__light__component_8h_1a0b53b12b4c5362cdf19d5d8f59f5cc49.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_point__light__component_8h_1a0b53b12b4c5362cdf19d5d8f59f5cc49.html#function-documentation) +- [Function DECLARE_COMPONENT(ShortMusicComponent)](https://rootex.readthedocs.io/en/latest/api/function_short__music__component_8h_1ac4d58dfdc3fcb9a4b58dda78543c5cc2.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_short__music__component_8h_1ac4d58dfdc3fcb9a4b58dda78543c5cc2.html#function-documentation) +- [Function DECLARE_COMPONENT(SkyComponent)](https://rootex.readthedocs.io/en/latest/api/function_sky__component_8h_1a64ad2513f0b08a71ee28e0847707af9f.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_sky__component_8h_1a64ad2513f0b08a71ee28e0847707af9f.html#function-documentation) +- [Function DECLARE_COMPONENT(SphereColliderComponent)](https://rootex.readthedocs.io/en/latest/api/function_sphere__collider__component_8h_1a1ac11639437ba3a211a8cd8a711a85f6.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_sphere__collider__component_8h_1a1ac11639437ba3a211a8cd8a711a85f6.html#function-documentation) +- [Function DECLARE_COMPONENT(SpotLightComponent)](https://rootex.readthedocs.io/en/latest/api/function_spot__light__component_8h_1a264f7fec30db3fdcb70aa4eaa8a61c30.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_spot__light__component_8h_1a264f7fec30db3fdcb70aa4eaa8a61c30.html#function-documentation) +- [Function DECLARE_COMPONENT(SpriteComponent)](https://rootex.readthedocs.io/en/latest/api/function_sprite__component_8h_1a587d75221fa2f5e5de97ba9342400610.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_sprite__component_8h_1a587d75221fa2f5e5de97ba9342400610.html#function-documentation) +- [Function DECLARE_COMPONENT(StaticMeshColliderComponent)](https://rootex.readthedocs.io/en/latest/api/function_static__mesh__collider__component_8h_1a306ac0b947805915ff6bc78e2acc1b21.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_static__mesh__collider__component_8h_1a306ac0b947805915ff6bc78e2acc1b21.html#function-documentation) +- [Function DECLARE_COMPONENT(StaticPointLightComponent)](https://rootex.readthedocs.io/en/latest/api/function_static__point__light__component_8h_1a9f2a604613655c76f5b25d2b02642a9d.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_static__point__light__component_8h_1a9f2a604613655c76f5b25d2b02642a9d.html#function-documentation) +- [Function DECLARE_COMPONENT(TextUIComponent)](https://rootex.readthedocs.io/en/latest/api/function_text__ui__component_8h_1aa858c33bd5fbefab336a1b121e601a4f.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_text__ui__component_8h_1aa858c33bd5fbefab336a1b121e601a4f.html#function-documentation) +- [Function DECLARE_COMPONENT(TransformAnimationComponent)](https://rootex.readthedocs.io/en/latest/api/function_transform__animation__component_8h_1a7f443938527e999af15de652eb2c15bd.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_transform__animation__component_8h_1a7f443938527e999af15de652eb2c15bd.html#function-documentation) +- [Function DECLARE_COMPONENT(TransformComponent)](https://rootex.readthedocs.io/en/latest/api/function_transform__component_8h_1a09e6bafe83be15991f96913a5ad00ddf.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_transform__component_8h_1a09e6bafe83be15991f96913a5ad00ddf.html#function-documentation) +- [Function DECLARE_COMPONENT(TriggerComponent)](https://rootex.readthedocs.io/en/latest/api/function_trigger__component_8h_1a3c0687d68f8eaf1b948859de4645692f.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_trigger__component_8h_1a3c0687d68f8eaf1b948859de4645692f.html#function-documentation) +- [Function DECLARE_COMPONENT(UIComponent)](https://rootex.readthedocs.io/en/latest/api/function_ui__component_8h_1a16977e5b0b2ef9cfaaefa228fbac458c.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_ui__component_8h_1a16977e5b0b2ef9cfaaefa228fbac458c.html#function-documentation) +- [Function ECSFactory::AddComponent](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a0508b6381fcba9914fb5e66f058c76e7.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a0508b6381fcba9914fb5e66f058c76e7.html#function-documentation) +- [Function ECSFactory::AddDefaultComponent](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a05f154d3209c7f3cab597f2a4f07f083.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a05f154d3209c7f3cab597f2a4f07f083.html#function-documentation) +- [Function ECSFactory::CopyEntity](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a1a5cf409cf5865717dbdd1504005a4a0.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a1a5cf409cf5865717dbdd1504005a4a0.html#function-documentation) +- [Function ECSFactory::FillEntity](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1acb6e7cb3abe268d144be265833f560b0.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1acb6e7cb3abe268d144be265833f560b0.html#function-documentation) +- [Function ECSFactory::FillEntityFromFile](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1abc080a0a747f373060c851583368edb3.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1abc080a0a747f373060c851583368edb3.html#function-documentation) +- [Function ECSFactory::FillRootEntity](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1ac2509f9510cb459ec2c08cd12e26bc05.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1ac2509f9510cb459ec2c08cd12e26bc05.html#function-documentation) +- [Function ECSFactory::GetComponentIDByName](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a5cfbf6b695658d4441ee1ddd48085811.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a5cfbf6b695658d4441ee1ddd48085811.html#function-documentation) +- [Function ECSFactory::GetComponentNameByID](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1adec5694cf349f5d425b638241b9e6544.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1adec5694cf349f5d425b638241b9e6544.html#function-documentation) +- [Function ECSFactory::Initialize](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1ad906b443169e41515429454e966529f0.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1ad906b443169e41515429454e966529f0.html#function-documentation) +- [Function ECSFactory::RemoveComponent](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a42c221050864a092971575b2ddae9d15.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_namespace_e_c_s_factory_1a42c221050864a092971575b2ddae9d15.html#function-documentation) +- [Template Function Extract](https://rootex.readthedocs.io/en/latest/api/function_types_8h_1adb954f0e8bd14c18a6b826057fb91e7b.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_types_8h_1adb954f0e8bd14c18a6b826057fb91e7b.html#function-documentation) +- [Function from_json(const JSON::json&, ParticleTemplate&)](https://rootex.readthedocs.io/en/latest/api/function_cpu__particles__component_8h_1a4de55a09bc5c5c6e1449a1185938daca.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_cpu__particles__component_8h_1a4de55a09bc5c5c6e1449a1185938daca.html#function-documentation) +- [Function from_json(const JSON::json&, InputDescription&)](https://rootex.readthedocs.io/en/latest/api/function_input__manager_8h_1a071d465e5f19adb3f9a2f62930041dc5.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_input__manager_8h_1a071d465e5f19adb3f9a2f62930041dc5.html#function-documentation) +- [Function from_json(const JSON::json&, InputScheme&)](https://rootex.readthedocs.io/en/latest/api/function_input__manager_8h_1a8370145cc8dc75cc36f7599ccb6f822d.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_input__manager_8h_1a8370145cc8dc75cc36f7599ccb6f822d.html#function-documentation) +- [Function from_json(const JSON::json&, BasicMaterialData&)](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a7daa475bfda33c32e33ff2e4d5241bdc.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a7daa475bfda33c32e33ff2e4d5241bdc.html#function-documentation) +- [Function from_json(const JSON::json&, SkyMaterialData&)](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a223319b88c58b26d1eff8b96bcf23d8c.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a223319b88c58b26d1eff8b96bcf23d8c.html#function-documentation) +- [Function from_json(const JSON::json&, CustomMaterialData&)](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a4239bdbbdb5ab8acd6354e8920bdba4d.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a4239bdbbdb5ab8acd6354e8920bdba4d.html#function-documentation) +- [Function from_json(const JSON::json&, DecalMaterialData&)](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a7c283c8a2e057bf5e03a13c3f8ddcc28.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a7c283c8a2e057bf5e03a13c3f8ddcc28.html#function-documentation) +- [Function from_json(const JSON::json&, ResourceFile::Type&)](https://rootex.readthedocs.io/en/latest/api/function_resource__file_8h_1a8aeb09ce19fbb7738dd3212aa4946eab.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_resource__file_8h_1a8aeb09ce19fbb7738dd3212aa4946eab.html#function-documentation) +- [Function from_json(const JSON::json&, SceneSettings&)](https://rootex.readthedocs.io/en/latest/api/function_scene_8h_1ad7f2318ea8065dd6d894a6fd1efdb239.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_scene_8h_1ad7f2318ea8065dd6d894a6fd1efdb239.html#function-documentation) +- [Function from_json(const JSON::json&, TransformPassDown&)](https://rootex.readthedocs.io/en/latest/api/function_transform__component_8h_1a4fbc7b1c855381fcc0f2e57216b319d8.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_transform__component_8h_1a4fbc7b1c855381fcc0f2e57216b319d8.html#function-documentation) +- [Function GetPayloadTypes](https://rootex.readthedocs.io/en/latest/api/function_resource__loader_8h_1ada8b5c7d38dbc92ff009c72961b52760.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_resource__loader_8h_1ada8b5c7d38dbc92ff009c72961b52760.html#function-documentation) +- [Function Interpolate](https://rootex.readthedocs.io/en/latest/api/function_maths_8h_1a8e59daa66b3ec2f92ce0b3db0fbd6773.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_maths_8h_1a8e59daa66b3ec2f92ce0b3db0fbd6773.html#function-documentation) +- [Function IsFileSupported](https://rootex.readthedocs.io/en/latest/api/function_resource__loader_8h_1aad57e29a571939a0c54850cd9db6b015.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_resource__loader_8h_1aad57e29a571939a0c54850cd9db6b015.html#function-documentation) +- [Function MatTobtTransform](https://rootex.readthedocs.io/en/latest/api/function_bullet__conversions_8h_1a30b2a34f8f4f6962d2fe15c96e7c6e85.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_bullet__conversions_8h_1a30b2a34f8f4f6962d2fe15c96e7c6e85.html#function-documentation) +- [Function RootexFPSGraph](https://rootex.readthedocs.io/en/latest/api/function_imgui__helpers_8h_1a60fab8a0cc2ad5d7a9e6595b71cfb3cb.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_imgui__helpers_8h_1a60fab8a0cc2ad5d7a9e6595b71cfb3cb.html#function-documentation) +- [Function RootexSelectableImage](https://rootex.readthedocs.io/en/latest/api/function_imgui__helpers_8h_1a248f41ce3f0886b4a1c08730099b3471.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_imgui__helpers_8h_1a248f41ce3f0886b4a1c08730099b3471.html#function-documentation) +- [Function RootexSelectableImageCube](https://rootex.readthedocs.io/en/latest/api/function_imgui__helpers_8h_1a3119c55d98bfaa0afd95f985de8c59d4.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_imgui__helpers_8h_1a3119c55d98bfaa0afd95f985de8c59d4.html#function-documentation) +- [Function Split](https://rootex.readthedocs.io/en/latest/api/function_types_8h_1af1c485fe28fcac37e442747e1004c6fb.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_types_8h_1af1c485fe28fcac37e442747e1004c6fb.html#function-documentation) +- [Function StringToWideString](https://rootex.readthedocs.io/en/latest/api/function_os_8h_1ae1139c760167cbf2d946993a6b29f535.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_os_8h_1ae1139c760167cbf2d946993a6b29f535.html#function-documentation) +- [Function to_json(JSON::json&, const ParticleTemplate)](https://rootex.readthedocs.io/en/latest/api/function_cpu__particles__component_8h_1ad744661678796adf0f46a73c3c0ada7c.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_cpu__particles__component_8h_1ad744661678796adf0f46a73c3c0ada7c.html#function-documentation) +- [Function to_json(JSON::json&, const InputDescription&)](https://rootex.readthedocs.io/en/latest/api/function_input__manager_8h_1a7924ded2d5bbc414c1096f880d6298a8.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_input__manager_8h_1a7924ded2d5bbc414c1096f880d6298a8.html#function-documentation) +- [Function to_json(JSON::json&, const InputScheme&)](https://rootex.readthedocs.io/en/latest/api/function_input__manager_8h_1a8409ec4fceb6f7cae1d2a2967267a8c4.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_input__manager_8h_1a8409ec4fceb6f7cae1d2a2967267a8c4.html#function-documentation) +- [Function to_json(JSON::json&, const BasicMaterialData&)](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a88a5e410ca235f12c5e239f67a6b3788.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a88a5e410ca235f12c5e239f67a6b3788.html#function-documentation) +- [Function to_json(JSON::json&, const SkyMaterialData&)](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1ade98f9f9170c7adc0e9f700e2ed83d7f.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1ade98f9f9170c7adc0e9f700e2ed83d7f.html#function-documentation) +- [Function to_json(JSON::json&, const CustomMaterialData&)](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a43a69c2b2a635c3bd4123814b1d39e2c.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1a43a69c2b2a635c3bd4123814b1d39e2c.html#function-documentation) +- [Function to_json(JSON::json&, const DecalMaterialData&)](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1ab29fb801398afb79fac3fd9e858c53fe.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_material__resource__file_8h_1ab29fb801398afb79fac3fd9e858c53fe.html#function-documentation) +- [Function to_json(JSON::json&, const ResourceFile::Type&)](https://rootex.readthedocs.io/en/latest/api/function_resource__file_8h_1a35373e692ae02462d01ae82ef09a07bd.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_resource__file_8h_1a35373e692ae02462d01ae82ef09a07bd.html#function-documentation) +- [Function to_json(JSON::json&, const SceneSettings&)](https://rootex.readthedocs.io/en/latest/api/function_scene_8h_1a769d2b73ba1d1e23331f89bfcb6d732d.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_scene_8h_1a769d2b73ba1d1e23331f89bfcb6d732d.html#function-documentation) +- [Function to_json(JSON::json&, const TransformPassDown&)](https://rootex.readthedocs.io/en/latest/api/function_transform__component_8h_1a6b50d9ff67c1467abee4628f57e8eb72.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_transform__component_8h_1a6b50d9ff67c1467abee4628f57e8eb72.html#function-documentation) +- [Function VecTobtVector3](https://rootex.readthedocs.io/en/latest/api/function_bullet__conversions_8h_1abbc02c36aa3af3074c25df165a7fdec2.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_bullet__conversions_8h_1abbc02c36aa3af3074c25df165a7fdec2.html#function-documentation) +- [Function WideStringToString](https://rootex.readthedocs.io/en/latest/api/function_os_8h_1acdcaac29f3694765a099cd8adfadc40e.html) +- [Function Documentation](https://rootex.readthedocs.io/en/latest/api/function_os_8h_1acdcaac29f3694765a099cd8adfadc40e.html#function-documentation) +### Variables +- [Variable CreatableFiles](https://rootex.readthedocs.io/en/latest/api/variable_resource__loader_8h_1a748c18293c6e7aa6be6a90e4e83a16ce.html) +- [Variable Documentation](https://rootex.readthedocs.io/en/latest/api/variable_resource__loader_8h_1a748c18293c6e7aa6be6a90e4e83a16ce.html#variable-documentation) +- [Variable ECSFactory::s_ComponentSets](https://rootex.readthedocs.io/en/latest/api/variable_namespace_e_c_s_factory_1abd783dc867e31d2ea1507228eb4eb74c.html) +- [Variable Documentation](https://rootex.readthedocs.io/en/latest/api/variable_namespace_e_c_s_factory_1abd783dc867e31d2ea1507228eb4eb74c.html#variable-documentation) +- [Variable FONT_ICON_BUFFER_NAME_ROOTEX](https://rootex.readthedocs.io/en/latest/api/variable_imgui__helpers_8h_1a6f6cf1b7d4e802bbb7209f1905247595.html) +- [Variable Documentation](https://rootex.readthedocs.io/en/latest/api/variable_imgui__helpers_8h_1a6f6cf1b7d4e802bbb7209f1905247595.html#variable-documentation) +- [Variable m_PayloadTypes](https://rootex.readthedocs.io/en/latest/api/variable_resource__loader_8h_1aa36559a94d9145f71803529296d1b753.html) +- [Variable Documentation](https://rootex.readthedocs.io/en/latest/api/variable_resource__loader_8h_1aa36559a94d9145f71803529296d1b753.html#variable-documentation) +- [Variable SupportedFiles](https://rootex.readthedocs.io/en/latest/api/variable_resource__loader_8h_1af6e641d166ae37fdfd5ea9ef6a6f2df2.html) +- [Variable Documentation](https://rootex.readthedocs.io/en/latest/api/variable_resource__loader_8h_1af6e641d166ae37fdfd5ea9ef6a6f2df2.html#variable-documentation) +### Defines +- [Define _WIN32_WINNT](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1ac50762666aa00bd3a4308158510f1748.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1ac50762666aa00bd3a4308158510f1748.html#define-documentation) +- [Define AL_CHECK](https://rootex.readthedocs.io/en/latest/api/define_audio__system_8h_1a0716cb1b78b62e43a6c1459c595e6226.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_audio__system_8h_1a0716cb1b78b62e43a6c1459c595e6226.html#define-documentation) +- [Define ALUT_CHECK](https://rootex.readthedocs.io/en/latest/api/define_audio__system_8h_1aa91124e61aacc81ecb871e0f05ff9e2f.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_audio__system_8h_1aa91124e61aacc81ecb871e0f05ff9e2f.html#define-documentation) +- [Define BONES_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a678e363f5ae846a4016474a73d661da8.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a678e363f5ae846a4016474a73d661da8.html#define-documentation) +- [Define BONES_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1abb68b921201d9e0336abf7d879994488.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1abb68b921201d9e0336abf7d879994488.html#define-documentation) +- [Define BUFFER_COUNT](https://rootex.readthedocs.io/en/latest/api/define_streaming__audio__buffer_8h_1a56c7105b7a827ead9f36384370c90f00.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_streaming__audio__buffer_8h_1a56c7105b7a827ead9f36384370c90f00.html#define-documentation) +- [Define COMPONENT](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a78f817e5be45241265b396b7fc1dd167.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a78f817e5be45241265b396b7fc1dd167.html#define-documentation) +- [Define CONCAT](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a88fa737059e67b4b17ec980e5877361e.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a88fa737059e67b4b17ec980e5877361e.html#define-documentation) +- [Define CONCAT](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a88fa737059e67b4b17ec980e5877361e.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a88fa737059e67b4b17ec980e5877361e.html#define-documentation) +- [Define CUSTOM_PER_FRAME_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ab9c884d8a8d37b44c90a82a56f7b14eb.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ab9c884d8a8d37b44c90a82a56f7b14eb.html#define-documentation) +- [Define CUSTOM_PER_FRAME_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a87cf65f769bb96a3ee73e1d777c7a4c7.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a87cf65f769bb96a3ee73e1d777c7a4c7.html#define-documentation) +- [Define CUSTOM_PER_OBJECT_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a5f21123257c4921920f0ed49a7966043.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a5f21123257c4921920f0ed49a7966043.html#define-documentation) +- [Define CUSTOM_PER_OBJECT_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ae625d9804df9f338ff786e72657e3163.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ae625d9804df9f338ff786e72657e3163.html#define-documentation) +- [Define CUSTOM_TEXTURE_0_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a0a750371f219e5bc8069e3608dab433a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a0a750371f219e5bc8069e3608dab433a.html#define-documentation) +- [Define CUSTOM_TEXTURE_0_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ae5a0a548a50b41e185f5fd8418ec32f0.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ae5a0a548a50b41e185f5fd8418ec32f0.html#define-documentation) +- [Define CUSTOM_TEXTURE_0_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a29848bc2af91575b313a129cfceb5857.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a29848bc2af91575b313a129cfceb5857.html#define-documentation) +- [Define CUSTOM_TEXTURE_0_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a61a2c138110762830963f3a2f5bc9df1.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a61a2c138110762830963f3a2f5bc9df1.html#define-documentation) +- [Define CUSTOM_TEXTURE_1_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1aadd0b0c6ae360b15225409852f332484.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1aadd0b0c6ae360b15225409852f332484.html#define-documentation) +- [Define CUSTOM_TEXTURE_1_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ac0d7d9125e816b6247630c5287f91dda.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ac0d7d9125e816b6247630c5287f91dda.html#define-documentation) +- [Define CUSTOM_TEXTURE_1_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a0f7dc9ad598cfb43dd156eb66a33bb4a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a0f7dc9ad598cfb43dd156eb66a33bb4a.html#define-documentation) +- [Define CUSTOM_TEXTURE_1_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a365ca1f9503e60d25e2115b0c99ab2fa.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a365ca1f9503e60d25e2115b0c99ab2fa.html#define-documentation) +- [Define CUSTOM_TEXTURE_2_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1abb593e7b2a3ae5b48833ddbd46210ac2.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1abb593e7b2a3ae5b48833ddbd46210ac2.html#define-documentation) +- [Define CUSTOM_TEXTURE_2_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a5f380cce2133da2e8b3f90704157ffaf.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a5f380cce2133da2e8b3f90704157ffaf.html#define-documentation) +- [Define CUSTOM_TEXTURE_2_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a33e5384e6cb5e21f51cf217cf324fc6d.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a33e5384e6cb5e21f51cf217cf324fc6d.html#define-documentation) +- [Define CUSTOM_TEXTURE_2_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a415fb9116bc6e6953cb0219a8dbf5f76.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a415fb9116bc6e6953cb0219a8dbf5f76.html#define-documentation) +- [Define CUSTOM_TEXTURE_3_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1afcb788696f4d1d762b68623001a34603.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1afcb788696f4d1d762b68623001a34603.html#define-documentation) +- [Define CUSTOM_TEXTURE_3_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ac0d570d67dd8ae9fdbea9905b677bc2c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ac0d570d67dd8ae9fdbea9905b677bc2c.html#define-documentation) +- [Define CUSTOM_TEXTURE_3_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1ac4b49d270b88ee42724a7edc380c23d7.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1ac4b49d270b88ee42724a7edc380c23d7.html#define-documentation) +- [Define CUSTOM_TEXTURE_3_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1ad5fe600a1b357d0825afa13304f5aace.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1ad5fe600a1b357d0825afa13304f5aace.html#define-documentation) +- [Define CUSTOM_TEXTURE_4_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a82d1cd9fb33b94f2bc1a85281a17d48b.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a82d1cd9fb33b94f2bc1a85281a17d48b.html#define-documentation) +- [Define CUSTOM_TEXTURE_4_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ac4d53340fa41623839927f881bac1113.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ac4d53340fa41623839927f881bac1113.html#define-documentation) +- [Define CUSTOM_TEXTURE_4_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1ab6fa1332ab44e74308f8f9e934d5cd0d.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1ab6fa1332ab44e74308f8f9e934d5cd0d.html#define-documentation) +- [Define CUSTOM_TEXTURE_4_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a7fdd9d07a4d86486f65ed5871b1b3a5c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a7fdd9d07a4d86486f65ed5871b1b3a5c.html#define-documentation) +- [Define DEBUG_PANIC](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a14b16e539b53881cda5847f5b25e0153.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a14b16e539b53881cda5847f5b25e0153.html#define-documentation) +- [Define DECLARE_COMPONENT](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a4cb5e9b8af489f7630eb33f085446aa1.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a4cb5e9b8af489f7630eb33f085446aa1.html#define-documentation) +- [Define DEFINE_COMPONENT](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1ac8915871be6fdbb8b985a1c39e7db63e.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1ac8915871be6fdbb8b985a1c39e7db63e.html#define-documentation) +- [Define DEFINE_EVENT](https://rootex.readthedocs.io/en/latest/api/define_event_8h_1a62de310b2a56986c1f65f982fdad34b1.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_event_8h_1a62de310b2a56986c1f65f982fdad34b1.html#define-documentation) +- [Define DEPENDENCY](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a76d6d3f7803dbffb744669d8311c8e62.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a76d6d3f7803dbffb744669d8311c8e62.html#define-documentation) +- [Define DEPENDS_ON](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a39898e05da40553d4dcabd4c37751025.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a39898e05da40553d4dcabd4c37751025.html#define-documentation) +- [Define DEPTH_TEXTURE_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a0d8cfe2086d20baf9c98b94356d3402d.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a0d8cfe2086d20baf9c98b94356d3402d.html#define-documentation) +- [Define DEPTH_TEXTURE_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a8a463710839a46b689f10d6e1b8c5c5a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a8a463710839a46b689f10d6e1b8c5c5a.html#define-documentation) +- [Define DIFFUSE_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a8a4ec9b457003a383f9c943793b1a3bf.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a8a4ec9b457003a383f9c943793b1a3bf.html#define-documentation) +- [Define DIFFUSE_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1aedc96b518f95c3c4f66f4fc23dd588df.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1aedc96b518f95c3c4f66f4fc23dd588df.html#define-documentation) +- [Define ENGINE_DIRECTORY](https://rootex.readthedocs.io/en/latest/api/define_os_8h_1a85bd0cbd65d9b5c6eb6f57a86e097f5c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_os_8h_1a85bd0cbd65d9b5c6eb6f57a86e097f5c.html#define-documentation) +- [Define ERR](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a48ad5f2a7c4a89b6b6e139ca9d94820b.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a48ad5f2a7c4a89b6b6e139ca9d94820b.html#define-documentation) +- [Define ERR_CUSTOM](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1aa68453e6281eb3b2529c4263f8794229.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1aa68453e6281eb3b2529c4263f8794229.html#define-documentation) +- [Define ERR_CUSTOM_SILENT](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a5735cacd0d14cf5a41ea8a93f6a18de9.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a5735cacd0d14cf5a41ea8a93f6a18de9.html#define-documentation) +- [Define ERR_SILENT](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a8099ea7e7d9bc0efcb00e714fbaab477.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a8099ea7e7d9bc0efcb00e714fbaab477.html#define-documentation) +- [Define FONT_ICON_BUFFER_NAME_ROOTEX](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a6b25ce71f9a10eb4d11cb32373b1ef75.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a6b25ce71f9a10eb4d11cb32373b1ef75.html#define-documentation) +- [Define FONT_ICON_BUFFER_SIZE_ROOTEX](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1af8fe35dcfe873885f39d2516420c7e5a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1af8fe35dcfe873885f39d2516420c7e5a.html#define-documentation) +- [Define GAME_DIRECTORY](https://rootex.readthedocs.io/en/latest/api/define_os_8h_1a1c27f7476a2015e455d415ab086d62e6.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_os_8h_1a1c27f7476a2015e455d415ab086d62e6.html#define-documentation) +- [Define GFX_ERR_CHECK](https://rootex.readthedocs.io/en/latest/api/define_dxgi__debug__interface_8h_1a25c197eb7eba704f86643b9026f20532.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_dxgi__debug__interface_8h_1a25c197eb7eba704f86643b9026f20532.html#define-documentation) +- [Define GOD_RAYS_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a9c50b09c0b90b2bf3da50afc24d2139e.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a9c50b09c0b90b2bf3da50afc24d2139e.html#define-documentation) +- [Define GOD_RAYS_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1acb00cd75679d36a57b51fadd2a23d885.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1acb00cd75679d36a57b51fadd2a23d885.html#define-documentation) +- [Define ICON_MAX_ROOTEX](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a228c12468ab889f9150908390a742257.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a228c12468ab889f9150908390a742257.html#define-documentation) +- [Define ICON_MIN_ROOTEX](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a59093648f36982b464968bad35386455.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a59093648f36982b464968bad35386455.html#define-documentation) +- [Define ICON_ROOTEX_BOOKMARK](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a8592deaaec82c5d58c2af6f45d9769a2.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a8592deaaec82c5d58c2af6f45d9769a2.html#define-documentation) +- [Define ICON_ROOTEX_CHECK](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1af87198e51adad82b57a34e2f4e2cd0f8.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1af87198e51adad82b57a34e2f4e2cd0f8.html#define-documentation) +- [Define ICON_ROOTEX_CLOCK_O](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a52cfa9cbcdd9eb7f5d9adac5341c8cac.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a52cfa9cbcdd9eb7f5d9adac5341c8cac.html#define-documentation) +- [Define ICON_ROOTEX_CLOUD](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a335c099d253d08a8ac3a0e727d2e4b15.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a335c099d253d08a8ac3a0e727d2e4b15.html#define-documentation) +- [Define ICON_ROOTEX_DATABASE](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a275c322ea2ec32228a2fc2f3fb6fb8ae.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a275c322ea2ec32228a2fc2f3fb6fb8ae.html#define-documentation) +- [Define ICON_ROOTEX_EXTERNAL_LINK](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1ada3953940264c20e0059fbfec312b328.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1ada3953940264c20e0059fbfec312b328.html#define-documentation) +- [Define ICON_ROOTEX_FILE](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a318b4a2f780bc60eb8eef2c1b10f2608.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a318b4a2f780bc60eb8eef2c1b10f2608.html#define-documentation) +- [Define ICON_ROOTEX_FILE_AUDIO_O](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a34ff13463806a2e59ec5128839a48be7.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a34ff13463806a2e59ec5128839a48be7.html#define-documentation) +- [Define ICON_ROOTEX_FILE_CODE_O](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a53863c7921dc5ee026e173e906deaf39.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a53863c7921dc5ee026e173e906deaf39.html#define-documentation) +- [Define ICON_ROOTEX_FILE_IMAGE_O](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a1071534bf420062f8e46854772c9c3e3.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a1071534bf420062f8e46854772c9c3e3.html#define-documentation) +- [Define ICON_ROOTEX_FILE_TEXT](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a2e587bc84fe0ef0e72020ac533118d5a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a2e587bc84fe0ef0e72020ac533118d5a.html#define-documentation) +- [Define ICON_ROOTEX_FILES_O](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a5efe6c7560b1dbc13bd8e2333867abce.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a5efe6c7560b1dbc13bd8e2333867abce.html#define-documentation) +- [Define ICON_ROOTEX_FLOPPY_O](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a5105e2a2e648184b7df0b58a7c41e9ee.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a5105e2a2e648184b7df0b58a7c41e9ee.html#define-documentation) +- [Define ICON_ROOTEX_FOLDER](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1abe22e8da4e2bf77e87e9b0397bfa6b04.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1abe22e8da4e2bf77e87e9b0397bfa6b04.html#define-documentation) +- [Define ICON_ROOTEX_FOLDER_OPEN](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1af3cb0e9bcddc4dba1709402bc2efed99.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1af3cb0e9bcddc4dba1709402bc2efed99.html#define-documentation) +- [Define ICON_ROOTEX_FONT](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1afd45a45f683b458e8ebe7821d76bdab3.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1afd45a45f683b458e8ebe7821d76bdab3.html#define-documentation) +- [Define ICON_ROOTEX_FORT_AWESOME](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a92221eadc523befac49060c6f662764b.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a92221eadc523befac49060c6f662764b.html#define-documentation) +- [Define ICON_ROOTEX_MINUS](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a516580ff35694e528cc5ecab7981e649.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a516580ff35694e528cc5ecab7981e649.html#define-documentation) +- [Define ICON_ROOTEX_MINUS_CIRCLE](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a51f08c58d6987740b205fac31dc3c5f2.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a51f08c58d6987740b205fac31dc3c5f2.html#define-documentation) +- [Define ICON_ROOTEX_PENCIL_SQUARE_O](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a8294c176a09e34f22fc8bbb6fa2d2ee4.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a8294c176a09e34f22fc8bbb6fa2d2ee4.html#define-documentation) +- [Define ICON_ROOTEX_PICTURE_O](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1aa057468ba730ec20e7ae1d4838121279.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1aa057468ba730ec20e7ae1d4838121279.html#define-documentation) +- [Define ICON_ROOTEX_PLUS](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a5372300880bdd075e1517fd8e007cc3f.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a5372300880bdd075e1517fd8e007cc3f.html#define-documentation) +- [Define ICON_ROOTEX_REFRESH](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1ab78662fa3c9195df96da8a44309bf160.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1ab78662fa3c9195df96da8a44309bf160.html#define-documentation) +- [Define ICON_ROOTEX_REPEAT](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a24b8bb0215f5af38f67490fabd16ffc1.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1a24b8bb0215f5af38f67490fabd16ffc1.html#define-documentation) +- [Define ICON_ROOTEX_SEARCH](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1aab77d20219834962697f4ce3bb5c9482.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1aab77d20219834962697f4ce3bb5c9482.html#define-documentation) +- [Define ICON_ROOTEX_WINDOW_CLOSE](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1af5dae6a230d5b9be3b02f7f9b2c60536.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_imgui__helpers_8h_1af5dae6a230d5b9be3b02f7f9b2c60536.html#define-documentation) +- [Define interface](https://rootex.readthedocs.io/en/latest/api/define_custom__render__interface_8h_1a8f8bdbe5685d2ab60ca313c61017b92a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_custom__render__interface_8h_1a8f8bdbe5685d2ab60ca313c61017b92a.html#define-documentation) +- [Define interface](https://rootex.readthedocs.io/en/latest/api/define_input__interface_8h_1a8f8bdbe5685d2ab60ca313c61017b92a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_input__interface_8h_1a8f8bdbe5685d2ab60ca313c61017b92a.html#define-documentation) +- [Define interface](https://rootex.readthedocs.io/en/latest/api/define_ui__component_8h_1a8f8bdbe5685d2ab60ca313c61017b92a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_ui__component_8h_1a8f8bdbe5685d2ab60ca313c61017b92a.html#define-documentation) +- [Define interface](https://rootex.readthedocs.io/en/latest/api/define_ui__system_8h_1a8f8bdbe5685d2ab60ca313c61017b92a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_ui__system_8h_1a8f8bdbe5685d2ab60ca313c61017b92a.html#define-documentation) +- [Define LIGHTMAP_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ad5806b69069bddac520dd89a25011a2e.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ad5806b69069bddac520dd89a25011a2e.html#define-documentation) +- [Define LIGHTMAP_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1aa65763082dc509e42fcd2f5982c38833.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1aa65763082dc509e42fcd2f5982c38833.html#define-documentation) +- [Define MAX_BONES](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1abdb86b63a3ac2d2b3d6ad946ea404087.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1abdb86b63a3ac2d2b3d6ad946ea404087.html#define-documentation) +- [Define MAX_BUFFER_QUEUE_LENGTH](https://rootex.readthedocs.io/en/latest/api/define_streaming__audio__buffer_8h_1abac951b509e9eeb028d1552827fd3ed5.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_streaming__audio__buffer_8h_1abac951b509e9eeb028d1552827fd3ed5.html#define-documentation) +- [Define MAX_COMPONENT_ARRAY_SIZE](https://rootex.readthedocs.io/en/latest/api/define_component__array_8h_1aa64a72d317f087fbe76af4e1f9095d6c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_component__array_8h_1aa64a72d317f087fbe76af4e1f9095d6c.html#define-documentation) +- [Define MAX_DYNAMIC_POINT_LIGHTS](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1afbf96e06878038cf817167f85f54b874.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1afbf96e06878038cf817167f85f54b874.html#define-documentation) +- [Define MAX_DYNAMIC_POINT_LIGHTS](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1afbf96e06878038cf817167f85f54b874.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1afbf96e06878038cf817167f85f54b874.html#define-documentation) +- [Define MAX_DYNAMIC_SPOT_LIGHTS](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a1c343d9bfaa7040804e3e99acf91e115.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a1c343d9bfaa7040804e3e99acf91e115.html#define-documentation) +- [Define MAX_DYNAMIC_SPOT_LIGHTS](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a1c343d9bfaa7040804e3e99acf91e115.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a1c343d9bfaa7040804e3e99acf91e115.html#define-documentation) +- [Define MAX_LOD_COUNT](https://rootex.readthedocs.io/en/latest/api/define_mesh_8h_1a858ae6fddbf8f015edeb5e3f16e6c1a1.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_mesh_8h_1a858ae6fddbf8f015edeb5e3f16e6c1a1.html#define-documentation) +- [Define MAX_PARTICLES](https://rootex.readthedocs.io/en/latest/api/define_cpu__particles__component_8h_1a43b318e80d2457f5ce3e00a6cf1543c8.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_cpu__particles__component_8h_1a43b318e80d2457f5ce3e00a6cf1543c8.html#define-documentation) +- [Define MAX_STATIC_POINT_LIGHTS](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ae502032c7445b61e9fcd816cb7095022.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ae502032c7445b61e9fcd816cb7095022.html#define-documentation) +- [Define MAX_STATIC_POINT_LIGHTS_AFFECTING_1_OBJECT](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1af9e38e99dc8e9eb43bda0fec08123a8a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1af9e38e99dc8e9eb43bda0fec08123a8a.html#define-documentation) +- [Define MIN_TO_S](https://rootex.readthedocs.io/en/latest/api/define_audio__source_8h_1a879fcc102be1f9d96e7e61ce540b9291.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_audio__source_8h_1a879fcc102be1f9d96e7e61ce540b9291.html#define-documentation) +- [Define MS_TO_NS](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1ab031f10fc3e40f899afdd9cf3d33d5b8.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1ab031f10fc3e40f899afdd9cf3d33d5b8.html#define-documentation) +- [Define MS_TO_S](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1afe34dc34d0506357833dc20fbe43980a.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1afe34dc34d0506357833dc20fbe43980a.html#define-documentation) +- [Define NOMINMAX](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a9f918755b601cf4bffca775992e6fb90.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a9f918755b601cf4bffca775992e6fb90.html#define-documentation) +- [Define NORMAL_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a1362a63e474297fd42b933b568ea324d.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a1362a63e474297fd42b933b568ea324d.html#define-documentation) +- [Define NORMAL_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ab2bf8f87ca985d5b99e906c6931dcacf.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ab2bf8f87ca985d5b99e906c6931dcacf.html#define-documentation) +- [Define NS_TO_MS](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a64496d890506bdbc5e0778140dd887db.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a64496d890506bdbc5e0778140dd887db.html#define-documentation) +- [Define PANIC](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a5db566f159c7138fc739eb6ab118a2bc.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a5db566f159c7138fc739eb6ab118a2bc.html#define-documentation) +- [Define PANIC_SILENT](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1ac9454922c0b1676d17cf16a241f71a37.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1ac9454922c0b1676d17cf16a241f71a37.html#define-documentation) +- [Define PER_CAMERA_CHANGE_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a602bb94d5c7d55365f5a44d46549168f.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a602bb94d5c7d55365f5a44d46549168f.html#define-documentation) +- [Define PER_CAMERA_CHANGE_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1aa584390d5abccc0624c3776d80256fb4.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1aa584390d5abccc0624c3776d80256fb4.html#define-documentation) +- [Define PER_CAMERA_CHANGE_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1abb22b2316188d38fe1915fc375c935bf.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1abb22b2316188d38fe1915fc375c935bf.html#define-documentation) +- [Define PER_CAMERA_CHANGE_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1abeb84a5ee4b6fa183e8d970b24134543.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1abeb84a5ee4b6fa183e8d970b24134543.html#define-documentation) +- [Define PER_DECAL_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a2630b3fdb8f766aa2049e82b891bf1a3.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a2630b3fdb8f766aa2049e82b891bf1a3.html#define-documentation) +- [Define PER_DECAL_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a9b8b4eb882643f2f3db3b44245601fe5.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a9b8b4eb882643f2f3db3b44245601fe5.html#define-documentation) +- [Define PER_FRAME_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a188262ed8e10972f58724503ba036b6c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a188262ed8e10972f58724503ba036b6c.html#define-documentation) +- [Define PER_FRAME_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a22953892bd350b27e9971dacde2d51da.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a22953892bd350b27e9971dacde2d51da.html#define-documentation) +- [Define PER_FRAME_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a9e5245db127e734a49c6f7c41406def4.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a9e5245db127e734a49c6f7c41406def4.html#define-documentation) +- [Define PER_FRAME_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a51f892c5820cd75cf684af4850d1a071.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a51f892c5820cd75cf684af4850d1a071.html#define-documentation) +- [Define PER_MODEL_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a5c85fd49cb87c9317abf39c13282e5b8.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a5c85fd49cb87c9317abf39c13282e5b8.html#define-documentation) +- [Define PER_MODEL_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a8723ffc634d75e6b73d51335a090640f.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a8723ffc634d75e6b73d51335a090640f.html#define-documentation) +- [Define PER_OBJECT_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ac628189378bf6b98cc438b6b7dfc1c82.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ac628189378bf6b98cc438b6b7dfc1c82.html#define-documentation) +- [Define PER_OBJECT_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a047debe80cf95df452036b96bea21ada.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a047debe80cf95df452036b96bea21ada.html#define-documentation) +- [Define PER_OBJECT_VS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a105c8eda9b2ba59b613a0f4a2dffcb46.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a105c8eda9b2ba59b613a0f4a2dffcb46.html#define-documentation) +- [Define PER_OBJECT_VS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a0413e2bff125db41d4bbbf281bef214d.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__vertex__shader_8h_1a0413e2bff125db41d4bbbf281bef214d.html#define-documentation) +- [Define PER_SCENE_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a3172315e43a34ee9dd2e4ab66ca6beb9.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a3172315e43a34ee9dd2e4ab66ca6beb9.html#define-documentation) +- [Define PER_SCENE_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a77b349ffee3e7a6767344e7b99fbce00.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a77b349ffee3e7a6767344e7b99fbce00.html#define-documentation) +- [Define PRINT](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a392194594f3d768f2de3791a9a5b4049.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a392194594f3d768f2de3791a9a5b4049.html#define-documentation) +- [Define PRINT_SILENT](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a47ed5b418e66ec4436133416952cdb41.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a47ed5b418e66ec4436133416952cdb41.html#define-documentation) +- [Define ROOT_MARKER_FILENAME](https://rootex.readthedocs.io/en/latest/api/define_os_8h_1a00b095959eaf7d49d2cfaf05b1e1fc57.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_os_8h_1a00b095959eaf7d49d2cfaf05b1e1fc57.html#define-documentation) +- [Define ROOT_SCENE_ID](https://rootex.readthedocs.io/en/latest/api/define_scene_8h_1a4203e8efacee0307282fe771521765fd.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_scene_8h_1a4203e8efacee0307282fe771521765fd.html#define-documentation) +- [Define S_TO_MS](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a5158f69fbfdd762018685cdd07fda63d.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a5158f69fbfdd762018685cdd07fda63d.html#define-documentation) +- [Define SAMPLER_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a207edbd481c11402861279e21e9254f3.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a207edbd481c11402861279e21e9254f3.html#define-documentation) +- [Define SAMPLER_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a5c18d330b7174230966402f981d431e1.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a5c18d330b7174230966402f981d431e1.html#define-documentation) +- [Define SKY_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ade75f722fee8a2d7b5c894d6d9941d8c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1ade75f722fee8a2d7b5c894d6d9941d8c.html#define-documentation) +- [Define SKY_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a10bc7f612f6845354be9b720f71f331c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a10bc7f612f6845354be9b720f71f331c.html#define-documentation) +- [Define SOFT_DEPENDS_ON](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a9f27e07b5940a7f4ae70c19261bc9d65.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a9f27e07b5940a7f4ae70c19261bc9d65.html#define-documentation) +- [Define SOL_ALL_SAFETIES_ON](https://rootex.readthedocs.io/en/latest/api/define_interpreter_8h_1af49a65454b3af6c3580a532b5f86028e.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_interpreter_8h_1af49a65454b3af6c3580a532b5f86028e.html#define-documentation) +- [Define SOL_PRINT_ERRORS](https://rootex.readthedocs.io/en/latest/api/define_interpreter_8h_1a9b336849f72b70b54cdc292f391fc103.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_interpreter_8h_1a9b336849f72b70b54cdc292f391fc103.html#define-documentation) +- [Define SOL_STD_VARIANT](https://rootex.readthedocs.io/en/latest/api/define_interpreter_8h_1a56711a7506261a26b38ba4a61c28ed93.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_interpreter_8h_1a56711a7506261a26b38ba4a61c28ed93.html#define-documentation) +- [Define SOL_USING_CXX_LUA](https://rootex.readthedocs.io/en/latest/api/define_interpreter_8h_1a6a104a8eefda3ed95f4aa9b93bf5575c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_interpreter_8h_1a6a104a8eefda3ed95f4aa9b93bf5575c.html#define-documentation) +- [Define SPECULAR_PS_CPP](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a40d7b378bc651eaec80ffe96d790bedf.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a40d7b378bc651eaec80ffe96d790bedf.html#define-documentation) +- [Define SPECULAR_PS_HLSL](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a0c3208c732aa04f8281fdca610ebec7c.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_register__locations__pixel__shader_8h_1a0c3208c732aa04f8281fdca610ebec7c.html#define-documentation) +- [Define STRICT](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a8a7c30a576d5706b6c0821834d01cbbc.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a8a7c30a576d5706b6c0821834d01cbbc.html#define-documentation) +- [Define WARN](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1aa764a4e2c091f29ebe63819732dbd58b.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1aa764a4e2c091f29ebe63819732dbd58b.html#define-documentation) +- [Define WARN_SILENT](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a52958d42c4af8285115c51dbeacf47e5.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_common_8h_1a52958d42c4af8285115c51dbeacf47e5.html#define-documentation) +- [Define WINVER](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a966cd377b9f3fdeb1432460c33352af1.html) +- [Define Documentation](https://rootex.readthedocs.io/en/latest/api/define_types_8h_1a966cd377b9f3fdeb1432460c33352af1.html#define-documentation) +### Typedefs +- [Typedef ALfloat](https://rootex.readthedocs.io/en/latest/api/typedef_audio__source_8h_1abb26671ce5d302ed61a2e55256624169.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_audio__source_8h_1abb26671ce5d302ed61a2e55256624169.html#typedef-documentation) +- [Typedef ALuint](https://rootex.readthedocs.io/en/latest/api/typedef_audio__source_8h_1ae0292edc5c1c47db9accee3f49933e6f.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_audio__source_8h_1ae0292edc5c1c47db9accee3f49933e6f.html#typedef-documentation) +- [Typedef Array](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a02a4d2ff451c16850ec9bccb6f4a651c.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a02a4d2ff451c16850ec9bccb6f4a651c.html#typedef-documentation) +- [Typedef Atomic](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ad193c890cb266cb98b80471731fed249.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ad193c890cb266cb98b80471731fed249.html#typedef-documentation) +- [Typedef BoundingBox](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a084fdc0028cb54a51c9c75461bf55333.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a084fdc0028cb54a51c9c75461bf55333.html#typedef-documentation) +- [Typedef Color](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a40273ae3939bd84f2757008bcc050abd.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a40273ae3939bd84f2757008bcc050abd.html#typedef-documentation) +- [Typedef ComponentID](https://rootex.readthedocs.io/en/latest/api/typedef_ecs__factory_8h_1a194ed8c0452b7ada84e379d91ecbabe7.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_ecs__factory_8h_1a194ed8c0452b7ada84e379d91ecbabe7.html#typedef-documentation) +- [Typedef ComponentID](https://rootex.readthedocs.io/en/latest/api/typedef_entity_8h_1a194ed8c0452b7ada84e379d91ecbabe7.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_entity_8h_1a194ed8c0452b7ada84e379d91ecbabe7.html#typedef-documentation) +- [Typedef DeviceButtonID](https://rootex.readthedocs.io/en/latest/api/typedef_input__manager_8h_1acd345b8bb14b855e8e42783262edabb8.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_input__manager_8h_1acd345b8bb14b855e8e42783262edabb8.html#typedef-documentation) +- [Typedef FileBuffer](https://rootex.readthedocs.io/en/latest/api/typedef_os_8h_1a9ea848ea177ca8a918f8eb03862a7542.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_os_8h_1a9ea848ea177ca8a918f8eb03862a7542.html#typedef-documentation) +- [Typedef FilePath](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a2a189a15a4c363f39b113a337ea6fe67.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a2a189a15a4c363f39b113a337ea6fe67.html#typedef-documentation) +- [Typedef FileTimePoint](https://rootex.readthedocs.io/en/latest/api/typedef_os_8h_1a82c3859d0e96b9a8de2c4db6c8804c83.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_os_8h_1a82c3859d0e96b9a8de2c4db6c8804c83.html#typedef-documentation) +- [Typedef Function](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a4c9eaf0a03e818763b65ec4f5080d963.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a4c9eaf0a03e818763b65ec4f5080d963.html#typedef-documentation) +- [Typedef Future](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1af04f4108c0cdd2e5d12a9b304efdc571.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1af04f4108c0cdd2e5d12a9b304efdc571.html#typedef-documentation) +- [Typedef HashMap](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a80dc096d861026ff1a96e5ab0b703cc0.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a80dc096d861026ff1a96e5ab0b703cc0.html#typedef-documentation) +- [Typedef InputBoolListenerFunction](https://rootex.readthedocs.io/en/latest/api/typedef_input__listener_8h_1a65fbd80a4fa60d907b026343972592c1.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_input__listener_8h_1a65fbd80a4fa60d907b026343972592c1.html#typedef-documentation) +- [Typedef InputFileStream](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ad13311ff4623a0469b27eb6f3e38e7b4.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ad13311ff4623a0469b27eb6f3e38e7b4.html#typedef-documentation) +- [Typedef InputFloatListenerFunction](https://rootex.readthedocs.io/en/latest/api/typedef_input__listener_8h_1a16cfced049ff9d34a1e37eb79bec756a.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_input__listener_8h_1a16cfced049ff9d34a1e37eb79bec756a.html#typedef-documentation) +- [Typedef InputOutputFileStream](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1aef36820cf765f41cdea1b532cabe2e59.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1aef36820cf765f41cdea1b532cabe2e59.html#typedef-documentation) +- [Typedef KeyboardButton](https://rootex.readthedocs.io/en/latest/api/typedef_input__manager_8h_1abd58e5bf424e5c2bdf205577977000c6.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_input__manager_8h_1abd58e5bf424e5c2bdf205577977000c6.html#typedef-documentation) +- [Typedef Map](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a279bd67efa50e32d06af7dda748b24e2.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a279bd67efa50e32d06af7dda748b24e2.html#typedef-documentation) +- [Typedef Matrix](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a24e73d18a34576ef690245cc0ce1b692.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a24e73d18a34576ef690245cc0ce1b692.html#typedef-documentation) +- [Typedef MouseButton](https://rootex.readthedocs.io/en/latest/api/typedef_input__manager_8h_1a4478ed6cdb419e4c13287c24a6405b92.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_input__manager_8h_1a4478ed6cdb419e4c13287c24a6405b92.html#typedef-documentation) +- [Typedef Mutex](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a2cdb1fa98da4079d3a6c4ad3e2afe4af.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a2cdb1fa98da4079d3a6c4ad3e2afe4af.html#typedef-documentation) +- [Typedef Optional](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1aaf8f4d77efd311ec7818ab421294ef82.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1aaf8f4d77efd311ec7818ab421294ef82.html#typedef-documentation) +- [Typedef OutputFileStream](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a51dee55bd8f33a93ce7ad3933586eee8.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a51dee55bd8f33a93ce7ad3933586eee8.html#typedef-documentation) +- [Typedef PadButton](https://rootex.readthedocs.io/en/latest/api/typedef_input__manager_8h_1a35cc4ff2746c6ab03c7ce0606b6ae258.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_input__manager_8h_1a35cc4ff2746c6ab03c7ce0606b6ae258.html#typedef-documentation) +- [Typedef Pair](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a038d13db1235029fc866df1c6c811b4d.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a038d13db1235029fc866df1c6c811b4d.html#typedef-documentation) +- [Typedef Promise](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1acb8bc417dfe321c48b1808b0b9683d25.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1acb8bc417dfe321c48b1808b0b9683d25.html#typedef-documentation) +- [Typedef Ptr](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a6b46abcd7303a8f484bd03805eb49bbc.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a6b46abcd7303a8f484bd03805eb49bbc.html#typedef-documentation) +- [Typedef Quaternion](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ab9b7bca1c68cda31fbcce164aeb5563a.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ab9b7bca1c68cda31fbcce164aeb5563a.html#typedef-documentation) +- [Typedef Ray](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1aa6864dcd9d3028d31437452d4e5541c5.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1aa6864dcd9d3028d31437452d4e5541c5.html#typedef-documentation) +- [Typedef RecursiveMutex](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ad7c45ea6d2ca18ba1698071f198b86a6.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ad7c45ea6d2ca18ba1698071f198b86a6.html#typedef-documentation) +- [Typedef Ref](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1add5e90b302c31b74a46619f240214bcc.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1add5e90b302c31b74a46619f240214bcc.html#typedef-documentation) +- [Typedef ResourceCollection](https://rootex.readthedocs.io/en/latest/api/typedef_resource__file_8h_1ae3809282f7ea082afa0cdde7f3f7a3da.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_resource__file_8h_1ae3809282f7ea082afa0cdde7f3f7a3da.html#typedef-documentation) +- [Typedef SceneID](https://rootex.readthedocs.io/en/latest/api/typedef_entity_8h_1a4276516c60e90dcc61adda40ef8dd0e5.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_entity_8h_1a4276516c60e90dcc61adda40ef8dd0e5.html#typedef-documentation) +- [Typedef Stack](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ad5001f0ee725caf5aed41c7eda1fd0a1.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ad5001f0ee725caf5aed41c7eda1fd0a1.html#typedef-documentation) +- [Typedef String](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1afbeda3fd1bdc8c37d01bdf9f5c8274ff.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1afbeda3fd1bdc8c37d01bdf9f5c8274ff.html#typedef-documentation) +- [Typedef StringStream](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a3b00b4d9ec9db1584a445acde990ced6.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a3b00b4d9ec9db1584a445acde990ced6.html#typedef-documentation) +- [Typedef TimePoint](https://rootex.readthedocs.io/en/latest/api/typedef_timer_8h_1a4ae0c5bc1434c462a4197f0d9e59e93b.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_timer_8h_1a4ae0c5bc1434c462a4197f0d9e59e93b.html#typedef-documentation) +- [Typedef Tuple](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a2815493df44cd8157eacb7ef39d58138.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a2815493df44cd8157eacb7ef39d58138.html#typedef-documentation) +- [Typedef Variant](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a3adce165484849ad66a6aec621b6753d.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a3adce165484849ad66a6aec621b6753d.html#typedef-documentation) +- [Typedef VariantVector](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a74283f33f14838a6a65abe8b6207fc54.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a74283f33f14838a6a65abe8b6207fc54.html#typedef-documentation) +- [Typedef Vector](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a81fe4d2f62958ae48f36d6a3beb16bb1.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a81fe4d2f62958ae48f36d6a3beb16bb1.html#typedef-documentation) +- [Typedef Vector2](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a4c46a251392ed9485da1aab2e9725361.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a4c46a251392ed9485da1aab2e9725361.html#typedef-documentation) +- [Typedef Vector3](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1afa85d38f1a7695b11573232835a4e7f9.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1afa85d38f1a7695b11573232835a4e7f9.html#typedef-documentation) +- [Typedef Vector4](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a4e5c62385295c16099cc64eef97fd81d.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a4e5c62385295c16099cc64eef97fd81d.html#typedef-documentation) +- [Typedef Weak](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a2e7a6e854788459c6106f8fb9734a306.html) +- [Typedef Documentation](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a2e7a6e854788459c6106f8fb9734a306.html#typedef-documentation) diff --git a/demo/document-rag/documents/Rootex_engine_architecture.md b/demo/document-rag/documents/Rootex_engine_architecture.md new file mode 100644 index 0000000..24e0465 --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_architecture.md @@ -0,0 +1,23 @@ + + +Architecture — Rootex documentation +- Architecture +# Architecture +Games using the Rootex Engine are build using the Entity-Component-System architecture. +Rootex is essentally a framework to allow ECS based implementations of games. Details about each of Rootex’ important modules is provided below. +- [Framework](https://rootex.readthedocs.io/en/latest/engine/framework.html) +- [Component](https://rootex.readthedocs.io/en/latest/engine/framework.html#component) +- [Entity](https://rootex.readthedocs.io/en/latest/engine/framework.html#entity) +- [System](https://rootex.readthedocs.io/en/latest/engine/framework.html#system) +- [Scene](https://rootex.readthedocs.io/en/latest/engine/framework.html#scene) +- [Pausing](https://rootex.readthedocs.io/en/latest/engine/framework.html#pausing) +- [Event Manager](https://rootex.readthedocs.io/en/latest/engine/events.html) +- [Multithreading](https://rootex.readthedocs.io/en/latest/engine/multithreading.html) +- [Resources](https://rootex.readthedocs.io/en/latest/engine/resources.html) +- [ResourceLoader](https://rootex.readthedocs.io/en/latest/engine/resources.html#resourceloader) +- [Audio](https://rootex.readthedocs.io/en/latest/engine/audio.html) +- [Rendering](https://rootex.readthedocs.io/en/latest/engine/rendering.html) +- [Physics](https://rootex.readthedocs.io/en/latest/engine/physics.html) +- [Inputs](https://rootex.readthedocs.io/en/latest/engine/inputs.html) +- [Scripting](https://rootex.readthedocs.io/en/latest/engine/scripting.html) +- [Scripting API](https://rootex.readthedocs.io/en/latest/engine/scripting.html#scripting-api) diff --git a/demo/document-rag/documents/Rootex_engine_audio.md b/demo/document-rag/documents/Rootex_engine_audio.md new file mode 100644 index 0000000..d85ed5d --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_audio.md @@ -0,0 +1,8 @@ + + +Audio — Rootex documentation +- Audio +# Audio +Audio in Rootex has been implemented using OpenAL 1.1. Rootex supports both stereo and mono sound effects, as well as longer duration music. +Rootex is aware of the delay that might occur while trying to play large length audio pieces at once. To rectify this, Rootex implements Audio Streaming with [Class MusicComponent](https://rootex.readthedocs.io/en/latest/api/class_music_component.html#class-musiccomponent) and shorter sound effects that need to be loaded and played as fast as possible are implemented as [Class ShortMusicComponent](https://rootex.readthedocs.io/en/latest/api/class_short_music_component.html#class-shortmusiccomponent). +Rootex also supports audio attenuation models like Linear, Exponential and their respective clamped versions, as offered by OpenAL. However, audio attenuation works only with mono channel audio. diff --git a/demo/document-rag/documents/Rootex_engine_events.md b/demo/document-rag/documents/Rootex_engine_events.md new file mode 100644 index 0000000..7c3702c --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_events.md @@ -0,0 +1,15 @@ + + +Event Manager — Rootex documentation +- Event Manager +# Event Manager +Events are a way to solve the problem of sphagetti code, and an efficient way to implement the publisher-subscriber design pattern in a considerably large codebase. +Events ([Class Event](https://rootex.readthedocs.io/en/latest/api/class_event.html#class-event)) in Rootex are the equivalent of broadcast messages of a particular channel and Rootex’ Event Manager ([Class EventManager](https://rootex.readthedocs.io/en/latest/api/class_event_manager.html#class-eventmanager)) is the equivalent of a broadcast company managing multiple channels. Functions can be registered as callbacks to events. When an event is called, all the functions registered to that event are called with the corresponding data related to the cause of origin of that event. Rootex’ event manager has the ability to call both global functions and member functions (with the corresponding object that registered its member function, as a parameter into the member function, which is how C++ implements member functions). +[Class EventManager](https://rootex.readthedocs.io/en/latest/api/class_event_manager.html#class-eventmanager) is a singleton, and all engine level events are passed by it. User events can also be channeled through with no issues. E.g. Input events that are configured by the user are sent through the engine level event manager. +Optionally EventManager allows [Typedef Variant](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1ab10036d197bc23eea4d105ef1d9026b1.html#typedef-variant) data to be passed along with an event call, which are further of 2 types: +- +Call: The registered handlers are called immediately. This is useful for non destructive activity. +- +Deferred Call: The registered handlers are called at the end of a frame. This is especially useful for destrutive activity related to entities, components and systems, as the engine is very likely to be iterating on them and deletion may lead to corruption. +Rootex editor has been made on top of the rootex engine. The engine is never aware of the existence of editor. This is made possible by the help of events. +See [Struct RootexEvents](https://rootex.readthedocs.io/en/latest/api/struct_rootex_events.html#struct-rootexevents), [Struct EditorEvents](https://rootex.readthedocs.io/en/latest/api/struct_editor_events.html#struct-editorevents), [Class Event](https://rootex.readthedocs.io/en/latest/api/class_event.html#class-event) diff --git a/demo/document-rag/documents/Rootex_engine_framework.md b/demo/document-rag/documents/Rootex_engine_framework.md new file mode 100644 index 0000000..fb1f371 --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_framework.md @@ -0,0 +1,33 @@ + + +Framework — Rootex documentation +- Framework +# Framework +Rootex has certain funcitionalities built-in to support an ECS + Godot-like Scene implementation. The main aim of this kind of a architecture is to break down the game into a collection of behaviors and perceiving the game as an interplay of behaviors, rather than hardcoded functionalities inside each game object through object oriented programming. It originates from a place in the programming world that suggests “Composition is better than Inheritance”. +ECS just implements a dynamic composition, in the sense that behaviors can be added to game objects at runtime. +One of the popular side benefits of ECS is increased cache coherency. +Scenes allow users to organise and connect their entities into trees, providing powerful features to manipulate entity hierarchies, both at level design time and runtime. +Rootex’ ECS architecture has 3 main parts. +# Component +[Class Component](https://rootex.readthedocs.io/en/latest/api/class_component.html#class-component) +A Component is a collection of data for the game to use. Components are analogous to behaviors and components store some peculiar data to maintain their behavior. Component do not do anything else. They may allow changing the data in a certain manner from their public API. +Instances of a type of component are often iterated en masse. Hence all instances of a component type are stored in a special kind of array which allows max MAX_COMPONENT_ARRAY_SIZE instances to exist, but prevents changing location of any instance. The sole ownership of the components lies with this array. Everything else gets raw pointers to elements of this array. +Components can also register dependencies on other components, in form of hard or soft [Define DEPENDENCY](https://rootex.readthedocs.io/en/latest/api/define_component_8h_1a76d6d3f7803dbffb744669d8311c8e62.html#define-dependency). Hard dependency on a component means that the dependent component cannot function without it and creation of the component is blocked if the dependency isn’t fulfilled. Soft dependency is optional dependency, without which a component may be able to function, albeit limitedly and component creation is not blocked if a soft dependency is not fulfilled. +# Entity +[Class Entity](https://rootex.readthedocs.io/en/latest/api/class_entity.html#class-entity) +An Entity in Rootex is a collection of components. The entity will have a name additionally but all data being used in the game will be stored in one of the components of an entity. Entities provide the component with an identity so that components can be theorized to “belong” to a thing in the game. Entites can be globally identified from their IDs, which is guaranteed to be uniquely generated. +An Entity stores a HashMap of all the components assigned to it. The sole ownership(Ptr) of an entity belongs to it’s owner scene. Everything else gets raw pointers. +# System +[Class System](https://rootex.readthedocs.io/en/latest/api/class_system.html#class-system) +A System in Rootex is containing all the logic/algorithms that are needs to make sense of the data that is stored inside a specific type of component. Systems only interact with a certain type of components. In Rootex, all components of similar type are stored in an array and all these arrays containing different types of components are stored in a hash map so that the array having an component type can be indexed and used for processing by a _exhale_class_class_system. +Systems often require iterating over componets of a type, this can only be accomplished by using range based for loops as we have a custom iterator that only returns “valid” elements from our custom component array. +## Scene +A scene is a hierarchical data structure which can optionally store and entity. It stores children nodes of the same type and controls their lifetime based of if the parent is alive. Once the parent has decided to kill itself, it makes sure its children also meet the same fate. +Scene’s store a [Typedef Ptr](https://rootex.readthedocs.io/en/latest/api/typedef_types_8h_1a6b46abcd7303a8f484bd03805eb49bbc.html#typedef-ptr) to an entity and provide structure to out hybrid ECS + Scene architecture. +Rootex uses JSON style serializations to store and load scenes. Entities are also created from these files with all the necessary components. +A scene subtree can be saved to a file/loaded from a file to allow functionality reuse both during level design phase and runtime. +Scene files are recognized by .scene.json file extensions. +Scene construction is handled by [Class Scene](https://rootex.readthedocs.io/en/latest/api/class_scene.html#class-scene) itself but it delegates the entity construction using the JSON data to [Class ECSFactory](https://rootex.readthedocs.io/en/latest/api/class_e_c_s_factory.html#class-ecsfactory). +Each [Class Component](https://rootex.readthedocs.io/en/latest/api/class_component.html#class-component) accepts owner entity and constituent json data in its constructor, allowing it to setup its data memebrs from the serialised data. Every Component also defines a getJSON method which serialises its members back to JSON format. The data retention across engine restarts is guarranteed through ensuring that the component use the same data which it generates while saving the scene. +# Pausing +Pausing is tightly bound to ECS + Scenes. The engine provides a pause UI scene, which is enbled on pressing ESC key. All scenes which have the “Stop Scene during Pause” checkbox checked will have most of their components and scripts being skipped by Systems, effectively bringing the game logic to a pause. Certain scenes can be exempted from being skipped to allow stuff like Music to keep playing. diff --git a/demo/document-rag/documents/Rootex_engine_inputs.md b/demo/document-rag/documents/Rootex_engine_inputs.md new file mode 100644 index 0000000..4c45760 --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_inputs.md @@ -0,0 +1,39 @@ + + +Inputs — Rootex documentation +- Inputs +# Inputs +User inputs in Rootex are handled with polling and/or callback based mechanisms using the event system. Rootex also abstracts the exact button press occurences from the user and forces the user to use keybinding names strings to query for input. +[Class InputManager](https://rootex.readthedocs.io/en/latest/api/class_input_manager.html#class-inputmanager) handles the input for the entire engine. The input manager is initially feeded with a collection of ‘input schemes’. These input schemes are defined in the level JSON files likewise: +``` +// flappy_bird.scene.json +{ + "camera": 23, + "inputSchemes": { + "FlappyBird": { + "bools": [ + { + "inputEvent": "Jump", + "device": 1, + "button": 99 + } + ], + "floats": [] + }, + }, + "startScheme": "FlappyBird" +} +``` +The field `inputSchemes` is the collection of input schemes that the input manager will recognize. In this example, there is only 1 scheme called “FlappyBird”, but a game can have multiple input schemes and only one of those input schemes can be active at a time. The field `startScheme` tells the Rootex engine which input scheme should be selected as soon as the level is loaded up. +Each input scheme has a name identified by its key and the following fields: +- +`bools`: Array of inputs that are represented as boolean values. Used for buttons that are either held down or not held down. +- +`floats`: Array of inputs that are not digital in nature and rather are analogous, like mouse positions and joystick movements. +Inside each input keybinding, there are fields: +- +`inputEvent`: The event name that gets emitted as soon as the input changes state. This need not be unique across other keybindings under the same input scheme or even across input schemes. +- +`device`: The device enum value that this input keybinding is present on. +- +`button`: The button value that is mapped to the keybinding diff --git a/demo/document-rag/documents/Rootex_engine_multithreading.md b/demo/document-rag/documents/Rootex_engine_multithreading.md new file mode 100644 index 0000000..d4704b9 --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_multithreading.md @@ -0,0 +1,10 @@ + + +Multithreading — Rootex documentation +- Multithreading +# Multithreading +Rootex engine is multithreading ready, however its main focus is on single threaded operations. +Every Rootex application ([Class Application](https://rootex.readthedocs.io/en/latest/api/class_application.html#class-application)) has a pool of threads, called simply a threadpool in common CS language. These threads can be assigned work either by the engine or game code. +Rootex uses the concept of Worker threads, a.k.a. Job Based multithreading. +At startup, Rootex’ threadpool manager ([Class ThreadPool](https://rootex.readthedocs.io/en/latest/api/class_thread_pool.html#class-threadpool)) queries the CPU and returns the number of logical CPU cores in the system. The threadpool allocates the same number of threads and uses one of them to be the master thread that distributes “jobs” to different threads. Jobs are implemented as simple overriden virtual functions of [Class Task](https://rootex.readthedocs.io/en/latest/api/class_task.html#class-task). +During testing Rootex was run simply as a single threaded engine. As time went on, certain functions of Rootex were run in separate threads in a controlled multithreading environment. diff --git a/demo/document-rag/documents/Rootex_engine_physics.md b/demo/document-rag/documents/Rootex_engine_physics.md new file mode 100644 index 0000000..d5c383c --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_physics.md @@ -0,0 +1,9 @@ + + +Physics — Rootex documentation +- Physics +# Physics +Physics in Rootex has been implemented using the Bullet Collision Detection and Physics library ([https://pybullet.org/Bullet/BulletFull/index.html](https://pybullet.org/Bullet/BulletFull/index.html)) +Rootex uses the concept of colliders, adopted from the Bullet Physics library, to represent objects responding to physics and being controlled by it. +[Class PhysicsSystem](https://rootex.readthedocs.io/en/latest/api/class_physics_system.html#class-physicssystem) is responsible for providing physics to the Rootex engine. The physics system uses [Class PhysicsColliderComponent](https://rootex.readthedocs.io/en/latest/api/class_physics_collider_component.html#class-physicscollidercomponent) instances to perform physics based calculations on them. Rootex engine currently supports all the collision shapes provides by Bullet. There are also extra features available like ray casting a ray into the world and reporting the colliders that the ray touched. +The Rootex Editor also helps in visualizing the collider shapes enabled by viewing the collider component in the Inspector. diff --git a/demo/document-rag/documents/Rootex_engine_rendering.md b/demo/document-rag/documents/Rootex_engine_rendering.md new file mode 100644 index 0000000..0daa713 --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_rendering.md @@ -0,0 +1,11 @@ + + +Rendering — Rootex documentation +- Rendering +# Rendering +Rootex uses DirectX 11 to render graphics. +Rendering in Rootex has been implemented with special attention so that it behaves properly with our ECS architecture. The [Class RenderSystem](https://rootex.readthedocs.io/en/latest/api/class_render_system.html#class-rendersystem) uses the [Class RenderableComponent](https://rootex.readthedocs.io/en/latest/api/class_renderable_component.html#class-renderablecomponent) to share common funcitonalities across different components which add to the visuals of the scene. +The [Class RenderSystem](https://rootex.readthedocs.io/en/latest/api/class_render_system.html#class-rendersystem) uses the owning [Class Scene](https://rootex.readthedocs.io/en/latest/api/class_scene.html#class-scene) of the renderable component to recursively traverse the object hierarchy, starting from the root entity (which is persistent across levels). Every time the render system recognizes a parent, before processing its children, the render system takes note of the transform (a representation of position, rotation and scale all at once) of the parent and appends it to the transformation stack. The transformation stack is an implementation for inheriting transforms from the parent entity of a child entity, used while performing a Depth-First-Search on the component hierarchy established by hierarchy component instances. +The transformation stack of UI components is kept separate from the transformation stack of 3D world visual components. +Once all transformations are updated, [Class RenderSystem](https://rootex.readthedocs.io/en/latest/api/class_render_system.html#class-rendersystem) loops over all the renderable components and does the rendering required to show them. +Rootex also performs sky, fog and related rendering effects and post processing effects. diff --git a/demo/document-rag/documents/Rootex_engine_resources.md b/demo/document-rag/documents/Rootex_engine_resources.md new file mode 100644 index 0000000..d436ace --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_resources.md @@ -0,0 +1,15 @@ + + +Resources — Rootex documentation +- Resources +# Resources +Resources are first class members in the Rootex Engine. By resources, we refer to any file loaded data, that is processed and ready to be used in the engine. +# ResourceLoader +Resources are created and owned by the [Class ResourceLoader](https://rootex.readthedocs.io/en/latest/api/class_resource_loader.html#class-resourceloader) and distributed to the user and the engine as pointers to instances of the polymorphic [Class ResourceFile](https://rootex.readthedocs.io/en/latest/api/class_resource_file.html#class-resourcefile). [Class ResourceFile](https://rootex.readthedocs.io/en/latest/api/class_resource_file.html#class-resourcefile) has been subclassed multiple times to store different kinds of data like sounds, music, images, fonts, 3D models, normal text files like Lua files or JSON files, etc. Look up the documentation on the resource loader for more information. +Resources are often the heaviest parts of a game in terms of actual memory that they occupy. [Class ResourceLoader](https://rootex.readthedocs.io/en/latest/api/class_resource_loader.html#class-resourceloader) has been designed in such a manner that stores resources and distributes the earlier cached resource again instead of loading the same resource again to save memory, in case the same resource is instructed to be loaded more than once. Hence, the engine and the users need not worry about not loading the same resources multiple times. +Resources may change in the file system after they have been loaded by the engine. To fix this, all resources can detect if they have been changed by the file system and have the ability to reload their contents and re-process it on demand. +Resource Loading can often be a bottleneck for complex scenes in a simple game engine. Rootex provides 2 mechanisms to make Resoource handling smooth: +- +Multithreaded loading: ResourceFiles are loaded to memory in parallel and the create objects are allowed to reference each other once basic setup is done. +- +Preloads: A scene can define a list of filepaths as preloads. These resources are loaded into Resource cache during the initial loading of the scene, even if they’re not being directly referenced. By specifying preloads, We can prevent frame drops due to disk reads loading mid-gameplay. diff --git a/demo/document-rag/documents/Rootex_engine_scripting.md b/demo/document-rag/documents/Rootex_engine_scripting.md new file mode 100644 index 0000000..e83927d --- /dev/null +++ b/demo/document-rag/documents/Rootex_engine_scripting.md @@ -0,0 +1,52 @@ + + +Scripting — Rootex documentation +- Scripting +# Scripting +Rootex Engine has a fully scriptable interface implemented using Lua and the Sol3 ([https://sol2.readthedocs.io/en/latest/](https://sol2.readthedocs.io/en/latest/)) library for creating bindings. +Scripts i.e. Lua files, can be attached to entities and can define functions upon those entities. +See Middleclass ([https://github.com/kikito/middleclass](https://github.com/kikito/middleclass)) for details on the `class()` based Lua OOP support. +``` +EmptyScript = class("EmptyScript") + +-- First method called after script initialisation +-- not safe to refer other entity script tables here +-- setup initial data members here that don't refer entities +function EmptyScript:begin(entity) +end + +-- Called after all `begin` for the frame have been called +-- safe to assume that all scripts have `begin`ed and have +-- data members +function EmptyScript:enterScene(entity) + print("Nothing is true") +end + +-- called once every frame +function EmptyScript:update(entity, delta) +end + +-- called during entity destruction +function EmptyScript:destroy(entity) + print("Everything is permitted") +end + +-- called when Collider of the entity detects a hit +function EmptyScript:hit(hit) + print("Everything is permitted") +end + +-- called when entity enters a TriggerComponent +function EmptyScript:enterTrigger(entity, trigger) +end + +-- called when entity exits a TriggerComponent +function EmptyScript:exitTrigger(entity, trigger) +end + +return EmptyScript +``` +The functions are called into Lua from Rootex on the command of the [Class ScriptSystem](https://rootex.readthedocs.io/en/latest/api/class_script_system.html#class-scriptsystem). +The script files are run in a Lua VM and the Rootex functions available are registed by the [Class LuaInterpreter](https://rootex.readthedocs.io/en/latest/api/class_lua_interpreter.html#class-luainterpreter)’s implementation. The Lua scripting interface for Rootex mostly looks the same as the Rootex engine API that the engine uses internally to provide as vast a scripting environment as possible. All Rootex class names and functions are hidden under the `RTX` global Lua variable. An object which has its constructor registered can be constructed from scripts as `RTX.Type.new(args)`. +# Scripting API +You can find the scripting API and related docs in the editor itself. diff --git a/demo/document-rag/documents/Rootex_guides_editor_layout.md b/demo/document-rag/documents/Rootex_guides_editor_layout.md new file mode 100644 index 0000000..8f9c689 --- /dev/null +++ b/demo/document-rag/documents/Rootex_guides_editor_layout.md @@ -0,0 +1,54 @@ + + +Editor Layout — Rootex documentation +- Editor Layout +# Editor Layout +The main editor area is laid out in 6 major sections. +## Toolbar Dock +The toolbar dock displays different settings that affect the editor’s view of the game world and modify the Rootex Engine’s overall state. +You can find data related to the Editor FPS, registered Events in the EventManager, the current camera being used to view the world, etc. You can try fiddling with the settings here to know what each thing does. The toolbar dock also allows playing the currently open level in game or play the game from the original starting level as defined in the game settings. +Tip +Try setting the Camera in RenderSystem tab to EditorCamera to allow easy navigation. +## Output Dock +The output dock is the Rootex Engine’s channel to report stuff happening internally in the engine and in the editor. You can expect to see error messages, warnings and plain reports in the output dock. You will also notice a text input bar at the bottom of the output dock. +Use the command input to run Lua code in Rootex’s Lua VM. All of Rootex’s scripting API is available through this command line. +## Viewport Dock +The viewport dock provides the view into the game world through the default camera, though you want to change that to EditorCamera. The EditorCamera is an editor-only entity. Viewing the game world in the editor also enables a few perks that are only accessible in the editor and not the game. The view mode for the game world can be changed using the View main menu, usually present at the top of the window, alongside the File main menu and others. +### EditorCamera +The EditorCamera is an editor-only entity which is setup to be the view of the editor into the game world. +To view the world though EditorCamera, select EditorCamera as the current camera entity from Viewport > Current Camera. The EditorCamera can be controlled from the editor by holding Right Mouse Button and using WASD/Space/Shift to move. You can tweak the camera turning speed and the moving speed. +### Gizmo +The 3D gizmo is an editor-only tool to let the user change the position, rotation, and scale of selected entity in either local or world space. Scenes can be selected from the scene dock. +The gizmo has 3 separate modes of working. +- +Translation +Select an entity and press Q. In this mode the gizmo takes the shape of 3 axes point in orthogonal directions. These axes are selectable with the mouse pointer and position of entities can be altered by dragging. +- +Rotation +Select and entity and press W. In this mode the gizmo takes the shape of 3 circles with their axes going in orthogonal directions. These circles denote the rotation of the entities in Euler angles and rotation of the entities can be altered by dragging. +- +Scale +Select an entity and press E. In this mode the gizmo takes the shape of 3 axes in orthogonal directions. These axes denote the scales which can be altered by dragging. +The gizmo has 2 modifiers to each of the modes. The Local modifier will apply changes in the local coordinate system. The World modifier will apply changes in the world coordinate system. +### EditorGrid +There is one more editor-only entity that is helpful to the viewport. The EditorGrid displays the grid defined by the grid cells sizes. You can alter the grid settings by selecting EditorGrid in the scene dock. +## Scene Dock +The scene dock displays the parent-child hierarchy of scenes in the current game world. +The hierarchy between scenes is defined by the Scene class and its children. Scenes can be selected by clicking on their name in the scene dock or selecting the associated entity in the inspector. You can also change the hierarchy between scenes by dragging and dropping the scene over your chosen parent scene. +## Inspector Dock +Inspector dock is the main hub of all data related to components in an entity. Data under each component is available for change using the inpector dock. Use the scene dock, or click on the scene in the viewport to select them. +Inspector dock also allows changing the name of the scene, attaching Lua scripts, adding or changing or removing components, resetting inter-component linkages and deleting entities, along with instantiating new scenes as children from files and saving scenes to files. +## Content Browser Dock +The Cotent Browser allows access to the filesystem in the game/ directory withing the engine itself. +Content Browser can recognize supported filetypes and shows special icons for them, which can directly be dragged and dropped into suitable places. +Current drag and drop support: +- +Image -> Texture slots in Materials +- +Audio -> Music source track for MusicComponent and ShortMusicComponent +- +Model -> 3D Mesh for ModelComponent, Rigged skeletal mesh for AnimatedModelComponent, Collision mesh in MeshColliderComponent +- +Material -> Custom .rmat file format for RenderableComponent +- +Script -> Lua files for entity scripts, RML file for UIComponent diff --git a/demo/document-rag/documents/Rootex_guides_getting_help.md b/demo/document-rag/documents/Rootex_guides_getting_help.md new file mode 100644 index 0000000..5f6df3c --- /dev/null +++ b/demo/document-rag/documents/Rootex_guides_getting_help.md @@ -0,0 +1,8 @@ + + +Getting Help — Rootex documentation +- Getting Help +# Getting Help +You can try searching in the documentation to answer your query. +If you are stuck with using Rootex or discover any bug, or if the documentation is not helping you, kindly report that at our issue tracker on Github: [http://github.com/sdslabs/rootex/issues](http://github.com/sdslabs/rootex/issues) +Also you can join SDSLabs’ Open Source related Discord server: [https://discord.gg/sn4CSvzepP](https://discord.gg/sn4CSvzepP) diff --git a/demo/document-rag/documents/Rootex_guides_getting_started.md b/demo/document-rag/documents/Rootex_guides_getting_started.md new file mode 100644 index 0000000..db5cd93 --- /dev/null +++ b/demo/document-rag/documents/Rootex_guides_getting_started.md @@ -0,0 +1,50 @@ + + +Getting Started — Rootex documentation +- Getting Started +# Getting Started +Rootex is a pure Entity-Component-System architectured game engine. We use terms like scenes, entities, components and systems analogous to the domain of Scene trees and ECS architecture. Find more information about these [here](https://en.wikipedia.org/wiki/Entity_component_system) +Note +This also means that the Rootex Editor is made with the Rootex Engine itself. +Rootex Editor is structured to work like popular game engines with its simplistic user interface. +Any user coming from ECS based game engines should be able to pick up the interface quickly. +- [Rootex Editor](https://rootex.readthedocs.io/en/latest/guides/running_the_editor.html) +- [Running the Editor](https://rootex.readthedocs.io/en/latest/guides/running_the_editor.html#running-the-editor) +- [Editor Layout](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html) +- [Toolbar Dock](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html#toolbar-dock) +- [Output Dock](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html#output-dock) +- [Viewport Dock](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html#viewport-dock) +- [Scene Dock](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html#scene-dock) +- [Inspector Dock](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html#inspector-dock) +- [Content Browser Dock](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html#content-browser-dock) +- [Animating Objects using TransformAnimationComponent](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html) +- [Setting up](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html#setting-up) +- [Interface](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html#interface) +- [Keyframes](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html#keyframes) +- [Transition Type Examples](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html#transition-type-examples) +- [Animation Mode Examples](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html#animation-mode-examples) +- [Reset](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html#reset) +- [Play in Editor](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html#play-in-editor) +- [Play on Start](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html#play-on-start) +- [Exploring the Graphical capabilities of Rootex](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html) +- [Create a scene](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#create-a-scene) +- [Create Empty scene](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#create-empty-scene) +- [Giving Components](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#giving-components) +- [Light Component](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#light-component) +- [Editor Camera](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#editor-camera) +- [Point Light](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#point-light) +- [Overriding a material](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#overriding-a-material) +- [Custom Material](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#custom-material) +- [Adding a shader](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#adding-a-shader) +- [Decal Component](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html#decal-component) +- [Making HUD using UI-component](https://rootex.readthedocs.io/en/latest/guides/making_HUD_using_ui-component.html) +- [RmlUi](https://rootex.readthedocs.io/en/latest/guides/making_HUD_using_ui-component.html#rmlui) +- [Basic HUD](https://rootex.readthedocs.io/en/latest/guides/making_HUD_using_ui-component.html#basic-hud) +- [Fade-In effect](https://rootex.readthedocs.io/en/latest/guides/making_HUD_using_ui-component.html#fade-in-effect) +- [Making effects using ParticleEffectComponent](https://rootex.readthedocs.io/en/latest/guides/effects_using_ParticleEffectComponent.html) +- [Effekseer](https://rootex.readthedocs.io/en/latest/guides/effects_using_ParticleEffectComponent.html#effekseer) +- [Using the Effekseer exports (Demo)](https://rootex.readthedocs.io/en/latest/guides/effects_using_ParticleEffectComponent.html#using-the-effekseer-exports-demo) +- [Start Frame](https://rootex.readthedocs.io/en/latest/guides/effects_using_ParticleEffectComponent.html#start-frame) +- [Moving](https://rootex.readthedocs.io/en/latest/guides/effects_using_ParticleEffectComponent.html#moving) +- [Use Speed](https://rootex.readthedocs.io/en/latest/guides/effects_using_ParticleEffectComponent.html#use-speed) +- [Getting Help](https://rootex.readthedocs.io/en/latest/guides/getting_help.html) diff --git a/demo/document-rag/documents/Rootex_guides_graphics_tutorial.md b/demo/document-rag/documents/Rootex_guides_graphics_tutorial.md new file mode 100644 index 0000000..94dce34 --- /dev/null +++ b/demo/document-rag/documents/Rootex_guides_graphics_tutorial.md @@ -0,0 +1,111 @@ + + +Exploring the Graphical capabilities of Rootex — Rootex documentation +- Exploring the Graphical capabilities of Rootex +# Exploring the Graphical capabilities of Rootex +This documentation aims to showcase the graphical capabilities of rootex and act as a tutorial for beginners to get started. +Let’s start by creating a scene. +## Create a scene +To Create a Scene +1. +Go to file->CreateScene. +1. +Name the scene and click create. +Now we Create an Empty Scene. An empty scene is nothing but objects. You can have different components in it, more on that later. +## Create Empty scene +To create an empty scene. +1. +Right-click the root scene. +1. +Click Add Empty Scene +## Giving Components +Now we give components to the empty scene. +1. +Right-click the empty scene. +1. +Click Edit Components. +1. +Check the appropriate components, in this case, transform and Model. Note: Transform Component is a must. +1. +Open inspector. +1. +Go to the model component in the inspector. +1. +Click the folder icon next to Model. +1. +Select the sponza 3D model file located at `Rootex\game\assets\sponza\sponza.obj` +For sponza initially, it would look like this: +This is due to the default settings of the sponza obj file. To get a better view, set the scale to (0.031, 0.031, 0.031) and set the LOD distance to 123: +We need to create an empty scene and add a light component to it to add light. +## Light Component +To add light, we now create an empty scene. +1. +Name the scene. +1. +Add transform and directional light components. +To move freely, we can change our camera mode to Editor Camera. This allows us to move freely. +## Editor Camera +To have complete control of movement, you can use an editor camera. +1. +Click the figure icon at the top left of the viewport. +1. +Open dropdown of camera. +1. +Select editor camera. +To move, you have to hold the right mouse button and then use WASD space and shift keys to move. The cursor for direction. Space to move up and shift to move down. +## Point Light +A point light is helpful if you have a source of light, e.g. a candle, bulb etc. To add a point light, follow the given steps. +1. +Add an empty scene and give it a point light component. +1. +You can tweak its transformation value by either inputting it or dragging it left or right. +If you press ‘q’, a transform gizmo will appear on the object you have selected. You can adjust light location through it. For rotation and scaling gizmo, press ‘w’ and ‘e’, respectively. +## Overriding a material +To change the properties of one object without changing the original material, we can use overriding materials. To override a material: +1. +Create a new basic material by going to file -> Create Resource. +1. +Name the material and click create. +1. +Go to the `Inspector-> Model Component->Materials`. +1. +Click on the folder icon on the corresponding overriding material. +1. +Select the newly created basic material located at `Rootex\game\assets\materials\new_cloth.basic.rmat` +Now you can change its basic textures by 1)clicking on the pencil icon 2)In the file viewer now click on the diffuse texture and select the appropriate diffuse texture. +## Custom Material +1. +Go to create Resource -> Custom Material. +1. +Enter material name. +1. +Now go to Inspector -> ModelComponent and then to Materials. +1. +Click on the folder icon and choose the material. +## Adding a shader +To Add shader: +1. +Click on the pencil icon on the overriding custom material. +1. +Now, in the file viewer you’ll get options to add vertex and pixel shaders. +1. +Click on the pixel shader. A dialog box will open now you can just select the shader. +You can use fire_pixel_shader from rootex/core/renderer/shaders +Clicking on the pencil icon opens an editor to customise the shader. +Note +You can only add shaders to custom materials. If you want to use default material, override the original default material with custom material and then add a shader to the overriding material. The overriding material does inherit the textures of the original materials. +## Decal Component +To add a decal component. +1. +Make a scene DECAL and give it transform and Decal Component. +1. +Create a decal material. By going to File -> CreateResource. And then slect Decal material in resource type dropdown. +1. +Now go to the inspector and click DecalComponent. +1. +Click on the folder icon and select the decal material. +1. +Click on the pencil icon and the in the file viewer click on Decal Texture. +1. +Shift its position by manipulating the transform component. +By default, the decal shader projects on the negative z-axis. You can rotate it till you get the desired result. diff --git a/demo/document-rag/documents/Rootex_guides_hud_tutorial.md b/demo/document-rag/documents/Rootex_guides_hud_tutorial.md new file mode 100644 index 0000000..d91c466 --- /dev/null +++ b/demo/document-rag/documents/Rootex_guides_hud_tutorial.md @@ -0,0 +1,143 @@ + + +Making HUD using UI-component — Rootex documentation +- Making HUD using UI-component +# Making HUD using UI-component +UI creation is quite easy in Rootex. The UI-component spans the viewport and is not affected by the camera transform, so it can act be used for creating HUD, Menus etc. +## RmlUi +The UI-component makes use of RmlUi. RmlUi is a C++ UI library based on the HTML and CSS standards. It contains of RML (based loosely around XHTML 1.0 and HTML 4.01) and RCSS (based on CSS2). +[RmlUi Documentation](https://mikke89.github.io/RmlUiDoc/) +[RmlUi Source](https://github.com/mikke89/RmlUi) +## Basic HUD +Let’s create an empty scene. +Add a UIComponent to this scene +Make an empty scene with a TransformComponent and ModelComponent (basic cube) +Now add the following RML script to the UIComponent +``` + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +``` +``` + + + Demo + + + +

This is a sample!

+ +
+``` +This will add sample text to the top left of our viewport which will react on mouse hover. +Upon playing this scene and switching to EditorCamera, when we move the EditorCamera around, we can see the view of the cube changing, but the HUD stays in place. +## Fade-In effect +We can also make a simple fade-in effect. +RML code: +``` + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +``` +``` + + + Transition + + + + + +``` +Upon loading this in the UIComponent of our scene, a fade effect will trigger and the scene will go black. diff --git a/demo/document-rag/documents/Rootex_guides_particle_effects.md b/demo/document-rag/documents/Rootex_guides_particle_effects.md new file mode 100644 index 0000000..22e6062 --- /dev/null +++ b/demo/document-rag/documents/Rootex_guides_particle_effects.md @@ -0,0 +1,39 @@ + + +Making effects using ParticleEffectComponent — Rootex documentation +- Making effects using ParticleEffectComponent +# Making effects using ParticleEffectComponent +ParticleEffectComponent in Rootex can be used to make awesome particle effects! +## Effekseer +The ParticleEffectComponent makes use of [Effekseer](https://effekseer.github.io/en/). Effekseer is a particle effect creation tool which can export the effect as an `*.efkefc` file. +This `*.efkefc` file is used by the ParticleEffectComponent in Rootex to import the effects. +You can follow the [documentation](https://effekseer.github.io/en/documentation.html) and [tutorial](https://effekseer.github.io/en/documentation.html#tutorial_sec) of Effekseer to know how to create effects using it. +## Using the Effekseer exports (Demo) +Let’s start by creating an empty scene and adding the TransformComponent and ParticleEffectComponent to it. +We have added a demo effect which we get bundled with Effekseer 1.62b (Laser01.efkefc). To get this effect, download [Effekseer 1.62b](https://effekseer.github.io/en/download.html), unzip it and you’ll find the effect in Sample/00_Basic/ of the unzipped folder. +Add the effect by going to your scene’s ParticleEffectComponent in the Inspector and clicking on the folder icon to select the file. +We have tweaked the [EditorCamera](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html#editorcamera) to get a better view of the scene. +Explanation of options: +- +Play : Plays the loaded effect +- +Stop : Stops the playing effect +- +Play On Start : Sets whether to play the effect at editor and game start or not. +- +Start Frame : Sets the frame from which effect starts. +- +Moving : Sets if the effect moves with the transform component once playing. +- +Use Speed : Running the effect at a user defined spped. Default speed is 1. +While Play, Stop and Play On Start are pretty self-explanatory, The following explains the other options. +## Start Frame +An example with Start Frame as Frame 0 +An example with Start Frame as Frame 40 +In the 2nd example it is visible that the effect starts after skipping the initial part of the first example. +## Moving +An example with Moving off +An example with Moving on +## Use Speed +At speed 1 +At speed 0.1 diff --git a/demo/document-rag/documents/Rootex_guides_running_the_editor.md b/demo/document-rag/documents/Rootex_guides_running_the_editor.md new file mode 100644 index 0000000..7c381fe --- /dev/null +++ b/demo/document-rag/documents/Rootex_guides_running_the_editor.md @@ -0,0 +1,16 @@ + + +Rootex Editor — Rootex documentation +- Rootex Editor +# Rootex Editor +Rootex comes with a separate editor for making games, called the Rootex Editor. +Rootex Editor is built using [Dear ImGui](https://github.com/ocornut/imgui) . The editor UI is subject to change but the overall workings shall remain the same. +## Running the Editor +1. +Run the editor by opening the editor executable. +1. +Once editor is open you are greeted by the editor UI. +1. +Try to open a scene from the File menu > Open Scene option. +Note +If you get an error along the lines of `dxgidebug.dll not loaded` while opening the executable, install Graphics Tools by following this [guide](https://docs.microsoft.com/en-us/windows/uwp/gaming/use-the-directx-runtime-and-visual-studio-graphics-diagnostic-features). diff --git a/demo/document-rag/documents/Rootex_guides_transform_animation.md b/demo/document-rag/documents/Rootex_guides_transform_animation.md new file mode 100644 index 0000000..e327c22 --- /dev/null +++ b/demo/document-rag/documents/Rootex_guides_transform_animation.md @@ -0,0 +1,76 @@ + + +Animating Objects using TransformAnimationComponent — Rootex documentation +- Animating Objects using TransformAnimationComponent +# Animating Objects using TransformAnimationComponent +TransformAnimationComponent can be used to easily animate scenes which have to be animated endlessly without relying on scripting for the same. +It is quite easy to as it uses a keyframe based interface to achieve this. +## Setting up +Create a new scene and assign the TransformComponent, ModelComponent and TransformAnimationComponent to it. +Creating a new scene: +Adding the Components by right clicking on the scene in inspector: +Select the EditorCamera: +Set the camera to EditorCamera and position it such that it has a good view of the scene. +Select the EditorCamera as default camera for the scene. +## Interface +- +Transition Type : How the transition must be between keyframes. Following are the possible values for the Transition Type: +- +SmashSmash : Abrupt start and end transition +- +EaseEase : Smooth start and end transition +- +SmashEase : Abrubt start and smooth end transition +- +EaseSmash : Smooth start and abrupt end transition +- +Animation Mode : How the animation should play. Explaining the options: +- +Looping : Animation plays from Start time to End time unidirectionally. +- +Alternating : Animation plays from Start time to EndTime and then reverses from End time to Start time. +- +Time : Shows the time while animation plays +- +Reset : Resets the animation +- +Play in Editor : Play the animation in editor +- +Play on start : Play the animation when game starts +- +Keyframes : To set the animation keyframes +- +Set Keyframe : To add a new keyframe at the end +- +Pop Keyframe : To remove the last keyframe +## Keyframes +Open the keyframes dropdown to get a list of keyframes. +Each keyframe has a timestamp and a jump button. +More keyframes can be added using the Set Keyframe Button. For now We will have 3 Keyframes. +Now, to animate any object, we need to set it to its desired location for a given keyframe and specify the time in the keyframe. +Let’s see how. +For example, if I want our object to move up to X,Y = 0,5 and have a certain rotation at 0.5 sec of our rotation: +What I did here is entered a Keyframe using the Jump button next to it, gave the object its new Transform values (which I want at that keyframe), set the transform using the exit jump button and gave it a timestamp of 0.5 sec. +Setting Another Keyframe. +Playing the Animation: +Popping a Keyframe removes the last keyframe. +Playing after popping the last keyframe: +This way, more keyframes can be added using Set Keyframe to add more steps to the animation or keyframes can be removed using Pop Keyframe. +## Transition Type Examples +Examples of different transition types for better understanding. +SmashSmash: +EaseEase: +SmashEase: +EaseSmash: +## Animation Mode Examples +Showing demo of each mode for better understanding. +None: Animation plays only once. +Looping: Animation plays unidirectionally and repeats afteer ending. +Alternating: Animation plays back and forth (bidirectionally). +Check the time progressbar for more clarity. +## Reset +Resets the animation to the starting frame (Time 0.00). +## Play in Editor +Plays the Animation in EditorView. +## Play on Start +Plays the Animation on game start. diff --git a/demo/document-rag/documents/Rootex_index.md b/demo/document-rag/documents/Rootex_index.md new file mode 100644 index 0000000..8511343 --- /dev/null +++ b/demo/document-rag/documents/Rootex_index.md @@ -0,0 +1,42 @@ + + +Welcome to Rootex’s documentation! — Rootex documentation +- Welcome to Rootex’s documentation! +# Welcome to Rootex’s documentation! +Rootex is a Windows based 3D multithreaded game engine written in C++ and powers an in-production game being developed at [SDSLabs](https://sdslabs.co) . +- +Issue Tracker: [http://github.com/sdslabs/rootex/issues](http://github.com/sdslabs/rootex/issues) +- +Source Code: [http://github.com/sdslabs/rootex](http://github.com/sdslabs/rootex) +- +Discord : [https://discord.gg/ZDxvjm9dX8](https://discord.gg/ZDxvjm9dX8) +## Support +If you are having issues, please let us know. SDSLabs has a public chat channel at [http://chat.sdslabs.co](http://chat.sdslabs.co) +## License +The project is licensed under the MIT license. See THIRDPARTY.md for thirdparty licenses. +- [Getting Started](https://rootex.readthedocs.io/en/latest/guides/getting_started.html) +- [Rootex Editor](https://rootex.readthedocs.io/en/latest/guides/running_the_editor.html) +- [Editor Layout](https://rootex.readthedocs.io/en/latest/guides/editor_layout.html) +- [Animating Objects using TransformAnimationComponent](https://rootex.readthedocs.io/en/latest/guides/using_TransformAnimationComponent.html) +- [Exploring the Graphical capabilities of Rootex](https://rootex.readthedocs.io/en/latest/guides/graphics_tutorial.html) +- [Making HUD using UI-component](https://rootex.readthedocs.io/en/latest/guides/making_HUD_using_ui-component.html) +- [Making effects using ParticleEffectComponent](https://rootex.readthedocs.io/en/latest/guides/effects_using_ParticleEffectComponent.html) +- [Getting Help](https://rootex.readthedocs.io/en/latest/guides/getting_help.html) +- [Architecture](https://rootex.readthedocs.io/en/latest/engine/architecture.html) +- [Framework](https://rootex.readthedocs.io/en/latest/engine/framework.html) +- [Component](https://rootex.readthedocs.io/en/latest/engine/framework.html#component) +- [Entity](https://rootex.readthedocs.io/en/latest/engine/framework.html#entity) +- [System](https://rootex.readthedocs.io/en/latest/engine/framework.html#system) +- [Pausing](https://rootex.readthedocs.io/en/latest/engine/framework.html#pausing) +- [Event Manager](https://rootex.readthedocs.io/en/latest/engine/events.html) +- [Multithreading](https://rootex.readthedocs.io/en/latest/engine/multithreading.html) +- [Resources](https://rootex.readthedocs.io/en/latest/engine/resources.html) +- [ResourceLoader](https://rootex.readthedocs.io/en/latest/engine/resources.html#resourceloader) +- [Audio](https://rootex.readthedocs.io/en/latest/engine/audio.html) +- [Rendering](https://rootex.readthedocs.io/en/latest/engine/rendering.html) +- [Physics](https://rootex.readthedocs.io/en/latest/engine/physics.html) +- [Inputs](https://rootex.readthedocs.io/en/latest/engine/inputs.html) +- [Scripting](https://rootex.readthedocs.io/en/latest/engine/scripting.html) +- [Scripting API](https://rootex.readthedocs.io/en/latest/engine/scripting.html#scripting-api) +- [Rootex](https://rootex.readthedocs.io/en/latest/api/rootex.html) +- [Full API](https://rootex.readthedocs.io/en/latest/api/rootex.html#full-api) diff --git a/demo/document-rag/documents/VortexDB_docs_api-reference_grpc.md b/demo/document-rag/documents/VortexDB_docs_api-reference_grpc.md new file mode 100644 index 0000000..bb544b2 --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_api-reference_grpc.md @@ -0,0 +1,474 @@ +--- +title: "gRPC API" +description: "High-performance Protocol Buffer API reference" +--- + +# gRPC API Reference + +VortexDB's gRPC API provides high-performance vector operations using Protocol Buffers over HTTP/2. + +## Connection + +| Parameter | Default | +|-----------|---------| +| Host | `localhost` | +| Port | `50051` | +| Protocol | HTTP/2 (plaintext) | + +```bash +# Test connection with grpcurl +grpcurl -plaintext localhost:50051 list +``` + +## Authentication + +All gRPC calls require the `authorization` header with your API key: + +```bash +-H "authorization: your-api-key" +``` + +Valid keys come from the JSON file pointed to by the `VORTEXDB_KEYS_FILE` environment variable, shared with the HTTP server. `readonly` keys can call `GetPoint`, `SearchPoints`, and `SearchPointsBatch`; `readwrite` keys can additionally call `InsertVector`, `InsertVectorsBatch`, and `DeletePoint`. A `readonly` key calling a write RPC gets a `PERMISSION_DENIED` status. + +--- + +## Service Definition + +```protobuf +syntax = "proto3"; +package vectordb; + +service VectorDB { + rpc InsertVector(InsertVectorRequest) returns (PointID); + rpc InsertVectorsBatch(InsertVectorsBatchRequest) returns (InsertVectorsBatchResponse); + rpc DeletePoint(PointID) returns (google.protobuf.Empty); + rpc GetPoint(PointID) returns (Point); + rpc SearchPoints(SearchRequest) returns (SearchResponse); + rpc SearchPointsBatch(SearchPointsBatchRequest) returns (SearchPointsBatchResponse); +} +``` + +--- + +## Methods + +### InsertVector + +Insert a vector with its associated payload. + + + The vector to insert. Must match the configured `DIMENSION`. + + + + Metadata associated with the vector. + + +**Request:** + +```protobuf +message InsertVectorRequest { + DenseVector vector = 1; + Payload payload = 2; +} +``` + +**Response:** + +```protobuf +message PointID { + UUID id = 1; +} +``` + +**Example:** + +```bash +grpcurl -plaintext \ + -H "authorization: secret" \ + -d '{ + "vector": {"values": [0.1, 0.2, 0.3, 0.4]}, + "payload": {"content_type": 1, "content": "Hello world"} + }' \ + localhost:50051 vectordb.VectorDB/InsertVector +``` + +**Response:** + +```json +{ + "id": { + "value": "550e8400-e29b-41d4-a716-446655440000" + } +} +``` + +--- + +### GetPoint + +Retrieve a point by its ID. + + + The unique identifier of the point. + + +**Request:** + +```protobuf +message PointID { + UUID id = 1; +} +``` + +**Response:** + +```protobuf +message Point { + PointID id = 1; + Payload payload = 2; + DenseVector vector = 3; +} +``` + +**Example:** + +```bash +grpcurl -plaintext \ + -H "authorization: secret" \ + -d '{"id": {"value": "550e8400-e29b-41d4-a716-446655440000"}}' \ + localhost:50051 vectordb.VectorDB/GetPoint +``` + +**Response:** + +```json +{ + "id": { + "id": { + "value": "550e8400-e29b-41d4-a716-446655440000" + } + }, + "payload": { + "contentType": "Text", + "content": "Hello world" + }, + "vector": { + "values": [0.1, 0.2, 0.3, 0.4] + } +} +``` + +--- + +### DeletePoint + +Delete a point by its ID. + + + The unique identifier of the point to delete. + + +**Request:** + +```protobuf +message PointID { + UUID id = 1; +} +``` + +**Response:** + +```protobuf +google.protobuf.Empty +``` + +**Example:** + +```bash +grpcurl -plaintext \ + -H "authorization: secret" \ + -d '{"id": {"value": "550e8400-e29b-41d4-a716-446655440000"}}' \ + localhost:50051 vectordb.VectorDB/DeletePoint +``` + +**Response:** + +```json +{} +``` + +--- + +### SearchPoints + +Search for the k nearest neighbors to a query vector. + + + The vector to search with. Must match the configured `DIMENSION`. + + + + The distance metric to use. + + + + Maximum number of results to return. + + + + Search breadth for HNSW. Larger values trade speed for accuracy. Defaults to the server's `HNSW_EF` setting. + + +**Request:** + +```protobuf +message SearchRequest { + DenseVector query_vector = 1; + Similarity similarity = 2; + uint64 limit = 3; + uint64 ef = 4; +} +``` + +**Response:** + +```protobuf +message SearchResponse { + repeated PointID result_point_ids = 1; +} +``` + +**Example:** + +```bash +grpcurl -plaintext \ + -H "authorization: secret" \ + -d '{ + "query_vector": {"values": [0.1, 0.2, 0.3, 0.4]}, + "similarity": 3, + "limit": 5, + "ef": 200 + }' \ + localhost:50051 vectordb.VectorDB/SearchPoints +``` + +**Response:** + +```json +{ + "resultPointIds": [ + {"id": {"value": "550e8400-e29b-41d4-a716-446655440000"}}, + {"id": {"value": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"}} + ] +} +``` + +--- + +### InsertVectorsBatch + +Insert multiple vectors in a single request. + +**Request:** + +```protobuf +message InsertVectorsBatchRequest { + repeated InsertVectorRequest vectors = 1; +} +``` + +**Response:** + +```protobuf +message InsertVectorsBatchResponse { + repeated PointID ids = 1; +} +``` + +**Example:** + +```bash +grpcurl -plaintext \ + -H "authorization: secret" \ + -d '{ + "vectors": [ + {"vector": {"values": [0.1, 0.2, 0.3]}, "payload": {"content_type": 1, "content": "doc one"}}, + {"vector": {"values": [0.4, 0.5, 0.6]}, "payload": {"content_type": 1, "content": "doc two"}} + ] + }' \ + localhost:50051 vectordb.VectorDB/InsertVectorsBatch +``` + +--- + +### SearchPointsBatch + +Search against multiple query vectors in a single request. + +**Request:** + +```protobuf +message SearchPointsBatchRequest { + repeated SearchRequest queries = 1; +} +``` + +**Response:** + +```protobuf +message SearchPointsBatchResponse { + repeated SearchResponse results = 1; +} +``` + +**Example:** + +```bash +grpcurl -plaintext \ + -H "authorization: secret" \ + -d '{ + "queries": [ + {"query_vector": {"values": [0.1, 0.2, 0.3]}, "similarity": 3, "limit": 2}, + {"query_vector": {"values": [0.4, 0.5, 0.6]}, "similarity": 0, "limit": 2} + ] + }' \ + localhost:50051 vectordb.VectorDB/SearchPointsBatch +``` + +--- + +## Message Types + +### UUID + +```protobuf +message UUID { + string value = 1; // UUID v4 string +} +``` + +### DenseVector + +```protobuf +message DenseVector { + repeated float values = 1; // Vector components +} +``` + +### Point + +```protobuf +message Point { + PointID id = 1; // Unique identifier + Payload payload = 2; // Associated metadata + DenseVector vector = 3; // Vector values +} +``` + +### PointID + +```protobuf +message PointID { + UUID id = 1; +} +``` + +### Payload + +```protobuf +message Payload { + ContentType content_type = 1; // Type of content + string content = 2; // Content string +} +``` + +--- + +## Enums + +### Similarity + +Distance/similarity metric for search operations. + +| Value | Name | Description | +|-------|------|-------------| +| `0` | `Euclidean` | L2 distance (straight line) | +| `1` | `Manhattan` | L1 distance (city block) | +| `2` | `Hamming` | Count of differing elements | +| `3` | `Cosine` | Angular distance | + +### ContentType + +Type of payload content. + +| Value | Name | Description | +|-------|------|-------------| +| `0` | `Image` | Image reference or data | +| `1` | `Text` | Text content | + +--- + +## Error Codes + +| gRPC Code | Name | Description | +|-----------|------|-------------| +| `0` | `OK` | Success | +| `3` | `INVALID_ARGUMENT` | Invalid request (e.g., wrong dimensions) | +| `5` | `NOT_FOUND` | Point does not exist | +| `13` | `INTERNAL` | Server error | +| `16` | `UNAUTHENTICATED` | Invalid or missing API key | + +--- + +## Client Libraries + +### Python + +```python +from vortexdb import VortexDB, DenseVector, Payload, Similarity + +with VortexDB(grpc_url="localhost:50051", api_key="secret") as db: + # Insert + point_id = db.insert( + vector=DenseVector([0.1, 0.2, 0.3, 0.4]), + payload=Payload.text("Hello") + ) + + # Batch insert + ids = db.batch_insert(items=[ + (DenseVector([0.1, 0.2, 0.3]), Payload.text("doc one")), + (DenseVector([0.4, 0.5, 0.6]), Payload.text("doc two")), + ]) + + # Search with ef parameter + results = db.search( + vector=DenseVector([0.1, 0.2, 0.3, 0.4]), + similarity=Similarity.COSINE, + limit=5, + ef=200, + ) +``` + +### Generate Clients + +Use `protoc` to generate clients in any language: + +```bash +python -m grpc_tools.protoc \ + -I./crates/grpc/proto \ + --python_out=./client \ + --grpc_python_out=./client \ + ./crates/grpc/proto/vector-db.proto +``` + +## Next Steps + + + + REST API reference + + + Python client documentation + + diff --git a/demo/document-rag/documents/VortexDB_docs_api-reference_http.md b/demo/document-rag/documents/VortexDB_docs_api-reference_http.md new file mode 100644 index 0000000..26ae86f --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_api-reference_http.md @@ -0,0 +1,404 @@ +--- +title: "HTTP API" +description: "RESTful JSON API reference" +--- + +# HTTP API Reference + +VortexDB's HTTP API provides a RESTful interface for vector operations using JSON over HTTP/1.1. + +## Base URL + +``` +http://localhost:3000 +``` + +The port can be configured via the `HTTP_PORT` environment variable. The server binds to `127.0.0.1` by default. + + +The HTTP API has no authentication. Always deploy behind a reverse proxy or disable it with `DISABLE_HTTP=true` in production. + + +--- + +## Endpoints + +### Health Check + +Check if the server is running. + + + Returns "OK" if the server is healthy. + + + +```bash Request +curl http://localhost:3000/health +``` + +```text Response +OK +``` + + +--- + +### Root + +Verify the server is running. + + +```bash Request +curl http://localhost:3000/ +``` + +```text Response +Vector Database server is running! +``` + + +--- + +### Insert Point + + + Array of floating-point numbers representing the vector. Must match the configured `DIMENSION`. + + + + Metadata object associated with the vector. + + + + Type of content: `"Text"` or `"Image"` + + + The content string + + + + + + UUID of the created point. + + + +```bash Request +curl -X POST http://localhost:3000/points \ + -H "Content-Type: application/json" \ + -d '{ + "vector": [0.1, 0.2, 0.3, 0.4], + "payload": { + "content_type": "Text", + "content": "Hello, VortexDB!" + } + }' +``` + +```json Response (201 Created) +{ + "point_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + + +**Error Responses:** + +| Status | Description | +|--------|-------------| +| `400 Bad Request` | Invalid JSON or missing fields | +| `500 Internal Server Error` | Server error during insertion | + +--- + +### Batch Insert + +Insert multiple vectors in a single request. + + + Array of insert objects, each with `vector` and `payload` fields (same shape as single insert). + + + + Array of UUIDs for each created point, in the same order as the input. + + + +```bash Request +curl -X POST http://localhost:3000/points/batch \ + -H "Content-Type: application/json" \ + -d '{ + "vectors": [ + { + "vector": [0.1, 0.2, 0.3, 0.4], + "payload": {"content_type": "Text", "content": "Document one"} + }, + { + "vector": [0.5, 0.6, 0.7, 0.8], + "payload": {"content_type": "Text", "content": "Document two"} + } + ] + }' +``` + +```json Response (201 Created) +{ + "point_ids": [ + "550e8400-e29b-41d4-a716-446655440000", + "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + ] +} +``` + + +**Error Responses:** + +| Status | Description | +|--------|-------------| +| `400 Bad Request` | Invalid JSON or missing fields | +| `500 Internal Server Error` | Server error during insertion | + +--- + +### Get Point + +Retrieve a point by its ID. + + + UUID of the point to retrieve. + + + + The point's UUID. + + + + The stored vector values. + + + + The associated payload metadata. + + + +```bash Request +curl http://localhost:3000/points/550e8400-e29b-41d4-a716-446655440000 +``` + +```json Response (200 OK) +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "vector": [0.1, 0.2, 0.3, 0.4], + "payload": { + "content_type": "Text", + "content": "Hello, VortexDB!" + } +} +``` + + +**Error Responses:** + +| Status | Description | +|--------|-------------| +| `404 Not Found` | Point does not exist | +| `500 Internal Server Error` | Server error during retrieval | + +--- + +### Delete Point + +Delete a point by its ID. + + + UUID of the point to delete. + + + +```bash Request +curl -X DELETE http://localhost:3000/points/550e8400-e29b-41d4-a716-446655440000 +``` + +```text Response (204 No Content) +(empty body) +``` + + +**Error Responses:** + +| Status | Description | +|--------|-------------| +| `500 Internal Server Error` | Server error during deletion | + +--- + +### Search Points + +Search for the k nearest neighbors to a query vector. + + + Query vector. Must match the configured `DIMENSION`. + + + + Distance metric: `"Euclidean"`, `"Manhattan"`, `"Hamming"`, or `"Cosine"` + + + + Maximum number of results to return. + + + + Array of point IDs ordered by similarity (closest first). + + + +```bash Request +curl -X POST http://localhost:3000/points/search \ + -H "Content-Type: application/json" \ + -d '{ + "vector": [0.1, 0.2, 0.3, 0.4], + "similarity": "Cosine", + "limit": 5 + }' +``` + +```json Response (200 OK) +{ + "results": [ + "550e8400-e29b-41d4-a716-446655440000", + "6ba7b810-9dad-11d1-80b4-00c04fd430c8", + "f47ac10b-58cc-4372-a567-0e02b2c3d479" + ] +} +``` + + +**Error Responses:** + +| Status | Description | +|--------|-------------| +| `400 Bad Request` | Invalid JSON or missing fields | +| `500 Internal Server Error` | Server error during search | + +--- + +### Batch Search + +Search against multiple query vectors in a single request. + + + Array of search query objects, each with `vector`, `similarity`, and `limit`. + + + + Array of result arrays, one per input query, each ordered by similarity. + + + +```bash Request +curl -X POST http://localhost:3000/points/search/batch \ + -H "Content-Type: application/json" \ + -d '{ + "queries": [ + {"vector": [0.1, 0.2, 0.3, 0.4], "similarity": "Cosine", "limit": 2}, + {"vector": [0.5, 0.6, 0.7, 0.8], "similarity": "Euclidean", "limit": 2} + ] + }' +``` + +```json Response (200 OK) +{ + "results": [ + ["550e8400-...", "6ba7b810-..."], + ["f47ac10b-...", "9a1b2c3d-..."] + ] +} +``` + + +**Error Responses:** + +| Status | Description | +|--------|-------------| +| `400 Bad Request` | Invalid JSON or missing fields | +| `500 Internal Server Error` | Server error during search | + +--- + +## Error Format + +Errors are returned as plain text with an appropriate HTTP status code: + +```bash +curl -v http://localhost:3000/points/nonexistent-id +``` + +``` +< HTTP/1.1 404 Not Found +< content-type: text/plain; charset=utf-8 +< +Point not found +``` + +--- + +## Examples + +### Complete Workflow + +```bash +# 1. Check health +curl http://localhost:3000/health +# OK + +# 2. Insert a vector +POINT_ID=$(curl -s -X POST http://localhost:3000/points \ + -H "Content-Type: application/json" \ + -d '{ + "vector": [0.1, 0.2, 0.3, 0.4], + "payload": {"content_type": "Text", "content": "First document"} + }' | jq -r '.point_id') + +echo "Created point: $POINT_ID" + +# 3. Get the point +curl http://localhost:3000/points/$POINT_ID + +# 4. Search for similar vectors +curl -X POST http://localhost:3000/points/search \ + -H "Content-Type: application/json" \ + -d '{ + "vector": [0.15, 0.25, 0.35, 0.45], + "similarity": "Cosine", + "limit": 10 + }' + +# 5. Delete the point +curl -X DELETE http://localhost:3000/points/$POINT_ID +``` + +--- + +## OpenAPI Specification + +The complete OpenAPI specification is available at: + +``` +/docs/openapi.yaml +``` + +You can import this into tools like Postman or Swagger UI for interactive API exploration. + +--- + +## Next Steps + + + + High-performance gRPC API reference + + + Python client documentation + + diff --git a/demo/document-rag/documents/VortexDB_docs_api-reference_overview.md b/demo/document-rag/documents/VortexDB_docs_api-reference_overview.md new file mode 100644 index 0000000..57d29a7 --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_api-reference_overview.md @@ -0,0 +1,180 @@ +--- +title: "API Overview" +description: "VortexDB exposes two APIs: gRPC and HTTP" +--- + +# API Overview + +VortexDB provides two complementary APIs for different use cases: + +## Available APIs + + + + High-performance Protocol Buffer interface for production workloads + + + RESTful JSON interface for quick testing and prototyping + + + +## Comparison + +| Feature | gRPC | HTTP | +|---------|------|------| +| **Protocol** | HTTP/2 + Protobuf | HTTP/1.1 + JSON | +| **Default Port** | 50051 | 3000 | +| **Authentication** | API key required | API key required | +| **Performance** | Higher throughput | Lower latency for simple requests | +| **Client Libraries** | Auto-generated | Any HTTP client | +| **Best For** | Production, SDKs | Debugging, curl | + +## Authentication + +### gRPC + +The gRPC API requires authentication via the `authorization` header: + +```bash +# Using grpcurl +grpcurl -plaintext \ + -H "authorization: your-api-key" \ + localhost:50051 vectordb.VectorDB/GetPoint +``` + +```python +# Python SDK +db = VortexDB( + grpc_url="localhost:50051", + api_key="your-api-key" +) +``` + +### HTTP + +The HTTP API requires an `api-key` header on every request under `/points`. `/` and `/health` remain open for health checks. + +```bash +curl -X POST "http://localhost:3000/points/search" \ + -H "api-key: your-api-key" \ + -H "Content-Type: application/json" \ + -d '{"vector": [0.1, 0.2, 0.3], "similarity": "Cosine", "limit": 5}' +``` + +Keys come from the same `VORTEXDB_KEYS_FILE` used by gRPC (see [gRPC API](/api-reference/grpc)), but the HTTP API additionally enforces each key's role: `readonly` keys can fetch and search points; `readwrite` keys can also insert, batch-insert, and delete. A `readonly` key used against a write route gets `403 Forbidden`; a missing or unrecognized key gets `401 Unauthorized`. + +## Common Operations + +Both APIs support the same core operations: + +| Operation | gRPC Method | HTTP Endpoint | +|-----------|-------------|---------------| +| Insert vector | `InsertVector` | `POST /points` | +| Batch insert | `InsertVectorsBatch` | `POST /points/batch` | +| Get point | `GetPoint` | `GET /points/:id` | +| Delete point | `DeletePoint` | `DELETE /points/:id` | +| Search vectors | `SearchPoints` | `POST /points/search` | +| Batch search | `SearchPointsBatch` | `POST /points/search/batch` | +| Health check | - | `GET /health` | + +## Error Handling + +### gRPC Error Codes + +| Code | Name | Description | +|------|------|-------------| +| `0` | OK | Success | +| `3` | INVALID_ARGUMENT | Invalid request parameters | +| `5` | NOT_FOUND | Point not found | +| `13` | INTERNAL | Server error | +| `16` | UNAUTHENTICATED | Invalid or missing API key | + +### HTTP Status Codes + +| Code | Description | +|------|-------------| +| `200` | Success | +| `201` | Created (for insert) | +| `204` | No Content (for delete) | +| `400` | Bad Request | +| `404` | Not Found | +| `500` | Internal Server Error | + +## Data Types + +### Vector + +A dense vector of floating-point values: + + + + ```protobuf + message DenseVector { + repeated float values = 1; + } + ``` + + + ```json + { + "vector": [0.1, 0.2, 0.3, 0.4] + } + ``` + + + +### Payload + +Metadata attached to vectors: + + + + ```protobuf + enum ContentType { + Image = 0; + Text = 1; + } + + message Payload { + ContentType content_type = 1; + string content = 2; + } + ``` + + + ```json + { + "payload": { + "content_type": "Text", + "content": "Hello, world!" + } + } + ``` + + + +### Similarity Metric + +Distance function for search: + +| Value | gRPC Enum | HTTP String | +|-------|-----------|-------------| +| Euclidean (L2) | `0` | `"Euclidean"` | +| Manhattan (L1) | `1` | `"Manhattan"` | +| Hamming | `2` | `"Hamming"` | +| Cosine | `3` | `"Cosine"` | + +## Rate Limiting + +VortexDB does not implement built-in rate limiting. For production deployments, use a reverse proxy or API gateway to enforce limits. + +## Next Steps + + + + Complete gRPC API documentation + + + Complete HTTP API documentation + + diff --git a/demo/document-rag/documents/VortexDB_docs_concepts_architecture.md b/demo/document-rag/documents/VortexDB_docs_concepts_architecture.md new file mode 100644 index 0000000..5d31837 --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_concepts_architecture.md @@ -0,0 +1,184 @@ +--- +title: "Architecture" +description: "Understanding VortexDB's modular architecture" +--- + +# Architecture + +VortexDB is a high-performance vector database built in Rust, designed with modularity and flexibility at its core. + +## Crate Structure + +VortexDB is organized as a Rust workspace with the following crates: + +| Crate | Purpose | +|-------|---------| +| `server` | Main entry point, configuration, server startup | +| `api` | Core database logic and error handling | +| `grpc` | Protocol Buffers definitions and gRPC service | +| `http` | REST API handlers using Axum | +| `index` | Vector indexing algorithms (Flat, KD-Tree, HNSW) | +| `storage` | Persistence backends (InMemory, RocksDB) | +| `snapshot` | Point-in-time backup and restore | +| `defs` | Shared type definitions | +| `tui` | Terminal user interface | + +## Transport Layers + +VortexDB exposes two APIs that share the same underlying database: + +### gRPC API + +The **gRPC layer** is the primary high-performance interface: + +- **Protocol**: HTTP/2 with Protocol Buffers +- **Port**: 50051 (default) +- **Authentication**: API key via `authorization` header +- **Use cases**: Production workloads, SDKs, high-throughput scenarios + +```protobuf +service VectorDB { + rpc InsertVector(InsertVectorRequest) returns (PointID); + rpc DeletePoint(PointID) returns (google.protobuf.Empty); + rpc GetPoint(PointID) returns (Point); + rpc SearchPoints(SearchRequest) returns (SearchResponse); +} +``` + +### HTTP API + +The **HTTP layer** provides a RESTful interface: + +- **Protocol**: HTTP/1.1 with JSON +- **Port**: 3000 (default) +- **Authentication**: None (designed for internal/trusted networks) +- **Use cases**: Quick testing, curl commands, prototyping + +| Method | Endpoint | Description | +|--------|----------|-------------| +| `GET` | `/` | Root endpoint | +| `GET` | `/health` | Health check | +| `POST` | `/points` | Insert a point | +| `GET` | `/points/:id` | Get a point by ID | +| `DELETE` | `/points/:id` | Delete a point | +| `POST` | `/points/search` | Search for similar vectors | + +### When to Use Which? + + + + - Building production applications + - Using the Python SDK (it uses gRPC) + - Need authentication + - Processing high volumes of requests + - Want strongly-typed client libraries + + + - Quick prototyping with curl + - Integrating with systems that don't support gRPC + - Debugging and testing + - Building browser-based tools + + + +## Storage Layer + +The storage layer persists vectors and their payloads using a trait-based design: + +```rust +pub trait StorageEngine: Send + Sync { + fn insert(&self, vector: DenseVector, payload: Payload) -> Result; + fn get(&self, point_id: PointId) -> Result>; + fn delete(&self, point_id: PointId) -> Result; + fn checkpoint_at(&self, path: &Path) -> Result; + fn restore_checkpoint(&mut self, checkpoint: &StorageCheckpoint) -> Result<()>; +} +``` + +### Available Backends + +| Backend | Description | Use Case | +|---------|-------------|----------| +| **InMemory** | Stores data in RAM | Development, testing, ephemeral workloads | +| **RocksDB** | LSM-tree persistent storage | Production deployments | + +Set the backend via the `STORAGE_TYPE` environment variable: +```bash +STORAGE_TYPE=rocksdb # or 'inmemory' +``` + +## Index Layer + +The index layer provides fast similarity search. See [Indexers](/concepts/indexers) for details on choosing the right index. + +```rust +pub trait VectorIndex: Send + Sync { + fn insert(&mut self, vector: IndexedVector) -> Result<()>; + fn delete(&mut self, point_id: PointId) -> Result; + fn search(&self, query: DenseVector, similarity: Similarity, k: usize) -> Result>; +} +``` + +## Data Flow + +Here's what happens when you insert a vector: + + + + Client sends insert request via gRPC or HTTP + + + Server validates vector dimensions match configuration + + + Vector and payload are persisted to the storage backend + + + Vector is added to the index for fast searching + + + Server returns the generated point ID to client + + + +## Configuration + +VortexDB is configured via environment variables: + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `VORTEXDB_KEYS_FILE` | Yes | - | Path to a JSON file of API keys shared by the HTTP and gRPC servers | +| `DIMENSION` | Yes | - | Vector dimensionality | +| `DATA_PATH` | No | system temp dir | Directory for persistent storage | +| `HTTP_HOST` | No | `127.0.0.1` | HTTP server bind address | +| `HTTP_PORT` | No | `3000` | HTTP server port | +| `GRPC_HOST` | No | `127.0.0.1` | gRPC server bind address | +| `GRPC_PORT` | No | `50051` | gRPC server port | +| `STORAGE_TYPE` | No | `inmemory` | Storage backend: `inmemory` or `rocksdb` | +| `INDEX_TYPE` | No | `flat` | Index algorithm: `flat`, `kdtree`, or `hnsw` | +| `SIMILARITY` | No | `cosine` | Default metric: `cosine`, `euclidean`, `manhattan`, or `hamming` | +| `LOGGING` | No | `true` | Enable logging | +| `DISABLE_HTTP` | No | `false` | Run gRPC only | +| `HNSW_M` | No | `16` | HNSW max connections per layer | +| `HNSW_M0` | No | `2 * HNSW_M` | HNSW max connections for layer 0 | +| `HNSW_EF_CONSTRUCTION` | No | `200` | HNSW search breadth during construction | +| `HNSW_EF` | No | `100` | HNSW default search breadth at query time | + +## Thread Safety + +VortexDB is designed for concurrent access: + +- The **storage layer** uses `Arc` for thread-safe reference counting +- The **index layer** uses `RwLock` for concurrent reads with exclusive writes +- Both gRPC and HTTP handlers are fully async using Tokio + +## Next Steps + + + + Learn about index algorithms and when to use each + + + Understand backup and restore mechanisms + + diff --git a/demo/document-rag/documents/VortexDB_docs_concepts_indexers.md b/demo/document-rag/documents/VortexDB_docs_concepts_indexers.md new file mode 100644 index 0000000..bbf8056 --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_concepts_indexers.md @@ -0,0 +1,147 @@ +--- +title: "Indexers" +description: "Choosing the right vector index for your use case" +--- + +# Indexers + +VortexDB supports multiple indexing algorithms, each optimized for different use cases. The index determines how vectors are organized for similarity search. + +## Flat Index + +The **Flat** index performs brute-force exhaustive search by computing distances to every vector. + +### When to Use + +- **Dataset size**: < 10,000 vectors +- **Requirements**: Need exact/guaranteed results +- **Use cases**: Testing, prototyping, small production workloads + +```bash +INDEX_TYPE=flat +``` + +## KD-Tree Index + +The **KD-Tree** (k-dimensional tree) is a space-partitioning data structure that recursively divides the vector space. + +At each level, the tree splits data along a different dimension (cycling through x, y, z, ...). + +### When to Use + +- **Vector dimensions**: < 20 dimensions +- **Dataset size**: Thousands to hundreds of thousands of vectors +- **Use cases**: Geographic data, low-dimensional embeddings, spatial queries + +```bash +INDEX_TYPE=kdtree +``` + + +For high-dimensional embeddings (e.g., 384, 768, 1536 dimensions common in ML), KD-Tree is not recommended. Use HNSW instead. + + +## HNSW Index + +**HNSW** (Hierarchical Navigable Small World) is a state-of-the-art approximate nearest neighbor algorithm based on proximity graphs. It constructs a multi-layered graph where each layer represents a different level of granularity, enabling efficient navigation from coarse to fine-grained similarity search. + +Check out this [blog post](https://blog.sdslabs.co/2026/03/hnsw-index) for more theoretical details and [this blog](https://blog.sdslabs.co/2026/03/hnsw-indexp2) covering implementation in VortexDB. + +### When to Use + +- **Vector dimensions**: Any, but especially > 20 dimensions +- **Dataset size**: 100,000+ vectors +- **Use cases**: Semantic search, recommendation systems, RAG applications + +```bash +INDEX_TYPE=hnsw +``` + +## Distance Metrics + +All indexes support four distance/similarity metrics: + + + + Measures the angle between two vectors, ignoring magnitude. + + $$d = 1 - \frac{A \cdot B}{\|A\| \|B\|}$$ + + - **Range**: 0 (identical) to 2 (opposite) + - **Best for**: Text embeddings, normalized vectors + + ```python + similarity=Similarity.COSINE + ``` + + + L2 distance—the straight-line distance between points. + + $$d = \sqrt{\sum_{i=1}^{n} (a_i - b_i)^2}$$ + + - **Range**: 0 (identical) to ∞ + - **Best for**: General purpose + + ```python + similarity=Similarity.EUCLIDEAN + ``` + + + L1 distance—the sum of absolute differences. + + $$d = \sum_{i=1}^{n} |a_i - b_i|$$ + + - **Range**: 0 (identical) to ∞ + - **Best for**: Sparse vectors, grid-based data + + ```python + similarity=Similarity.MANHATTAN + ``` + + + Counts positions where elements differ. + + - **Range**: 0 (identical) to n (completely different) + - **Best for**: Binary vectors, categorical data + + ```python + similarity=Similarity.HAMMING + ``` + + + +## Choosing an Index + +Use this decision tree: + +| Vectors | Dimensions | Recommended Index | +|---------|------------|-------------------| +| < 10,000 | Any | **Flat** | +| 10K - 100K | < 20 | **KD-Tree** | +| 10K - 100K | ≥ 20 | **HNSW** | +| > 100K | Any | **HNSW** | + +## Configuration Example + +```bash +# For a semantic search application with OpenAI embeddings +DIMENSION=1536 +INDEX_TYPE=hnsw +STORAGE_TYPE=rocksdb + +# For a small prototype with sentence-transformers +DIMENSION=384 +INDEX_TYPE=flat +STORAGE_TYPE=inmemory +``` + +## Next Steps + + + + Learn how to backup and restore your index + + + Explore the complete API + + diff --git a/demo/document-rag/documents/VortexDB_docs_concepts_snapshots.md b/demo/document-rag/documents/VortexDB_docs_concepts_snapshots.md new file mode 100644 index 0000000..5443828 --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_concepts_snapshots.md @@ -0,0 +1,196 @@ +--- +title: "Snapshots" +description: "Backup and restore your vector database" +--- + +# Snapshots + +VortexDB's snapshot system provides point-in-time backups of your entire database, including both the index topology and stored data. + +## Overview + +A snapshot captures: +- **Index topology**: The structure of your index (graph edges, tree nodes, etc.) +- **Index metadata**: Configuration specific to the index type +- **Storage data**: All vectors and their payloads +- **Database metadata**: Dimensions, timestamps, version info + +## How It Works + +### Creating a Snapshot + +The snapshot process involves two parallel operations: + + + + The index serializes its topology and metadata into binary format: + ```rust + pub trait SerializableIndex { + fn serialize_topology(&self) -> Result>; + fn serialize_metadata(&self) -> Result>; + fn snapshot(&self) -> Result; + } + ``` + + + The storage backend creates a consistent checkpoint: + ```rust + fn checkpoint_at(&self, path: &Path) -> Result; + ``` + For RocksDB, this uses the native checkpoint API for efficiency. + + + A manifest file is created with: + - Snapshot UUID + - Timestamp + - Parser version (for compatibility) + - SHA-256 checksums of all files + + + All files are bundled into a compressed tarball (`.tar.gz`). + + + +### Restoring a Snapshot + + + + The tarball is extracted to a temporary directory. + + + All file checksums are verified against the manifest. + + + The parser version is checked for compatibility. + + + The storage checkpoint is restored first. + + + The index is deserialized from topology and metadata, then populated with vectors from storage. + + + +## Index-Specific Serialization + +Each index type has its own serialization format: + + + + - **Topology**: List of point IDs in insertion order + - **Metadata**: Minimal (just magic bytes) + - **Restore**: O(n) - simply rebuild the vector list + + + - **Topology**: Tree structure with node relationships + - **Metadata**: Dimension count, tree depth + - **Restore**: O(n) - rebuild tree structure, populate vectors + + + - **Topology**: Multi-layer graph with all edges + - **Metadata**: Level multiplier, max connections, entry point + - **Restore**: O(n) - rebuild graph layers, populate vectors + + + +## Snapshot Data Structures + +### Snapshot Object + +```rust +pub struct Snapshot { + pub id: Uuid, // Unique identifier + pub date: SystemTime, // Creation timestamp + pub sem_ver: Version, // Parser version + pub index_snapshot: IndexSnapshot, // Index data + pub storage_snapshot: StorageCheckpoint, // Storage data + pub dimensions: usize, // Vector dimensions +} +``` + +### Index Snapshot + +```rust +pub struct IndexSnapshot { + pub index_type: IndexType, // Flat, KDTree, or HNSW + pub magic: Magic, // 4-byte identifier + pub topology_b: Vec, // Serialized graph/tree + pub metadata_b: Vec, // Index-specific config +} +``` + +### Storage Checkpoint + +```rust +pub struct StorageCheckpoint { + pub path: PathBuf, // Path to checkpoint files + pub storage_type: StorageType, // InMemory or RocksDB +} +``` + +## Snapshot Engine + +The `SnapshotEngine` manages snapshot creation and restoration: + +### Registry Interface + +```rust +pub trait SnapshotRegistry: Send + Sync { + // Add a new snapshot to the registry + fn add_snapshot(&mut self, path: &Path) -> Result; + + // List snapshots with pagination + fn list_snapshots(&mut self, limit: usize, offset: usize) -> Result; + + // Get the most recent snapshot + fn get_latest_snapshot(&mut self) -> Result; + + // Get metadata for a specific snapshot + fn get_metadata(&mut self, id: String) -> Result; + + // Remove a snapshot + fn remove_snapshot(&mut self, id: String) -> Result; + + // Load and restore a snapshot + fn load(&mut self, id: String, path: &Path) -> Result; +} +``` + +## Manifest Format + +The manifest (`manifest.json`) contains all metadata needed for restore: + +```json +{ + "snapshot_id": "550e8400-e29b-41d4-a716-446655440000", + "created_at": "2026-03-31T12:00:00Z", + "parser_version": "1.0.0", + "dimensions": 384, + "index_type": "HNSW", + "files": { + "index_metadata": { + "filename": "hnsw-index-meta.bin", + "checksum": "sha256:abc123..." + }, + "index_topology": { + "filename": "hnsw-index-topo.bin", + "checksum": "sha256:def456..." + }, + "storage": { + "filename": "rocksdb-checkpoint/", + "checksum": "sha256:789ghi..." + } + } +} +``` + +## Next Steps + + + + Understand how components fit together + + + Choose the right index for your use case + + diff --git a/demo/document-rag/documents/VortexDB_docs_getting-started_installation.md b/demo/document-rag/documents/VortexDB_docs_getting-started_installation.md new file mode 100644 index 0000000..802a683 --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_getting-started_installation.md @@ -0,0 +1,156 @@ +--- +title: "Installation" +description: "Get VortexDB up and running in minutes" +--- + +# Installation + +VortexDB can be installed using Docker (recommended) or built from source. This guide covers both methods. + +## Prerequisites + + + + Docker 20.10+ and Docker Compose v2 + + + Rust 1.88+, protobuf-compiler, clang + + + +## Docker Installation (Recommended) + +The fastest way to get started is using Docker: + +```bash +# Clone the repository +git clone https://github.com/sdslabs/VortexDB.git +cd VortexDB + +# Copy the environment template +cp .env.example .env +``` + +### Configure Environment + +Edit the `.env` file with your settings: + +```bash +# Required settings +VORTEXDB_KEYS_FILE=./keys.json # see keys.example.json for the format +DIMENSION=384 # Vector dimension (e.g., 384 for MiniLM embeddings) +DATA_PATH=/data + +# Optional settings (with defaults) +HTTP_HOST=127.0.0.1 +HTTP_PORT=3000 +GRPC_HOST=127.0.0.1 +GRPC_PORT=50051 +STORAGE_TYPE=inmemory # inmemory | rocksdb +INDEX_TYPE=flat # flat | kdtree | hnsw +SIMILARITY=cosine # cosine | euclidean | manhattan | hamming +LOGGING=true +DISABLE_HTTP=false +``` + +### Start VortexDB + +```bash +docker compose up +``` + + +Use `docker compose up --build` after making code changes to rebuild the image. + + +VortexDB is now running: +- **HTTP API**: http://localhost:3000 +- **gRPC API**: localhost:50051 + +## Building from Source + +### Install Dependencies + + + + ```bash + sudo apt-get update && sudo apt-get install -y \ + protobuf-compiler \ + clang \ + libclang-dev \ + llvm-dev \ + build-essential + ``` + + + ```bash + brew install protobuf llvm + ``` + + + ```bash + sudo pacman -S protobuf clang llvm + ``` + + + +### Build and Run + +```bash +# Clone the repository +git clone https://github.com/sdslabs/VortexDB.git +cd VortexDB + +# Build in release mode +cargo build --release + +# Run the server +./target/release/server +``` + +## Python SDK Installation + +Install the Python client to interact with VortexDB: + +```bash +pip install vortexdb +``` + +Or install from source: + +```bash +cd client/python +pip install -e . +``` + + +The Python SDK requires Python 3.9+ and communicates with VortexDB via gRPC. + + +## Verify Installation + +Check that VortexDB is running correctly: + + +```bash Health Check (HTTP) +curl http://localhost:3000/health +# Response: OK +``` + +```python Health Check (Python) +from vortexdb import VortexDB + +db = VortexDB( + grpc_url="localhost:50051", + api_key="your-secure-password" +) +print("Connected successfully!") +db.close() +``` + + +## Next Steps + + + Insert your first vector in 60 seconds + diff --git a/demo/document-rag/documents/VortexDB_docs_getting-started_quickstart.md b/demo/document-rag/documents/VortexDB_docs_getting-started_quickstart.md new file mode 100644 index 0000000..8c9622c --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_getting-started_quickstart.md @@ -0,0 +1,225 @@ +--- +title: "Quickstart" +description: "Insert your first vector in 60 seconds" +--- + +# Quickstart + +This guide will have you inserting and searching vectors in under 60 seconds. + +## Prerequisites + +Make sure VortexDB is running (see [Installation](/getting-started/installation)). + +## Insert Your First Vector + + + + ```python + from vortexdb import VortexDB, DenseVector, Payload, Similarity + + # Connect to VortexDB + db = VortexDB( + grpc_url="localhost:50051", + api_key="your-secure-password" + ) + + # Insert a vector with a text payload + point_id = db.insert( + vector=DenseVector([0.1, 0.2, 0.3, 0.4]), + payload=Payload.text("Hello, VortexDB!") + ) + print(f"Inserted point: {point_id}") + + # Clean up + db.close() + ``` + + + ```bash + curl -X POST http://localhost:3000/points \ + -H "Content-Type: application/json" \ + -d '{ + "vector": [0.1, 0.2, 0.3, 0.4], + "payload": { + "content_type": "Text", + "content": "Hello, VortexDB!" + } + }' + ``` + + Response: + ```json + { + "point_id": "550e8400-e29b-41d4-a716-446655440000" + } + ``` + + + ```protobuf + // Using grpcurl + grpcurl -plaintext \ + -H "authorization: your-secure-password" \ + -d '{ + "vector": {"values": [0.1, 0.2, 0.3, 0.4]}, + "payload": {"content_type": 1, "content": "Hello, VortexDB!"} + }' \ + localhost:50051 vectordb.VectorDB/InsertVector + ``` + + + +## Search for Similar Vectors + + + + ```python + from vortexdb import VortexDB, DenseVector, Similarity + + db = VortexDB( + grpc_url="localhost:50051", + api_key="your-secure-password" + ) + + # Search for 5 most similar vectors using cosine similarity + results = db.search( + vector=DenseVector([0.1, 0.2, 0.3, 0.4]), + similarity=Similarity.COSINE, + limit=5 + ) + + print(f"Found {len(results)} similar vectors:") + for point_id in results: + print(f" - {point_id}") + + db.close() + ``` + + + ```bash + curl -X POST http://localhost:3000/points/search \ + -H "Content-Type: application/json" \ + -d '{ + "vector": [0.1, 0.2, 0.3, 0.4], + "similarity": "Cosine", + "limit": 5 + }' + ``` + + Response: + ```json + { + "results": [ + "550e8400-e29b-41d4-a716-446655440000", + "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + ] + } + ``` + + + +## Retrieve a Point + + + + ```python + # Get the point you just inserted + point = db.get(point_id=point_id) + + if point: + print(point.pretty()) + # Output: + # Point ID: 550e8400-e29b-41d4-a716-446655440000 + # Vector: [0.1, 0.2, 0.3, 0.4] + # Payload: Hello, VortexDB! + ``` + + + ```bash + curl http://localhost:3000/points/550e8400-e29b-41d4-a716-446655440000 + ``` + + Response: + ```json + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "vector": [0.1, 0.2, 0.3, 0.4], + "payload": { + "content_type": "Text", + "content": "Hello, VortexDB!" + } + } + ``` + + + +## Delete a Point + + + + ```python + db.delete(point_id=point_id) + print("Point deleted successfully") + ``` + + + ```bash + curl -X DELETE http://localhost:3000/points/550e8400-e29b-41d4-a716-446655440000 + ``` + + + +## Complete Example + +Here's a complete example using the Python SDK with context manager: + +```python +from vortexdb import VortexDB, DenseVector, Payload, Similarity + +# Using context manager for automatic cleanup +with VortexDB(grpc_url="localhost:50051", api_key="secret") as db: + # Insert some vectors + vectors = [ + ([0.1, 0.2, 0.3, 0.4], "First document"), + ([0.2, 0.3, 0.4, 0.5], "Second document"), + ([0.9, 0.8, 0.7, 0.6], "Third document"), + ] + + point_ids = [] + for vec, text in vectors: + pid = db.insert( + vector=DenseVector(vec), + payload=Payload.text(text) + ) + point_ids.append(pid) + print(f"Inserted: {text} -> {pid}") + + # Search for vectors similar to the first one + results = db.search( + vector=DenseVector([0.15, 0.25, 0.35, 0.45]), + similarity=Similarity.COSINE, + limit=2 + ) + + print(f"\nTop 2 similar vectors:") + for pid in results: + point = db.get(point_id=pid) + print(f" - {point.payload.content}") +``` + +## Next Steps + + + + Understand how VortexDB works under the hood + + + Choose the right index for your use case + + + Python client documentation + + + Explore the complete API + + diff --git a/demo/document-rag/documents/VortexDB_docs_sdk_examples.md b/demo/document-rag/documents/VortexDB_docs_sdk_examples.md new file mode 100644 index 0000000..0ba249c --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_sdk_examples.md @@ -0,0 +1,144 @@ +--- +title: "SDK Examples" +description: "Working code examples for the VortexDB Python SDK" +--- + +# SDK Examples + +Ready-to-run code examples demonstrating VortexDB Python SDK usage. + +## Basic Usage + +```python +from vortexdb import VortexDB, DenseVector, Payload, Similarity + +with VortexDB(grpc_url="localhost:50051", api_key="secret") as db: + # Insert + point_id = db.insert( + vector=DenseVector([0.1, 0.2, 0.3]), + payload=Payload.text("hello world"), + ) + print(f"Inserted: {point_id}") + + # Batch insert + ids = db.batch_insert(items=[ + (DenseVector([0.1, 0.2, 0.3]), Payload.text("doc one")), + (DenseVector([0.4, 0.5, 0.6]), Payload.text("doc two")), + ]) + + # Search + results = db.search( + vector=DenseVector([0.1, 0.2, 0.3]), + similarity=Similarity.COSINE, + limit=3, + ) + print(f"Found {len(results)} results") +``` + +--- + +## Semantic Search + +Using sentence-transformers for text embedding: + +```python +from vortexdb import VortexDB, DenseVector, Payload, Similarity +from sentence_transformers import SentenceTransformer + +model = SentenceTransformer('all-MiniLM-L6-v2') + +documents = [ + "The quick brown fox jumps over the lazy dog", + "Machine learning is a subset of artificial intelligence", + "Python is a popular programming language", +] + +def embed(text: str) -> DenseVector: + return DenseVector(model.encode(text).tolist()) + +with VortexDB(grpc_url="localhost:50051", api_key="secret") as db: + for doc in documents: + db.insert(vector=embed(doc), payload=Payload.text(doc)) + + results = db.search( + vector=embed("AI and programming"), + similarity=Similarity.COSINE, + limit=2, + ) + for pid in results: + point = db.get(point_id=pid) + print(f" {point.payload.content}") +``` + +--- + +## Batch Processing + +```python +from vortexdb import VortexDB, DenseVector, Payload, Similarity + +with VortexDB(grpc_url="localhost:50051", api_key="secret") as db: + items = [(DenseVector([i * 0.1 for _ in range(3)]), Payload.text(f"doc {i}")) for i in range(100)] + ids = db.batch_insert(items=items) + + # Batch search + queries = [ + (DenseVector([0.1, 0.2, 0.3]), Similarity.COSINE, 3), + (DenseVector([0.4, 0.5, 0.6]), Similarity.EUCLIDEAN, 3), + ] + batch_results = db.batch_search(queries=queries) + for i, res in enumerate(batch_results): + print(f"Query {i}: {len(res)} results") +``` + +--- + +## Testing with pytest + +```python +import pytest +from vortexdb import VortexDB, DenseVector, Payload, Similarity + +@pytest.fixture +def db(): + client = VortexDB(grpc_url="localhost:50051", api_key="secret") + yield client + client.close() + +class TestVortexDB: + def test_insert_and_get(self, db): + point_id = db.insert( + vector=DenseVector([0.1, 0.2, 0.3, 0.4]), + payload=Payload.text("Test document"), + ) + point = db.get(point_id=point_id) + assert point is not None + assert point.payload.content == "Test document" + db.delete(point_id=point_id) + + def test_search(self, db): + point_id = db.insert( + vector=DenseVector([1.0, 2.0, 3.0]), + payload=Payload.text("target"), + ) + results = db.search( + vector=DenseVector([1.0, 2.0, 3.0]), + similarity=Similarity.COSINE, + limit=10, + ) + assert point_id in results + db.delete(point_id=point_id) +``` + +--- + +## Next Steps + + + + Complete API documentation + + + gRPC and HTTP API docs + + diff --git a/demo/document-rag/documents/VortexDB_docs_sdk_reference.md b/demo/document-rag/documents/VortexDB_docs_sdk_reference.md new file mode 100644 index 0000000..ad1e6e2 --- /dev/null +++ b/demo/document-rag/documents/VortexDB_docs_sdk_reference.md @@ -0,0 +1,626 @@ +--- +title: "SDK Reference" +description: "Complete Python SDK API documentation" +--- + +# Python SDK Reference + +Complete API documentation for the VortexDB Python client. + +## Installation + +```bash +pip install vortexdb +``` + +--- + +## VortexDB + +The main client class for interacting with VortexDB. + +```python +from vortexdb import VortexDB +``` + +### Constructor + +```python +VortexDB( + *, + grpc_url: str | None = None, + api_key: str | None = None, + timeout: float | None = None, +) +``` + + + The gRPC server address. Can also be set via `VORTEXDB_GRPC_URL` environment variable. + + + + Authentication key for the gRPC API. Can also be set via `VORTEXDB_API_KEY` environment variable. + + + + Request timeout in seconds. Can also be set via `VORTEXDB_TIMEOUT` environment variable. + + +**Example:** + +```python +# Explicit configuration +db = VortexDB( + grpc_url="localhost:50051", + api_key="secret", + timeout=60.0, +) + +# Using environment variables +import os +os.environ["VORTEXDB_GRPC_URL"] = "localhost:50051" +os.environ["VORTEXDB_API_KEY"] = "secret" +db = VortexDB() +``` + +--- + +### Methods + +#### insert + +Insert a vector with its payload into the database. + +```python +def insert( + self, + *, + vector: DenseVector, + payload: Payload, +) -> str +``` + + + UUID of the created point. + + +**Example:** + +```python +point_id = db.insert( + vector=DenseVector([0.1, 0.2, 0.3, 0.4]), + payload=Payload.text("My document"), +) +``` + +--- + +#### batch_insert + +Insert multiple vectors in a single request. + +```python +def batch_insert( + self, + *, + items: list[tuple[DenseVector, Payload]], +) -> list[str] +``` + + + List of UUIDs for the created points, in input order. + + +**Example:** + +```python +ids = db.batch_insert(items=[ + (DenseVector([0.1, 0.2, 0.3]), Payload.text("doc one")), + (DenseVector([0.4, 0.5, 0.6]), Payload.text("doc two")), +]) +``` + +--- + +#### get + +Retrieve a point by its ID. + +```python +def get( + self, + *, + point_id: str, +) -> Point | None +``` + + + The point if found, `None` otherwise. + + +**Example:** + +```python +point = db.get(point_id="550e8400-e29b-41d4-a716-446655440000") +if point: + print(f"Vector: {point.vector.to_list()}") + print(f"Payload: {point.payload.content}") +``` + +--- + +#### search + +Search for the k nearest neighbors to a query vector. + +```python +def search( + self, + *, + vector: DenseVector | None = None, + similarity: Similarity | None = None, + limit: int | None = None, + query: SearchQuery | None = None, + ef: int | None = None, +) -> List[str] +``` + + + A `SearchQuery` object bundling vector, similarity, and limit. Use this or pass individual args. + + + + Search breadth for HNSW. Uses server default if not set. + + + + List of point IDs ordered by similarity (closest first). + + +**Example:** + +```python +# Using a SearchQuery +query = SearchQuery(DenseVector([0.1, 0.2, 0.3, 0.4]), Similarity.COSINE, 10) +results = db.search(query=query) + +# Using individual args with ef +results = db.search( + vector=DenseVector([0.1, 0.2, 0.3, 0.4]), + similarity=Similarity.COSINE, + limit=10, + ef=200, +) +``` + +--- + +#### batch_search + +Search against multiple query vectors in a single request. + +```python +def batch_search( + self, + *, + queries, + similarity: Similarity | None = None, + limit: int | None = None, + ef: int | None = None, +) -> List[List[str]] +``` + +Accepts `List[SearchQuery]`, `List[(DenseVector, Similarity, int)]`, or bare `List[DenseVector]` with global `similarity` and `limit`. + + + One result list per input query. + + +**Example:** + +```python +results = db.batch_search(queries=[ + SearchQuery(DenseVector([0.1, 0.2, 0.3]), Similarity.COSINE, 5), + (DenseVector([0.4, 0.5, 0.6]), Similarity.EUCLIDEAN, 3), +]) +``` + +--- + +#### delete + +Delete a point by its ID. + +```python +def delete( + self, + *, + point_id: str, +) -> None +``` + +**Example:** + +```python +db.delete(point_id="550e8400-e29b-41d4-a716-446655440000") +``` + +--- + +#### close + +Close the gRPC connection. + +```python +def close(self) -> None +``` + +**Example:** + +```python +db = VortexDB(grpc_url="localhost:50051", api_key="secret") +# ... use the client ... +db.close() +``` + +--- + +### Context Manager + +The client supports the context manager protocol for automatic cleanup: + +```python +with VortexDB(grpc_url="localhost:50051", api_key="secret") as db: + point_id = db.insert( + vector=DenseVector([0.1, 0.2, 0.3]), + payload=Payload.text("Hello"), + ) +# Connection automatically closed +``` + +--- + +## DenseVector + +An immutable dense vector of floating-point values. + +```python +from vortexdb import DenseVector +``` + +### Constructor + +```python +DenseVector(values: List[float] | Tuple[float, ...]) +``` + + + The vector components. Must be non-empty and contain numeric values. + + +**Raises:** +- `TypeError`: If values is not a list or tuple +- `ValueError`: If values is empty +- `TypeError`: If any value is not numeric + +**Example:** + +```python +# From list +vec = DenseVector([0.1, 0.2, 0.3, 0.4]) + +# From tuple +vec = DenseVector((0.1, 0.2, 0.3, 0.4)) + +# Integers are converted to floats +vec = DenseVector([1, 2, 3, 4]) # -> [1.0, 2.0, 3.0, 4.0] +``` + +### Methods + +#### to_list + +Convert the vector to a Python list. + +```python +def to_list(self) -> list[float] +``` + +**Example:** + +```python +vec = DenseVector([0.1, 0.2, 0.3]) +values = vec.to_list() # [0.1, 0.2, 0.3] +``` + +### Properties + +#### values + +Access the vector values (read-only). + +```python +vec = DenseVector([0.1, 0.2, 0.3]) +print(vec.values) # (0.1, 0.2, 0.3) +``` + +--- + +## Payload + +Metadata associated with a vector. + +```python +from vortexdb import Payload +``` + +### Factory Methods + +#### text + +Create a text payload. + +```python +@staticmethod +def text(content: str) -> Payload +``` + +**Example:** + +```python +payload = Payload.text("This is my document content") +``` + +#### image + +Create an image payload. + +```python +@staticmethod +def image(content: str) -> Payload +``` + +**Example:** + +```python +payload = Payload.image("path/to/image.jpg") +``` + +### Constructor + +```python +Payload(content_type: ContentType, content: str) +``` + + + The type of content (`ContentType.TEXT` or `ContentType.IMAGE`). + + + + The content string. + + +### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `content_type` | `ContentType` | Type of payload | +| `content` | `str` | Content string | + +--- + +## Point + +A point returned from the database (vector + payload + ID). + +```python +from vortexdb.models import Point +``` + +### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `id` | `str` | Point UUID | +| `vector` | `DenseVector` | The vector values | +| `payload` | `Payload` | Associated metadata | + +### Methods + +#### pretty + +Return a formatted string representation. + +```python +def pretty(self) -> str +``` + +**Example:** + +```python +point = db.get(point_id="...") +print(point.pretty()) +# Output: +# Point ID: 550e8400-e29b-41d4-a716-446655440000 +# Vector: [0.1, 0.2, 0.3, 0.4] +# Payload Type: Text +# Payload Content: My document +``` + +--- + +## Similarity + +Enum for distance/similarity metrics. + +```python +from vortexdb import Similarity +``` + +### Values + +| Value | Description | +|-------|-------------| +| `Similarity.EUCLIDEAN` | L2 distance (straight line) | +| `Similarity.MANHATTAN` | L1 distance (city block) | +| `Similarity.HAMMING` | Count of differing elements | +| `Similarity.COSINE` | Angular distance | + +**Example:** + +```python +from vortexdb import Similarity + +# Use in search +results = db.search( + vector=DenseVector([0.1, 0.2, 0.3]), + similarity=Similarity.COSINE, + limit=5, +) +``` + +--- + +## ContentType + +Enum for payload content types. + +```python +from vortexdb.models import ContentType +``` + +### Values + +| Value | Description | +|-------|-------------| +| `ContentType.TEXT` | Text content | +| `ContentType.IMAGE` | Image reference | + +--- + +## SearchQuery + +Bundles a search's parameters into a single object. + +```python +from vortexdb import SearchQuery + +query = SearchQuery(DenseVector([0.1, 0.2, 0.3]), Similarity.COSINE, 10) +results = db.search(query=query) +``` + +| Param | Type | Description | +|-------|------|-------------| +| `vector` | `DenseVector` | Query vector | +| `similarity` | `Similarity` | Distance metric | +| `limit` | `int` | Max results | + +--- + +## Exceptions + +All exceptions inherit from `VortexDBError`. + +```python +from vortexdb import ( + VortexDBError, + AuthenticationError, + NotFoundError, + InvalidArgumentError, + TimeoutError, + ServiceUnavailableError, + InternalServerError, +) +``` + +### Exception Hierarchy + +| Exception | Description | +|-----------|-------------| +| `VortexDBError` | Base exception for all errors | +| `AuthenticationError` | Invalid or missing API key | +| `NotFoundError` | Requested resource not found | +| `InvalidArgumentError` | Invalid input parameters | +| `TimeoutError` | Request timed out | +| `ServiceUnavailableError` | Server is unavailable | +| `InternalServerError` | Server-side error | + +**Example:** + +```python +from vortexdb.exceptions import ( + AuthenticationError, + NotFoundError, + VortexDBError, +) + +try: + point = db.get(point_id="nonexistent") +except NotFoundError: + print("Point does not exist") +except AuthenticationError: + print("Check your API key") +except VortexDBError as e: + print(f"Unexpected error: {e}") +``` + +--- + +## Configuration + +### VortexDBConfig + +Internal configuration class (usually not used directly). + +```python +from vortexdb.config import VortexDBConfig + +config = VortexDBConfig.from_env( + grpc_url="localhost:50051", + api_key="secret", + timeout=30.0, +) +``` + +### Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `VORTEXDB_GRPC_URL` | Server address | `localhost:50051` | +| `VORTEXDB_API_KEY` | Authentication key | None | +| `VORTEXDB_TIMEOUT` | Request timeout (seconds) | `30.0` | + +--- + +## Type Hints + +The SDK is fully typed. Import types for type hints: + +```python +from typing import List, Optional +from vortexdb import VortexDB, DenseVector, Payload, Similarity +from vortexdb.models import Point, ContentType + +def search_documents( + db: VortexDB, + query_vector: List[float], + limit: int = 10, +) -> List[Optional[Point]]: + results = db.search( + vector=DenseVector(query_vector), + similarity=Similarity.COSINE, + limit=limit, + ) + return [db.get(point_id=pid) for pid in results] +``` + +## Next Steps + + + + Working code examples + + + gRPC and HTTP API docs + + diff --git a/demo/document-rag/documents/Watchdog_README.md b/demo/document-rag/documents/Watchdog_README.md new file mode 100644 index 0000000..5bf9976 --- /dev/null +++ b/demo/document-rag/documents/Watchdog_README.md @@ -0,0 +1,145 @@ +# Watchdog + +> Lightweight server access management system + +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/sdslabs/watchdog/blob/master/LICENSE.md) + +Watchdog is a personalised server access management tool (and a slack bot) which keeps a track of all the administrative rights attempts (like sudo and su) on server (via SSH) and allows/disallows log-in attempts based on public key of user and logs all activity in form of slack message. It provides easy granting/revoking access to servers to team members through pull requests on a keyhouse repository. + +Check out this blog post to know how watchdog works and design methodologies behind it: https://blog.sdslabs.co/2020/04/watchdog + +## Contents + +* [Features](#features) +* [Dependencies](#dependencies) +* [Installation](#installation) +* [Usage](#usage) +* [Development](#development) +* [Contact](#contact) + + +## Features + +* Request SSH access to a server just by creating a PR to the Keyhouse repository. +* Stateless and serverless. Watchdog runs on a single binary. +* Optional server activity logs to your favourite workspace like Slack or Discord. +* Easy Installation and Configuration +* Get notified when someone escalates privileges or performs administrative tasks using `sudo` or `su` + +## Dependencies + +The following softwares are required for running Watchdog:- + +* PAM +* OpenSSH server + +## Installation + +1. Create a Keyhouse Repository using the template repository [here](https://github.com/sdslabs/keyhouse-template). + +2. Clone the watchdog repository + + `git clone https://github.com/sdslabs/watchdog.git` + +3. Change into the repository directory and build the latest binaries using Cargo + + `cargo build --release` + +4. Copy `sample.config.toml` to `config.toml` and make changes to the config this way: + + ```toml + # Hostname of the machine running watchdog. Note that this should be + # same as the file you create in the `hosts` directory in keyhouse. + hostname = 'virtual-machine' + + # Keyhouse repository configuration + [keyhouse] + + # URL of the Keyhouse repository, it should be of the format + # `https://api.github.com/repos///contents` + base_url = 'https://api.github.com/repos/sdslabs/keyhouse-template/contents' + + # This should be a personal access token made by a member of organization on his/her + # behalf who can read the Keyhouse repository. Go to this + # https://github.com/settings/tokens/new?description=Keyhouse%20Token&scopes=repo + # to make a new token with correct scopes. + token = 'secret_token' + + # Webhook APIs corresponding to various notifiers + [notifiers] + + # Make an incoming hook to your Slack workspace from this + # app(https://slack.com/apps/A0F7XDUAZ-incoming-webhooks) + # and paste the hook URL here. You can customize the icon and name as you like. + slack = 'https://hooks.slack.com/services/ABCDEFGHI/ABCDEFGHI/abcdefghijklmnopqrstuvwx' + ``` + +5. Once you are done configuring, run this command with root(sudo) privileges + + `cd install && sudo ./install.sh` + +6. Add `/opt/watchdog/bin` to your PATH variable. + +## Usage + +``` +$ watchdog --help + +Watchdog 0.1.0 +SDSLabs +Simple server access management system on a binary + +USAGE: + watchdog [SUBCOMMAND] + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + +SUBCOMMANDS: + auth Authorizes users based on from keyhouse repository. This command is passed through + `AuthorizedKeysCommand` in sshd_config. + config Get or set Watchdog configuration + help Prints this message or the help of the given subcommand(s) + logs Get the global watchdog logs + ssh Handles the PAM SSH calls by pam_exec for Watchdog + su Handles the PAM su calls by pam_exec for Watchdog + sudo Handles the PAM sudo calls by pam_exec for Watchdog +``` + +Though most of the commands are for internal use of PAM, you can edit configuration of Watchdog any time + +```sh +$ watchdog config --help +``` + +_NOTE:_ config can be fetched/edited only with `root` (`sudo`) access. + +To view logs + +```sh +$ watchdog logs --help +``` + +## Development + +You need to have [Rust](https://www.rust-lang.org/tools/install) installed along with the mentioned [dependencies](#dependencies) + +Open your favourite terminal and perform the following tasks:- + +1. Clone this repository. + +```bash +$ git clone https://github.com/sdslabs/watchdog +``` + +2. Make the required changes inside the source code directory ([src/](src/)) + +3. Run `cargo test` to test your changes. + +4. Rebuild the binary using `cargo build` command. + +## Contact + +If you have a query regarding the product or just want to say hello then feel free to visit +[chat.sdslabs.co](http://chat.sdslabs.co/) or drop a mail at [contact@sdslabs.co.in](mailto:contact@sdslabs.co.in) diff --git a/demo/document-rag/documents/api-integration-guide.md b/demo/document-rag/documents/api-integration-guide.md deleted file mode 100644 index ec0b0a7..0000000 --- a/demo/document-rag/documents/api-integration-guide.md +++ /dev/null @@ -1,26 +0,0 @@ -# Vortex Cloud API Integration Guide - -**API version:** v2 -**Updated:** 2026-05-18 - -The Vortex Cloud API lets customers read site telemetry, acknowledge alarms, and export reports. API access is available on Growth and Enterprise plans. - -## Authentication - -Use an organization-scoped service account with a short-lived OAuth token. Tokens expire after one hour. Service accounts receive only the scopes explicitly assigned to them; use `telemetry:read`, `alarms:write`, and `reports:read` rather than a broad administrative scope. - -## Key endpoints - -`GET /v2/sites/{site_id}/telemetry?from=&to=` returns normalized readings. The maximum query window is 31 days. - -`GET /v2/alarms` accepts filters for status, severity, site, and tag. Alarm timestamps are in UTC. - -`POST /v2/alarms/{alarm_id}/acknowledgements` records the named operator, timestamp, and optional note. It does not resolve the alarm; resolution requires the `alarms:resolve` scope. - -`POST /v2/exports` creates an asynchronous CSV or Parquet export. Exports are retained for 24 hours and are listed in the audit log. - -## Rate limits and backfill - -The default limit is 120 requests per minute per service account. During an Aster connectivity recovery, telemetry can arrive up to 72 hours late; clients should order records by `observed_at`, not API receipt time. - -Never embed service-account secrets in Aster configuration files or source repositories. diff --git a/demo/document-rag/documents/company-overview.md b/demo/document-rag/documents/company-overview.md deleted file mode 100644 index 2add43b..0000000 --- a/demo/document-rag/documents/company-overview.md +++ /dev/null @@ -1,22 +0,0 @@ -# Vortex Lab: Company Overview - -**Updated:** 2026-06-12 -**Owner:** Maya Chen, Chief Executive Officer - -Vortex Lab builds software for teams operating distributed energy equipment: solar farms, battery sites, and microgrids. Its platform combines rugged edge gateways with a cloud control plane that turns noisy device readings into actionable operating signals. - -## Products - -- **Aster Edge** collects telemetry from site controllers, normalizes common industrial protocols, and can keep operating while disconnected. -- **Vortex Cloud** stores, visualizes, and routes telemetry, alarms, maintenance notes, and reports. -- **Pulse** is the anomaly-detection service used to prioritize alerts. - -## Customers and operating model - -Our customers are asset owners and service providers with 20 to 2,000 sites. They typically use Vortex during daily operations, incident response, and monthly performance reviews. Customer Success owns adoption; Reliability Engineering owns the shared cloud platform; Support owns first response for production incidents. - -Vortex Lab’s stated operating principle is: **an alert should lead to a clear next action, not merely more data**. - -## Current priorities - -For the second half of 2026, the company is focused on reducing false-positive alerts, expanding offline workflows in Aster Edge, and improving export controls for enterprise customers. The public product roadmap is in `product-roadmap-2026-h2.md`. diff --git a/demo/document-rag/documents/employee-onboarding.md b/demo/document-rag/documents/employee-onboarding.md deleted file mode 100644 index 9664e96..0000000 --- a/demo/document-rag/documents/employee-onboarding.md +++ /dev/null @@ -1,20 +0,0 @@ -# Employee Onboarding: Operations and Engineering - -**Updated:** 2026-06-08 - -Welcome to Vortex Lab. During your first week, complete security training, request the least-privilege role for your work, and join your team’s service rotation shadow session. - -## Required setup - -1. Enroll a hardware security key in SSO. -2. Activate the password manager and store no customer credentials elsewhere. -3. Read the Security and Access Policy and acknowledge the acceptable-use statement. -4. Request staging access through Access Hub; do not request production access until your manager identifies a business need. - -## Working with customer information - -Use a customer’s organization ID, not its name, in internal engineering logs whenever practical. Do not download raw telemetry to personal machines. Support attachments must be placed in the case workspace, which applies access controls and retention rules. - -## Escalation - -For a production issue, page the on-call reliability engineer. For a suspected security event, follow the one-hour reporting requirement in `security-and-access-policy.md`. diff --git a/demo/document-rag/documents/engineering-decision-record-042.md b/demo/document-rag/documents/engineering-decision-record-042.md deleted file mode 100644 index 7c135e9..0000000 --- a/demo/document-rag/documents/engineering-decision-record-042.md +++ /dev/null @@ -1,18 +0,0 @@ -# ADR-042: Keep a 10-Minute Alert Watermark - -**Date:** 2026-05-28 -**Status:** Accepted - -## Context - -Pulse must handle delayed telemetry from Aster Edge without causing duplicate or misleading pages. A May incident demonstrated that a globally expanded 60-minute watermark made operational alerts unacceptably late for battery customers. - -## Decision - -The default alert watermark remains 10 minutes. Any exception longer than 15 minutes must be organization-scoped, approved by Reliability Engineering, have an expiry date, and be covered by a replay test using delayed events. - -Events received after the watermark are marked as backfill. They update historical charts and reports but do not reopen a resolved alarm automatically. Operators can manually review a backfill event from the alarm timeline. - -## Consequences - -Some intermittently connected sites may continue to create duplicate candidate alerts, which Pulse deduplicates using rule and event identity. This is preferable to silently delaying high-severity alerts across unrelated customers. diff --git a/demo/document-rag/documents/incident-2026-05-northstar.md b/demo/document-rag/documents/incident-2026-05-northstar.md deleted file mode 100644 index 4ab1f08..0000000 --- a/demo/document-rag/documents/incident-2026-05-northstar.md +++ /dev/null @@ -1,19 +0,0 @@ -# Incident Report: Northstar Alert Delay - -**Date:** 2026-05-14 -**Severity:** SEV-2 -**Status:** Closed - -## Summary - -Northstar Energy received delayed high-temperature alerts for three battery sites between 09:12 and 10:03 UTC. No equipment damage occurred. Operators identified the condition through their local SCADA system and placed the affected units in a safe operating mode. - -## Cause - -On May 12, an alert-processing configuration change increased Pulse’s late-event watermark from 10 to 60 minutes for all organizations. The change was intended to reduce duplicate pages caused by one intermittent site. Aster Edge gateways at Northstar backfilled valid events after brief cellular outages; Pulse withheld their alert evaluation until the expanded watermark elapsed. - -## Resolution and follow-up - -The watermark was restored to 10 minutes at 10:03 UTC. We added organization-scoped configuration validation, a test fixture for delayed Aster events, and a dashboard showing alert-evaluation lag. The previous customer-specific workaround was removed. - -Northstar received a written incident summary, a 30-day alerting credit, and weekly progress updates until the follow-up items were complete. See `q2-customer-success-notes.md` for the commercial commitments. diff --git a/demo/document-rag/documents/monthly-operations-report-june-2026.md b/demo/document-rag/documents/monthly-operations-report-june-2026.md deleted file mode 100644 index 0297d78..0000000 --- a/demo/document-rag/documents/monthly-operations-report-june-2026.md +++ /dev/null @@ -1,19 +0,0 @@ -# Monthly Operations Report — June 2026 - -**Prepared:** 2026-07-05 - -## Reliability snapshot - -Vortex Cloud availability was 99.96% in June. Median telemetry ingestion latency was 4.2 seconds for connected sites. 94.1% of delayed Aster uploads completed within 12 minutes after connectivity was restored. - -## Alert quality - -High-severity alert median evaluation time was 7.8 minutes, down from 18.4 minutes in May. The improvement followed the restoration of the 10-minute Pulse watermark and the addition of evaluation-lag monitoring. The Northstar pilot reported two actionable backfill labels and no delayed high-temperature pages during the month. - -## Open work - -Engineering is completing API support for `is_backfill`; this remains targeted for July. Customer Success is validating the weekly alert-quality report before offering it to other battery customers. - -## Retention reminder - -Raw telemetry retention follows each customer’s plan or contract. Growth includes 90 days under the April 2026 plan update; data exports are available for 24 hours after creation. diff --git a/demo/document-rag/documents/platform-architecture.md b/demo/document-rag/documents/platform-architecture.md deleted file mode 100644 index b2e2d5a..0000000 --- a/demo/document-rag/documents/platform-architecture.md +++ /dev/null @@ -1,21 +0,0 @@ -# Platform Architecture - -**Updated:** 2026-06-10 -**Audience:** Engineering and Security - -Vortex Cloud separates ingestion, operational storage, alert evaluation, and customer-facing applications. - -1. **Ingress** validates device identity, schema, and message signatures. -2. **Stream processing** enriches events with site metadata and writes immutable raw telemetry. -3. **Pulse** evaluates anomaly models and deterministic alert rules. -4. **Operations API** serves the web application, reports, and customer integrations. - -Raw telemetry is encrypted at rest and logically partitioned by organization ID. The web application uses the Operations API; it does not directly query the telemetry store. Customer data is replicated within the selected hosting region for durability. - -## Resilience - -Ingress accepts delayed events from Aster Edge and deduplicates them using gateway ID, sequence number, and observed timestamp. Pulse uses a 10-minute watermark for normal alert evaluation; events older than the watermark are marked as backfill and may update reports, but do not automatically reopen a resolved incident. - -## Known trade-off - -The watermark avoids duplicate pages during connectivity recovery, but it can delay alert classification for intermittently connected sites. The Northstar incident in May 2026 exposed a configuration error in this boundary; details are in `incident-2026-05-northstar.md`. diff --git a/demo/document-rag/documents/pricing-and-plans.md b/demo/document-rag/documents/pricing-and-plans.md deleted file mode 100644 index 16dd682..0000000 --- a/demo/document-rag/documents/pricing-and-plans.md +++ /dev/null @@ -1,20 +0,0 @@ -# Pricing and Plans - -**Effective:** 2026-04-01 -**Owner:** Revenue Operations - -| Plan | Monthly platform fee | Included sites | Raw telemetry retention | API access | -| --- | ---: | ---: | --- | --- | -| Starter | $1,200 | 10 | 30 days | No | -| Growth | $3,500 | 50 | 90 days | Yes | -| Enterprise | Custom | 51+ | 365 days | Yes | - -Additional sites are billed annually. Aster Edge hardware is quoted separately. Enterprise includes regional hosting selection, SAML SSO, quarterly security reviews, and a named Customer Success Manager. - -## Alerting and support - -All plans include email and in-app alarms. SMS and webhook delivery are available on Growth and Enterprise. Starter support responds during business hours; Growth receives 8x5 support with a four-business-hour target; Enterprise receives 24x7 severity-one response. - -## April 2026 change - -Before 2026-04-01, Growth plans included 60 days of raw telemetry. New and renewing Growth agreements now include 90 days. Contract terms take precedence if a signed agreement specifies a different retention period. diff --git a/demo/document-rag/documents/product-guide-aster-edge.md b/demo/document-rag/documents/product-guide-aster-edge.md deleted file mode 100644 index 873810a..0000000 --- a/demo/document-rag/documents/product-guide-aster-edge.md +++ /dev/null @@ -1,22 +0,0 @@ -# Aster Edge Product Guide - -**Version:** 3.4 -**Updated:** 2026-06-01 - -Aster Edge is a site-installed gateway that reads device telemetry, applies local rules, and securely synchronizes with Vortex Cloud. - -## Core behavior - -Aster polls supported devices every 15 seconds by default. It signs and batches observations before upload. If a site loses internet access, Aster writes events and commands to an encrypted local queue. On reconnect, it uploads queued telemetry in chronological order and reports a `backfill_complete` event to Vortex Cloud. - -The local queue is capped at 72 hours of standard telemetry. If the queue is full, Aster preserves alarm and command-audit events and begins sampling ordinary telemetry at five-minute intervals. This does not affect local safety interlocks, which remain on the controller. - -## Local rules - -Operators may deploy approved threshold rules from Vortex Cloud. A local rule can create an alarm, attach a recommended runbook, or hold a non-safety command for operator review. Aster never autonomously changes inverter set points unless the site has the optional Closed Loop Automation entitlement and an approved site policy. - -## Installation notes - -Installers register each gateway against a single customer organization and site. The registration token expires after 30 minutes. Aster requires outbound HTTPS access to `ingest.vortexlab.example` and time synchronization via NTP. - -See `api-integration-guide.md` for data payloads and `security-and-access-policy.md` for credential handling. diff --git a/demo/document-rag/documents/product-roadmap-2026-h2.md b/demo/document-rag/documents/product-roadmap-2026-h2.md deleted file mode 100644 index 5c46194..0000000 --- a/demo/document-rag/documents/product-roadmap-2026-h2.md +++ /dev/null @@ -1,23 +0,0 @@ -# Product Roadmap: H2 2026 - -**Published:** 2026-06-30 -**Status:** Directional; dates may change - -## July–August - -- Add `is_backfill` and `ingested_at` to the v2 telemetry API. -- Release alert-evaluation lag indicators in the operator console. -- Pilot weekly alert-quality reports with Northstar Energy. - -## September–October - -- Expand Aster Edge offline queue observability, including local storage pressure warnings. -- Introduce organization-scoped Pulse configuration guardrails. -- Launch self-service webhook signing-key rotation for Growth and Enterprise customers. - -## November–December - -- Limited beta for Closed Loop Automation policy templates. -- Enterprise export controls: approval workflows and region-aware export storage. - -The roadmap intentionally does not promise delivery dates to customers. Customer commitments recorded in signed agreements or success plans take priority over this document. diff --git a/demo/document-rag/documents/q2-customer-success-notes.md b/demo/document-rag/documents/q2-customer-success-notes.md deleted file mode 100644 index 7d70915..0000000 --- a/demo/document-rag/documents/q2-customer-success-notes.md +++ /dev/null @@ -1,17 +0,0 @@ -# Q2 Customer Success Notes: Northstar Energy - -**Meeting date:** 2026-05-20 -**Participants:** Elena Ruiz (Northstar), Priya Nair (Vortex), Omar Bell (Vortex) - -## Customer feedback - -Northstar values Aster’s offline telemetry recovery but wants a clear indication that an alarm is based on delayed data. Its operations team also asked for a site-level weekly alert-quality report and an API field that identifies backfilled events. - -## Vortex commitments - -- Provide a written root-cause analysis by May 22 and weekly follow-ups through June. -- Deliver an `is_backfill` field in the v2 telemetry API by July 15. -- Pilot the weekly alert-quality report for Northstar’s three battery sites in June. -- Apply a 30-day alerting credit to the June invoice. - -Northstar will evaluate a Growth-to-Enterprise upgrade in August if the pilot demonstrates fewer false-positive escalations. Pricing questions should reference the current plan sheet, not the pre-April proposal. diff --git a/demo/document-rag/documents/sales-proposal-northstar-draft.md b/demo/document-rag/documents/sales-proposal-northstar-draft.md deleted file mode 100644 index 12fa530..0000000 --- a/demo/document-rag/documents/sales-proposal-northstar-draft.md +++ /dev/null @@ -1,10 +0,0 @@ -# Draft Proposal: Northstar Energy Enterprise Upgrade - -**Drafted:** 2026-01-20 -**Status:** Superseded — do not use for current pricing - -This draft proposed an Enterprise upgrade for Northstar’s 42 sites. It described a 60-day raw telemetry retention period for Growth and listed webhook delivery as a paid add-on. - -The proposal was never signed. The plan catalogue changed on 2026-04-01: Growth now includes 90 days of raw telemetry, while webhooks are included for Growth and Enterprise. For current terms, use `pricing-and-plans.md` and the customer’s signed agreement. - -This document is retained only to demonstrate how retrieval systems should recognize stale commercial information. diff --git a/demo/document-rag/documents/security-and-access-policy.md b/demo/document-rag/documents/security-and-access-policy.md deleted file mode 100644 index 3c67e0e..0000000 --- a/demo/document-rag/documents/security-and-access-policy.md +++ /dev/null @@ -1,20 +0,0 @@ -# Security and Access Policy - -**Effective:** 2026-06-15 -**Policy owner:** Security Engineering - -## Access principles - -Vortex uses least privilege, organization isolation, and time-bounded elevated access. Employees authenticate with SSO and phishing-resistant MFA. Production access is granted through named roles and logged. - -Contractors may access staging systems and sanitized support reproductions. They may not access production telemetry, production databases, customer exports, or incident channels containing customer data unless the Chief Information Security Officer grants a documented, time-limited exception. - -## Customer data retention - -Raw telemetry retention is controlled by the customer plan: Starter retains 30 days, Growth retains 90 days, and Enterprise retains 365 days by default. Enterprise customers can purchase an archival extension of up to seven years. Aggregated monthly metrics are retained for the life of an active account plus 12 months. - -This policy supersedes the draft retention language in the January planning notes. Legal holds suspend deletion for the data in scope. - -## Incident handling - -Suspected unauthorized access must be reported to `security@vortexlab.example` and the on-call reliability engineer within one hour. Security Engineering coordinates investigation, customer notification, and required regulatory reporting. diff --git a/demo/document-rag/documents/support-ticket-1842.md b/demo/document-rag/documents/support-ticket-1842.md deleted file mode 100644 index fa1c634..0000000 --- a/demo/document-rag/documents/support-ticket-1842.md +++ /dev/null @@ -1,11 +0,0 @@ -# Support Ticket 1842: Missing Telemetry After Storm - -**Customer:** Rivermark Solar -**Opened:** 2026-06-03 -**Status:** Resolved - -Rivermark reported a gap in inverter telemetry after a regional storm. Support verified that the Aster gateway remained powered but had no cellular route from 02:18 to 06:47 local time. - -After connectivity returned, Aster uploaded the queued readings. The customer dashboard showed a temporary gap because its selected view was sorted by ingestion time; engineering confirmed the records existed when queried by `observed_at`. - -Support advised Rivermark to use the “event time” option in reporting and linked the API integration guide. No data was lost. The case also prompted a UI improvement request to label backfilled data more clearly. diff --git a/demo/document-rag/documents/support-ticket-1907.md b/demo/document-rag/documents/support-ticket-1907.md deleted file mode 100644 index c0ec3a3..0000000 --- a/demo/document-rag/documents/support-ticket-1907.md +++ /dev/null @@ -1,11 +0,0 @@ -# Support Ticket 1907: Request for Contractor Access - -**Customer:** Orion Field Services -**Opened:** 2026-06-18 -**Status:** Closed — guidance provided - -Orion asked whether a third-party maintenance contractor could receive a Vortex login to investigate an active inverter alarm. - -Support explained that customer-managed users can be invited with the Site Technician role, limited to selected sites and alarm acknowledgement. The customer remains responsible for approving and removing that user. Vortex Lab contractors cannot receive production telemetry access under the standard Security and Access Policy. - -For a Vortex-assisted investigation, Support can create a sanitized reproduction or request a documented, time-limited security exception. Orion elected to invite its own technician and requested an audit-log export after the work was completed.