@syntrologie/adapt-chatbot 0.0.0-semantically-released → 2.0.1
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/dist/ChatAssistant.d.ts +18 -0
- package/dist/ChatAssistant.d.ts.map +1 -0
- package/dist/ChatAssistant.js +175 -0
- package/dist/actionParser.d.ts +15 -0
- package/dist/actionParser.d.ts.map +1 -0
- package/dist/actionParser.js +52 -0
- package/dist/apiClient.d.ts +23 -0
- package/dist/apiClient.d.ts.map +1 -0
- package/dist/apiClient.js +51 -0
- package/dist/cdn.d.ts +44 -0
- package/dist/cdn.d.ts.map +1 -0
- package/dist/cdn.js +37 -0
- package/dist/editor.d.ts +17 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +147 -0
- package/dist/runtime.d.ts +28 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +31 -0
- package/dist/schema.d.ts +39 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +19 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/useChat.d.ts +25 -0
- package/dist/useChat.d.ts.map +1 -0
- package/dist/useChat.js +106 -0
- package/package.json +6 -6
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - ChatAssistant Component
|
|
3
|
+
*
|
|
4
|
+
* Main React component for the AI chat assistant widget.
|
|
5
|
+
* Renders a message list with auto-scroll, loading indicator, and input form.
|
|
6
|
+
*/
|
|
7
|
+
import type { ChatbotWidgetRuntime, ChatbotConfig } from './types';
|
|
8
|
+
export interface ChatAssistantProps {
|
|
9
|
+
config: ChatbotConfig;
|
|
10
|
+
runtime: ChatbotWidgetRuntime;
|
|
11
|
+
tileId: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function ChatAssistant({ config, runtime, tileId }: ChatAssistantProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare const ChatAssistantMountableWidget: {
|
|
15
|
+
mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void;
|
|
16
|
+
};
|
|
17
|
+
export default ChatAssistant;
|
|
18
|
+
//# sourceMappingURL=ChatAssistant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatAssistant.d.ts","sourceRoot":"","sources":["../src/ChatAssistant.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAe,oBAAoB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA8GhF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,kBAAkB,2CAwE5E;AAMD,eAAO,MAAM,4BAA4B;qBACtB,WAAW,gBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CA4CpE,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Adaptive Chatbot - ChatAssistant Component
|
|
4
|
+
*
|
|
5
|
+
* Main React component for the AI chat assistant widget.
|
|
6
|
+
* Renders a message list with auto-scroll, loading indicator, and input form.
|
|
7
|
+
*/
|
|
8
|
+
import React, { useRef, useEffect } from 'react';
|
|
9
|
+
import { createRoot } from 'react-dom/client';
|
|
10
|
+
import { useChat } from './useChat';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Styles
|
|
13
|
+
// ============================================================================
|
|
14
|
+
const styles = {
|
|
15
|
+
container: {
|
|
16
|
+
display: 'flex',
|
|
17
|
+
flexDirection: 'column',
|
|
18
|
+
height: '100%',
|
|
19
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
20
|
+
fontSize: '14px',
|
|
21
|
+
},
|
|
22
|
+
messageList: {
|
|
23
|
+
flex: 1,
|
|
24
|
+
overflowY: 'auto',
|
|
25
|
+
padding: '12px',
|
|
26
|
+
display: 'flex',
|
|
27
|
+
flexDirection: 'column',
|
|
28
|
+
gap: '8px',
|
|
29
|
+
},
|
|
30
|
+
messageBubble: {
|
|
31
|
+
maxWidth: '85%',
|
|
32
|
+
padding: '8px 12px',
|
|
33
|
+
borderRadius: '12px',
|
|
34
|
+
lineHeight: 1.5,
|
|
35
|
+
wordBreak: 'break-word',
|
|
36
|
+
},
|
|
37
|
+
userMessage: {
|
|
38
|
+
alignSelf: 'flex-end',
|
|
39
|
+
backgroundColor: '#6366f1',
|
|
40
|
+
color: '#ffffff',
|
|
41
|
+
borderBottomRightRadius: '4px',
|
|
42
|
+
},
|
|
43
|
+
assistantMessage: {
|
|
44
|
+
alignSelf: 'flex-start',
|
|
45
|
+
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
|
46
|
+
color: '#e2e8f0',
|
|
47
|
+
borderBottomLeftRadius: '4px',
|
|
48
|
+
},
|
|
49
|
+
loadingDots: {
|
|
50
|
+
alignSelf: 'flex-start',
|
|
51
|
+
padding: '8px 16px',
|
|
52
|
+
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
53
|
+
borderRadius: '12px',
|
|
54
|
+
color: '#94a3b8',
|
|
55
|
+
fontSize: '13px',
|
|
56
|
+
},
|
|
57
|
+
errorBanner: {
|
|
58
|
+
padding: '8px 12px',
|
|
59
|
+
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
|
60
|
+
color: '#ef4444',
|
|
61
|
+
fontSize: '13px',
|
|
62
|
+
borderRadius: '8px',
|
|
63
|
+
margin: '0 12px',
|
|
64
|
+
},
|
|
65
|
+
inputForm: {
|
|
66
|
+
display: 'flex',
|
|
67
|
+
gap: '8px',
|
|
68
|
+
padding: '12px',
|
|
69
|
+
borderTop: '1px solid rgba(255, 255, 255, 0.06)',
|
|
70
|
+
},
|
|
71
|
+
input: {
|
|
72
|
+
flex: 1,
|
|
73
|
+
padding: '8px 12px',
|
|
74
|
+
borderRadius: '8px',
|
|
75
|
+
border: '1px solid rgba(255, 255, 255, 0.1)',
|
|
76
|
+
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
|
77
|
+
color: '#f1f5f9',
|
|
78
|
+
fontSize: '14px',
|
|
79
|
+
outline: 'none',
|
|
80
|
+
fontFamily: 'inherit',
|
|
81
|
+
},
|
|
82
|
+
sendButton: {
|
|
83
|
+
padding: '8px 16px',
|
|
84
|
+
borderRadius: '8px',
|
|
85
|
+
border: 'none',
|
|
86
|
+
backgroundColor: '#6366f1',
|
|
87
|
+
color: '#ffffff',
|
|
88
|
+
fontWeight: 600,
|
|
89
|
+
fontSize: '13px',
|
|
90
|
+
cursor: 'pointer',
|
|
91
|
+
whiteSpace: 'nowrap',
|
|
92
|
+
},
|
|
93
|
+
sendButtonDisabled: {
|
|
94
|
+
opacity: 0.5,
|
|
95
|
+
cursor: 'not-allowed',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// MessageBubble Component
|
|
100
|
+
// ============================================================================
|
|
101
|
+
function MessageBubble({ message }) {
|
|
102
|
+
const isUser = message.role === 'user';
|
|
103
|
+
const bubbleStyle = {
|
|
104
|
+
...styles.messageBubble,
|
|
105
|
+
...(isUser ? styles.userMessage : styles.assistantMessage),
|
|
106
|
+
};
|
|
107
|
+
return _jsx("div", { style: bubbleStyle, children: message.text });
|
|
108
|
+
}
|
|
109
|
+
export function ChatAssistant({ config, runtime, tileId }) {
|
|
110
|
+
const { messages, isLoading, error, sendMessage, clearMessages: _clearMessages, } = useChat({
|
|
111
|
+
backendUrl: config.backendUrl,
|
|
112
|
+
tileId,
|
|
113
|
+
runtime,
|
|
114
|
+
greeting: config.greeting,
|
|
115
|
+
maxHistory: config.maxHistory,
|
|
116
|
+
mlflowRunId: config.mlflowRunId,
|
|
117
|
+
});
|
|
118
|
+
const messageListRef = useRef(null);
|
|
119
|
+
const inputRef = useRef(null);
|
|
120
|
+
// Auto-scroll to bottom on new messages
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (messageListRef.current) {
|
|
123
|
+
messageListRef.current.scrollTop = messageListRef.current.scrollHeight;
|
|
124
|
+
}
|
|
125
|
+
}, [messages, isLoading]);
|
|
126
|
+
const handleSubmit = (e) => {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
const input = inputRef.current;
|
|
129
|
+
if (!input || !input.value.trim() || isLoading)
|
|
130
|
+
return;
|
|
131
|
+
const text = input.value;
|
|
132
|
+
input.value = '';
|
|
133
|
+
sendMessage(text);
|
|
134
|
+
};
|
|
135
|
+
return (_jsxs("div", { style: styles.container, "data-testid": "chat-assistant", children: [_jsxs("div", { ref: messageListRef, style: styles.messageList, children: [messages.map((msg) => (_jsx(MessageBubble, { message: msg }, msg.id))), isLoading && _jsx("div", { style: styles.loadingDots, children: "Thinking..." })] }), error && _jsx("div", { style: styles.errorBanner, children: error }), _jsxs("form", { onSubmit: handleSubmit, style: styles.inputForm, children: [_jsx("input", { ref: inputRef, style: styles.input, placeholder: "Ask anything...", disabled: isLoading, "data-testid": "chat-input" }), _jsx("button", { type: "submit", disabled: isLoading, style: {
|
|
136
|
+
...styles.sendButton,
|
|
137
|
+
...(isLoading ? styles.sendButtonDisabled : {}),
|
|
138
|
+
}, "data-testid": "chat-send", children: "Send" })] })] }));
|
|
139
|
+
}
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// Mountable Widget Interface
|
|
142
|
+
// ============================================================================
|
|
143
|
+
export const ChatAssistantMountableWidget = {
|
|
144
|
+
mount(container, mountConfig) {
|
|
145
|
+
const { config, runtime, tileId = 'chatbot-widget', } = (mountConfig || {});
|
|
146
|
+
// React rendering when config + runtime + ReactDOM are available
|
|
147
|
+
if (config && runtime && typeof createRoot === 'function') {
|
|
148
|
+
const root = createRoot(container);
|
|
149
|
+
root.render(React.createElement(ChatAssistant, {
|
|
150
|
+
config,
|
|
151
|
+
runtime,
|
|
152
|
+
tileId,
|
|
153
|
+
}));
|
|
154
|
+
return () => { root.unmount(); };
|
|
155
|
+
}
|
|
156
|
+
if (!config || !runtime) {
|
|
157
|
+
container.innerHTML =
|
|
158
|
+
'<div style="padding: 16px; color: #94a3b8;">Chat widget requires config and runtime.</div>';
|
|
159
|
+
return () => {
|
|
160
|
+
container.innerHTML = '';
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
// HTML fallback
|
|
164
|
+
container.innerHTML = `
|
|
165
|
+
<div style="padding: 16px; font-family: system-ui; color: #e2e8f0;">
|
|
166
|
+
<p style="margin: 0 0 8px; color: #94a3b8;">${config.greeting || 'Hi! How can I help?'}</p>
|
|
167
|
+
<p style="margin: 0; font-size: 12px; color: #64748b;">Chat widget mounted. Awaiting React renderer.</p>
|
|
168
|
+
</div>
|
|
169
|
+
`;
|
|
170
|
+
return () => {
|
|
171
|
+
container.innerHTML = '';
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
export default ChatAssistant;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Action Parser
|
|
3
|
+
*
|
|
4
|
+
* Extracts JSON action blocks from LLM markdown responses.
|
|
5
|
+
* Actions are identified by having a `kind` field in a ```json code fence.
|
|
6
|
+
*/
|
|
7
|
+
import type { ParsedResponse } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* Parse an LLM response, extracting action blocks from JSON code fences.
|
|
10
|
+
*
|
|
11
|
+
* - Blocks with a `kind` field are treated as actions and removed from display text.
|
|
12
|
+
* - Blocks without `kind` or with invalid JSON are left in the display text.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseActions(input: string): ParsedResponse;
|
|
15
|
+
//# sourceMappingURL=actionParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionParser.d.ts","sourceRoot":"","sources":["../src/actionParser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,SAAS,CAAC;AAI3D;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CA8C1D"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Action Parser
|
|
3
|
+
*
|
|
4
|
+
* Extracts JSON action blocks from LLM markdown responses.
|
|
5
|
+
* Actions are identified by having a `kind` field in a ```json code fence.
|
|
6
|
+
*/
|
|
7
|
+
const JSON_FENCE_RE = /```json\s*\n([\s\S]*?)```/g;
|
|
8
|
+
/**
|
|
9
|
+
* Parse an LLM response, extracting action blocks from JSON code fences.
|
|
10
|
+
*
|
|
11
|
+
* - Blocks with a `kind` field are treated as actions and removed from display text.
|
|
12
|
+
* - Blocks without `kind` or with invalid JSON are left in the display text.
|
|
13
|
+
*/
|
|
14
|
+
export function parseActions(input) {
|
|
15
|
+
const actions = [];
|
|
16
|
+
let displayText = input;
|
|
17
|
+
// Collect matches in reverse order so we can splice display text without index shifting
|
|
18
|
+
const matches = [];
|
|
19
|
+
let match;
|
|
20
|
+
// Reset regex state
|
|
21
|
+
JSON_FENCE_RE.lastIndex = 0;
|
|
22
|
+
while ((match = JSON_FENCE_RE.exec(input)) !== null) {
|
|
23
|
+
matches.push({
|
|
24
|
+
fullMatch: match[0],
|
|
25
|
+
json: match[1],
|
|
26
|
+
index: match.index,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// Process in reverse to preserve indices when removing from displayText
|
|
30
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
31
|
+
const { fullMatch, json } = matches[i];
|
|
32
|
+
let parsed;
|
|
33
|
+
try {
|
|
34
|
+
parsed = JSON.parse(json);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Malformed JSON — leave in display text
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (typeof parsed === 'object' &&
|
|
41
|
+
parsed !== null &&
|
|
42
|
+
'kind' in parsed &&
|
|
43
|
+
typeof parsed.kind === 'string') {
|
|
44
|
+
actions.unshift(parsed);
|
|
45
|
+
// Remove the action block from display text
|
|
46
|
+
displayText = displayText.replace(fullMatch, '');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Clean up extra blank lines left from removed blocks
|
|
50
|
+
displayText = displayText.replace(/\n{3,}/g, '\n\n').trim();
|
|
51
|
+
return { displayText, actions };
|
|
52
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - API Client
|
|
3
|
+
*
|
|
4
|
+
* Auth-aware fetch client that reads Stytch JWT from cookies
|
|
5
|
+
* and workspace ID from localStorage.
|
|
6
|
+
*/
|
|
7
|
+
import type { ChatApiRequest, ChatApiResponse } from './types';
|
|
8
|
+
export declare class AuthError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Read auth credentials from browser cookie and localStorage.
|
|
13
|
+
* Throws AuthError if credentials are missing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getAuthHeaders(): {
|
|
16
|
+
Authorization: string;
|
|
17
|
+
'X-Workspace-Id': string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Send a chat message to the backend.
|
|
21
|
+
*/
|
|
22
|
+
export declare function sendMessage(url: string, request: ChatApiRequest): Promise<ChatApiResponse>;
|
|
23
|
+
//# sourceMappingURL=apiClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/D,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAepF;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAoBhG"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - API Client
|
|
3
|
+
*
|
|
4
|
+
* Auth-aware fetch client that reads Stytch JWT from cookies
|
|
5
|
+
* and workspace ID from localStorage.
|
|
6
|
+
*/
|
|
7
|
+
export class AuthError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'AuthError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Read auth credentials from browser cookie and localStorage.
|
|
15
|
+
* Throws AuthError if credentials are missing.
|
|
16
|
+
*/
|
|
17
|
+
export function getAuthHeaders() {
|
|
18
|
+
const cookieMatch = document.cookie.match(/stytch_session_jwt=([^;]*)/);
|
|
19
|
+
if (!cookieMatch || !cookieMatch[1]) {
|
|
20
|
+
throw new AuthError('No authentication token found');
|
|
21
|
+
}
|
|
22
|
+
const workspaceId = localStorage.getItem('syntrologie_workspace_id');
|
|
23
|
+
if (!workspaceId) {
|
|
24
|
+
throw new AuthError('No workspace ID found');
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
Authorization: `Bearer ${cookieMatch[1]}`,
|
|
28
|
+
'X-Workspace-Id': workspaceId,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Send a chat message to the backend.
|
|
33
|
+
*/
|
|
34
|
+
export async function sendMessage(url, request) {
|
|
35
|
+
const authHeaders = getAuthHeaders();
|
|
36
|
+
const response = await fetch(url, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
...authHeaders,
|
|
41
|
+
},
|
|
42
|
+
body: JSON.stringify(request),
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
if (response.status === 401) {
|
|
46
|
+
throw new AuthError('Session expired or unauthorized');
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`Chat request failed: ${response.status} ${response.statusText}`);
|
|
49
|
+
}
|
|
50
|
+
return response.json();
|
|
51
|
+
}
|
package/dist/cdn.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDN Entry Point for Adaptive Chatbot
|
|
3
|
+
*
|
|
4
|
+
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
|
+
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
|
+
*/
|
|
7
|
+
import ChatbotEditor from './editor';
|
|
8
|
+
/**
|
|
9
|
+
* App manifest for registry registration.
|
|
10
|
+
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
11
|
+
*/
|
|
12
|
+
export declare const manifest: {
|
|
13
|
+
id: string;
|
|
14
|
+
version: string;
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
runtime: {
|
|
18
|
+
actions: never[];
|
|
19
|
+
widgets: {
|
|
20
|
+
id: string;
|
|
21
|
+
component: {
|
|
22
|
+
mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void;
|
|
23
|
+
};
|
|
24
|
+
metadata: {
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
icon: string;
|
|
28
|
+
};
|
|
29
|
+
}[];
|
|
30
|
+
};
|
|
31
|
+
editor: {
|
|
32
|
+
component: typeof ChatbotEditor;
|
|
33
|
+
panel: {
|
|
34
|
+
title: string;
|
|
35
|
+
icon: string;
|
|
36
|
+
description: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
metadata: {
|
|
40
|
+
isBuiltIn: boolean;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export default manifest;
|
|
44
|
+
//# sourceMappingURL=cdn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,aAA8B,MAAM,UAAU,CAAC;AAGtD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAapB,CAAC;AAaF,eAAe,QAAQ,CAAC"}
|
package/dist/cdn.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDN Entry Point for Adaptive Chatbot
|
|
3
|
+
*
|
|
4
|
+
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
|
+
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
|
+
*/
|
|
7
|
+
import ChatbotEditor, { editorPanel } from './editor';
|
|
8
|
+
import { runtime } from './runtime';
|
|
9
|
+
/**
|
|
10
|
+
* App manifest for registry registration.
|
|
11
|
+
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
12
|
+
*/
|
|
13
|
+
export const manifest = {
|
|
14
|
+
id: 'adaptive-chatbot',
|
|
15
|
+
version: runtime.version,
|
|
16
|
+
name: runtime.name,
|
|
17
|
+
description: runtime.description,
|
|
18
|
+
runtime: {
|
|
19
|
+
actions: [],
|
|
20
|
+
widgets: runtime.widgets,
|
|
21
|
+
},
|
|
22
|
+
editor: { component: ChatbotEditor, panel: editorPanel },
|
|
23
|
+
metadata: {
|
|
24
|
+
isBuiltIn: false,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Self-register with global registry if available.
|
|
29
|
+
* This happens when loaded via script tag (UMD).
|
|
30
|
+
*/
|
|
31
|
+
if (typeof window !== 'undefined') {
|
|
32
|
+
const registry = window.SynOS?.appRegistry;
|
|
33
|
+
if (registry && typeof registry.register === 'function') {
|
|
34
|
+
registry.register(manifest);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export default manifest;
|
package/dist/editor.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Editor Component
|
|
3
|
+
*
|
|
4
|
+
* Visual editor panel for configuring chatbot tile props.
|
|
5
|
+
*/
|
|
6
|
+
import type { EditorPanelProps } from './types';
|
|
7
|
+
export declare function ChatbotEditor({ config, onChange, editor }: EditorPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
* Editor panel configuration for the app registry.
|
|
10
|
+
*/
|
|
11
|
+
export declare const editorPanel: {
|
|
12
|
+
title: string;
|
|
13
|
+
icon: string;
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
export default ChatbotEditor;
|
|
17
|
+
//# sourceMappingURL=editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAiB,MAAM,SAAS,CAAC;AAwH/D,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAkG3E;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
package/dist/editor.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Adaptive Chatbot - Editor Component
|
|
4
|
+
*
|
|
5
|
+
* Visual editor panel for configuring chatbot tile props.
|
|
6
|
+
*/
|
|
7
|
+
import { useEffect, useRef } from 'react';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Editor Styles
|
|
10
|
+
// ============================================================================
|
|
11
|
+
const styles = {
|
|
12
|
+
container: {
|
|
13
|
+
display: 'flex',
|
|
14
|
+
flexDirection: 'column',
|
|
15
|
+
height: '100%',
|
|
16
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
17
|
+
},
|
|
18
|
+
header: {
|
|
19
|
+
padding: '16px',
|
|
20
|
+
borderBottom: '1px solid #334155',
|
|
21
|
+
display: 'flex',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
gap: '12px',
|
|
24
|
+
},
|
|
25
|
+
backButton: {
|
|
26
|
+
padding: '6px 12px',
|
|
27
|
+
borderRadius: '6px',
|
|
28
|
+
border: 'none',
|
|
29
|
+
backgroundColor: 'rgba(255,255,255,0.05)',
|
|
30
|
+
color: '#94a3b8',
|
|
31
|
+
fontSize: '13px',
|
|
32
|
+
cursor: 'pointer',
|
|
33
|
+
},
|
|
34
|
+
title: {
|
|
35
|
+
margin: 0,
|
|
36
|
+
fontSize: '15px',
|
|
37
|
+
fontWeight: 600,
|
|
38
|
+
color: '#f8fafc',
|
|
39
|
+
},
|
|
40
|
+
subtitle: {
|
|
41
|
+
margin: '2px 0 0 0',
|
|
42
|
+
fontSize: '11px',
|
|
43
|
+
color: '#64748b',
|
|
44
|
+
},
|
|
45
|
+
content: {
|
|
46
|
+
flex: 1,
|
|
47
|
+
overflow: 'auto',
|
|
48
|
+
padding: '16px',
|
|
49
|
+
},
|
|
50
|
+
section: {
|
|
51
|
+
marginBottom: '24px',
|
|
52
|
+
},
|
|
53
|
+
sectionTitle: {
|
|
54
|
+
fontSize: '12px',
|
|
55
|
+
fontWeight: 600,
|
|
56
|
+
color: '#94a3b8',
|
|
57
|
+
textTransform: 'uppercase',
|
|
58
|
+
letterSpacing: '0.05em',
|
|
59
|
+
marginBottom: '12px',
|
|
60
|
+
},
|
|
61
|
+
inputGroup: {
|
|
62
|
+
marginBottom: '12px',
|
|
63
|
+
},
|
|
64
|
+
inputLabel: {
|
|
65
|
+
display: 'block',
|
|
66
|
+
fontSize: '11px',
|
|
67
|
+
color: '#94a3b8',
|
|
68
|
+
marginBottom: '4px',
|
|
69
|
+
},
|
|
70
|
+
input: {
|
|
71
|
+
width: '100%',
|
|
72
|
+
padding: '10px 12px',
|
|
73
|
+
borderRadius: '6px',
|
|
74
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
75
|
+
backgroundColor: 'rgba(255,255,255,0.05)',
|
|
76
|
+
color: '#f8fafc',
|
|
77
|
+
fontSize: '13px',
|
|
78
|
+
},
|
|
79
|
+
textarea: {
|
|
80
|
+
width: '100%',
|
|
81
|
+
padding: '10px 12px',
|
|
82
|
+
borderRadius: '6px',
|
|
83
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
84
|
+
backgroundColor: 'rgba(255,255,255,0.05)',
|
|
85
|
+
color: '#f8fafc',
|
|
86
|
+
fontSize: '13px',
|
|
87
|
+
minHeight: '60px',
|
|
88
|
+
resize: 'vertical',
|
|
89
|
+
fontFamily: 'inherit',
|
|
90
|
+
},
|
|
91
|
+
footer: {
|
|
92
|
+
padding: '12px 16px',
|
|
93
|
+
borderTop: '1px solid #334155',
|
|
94
|
+
display: 'flex',
|
|
95
|
+
gap: '8px',
|
|
96
|
+
},
|
|
97
|
+
saveButton: {
|
|
98
|
+
flex: 1,
|
|
99
|
+
padding: '10px',
|
|
100
|
+
borderRadius: '8px',
|
|
101
|
+
border: 'none',
|
|
102
|
+
background: 'rgba(59, 130, 246, 0.15)',
|
|
103
|
+
color: '#3b82f6',
|
|
104
|
+
fontSize: '13px',
|
|
105
|
+
fontWeight: 600,
|
|
106
|
+
cursor: 'pointer',
|
|
107
|
+
},
|
|
108
|
+
publishButton: {
|
|
109
|
+
flex: 1,
|
|
110
|
+
padding: '10px',
|
|
111
|
+
borderRadius: '8px',
|
|
112
|
+
border: 'none',
|
|
113
|
+
background: '#22c55e',
|
|
114
|
+
color: 'white',
|
|
115
|
+
fontSize: '13px',
|
|
116
|
+
fontWeight: 600,
|
|
117
|
+
cursor: 'pointer',
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
// ============================================================================
|
|
121
|
+
// ChatbotEditor Component
|
|
122
|
+
// ============================================================================
|
|
123
|
+
export function ChatbotEditor({ config, onChange, editor }) {
|
|
124
|
+
const typedConfig = config;
|
|
125
|
+
// Consume initial navigation payload on mount (chatbot is form-based, no sub-items)
|
|
126
|
+
const initialConsumed = useRef(false);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (!initialConsumed.current && (editor.initialEditKey != null || editor.initialCreate)) {
|
|
129
|
+
initialConsumed.current = true;
|
|
130
|
+
editor.clearInitialState?.();
|
|
131
|
+
}
|
|
132
|
+
}, [editor]);
|
|
133
|
+
const handleChange = (field, value) => {
|
|
134
|
+
onChange({ ...config, [field]: value });
|
|
135
|
+
editor.setDirty(true);
|
|
136
|
+
};
|
|
137
|
+
return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsx("button", { onClick: () => editor.navigateHome(), style: styles.backButton, children: "\u2190 Back" }), _jsxs("div", { children: [_jsx("h2", { style: styles.title, children: "Chat Assistant" }), _jsx("p", { style: styles.subtitle, children: "Configure AI chat assistant" })] })] }), _jsxs("div", { style: styles.content, children: [_jsxs("div", { style: styles.section, children: [_jsx("div", { style: styles.sectionTitle, children: "API Configuration" }), _jsxs("div", { style: styles.inputGroup, children: [_jsx("label", { style: styles.inputLabel, children: "Backend URL" }), _jsx("input", { type: "text", value: typedConfig.backendUrl || '', onChange: (e) => handleChange('backendUrl', e.target.value), style: styles.input, placeholder: "/api/chat/message" })] }), _jsxs("div", { style: styles.inputGroup, children: [_jsx("label", { style: styles.inputLabel, children: "MLflow Run ID (optional)" }), _jsx("input", { type: "text", value: typedConfig.mlflowRunId || '', onChange: (e) => handleChange('mlflowRunId', e.target.value), style: styles.input, placeholder: "e.g., abc123" })] })] }), _jsxs("div", { style: styles.section, children: [_jsx("div", { style: styles.sectionTitle, children: "Chat Settings" }), _jsxs("div", { style: styles.inputGroup, children: [_jsx("label", { style: styles.inputLabel, children: "Greeting Message" }), _jsx("textarea", { value: typedConfig.greeting || '', onChange: (e) => handleChange('greeting', e.target.value), style: styles.textarea, placeholder: "Hi! How can I help?" })] }), _jsxs("div", { style: styles.inputGroup, children: [_jsx("label", { style: styles.inputLabel, children: "Max History (messages sent to backend)" }), _jsx("input", { type: "number", value: typedConfig.maxHistory || 20, onChange: (e) => handleChange('maxHistory', parseInt(e.target.value, 10)), style: styles.input, min: 1, max: 100 })] })] })] }), _jsxs("div", { style: styles.footer, children: [_jsx("button", { onClick: () => editor.save(), style: styles.saveButton, children: "Save Draft" }), _jsx("button", { onClick: () => editor.publish(), style: styles.publishButton, children: "Publish" })] })] }));
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Editor panel configuration for the app registry.
|
|
141
|
+
*/
|
|
142
|
+
export const editorPanel = {
|
|
143
|
+
title: 'Chat Assistant',
|
|
144
|
+
icon: '💬',
|
|
145
|
+
description: 'AI chat assistant with action execution',
|
|
146
|
+
};
|
|
147
|
+
export default ChatbotEditor;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Runtime Module
|
|
3
|
+
*
|
|
4
|
+
* Runtime manifest for the AI chat assistant adaptive.
|
|
5
|
+
* This is a widget-based adaptive with no action executors.
|
|
6
|
+
*/
|
|
7
|
+
export declare const runtime: {
|
|
8
|
+
id: string;
|
|
9
|
+
version: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
/** No action executors — chatbot uses existing action kinds via applyBatch */
|
|
13
|
+
executors: never[];
|
|
14
|
+
/** Widget definitions for the runtime's WidgetRegistry */
|
|
15
|
+
widgets: {
|
|
16
|
+
id: string;
|
|
17
|
+
component: {
|
|
18
|
+
mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void;
|
|
19
|
+
};
|
|
20
|
+
metadata: {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
icon: string;
|
|
24
|
+
};
|
|
25
|
+
}[];
|
|
26
|
+
};
|
|
27
|
+
export default runtime;
|
|
28
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,eAAO,MAAM,OAAO;;;;;IAMlB,8EAA8E;;IAG9E,0DAA0D;;;;;;;;;;;;CAY3D,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Runtime Module
|
|
3
|
+
*
|
|
4
|
+
* Runtime manifest for the AI chat assistant adaptive.
|
|
5
|
+
* This is a widget-based adaptive with no action executors.
|
|
6
|
+
*/
|
|
7
|
+
import { ChatAssistantMountableWidget } from './ChatAssistant';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// App Runtime Manifest
|
|
10
|
+
// ============================================================================
|
|
11
|
+
export const runtime = {
|
|
12
|
+
id: 'adaptive-chatbot',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
name: 'Chat Assistant',
|
|
15
|
+
description: 'AI chat assistant with action execution capabilities',
|
|
16
|
+
/** No action executors — chatbot uses existing action kinds via applyBatch */
|
|
17
|
+
executors: [],
|
|
18
|
+
/** Widget definitions for the runtime's WidgetRegistry */
|
|
19
|
+
widgets: [
|
|
20
|
+
{
|
|
21
|
+
id: 'adaptive-chatbot:assistant',
|
|
22
|
+
component: ChatAssistantMountableWidget,
|
|
23
|
+
metadata: {
|
|
24
|
+
name: 'Chat Assistant',
|
|
25
|
+
description: 'AI-powered chat assistant that can execute DOM actions',
|
|
26
|
+
icon: '💬',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
export default runtime;
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Config Schema
|
|
3
|
+
*
|
|
4
|
+
* Zod schema for validating chatbot widget configuration.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
export declare const configSchema: z.ZodObject<{
|
|
8
|
+
/** Backend URL for the chat API endpoint */
|
|
9
|
+
backendUrl: z.ZodString;
|
|
10
|
+
/** MLflow run ID for experiment tracking */
|
|
11
|
+
mlflowRunId: z.ZodOptional<z.ZodString>;
|
|
12
|
+
/** Greeting message shown when the chat opens */
|
|
13
|
+
greeting: z.ZodDefault<z.ZodString>;
|
|
14
|
+
/** Maximum number of history messages sent to the backend */
|
|
15
|
+
maxHistory: z.ZodDefault<z.ZodNumber>;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
backendUrl: string;
|
|
18
|
+
greeting: string;
|
|
19
|
+
maxHistory: number;
|
|
20
|
+
mlflowRunId?: string | undefined;
|
|
21
|
+
}, {
|
|
22
|
+
backendUrl: string;
|
|
23
|
+
greeting?: string | undefined;
|
|
24
|
+
maxHistory?: number | undefined;
|
|
25
|
+
mlflowRunId?: string | undefined;
|
|
26
|
+
}>;
|
|
27
|
+
export type ChatbotConfigSchema = z.infer<typeof configSchema>;
|
|
28
|
+
export declare function validateChatbotConfig(data: unknown): z.SafeParseReturnType<{
|
|
29
|
+
backendUrl: string;
|
|
30
|
+
greeting?: string | undefined;
|
|
31
|
+
maxHistory?: number | undefined;
|
|
32
|
+
mlflowRunId?: string | undefined;
|
|
33
|
+
}, {
|
|
34
|
+
backendUrl: string;
|
|
35
|
+
greeting: string;
|
|
36
|
+
maxHistory: number;
|
|
37
|
+
mlflowRunId?: string | undefined;
|
|
38
|
+
}>;
|
|
39
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,YAAY;IACvB,4CAA4C;;IAE5C,4CAA4C;;IAE5C,iDAAiD;;IAEjD,6DAA6D;;;;;;;;;;;;EAE7D,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAE/D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO;;;;;;;;;;GAElD"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Config Schema
|
|
3
|
+
*
|
|
4
|
+
* Zod schema for validating chatbot widget configuration.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
export const configSchema = z.object({
|
|
8
|
+
/** Backend URL for the chat API endpoint */
|
|
9
|
+
backendUrl: z.string().min(1, 'Backend URL is required'),
|
|
10
|
+
/** MLflow run ID for experiment tracking */
|
|
11
|
+
mlflowRunId: z.string().optional(),
|
|
12
|
+
/** Greeting message shown when the chat opens */
|
|
13
|
+
greeting: z.string().default('Hi! How can I help?'),
|
|
14
|
+
/** Maximum number of history messages sent to the backend */
|
|
15
|
+
maxHistory: z.number().int().min(1).max(100).default(20),
|
|
16
|
+
});
|
|
17
|
+
export function validateChatbotConfig(data) {
|
|
18
|
+
return configSchema.safeParse(data);
|
|
19
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the AI chat assistant adaptive.
|
|
5
|
+
*/
|
|
6
|
+
export type MessageRole = 'user' | 'assistant';
|
|
7
|
+
export interface ChatMessage {
|
|
8
|
+
id: string;
|
|
9
|
+
role: MessageRole;
|
|
10
|
+
text: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ChatApiRequest {
|
|
14
|
+
message: string;
|
|
15
|
+
history: Array<{
|
|
16
|
+
role: MessageRole;
|
|
17
|
+
content: string;
|
|
18
|
+
}>;
|
|
19
|
+
mlflow_run_id?: string;
|
|
20
|
+
current_config?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export interface ChatApiResponse {
|
|
23
|
+
response: string;
|
|
24
|
+
success: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ActionBlock {
|
|
27
|
+
kind: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface ParsedResponse {
|
|
31
|
+
displayText: string;
|
|
32
|
+
actions: ActionBlock[];
|
|
33
|
+
}
|
|
34
|
+
export interface ChatbotConfig {
|
|
35
|
+
backendUrl: string;
|
|
36
|
+
mlflowRunId?: string;
|
|
37
|
+
greeting?: string;
|
|
38
|
+
maxHistory?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface ActionHandle {
|
|
41
|
+
id: string;
|
|
42
|
+
revert: () => Promise<void>;
|
|
43
|
+
isApplied: () => boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface BatchActionHandle {
|
|
46
|
+
id: string;
|
|
47
|
+
handles: ActionHandle[];
|
|
48
|
+
revertAll: () => Promise<void>;
|
|
49
|
+
isApplied: () => boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface ActionEngine {
|
|
52
|
+
apply: (action: unknown) => Promise<ActionHandle>;
|
|
53
|
+
applyBatch: (actions: unknown[]) => Promise<BatchActionHandle>;
|
|
54
|
+
}
|
|
55
|
+
export interface ChatbotWidgetRuntime {
|
|
56
|
+
actions: ActionEngine;
|
|
57
|
+
events: {
|
|
58
|
+
publish: (name: string, props?: Record<string, unknown>) => void;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export interface EditorPanelProps {
|
|
62
|
+
config: Record<string, unknown>;
|
|
63
|
+
onChange: (config: Record<string, unknown>) => void;
|
|
64
|
+
editor: {
|
|
65
|
+
setDirty: (dirty: boolean) => void;
|
|
66
|
+
navigateHome: () => Promise<boolean>;
|
|
67
|
+
save: () => Promise<void>;
|
|
68
|
+
publish: (captureScreenshot?: boolean) => Promise<void>;
|
|
69
|
+
/** Flat action index to open in edit mode (from accordion navigation). */
|
|
70
|
+
initialEditKey?: string;
|
|
71
|
+
/** Open the editor in create mode. */
|
|
72
|
+
initialCreate?: boolean;
|
|
73
|
+
/** Clear the initial navigation state (call after consuming). */
|
|
74
|
+
clearInitialState?: () => void;
|
|
75
|
+
};
|
|
76
|
+
platformClient?: unknown;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - useChat Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook managing chat message state, API communication,
|
|
5
|
+
* history management, and action execution via the runtime ActionEngine.
|
|
6
|
+
*/
|
|
7
|
+
import type { ChatMessage, ChatbotWidgetRuntime } from './types';
|
|
8
|
+
export interface UseChatOptions {
|
|
9
|
+
backendUrl: string;
|
|
10
|
+
tileId: string;
|
|
11
|
+
runtime: ChatbotWidgetRuntime;
|
|
12
|
+
greeting?: string;
|
|
13
|
+
maxHistory?: number;
|
|
14
|
+
mlflowRunId?: string;
|
|
15
|
+
config?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface UseChatReturn {
|
|
18
|
+
messages: ChatMessage[];
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
error: string | null;
|
|
21
|
+
sendMessage: (text: string) => Promise<void>;
|
|
22
|
+
clearMessages: () => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function useChat(options: UseChatOptions): UseChatReturn;
|
|
25
|
+
//# sourceMappingURL=useChat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../src/useChat.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAqB,MAAM,SAAS,CAAC;AAEpF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAOD,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CA4G9D"}
|
package/dist/useChat.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - useChat Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook managing chat message state, API communication,
|
|
5
|
+
* history management, and action execution via the runtime ActionEngine.
|
|
6
|
+
*/
|
|
7
|
+
import { useState, useCallback, useRef } from 'react';
|
|
8
|
+
import { parseActions } from './actionParser';
|
|
9
|
+
import { sendMessage } from './apiClient';
|
|
10
|
+
let nextId = 0;
|
|
11
|
+
function generateId() {
|
|
12
|
+
return `msg-${Date.now()}-${++nextId}`;
|
|
13
|
+
}
|
|
14
|
+
export function useChat(options) {
|
|
15
|
+
const { backendUrl, tileId, runtime, greeting, maxHistory = 20, mlflowRunId, config } = options;
|
|
16
|
+
const [messages, setMessages] = useState(() => {
|
|
17
|
+
if (greeting) {
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
id: generateId(),
|
|
21
|
+
role: 'assistant',
|
|
22
|
+
text: greeting,
|
|
23
|
+
timestamp: Date.now(),
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
return [];
|
|
28
|
+
});
|
|
29
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
30
|
+
const [error, setError] = useState(null);
|
|
31
|
+
const batchHandleRef = useRef(null);
|
|
32
|
+
const send = useCallback(async (text) => {
|
|
33
|
+
const trimmed = text.trim();
|
|
34
|
+
if (!trimmed)
|
|
35
|
+
return;
|
|
36
|
+
setError(null);
|
|
37
|
+
const userMessage = {
|
|
38
|
+
id: generateId(),
|
|
39
|
+
role: 'user',
|
|
40
|
+
text: trimmed,
|
|
41
|
+
timestamp: Date.now(),
|
|
42
|
+
};
|
|
43
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
44
|
+
setIsLoading(true);
|
|
45
|
+
try {
|
|
46
|
+
// Build history from current messages + new user message
|
|
47
|
+
const currentMessages = [...messages, userMessage];
|
|
48
|
+
const historySlice = currentMessages.slice(-maxHistory).map((m) => ({
|
|
49
|
+
role: m.role,
|
|
50
|
+
content: m.text,
|
|
51
|
+
}));
|
|
52
|
+
const response = await sendMessage(backendUrl, {
|
|
53
|
+
message: trimmed,
|
|
54
|
+
history: historySlice,
|
|
55
|
+
mlflow_run_id: mlflowRunId,
|
|
56
|
+
current_config: config,
|
|
57
|
+
});
|
|
58
|
+
// Parse actions from the LLM response
|
|
59
|
+
const { displayText, actions } = parseActions(response.response);
|
|
60
|
+
const assistantMessage = {
|
|
61
|
+
id: generateId(),
|
|
62
|
+
role: 'assistant',
|
|
63
|
+
text: displayText,
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
};
|
|
66
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
67
|
+
// Execute actions if present
|
|
68
|
+
if (actions.length > 0) {
|
|
69
|
+
// Revert previous batch
|
|
70
|
+
if (batchHandleRef.current?.isApplied()) {
|
|
71
|
+
await batchHandleRef.current.revertAll();
|
|
72
|
+
}
|
|
73
|
+
batchHandleRef.current = await runtime.actions.applyBatch(actions);
|
|
74
|
+
runtime.events.publish('chatbot.actions_applied', {
|
|
75
|
+
count: actions.length,
|
|
76
|
+
kinds: actions.map((a) => a.kind),
|
|
77
|
+
tileId,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const message = err instanceof Error ? err.message : 'An unexpected error occurred';
|
|
83
|
+
setError(message);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
setIsLoading(false);
|
|
87
|
+
}
|
|
88
|
+
}, [backendUrl, messages, maxHistory, mlflowRunId, config, runtime, tileId]);
|
|
89
|
+
const clearMessages = useCallback(() => {
|
|
90
|
+
setMessages([]);
|
|
91
|
+
setError(null);
|
|
92
|
+
// Revert any active actions
|
|
93
|
+
if (batchHandleRef.current?.isApplied()) {
|
|
94
|
+
batchHandleRef.current.revertAll();
|
|
95
|
+
batchHandleRef.current = null;
|
|
96
|
+
}
|
|
97
|
+
sessionStorage.removeItem(`syntro:chatbot:history:${tileId}`);
|
|
98
|
+
}, [tileId]);
|
|
99
|
+
return {
|
|
100
|
+
messages,
|
|
101
|
+
isLoading,
|
|
102
|
+
error,
|
|
103
|
+
sendMessage: send,
|
|
104
|
+
clearMessages,
|
|
105
|
+
};
|
|
106
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syntrologie/adapt-chatbot",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Adaptive Chatbot - AI chat assistant widget with action execution",
|
|
5
5
|
"license": "Proprietary",
|
|
6
6
|
"private": false,
|
|
@@ -45,13 +45,13 @@
|
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@testing-library/react": "^16.3.2",
|
|
48
|
-
"@types/react": "^
|
|
48
|
+
"@types/react": "^19.1.0",
|
|
49
49
|
"@types/react-dom": "^19.2.3",
|
|
50
|
-
"jsdom": "^25.0.
|
|
50
|
+
"jsdom": "^25.0.1",
|
|
51
51
|
"react": "^19.2.4",
|
|
52
52
|
"react-dom": "^19.2.4",
|
|
53
|
-
"typescript": "^5.
|
|
54
|
-
"vitest": "^2.
|
|
55
|
-
"zod": "^3.25.
|
|
53
|
+
"typescript": "^5.7.3",
|
|
54
|
+
"vitest": "^2.1.9",
|
|
55
|
+
"zod": "^3.25.76"
|
|
56
56
|
}
|
|
57
57
|
}
|