Skip to content

Repository files navigation

Primzel API Assistant

A RAG-powered chatbot for OpenAPI / Swagger documentation. Ingest any API spec by URL, then ask natural-language questions about it. Supports multiple indexes, persistent chat history, and an embeddable widget for third-party sites.


Architecture

┌─────────────────────┐      ┌──────────────────────┐      ┌──────────────┐
│   React Frontend    │ ───► │  FastAPI (api.py)     │ ───► │ Elasticsearch│
│   Vite dev server   │      │  port 8000            │      │  port 9200   │
│   port 5173         │      │                       │      └──────────────┘
└─────────────────────┘      │  SQLite (chat_history │
                             │  .db)                 │      ┌──────────────┐
                             │                       │ ───► │  OpenAI API  │
                             └──────────────────────┘      │  gpt-4.1-mini│
                                                           └──────────────┘

Ingest flow: URL → fetch spec → parse YAML/JSON → extract endpoints → embed + store in Elasticsearch index (dense vector field)

Query flow: user message → embed query → Elasticsearch kNN vector search (top-k hits) → inject context into LLM prompt → stream answer


Prerequisites

Requirement Version
Python 3.11+
Node.js 18+
Elasticsearch 8.10.x
OpenAI API key

Start Elasticsearch

# Docker Compose (recommended)
docker compose up -d elasticsearch

# Or run directly
docker run -d --name elasticsearch \
  -p 9200:9200 \
  -e discovery.type=single-node \
  -e xpack.security.enabled=false \
  -e xpack.security.http.ssl.enabled=false \
  docker.elastic.co/elasticsearch/elasticsearch:8.10.2

Setup

Backend

# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate      # Windows: .venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

# Set required environment variables
export OPENAI_API_KEY=sk-...
export ES_URL=http://localhost:9200           # default
export SQLITE_PATH=chat_history.db           # default
export CORS_ORIGINS=http://localhost:5173     # comma-separated allow-list

# Start the API server
uvicorn api:app --reload --port 8000

Frontend

cd frontend
npm install
npm run dev        # starts at http://localhost:5173

Open http://localhost:5173 in a browser.


Usage

1 — Ingest an API spec

Go to the Ingest tab, paste a Swagger / OpenAPI URL (YAML or JSON), and click Ingest. Each spec gets its own Elasticsearch index. Ingestion runs in the background; the status bar and index table update automatically.

2 — Chat

Select an index from the dropdown in the Chat tab and ask questions in plain English. Answers include code examples, endpoint paths, request/response shapes, and HTTP methods drawn from the real spec.

Conversations are persisted in SQLite (chat_history.db) and listed in the sidebar. Click any past conversation to resume it; hover to reveal the delete button.


Embeddable Widget

Drop a single <script> tag onto any web page to add a floating chat button:

<script
  src="https://your-app.com/embed.js"
  data-api-url="https://your-app.com"
  data-index="my-api"
  data-primary-color="#6B21E8"
  data-position="bottom-right">
</script>
Attribute Default Description
data-api-url current origin Base URL of the deployed Primzel app
data-index Pre-select an Elasticsearch index; omit to show a picker inside the widget
data-primary-color #6B21E8 Accent colour for the button and bubbles
data-position bottom-right bottom-right or bottom-left

The widget renders in a sandboxed <iframe> (widget.html) so its styles never conflict with the host page. The iframe loads lazily — not until the user first opens it.

Local development: use data-api-url="http://localhost:5173" so the iframe loads through the Vite dev server (which proxies API calls to FastAPI).


API Reference

All endpoints are served by FastAPI. Interactive docs at http://localhost:8000/docs.

Ingest

Method Path Description
POST /ingest Start background ingestion of an OpenAPI spec URL
GET /ingest/status/{index_name} Poll ingestion status
GET /indexes List all Elasticsearch indexes with status and doc count

POST /ingest body

{ "url": "https://example.com/api.yaml", "index_name": "my-api" }

Chat

Method Path Description
POST /chat Send a message; creates a session if session_id is omitted
GET /chat/sessions List all persisted sessions
GET /chat/{session_id} Retrieve full history for a session
DELETE /chat/{session_id} Delete a session and its messages

POST /chat body

{
  "message": "How do I create a purchase order?",
  "index_name": "my-api",
  "session_id": "optional-uuid",
  "k": 3
}

One-shot query (no session)

Method Path Description
POST /query Single-turn RAG query, no session tracking

Project Structure

├── api.py                    # FastAPI app — ingest, chat, query endpoints
├── api_swagger_to_es.py      # OpenAPI → Elasticsearch ingestion helpers
├── db.py                     # SQLite layer for chat session persistence
├── requirements.txt
└── frontend/
    ├── index.html            # Main app entry
    ├── widget.html           # Embeddable widget entry
    ├── public/
    │   └── embed.js          # Widget loader script (vanilla JS, no deps)
    ├── vite.config.js
    └── src/
        ├── main.jsx          # Main app mount
        ├── App.jsx           # Main app — Chat + Ingest tabs
        ├── App.css
        ├── CompanyLogo.jsx    # SVG logo component
        ├── widget-main.jsx   # Widget app mount
        ├── WidgetApp.jsx     # Embeddable chat widget
        └── WidgetApp.css

Environment Variables

Variable Default Description
OPENAI_API_KEY Required. OpenAI secret key
ES_URL http://localhost:9200 Elasticsearch base URL
SQLITE_PATH chat_history.db Path to the SQLite database file
CORS_ORIGINS http://localhost:5173,http://localhost:3000 Comma-separated list of allowed origins

Production Build

cd frontend
npm run build      # outputs to frontend/dist/

Serve frontend/dist/ as static files behind a reverse proxy (nginx, Caddy, etc.) and proxy /chat, /query, /ingest, /indexes, /health to the FastAPI process.

Minimal nginx snippet:

server {
    listen 443 ssl;
    server_name your-app.com;

    root /path/to/frontend/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location ~ ^/(chat|query|ingest|indexes|health) {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
    }
}

Set CORS_ORIGINS to the list of domains that are allowed to embed the widget.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages