UNPKG

@kitn.ai/chat

Version:

Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS.

69 lines (57 loc) 2.23 kB
import { Meta } from '@storybook/addon-docs/blocks'; <Meta title="Docs/Recipes/Streaming (OpenRouter)" /> # Streaming from OpenRouter [OpenRouter](https://openrouter.ai) exposes an OpenAI-compatible streaming API (Server-Sent Events). Wire it into the `submit` event: > **Security:** never ship an API key to the browser. In production, point `fetch` at your own backend that proxies to OpenRouter and injects the key. ```js chat.addEventListener('kc-submit', async (e) => { const text = e.detail.value.trim(); if (!text) return; // 1. Show the user message const history = [...chat.messages, { id: crypto.randomUUID(), role: 'user', content: text }]; chat.messages = history; chat.loading = true; // 2. Empty assistant placeholder to stream into const assistantId = crypto.randomUUID(); chat.messages = [...history, { id: assistantId, role: 'assistant', content: '' }]; // In production, replace this with your own proxy endpoint. const res = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ model: 'anthropic/claude-sonnet-4', stream: true, messages: history.map((m) => ({ role: m.role, content: m.content })), }), }); const reader = res.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; let answer = ''; while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop(); for (const line of lines) { const s = line.trim(); if (!s.startsWith('data:')) continue; const payload = s.slice(5).trim(); if (payload === '[DONE]') continue; try { const delta = JSON.parse(payload).choices?.[0]?.delta?.content; if (!delta) continue; answer += delta; chat.messages = chat.messages.map((m) => m.id === assistantId ? { ...m, content: answer } : m ); } catch { /* ignore non-JSON keep-alive lines */ } } } chat.loading = false; }); ```