# How to Implement Real-time Chat

Learn how to implement chat with streaming to show the response as it's being generated.

### Problem

You want to create a smooth chat experience where the user sees the response being typed in real-time.

### Solution

Use the `/v1/kb/chat/stream` endpoint that returns the response via Server-Sent Events (SSE).

### Endpoint

```
POST /v1/kb/chat/stream
Content-Type: application/json
x-api-key: YOUR_API_KEY
```

### Response Format

The response comes as SSE events:

```
data: {"event":"message","data":{"content":"First"}}

data: {"event":"message","data":{"content":" part"}}

data: {"event":"message","data":{"content":" of the response"}}

data: [DONE]
```

### JavaScript Implementation

#### Using fetch + ReadableStream

```javascript
async function chatStream(query, tenantId, apiKey) {
  const response = await fetch('https://api-integrations.snackprompt.com/v1/kb/chat/stream', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': apiKey,
    },
    body: JSON.stringify({
      query: query,
      filters: {
        tenant_id: tenantId
      }
    })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let fullResponse = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    const lines = chunk.split('\n');

    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = line.slice(6);

        if (data === '[DONE]') {
          console.log('Stream complete');
          return fullResponse;
        }

        try {
          const parsed = JSON.parse(data);
          if (parsed.event === 'message') {
            const content = parsed.data.content;
            fullResponse += content;
            // Update UI in real-time
            updateUI(content);
          } else if (parsed.event === 'error') {
            console.error('Stream error:', parsed.data.message);
          }
        } catch (e) {
          // Ignore lines that aren't valid JSON
        }
      }
    }
  }

  return fullResponse;
}

function updateUI(content) {
  const chatBox = document.getElementById('chat-response');
  chatBox.textContent += content;
}
```

#### Using EventSource (simpler)

> **Note:** EventSource only works with GET, so you'll need a proxy or use fetch.

```javascript
// With a proxy that converts POST to GET
const eventSource = new EventSource(
  `/api/chat-stream?query=${encodeURIComponent(query)}&tenant_id=${tenantId}`
);

eventSource.onmessage = (event) => {
  if (event.data === '[DONE]') {
    eventSource.close();
    return;
  }

  try {
    const data = JSON.parse(event.data);
    if (data.event === 'message') {
      document.getElementById('response').textContent += data.data.content;
    }
  } catch (e) {
    // Ignore
  }
};

eventSource.onerror = (error) => {
  console.error('SSE error:', error);
  eventSource.close();
};
```

### Python Implementation

```python
import requests
import json

def chat_stream(query: str, tenant_id: str, api_key: str):
    response = requests.post(
        'https://api-integrations.snackprompt.com/v1/kb/chat/stream',
        headers={'x-api-key': api_key},
        json={
            'query': query,
            'filters': {'tenant_id': tenant_id}
        },
        stream=True
    )

    full_response = ''

    for line in response.iter_lines():
        if line:
            line = line.decode('utf-8')
            if line.startswith('data: '):
                data = line[6:]

                if data == '[DONE]':
                    print('\n[Stream complete]')
                    break

                try:
                    parsed = json.loads(data)
                    if parsed.get('event') == 'message':
                        content = parsed['data']['content']
                        full_response += content
                        print(content, end='', flush=True)
                    elif parsed.get('event') == 'error':
                        print(f"\n[Error: {parsed['data']['message']}]")
                except json.JSONDecodeError:
                    pass

    return full_response


# Usage
response = chat_stream("what are the products?", "tenant-123", "YOUR_API_KEY")
```

### React Implementation

```jsx
import { useState, useCallback } from 'react';

function ChatComponent() {
  const [response, setResponse] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  const sendMessage = useCallback(async (query) => {
    setIsLoading(true);
    setResponse('');

    try {
      const res = await fetch('/v1/kb/chat/stream', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': 'YOUR_API_KEY'
        },
        body: JSON.stringify({
          query,
          filters: { tenant_id: 'your-tenant-id' }
        })
      });

      const reader = res.body.getReader();
      const decoder = new TextDecoder();

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunk = decoder.decode(value);
        const lines = chunk.split('\n');

        for (const line of lines) {
          if (line.startsWith('data: ')) {
            const data = line.slice(6);
            if (data === '[DONE]') continue;

            try {
              const parsed = JSON.parse(data);
              if (parsed.event === 'message') {
                setResponse(prev => prev + parsed.data.content);
              }
            } catch (e) {}
          }
        }
      }
    } catch (error) {
      console.error('Error:', error);
    } finally {
      setIsLoading(false);
    }
  }, []);

  return (
    <div>
      <button onClick={() => sendMessage('your question')} disabled={isLoading}>
        {isLoading ? 'Loading...' : 'Send'}
      </button>
      <div className="response">
        {response}
        {isLoading && <span className="cursor">|</span>}
      </div>
    </div>
  );
}
```

### Handling Errors

```javascript
for (const line of lines) {
  if (line.startsWith('data: ')) {
    const data = line.slice(6);

    try {
      const parsed = JSON.parse(data);

      switch (parsed.event) {
        case 'message':
          // Normal content
          handleContent(parsed.data.content);
          break;

        case 'error':
          // Error during streaming
          handleError(parsed.data.message);
          break;
      }
    } catch (e) {
      // Not JSON, probably [DONE]
      if (data === '[DONE]') {
        handleComplete();
      }
    }
  }
}
```

### Tips

#### 1. Show Loading Indicator

```css
.cursor {
  animation: blink 1s infinite;
}

@keyframes blink {
  50% { opacity: 0; }
}
```

#### 2. Auto-scroll

```javascript
function updateUI(content) {
  const chatBox = document.getElementById('chat-response');
  chatBox.textContent += content;
  chatBox.scrollTop = chatBox.scrollHeight; // Auto-scroll
}
```

#### 3. Allow Cancellation

```javascript
const controller = new AbortController();

fetch(url, { signal: controller.signal });

// To cancel
controller.abort();
```

### Related

* [Chat with your Data](/bring-your-data-into-ai/get-started/chat-with-your-data-rag.md)
* [Error Handling](/bring-your-data-into-ai/how-to/how-to-handle-errors.md)
* [Endpoints](/bring-your-data-into-ai/reference/endpoints.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.snackprompt.com/bring-your-data-into-ai/how-to/how-to-implement-real-time-chat.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
