|  | 
|  | 1 | +# AI Chat Demo | 
|  | 2 | + | 
|  | 3 | +Multi‑room real‑time chat with optional AI answers. Start locally in a minute. Move to Azure when you're ready—no code changes. | 
|  | 4 | + | 
|  | 5 | +> Want architecture diagrams, CloudEvents/tunnel details, RBAC & env matrix? See **[docs/ADVANCED.md](./docs/ADVANCED.md)**. | 
|  | 6 | +> Release history: **[RELEASE_NOTES.md](./RELEASE_NOTES.md)** | 
|  | 7 | +
 | 
|  | 8 | +## What You Get | 
|  | 9 | +- Real‑time rooms (create / join instantly) | 
|  | 10 | +- AI bot responses (GitHub Models via your PAT) | 
|  | 11 | +- Persistence + scale when on Azure Web PubSub | 
|  | 12 | +- Same React UI + Python backend in all modes | 
|  | 13 | + | 
|  | 14 | +## Quick Start | 
|  | 15 | + | 
|  | 16 | +Prereqs: | 
|  | 17 | +* Python 3.12+ | 
|  | 18 | +* Node 18+ | 
|  | 19 | + | 
|  | 20 | +1. Create a PAT with **Models – Read**: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens | 
|  | 21 | +2. Install backend + frontend deps: | 
|  | 22 | +  ```bash | 
|  | 23 | +  pip install -r requirements.txt | 
|  | 24 | +  # optional (tests, typing): pip install -r requirements-dev.txt | 
|  | 25 | +  ``` | 
|  | 26 | +3. (Set token if you want AI answers) | 
|  | 27 | +  ```bash | 
|  | 28 | +  export GITHUB_TOKEN=<your_pat>        # bash/zsh | 
|  | 29 | +  # PowerShell | 
|  | 30 | +  $env:GITHUB_TOKEN="<your_pat>" | 
|  | 31 | +  ``` | 
|  | 32 | +4. Start everything (serves React build automatically): | 
|  | 33 | +  ```bash | 
|  | 34 | +  python start_dev.py | 
|  | 35 | +  ``` | 
|  | 36 | +5. Open http://localhost:5173 | 
|  | 37 | + | 
|  | 38 | +Running services: | 
|  | 39 | +* HTTP API :5000 | 
|  | 40 | +* Local WebSocket :5001 (self transport) | 
|  | 41 | + | 
|  | 42 | +## Using the App | 
|  | 43 | +1. Browse to http://localhost:5173 | 
|  | 44 | +2. You're placed in room `public` | 
|  | 45 | +3. Create / join a room: type a name → Enter | 
|  | 46 | +4. Ask something; the AI bot replies (uses your GitHub token) | 
|  | 47 | +5. Open a second window to watch live streaming & room isolation | 
|  | 48 | + | 
|  | 49 | +### Tests | 
|  | 50 | + | 
|  | 51 | +Install dev extras first: | 
|  | 52 | +```bash | 
|  | 53 | +pip install -r requirements-dev.txt | 
|  | 54 | +``` | 
|  | 55 | + | 
|  | 56 | +Backend (pytest): | 
|  | 57 | +```bash | 
|  | 58 | +python -m pytest python_server/tests -q | 
|  | 59 | +``` | 
|  | 60 | +Single file: | 
|  | 61 | +```bash | 
|  | 62 | +python -m pytest python_server/tests/test_runtime_config.py -q | 
|  | 63 | +``` | 
|  | 64 | +Verbose / timing: | 
|  | 65 | +```bash | 
|  | 66 | +python -m pytest -vv | 
|  | 67 | +``` | 
|  | 68 | + | 
|  | 69 | +Frontend (Vitest + RTL): | 
|  | 70 | +```bash | 
|  | 71 | +npm --prefix client test | 
|  | 72 | +``` | 
|  | 73 | +Selected coverage areas (backend): config merge, room store limits, transport factory, room lifecycle, streaming send path. | 
|  | 74 | +Coverage snapshot: | 
|  | 75 | +* Runtime config validation & merging | 
|  | 76 | +* In‑memory room store behavior / limits | 
|  | 77 | +* Chat service builder (self vs webpubsub path & credential preconditions) | 
|  | 78 | +* Room lifecycle (add/remove) | 
|  | 79 | +* Streaming send path basic invariants | 
|  | 80 | + | 
|  | 81 | +#### Frontend (Vitest + RTL) | 
|  | 82 | +Location: `client/src/__tests__` | 
|  | 83 | + | 
|  | 84 | +Run: | 
|  | 85 | +``` | 
|  | 86 | +npm --prefix client test | 
|  | 87 | +``` | 
|  | 88 | +Included tests: | 
|  | 89 | +* Room switching: cached messages isolated & textarea present after switch | 
|  | 90 | +* Sender fallback: history messages without `from` show `AI Assistant` | 
|  | 91 | + | 
|  | 92 | +Add more ideas: | 
|  | 93 | +* Mid‑stream room switch preserves previous room’s partial content when returning | 
|  | 94 | +* Simulated error banner rendering | 
|  | 95 | +* Theme / avatar contexts snapshot | 
|  | 96 | + | 
|  | 97 | + | 
|  | 98 | +## Core Environment Variables | 
|  | 99 | +| Variable | Purpose | | 
|  | 100 | +|----------|---------| | 
|  | 101 | +| `GITHUB_TOKEN` | Enables AI responses (GitHub Models) | | 
|  | 102 | +| `TRANSPORT_MODE` | `self` (default) or `webpubsub` to use Azure Web PubSub service | | 
|  | 103 | +| `STORAGE_MODE` | `memory` (default) or `table` for Azure Table / Azurite persistence | | 
|  | 104 | +| `WEBPUBSUB_ENDPOINT` or `WEBPUBSUB_CONNECTION_STRING` | The Azure Web PubSub endpoint when transport_mode is `webpubsub` | | 
|  | 105 | +| `AZURE_STORAGE_ACCOUNT` or `AZURE_STORAGE_CONNECTION_STRING`| The Azure Storage endpoint when storage_mode is `table` | | 
|  | 106 | +| `WEBPUBSUB_HUB` | (Optional) Override the hub name used for the chat app when using Web PubSub (default: demo_ai_chat) | | 
|  | 107 | +| `CHAT_TABLE_NAME` | (Optional) Override Azure Table name (default: chatmessages) | | 
|  | 108 | + | 
|  | 109 | +Notes: | 
|  | 110 | +- In Azure deployment the Bicep sets `TRANSPORT_MODE=webpubsub` and `STORAGE_MODE=table`. | 
|  | 111 | +- Locally you can mix and match (e.g. `self+table` with Azurite or `webpubsub+memory`). | 
|  | 112 | + | 
|  | 113 | +## Custom Resource Names (Optional) | 
|  | 114 | +Want predictable names? Provide overrides (must be globally unique where required): | 
|  | 115 | +```bash | 
|  | 116 | +azd provision --set webPubSubNameOverride=mywps1234 --set webAppNameOverride=mychatweb1234 | 
|  | 117 | +``` | 
|  | 118 | + | 
|  | 119 | +## Iteration Cheatsheet | 
|  | 120 | +| Change | Command | | 
|  | 121 | +|--------|---------| | 
|  | 122 | +| Backend / frontend code | `azd deploy` | | 
|  | 123 | +| Infra (Bicep) changes | `azd provision && azd deploy` | | 
|  | 124 | +| New environment | `azd env new <name> --location <region>` then `azd up` | | 
|  | 125 | + | 
|  | 126 | +## Hybrid Local + Azure (Short Version) | 
|  | 127 | +Run the backend locally while using a real Web PubSub instance (and optionally Table storage) in Azure: | 
|  | 128 | +1. `azd up` (once) provisions resources. | 
|  | 129 | +2. Create a local `.env` with: | 
|  | 130 | +   ``` | 
|  | 131 | +   TRANSPORT_MODE=webpubsub | 
|  | 132 | +   WEBPUBSUB_ENDPOINT=https://<name>.webpubsub.azure.com | 
|  | 133 | +   # optional persistence | 
|  | 134 | +   STORAGE_MODE=table | 
|  | 135 | +   ``` | 
|  | 136 | +3. `python start_dev.py` | 
|  | 137 | +4. (Optional) Tunnel for CloudEvents (see ADVANCED.md §2.3) | 
|  | 138 | + | 
|  | 139 | +More: **[ADVANCED.md](./docs/ADVANCED.md#2-local-development-paths)** | 
|  | 140 | + | 
|  | 141 | +## FAQ (Quick) | 
|  | 142 | +**Do I need Azure to try it?** No—local mode works offline. | 
|  | 143 | + | 
|  | 144 | +**Why Web PubSub?** Managed scale, groups, CloudEvents, secure client negotiation. | 
|  | 145 | + | 
|  | 146 | +**Where are advanced knobs?** All in `docs/ADVANCED.md` (RBAC, credential chain, persistence, CloudEvents, tunnels). | 
|  | 147 | + | 
|  | 148 | +## Next Steps | 
|  | 149 | +- Explore advanced capabilities: [docs/ADVANCED.md](./docs/ADVANCED.md) | 
|  | 150 | +- Try hybrid tunnel mode for full lifecycle locally | 
|  | 151 | +- Customize AI logic in `python_server/chat_model_client.py` | 
|  | 152 | +- Review how scalable history works now (Table storage) in the persistence section of the advanced doc. | 
|  | 153 | + | 
|  | 154 | +--- | 
|  | 155 | +Happy hacking! Open an issue or PR in [our GitHub repo](https://github.com/Azure/azure-webpubsub) with feedback. | 
|  | 156 | + | 
0 commit comments