AdaptiveUI_UX / vivek /UpdatedReadme.md
saivivek6's picture
Deploy vivek app to Space: Vue UI (bundled dist), streaming widgets, JSON schema renderer
c97e8a9
|
Raw
History Blame
18.7 kB

Vivek: Adaptive Presentation Engine β€” Complete Reference

A professional, modular backend demonstrating Bayesian strategy selection and real-time posterior updating. This app learns user preferences and adapts its response format (bullet points, prose, questions, etc.) based on observed rewards.


Table of Contents


Project Structure

backend/
β”œβ”€β”€ __init__.py           # Package entrypoint
β”œβ”€β”€ config.py             # Environment + constants
β”œβ”€β”€ utils.py              # Math, heuristics, post-processing
β”œβ”€β”€ llm.py                # LLM backends (Anthropic, OpenAI-compatible)
β”œβ”€β”€ engine.py             # Bayesian learner
└── server.py             # HTTP handlers + runner

Root folder
β”œβ”€β”€ app.py                # Launcher (imports backend.server.run_server)
β”œβ”€β”€ frontend/
β”‚   └── index.html        # Frontend demo UI
β”œβ”€β”€ Dockerfile            # Container image
β”œβ”€β”€ requirements.txt      # Python dependencies
β”œβ”€β”€ README.md             # Original readme (kept for reference)
└── UpdatedReadme.md      # This file

File Manifest

backend/__init__.py

Purpose: Package initialization and public API.

Exposes run_server() so callers only need:

from backend import run_server
run_server()

backend/config.py

Purpose: Centralized configuration and constants.

Key variables:

  • LLM_MODE β€” selects backend: "openai_compat" or "anthropic"
  • OPENAI_BASE_URL, OPENAI_API_KEY, OPENAI_MODEL β€” remote OpenAI-compatible API (Groq)
  • ANTHROPIC_API_KEY, ANTHROPIC_MODEL β€” Anthropic Claude API configuration
  • D=10 β€” feature vector dimensionality
  • LAMBDA, GAMMA, ALPHA_G, TS_TEMPERATURE β€” Bayesian hyperparameters
  • STRATEGIES β€” dict of 5 response primitives (bulleted, narrative, concise, Socratic, step-by-step)
  • HERE, INDEX_HTML β€” paths for serving the frontend

Usage: Other modules import from this single source of truth.


backend/utils.py

Purpose: Small, reusable utilities.

Functions:

  • sigmoid(x) β€” numerically stable logistic function
  • mean_uncertainty(sigma_inv) β€” summarize posterior variance from precision matrix
  • fast_valence(message, prev_response) β€” lightweight regex-based sentiment heuristic; returns {"pos", "neg", "reason"}
  • enforce_response(strategy, text) β€” post-process LLM output to match the chosen format (strips questions, forces bullets/numbers, caps sentence counts)

Helpers (module-level):

  • _POS, _NEG, _REPHRASE β€” regex patterns for auto-reward detection

Usage: Called by server during chat turns and posterior updates.


backend/llm.py

Purpose: LLM API wrappers for Anthropic and OpenAI-compatible endpoints.

Public functions:

  • call_openai_compat(prompt, system, timeout=120) β€” POST to OpenAI-compatible endpoint; returns (text, elapsed_sec, mode)
  • call_anthropic(prompt, system, timeout=120) β€” call Anthropic Messages API; returns (text, elapsed_sec, mode)
  • openai_health(timeout=10) β€” check OpenAI-compatible endpoint and available models
  • anthropic_health() β€” lightweight Anthropic config health summary

Internal helpers:

  • _post_json_url(), _get_json_url() β€” raw HTTP wrappers

Usage: Server calls these during /api/chat to fetch responses from the LLM.


backend/engine.py

Purpose: The core Bayesian learner for strategy selection and posterior updating.

Class: BayesianEngine

  • __init__() β€” initialize global and per-user posterior means (mu) and precision matrices (sigma_inv)
  • get_user(uid) β€” fetch or create user state
  • featurize(message, user) β€” convert message + history into a fixed-length feature vector (10-dim)
  • select(uid, message) β€” use Thompson Sampling to pick a strategy; return (strategy, scores, x)
  • update(uid, strategy, x, reward) β€” Bayesian update for user and global posterior
  • apply_preferences(uid, strategy_names) β€” apply soft bias or hard-lock if user selects one strategy
  • posterior_summary(), user_posterior(), global_posterior() β€” compute compact summaries of expected reward + uncertainty

Singletons:

  • engine β€” global instance used by the server
  • USERB_ID β€” reserved user ID for a secondary reference posterior (demo artifact)

Bayesian model: Online logistic regression with exponential decay (Ξ³=0.99) and per-strategy Gaussian posteriors.

Usage: Server calls during /api/chat, /api/reward, /api/preference endpoints.


backend/server.py

Purpose: HTTP server and request handlers.

Class: Handler(BaseHTTPRequestHandler) Handles:

  • GET / β€” serve index.html frontend
  • GET /api/health β€” return LLM backend status
  • GET /api/state β€” return user's current posterior and global stats
  • POST /api/chat β€” accept user message, auto-reward previous turn, select strategy, call LLM, enforce format, persist state
  • POST /api/reward β€” accept explicit user reward
  • POST /api/preference β€” set user strategy preferences
  • POST /api/reset β€” reset user state
  • OPTIONS * β€” handle CORS preflight

Private helpers:

  • _json() β€” JSON response with CORS headers
  • _html() β€” serve frontend file
  • _body() β€” parse JSON request body
  • _cors() β€” set CORS headers
  • log_message() β€” suppress access logs for cleanliness

Function: run_server()

  • Prints startup banner
  • Performs lightweight health checks on Anthropic/OpenAI endpoint
  • Starts ThreadedServer on port 5051 (configurable via PORT env var)

Class: ThreadedServer(ThreadingMixIn, HTTPServer)

  • Allows concurrent request handling

Usage: Imported and called by app.py.


app.py (Root)

Purpose: Lightweight launcher script.

Simply imports and calls:

from backend.server import run_server

if __name__ == "__main__":
    run_server()

This keeps a familiar entrypoint (python app.py) while the implementation lives in the package.


index.html

Purpose: Frontend React/Preact demo.

Communicates with backend via:

  • GET /api/health β€” check LLM status on load
  • GET /api/state β€” fetch user posteriors
  • POST /api/chat β€” send message, receive response + Bayesian state
  • POST /api/reward β€” send user feedback
  • POST /api/preference β€” set strategy lock
  • POST /api/reset β€” reset session

Displays:

  • Strategy label and instruction
  • Expected reward scores per strategy
  • Feature vector (x ∈ ℝ¹⁰)
  • Posterior bar charts (mean + uncertainty)
  • Auto-detected valence reason

Dockerfile

Purpose: Container build for deployment.

Installs Python, dependencies, and runs python app.py.


requirements.txt

Purpose: Python package dependencies.

Currently:

  • numpy β€” linear algebra for Bayesian updates
  • python-dotenv β€” load .env for API keys

System Architecture

High-Level Component Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Frontend (index.html)                       β”‚
β”‚         Browser UI β†’ POST /api/chat, GET /api/state, etc.          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  app.py (Launcher)                                  β”‚
β”‚              from backend.server import run_server()                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          HTTP Server + Handler (backend/server.py)                    β”‚
β”‚  β”œβ”€ GET /api/health     β†’ check LLM backend                        β”‚
β”‚  β”œβ”€ GET /api/state      β†’ fetch posteriors                         β”‚
β”‚  β”œβ”€ POST /api/chat      β†’ process user message                     β”‚
β”‚  β”œβ”€ POST /api/reward    β†’ apply explicit reward                    β”‚
β”‚  β”œβ”€ POST /api/preferenceβ†’ lock strategy                            β”‚
β”‚  └─ POST /api/reset     β†’ clear user data                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚         β”‚              β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚              └──────────────┐
         β”‚                         β”‚                             β”‚
         β–Ό                         β–Ό                             β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Bayesian     β”‚          β”‚ LLM Backendsβ”‚            β”‚ Utils (Heuristics
  β”‚ Engine       β”‚          β”‚ (backend/llm) β”‚            β”‚ & Post-process)
  β”‚ (backend/      β”‚          β”‚  β”Œβ”€ Anthropic β”‚          β”‚  β”œβ”€ sigmoid()
  β”‚  engine.py)  β”‚          β”‚  └─ OpenAI   β”‚            β”‚  β”œβ”€ fast_valence()
  β”‚              β”‚          β”‚             β”‚            β”‚  └─ enforce_response()
   β”‚ β€’ select()   │◄────────── β€’ call_*()  β”‚            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
   β”‚ β€’ update()   β”‚          β”‚ β€’ health()  β”‚
   β”‚ β€’ featurize()β”‚          β”‚ β€’ format_*()β”‚
   β”‚ β€’ apply_prefβ”‚          β”‚             β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β–²
         β”‚ (reads hyperparams + strategy list)
         β”‚
         β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Config (backend/config.py)                 β”‚
  β”‚ β”œβ”€ LLM_MODE, OPENAI_*, ANTHROPIC_*      β”‚
   β”‚ β”œβ”€ D, LAMBDA, GAMMA, ALPHA_G, TS_TEMP   β”‚
   β”‚ β”œβ”€ STRATEGIES dict + STRATEGY_NAMES      β”‚
   β”‚ └─ HERE, INDEX_HTML paths               β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

How the App Works

1. Initialization

  • User loads http://localhost:5051 β†’ frontend fetches /api/health and /api/state
  • Server initializes or fetches user state (in-memory dict keyed by uid)
  • User inherits global posterior (hierarchical Bayesian prior)

2. User Sends a Message

User message
    ↓
POST /api/chat {uid, message}
    ↓
[Server] fast_valence(message, prev_response)
    β”œβ”€ Auto-reward previous turn (if exists)
    └─ Call engine.update(uid, strategy, x, reward)
    ↓
[Server] engine.select(uid, message)
    β”œβ”€ featurize(message, user) β†’ x ∈ ℝ¹⁰
    β”œβ”€ Thompson Sampling per strategy
    └─ Return best strategy + scores
    ↓
[Server] Build system prompt with FORMAT RULE
    β”œβ”€ Include selected strategy instruction
    β”œβ”€ Add recent conversation history
    └─ Send to LLM
    ↓
[LLM] Generate response (single-call)
    └─ Return text + optional widget HTML
    ↓
[Server] enforce_response(strategy, text)
    β”œβ”€ Strip unwanted questions
    β”œβ”€ Force format (bullets, numbers, etc.)
    └─ Return polished response
    ↓
[Server] Persist conversation + state
    └─ Update user["history"], ["last_response"], ["last_x"]
    ↓
[Server] Return JSON response
    β”œβ”€ response text + strategy label
    β”œβ”€ instruction (what the system told the LLM)
    β”œβ”€ scores (expected reward per strategy)
    β”œβ”€ x_vec (feature vector used)
    β”œβ”€ posteriors (updated beliefs)
    β”œβ”€ auto_detected + auto_r (heuristic reward)
    └─ auto_reason (why the heuristic fired)
    ↓
[Frontend] Display response
    β”œβ”€ Show strategy + instruction
    β”œβ”€ Show bar charts for expected reward
    β”œβ”€ Show feature vector
    └─ Display πŸ‘/πŸ‘Ž buttons

3. User Rates Response

User clicks πŸ‘ or πŸ‘Ž
    ↓
POST /api/reward {uid, strategy, x_vec, reward}
    ↓
[Server] engine.update(uid, strategy, x, reward)
    β”œβ”€ Update user["mu"][strategy] via logistic regression
    β”œβ”€ Update user["sigma_inv"][strategy] (precision matrix)
    β”œβ”€ Also update global posterior (with weight Ξ±=0.05)
    └─ Append to user["reward_log"]
    ↓
[Server] Recompute posteriors
    └─ Return new bar chart data
    ↓
[Frontend] Animate bar chart updates
    └─ Show how beliefs changed

4. Posterior Update (Bayesian Mechanics)

The engine maintains per-user Gaussian posteriors Ξ² ~ N(ΞΌ, Ξ£) for each strategy.

Model: Logistic regression where reward rΜ‚ = sigmoid(x^T Ξ²)

Update rule:

  • rΜ‚_old = sigmoid(x^T ΞΌ_old)
  • Ξ£_new = Ξ£_old^{-1} + x x^T Β· w + Ξ» I (w = rΜ‚(1 - rΜ‚))
  • ΞΌ_new = ΞΌ_old + Ξ£_new^{-1} x (r - rΜ‚_old)

Global posterior gets a small, weighted update (Ξ±=0.05) so all users benefit from collective learning.

Exponential decay (Ξ³=0.99) biases the engine toward recent history.


Setup & Running

1. Install Python 3.8+

python3 --version

2. Clone/download the repo

cd backend

3. Create a virtual environment (recommended)

python3 -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

4. Install dependencies

pip install -r requirements.txt

5. LLM backend

This repository supports OpenAI-compatible providers and Anthropic Claude.

6. Run the app

python app.py

You should see a startup banner and then the server will be live at http://localhost:5051.

7. Open browser

http://localhost:5051

Configuration

Environment Variables

Create a .env file in the root folder:

# Backend selection
LLM_MODE=openai_compat  # or "anthropic"

# OpenAI-compatible (Groq, etc.)
OPENAI_BASE_URL=https://api.groq.com/openai/v1
OPENAI_API_KEY=your_groq_api_key_here
OPENAI_MODEL=llama-3.1-8b-instant

# Anthropic (Claude)
ANTHROPIC_API_KEY=your_anthropic_key_here
ANTHROPIC_MODEL=claude-opus-4-6

# Port
PORT=5051

Bayesian Hyperparameters

Edit backend/config.py:

D = 10              # Feature vector dimension
LAMBDA = 0.01       # L2 regularization on posteriors
GAMMA = 0.99        # Exponential decay (prefer recent history)
ALPHA_G = 0.05      # Global posterior update weight
TS_TEMPERATURE = 2.0  # Thompson Sampling variance scale (exploration)

API Reference

GET /api/health

Returns: LLM backend status.

Response:

{
  "server": "ok",
  "mode": "openai_compat",
  "openai_base_url": "https://api.groq.com/openai/v1",
  "model": "llama-3.1-8b-instant",
  "ok": true,
  "reachable": true,
  "models": ["llama-3.1-8b-instant", "mixtral-8x7b-32768"]
}

GET /api/state?uid=demo

Returns: Current user state and posteriors.

Response:

{
  "posterior": {
    "structured_bullets": {"r": 0.52, "u": 0.34},
    "narrative_prose": {"r": 0.48, "u": 0.35},
    ...
  },
  "global": {...},
  "userb": {...},
  "global_n": 42,
  "n_users": 3,
  "msg_count": 5
}

POST /api/chat

Body:

{
  "uid": "demo",
  "message": "How do I make pasta?"
}

Response:

{
  "response": "- Cook 1 liter of water\n- Add salt\n- ...",
  "strategy": "step_by_step",
  "instruction": "Numbered list of 3-6 steps only.",
  "elapsed": 2.3,
  "llm_mode": "anthropic",
  "scores": {
    "structured_bullets": 0.54,
    "narrative_prose": 0.48,
    ...
  },
  "x_vec": [0.12, 0.34, ...],
  "posterior": {...},
  "global": {...},
  "auto_detected": true,
  "auto_r": 0.75,
  "auto_reason": "positive signal(s)"
}

POST /api/reward

Body:

{
  "uid": "demo",
  "strategy": "step_by_step",
  "x_vec": [0.12, 0.34, ...],
  "reward": 0.9
}

Response:

{
  "posterior": {...},
  "global": {...},
  "global_n": 43
}

POST /api/preference

Body:

{
  "uid": "demo",
  "strategies": ["structured_bullets"]
}

Response:

{
  "posterior": {...}
}

POST /api/reset

Body:

{
  "uid": "demo"
}

Response:

{
  "ok": true
}

Troubleshooting

Server won't start

  • Ensure port 5051 is available: lsof -i :5051
  • Check Python version: python --version (need 3.8+)
  • Verify dependencies: pip install -r requirements.txt

LLM returns empty responses

  • If using OpenAI-compatible: verify API key in .env
  • If using Anthropic: verify ANTHROPIC_API_KEY and ANTHROPIC_MODEL
  • Check model name matches OPENAI_MODEL / ANTHROPIC_MODEL

Feature vector or posterior looks weird

  • This is expected! Bayesian posteriors start with high uncertainty.
  • Send a few more messages and rate them β€” posteriors will stabilize.

Valence heuristic seems wrong

  • The regex patterns in utils.py are intentionally simple.
  • For production, replace with a small classifier model.

License & Attribution

Built as a demonstration of hierarchical Bayesian architecture. Feel free to adapt for your use case.

For questions or contributions, reach out to the team.