Bright Wrapper LLM Integration Guide ===================================== Bright Wrapper provides JavaScript components and backend APIs for integrating LLM chat into your application. MODEL SELECTION --------------- Bright Wrapper manages model selection internally. You do NOT need to specify which model to use - Bright Wrapper automatically uses the best available model for each endpoint type. To see current models: GET https://brightwrapper.com/api/model Returns: {"text_model": "...", "image_model": "...", "note": "..."} QUICK START ----------- 1. Add a container div and load the component script:
2. Create backend proxy endpoints that add your Bright Wrapper API key server-side and forward requests to https://brightwrapper.com BACKEND PROXY SETUP ------------------- Your backend must proxy these endpoints: /api/brightwrapper/chat/stream → POST https://brightwrapper.com/api/chat/stream /api/brightwrapper/estimate → POST https://brightwrapper.com/api/estimate /api/brightwrapper/version → GET https://brightwrapper.com/api/version Each proxy endpoint: - Receives request from frontend component - Adds header: X-API-Key: - Forwards to Bright Wrapper - Streams response back to frontend This keeps your API key secure on the server. COMPONENT OPTIONS ----------------- new LLMChatComponent({ containerId: 'chat-container', // Required: DOM element ID projectName: 'my_app', // Required: identifier for logging apiUrl: '...', // Required: your proxy endpoint for chat estimateUrl: '...', // Required: your proxy endpoint for estimates versionUrl: '...', // Required: your proxy endpoint for version temperature: 0.7, // Optional: 0.0-1.0 systemPrompt: '...', // Optional: system instructions initialMessage: '...', // Optional: greeting message headers: {} // Optional: extra headers for requests }); Note: Model selection is handled by Bright Wrapper - do not specify a model. API REFERENCE ------------- POST /api/chat/stream Streaming chat endpoint. Model is chosen by Bright Wrapper. Request body: { "message": "user message", "project_name": "my_app", "system_prompt": "optional instructions", // optional "temperature": 0.7 // optional } Headers: X-API-Key: Response: Server-Sent Events stream data: {"chunk": "text"} data: {"done": true} data: {"error": "message"} // on error POST /api/estimate Get estimated response time based on historical data. Request body: { "prompt": "user message", "project_name": "my_app", // optional: scope estimate to project "endpoint_version": "1" // optional: version for fresh timing data } Response: { "estimated_seconds": 5.2, "has_version_data": true // true if estimate uses this version's data } GET /api/version Get service version. Response: {"version": "1.0.0"} GET /api/model Get current model info and tradeoff options. No authentication required. Response: { "default_model": "gemini-2.5-pro-preview-06-05", "image_model": "gemini-2.0-flash-preview-image-generation", "api_version": "1.0.0", "tradeoff_selection": { "description": "Specify speed and quality (0-1 each) to influence model selection", "examples": { "balanced": {"speed": 0.5, "quality": 0.5, "selects": "..."}, "fast": {"speed": 0.8, "quality": 0.2, "selects": "..."}, "quality": {"speed": 0.2, "quality": 0.8, "selects": "..."} } }, "available_models": [...] } POST /api/structured_llm_call Structured output with JSON schema. Model is chosen by Bright Wrapper. Request body: { "prompt": "your prompt", "project_name": "my_app", "text_format_schema": { "properties": { "field_name": {"type": "string"} }, "required": ["field_name"] }, "should_log": true, // optional, default true "max_completion_tokens": 4096, // optional "speed": 0.5, // optional, 0-1 (higher = faster model) "quality": 0.5, // optional, 0-1 (higher = better model) "endpoint_version": "1" // optional, for timing estimates } Headers: X-API-Key: Response: { "model_used": "gemini-2.5-pro-preview-06-05", "duration_seconds": 1.234, "result": {...} // JSON matching your schema } POST /api/structured_llm_call/async Async version - returns immediately with task_id. Model is chosen by Bright Wrapper. Request body: Same as /api/structured_llm_call Response (202): {"task_id": "...", "status": "pending", "poll_url": "/api/task/..."} GET /api/task/{task_id} Poll async task status. Response: Always 200 (or 404 if task not found) { "task_id": "...", "status": "pending|running|completed|failed", "result": {...}, // when status="completed" "error": "...", // when status="failed" "logs": ["[HH:MM:SS] message", ...], // timestamped log messages "progress": 0.5 // 0.0-1.0 scale (not percentage) } LLM PROGRESS COMPONENT ---------------------- Bright Wrapper provides a reusable progress bar component for long-running LLM operations. Include it in your page and use it to show progress during any async LLM call. Include the component: Basic usage:
Constructor options: new LLMProgress({ container: element, // Required: DOM element to append progress bar to estimateUrl: '...', // URL to fetch time estimates (default: brightwrapper.com) projectName: 'my_app', // Project name for estimate lookup theme: 'light', // 'light' or 'dark' theme showDelay: 300, // ms delay before showing (avoids flash for fast responses) headers: {} // Extra headers for API calls }); Methods: await progress.start(title, options) Start showing progress. Fetches estimate and begins animation. - title: Text to display (e.g., "Generating response...") - options.prompt: Prompt text for better estimate - options.estimatedTime: Override estimate (seconds) progress.complete(message) Fill bar to 100% and show completion message. - message: Optional custom completion text progress.hide() Hide the progress bar immediately. progress.destroy() Remove the progress bar from DOM. progress.updateLogs(logs) Update the log display with array of log messages. - logs: Array of strings (e.g., ["[12:00:01] Starting...", "[12:00:02] Done"]) progress.showRetry(attemptNumber) Show retry warning when API retries occur. await progress.pollForTask(taskId, taskUrl, options) Poll for async task completion. Updates progress bar automatically. - taskId: The task ID from async endpoint - taskUrl: Base URL (e.g., '/api/task') - options.maxAttempts: Max poll attempts (default: 300 = 5 min) - options.pollInterval: Ms between polls (default: 1000) Returns: Completed task result Throws: Error on timeout or task failure Features: - 300ms delay before showing (avoids flash for cached/fast responses) - Fetches time estimate from Bright Wrapper - Shows "X seconds remaining" countdown - Caps at 95% until complete (never shows false 100%) - Auto-scrolling log display - Retry detection and display - Light and dark theme support SMART PROGRESS BARS (Manual Implementation) ------------------------------------------- For long-running async tasks, implement a progress bar UI that polls /api/task/{task_id}: 1. INITIATE: POST to async endpoint, get task_id (HTTP 202) 2. POLL: GET /api/task/{task_id} every 300ms 3. UPDATE: Set progress bar width from `progress` field (0.0-1.0 scale, multiply by 100 for %) 4. LOGS: Display `logs` array in scrollable container (newest at bottom) 5. COMPLETE: Stop polling when status="completed" or status="failed" Log Format: Logs are timestamped with emoji indicators: [14:32:01] 🔍 Starting task... [14:32:02] 📊 Processing data... [14:32:03] ✅ Complete! Example Progress Bar CSS: .progress-bar-wrapper { background: #333; border-radius: 8px; height: 24px; overflow: hidden; position: relative; } .progress-bar { height: 100%; background: linear-gradient(90deg, #00d4ff, #4ade80); transition: width 0.3s; } .progress-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; } .progress-logs { max-height: 150px; overflow-y: auto; font-family: monospace; font-size: 12px; } Example JavaScript: async function pollTask(taskId) { while (true) { await new Promise(r => setTimeout(r, 300)); const res = await fetch(`/api/task/${taskId}`); const data = await res.json(); // Update UI (progress is 0.0-1.0, multiply by 100 for percentage) const pct = (data.progress || 0) * 100; progressBar.style.width = `${pct}%`; progressText.textContent = `${Math.round(pct)}%`; logsContainer.innerHTML = data.logs.map(l => `
${l}
`).join(''); logsContainer.scrollTop = logsContainer.scrollHeight; if (data.status === 'completed') return data.result; if (data.status === 'failed') throw new Error(data.error); } } Backend Implementation (log_user_message): When implementing async endpoints, use timestamped log messages: def log_user_message(task_id: str, message: str): """Log a user-facing message (timestamps added automatically).""" timestamp = time.strftime('%H:%M:%S') task_status[task_id]['logs'].append(f"[{timestamp}] {message}") # Usage: log_user_message(task_id, "🔍 Starting to process...") log_user_message(task_id, "📊 Analyzing data...") log_user_message(task_id, "✅ Complete!") # To update progress separately: task_status[task_id]['progress'] = 0.5 # 0.0-1.0 scale COMBINED PROGRESS BAR + LOG CONSOLE PATTERN -------------------------------------------- For long-running operations, show BOTH a progress bar AND a scrolling log console. This gives users both an at-a-glance status and detailed visibility into what's happening. Layout: ┌─────────────────────────────────────────────────────┐ │ [===========> ] 45% - 12s left │ ← Progress bar ├─────────────────────────────────────────────────────┤ │ [19:02:24] Starting task... │ ← Scrolling log │ [19:02:25] Downloading video... │ │ [19:02:33] Video cached (197.9 MB) │ │ [19:02:41] Generating hierarchical summary... │ │ [19:02:59] Expanded section 1/4: Introduction... │ └─────────────────────────────────────────────────────┘ Example HTML:
Starting...
Example CSS: .progress-log-container { background: #1e1e1e; border-radius: 8px; overflow: hidden; } .progress-section { padding: 10px 15px; border-bottom: 1px solid #333; } .progress-bar-wrapper { background: #333; border-radius: 4px; height: 24px; position: relative; } .progress-bar { height: 100%; background: linear-gradient(90deg, #00d4ff, #4ade80); border-radius: 4px; transition: width 0.3s; } .progress-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 13px; white-space: nowrap; } .log-console { color: #d4d4d4; font-family: monospace; font-size: 13px; height: 200px; overflow-y: auto; padding: 10px 15px; white-space: pre-wrap; } Example JavaScript (polling pattern): async function pollWithProgressAndLogs(taskId) { const progressBar = document.getElementById('progressBar'); const progressText = document.getElementById('progressText'); const logConsole = document.getElementById('logConsole'); while (true) { await new Promise(r => setTimeout(r, 1000)); const res = await fetch(`/api/task/${taskId}`); const data = await res.json(); // Update progress bar (progress is 0.0-1.0, multiply by 100 for %) const pct = (data.progress || 0) * 100; progressBar.style.width = `${pct}%`; progressText.textContent = `${Math.round(pct)}% - ${data.status}`; // Update log console if (data.logs) { logConsole.innerHTML = data.logs.map(l => `
${l}
`).join(''); logConsole.scrollTop = logConsole.scrollHeight; // Auto-scroll to bottom } if (data.status === 'completed') return data.result; if (data.status === 'failed') throw new Error(data.error); } } Backend pattern (add logs and progress together): def log_with_progress(task_id: str, message: str, progress: float = None): """Log a message and optionally update progress (0.0-1.0 scale).""" timestamp = time.strftime('%H:%M:%S') task = tasks[task_id] task['logs'].append(f"[{timestamp}] {message}") if progress is not None: task['progress'] = progress # Usage - log messages appear in console, progress updates the bar: log_with_progress(task_id, "Using cached video (197.9 MB)", 0.1) log_with_progress(task_id, "Generating hierarchical summary...", 0.2) log_with_progress(task_id, "Expanded section 1/4: Introduction...", 0.4) log_with_progress(task_id, "Expanded section 2/4: Main content...", 0.55) Benefits of combined approach: - Progress bar gives quick visual status (how much longer?) - Log console shows detailed activity (what's happening now?) - Users can ignore logs for casual use, or inspect them for debugging - No "fake" multi-step progress - logs show real operations - Log timestamps help identify slow steps for optimization PROMPT TRANSPARENCY ------------------- For long-running LLM calls, show users what's actually happening. This builds trust and helps debugging. Key principles: 1. Show the actual prompt being sent (collapsible) 2. Be honest about timing uncertainty ("Waiting for response..." not fake multi-step) 3. Show the raw response after completion (collapsible) Don't fake multi-step processes. If it's one LLM call, say "Waiting for Claude..." not "Step 1: Analyzing... Step 2: Processing..." - users can tell when you're lying. Example HTML Structure:
Waiting for Claude to respond...
12s elapsed
View prompt being sent →
[actual prompt text here]
View raw API response →
[full JSON response]
Example JavaScript: function showPrompt(prompt) { document.getElementById('prompt-display').textContent = prompt; } function showRawResponse(data) { document.getElementById('raw-response').textContent = JSON.stringify(data, null, 2); } Why This Matters: - Technical users want to verify what's being sent - Debugging is easier when you can see the actual prompt/response - Honest progress bars build trust (fake ones erode it) - Collapsible sections keep casual users uncluttered while power users can inspect POST /api/generate_image Generate image. Model is chosen by Bright Wrapper. Request body: { "prompt": "image description", "output_path": "/path/to/save.png", "project_name": "my_app", "aspect_ratio": "9:16", // optional, default "9:16" "should_log": true // optional, default true } Headers: X-API-Key: Response: {"image_path": "/path/to/saved.png"} EXAMPLE: PYTHON PROXY (FastAPI) ------------------------------- import httpx from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse BRIGHT_WRAPPER_URL = "https://brightwrapper.com" BRIGHT_WRAPPER_API_KEY = "your_api_key_here" app = FastAPI() @app.post("/api/brightwrapper/chat/stream") async def proxy_chat(request: Request): body = await request.json() async def stream(): async with httpx.AsyncClient() as client: async with client.stream( "POST", f"{BRIGHT_WRAPPER_URL}/api/chat/stream", json=body, headers={"X-API-Key": BRIGHT_WRAPPER_API_KEY} ) as response: async for chunk in response.aiter_bytes(): yield chunk return StreamingResponse(stream(), media_type="text/event-stream") @app.post("/api/brightwrapper/estimate") async def proxy_estimate(request: Request): body = await request.json() async with httpx.AsyncClient() as client: response = await client.post( f"{BRIGHT_WRAPPER_URL}/api/estimate", json=body ) return response.json() @app.get("/api/brightwrapper/version") async def proxy_version(): async with httpx.AsyncClient() as client: response = await client.get(f"{BRIGHT_WRAPPER_URL}/api/version") return response.json() EXAMPLE: NODE.JS PROXY (EXPRESS) -------------------------------- const express = require('express'); const fetch = require('node-fetch'); const app = express(); const BRIGHT_WRAPPER_URL = 'https://brightwrapper.com'; const BRIGHT_WRAPPER_API_KEY = 'your_api_key_here'; app.use(express.json()); app.post('/api/brightwrapper/chat/stream', async (req, res) => { const response = await fetch(`${BRIGHT_WRAPPER_URL}/api/chat/stream`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': BRIGHT_WRAPPER_API_KEY }, body: JSON.stringify(req.body) }); res.setHeader('Content-Type', 'text/event-stream'); response.body.pipe(res); }); app.post('/api/brightwrapper/estimate', async (req, res) => { const response = await fetch(`${BRIGHT_WRAPPER_URL}/api/estimate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(req.body) }); res.json(await response.json()); }); app.get('/api/brightwrapper/version', async (req, res) => { const response = await fetch(`${BRIGHT_WRAPPER_URL}/api/version`); res.json(await response.json()); }); API KEY MANAGEMENT ------------------ Create API key: POST /api/create_key Body: {"auth_token": ""} Response: {"api_key": "...", "user_id": 1, "user_email": "..."} List your keys: GET /api/keys Headers: X-API-Key: Revoke a key: POST /api/revoke_key Headers: X-API-Key: Body: {"api_key": ""} VERSIONING & UPGRADE NOTIFICATIONS ---------------------------------- Bright Wrapper uses API response bulletins to notify clients about upgrades, deprecations, and important changes. Clients should check /api/status periodically. GET /api/status Get complete service status (no authentication required). Response: { "api_version": "1.0.0", "min_supported_version": "1.0.0", "service_version": "1.2.3", "text_model": "gemini-3-pro-preview", "image_model": "gemini-3-pro-image-preview", "bulletins": [ {"message": "Model upgraded", "severity": "info", "expires": "2025-01-15"} ], "deprecations": { "/api/old_endpoint": { "deprecated": true, "sunset_date": "2025-02-01", "replacement": "/api/new_endpoint" } } } Bulletin Severity Levels: - "info" - Informational, no action required - "warning" - Action may be required soon - "critical" - Immediate action required Best Practices for Clients: 1. Check /api/status on startup or periodically (e.g., daily) 2. Log bulletins with severity >= "warning" for developer review 3. Display critical bulletins to users if appropriate 4. Monitor sunset_date for any deprecated endpoints you use 5. Plan upgrades before sunset dates to avoid service interruption Version Compatibility: - Clients using API version >= min_supported_version will continue working - When min_supported_version increases, older clients may receive errors - Bulletins will warn about upcoming version changes before they happen