File size: 5,747 Bytes
55b41ab 5383ef0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | ---
title: Chat
emoji: π
colorFrom: pink
colorTo: purple
sdk: docker
pinned: false
short_description: Inference Port Web Chat
---
HuggingFace Docker Space β Node.js WebSocket chat interface for InferencePort AI.
## Environment Variables
Set these as **Secrets** in your HuggingFace Space settings:
| Variable | Required | Description |
|---|---|---|
| `SUPABASE_ANON_KEY` | Optional | Supabase anon key (falls back to hardcoded value from app) |
| `PUBLIC_URL` | Recommended | Full public URL of your Space, e.g. `https://your-user-your-space.hf.space` |
No `SUPABASE_SERVICE_ROLE_KEY` is needed β all DB operations use the user's own access token via RLS.
## HuggingFace Space Setup
1. Create a new **Docker** Space on HuggingFace
2. Push this repository to the Space
3. Set `PUBLIC_URL` secret to your space URL (needed for share links)
4. The app listens on port `7860` which HuggingFace maps automatically
## Supabase Tables Required
Run this SQL in your Supabase project's **SQL Editor**:
```sql
-- Web sessions (separate from Electron app's chat_sessions)
CREATE TABLE IF NOT EXISTS public.web_sessions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name text NOT NULL DEFAULT 'New Chat',
history jsonb DEFAULT '[]',
model text,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now()
);
ALTER TABLE public.web_sessions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can manage their own web sessions"
ON public.web_sessions FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Shared sessions (readable by anyone, writable by owner)
CREATE TABLE IF NOT EXISTS public.shared_sessions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
token text UNIQUE NOT NULL,
owner_id uuid REFERENCES auth.users(id) ON DELETE CASCADE,
session_snapshot jsonb NOT NULL,
created_at timestamptz DEFAULT now()
);
ALTER TABLE public.shared_sessions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Anyone can read shared sessions"
ON public.shared_sessions FOR SELECT USING (true);
CREATE POLICY "Owners can insert shared sessions"
ON public.shared_sessions FOR INSERT
WITH CHECK (auth.uid() = owner_id);
-- User settings (theme, tool toggles)
CREATE TABLE IF NOT EXISTS public.user_settings (
user_id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
settings jsonb DEFAULT '{}',
updated_at timestamptz DEFAULT now()
);
ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users manage own settings"
ON public.user_settings FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
```
The `profiles` table already exists from the Electron app and is reused as-is.
## File Structure
```
inferenceport-web/
βββ Dockerfile
βββ package.json
βββ README.md
βββ server/
β βββ index.js β Express + WebSocket server entry point
β βββ wsHandler.js β All WebSocket message routing
β βββ sessionStore.js β In-memory + Supabase session storage
β βββ chatStream.js β Lightning API streaming + tool execution
β βββ auth.js β Supabase auth verification, profile, subscription
β βββ rateLimiter.js β Simple sliding-window rate limiter
βββ public/
βββ index.html β Single page app shell
βββ oauth-callback.html β OAuth redirect handler
βββ css/
β βββ tokens.css β CSS variables (colors, spacing, fonts)
β βββ base.css β Reset, global styles, animations
β βββ sidebar.css β Collapsible sidebar, session list
β βββ chat.css β Chat messages, code blocks, media
β βββ input.css β Center and bottom input bars
β βββ modals.css β Modal overlays, settings, auth forms
βββ js/
βββ ws.js β WebSocket client (auto-reconnect, request/response)
βββ auth.js β Auth state, Supabase sign-in/out, OAuth
βββ sessions.js β Session list management and rendering
βββ chat.js β Chat rendering, streaming, versioning, editing
βββ ui.js β Notifications, context menus, markdown renderer
βββ modals.js β All modal dialogs (auth, share, settings, tool detail)
βββ settings.js β Settings modal, theme application
βββ app.js β Bootstrap, input handling, sidebar, paste/attach
```
## Architecture Notes
- **WebSocket** is the primary transport for all chat, auth, and session operations
- **Server memory** stores active sessions; Supabase is the persistence layer for logged-in users
- **Guest users** get a `tempId` stored in `localStorage`; sessions live in server memory for 12h inactivity / 24h max, with a 15-message daily limit
- **On login**, temp sessions are transferred to the user's Supabase account automatically
- **Chat versioning**: each message stores `versions[]`, where each version contains the message content plus the `tail` (all following messages at time of edit). Navigating versions via the `βΉ 1/N βΊ` arrows replays the stored tail
- **Device sessions** are tracked in server memory only (lost on restart) β no service role key needed
- **Lightning** at `https://sharktide-lightning.hf.space/gen` is used for all chat streaming and media generation
## Local Development
```bash
npm install
PUBLIC_URL=http://localhost:7860 npm run dev
```
Then open `http://localhost:7860`.
|