@makemore/agent-frontend 2.8.1 → 2.9.0
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/chat-widget.cjs.js +378 -253
- package/dist/chat-widget.css +395 -19
- package/dist/chat-widget.esm.js +378 -253
- package/dist/chat-widget.js +342 -217
- package/dist/react.cjs.js +377 -252
- package/dist/react.esm.js +377 -252
- package/package.json +2 -2
- package/src/components/ChatWidget.js +51 -9
- package/src/components/DevToolbar.js +135 -0
- package/src/components/ModelSelector.js +34 -10
- package/src/hooks/useChat.js +17 -2
- package/src/hooks/useModels.js +28 -4
- package/src/hooks/useSystems.js +163 -0
- package/src/index.js +17 -0
- package/src/utils/config.js +7 -0
- package/src/utils/helpers.js +35 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makemore/agent-frontend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "A lightweight chat widget for AI agents. Use as an embeddable script tag or import directly into React/Preact projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/chat-widget.cjs.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"build:dev": "node build.js --dev",
|
|
31
31
|
"build:embed": "esbuild src/index.js --bundle --minify --format=iife --global-name=ChatWidgetModule --outfile=dist/chat-widget.js",
|
|
32
32
|
"watch": "node watch.js",
|
|
33
|
-
"copy": "cp -f dist/chat-widget.js ../django_agent_studio/static/agent-frontend/
|
|
33
|
+
"copy": "mkdir -p ../django_agent_studio/static/agent-frontend && cp -f dist/chat-widget.js dist/chat-widget.css dist/chat-widget-markdown.js ../django_agent_studio/static/agent-frontend/ 2>/dev/null || true && echo 'Copied to django_agent_studio'",
|
|
34
34
|
"prepublishOnly": "npm run build",
|
|
35
35
|
"serve": "python -m http.server 8080"
|
|
36
36
|
},
|
|
@@ -10,13 +10,16 @@ import { InputForm } from './InputForm.js';
|
|
|
10
10
|
import { Sidebar } from './Sidebar.js';
|
|
11
11
|
import { ModelSelector } from './ModelSelector.js';
|
|
12
12
|
import { TaskList } from './TaskList.js';
|
|
13
|
+
import { DevToolbar } from './DevToolbar.js';
|
|
13
14
|
import { useChat } from '../hooks/useChat.js';
|
|
14
15
|
import { useModels } from '../hooks/useModels.js';
|
|
15
16
|
import { useTasks } from '../hooks/useTasks.js';
|
|
17
|
+
import { useSystems } from '../hooks/useSystems.js';
|
|
16
18
|
import { createApiClient } from '../utils/api.js';
|
|
17
|
-
import { createStorage } from '../utils/helpers.js';
|
|
19
|
+
import { createStorage, getContrastingTextColor } from '../utils/helpers.js';
|
|
18
20
|
|
|
19
21
|
export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
22
|
+
console.log('[ChatWidget] Config:', { showConversationSidebar: config.showConversationSidebar, apiPaths: config.apiPaths });
|
|
20
23
|
// UI state
|
|
21
24
|
const [isOpen, setIsOpen] = useState(config.embedded || config.forceOpen === true);
|
|
22
25
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
@@ -54,8 +57,21 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
54
57
|
return createApiClient(config, getState, setState);
|
|
55
58
|
}, [config, authToken, storage]);
|
|
56
59
|
|
|
57
|
-
//
|
|
58
|
-
const
|
|
60
|
+
// Systems/agents discovery hook (for dev tools)
|
|
61
|
+
const systemsHook = useSystems(config, api, storage);
|
|
62
|
+
|
|
63
|
+
// Create a config override with the effective agent key from dev tools
|
|
64
|
+
const effectiveConfig = useMemo(() => {
|
|
65
|
+
if (!config.showDevTools) return config;
|
|
66
|
+
const effectiveKey = systemsHook.getEffectiveAgentKey();
|
|
67
|
+
if (effectiveKey && effectiveKey !== config.agentKey) {
|
|
68
|
+
return { ...config, agentKey: effectiveKey };
|
|
69
|
+
}
|
|
70
|
+
return config;
|
|
71
|
+
}, [config, systemsHook.getEffectiveAgentKey]);
|
|
72
|
+
|
|
73
|
+
// Chat hook — uses effective config so agentKey is dynamic
|
|
74
|
+
const chat = useChat(effectiveConfig, api, storage);
|
|
59
75
|
|
|
60
76
|
// Models hook
|
|
61
77
|
const models = useModels(config, api, storage);
|
|
@@ -86,7 +102,10 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
86
102
|
// Load initial conversation if stored
|
|
87
103
|
useEffect(() => {
|
|
88
104
|
const storedConvId = storage.get(config.conversationIdKey);
|
|
105
|
+
console.log('[ChatWidget] Initial load - storedConvId:', storedConvId, 'key:', config.conversationIdKey);
|
|
106
|
+
console.log('[ChatWidget] apiPaths.conversations:', config.apiPaths.conversations);
|
|
89
107
|
if (storedConvId) {
|
|
108
|
+
console.log('[ChatWidget] Loading conversation:', storedConvId);
|
|
90
109
|
chat.loadConversation(storedConvId);
|
|
91
110
|
}
|
|
92
111
|
}, []);
|
|
@@ -109,12 +128,13 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
109
128
|
// Load conversations for sidebar
|
|
110
129
|
const loadConversations = useCallback(async () => {
|
|
111
130
|
if (!config.showConversationSidebar) return;
|
|
112
|
-
|
|
131
|
+
|
|
113
132
|
setConversationsLoading(true);
|
|
114
133
|
try {
|
|
115
|
-
const
|
|
134
|
+
const agentKey = effectiveConfig.agentKey;
|
|
135
|
+
const url = `${config.backendUrl}${config.apiPaths.conversations}?agent_key=${encodeURIComponent(agentKey)}`;
|
|
116
136
|
const response = await fetch(url, api.getFetchOptions({ method: 'GET' }));
|
|
117
|
-
|
|
137
|
+
|
|
118
138
|
if (response.ok) {
|
|
119
139
|
const data = await response.json();
|
|
120
140
|
setConversations(data.results || data);
|
|
@@ -125,7 +145,7 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
125
145
|
} finally {
|
|
126
146
|
setConversationsLoading(false);
|
|
127
147
|
}
|
|
128
|
-
}, [config, api]);
|
|
148
|
+
}, [config, effectiveConfig, api]);
|
|
129
149
|
|
|
130
150
|
// Handlers
|
|
131
151
|
const handleToggleSidebar = useCallback(() => {
|
|
@@ -149,6 +169,7 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
149
169
|
const handleSend = useCallback((content) => {
|
|
150
170
|
chat.sendMessage(content, {
|
|
151
171
|
model: models.selectedModel,
|
|
172
|
+
thinking: models.thinkingEnabled && models.supportsThinking(),
|
|
152
173
|
onAssistantMessage: (assistantContent) => {
|
|
153
174
|
// TTS callback when assistant finishes
|
|
154
175
|
if (enableTTS && assistantContent) {
|
|
@@ -156,7 +177,7 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
156
177
|
}
|
|
157
178
|
}
|
|
158
179
|
});
|
|
159
|
-
}, [chat, enableTTS, models.selectedModel]);
|
|
180
|
+
}, [chat, enableTTS, models.selectedModel, models.thinkingEnabled, models.supportsThinking]);
|
|
160
181
|
|
|
161
182
|
// Handle tab switching
|
|
162
183
|
const handleTabChange = useCallback((tab) => {
|
|
@@ -205,8 +226,11 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
205
226
|
config.embedded && 'cw-widget-embedded',
|
|
206
227
|
].filter(Boolean).join(' ');
|
|
207
228
|
|
|
229
|
+
// Calculate header text color for contrast
|
|
230
|
+
const headerTextColor = config.headerTextColor || getContrastingTextColor(config.primaryColor);
|
|
231
|
+
|
|
208
232
|
return html`
|
|
209
|
-
<div class=${widgetClasses} style=${{ '--cw-primary': config.primaryColor }}>
|
|
233
|
+
<div class=${widgetClasses} style=${{ '--cw-primary': config.primaryColor, '--cw-header-text': headerTextColor }}>
|
|
210
234
|
${config.showConversationSidebar && html`
|
|
211
235
|
<${Sidebar}
|
|
212
236
|
isOpen=${sidebarOpen}
|
|
@@ -235,6 +259,22 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
235
259
|
onToggleSidebar=${handleToggleSidebar}
|
|
236
260
|
/>
|
|
237
261
|
|
|
262
|
+
${config.showDevTools && html`
|
|
263
|
+
<${DevToolbar}
|
|
264
|
+
systems=${systemsHook.systems}
|
|
265
|
+
agents=${systemsHook.agents}
|
|
266
|
+
selectedSystem=${systemsHook.selectedSystem}
|
|
267
|
+
selectedAgent=${systemsHook.selectedAgent}
|
|
268
|
+
selectedSystemVersion=${systemsHook.selectedSystemVersion}
|
|
269
|
+
selectedAgentVersion=${systemsHook.selectedAgentVersion}
|
|
270
|
+
onSelectSystem=${systemsHook.selectSystem}
|
|
271
|
+
onSelectAgent=${systemsHook.selectAgent}
|
|
272
|
+
onSelectSystemVersion=${systemsHook.selectSystemVersion}
|
|
273
|
+
onSelectAgentVersion=${systemsHook.selectAgentVersion}
|
|
274
|
+
disabled=${chat.isLoading}
|
|
275
|
+
/>
|
|
276
|
+
`}
|
|
277
|
+
|
|
238
278
|
${config.showTasksTab !== false && html`
|
|
239
279
|
<div class="cw-tabs">
|
|
240
280
|
<button
|
|
@@ -276,6 +316,8 @@ export function ChatWidget({ config, onStateChange, markdownParser, apiRef }) {
|
|
|
276
316
|
availableModels=${models.availableModels}
|
|
277
317
|
selectedModel=${models.selectedModel}
|
|
278
318
|
onSelectModel=${models.selectModel}
|
|
319
|
+
thinkingEnabled=${models.thinkingEnabled}
|
|
320
|
+
onToggleThinking=${models.toggleThinking}
|
|
279
321
|
disabled=${chat.isLoading}
|
|
280
322
|
/>
|
|
281
323
|
`}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevToolbar component - system/agent/version picker for developers and testers.
|
|
3
|
+
*
|
|
4
|
+
* Always shows when rendered — displays info badges for single items,
|
|
5
|
+
* dropdowns when there are multiple options to choose from.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { html } from 'htm/preact';
|
|
9
|
+
import { escapeHtml } from '../utils/helpers.js';
|
|
10
|
+
|
|
11
|
+
export function DevToolbar({
|
|
12
|
+
systems,
|
|
13
|
+
agents,
|
|
14
|
+
selectedSystem,
|
|
15
|
+
selectedAgent,
|
|
16
|
+
selectedSystemVersion,
|
|
17
|
+
selectedAgentVersion,
|
|
18
|
+
onSelectSystem,
|
|
19
|
+
onSelectAgent,
|
|
20
|
+
onSelectSystemVersion,
|
|
21
|
+
onSelectAgentVersion,
|
|
22
|
+
disabled,
|
|
23
|
+
}) {
|
|
24
|
+
// Find current system and agent objects
|
|
25
|
+
const currentSystem = systems.find(s => s.slug === selectedSystem);
|
|
26
|
+
const currentAgent = agents.find(a => a.slug === selectedAgent);
|
|
27
|
+
|
|
28
|
+
// Get versions for current selections
|
|
29
|
+
const systemVersions = currentSystem?.versions || [];
|
|
30
|
+
const agentVersions = currentAgent?.versions || [];
|
|
31
|
+
|
|
32
|
+
// Nothing loaded yet
|
|
33
|
+
if (systems.length === 0 && agents.length === 0) return null;
|
|
34
|
+
|
|
35
|
+
const hasMultipleSystems = systems.length > 1;
|
|
36
|
+
const hasMultipleAgents = agents.length > 1;
|
|
37
|
+
const hasMultipleSystemVersions = systemVersions.length > 1;
|
|
38
|
+
const hasMultipleAgentVersions = agentVersions.length > 1;
|
|
39
|
+
|
|
40
|
+
return html`
|
|
41
|
+
<div class="cw-dev-toolbar">
|
|
42
|
+
<div class="cw-dev-toolbar-label">
|
|
43
|
+
<span class="cw-dev-toolbar-icon">🛠️</span>
|
|
44
|
+
<span>Dev</span>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="cw-dev-toolbar-selectors">
|
|
48
|
+
<!-- System -->
|
|
49
|
+
<div class="cw-dev-select-group">
|
|
50
|
+
<label class="cw-dev-label">System</label>
|
|
51
|
+
${hasMultipleSystems ? html`
|
|
52
|
+
<select
|
|
53
|
+
class="cw-dev-select"
|
|
54
|
+
value=${selectedSystem || ''}
|
|
55
|
+
onChange=${(e) => onSelectSystem(e.target.value)}
|
|
56
|
+
disabled=${disabled}
|
|
57
|
+
>
|
|
58
|
+
${systems.map(sys => html`
|
|
59
|
+
<option key=${sys.slug} value=${sys.slug}>
|
|
60
|
+
${sys.name}
|
|
61
|
+
</option>
|
|
62
|
+
`)}
|
|
63
|
+
</select>
|
|
64
|
+
` : html`
|
|
65
|
+
<span class="cw-dev-badge">${currentSystem?.name || '—'}</span>
|
|
66
|
+
`}
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- System Version -->
|
|
70
|
+
<div class="cw-dev-select-group">
|
|
71
|
+
<label class="cw-dev-label">Sys Ver</label>
|
|
72
|
+
${hasMultipleSystemVersions ? html`
|
|
73
|
+
<select
|
|
74
|
+
class="cw-dev-select"
|
|
75
|
+
value=${selectedSystemVersion || ''}
|
|
76
|
+
onChange=${(e) => onSelectSystemVersion(e.target.value || null)}
|
|
77
|
+
disabled=${disabled}
|
|
78
|
+
>
|
|
79
|
+
${systemVersions.map(v => html`
|
|
80
|
+
<option key=${v.version} value=${v.version}>
|
|
81
|
+
${v.version}${v.is_active ? ' ✓' : ''}${v.is_draft ? ' (draft)' : ''}
|
|
82
|
+
</option>
|
|
83
|
+
`)}
|
|
84
|
+
</select>
|
|
85
|
+
` : html`
|
|
86
|
+
<span class="cw-dev-badge">${systemVersions.length === 1 ? systemVersions[0].version : 'none'}</span>
|
|
87
|
+
`}
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Agent -->
|
|
91
|
+
<div class="cw-dev-select-group">
|
|
92
|
+
<label class="cw-dev-label">Agent</label>
|
|
93
|
+
${hasMultipleAgents ? html`
|
|
94
|
+
<select
|
|
95
|
+
class="cw-dev-select"
|
|
96
|
+
value=${selectedAgent || ''}
|
|
97
|
+
onChange=${(e) => onSelectAgent(e.target.value)}
|
|
98
|
+
disabled=${disabled}
|
|
99
|
+
>
|
|
100
|
+
${agents.map(agent => html`
|
|
101
|
+
<option key=${agent.slug} value=${agent.slug}>
|
|
102
|
+
${agent.name}${currentSystem?.entry_agent?.slug === agent.slug ? ' ★' : ''}
|
|
103
|
+
</option>
|
|
104
|
+
`)}
|
|
105
|
+
</select>
|
|
106
|
+
` : html`
|
|
107
|
+
<span class="cw-dev-badge">${currentAgent?.name || agents[0]?.name || '—'}${currentSystem?.entry_agent?.slug === (currentAgent?.slug || agents[0]?.slug) ? ' ★' : ''}</span>
|
|
108
|
+
`}
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<!-- Agent Version -->
|
|
112
|
+
<div class="cw-dev-select-group">
|
|
113
|
+
<label class="cw-dev-label">Agent Ver</label>
|
|
114
|
+
${hasMultipleAgentVersions ? html`
|
|
115
|
+
<select
|
|
116
|
+
class="cw-dev-select"
|
|
117
|
+
value=${selectedAgentVersion || ''}
|
|
118
|
+
onChange=${(e) => onSelectAgentVersion(e.target.value || null)}
|
|
119
|
+
disabled=${disabled}
|
|
120
|
+
>
|
|
121
|
+
${agentVersions.map(v => html`
|
|
122
|
+
<option key=${v.version} value=${v.version}>
|
|
123
|
+
${v.version}${v.is_active ? ' ✓' : ''}${v.is_draft ? ' (draft)' : ''}
|
|
124
|
+
</option>
|
|
125
|
+
`)}
|
|
126
|
+
</select>
|
|
127
|
+
` : html`
|
|
128
|
+
<span class="cw-dev-badge">${agentVersions.length === 1 ? agentVersions[0].version : (currentAgent?.active_version || '—')}</span>
|
|
129
|
+
`}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
135
|
+
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ModelSelector component - dropdown for selecting LLM model
|
|
2
|
+
* ModelSelector component - dropdown for selecting LLM model with thinking toggle
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { html } from 'htm/preact';
|
|
6
6
|
import { useState } from 'preact/hooks';
|
|
7
7
|
import { escapeHtml } from '../utils/helpers.js';
|
|
8
8
|
|
|
9
|
-
export function ModelSelector({
|
|
10
|
-
availableModels,
|
|
11
|
-
selectedModel,
|
|
9
|
+
export function ModelSelector({
|
|
10
|
+
availableModels,
|
|
11
|
+
selectedModel,
|
|
12
12
|
onSelectModel,
|
|
13
|
-
|
|
13
|
+
thinkingEnabled,
|
|
14
|
+
onToggleThinking,
|
|
15
|
+
disabled
|
|
14
16
|
}) {
|
|
15
17
|
const [isOpen, setIsOpen] = useState(false);
|
|
16
18
|
|
|
@@ -20,6 +22,7 @@ export function ModelSelector({
|
|
|
20
22
|
|
|
21
23
|
const selectedModelInfo = availableModels.find(m => m.id === selectedModel);
|
|
22
24
|
const displayName = selectedModelInfo?.name || 'Select Model';
|
|
25
|
+
const supportsThinking = selectedModelInfo?.supports_thinking || false;
|
|
23
26
|
|
|
24
27
|
const handleToggle = () => {
|
|
25
28
|
if (!disabled) {
|
|
@@ -32,10 +35,17 @@ export function ModelSelector({
|
|
|
32
35
|
setIsOpen(false);
|
|
33
36
|
};
|
|
34
37
|
|
|
38
|
+
const handleThinkingToggle = (e) => {
|
|
39
|
+
e.stopPropagation();
|
|
40
|
+
if (onToggleThinking && supportsThinking) {
|
|
41
|
+
onToggleThinking(!thinkingEnabled);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
35
45
|
return html`
|
|
36
46
|
<div class="cw-model-selector">
|
|
37
|
-
<button
|
|
38
|
-
class="cw-model-btn"
|
|
47
|
+
<button
|
|
48
|
+
class="cw-model-btn"
|
|
39
49
|
onClick=${handleToggle}
|
|
40
50
|
disabled=${disabled}
|
|
41
51
|
title="Select Model"
|
|
@@ -44,16 +54,30 @@ export function ModelSelector({
|
|
|
44
54
|
<span class="cw-model-name">${escapeHtml(displayName)}</span>
|
|
45
55
|
<span class="cw-model-chevron">${isOpen ? '▲' : '▼'}</span>
|
|
46
56
|
</button>
|
|
47
|
-
|
|
57
|
+
|
|
58
|
+
${supportsThinking && onToggleThinking && html`
|
|
59
|
+
<button
|
|
60
|
+
class="cw-thinking-toggle ${thinkingEnabled ? 'cw-thinking-enabled' : ''}"
|
|
61
|
+
onClick=${handleThinkingToggle}
|
|
62
|
+
disabled=${disabled}
|
|
63
|
+
title=${thinkingEnabled ? 'Thinking enabled - click to disable' : 'Enable extended thinking'}
|
|
64
|
+
>
|
|
65
|
+
<span class="cw-thinking-icon">🧠</span>
|
|
66
|
+
</button>
|
|
67
|
+
`}
|
|
68
|
+
|
|
48
69
|
${isOpen && html`
|
|
49
70
|
<div class="cw-model-dropdown">
|
|
50
71
|
${availableModels.map(model => html`
|
|
51
|
-
<button
|
|
72
|
+
<button
|
|
52
73
|
key=${model.id}
|
|
53
74
|
class="cw-model-option ${model.id === selectedModel ? 'cw-model-option-selected' : ''}"
|
|
54
75
|
onClick=${() => handleSelect(model.id)}
|
|
55
76
|
>
|
|
56
|
-
<span class="cw-model-option-name"
|
|
77
|
+
<span class="cw-model-option-name">
|
|
78
|
+
${escapeHtml(model.name)}
|
|
79
|
+
${model.supports_thinking && html`<span class="cw-thinking-badge" title="Supports extended thinking">🧠</span>`}
|
|
80
|
+
</span>
|
|
57
81
|
<span class="cw-model-option-provider">${escapeHtml(model.provider)}</span>
|
|
58
82
|
${model.description && html`
|
|
59
83
|
<span class="cw-model-option-desc">${escapeHtml(model.description)}</span>
|
package/src/hooks/useChat.js
CHANGED
|
@@ -224,7 +224,7 @@ export function useChat(config, api, storage) {
|
|
|
224
224
|
options = optionsOrFiles || {};
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
const { model, onAssistantMessage, supersedeFromMessageIndex } = options;
|
|
227
|
+
const { model, thinking, onAssistantMessage, supersedeFromMessageIndex } = options;
|
|
228
228
|
|
|
229
229
|
setIsLoading(true);
|
|
230
230
|
setError(null);
|
|
@@ -271,6 +271,10 @@ export function useChat(config, api, storage) {
|
|
|
271
271
|
formData.append('model', model);
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
if (thinking) {
|
|
275
|
+
formData.append('thinking', 'true');
|
|
276
|
+
}
|
|
277
|
+
|
|
274
278
|
// Append each file
|
|
275
279
|
files.forEach(file => {
|
|
276
280
|
formData.append('files', file);
|
|
@@ -289,6 +293,7 @@ export function useChat(config, api, storage) {
|
|
|
289
293
|
messages: [{ role: 'user', content: content.trim() }],
|
|
290
294
|
metadata: { ...config.metadata, journeyType: config.defaultJourneyType },
|
|
291
295
|
...(model && { model }),
|
|
296
|
+
...(thinking && { thinking: true }),
|
|
292
297
|
// Edit/retry support: tell backend to mark old runs as superseded
|
|
293
298
|
...(supersedeFromMessageIndex !== undefined && { supersedeFromMessageIndex }),
|
|
294
299
|
});
|
|
@@ -446,6 +451,7 @@ export function useChat(config, api, storage) {
|
|
|
446
451
|
};
|
|
447
452
|
|
|
448
453
|
const loadConversation = useCallback(async (convId) => {
|
|
454
|
+
console.log('[ChatWidget] loadConversation called with:', convId);
|
|
449
455
|
setIsLoading(true);
|
|
450
456
|
setMessages([]);
|
|
451
457
|
setConversationId(convId);
|
|
@@ -454,21 +460,30 @@ export function useChat(config, api, storage) {
|
|
|
454
460
|
const token = await api.getOrCreateSession();
|
|
455
461
|
const limit = 10;
|
|
456
462
|
const url = `${config.backendUrl}${config.apiPaths.conversations}${convId}/?limit=${limit}&offset=0`;
|
|
463
|
+
console.log('[ChatWidget] Fetching conversation from:', url);
|
|
457
464
|
|
|
458
465
|
const response = await fetch(url, api.getFetchOptions({ method: 'GET' }, token));
|
|
466
|
+
console.log('[ChatWidget] Response status:', response.status);
|
|
459
467
|
|
|
460
468
|
if (response.ok) {
|
|
461
469
|
const rawConversation = await response.json();
|
|
470
|
+
console.log('[ChatWidget] Raw conversation:', rawConversation);
|
|
462
471
|
const conversation = api.transformResponse(rawConversation);
|
|
472
|
+
console.log('[ChatWidget] Transformed conversation:', conversation);
|
|
463
473
|
if (conversation.messages) {
|
|
464
474
|
// Use flatMap to handle tool_calls which return arrays, filter out nulls (empty messages)
|
|
465
|
-
|
|
475
|
+
const mappedMessages = conversation.messages.flatMap(mapApiMessage).filter(Boolean);
|
|
476
|
+
console.log('[ChatWidget] Mapped messages:', mappedMessages);
|
|
477
|
+
setMessages(mappedMessages);
|
|
466
478
|
}
|
|
467
479
|
setHasMoreMessages(conversation.hasMore || false);
|
|
468
480
|
setMessagesOffset(conversation.messages?.length || 0);
|
|
469
481
|
} else if (response.status === 404) {
|
|
482
|
+
console.log('[ChatWidget] Conversation not found, clearing');
|
|
470
483
|
setConversationId(null);
|
|
471
484
|
storage?.set(config.conversationIdKey, null);
|
|
485
|
+
} else {
|
|
486
|
+
console.error('[ChatWidget] Unexpected response status:', response.status);
|
|
472
487
|
}
|
|
473
488
|
} catch (err) {
|
|
474
489
|
console.error('[ChatWidget] Failed to load conversation:', err);
|
package/src/hooks/useModels.js
CHANGED
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Models hook - manages available models and
|
|
2
|
+
* Models hook - manages available models, selection, and thinking mode
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useState, useEffect, useCallback } from 'preact/hooks';
|
|
6
6
|
|
|
7
|
+
const THINKING_KEY = 'cw_thinking_enabled';
|
|
8
|
+
|
|
7
9
|
export function useModels(config, api, storage) {
|
|
8
10
|
const [availableModels, setAvailableModels] = useState([]);
|
|
9
11
|
const [selectedModel, setSelectedModel] = useState(null);
|
|
10
12
|
const [defaultModel, setDefaultModel] = useState(null);
|
|
11
13
|
const [isLoading, setIsLoading] = useState(false);
|
|
14
|
+
const [thinkingEnabled, setThinkingEnabled] = useState(false);
|
|
12
15
|
|
|
13
16
|
// Load available models on mount
|
|
14
17
|
useEffect(() => {
|
|
15
18
|
const loadModels = async () => {
|
|
16
19
|
if (!config.showModelSelector) return;
|
|
17
|
-
|
|
20
|
+
|
|
18
21
|
setIsLoading(true);
|
|
19
22
|
try {
|
|
20
23
|
const response = await fetch(
|
|
21
24
|
`${config.backendUrl}${config.apiPaths.models}`,
|
|
22
25
|
api.getFetchOptions({ method: 'GET' })
|
|
23
26
|
);
|
|
24
|
-
|
|
27
|
+
|
|
25
28
|
if (response.ok) {
|
|
26
29
|
const data = await response.json();
|
|
27
30
|
const models = data.models || [];
|
|
28
31
|
setAvailableModels(models);
|
|
29
32
|
setDefaultModel(data.default);
|
|
30
|
-
|
|
33
|
+
|
|
31
34
|
// Restore saved model or use default
|
|
32
35
|
const savedModel = storage?.get(config.modelKey);
|
|
33
36
|
if (savedModel && models.some(m => m.id === savedModel)) {
|
|
@@ -35,6 +38,12 @@ export function useModels(config, api, storage) {
|
|
|
35
38
|
} else {
|
|
36
39
|
setSelectedModel(data.default);
|
|
37
40
|
}
|
|
41
|
+
|
|
42
|
+
// Restore thinking preference
|
|
43
|
+
const savedThinking = storage?.get(THINKING_KEY);
|
|
44
|
+
if (savedThinking === 'true') {
|
|
45
|
+
setThinkingEnabled(true);
|
|
46
|
+
}
|
|
38
47
|
}
|
|
39
48
|
} catch (err) {
|
|
40
49
|
console.warn('[ChatWidget] Failed to load models:', err);
|
|
@@ -52,11 +61,23 @@ export function useModels(config, api, storage) {
|
|
|
52
61
|
storage?.set(config.modelKey, modelId);
|
|
53
62
|
}, [config.modelKey, storage]);
|
|
54
63
|
|
|
64
|
+
// Toggle thinking mode
|
|
65
|
+
const toggleThinking = useCallback((enabled) => {
|
|
66
|
+
setThinkingEnabled(enabled);
|
|
67
|
+
storage?.set(THINKING_KEY, enabled ? 'true' : 'false');
|
|
68
|
+
}, [storage]);
|
|
69
|
+
|
|
55
70
|
// Get the currently selected model object
|
|
56
71
|
const getSelectedModelInfo = useCallback(() => {
|
|
57
72
|
return availableModels.find(m => m.id === selectedModel) || null;
|
|
58
73
|
}, [availableModels, selectedModel]);
|
|
59
74
|
|
|
75
|
+
// Check if current model supports thinking
|
|
76
|
+
const supportsThinking = useCallback(() => {
|
|
77
|
+
const model = getSelectedModelInfo();
|
|
78
|
+
return model?.supports_thinking || false;
|
|
79
|
+
}, [getSelectedModelInfo]);
|
|
80
|
+
|
|
60
81
|
return {
|
|
61
82
|
availableModels,
|
|
62
83
|
selectedModel,
|
|
@@ -64,6 +85,9 @@ export function useModels(config, api, storage) {
|
|
|
64
85
|
isLoading,
|
|
65
86
|
selectModel,
|
|
66
87
|
getSelectedModelInfo,
|
|
88
|
+
thinkingEnabled,
|
|
89
|
+
toggleThinking,
|
|
90
|
+
supportsThinking,
|
|
67
91
|
};
|
|
68
92
|
}
|
|
69
93
|
|