npm package: @geepers/sse
Description: SSE streaming chat client for multi-provider LLM APIs. Handles Server-Sent Events parsing, content extraction from various provider response formats, and streaming display. Zero framework dependencies.
Author: Luke Steuber
Latest version: 1.0.1
Deprecated: This package has been inlined into @geepers/chat. Install @geepers/chat instead.

# @lukeslp/geepers-chat

SSE streaming chat client for multi-provider LLM APIs — no framework required.

![npm](https://img.shields.io/npm/v/@lukeslp/geepers-chat.svg)
![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)

Handles Server-Sent Events parsing, content extraction from OpenAI / Anthropic / xAI / custom gateway response formats, and streaming display. Drop it into any project — vanilla JS, React, Vue, whatever.

## Install

```bash
npm install @lukeslp/geepers-chat
```

## Usage

```ts
import { SSEChat } from '@lukeslp/geepers-chat';

const chat = new SSEChat({
  apiUrl: 'https://api.example.com/v1/llm/chat',
  apiKey: 'sk_...',
});

await chat.send({
  provider: 'openai',
  model: 'gpt-4',
  messages: [{ role: 'user', content: 'Hello!' }],
  onChunk: (chunk, accumulated) => {
    document.getElementById('output').textContent = accumulated;
  },
  onDone: (fullText) => console.log('Done:', fullText),
  onError: (err) => console.error(err),
});
```

### Cancellation

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

chat.send({
  provider: 'anthropic',
  model: 'claude-3-5-sonnet-20241022',
  messages: [{ role: 'user', content: 'Write me a novel...' }],
  signal: controller.signal,
  onDone: (text) => console.log(text),
});

// Cancel mid-stream — onDone fires with whatever arrived
setTimeout(() => controller.abort(), 2000);
```

### Extra parameters

```ts
await chat.send({
  provider: 'openai',
  model: 'gpt-4o',
  messages: [...],
  extraParams: { temperature: 0.7, max_tokens: 1000 },
  onChunk: (chunk) => process.stdout.write(chunk),
  onDone: (full) => console.log('\nDone.'),
});
```

## API

### `new SSEChat(options)`

| Option | Type | Description |
|--------|------|-------------|
| `apiUrl` | `string` | Chat completions endpoint |
| `apiKey` | `string?` | Sent as `X-API-Key` header |
| `headers` | `Record<string, string>?` | Additional headers |

### `chat.send(options): Promise<string>`

Streams the response and resolves with the full text when complete.

| Option | Type | Description |
|--------|------|-------------|
| `provider` | `string` | Provider name (e.g. `"openai"`, `"anthropic"`) |
| `model` | `string` | Model identifier |
| `messages` | `ChatMessage[]` | Conversation history |
| `onChunk` | `(chunk, accumulated) => void` | Called with each text chunk |
| `onDone` | `(fullText) => void` | Called when streaming completes |
| `onError` | `(error) => void` | Called on error |
| `signal` | `AbortSignal?` | For cancellation |
| `extraParams` | `Record<string, unknown>?` | Extra body params |

### `chat.setApiKey(key)`

Update the API key at runtime without creating a new instance.

### Provider format support

Automatically detects and extracts content from:
- **OpenAI** — `choices[0].delta.content`
- **Anthropic** — `delta.text`, `content_block.text`
- **xAI / custom gateways** — direct `content` field

## Author

**Luke Steuber**
- Website: [lukesteuber.com](https://lukesteuber.com)
- Bluesky: [@lukesteuber.com](https://bsky.app/profile/lukesteuber.com)
- Email: luke@lukesteuber.com

## License

MIT License — see [LICENSE](LICENSE) for details.