-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathApp.tsx
More file actions
79 lines (73 loc) · 3.12 KB
/
Copy pathApp.tsx
File metadata and controls
79 lines (73 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import 'react-chorus/styles.css';
import React from 'react';
import { Chorus, createWebSocketTransport, useChorusStream } from 'react-chorus';
import type { ChorusOnSend, Message } from 'react-chorus';
/**
* `createWebSocketTransport` opens a socket to the local `ws` server in
* `./server`. That server streams Anthropic Messages frames; the WebSocket
* transport treats each inbound frame as one SSE payload, so the built-in
* `anthropic` connector parses `content_block_delta` / `message_stop` exactly
* as it would over an HTTP SSE stream.
*
* The bundled server uses canned frames so the demo runs with no API key. Its
* README shows the one-line swap to the real `ws` + `@anthropic-ai/sdk` backend
* documented in the root README's "Using the WebSocket transport" section.
*
* The transport's `onOpen`/`onClose`/`onError` lifecycle callbacks drive the
* connection-status banner below — this is the runnable reference for the
* connection-status pattern in that README section. There is no `'connecting'`
* state on purpose: in the default per-send-socket mode a socket opens and
* `onOpen` fires almost immediately, so a transient "Connecting…" banner would
* never actually be visible. A normal close reports code 1000, so only an
* abnormal close or a socket error surfaces a banner.
*/
const WS_URL = 'ws://localhost:8787';
export default function App() {
const [messages, setMessages] = React.useState<Message[]>([]);
const [connectionStatus, setConnectionStatus] = React.useState('idle');
const transport = React.useMemo(
() =>
createWebSocketTransport(WS_URL, {
onOpen: () => setConnectionStatus('open'),
onClose: (code, reason) =>
setConnectionStatus(
code === 1000 ? 'closed' : `disconnected (${code}: ${reason || 'no reason'})`,
),
onError: () => setConnectionStatus('error'),
}),
[],
);
const { send, sending } = useChorusStream(transport, { connector: 'anthropic' });
const handleSend: ChorusOnSend = async (text, msgs, helpers) => {
await send(
text,
msgs,
helpers.streamCallbacks?.() ?? { onChunk: helpers.appendAssistant, onDone: helpers.finalizeAssistant },
helpers.signal,
);
};
return (
<div style={{ height: '100dvh', display: 'flex', flexDirection: 'column' }}>
{connectionStatus.startsWith('disconnected') && (
<div role="alert">Disconnected from the WebSocket server.</div>
)}
{connectionStatus === 'error' && (
<div role="alert">WebSocket connection error — is the ws server running on port 8787?</div>
)}
<Chorus
style={{ flex: 1, minHeight: 0 }}
value={messages}
onChange={setMessages}
onSend={handleSend}
sending={sending}
placeholder="Type a message and press Enter…"
suggestedPrompts={[
'Stream a reply over a WebSocket',
'Show me the anthropic connector over ws',
]}
errorMessage="The WebSocket example could not complete that request. Make sure the ws server is running on port 8787."
onError={(error) => console.error(error)}
/>
</div>
);
}