spres-react 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/ChatPanel.d.ts +2 -0
- package/dist/ChatPanel.js +89 -0
- package/dist/ChatWidget.d.ts +2 -0
- package/dist/ChatWidget.js +99 -0
- package/dist/brainbox-sdk.d.ts +13 -0
- package/dist/brainbox-sdk.js +100 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.js +1 -0
- package/dist/useBrainboxChat.d.ts +3 -0
- package/dist/useBrainboxChat.js +85 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useBrainboxChat } from './useBrainboxChat';
|
|
4
|
+
const sidebarStyle = {
|
|
5
|
+
width: '220px',
|
|
6
|
+
background: '#F8FAFC',
|
|
7
|
+
borderRight: '1px solid #E2E8F0',
|
|
8
|
+
padding: '16px',
|
|
9
|
+
boxSizing: 'border-box'
|
|
10
|
+
};
|
|
11
|
+
const panelStyle = {
|
|
12
|
+
display: 'flex',
|
|
13
|
+
height: '100%',
|
|
14
|
+
width: '100%',
|
|
15
|
+
borderRadius: '24px',
|
|
16
|
+
overflow: 'hidden',
|
|
17
|
+
boxShadow: '0 24px 80px rgba(15, 23, 42, 0.12)'
|
|
18
|
+
};
|
|
19
|
+
const bubbleStyle = {
|
|
20
|
+
borderRadius: '18px',
|
|
21
|
+
padding: '12px 14px',
|
|
22
|
+
marginBottom: '12px',
|
|
23
|
+
maxWidth: '100%'
|
|
24
|
+
};
|
|
25
|
+
function renderMessage(message, primaryColor, accentColor) {
|
|
26
|
+
const isUser = message.role === 'user';
|
|
27
|
+
return (_jsx("div", { style: { display: 'flex', justifyContent: isUser ? 'flex-end' : 'flex-start' }, children: _jsx("div", { style: {
|
|
28
|
+
...bubbleStyle,
|
|
29
|
+
background: isUser ? primaryColor : accentColor,
|
|
30
|
+
color: '#fff',
|
|
31
|
+
borderRadius: isUser ? '18px 18px 6px 18px' : '18px 18px 18px 6px'
|
|
32
|
+
}, children: message.text }) }, message.id));
|
|
33
|
+
}
|
|
34
|
+
export function ChatPanel({ sdk, primaryColor = '#0F172A', accentColor = '#2563EB', backgroundColor = '#FFFFFF', headerText = 'Brainbox Chat', sidebarTitle = 'History', initialSessionId, design = 'cloud' }) {
|
|
35
|
+
const { messages, loading, error, sendMessage, sendVoiceNote, createSession, sessionId } = useBrainboxChat(sdk);
|
|
36
|
+
const [input, setInput] = useState('');
|
|
37
|
+
const [voiceNote, setVoiceNote] = useState('');
|
|
38
|
+
const endRef = useRef(null);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (initialSessionId) {
|
|
41
|
+
// session concept is preserved but not used for UI state yet
|
|
42
|
+
}
|
|
43
|
+
}, [initialSessionId]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
var _a;
|
|
46
|
+
(_a = endRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' });
|
|
47
|
+
}, [messages]);
|
|
48
|
+
const handleSend = async () => {
|
|
49
|
+
if (!input.trim())
|
|
50
|
+
return;
|
|
51
|
+
await sendMessage(input.trim());
|
|
52
|
+
setInput('');
|
|
53
|
+
};
|
|
54
|
+
const handleVoiceNote = async () => {
|
|
55
|
+
if (!voiceNote.trim())
|
|
56
|
+
return;
|
|
57
|
+
await sendVoiceNote(voiceNote.trim());
|
|
58
|
+
setVoiceNote('');
|
|
59
|
+
};
|
|
60
|
+
const statusList = useMemo(() => [
|
|
61
|
+
{ label: 'API status', value: 'Connected' },
|
|
62
|
+
{ label: 'Session', value: sessionId || 'None' },
|
|
63
|
+
{ label: 'Design', value: design },
|
|
64
|
+
{ label: 'Real-time', value: 'Stream-ready' }
|
|
65
|
+
], [sessionId, design]);
|
|
66
|
+
return (_jsxs("div", { style: { ...panelStyle, background: backgroundColor, minHeight: '560px' }, children: [_jsxs("aside", { style: sidebarStyle, children: [_jsxs("div", { style: { marginBottom: '20px' }, children: [_jsx("h3", { style: { margin: 0, fontSize: '18px', color: '#111827' }, children: sidebarTitle }), _jsx("p", { style: { marginTop: '8px', fontSize: '13px', color: '#475569' }, children: "Saved chat threads and endpoints." })] }), _jsx("div", { style: { marginBottom: '18px' }, children: statusList.map(item => (_jsxs("div", { style: { marginBottom: '12px' }, children: [_jsx("strong", { style: { display: 'block', color: '#334155', fontSize: '13px' }, children: item.label }), _jsx("span", { style: { color: '#475569', fontSize: '13px' }, children: item.value })] }, item.label))) }), _jsx("button", { onClick: () => createSession('Chat Panel Session'), style: {
|
|
67
|
+
width: '100%',
|
|
68
|
+
background: accentColor,
|
|
69
|
+
color: '#fff',
|
|
70
|
+
border: 'none',
|
|
71
|
+
borderRadius: '12px',
|
|
72
|
+
padding: '12px',
|
|
73
|
+
cursor: 'pointer'
|
|
74
|
+
}, children: "Create Session" })] }), _jsxs("main", { style: { flex: 1, display: 'flex', flexDirection: 'column', padding: '20px', boxSizing: 'border-box' }, children: [_jsxs("header", { style: { marginBottom: '16px' }, children: [_jsx("h2", { style: { margin: 0, color: primaryColor }, children: headerText }), _jsx("p", { style: { marginTop: '8px', color: '#475569' }, children: "Use the built-in chat UI with instant backend communication." })] }), _jsxs("section", { style: { flex: 1, overflowY: 'auto', paddingRight: '8px', marginBottom: '16px' }, children: [messages.length === 0 ? (_jsx("div", { style: { color: '#64748B', fontSize: '14px' }, children: "Your chat history appears here. Send a message to start." })) : (messages.map(message => renderMessage(message, accentColor, '#0F172A'))), _jsx("div", { ref: endRef })] }), _jsxs("div", { style: { borderTop: '1px solid #E2E8F0', paddingTop: '16px' }, children: [_jsxs("div", { style: { display: 'flex', gap: '8px', marginBottom: '10px', flexWrap: 'wrap' }, children: [_jsx("input", { value: input, onChange: event => setInput(event.target.value), onKeyDown: event => event.key === 'Enter' && handleSend(), placeholder: "Type a message...", style: { flex: 1, minWidth: '200px', padding: '12px 14px', border: '1px solid #CBD5E1', borderRadius: '14px' } }), _jsx("button", { onClick: handleSend, style: {
|
|
75
|
+
background: primaryColor,
|
|
76
|
+
color: '#fff',
|
|
77
|
+
border: 'none',
|
|
78
|
+
borderRadius: '14px',
|
|
79
|
+
padding: '12px 18px',
|
|
80
|
+
cursor: 'pointer'
|
|
81
|
+
}, children: loading ? 'Sending...' : 'Send' })] }), _jsxs("div", { style: { display: 'flex', gap: '8px', alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx("input", { value: voiceNote, onChange: event => setVoiceNote(event.target.value), placeholder: "Voice note text (demo)", style: { flex: 1, minWidth: '200px', padding: '12px 14px', border: '1px solid #CBD5E1', borderRadius: '14px' } }), _jsx("button", { onClick: handleVoiceNote, style: {
|
|
82
|
+
background: accentColor,
|
|
83
|
+
color: '#fff',
|
|
84
|
+
border: 'none',
|
|
85
|
+
borderRadius: '14px',
|
|
86
|
+
padding: '12px 18px',
|
|
87
|
+
cursor: 'pointer'
|
|
88
|
+
}, children: "Add Voice Note" })] }), error && _jsx("div", { style: { marginTop: '12px', color: '#DC2626' }, children: error })] })] })] }));
|
|
89
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useBrainboxChat } from './useBrainboxChat';
|
|
4
|
+
const defaultButtonStyle = {
|
|
5
|
+
border: 'none',
|
|
6
|
+
borderRadius: '999px',
|
|
7
|
+
color: '#fff',
|
|
8
|
+
cursor: 'pointer',
|
|
9
|
+
boxShadow: '0 8px 18px rgba(0,0,0,0.16)'
|
|
10
|
+
};
|
|
11
|
+
const bubbleStyle = {
|
|
12
|
+
borderRadius: '18px',
|
|
13
|
+
padding: '12px 14px',
|
|
14
|
+
marginBottom: '10px',
|
|
15
|
+
lineHeight: 1.5,
|
|
16
|
+
maxWidth: '100%'
|
|
17
|
+
};
|
|
18
|
+
function renderMessage(message, primaryColor, accentColor) {
|
|
19
|
+
const isUser = message.role === 'user';
|
|
20
|
+
const containerStyle = {
|
|
21
|
+
display: 'flex',
|
|
22
|
+
justifyContent: isUser ? 'flex-end' : 'flex-start'
|
|
23
|
+
};
|
|
24
|
+
return (_jsx("div", { style: containerStyle, children: _jsx("div", { style: {
|
|
25
|
+
...bubbleStyle,
|
|
26
|
+
background: isUser ? primaryColor : accentColor,
|
|
27
|
+
color: '#fff',
|
|
28
|
+
borderRadius: isUser ? '18px 18px 6px 18px' : '18px 18px 18px 6px'
|
|
29
|
+
}, children: message.text }) }, message.id));
|
|
30
|
+
}
|
|
31
|
+
export function ChatWidget({ sdk, position = 'bottom-right', primaryColor = '#3B82F6', accentColor = '#111827', backgroundColor = '#F8FAFC', buttonText = 'Support', placeholder = 'Ask a question...', width = '340px', height = '480px', design = 'support' }) {
|
|
32
|
+
const [open, setOpen] = useState(false);
|
|
33
|
+
const [input, setInput] = useState('');
|
|
34
|
+
const { messages, loading, error, sendMessage } = useBrainboxChat(sdk);
|
|
35
|
+
const endRef = useRef(null);
|
|
36
|
+
const positionStyle = useMemo(() => {
|
|
37
|
+
const base = {
|
|
38
|
+
position: 'fixed',
|
|
39
|
+
zIndex: 9999,
|
|
40
|
+
maxWidth: width,
|
|
41
|
+
width
|
|
42
|
+
};
|
|
43
|
+
if (position.includes('bottom'))
|
|
44
|
+
base.bottom = '20px';
|
|
45
|
+
if (position.includes('top'))
|
|
46
|
+
base.top = '20px';
|
|
47
|
+
if (position.includes('right'))
|
|
48
|
+
base.right = '20px';
|
|
49
|
+
if (position.includes('left'))
|
|
50
|
+
base.left = '20px';
|
|
51
|
+
if (position === 'center') {
|
|
52
|
+
base.left = '50%';
|
|
53
|
+
base.transform = 'translateX(-50%)';
|
|
54
|
+
base.bottom = '20px';
|
|
55
|
+
}
|
|
56
|
+
return base;
|
|
57
|
+
}, [position, width]);
|
|
58
|
+
const handleSend = async () => {
|
|
59
|
+
if (!input.trim())
|
|
60
|
+
return;
|
|
61
|
+
await sendMessage(input.trim());
|
|
62
|
+
setInput('');
|
|
63
|
+
};
|
|
64
|
+
const headerText = design === 'support' ? 'Support Chat' : 'AI Assistant';
|
|
65
|
+
return (_jsxs("div", { style: positionStyle, children: [open && (_jsxs("div", { style: {
|
|
66
|
+
borderRadius: '24px',
|
|
67
|
+
background: '#fff',
|
|
68
|
+
boxShadow: '0 24px 80px rgba(15, 23, 42, 0.12)',
|
|
69
|
+
overflow: 'hidden',
|
|
70
|
+
height
|
|
71
|
+
}, children: [_jsx("div", { style: { background: primaryColor, color: '#fff', padding: '14px 18px' }, children: _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsxs("div", { children: [_jsx("strong", { children: headerText }), _jsx("div", { style: { fontSize: '12px', opacity: 0.85 }, children: "Powered by Brainbox" })] }), _jsx("button", { onClick: () => setOpen(false), style: {
|
|
72
|
+
background: 'transparent',
|
|
73
|
+
border: 'none',
|
|
74
|
+
color: '#fff',
|
|
75
|
+
fontSize: '18px',
|
|
76
|
+
cursor: 'pointer'
|
|
77
|
+
}, children: "\u00D7" })] }) }), _jsxs("div", { style: { padding: '16px', background: backgroundColor, height: `calc(${height} - 132px)`, overflowY: 'auto' }, children: [messages.length === 0 ? (_jsx("div", { style: { color: '#475569', fontSize: '14px' }, children: design === 'support'
|
|
78
|
+
? 'Ask a support question and get an answer in real time.'
|
|
79
|
+
: 'Start chatting with AI instantly.' })) : (messages.map(message => renderMessage(message, primaryColor, accentColor))), _jsx("div", { ref: endRef })] }), _jsxs("div", { style: { padding: '12px 16px', background: '#fff', borderTop: '1px solid #E2E8F0' }, children: [_jsxs("div", { style: { display: 'flex', gap: '8px' }, children: [_jsx("input", { value: input, onChange: event => setInput(event.target.value), onKeyDown: event => event.key === 'Enter' && handleSend(), placeholder: placeholder, style: {
|
|
80
|
+
flex: 1,
|
|
81
|
+
border: '1px solid #CBD5E1',
|
|
82
|
+
borderRadius: '999px',
|
|
83
|
+
padding: '12px 16px',
|
|
84
|
+
outline: 'none'
|
|
85
|
+
} }), _jsx("button", { onClick: handleSend, style: {
|
|
86
|
+
...defaultButtonStyle,
|
|
87
|
+
background: primaryColor,
|
|
88
|
+
padding: '0 18px',
|
|
89
|
+
minWidth: '95px'
|
|
90
|
+
}, children: loading ? 'Sending...' : 'Send' })] }), error && _jsx("div", { style: { marginTop: '10px', color: '#EF4444' }, children: error })] })] })), _jsx("button", { onClick: () => setOpen(!open), style: {
|
|
91
|
+
...defaultButtonStyle,
|
|
92
|
+
background: primaryColor,
|
|
93
|
+
padding: '14px 18px',
|
|
94
|
+
display: 'flex',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
justifyContent: 'center',
|
|
97
|
+
minWidth: '120px'
|
|
98
|
+
}, children: buttonText })] }));
|
|
99
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BrainboxChatResponse } from './types';
|
|
2
|
+
export declare class BrainboxReactSDK {
|
|
3
|
+
private apiUrl;
|
|
4
|
+
private apiKey;
|
|
5
|
+
private tenantId;
|
|
6
|
+
private client;
|
|
7
|
+
constructor(apiUrl: string, apiKey: string, tenantId: string);
|
|
8
|
+
ingest(sourceType: string, content: string, filePath?: string, metadata?: Record<string, any>): Promise<any>;
|
|
9
|
+
chat(question: string, sessionId?: string): Promise<BrainboxChatResponse>;
|
|
10
|
+
streamChat(question: string, sessionId: string | undefined, onChunk: (chunk: string) => void, onComplete?: (result: any) => void, onError?: (error: Error) => void): Promise<void>;
|
|
11
|
+
createChatSession(title?: string): Promise<any>;
|
|
12
|
+
healthCheck(): Promise<any>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export class BrainboxReactSDK {
|
|
3
|
+
constructor(apiUrl, apiKey, tenantId) {
|
|
4
|
+
this.apiUrl = apiUrl.replace(/\/$/, '');
|
|
5
|
+
this.apiKey = apiKey;
|
|
6
|
+
this.tenantId = tenantId;
|
|
7
|
+
this.client = axios.create({
|
|
8
|
+
baseURL: this.apiUrl,
|
|
9
|
+
headers: {
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
Authorization: `Bearer ${apiKey}`
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async ingest(sourceType, content, filePath, metadata) {
|
|
16
|
+
const payload = {
|
|
17
|
+
tenant_id: this.tenantId,
|
|
18
|
+
source_type: sourceType,
|
|
19
|
+
content,
|
|
20
|
+
file_path: filePath,
|
|
21
|
+
metadata: metadata || {}
|
|
22
|
+
};
|
|
23
|
+
const response = await this.client.post('/api/ingest', payload);
|
|
24
|
+
return response.data;
|
|
25
|
+
}
|
|
26
|
+
async chat(question, sessionId) {
|
|
27
|
+
const payload = {
|
|
28
|
+
tenant_id: this.tenantId,
|
|
29
|
+
question,
|
|
30
|
+
session_id: sessionId
|
|
31
|
+
};
|
|
32
|
+
const response = await this.client.post('/api/chat', payload);
|
|
33
|
+
return response.data;
|
|
34
|
+
}
|
|
35
|
+
async streamChat(question, sessionId, onChunk, onComplete, onError) {
|
|
36
|
+
const payload = {
|
|
37
|
+
tenant_id: this.tenantId,
|
|
38
|
+
question,
|
|
39
|
+
session_id: sessionId
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
if (typeof fetch !== 'undefined') {
|
|
43
|
+
const response = await fetch(`${this.apiUrl}/api/chat/stream`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(payload)
|
|
50
|
+
});
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw new Error(`Stream request failed: ${response.status}`);
|
|
53
|
+
}
|
|
54
|
+
if (!response.body || typeof response.body.getReader !== 'function') {
|
|
55
|
+
const text = await response.text();
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = JSON.parse(text);
|
|
59
|
+
}
|
|
60
|
+
catch (_a) {
|
|
61
|
+
parsed = { response: text };
|
|
62
|
+
}
|
|
63
|
+
onChunk(parsed.response || text || '');
|
|
64
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete(parsed);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const reader = response.body.getReader();
|
|
68
|
+
const decoder = new TextDecoder('utf-8');
|
|
69
|
+
while (true) {
|
|
70
|
+
const { value, done } = await reader.read();
|
|
71
|
+
if (done)
|
|
72
|
+
break;
|
|
73
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
74
|
+
onChunk(chunk);
|
|
75
|
+
}
|
|
76
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete({ success: true });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const result = await this.chat(question, sessionId);
|
|
80
|
+
onChunk(result.response || JSON.stringify(result));
|
|
81
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete(result);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
const message = (error === null || error === void 0 ? void 0 : error.message) || 'Unknown stream error';
|
|
85
|
+
onError === null || onError === void 0 ? void 0 : onError(new Error(message));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async createChatSession(title) {
|
|
89
|
+
const payload = {
|
|
90
|
+
tenant_id: this.tenantId,
|
|
91
|
+
title: title || 'New Session'
|
|
92
|
+
};
|
|
93
|
+
const response = await this.client.post('/api/chat/session', payload);
|
|
94
|
+
return response.data;
|
|
95
|
+
}
|
|
96
|
+
async healthCheck() {
|
|
97
|
+
const response = await this.client.get('/api/health');
|
|
98
|
+
return response.data;
|
|
99
|
+
}
|
|
100
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BrainboxReactSDK } from './brainbox-sdk';
|
|
2
|
+
export { useBrainboxChat } from './useBrainboxChat';
|
|
3
|
+
export { ChatWidget } from './ChatWidget';
|
|
4
|
+
export { ChatPanel } from './ChatPanel';
|
|
5
|
+
export type { ChatMessage, ChatWidgetProps, ChatPanelProps } from './types';
|
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { BrainboxReactSDK } from './brainbox-sdk';
|
|
2
|
+
export type ChatRole = 'user' | 'assistant' | 'system';
|
|
3
|
+
export interface ChatMessage {
|
|
4
|
+
id: string;
|
|
5
|
+
role: ChatRole;
|
|
6
|
+
text: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ChatWidgetProps {
|
|
10
|
+
sdk: BrainboxReactSDK;
|
|
11
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'center';
|
|
12
|
+
primaryColor?: string;
|
|
13
|
+
accentColor?: string;
|
|
14
|
+
backgroundColor?: string;
|
|
15
|
+
buttonText?: string;
|
|
16
|
+
placeholder?: string;
|
|
17
|
+
width?: string;
|
|
18
|
+
height?: string;
|
|
19
|
+
design?: 'support' | 'assistant';
|
|
20
|
+
}
|
|
21
|
+
export interface ChatPanelProps {
|
|
22
|
+
sdk: BrainboxReactSDK;
|
|
23
|
+
primaryColor?: string;
|
|
24
|
+
accentColor?: string;
|
|
25
|
+
backgroundColor?: string;
|
|
26
|
+
headerText?: string;
|
|
27
|
+
sidebarTitle?: string;
|
|
28
|
+
initialSessionId?: string;
|
|
29
|
+
design?: 'cloud' | 'classic';
|
|
30
|
+
}
|
|
31
|
+
export interface UseBrainboxChatHook {
|
|
32
|
+
messages: ChatMessage[];
|
|
33
|
+
loading: boolean;
|
|
34
|
+
error: string | null;
|
|
35
|
+
sessionId: string | null;
|
|
36
|
+
sendMessage: (text: string) => Promise<void>;
|
|
37
|
+
sendVoiceNote: (note: string) => Promise<void>;
|
|
38
|
+
createSession: (title?: string) => Promise<void>;
|
|
39
|
+
reset: () => void;
|
|
40
|
+
}
|
|
41
|
+
export interface BrainboxChatResponse {
|
|
42
|
+
response: string;
|
|
43
|
+
session_id?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ChatSessionPayload {
|
|
46
|
+
tenant_id: string;
|
|
47
|
+
title?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface ChatPayload {
|
|
50
|
+
tenant_id: string;
|
|
51
|
+
question: string;
|
|
52
|
+
session_id?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface IngestPayload {
|
|
55
|
+
tenant_id: string;
|
|
56
|
+
source_type: string;
|
|
57
|
+
content: string;
|
|
58
|
+
file_path?: string;
|
|
59
|
+
metadata?: Record<string, any>;
|
|
60
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
const createMessage = (role, text) => ({
|
|
3
|
+
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
4
|
+
role,
|
|
5
|
+
text,
|
|
6
|
+
timestamp: new Date().toISOString()
|
|
7
|
+
});
|
|
8
|
+
export function useBrainboxChat(sdk) {
|
|
9
|
+
const [messages, setMessages] = useState([]);
|
|
10
|
+
const [loading, setLoading] = useState(false);
|
|
11
|
+
const [error, setError] = useState(null);
|
|
12
|
+
const [sessionId, setSessionId] = useState(null);
|
|
13
|
+
const appendMessage = useCallback((message) => {
|
|
14
|
+
setMessages(current => [...current, message]);
|
|
15
|
+
}, []);
|
|
16
|
+
const sendMessage = useCallback(async (text) => {
|
|
17
|
+
setError(null);
|
|
18
|
+
const userMessage = createMessage('user', text);
|
|
19
|
+
appendMessage(userMessage);
|
|
20
|
+
setLoading(true);
|
|
21
|
+
try {
|
|
22
|
+
await sdk.streamChat(text, sessionId || undefined, chunk => {
|
|
23
|
+
setMessages(current => {
|
|
24
|
+
const existing = current[current.length - 1];
|
|
25
|
+
if (existing && existing.role === 'assistant') {
|
|
26
|
+
return [
|
|
27
|
+
...current.slice(0, -1),
|
|
28
|
+
{
|
|
29
|
+
...existing,
|
|
30
|
+
text: existing.text + chunk
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
return [...current, createMessage('assistant', chunk)];
|
|
35
|
+
});
|
|
36
|
+
}, result => {
|
|
37
|
+
if (result === null || result === void 0 ? void 0 : result.session_id) {
|
|
38
|
+
setSessionId(result.session_id);
|
|
39
|
+
}
|
|
40
|
+
setLoading(false);
|
|
41
|
+
}, err => {
|
|
42
|
+
setError(err.message);
|
|
43
|
+
setLoading(false);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
setError((err === null || err === void 0 ? void 0 : err.message) || 'Chat failed');
|
|
48
|
+
setLoading(false);
|
|
49
|
+
}
|
|
50
|
+
}, [appendMessage, messages, sdk, sessionId]);
|
|
51
|
+
const sendVoiceNote = useCallback(async (note) => {
|
|
52
|
+
setError(null);
|
|
53
|
+
const voiceMessage = createMessage('user', `Voice note: ${note}`);
|
|
54
|
+
appendMessage(voiceMessage);
|
|
55
|
+
await sendMessage(`Voice note: ${note}`);
|
|
56
|
+
}, [appendMessage, sendMessage]);
|
|
57
|
+
const createSession = useCallback(async (title) => {
|
|
58
|
+
setError(null);
|
|
59
|
+
try {
|
|
60
|
+
const response = await sdk.createChatSession(title);
|
|
61
|
+
if (response === null || response === void 0 ? void 0 : response.session_id) {
|
|
62
|
+
setSessionId(response.session_id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
setError((err === null || err === void 0 ? void 0 : err.message) || 'Unable to create session');
|
|
67
|
+
}
|
|
68
|
+
}, [sdk]);
|
|
69
|
+
const reset = useCallback(() => {
|
|
70
|
+
setMessages([]);
|
|
71
|
+
setError(null);
|
|
72
|
+
setLoading(false);
|
|
73
|
+
setSessionId(null);
|
|
74
|
+
}, []);
|
|
75
|
+
return {
|
|
76
|
+
messages,
|
|
77
|
+
loading,
|
|
78
|
+
error,
|
|
79
|
+
sessionId,
|
|
80
|
+
sendMessage,
|
|
81
|
+
sendVoiceNote,
|
|
82
|
+
createSession,
|
|
83
|
+
reset
|
|
84
|
+
};
|
|
85
|
+
}
|