From f1e65cc6fee97cdb9e0adfd836e288ddf7f42439 Mon Sep 17 00:00:00 2001 From: Cayman Roden Date: Tue, 19 May 2026 19:04:54 -0700 Subject: [PATCH 1/2] fix(readme): remove broken live-demo claims, point to self-host Both live-demo URLs return 404: - https://chatbot-widget-dashboard.vercel.app/ (Vercel: 404 on / and /login, post cold-start wait) - https://chatbot-widget-api.onrender.com/ (Render: 404 on /, /docs, /health) Removed: top "Dashboard-Live" badge linking to the dead Vercel URL, plus the two bullets in the Live Demo section claiming working hosted URLs. Renamed section to "Demo (Self-Host)" and kept the three real screenshots (real captures of when the app was running, still reproducible via docker-compose). WIP modifications in working tree (api/, dashboard/, widget/) unaffected; this change is README-only on a branch from origin/main. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ae64b3d..aa68a5b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ ![Next.js](https://img.shields.io/badge/Next.js-16.2-000000) ![Claude](https://img.shields.io/badge/Claude-Sonnet-blueviolet) ![CI](https://github.com/ChunkyTortoise/chatbot-widget/actions/workflows/ci.yml/badge.svg) -[![Dashboard](https://img.shields.io/badge/Dashboard-Live-brightgreen)](https://chatbot-widget-dashboard.vercel.app) # Chatbot Widget SaaS @@ -203,10 +202,9 @@ The dashboard includes a [live playground](/dashboard/app/playground/page.tsx) f *Required for production. API runs without these in development mode. -## Live Demo +## Demo (Self-Host) -- **Dashboard**: [chatbot-widget-dashboard.vercel.app](https://chatbot-widget-dashboard.vercel.app) — "Try Demo" button on the login page (no account needed) -- **Widget demo page**: `GET /demo` — portfolio-quality showcase with live widget, copy-paste embed snippet, and cold-start indicator +Screenshots below are from a local self-hosted run. See [Self-Hosting](#self-hosting) for full setup. ### Dashboard Login (with demo button) ![Dashboard Login](docs/screenshots/dashboard-login.png) @@ -214,7 +212,7 @@ The dashboard includes a [live playground](/dashboard/app/playground/page.tsx) f ### Demo Page ![Demo Page](docs/screenshots/demo-page.png) -Run locally: +Run the widget demo locally: ```bash DEMO_MODE=true uvicorn api.main:app --reload # then open http://localhost:8000/demo From a802d830bcdadef16b0bf2545fa3986a44fa2c5d Mon Sep 17 00:00:00 2001 From: Cayman Roden Date: Tue, 19 May 2026 19:20:14 -0700 Subject: [PATCH 2/2] fix(ci): green inherited-red CI (lint fixes) origin/main CI has been failing since 2026-04-17 (Lint step, ruff). This commit fixes the failures without touching the user's pre-existing WIP modifications in the working tree. Zero file overlap verified. Fixes: - ruff --fix auto-fixed 18 of 37 errors (unused imports, etc.) - F841 in tests/test_chat.py: dropped unused `response = ` assignment - F821 in api/models/*: SQLAlchemy Mapped[] forward refs handled via --per-file-ignores in ci.yml. Idiomatic SQLAlchemy 2.0 pattern that matches the existing `# type: ignore[name-defined]` comments and resolves at mapper-config time, not at annotation-eval time. - E712 in api/: SQLAlchemy column equality (`is_active == True`); added to ruff --ignore in ci.yml since these are intentional SQL filter expressions, not Python truthy comparisons. - `from __future__ import annotations` added to 6 model files (defensive: lazy annotation evaluation, SQLAlchemy 2.0 compatible). Side effect: PostToolUse hook YAML formatter normalized ci.yml's block-scalar formatting (multi-line --health-* options collapsed to single lines). Functionally equivalent. WIP isolation: this commit was prepared with the user's WIP stashed (`wip backup before CI lint fix - 2026-05-19 night`). The 19 files touched here do NOT include test_chatbots.py, test_escalation.py, test_leads.py, test_webhooks.py, leads.py, webhooks.py, etc. The WIP will be restored after CI verifies green. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 14 +++----------- api/models/chatbot.py | 2 ++ api/models/conversation.py | 2 ++ api/models/document_chunk.py | 4 +++- api/models/knowledge_doc.py | 2 ++ api/models/message.py | 2 ++ api/models/user.py | 3 ++- api/routes/analytics.py | 2 +- api/routes/chatbots.py | 2 +- tests/test_analytics.py | 2 +- tests/test_auth.py | 3 +-- tests/test_billing.py | 1 - tests/test_chat.py | 2 +- tests/test_chatbot_quota.py | 2 +- tests/test_demo.py | 3 +-- tests/test_documents.py | 2 +- tests/test_health.py | 1 - tests/test_kb_quota.py | 2 +- tests/test_websocket.py | 2 +- 19 files changed, 26 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba60a27..6a312cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,5 @@ name: CI - on: [push, pull_request] - jobs: test: runs-on: ubuntu-latest @@ -15,19 +13,13 @@ jobs: ports: - 5432:5432 options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 + --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 redis: image: redis:7 ports: - 6379:6379 options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 + --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -37,7 +29,7 @@ jobs: - run: pip install -r requirements.txt - run: pip install ruff pytest pytest-asyncio pytest-cov - name: Lint - run: ruff check api/ tests/ --ignore E501 + run: ruff check api/ tests/ --ignore E501,E712 --per-file-ignores "api/models/*:F821" - name: Type check run: pip install mypy types-redis && mypy api/ --ignore-missing-imports - name: Test diff --git a/api/models/chatbot.py b/api/models/chatbot.py index 6e1d576..b481d64 100644 --- a/api/models/chatbot.py +++ b/api/models/chatbot.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import uuid from sqlalchemy import String, Text, Boolean, ForeignKey from sqlalchemy.orm import Mapped, mapped_column, relationship diff --git a/api/models/conversation.py b/api/models/conversation.py index fe22bfa..b79566b 100644 --- a/api/models/conversation.py +++ b/api/models/conversation.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import uuid from sqlalchemy import String, Integer, ForeignKey from sqlalchemy.orm import Mapped, mapped_column, relationship diff --git a/api/models/document_chunk.py b/api/models/document_chunk.py index f7a342f..e6a3f09 100644 --- a/api/models/document_chunk.py +++ b/api/models/document_chunk.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import uuid -from sqlalchemy import String, Text, Integer, ForeignKey, Index +from sqlalchemy import Text, Integer, ForeignKey, Index from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.dialects.postgresql import UUID from pgvector.sqlalchemy import Vector diff --git a/api/models/knowledge_doc.py b/api/models/knowledge_doc.py index 2d1f0c2..78c7d73 100644 --- a/api/models/knowledge_doc.py +++ b/api/models/knowledge_doc.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import uuid from sqlalchemy import String, Text, Integer, ForeignKey from sqlalchemy.orm import Mapped, mapped_column, relationship diff --git a/api/models/message.py b/api/models/message.py index c51ce7b..4209a3e 100644 --- a/api/models/message.py +++ b/api/models/message.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import uuid from sqlalchemy import String, Text, ForeignKey from sqlalchemy.orm import Mapped, mapped_column, relationship diff --git a/api/models/user.py b/api/models/user.py index cfece3a..cb13cca 100644 --- a/api/models/user.py +++ b/api/models/user.py @@ -1,4 +1,5 @@ -import uuid +from __future__ import annotations + from sqlalchemy import String from sqlalchemy.orm import Mapped, mapped_column, relationship from .base import Base, UUIDMixin, TimestampMixin diff --git a/api/routes/analytics.py b/api/routes/analytics.py index 2fc5a6a..1902e93 100644 --- a/api/routes/analytics.py +++ b/api/routes/analytics.py @@ -5,7 +5,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, func, cast, Date, text +from sqlalchemy import select, func, text from api.dependencies import get_db from api.auth.middleware import get_current_user, CurrentUser from api.models.chatbot import Chatbot diff --git a/api/routes/chatbots.py b/api/routes/chatbots.py index 37905d8..0877b6e 100644 --- a/api/routes/chatbots.py +++ b/api/routes/chatbots.py @@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, update from api.dependencies import get_db, get_admin_key, get_admin_or_owner -from api.auth.middleware import get_optional_user, get_current_user, CurrentUser +from api.auth.middleware import get_current_user, CurrentUser from api.models.chatbot import Chatbot from api.schemas.chatbot import ( ChatbotCreate, diff --git a/tests/test_analytics.py b/tests/test_analytics.py index 118100f..e17dcf9 100644 --- a/tests/test_analytics.py +++ b/tests/test_analytics.py @@ -1,6 +1,6 @@ import pytest import uuid -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock from datetime import datetime, timezone, timedelta from jose import jwt from httpx import AsyncClient, ASGITransport diff --git a/tests/test_auth.py b/tests/test_auth.py index b5f4b4c..c341403 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -1,6 +1,5 @@ import pytest -import uuid -from unittest.mock import patch, AsyncMock +from unittest.mock import AsyncMock from datetime import datetime, timezone, timedelta from jose import jwt from httpx import AsyncClient, ASGITransport diff --git a/tests/test_billing.py b/tests/test_billing.py index 36b8a4b..d79b536 100644 --- a/tests/test_billing.py +++ b/tests/test_billing.py @@ -6,7 +6,6 @@ from api.main import app from api.dependencies import get_db, get_redis from api.billing.quota import ( - get_message_count, increment_message_count, check_quota, get_plan_limit, diff --git a/tests/test_chat.py b/tests/test_chat.py index 642a88e..13c268f 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -223,7 +223,7 @@ async def fake_redis_get(key): mock_redis.expire = AsyncMock() with patch("api.services.chat_service.similarity_search", return_value=[]): - response = await client.post( + await client.post( f"/api/v1/chat/{chatbot_id}", json={"message": "Hello", "session_id": "test-session"}, ) diff --git a/tests/test_chatbot_quota.py b/tests/test_chatbot_quota.py index 4cb77dd..b3a0b15 100644 --- a/tests/test_chatbot_quota.py +++ b/tests/test_chatbot_quota.py @@ -1,6 +1,6 @@ import pytest import uuid -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock from datetime import datetime, timezone, timedelta from jose import jwt from httpx import AsyncClient, ASGITransport diff --git a/tests/test_demo.py b/tests/test_demo.py index 048aa9e..182b42a 100644 --- a/tests/test_demo.py +++ b/tests/test_demo.py @@ -2,7 +2,6 @@ import pytest from pathlib import Path from httpx import AsyncClient -from api.main import app class TestDemoUI: @@ -104,7 +103,7 @@ async def test_chat_with_demo_id_in_demo_mode( ) -> None: """POST /chat/demo should work when DEMO_MODE=true.""" response = await demo_client.post( - f"/api/v1/chat/demo", + "/api/v1/chat/demo", json={"message": "Hello", "session_id": "test-session"}, ) # Note: This may fail if no LLM API key is configured, but the routing should work diff --git a/tests/test_documents.py b/tests/test_documents.py index 75812cb..305cbf4 100644 --- a/tests/test_documents.py +++ b/tests/test_documents.py @@ -1,7 +1,7 @@ import pytest import uuid from io import BytesIO -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import MagicMock from datetime import datetime, timezone, timedelta from jose import jwt diff --git a/tests/test_health.py b/tests/test_health.py index 1dafa1c..7b54293 100644 --- a/tests/test_health.py +++ b/tests/test_health.py @@ -3,7 +3,6 @@ @pytest.mark.asyncio async def test_health_endpoint(client, mock_db, mock_redis): - from sqlalchemy import text mock_db.execute = pytest.importorskip # will be called mock_db.execute = __import__("unittest.mock", fromlist=["AsyncMock"]).AsyncMock() diff --git a/tests/test_kb_quota.py b/tests/test_kb_quota.py index f45e7fb..cc5d5b3 100644 --- a/tests/test_kb_quota.py +++ b/tests/test_kb_quota.py @@ -1,7 +1,7 @@ import pytest import uuid from unittest.mock import AsyncMock, MagicMock -from api.billing.kb_quota import check_knowledge_base_quota, KB_LIMITS, KBQuotaResult +from api.billing.kb_quota import check_knowledge_base_quota, KB_LIMITS @pytest.fixture diff --git a/tests/test_websocket.py b/tests/test_websocket.py index ef4c0ef..a25db39 100644 --- a/tests/test_websocket.py +++ b/tests/test_websocket.py @@ -4,7 +4,7 @@ import pytest from unittest.mock import AsyncMock, MagicMock, patch from starlette.testclient import TestClient -from fastapi import FastAPI, WebSocket, Depends +from fastapi import FastAPI from api.routes.chat import router from api.dependencies import get_db, get_redis