diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9f0710c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.git +.gitignore +__pycache__/ +*.pyc +venv/ +.env +node_modules/ diff --git a/.gitignore b/.gitignore index 59c854a..9f6a301 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ __pycache__/ *.py[cod] *$py.class *.so +*.box .Python build/ develop-eggs/ diff --git a/Dockerfile b/Dockerfile index d9c52ab..afa3b70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,20 @@ -# -------- Stage 1: Builder -------- +# ============================ +# Stage 1 — Python Builder +# ============================ FROM python:3.12-slim AS builder ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ VIRTUAL_ENV=/opt/venv -# Install build dependencies in one layer and clean up +# System deps for building Python wheels RUN apt-get update && \ apt-get install -y --no-install-recommends \ git \ build-essential \ python3-dev && \ - python -m venv $VIRTUAL_ENV + python -m venv $VIRTUAL_ENV && \ + rm -rf /var/lib/apt/lists/* ENV PATH="$VIRTUAL_ENV/bin:$PATH" @@ -19,39 +22,62 @@ COPY requirements.txt . RUN pip install --no-cache-dir --upgrade pip && \ pip install --no-cache-dir -r requirements.txt -# Clone demo client and remove unnecessary files immediately -RUN git clone --depth 1 https://github.com/lakeraai/guard-demo-client /home/lakeraai && \ +# Clone frontend repo +RUN git clone --depth 1 https://github.com/vmummer/guard-demo-client /home/lakeraai && \ rm -rf /home/lakeraai/.git -# -------- Stage 2: Final Image -------- -# Using a single-layer approach for runtime dependencies + +# ============================ +# Stage 2 — Final Runtime Image +# ============================ FROM python:3.12-slim -ENV VIRTUAL_ENV=/opt/venv \ +ENV PYTHONUNBUFFERED=1 \ + VIRTUAL_ENV=/opt/venv \ PATH="/opt/venv/bin:$PATH" \ - PYTHONUNBUFFERED=1 \ - NODE_MAJOR=20 + NODE_MAJOR=20 \ + BROWSERSLIST_UPDATE_DB=0 WORKDIR /home/lakeraai -# Combine all system setup: Node.js, Curl, and Cleanup +# Create non-root user +RUN groupadd -g 10001 appuser && \ + useradd -u 10001 -g appuser -m -s /usr/sbin/nologin lakeraai-user + +# Install Node.js (clean, multi-arch safe) RUN apt-get update && \ apt-get install -y --no-install-recommends curl ca-certificates gnupg && \ mkdir -p /etc/apt/keyrings && \ - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ + | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" \ + > /etc/apt/sources.list.d/nodesource.list && \ apt-get update && \ apt-get install -y --no-install-recommends nodejs && \ - # Remove package lists to save space - apt-get purge -y --auto-remove gnupg && \ + apt-get purge -y --auto-remove gnupg curl && \ rm -rf /var/lib/apt/lists/* -# Copy only necessary artifacts from builder -COPY --from=builder $VIRTUAL_ENV $VIRTUAL_ENV -COPY --from=builder /home/lakeraai /home/lakeraai +# Copy Python venv + frontend code from builder +COPY --from=builder --chown=lakeraai-user:appuser $VIRTUAL_ENV $VIRTUAL_ENV +COPY --from=builder --chown=lakeraai-user:appuser /home/lakeraai /home/lakeraai + +# Copy backend code +COPY --chown=lakeraai-user:appuser . . + +# Install frontend deps at build time (NOT runtime) +#RUN npm ci --omit=dev || npm install --omit=dev + +RUN npm ci || npm install || npm run build + +# Fix permissions for non-root user + +RUN mkdir -p /home/lakeraai/chroma && \ + mkdir -p /home/lakeraai/backend/data/chroma && \ + mkdir -p /home/lakeraai/data/chroma && \ + chown -R lakeraai-user:appuser /home/lakeraai && \ + chmod -R g+w /home/lakeraai -# Copy local files (ensure .dockerignore excludes node_modules or venv) -COPY . . +USER lakeraai-user EXPOSE 8000 diff --git a/Dockerfile.box b/Dockerfile.box new file mode 100644 index 0000000..d9c52ab --- /dev/null +++ b/Dockerfile.box @@ -0,0 +1,58 @@ +# -------- Stage 1: Builder -------- +FROM python:3.12-slim AS builder + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + VIRTUAL_ENV=/opt/venv + +# Install build dependencies in one layer and clean up +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + git \ + build-essential \ + python3-dev && \ + python -m venv $VIRTUAL_ENV + +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +COPY requirements.txt . +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir -r requirements.txt + +# Clone demo client and remove unnecessary files immediately +RUN git clone --depth 1 https://github.com/lakeraai/guard-demo-client /home/lakeraai && \ + rm -rf /home/lakeraai/.git + +# -------- Stage 2: Final Image -------- +# Using a single-layer approach for runtime dependencies +FROM python:3.12-slim + +ENV VIRTUAL_ENV=/opt/venv \ + PATH="/opt/venv/bin:$PATH" \ + PYTHONUNBUFFERED=1 \ + NODE_MAJOR=20 + +WORKDIR /home/lakeraai + +# Combine all system setup: Node.js, Curl, and Cleanup +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl ca-certificates gnupg && \ + mkdir -p /etc/apt/keyrings && \ + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends nodejs && \ + # Remove package lists to save space + apt-get purge -y --auto-remove gnupg && \ + rm -rf /var/lib/apt/lists/* + +# Copy only necessary artifacts from builder +COPY --from=builder $VIRTUAL_ENV $VIRTUAL_ENV +COPY --from=builder /home/lakeraai /home/lakeraai + +# Copy local files (ensure .dockerignore excludes node_modules or venv) +COPY . . + +EXPOSE 8000 + +CMD ["python", "start_all.py"] diff --git a/Helm Chart.md b/Helm Chart.md new file mode 100644 index 0000000..5b32d1e --- /dev/null +++ b/Helm Chart.md @@ -0,0 +1,44 @@ + +image + +# Deploying the Check Point AI Guardrails Demo in Kubernetes Cluster via a Helm Chart + +These instructions should work for a standard Kubernetes Cluster. x86 (amd64) & arm64 based deployments + +--- + +## Deploy the Toolhive Operator CRDS and Toolhive Operator +```bash +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds + +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n checkpoint --create-namespace +``` + +## Deploy the Check Point AI Guardrails Demo Helm Chart +**Mac / Windows:** + +```bash + helm upgrade --install cpaiguard oci://registry-1.docker.io/vmummer/cpaiguard --version 0.2.0 -n checkpoint --create-namespace +``` +Options: + + --set guarddemo.volumePaths.data="/home/checkpoint/guard-data" \ + --set guarddemo.volumePaths.upload="/home/checkpoint/guard-data/uploads" \ + --set guarddemo.replicas=1 \ + --set ingress.host="172.20.27.76.nip.io" \ + --set env.OPENAI_BASE_URL="http://your-ollama-url" <<< Optional - Remove to default to OpenAI API +``` + + +# Admin Console Settings + +Verify the Toolhive Fetch is set to the following: + +Fetch +Type: HTTP(MCP Endpoint) +Endpoint: http://mcp-cpaiguard-fetch-proxy:8080/mcp (Note: If you deploy the Toolhive in its own namespace, you must change it here. In this helm chart, it defaults to checkpoint namespace) + + +Files +Type: MCP (SSE Endpoint) +Endpoint: http://mcp-cpaiguard-filesystem-proxy:8080/mcp (Note: If you deploy the Toolhive in its own namespace, you must change it here. In this helm chart, it defaults to checkpoint namespace) diff --git a/Kurbernetes/Readme.md b/Kurbernetes/Readme.md new file mode 100644 index 0000000..7ea99a2 --- /dev/null +++ b/Kurbernetes/Readme.md @@ -0,0 +1,46 @@ + +image + + + +# Deploying the Check Point AI Guardrails Demo in Kubernetes Cluster via a Helm Chart + +These instructions should work for a standard Kubernetes Cluster. x86 (amd64) & arm64 based deployments + +--- + +## Deploy the Toolhive Operator CRDS and Toolhive Operator +```bash +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds + +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n checkpoint --create-namespace +``` + +## Deploy the Check Point AI Guardrails Demo Helm Chart +**Mac / Windows:** + +```bash + helm upgrade --install cpaiguard oci://registry-1.docker.io/vmummer/cpaiguard --version 0.2.0 -n checkpoint --create-namespace +``` +Options: + + --set guarddemo.volumePaths.data="/home/checkpoint/guard-data" \ + --set guarddemo.volumePaths.upload="/home/checkpoint/guard-data/uploads" \ + --set guarddemo.replicas=1 \ + --set ingress.host="172.20.27.76.nip.io" \ + --set env.OPENAI_BASE_URL="http://your-ollama-url" +``` + + +# Admin Console Settings + +Verify the Toolhive Fetch is set to the following: + +Fetch +Type: HTTP(MCP Endpoint) +Endpoint: http://mcp-cpaiguard-fetch-proxy:8080/mcp (Note: If you deploy the Toolhive in its own namespace, you must change it here. In this helm chart, it defaults to checkpoint namespace) + + +Files +Type: MCP (SSE Endpoint) +Endpoint: http://mcp-cpaiguard-filesystem-proxy:8080/mcp (Note: If you deploy the Toolhive in its own namespace, you must change it here. In this helm chart, it defaults to checkpoint namespace) diff --git a/README.md b/README.md index 431df93..5dfb2a2 100644 --- a/README.md +++ b/README.md @@ -231,9 +231,11 @@ All API routes are under the `/api` prefix. - `POST /api/chat` - Send message to AI assistant ### RAG -- `POST /api/rag/upload` - Upload documents +- `POST /api/rag/upload` - Upload and Ingest documents - `POST /api/rag/generate` - Generate AI content - `GET /api/rag/search` - Search stored content +- `GET /api/rag/sources` - Request all RAG sources +- `GET /api/rag/clear` - Clear All RAG content ### Tools - `GET /api/tools` - List tools @@ -241,6 +243,7 @@ All API routes are under the `/api` prefix. - `PUT /api/tools/{id}` - Update tool - `DELETE /api/tools/{id}` - Delete tool - `POST /api/tools/test/{id}` - Test tool +- `GET /api/tools/{id}/capabilities` - Request Capabilities of an MCP Tool ### Lakera - `GET /api/lakera/last` - Get last guardrail result @@ -253,6 +256,10 @@ All API routes are under the `/api` prefix. - `DELETE /api/demo-prompts/{id}` - Delete demo prompt - `POST /api/demo-prompts/{id}/use` - Track prompt usage +### AI Models +- `GET /api/models` - Request available Models +- `GET /api/embeddings-models` - Request available Embedding Models + ## 📁 Project Structure ``` diff --git a/backend/lakera.py b/backend/lakera.py index 2b0bd22..05e4546 100644 --- a/backend/lakera.py +++ b/backend/lakera.py @@ -1,9 +1,10 @@ +import os from typing import Any, Dict, List, Optional import httpx -LAKERA_URL = "https://api.lakera.ai/v2/guard" -LAKERA_RESULTS_URL = "https://api.lakera.ai/v2/guard/results" +LAKERA_URL = os.environ.get("LAKERA_URL", "https://api.lakera.ai/v2/guard") +LAKERA_RESULTS_URL = os.environ.get("LAKERA_RESULTS_URL", "https://api.lakera.ai/v2/guard/results") # /guard/results returns confidence levels; treat these as detector hits for UI surfacing. _RESULTS_DETECTED_LEVELS = frozenset({"l1_confident", "l2_very_likely", "l3_likely"}) diff --git a/backend/llm_client.py b/backend/llm_client.py index 3d76904..c9f6655 100644 --- a/backend/llm_client.py +++ b/backend/llm_client.py @@ -17,12 +17,13 @@ STATIC_MODELS = [ "gpt-5", "gpt-5-mini", - "gpt-5-nano", - "gpt-4o", + "gpt-5-nano","gpt-4o", "gpt-4o-mini", "gpt-4", "gpt-4-turbo", "gpt-3.5-turbo", + "llama3", + "qwen2.5", "anthropic-claude", "ollama-llama", "ollama-mistral", @@ -31,10 +32,10 @@ def _supports_custom_temperature(model: str) -> bool: """ - GPT-5 family currently rejects non-default temperature values on chat completions. + Some models may not support custom temperature values. """ name = (model or "").strip().lower() - return not name.startswith("gpt-5") + return True # Most models support custom temperature class LiteLLMGuardrailError(Exception): @@ -184,23 +185,23 @@ def _call_litellm_chat( return response.model_dump() -def _get_embeddings_openai(texts: List[str], api_key: str) -> List[List[float]]: +def _get_embeddings_openai(texts: List[str], api_key: str, model: str = "text-embedding-3-small") -> List[List[float]]: """Get embeddings from OpenAI directly""" client = openai.OpenAI(api_key=api_key) response = client.embeddings.create( - model="text-embedding-ada-002", + model=model, input=texts, ) return [embedding.embedding for embedding in response.data] def _get_embeddings_litellm( - texts: List[str], api_key: str, base_url: str + texts: List[str], api_key: str, base_url: str, model: str = "text-embedding-3-small" ) -> List[List[float]]: """Get embeddings from LiteLLM proxy""" client = openai.OpenAI(api_key=api_key, base_url=f"{base_url.rstrip('/')}/v1") response = client.embeddings.create( - model="text-embedding-ada-002", + model=model, input=texts, ) return [embedding.embedding for embedding in response.data] @@ -284,6 +285,15 @@ def get_embeddings(texts: List[str], config: Optional[AppConfig] = None) -> List raise Exception("Configure LLM API key in Admin → Security") api_key = effective_llm_api_key(cfg) or "" + embeddings_model = getattr(cfg, "embeddings_model", None) + valid_models = get_embedding_models(cfg) + if valid_models: + if not embeddings_model or embeddings_model not in valid_models: + embeddings_model = valid_models[0] + elif not embeddings_model: + raise Exception( + "Unable to determine available embedding models. Check API credentials or LiteLLM connection." + ) litellm_base_url = getattr(cfg, "litellm_base_url", None) or "http://localhost:4000" @@ -293,9 +303,10 @@ def get_embeddings(texts: List[str], config: Optional[AppConfig] = None) -> List texts=texts, api_key=api_key, base_url=litellm_base_url, + model=embeddings_model, ) else: - return _get_embeddings_openai(texts=texts, api_key=api_key) + return _get_embeddings_openai(texts=texts, api_key=api_key, model=embeddings_model) except openai.APIConnectionError as e: if use_litellm: raise Exception( @@ -330,20 +341,134 @@ def _get_models_litellm(api_key: str, base_url: str) -> Optional[List[str]]: return result if result else None +def _is_standard_model(model_id: Optional[str]) -> bool: + if not isinstance(model_id, str) or not model_id.strip(): + return False + normalized = model_id.strip().lower() + if "embed" in normalized or "embedding" in normalized: + return False + return True + + def get_models(config: Optional[AppConfig] = None) -> List[str]: """ Get available models. When using LiteLLM, returns key-specific models from proxy. Falls back to static list when not using LiteLLM, or when LiteLLM fetch fails/returns empty. + + Uses also STATIC_MODELS as a Whitelist filter to avoid of older or non-functional models. """ cfg = config or _get_config() if not cfg: return STATIC_MODELS + use_litellm = getattr(cfg, "use_litellm", False) api_key = effective_llm_api_key(cfg) - if not use_litellm or not api_key: + + if not api_key and not use_litellm: return STATIC_MODELS + + # --- OpenAI direct mode --- + if not use_litellm: + models = _get_models_openai(api_key) + if models: + # filtered_models = [model for model in models if _is_standard_model(model)] + # Filter for standard models AND ensure they are in the STATIC_MODELS whitelist + filtered_models = [ + model for model in models + if _is_standard_model(model) and model in STATIC_MODELS + ] + return filtered_models or STATIC_MODELS + return STATIC_MODELS + + # --- LiteLLM mode --- litellm_base_url = getattr(cfg, "litellm_base_url", None) or "http://localhost:4000" - models = _get_models_litellm(api_key=api_key, base_url=litellm_base_url) + models = _get_models_litellm(api_key=api_key or "", base_url=litellm_base_url) if models: - return models + filtered_models = [model for model in models + if _is_standard_model(model) and model in STATIC_MODELS] + return filtered_models or STATIC_MODELS + return STATIC_MODELS + + + +def _get_models_openai(api_key: str) -> Optional[List[str]]: + """ + Fetch available models directly from OpenAI. + Returns list of model ids on success, None on failure. + Added by Vince 6/9/26 + """ + try: + client = openai.OpenAI(api_key=api_key) + resp = client.models.list() + return [m.id for m in resp.data] if resp.data else None + except Exception: + return None + + +def get_embedding_models(config: Optional[AppConfig] = None) -> List[str]: + """ + Get available embeddings models. + For OpenAI direct: filter chat models to find embedding models. + For LiteLLM: call /v1/models and filter for embeddings. + Falls back to common defaults. + """ + EMBEDDING_DEFAULTS = [ + "text-embedding-3-large", + "text-embedding-3-small", + "text-embedding-ada-002", + "nomic-embed-text", + ] + + cfg = config or _get_config() + if not cfg: + return EMBEDDING_DEFAULTS + + use_litellm = getattr(cfg, "use_litellm", False) + api_key = effective_llm_api_key(cfg) + + # Helper to detect embedding model ids + def _filter_embedding_model(model_id: Optional[str]) -> bool: + if not isinstance(model_id, str) or not model_id.strip(): + return False + normalized = model_id.strip().lower() + return "embed" in normalized or "embedding" in normalized + + # --- OpenAI direct mode --- + if not use_litellm: + if not api_key: + return EMBEDDING_DEFAULTS + try: + client = openai.OpenAI(api_key=api_key) + models = client.models.list() + + # Filter for embedding models AND ensure they are in the EMBEDDING_DEFAULTS whitelist + embedding_models = [m.id for m in models.data + if _filter_embedding_model(m.id) and m.id in EMBEDDING_DEFAULTS] + result = embedding_models if embedding_models else EMBEDDING_DEFAULTS.copy() +# if "nomic-embed-text" not in result: +# result.append("nomic-embed-text") + return result + except Exception: + return EMBEDDING_DEFAULTS + + # --- LiteLLM mode --- + litellm_base_url = getattr(cfg, "litellm_base_url", None) or "http://localhost:4000" + url = f"{litellm_base_url.rstrip('/')}/v1/models" + headers = {"Authorization": f"Bearer {api_key}"} if api_key else {} + try: + with httpx.Client(timeout=LITELLM_MODELS_TIMEOUT) as client: + resp = client.get(url, headers=headers) + resp.raise_for_status() + data = resp.json() + models = data.get("data", []) + embedding_models = [m.get("id") for m in models + if isinstance(m, dict) and _filter_embedding_model(m.get("id") in EMBEDDING_DEFAULTS )] + + result = embedding_models if embedding_models else EMBEDDING_DEFAULTS.copy() +# if "nomic-embed-text" not in result: +# result.append("nomic-embed-text") + return result + except Exception: + return EMBEDDING_DEFAULTS + diff --git a/backend/main.py b/backend/main.py index c36b2b0..505475f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -73,6 +73,16 @@ def _migrate_demo_prompts_preferred_llm(): conn.commit() +def _migrate_app_config_embeddings_model(): + """Add embeddings_model to app_config if missing (existing DBs).""" + with engine.connect() as conn: + r = conn.execute(text("PRAGMA table_info(app_config)")) + columns = [row[1] for row in r.fetchall()] + if "embeddings_model" not in columns: + conn.execute(text("ALTER TABLE app_config ADD COLUMN embeddings_model VARCHAR")) + conn.commit() + + # Migration: add theme to app_config if missing (for UI theming) def _migrate_app_config_theme(): with engine.connect() as conn: @@ -86,6 +96,7 @@ def _migrate_app_config_theme(): _migrate_app_config_litellm() +_migrate_app_config_embeddings_model() def _migrate_app_config_litellm_virtual_key(): @@ -184,14 +195,16 @@ async def update_config(config_update: AppConfigUpdate, db: Session = Depends(ge for field, value in config_update.dict(exclude_unset=True).items(): setattr(config, field, value) - # Auto-pick model when saving LiteLLM virtual key: if current model invalid for key, set to first allowed - use_litellm = getattr(config, "use_litellm", False) - if use_litellm and getattr(config, "litellm_virtual_key", None): + # Auto-pick models only when missing/null (don't override user's explicit selection) + if not config.openai_model: allowed = llm_client.get_models(config) - if allowed and (not config.openai_model or config.openai_model not in allowed): + if allowed: config.openai_model = allowed[0] - elif not use_litellm and config.openai_model not in llm_client.STATIC_MODELS: - config.openai_model = llm_client.STATIC_MODELS[0] + + if not config.embeddings_model: + allowed_embeddings = llm_client.get_embedding_models(config) + if allowed_embeddings: + config.embeddings_model = allowed_embeddings[0] db.commit() db.refresh(config) @@ -1195,3 +1208,13 @@ async def get_available_models(): return {"models": models} except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get models: {str(e)}") from e + + +@app.get("/api/embeddings-models") +async def get_available_embeddings_models(): + """Get available embeddings models""" + try: + models = llm_client.get_embedding_models() + return {"models": models} + except Exception as e: + raise HTTPException(status_code=500, detail=f"Failed to get embeddings models: {str(e)}") from e diff --git a/backend/models.py b/backend/models.py index 0e48ea1..5be166d 100644 --- a/backend/models.py +++ b/backend/models.py @@ -20,6 +20,7 @@ class AppConfig(Base): logo_url = Column(String, nullable=True) system_prompt = Column(Text, nullable=True) openai_model = Column(String, default="gpt-4o-mini") + embeddings_model = Column(String, nullable=True) temperature = Column(String, default="7") lakera_enabled = Column(Boolean, default=True) lakera_blocking_mode = Column(Boolean, default=False) diff --git a/backend/schemas.py b/backend/schemas.py index 504788b..0560a58 100644 --- a/backend/schemas.py +++ b/backend/schemas.py @@ -20,6 +20,7 @@ class AppConfigBase(BaseModel): rag_content_scanning: bool = False rag_lakera_project_id: Optional[str] = None openai_model: str = "gpt-4o-mini" + embeddings_model: Optional[str] = None temperature: int = 7 system_prompt: Optional[str] = None # UI theme: e.g. "blue", "emerald", "purple", "amber" diff --git a/package-lock.json b/package-lock.json index 4050f21..450aa86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "agentic-demo-frontend", - "version": "0.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "agentic-demo-frontend", - "version": "0.0.0", + "version": "1.0.1", "dependencies": { "clsx": "^2.0.0", "lucide-react": "^0.294.0", @@ -869,11 +869,10 @@ } }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -919,11 +918,10 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1089,10 +1087,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", - "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", - "license": "MIT", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", "engines": { "node": ">=14.0.0" } @@ -1105,280 +1102,325 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.49.0.tgz", - "integrity": "sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.49.0.tgz", - "integrity": "sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz", - "integrity": "sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.49.0.tgz", - "integrity": "sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.49.0.tgz", - "integrity": "sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.49.0.tgz", - "integrity": "sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.49.0.tgz", - "integrity": "sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.49.0.tgz", - "integrity": "sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.49.0.tgz", - "integrity": "sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.49.0.tgz", - "integrity": "sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.49.0.tgz", - "integrity": "sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.49.0.tgz", - "integrity": "sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.49.0.tgz", - "integrity": "sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.49.0.tgz", - "integrity": "sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.49.0.tgz", - "integrity": "sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.49.0.tgz", - "integrity": "sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.49.0.tgz", - "integrity": "sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.49.0.tgz", - "integrity": "sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.49.0.tgz", - "integrity": "sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "cpu": [ + "x64" + ], + "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.49.0.tgz", - "integrity": "sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1761,11 +1803,10 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2196,9 +2237,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001737", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", - "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", + "version": "1.0.30001797", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", + "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==", "dev": true, "funding": [ { @@ -2213,8 +2254,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/chalk": { "version": "4.1.2", @@ -2917,11 +2957,10 @@ } }, "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2999,11 +3038,10 @@ } }, "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3207,11 +3245,10 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true }, "node_modules/for-each": { "version": "0.3.5", @@ -3437,11 +3474,10 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4156,11 +4192,10 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -4364,13 +4399,12 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -5048,12 +5082,11 @@ } }, "node_modules/react-router": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", - "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", - "license": "MIT", + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", "dependencies": { - "@remix-run/router": "1.23.0" + "@remix-run/router": "1.23.2" }, "engines": { "node": ">=14.0.0" @@ -5063,13 +5096,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", - "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", - "license": "MIT", + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", "dependencies": { - "@remix-run/router": "1.23.0", - "react-router": "6.30.1" + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" }, "engines": { "node": ">=14.0.0" @@ -5206,11 +5238,10 @@ } }, "node_modules/rollup": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz", - "integrity": "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -5222,26 +5253,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.49.0", - "@rollup/rollup-android-arm64": "4.49.0", - "@rollup/rollup-darwin-arm64": "4.49.0", - "@rollup/rollup-darwin-x64": "4.49.0", - "@rollup/rollup-freebsd-arm64": "4.49.0", - "@rollup/rollup-freebsd-x64": "4.49.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.49.0", - "@rollup/rollup-linux-arm-musleabihf": "4.49.0", - "@rollup/rollup-linux-arm64-gnu": "4.49.0", - "@rollup/rollup-linux-arm64-musl": "4.49.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.49.0", - "@rollup/rollup-linux-ppc64-gnu": "4.49.0", - "@rollup/rollup-linux-riscv64-gnu": "4.49.0", - "@rollup/rollup-linux-riscv64-musl": "4.49.0", - "@rollup/rollup-linux-s390x-gnu": "4.49.0", - "@rollup/rollup-linux-x64-gnu": "4.49.0", - "@rollup/rollup-linux-x64-musl": "4.49.0", - "@rollup/rollup-win32-arm64-msvc": "4.49.0", - "@rollup/rollup-win32-ia32-msvc": "4.49.0", - "@rollup/rollup-win32-x64-msvc": "4.49.0", + "@rollup/rollup-android-arm-eabi": "4.60.0", + "@rollup/rollup-android-arm64": "4.60.0", + "@rollup/rollup-darwin-arm64": "4.60.0", + "@rollup/rollup-darwin-x64": "4.60.0", + "@rollup/rollup-freebsd-arm64": "4.60.0", + "@rollup/rollup-freebsd-x64": "4.60.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", + "@rollup/rollup-linux-arm64-gnu": "4.60.0", + "@rollup/rollup-linux-arm64-musl": "4.60.0", + "@rollup/rollup-linux-loong64-gnu": "4.60.0", + "@rollup/rollup-linux-loong64-musl": "4.60.0", + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", + "@rollup/rollup-linux-ppc64-musl": "4.60.0", + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", + "@rollup/rollup-linux-riscv64-musl": "4.60.0", + "@rollup/rollup-linux-s390x-gnu": "4.60.0", + "@rollup/rollup-linux-x64-gnu": "4.60.0", + "@rollup/rollup-linux-x64-musl": "4.60.0", + "@rollup/rollup-openbsd-x64": "4.60.0", + "@rollup/rollup-openharmony-arm64": "4.60.0", + "@rollup/rollup-win32-arm64-msvc": "4.60.0", + "@rollup/rollup-win32-ia32-msvc": "4.60.0", + "@rollup/rollup-win32-x64-gnu": "4.60.0", + "@rollup/rollup-win32-x64-msvc": "4.60.0", "fsevents": "~2.3.2" } }, @@ -5763,11 +5799,11 @@ } }, "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -6160,11 +6196,10 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/package.json b/package.json index 7e42ada..5468990 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "agentic-demo-frontend", "private": true, - "version": "0.0.0", + "version": "1.0.2", "type": "module", "scripts": { "dev": "vite", @@ -10,13 +10,13 @@ "preview": "vite preview" }, "dependencies": { + "clsx": "^2.0.0", + "lucide-react": "^0.294.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-dropzone": "^14.2.3", "react-router-dom": "^6.20.1", - "lucide-react": "^0.294.0", - "clsx": "^2.0.0", - "tailwind-merge": "^2.0.0", - "react-dropzone": "^14.2.3" + "tailwind-merge": "^2.0.0" }, "devDependencies": { "@types/react": "^18.2.37", diff --git a/requirements.txt b/requirements.txt index c80ed49..badcc6d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,27 @@ -fastapi==0.104.1 -uvicorn[standard]==0.24.0 -python-multipart==0.0.6 -python-jose[cryptography]==3.3.0 -passlib[bcrypt]==1.7.4 -sqlalchemy==2.0.23 -chromadb==0.4.18 -openai==1.3.7 -pydantic==2.5.0 -pydantic-settings==2.1.0 -python-dotenv==1.0.0 -aiofiles==23.2.1 -pypdf2==3.0.1 -markdown==3.5.1 -pandas==2.1.4 -requests==2.31.0 -httpx==0.25.2 +# Web Framework & Server +fastapi>=0.110.0 +uvicorn[standard]>=0.29.0 +python-multipart>=0.0.9 + +# Security & Authentication +PyJWT[crypto]>=2.8.0 +passlib[bcrypt]>=1.7.4 +bcrypt>=4.1.0 + +# Database & Vector Store +sqlalchemy>=2.0.29 +chromadb>=0.5.0 + +# AI & Data Processing +openai>=1.20.0 +pydantic>=2.7.0 +pydantic-settings>=2.2.1 +python-dotenv>=1.0.1 + +# File Handling & Utilities +aiofiles>=23.2.1 +pypdf>=4.2.0 +markdown>=3.6 +pandas>=2.2.0 +requests>=2.31.0 +httpx>=0.27.0 diff --git a/requirements.txt.box b/requirements.txt.box new file mode 100644 index 0000000..c80ed49 --- /dev/null +++ b/requirements.txt.box @@ -0,0 +1,17 @@ +fastapi==0.104.1 +uvicorn[standard]==0.24.0 +python-multipart==0.0.6 +python-jose[cryptography]==3.3.0 +passlib[bcrypt]==1.7.4 +sqlalchemy==2.0.23 +chromadb==0.4.18 +openai==1.3.7 +pydantic==2.5.0 +pydantic-settings==2.1.0 +python-dotenv==1.0.0 +aiofiles==23.2.1 +pypdf2==3.0.1 +markdown==3.5.1 +pandas==2.1.4 +requests==2.31.0 +httpx==0.25.2 diff --git a/src/components/DemoPromptManager.tsx b/src/components/DemoPromptManager.tsx index 2d37d74..cec171d 100644 --- a/src/components/DemoPromptManager.tsx +++ b/src/components/DemoPromptManager.tsx @@ -335,7 +335,7 @@ const PromptForm: React.FC = ({ prompt, onSave, onCancel }) => {availableModels.map((model) => ( ))} diff --git a/src/pages/AdminConsole.tsx b/src/pages/AdminConsole.tsx index e479aa7..b594689 100644 --- a/src/pages/AdminConsole.tsx +++ b/src/pages/AdminConsole.tsx @@ -8,6 +8,7 @@ import ToolManager from '../components/ToolManager'; import GenerateContentModal from '../components/GenerateContentModal'; import RagManagement, { RagManagementRef } from '../components/RagManagement'; import DemoPromptManager from '../components/DemoPromptManager'; +import packageJson from '../../package.json'; type TabType = 'setup' | 'branding' | 'llm' | 'rag' | 'rag-scanning' | 'tools' | 'security' | 'prompts' | 'export'; @@ -24,6 +25,8 @@ const AdminConsole: React.FC = () => { const [showLakeraKey, setShowLakeraKey] = useState(false); const [showMCPInstructions, setShowMCPInstructions] = useState(false); const [availableModels, setAvailableModels] = useState([]); + const [availableEmbeddingModels, setAvailableEmbeddingModels] = useState([]); + const [embeddingModelsLoaded, setEmbeddingModelsLoaded] = useState(false); const [ragScanningNotificationCount, setRagScanningNotificationCount] = useState(0); const [ragScanningProgress, setRagScanningProgress] = useState<{isScanning: boolean; current: number; total: number; filename?: string} | null>(null); const progressPollingRef = useRef(null); @@ -46,6 +49,7 @@ const AdminConsole: React.FC = () => { useEffect(() => { loadConfig(); loadModels(); + loadEmbeddingModels(); loadRagScanningResult(); }, []); @@ -55,6 +59,17 @@ const AdminConsole: React.FC = () => { } }, [config?.theme]); + useEffect(() => { + if ( + config && + embeddingModelsLoaded && + availableEmbeddingModels.length > 0 && + !availableEmbeddingModels.includes(config.embeddings_model || '') + ) { + setConfig({ ...config, embeddings_model: availableEmbeddingModels[0] }); + } + }, [config, embeddingModelsLoaded, availableEmbeddingModels]); + const applyTheme = (theme?: string) => { const themes = ['blue', 'emerald', 'purple', 'amber']; const body = document.body; @@ -73,9 +88,13 @@ const AdminConsole: React.FC = () => { // Reload models when switching to LLM tab (e.g. after saving LiteLLM key in Security) useEffect(() => { if (activeTab === 'llm') { + loadConfig(); // Reload config to get latest saved model loadModels(); + if (!embeddingModelsLoaded) { + loadEmbeddingModels(); + } } - }, [activeTab]); + }, [activeTab, embeddingModelsLoaded]); // Poll for RAG scanning progress useEffect(() => { @@ -222,20 +241,49 @@ const AdminConsole: React.FC = () => { const loadModels = async () => { try { const modelsData = await apiService.getModels(); - setAvailableModels(modelsData.models); + const models = modelsData.models; + setAvailableModels(models); + + // If current model is not available, suggest switching + if (config && config.openai_model && !models.includes(config.openai_model)) { + console.warn(`Current model '${config.openai_model}' not available. Available models:`, models); + } } catch (error) { console.error('Failed to load models:', error); - // Fallback to hardcoded models if API fails + // Fallback to common models that are usually available (remove gpt-5 which often isn't) setAvailableModels([ - "gpt-5", - "gpt-5-mini", - "gpt-5-nano", "gpt-4o", "gpt-4o-mini", "gpt-4", "gpt-4-turbo", - "gpt-3.5-turbo" + "gpt-3.5-turbo", + "llama3.2", + "llama2" + ]); + } + }; + + const loadEmbeddingModels = async (force = false) => { + if (!force && embeddingModelsLoaded) { + return; + } + setEmbeddingModelsLoaded(false); + try { + const modelsData = await apiService.getEmbeddingModels(); + const models = modelsData.models; + setAvailableEmbeddingModels(models); + setEmbeddingModelsLoaded(true); + console.log('Loaded embedding models:', models); + } catch (error) { + console.error('Failed to load embedding models:', error); + // Fallback to common embedding models including Nomic + setAvailableEmbeddingModels([ + "text-embedding-3-large", + "text-embedding-3-small", + "text-embedding-ada-002", + "nomic-embed-text", ]); + setEmbeddingModelsLoaded(true); } }; @@ -268,6 +316,8 @@ const AdminConsole: React.FC = () => { if (configData.rag_content_scanning === undefined) { configData.rag_content_scanning = false; } + console.log('Loaded config:', configData); + console.log('Loaded model:', configData.openai_model); setConfig(configData); } catch (error) { console.error('Failed to load config:', error); @@ -297,6 +347,7 @@ const AdminConsole: React.FC = () => { rag_content_scanning: ragContentScanning, rag_lakera_project_id: updates.rag_lakera_project_id ?? config.rag_lakera_project_id, openai_model: updates.openai_model ?? config.openai_model, + embeddings_model: updates.embeddings_model ?? config.embeddings_model, temperature: updates.temperature ?? config.temperature, system_prompt: updates.system_prompt ?? config.system_prompt, openai_api_key: updates.openai_api_key ?? config.openai_api_key, @@ -312,14 +363,106 @@ const AdminConsole: React.FC = () => { await apiService.updateConfig(updatedConfig); await loadConfig(); - await loadModels(); setMessage({ type: 'success', text: 'Configuration updated successfully' }); } catch (error) { console.error('Failed to update config:', error); setMessage({ type: 'error', text: 'Failed to update configuration' }); + await loadConfig(); // Reload to revert to last saved state on error } }; + const handleModelChange = (newModel: string) => { + console.log('Model changed to:', newModel); + + // Optimistic update - immediately update UI without reloading + const updatedConfig: AppConfigUpdate = { + business_name: config.business_name, + tagline: config.tagline, + hero_text: config.hero_text, + hero_image_url: config.hero_image_url, + logo_url: config.logo_url, + theme: config.theme, + lakera_enabled: config.lakera_enabled, + lakera_blocking_mode: config.lakera_blocking_mode, + rag_content_scanning: config.rag_content_scanning, + rag_lakera_project_id: config.rag_lakera_project_id, + openai_model: newModel, + embeddings_model: config.embeddings_model, + temperature: config.temperature, + system_prompt: config.system_prompt, + openai_api_key: config.openai_api_key, + litellm_virtual_key: config.litellm_virtual_key, + litellm_guardrail_name: config.litellm_guardrail_name, + litellm_guardrail_monitor_name: config.litellm_guardrail_monitor_name, + lakera_api_key: config.lakera_api_key, + lakera_project_id: config.lakera_project_id, + use_litellm: config.use_litellm, + litellm_base_url: config.litellm_base_url, + }; + + console.log('Sending to backend:', updatedConfig); + + // Update UI immediately + setConfig({ ...config, openai_model: newModel }); + + // Save to backend without reloading + apiService.updateConfig(updatedConfig) + .then(() => { + console.log('Model saved successfully'); + setMessage({ type: 'success', text: 'Model updated' }); + }) + .catch((error) => { + console.error('Failed to update model:', error); + setMessage({ type: 'error', text: 'Failed to update model' }); + loadConfig(); // Revert on error + }); + }; + + const handleEmbeddingModelChange = (newModel: string) => { + console.log('Embedding model changed to:', newModel); + + // Optimistic update + const updatedConfig: AppConfigUpdate = { + business_name: config.business_name, + tagline: config.tagline, + hero_text: config.hero_text, + hero_image_url: config.hero_image_url, + logo_url: config.logo_url, + theme: config.theme, + lakera_enabled: config.lakera_enabled, + lakera_blocking_mode: config.lakera_blocking_mode, + rag_content_scanning: config.rag_content_scanning, + rag_lakera_project_id: config.rag_lakera_project_id, + openai_model: config.openai_model, + embeddings_model: newModel, + temperature: config.temperature, + system_prompt: config.system_prompt, + openai_api_key: config.openai_api_key, + litellm_virtual_key: config.litellm_virtual_key, + litellm_guardrail_name: config.litellm_guardrail_name, + litellm_guardrail_monitor_name: config.litellm_guardrail_monitor_name, + lakera_api_key: config.lakera_api_key, + lakera_project_id: config.lakera_project_id, + use_litellm: config.use_litellm, + litellm_base_url: config.litellm_base_url, + }; + + // Update UI immediately + setConfig({ ...config, embeddings_model: newModel }); + + // Save to backend + apiService.updateConfig(updatedConfig) + .then(() => { + console.log('Embedding model saved successfully'); + setMessage({ type: 'success', text: 'Embedding model updated' }); + }) + .catch((error) => { + console.error('Failed to update embedding model:', error); + setMessage({ type: 'error', text: 'Failed to update embedding model' }); + loadConfig(); // Revert on error + }); + }; + const handleExport = async () => { setIsExporting(true); setMessage(null); @@ -417,7 +560,10 @@ const AdminConsole: React.FC = () => { Back to Demo -

Admin Console

+
+

Admin Console

+

v{packageJson.version}

+
@@ -693,11 +839,6 @@ const AdminConsole: React.FC = () => { {activeTab === 'llm' && (

LLM Configuration

- {config.openai_model && availableModels.length > 0 && !availableModels.includes(config.openai_model) && ( -
- Current model may not be available for this key. Select a model below and save. -
- )}
+ {availableModels.length === 0 && ( +

Models are loading. Make sure you've entered a valid API key in Security settings.

+ )} +
+
+ + + {!embeddingModelsLoaded ? ( +

Embeddings models are loading...

+ ) : availableEmbeddingModels.length === 0 ? ( +

No available embedding models. Check your API key or LiteLLM connection.

+ ) : null}