Skip to content

API Service

ai-engine-api is a FastAPI app (Python 3.12+) that wraps the ai-engine library and serves the demo frontend. It is the single network boundary of the system.

flowchart TB
    subgraph client["Clients"]
        web["Demo frontend<br/>(Leaflet map + quiz)"]
        rsk["RudderStack webhook"]
    end

    subgraph api["ai-engine-api (FastAPI)"]
        mw["log_requests middleware<br/>+ CORS (allow all)"]
        static["/demo/* static pages"]
        search["/api/search*"]
        narr["/api/narrative"]
        dbg["/debug/*"]
        dbw["/db/*"]
        rsr["/api/* (conditional)"]
    end

    subgraph lib["ai-engine library"]
        gs["GlobalSearch"]
        ng["NarrativeGenerator"]
        pbi["ProjectionBuilder"]
        dbi["DB_Interface"]
        comps["recsys components"]
    end

    qd[("Qdrant")]
    pg[("PostgreSQL")]
    llm["Ollama / OpenRouter"]

    web --> static & search & narr
    rsk --> rsr
    search --> gs --> qd
    narr --> ng --> llm
    dbg --> pbi --> pg
    dbw --> dbi --> pg
    rsr --> comps

    classDef store fill:#EFEAE0,stroke:#A8895B,color:#423D34;
    class qd,pg store;

Route groups

Prefix Purpose Backed by
GET /, /demo/* Health + static demo pages FileResponse
GET /api/search* Semantic / geo / preference / profile search GlobalSearch, ProjectionBuilder
POST /api/narrative LLM narrative from items NarrativeGenerator
GET /debug/* User & item introspection ProjectionBuilder, DB_Interface, GlobalSearch
POST /db/* Register users / log events DB_Interface
/api/* Ingest webhook + recommend (when module present) recsys.api.make_router

Full endpoint detail with params, schemas, and a try-it console: the interactive REST API reference (Scalar, from the OpenAPI spec).

Cross-cutting

  • Middleware log_requests logs duration (ms), status, client IP, user-agent (Loguru).
  • CORS is wide open (allow_origins=["*"]): demo posture, tighten for prod.
  • searcher: GlobalSearch is a module-level singleton built with COLLECTION_NAME.
  • The recsys router is mounted only if ai_engine.recsys imports successfully and build_components() succeeds, graceful degradation when Redis/Qdrant are absent.

Persistence

PostgreSQL schema (schema.sql):

erDiagram
    visitor ||--o{ device : has
    visitor ||--o{ session : has
    device  ||--o{ session : on
    visitor ||--o{ visitor_event : logs

    visitor {
        bigint id PK
        text license_plate UK
        text email UK
        int age
        text gender
        text nationality
        bool personal_connection
        text payload
    }
    device {
        uuid id PK
        bigint user_id FK
        text device_id_token UK
        inet last_ip
    }
    session {
        uuid id PK
        bigint user_id FK
        uuid device_id FK
        text session_token UK
        timestamptz expires_at
        bool is_revoked
    }
    visitor_event {
        bigint id PK
        bigint user_id FK
        uuid session_id
        text item_id
        text event_type
        jsonb event_payload
        timestamptz ts
    }

DB_Interface.fetch_events pairs start/end rows into dwell_seconds; user registration auto-generates a unique license_plate (XX-XXXX, retry on collision).

Frontend (static demo)

Page Route Role
intent.html /demo Landing: search bar + mode cards (Explore / Guided learning / Story builder)
index.html /demo/app Explore: Leaflet map, geo radius, text search, 2D topic scatter, item detail modal
newQuiz.html /demo/newQuiz Step-by-step guided learning quiz
newQuizResults.html /demo/newQuiz/myResults Quiz results summary

app.js drives the Explore page: Leaflet + Nominatim location autocomplete (250 ms debounce), text search → /api/search, geo search → /api/search/geo, results cache for narrative generation, item detail overlay. ACTIVE_USER_ID defaults to 1110 (override via ?user_id=).

Deployment

docker-compose.yml ships the API plus a Jitsu analytics stack (Jitsu + Postgres + Redis + ClickHouse). API container exposes :8000. Runtime deps are lean, fastapi, uvicorn, and the ai-engine git dependency, all heavy ML deps come transitively from the library.