# AI Chat Page — Routstr Protocol Integration

## Overview

Build `ai.html` as a chat interface for AI models, paying per-request with Cashu ecash via the Routstr protocol. This page follows the same template pattern as all other pages in the project (header/body/footer/sidenav shell from `template.html`) and reuses the existing `cashu-wallet.mjs` controller for wallet operations.

The goal is to replicate the core functionality of [chat.routstr.com](https://chat.routstr.com/) — model selection, streaming chat, cashu payment — but in our vanilla JS/HTML style with no React/Next.js dependencies.

---

## Architecture

```mermaid
flowchart TD
    subgraph Browser
        AI[ai.html]
        CW[cashu-wallet.mjs]
        NDK[init-ndk.mjs / ndk-worker.js]
    end

    subgraph Routstr Provider
        API[/v1/chat/completions]
        MODELS[/v1/models]
        INFO[/v1/info]
        BAL[/v1/balance/*]
    end

    subgraph Nostr Relays
        DISC[kind:38421 Provider Events]
    end

    AI -->|imports| CW
    AI -->|imports| NDK
    AI -->|fetch| API
    AI -->|fetch| MODELS
    AI -->|fetch| INFO
    AI -->|fetch| BAL
    NDK -->|subscribe| DISC
    CW -->|sendToken| AI
    AI -->|receiveToken refund| CW
```

## Payment Flow per Request

```mermaid
sequenceDiagram
    participant User
    participant AI as ai.html
    participant Wallet as cashu-wallet.mjs
    participant Provider as Routstr Provider

    User->>AI: Type message and send
    AI->>Provider: sendChatMessage with history and model
    AI->>Provider: GET /v1/models - get pricing
    AI->>AI: Calculate required sats from max_cost
    AI->>Wallet: sendToken with required amount
    Wallet-->>AI: cashuToken string
    AI->>Provider: POST /v1/chat/completions with Authorization: Bearer cashuToken
    Provider-->>AI: SSE stream chunks
    AI-->>AI: onStreamChunk callbacks - render incrementally
    Provider-->>AI: Stream complete with usage stats
    AI->>Provider: POST /v1/balance/refund
    Provider-->>AI: Refund cashu token for unspent balance
    AI->>Wallet: receiveToken with refund token
    Wallet-->>AI: Updated balance
    AI-->>AI: onComplete with cost info
    AI->>AI: Update balance display and cost per message
```

---

## File Structure

| File | Purpose |
|------|---------|
| `www/ai.html` | Page HTML + inline styles + inline script wiring |
| `www/ai.html` (inline module script) | Routstr API client logic - provider management, model fetching, chat requests, SSE streaming, refund handling |
| `www/js/cashu-wallet.mjs` | Existing - Cashu wallet controller, used for sendToken/receiveToken |
| `www/js/init-ndk.mjs` | Existing - NDK page init, subscribe for provider discovery |
| `www/js/marked.min.js` | Existing - Markdown rendering |

---

## Detailed Plan

### 1. Implement Routstr API Client Logic in `www/ai.html`

This is the core implementation block and encapsulates all Routstr protocol logic directly in the page module script.

**Exports:**

- `createRoutstrClient({ walletController, subscribe })` — Factory that returns a client object
- Client methods:
  - `discoverProviders()` — Subscribe to kind:38421 Nostr events + fetch from hardcoded defaults
  - `fetchModels(providerUrl)` — GET `/v1/models` and cache pricing
  - `fetchProviderInfo(providerUrl)` — GET `/v1/info`
  - `getAvailableModels()` — Merged list from all discovered providers
  - `sendChatMessage({ messages, model, providerUrl, callbacks })` — Full request lifecycle:
    1. Look up model pricing from cache
    2. Calculate required sats (use `max_cost` from model or estimate from `sats_pricing`)
    3. Call `walletController.sendToken(amount)` to get a cashu token
    4. POST to `/v1/chat/completions` with `Authorization: Bearer <token>`, `stream: true`
    5. Parse SSE stream, call `callbacks.onChunk(text)` for each delta
    6. On stream complete, call `/v1/balance/refund` to get change token
    7. Call `walletController.receiveToken(refundToken)` to store change
    8. Call `callbacks.onComplete({ content, usage, cost })` with final stats
  - `getBalance(providerUrl)` — GET `/v1/balance/info`

**Key design decisions:**
- Use the simpler **cashu-as-bearer-token** approach from RIP-01 (send cashu token in Authorization header, get refund after)
- Support both `Authorization: Bearer <cashuToken>` and `X-Cashu: <cashuToken>` modes
- Default providers: `https://api.routstr.com/`, `https://api.nonkycai.com/`, `https://privateprovider.xyz/`
- SSE parsing: Read `response.body` as a ReadableStream, split on newlines, parse `data: {...}` JSON chunks, extract `choices[0].delta.content`

### 2. Create `ai.html` Page Layout

Based on `template.html` shell + chat UI inspired by `msg.html` bubble layout.

**Layout structure:**
```
#divBody
  #divAiLayout (flex row, full height)
    #divAiConversationsPane (left sidebar - conversation list, 30% width)
      #divAiConversationsTitle
      #divAiConversationsList (scrollable)
      #divAiNewChatBtn
    #divAiChatPane (right main area, flex column)
      #divAiChatHeader (model selector + provider info + cost display)
      #divAiMessages (scrollable message area)
      #divAiInputArea (input box + send button)
```

**Message rendering:**
- User messages: right-aligned bubble (like `msg.html` outgoing)
- AI messages: left-aligned bubble (like `msg.html` incoming)
- Markdown rendered via `marked.min.js` + sanitized with DOMPurify
- Code blocks with syntax highlighting via marked
- Streaming: AI bubble updates in real-time as chunks arrive
- Typing indicator: pulsing dots while waiting for first chunk

### 3. Wire Cashu Wallet

- Import `createCashuWalletController` from `cashu-wallet.mjs`
- Initialize wallet on page load (same pattern as `cashu.html`)
- Use `walletController.sendToken(amount, memo, mint)` to create payment tokens
- Use `walletController.receiveToken(token)` to receive refund tokens
- Display balance in footer `#divFooterBalance` (already in template)
- Subscribe to balance updates via `walletController.onWalletEvent('balance_updated', ...)`

### 4. Model and Provider Selection UI

**In `#divAiChatHeader`:**
- Dropdown/select for model (populated from `getAvailableModels()`)
- Shows: model name, provider, price per 1M tokens
- Small provider badge showing which provider is selected

**In sidenav:**
- Provider list with enable/disable toggles
- Add custom provider URL
- Mint URL configuration (which mint to pay from)

### 5. Provider Discovery

Two discovery methods:

1. **Hardcoded defaults** — Start with known providers:
   - `https://api.routstr.com/`
   - `https://api.nonkycai.com/`
   - `https://privateprovider.xyz/`

2. **Nostr discovery** — Subscribe to kind:38421 events via NDK:
   ```js
   subscribe({ kinds: [38421] }, { closeOnEose: true, cacheUsage: 'CACHE_FIRST' });
   ```
   Parse `u` tags for HTTP endpoints, `mint` tags for accepted mints.

### 6. Streaming Response Display

- Create AI message bubble immediately when request starts
- Show typing indicator (3 pulsing dots)
- As SSE chunks arrive, append to bubble content and re-render markdown
- Auto-scroll to bottom on each update
- On complete, finalize the message and show cost metadata

### 7. Cost Tracking

- Display per-message cost in `msgBubbleMeta` area (e.g., "2 sats")
- Track total session spend
- Show wallet balance in footer (updates after each refund)
- Show estimated cost before sending (based on model pricing)

### 8. Conversation Persistence

- Store conversations in localStorage keyed by conversation ID
- Each conversation: `{ id, title, model, provider, messages[], createdAt, updatedAt }`
- Auto-generate title from first user message
- List conversations in left sidebar
- New chat button creates fresh conversation
- Delete conversation option

### 9. System Prompt Configuration

- Collapsible section in sidenav or chat header
- Default system prompt: "You are a helpful assistant."
- Custom system prompt textarea
- System prompt saved per-conversation
- Prepended to message history on each request

---

## Implementation Order

The work should be done in this sequence, with each step building on the previous:

1. **ai.html Routstr logic** — Core request/payment logic first, testable in-page
2. **ai.html layout** — Page structure with CSS
3. **Wire wallet** — Connect cashu-wallet.mjs
4. **Model fetching** — Populate model selector from providers
5. **Chat send/receive** — Basic non-streaming first, then streaming
6. **Markdown rendering** — Rich message display
7. **Cost tracking** — Balance and per-message costs
8. **Conversation persistence** — Save/load conversations
9. **Provider discovery** — Nostr-based discovery
10. **System prompt** — Configuration UI
11. **Polish** — Error handling, loading states, edge cases

---

## Key References

- **RIP-01** (`routstr/RIP-01.md`): Proxy/Payments — Authorization header, endpoints, payment flow, X-Cashu header
- **RIP-02** (`routstr/RIP-02.md`): Discovery — Kind 38421 provider events
- **RIP-03** (`routstr/RIP-03.md`): Clients — Wallet management, overpay-then-refund cycle
- **RIP-05** (`routstr/RIP-05.md`): Pricing — Cost calculation, max_cost, client verification
- **routstr-chat/utils/apiUtils.ts**: Reference implementation of `routstrRequest()` and `fetchAIResponse()`
- **routstr-chat/sdk/client/StreamProcessor.ts**: SSE stream parsing reference
- **routstr-chat/sdk/wallet/CashuSpender.ts**: Token spending logic reference
- **www/msg.html**: Chat bubble UI patterns to reuse
- **www/cashu.html** + **www/js/cashu-wallet.mjs**: Existing wallet integration to reuse
