groove-dev 0.27.144 → 0.27.145
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/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/conversations.js +18 -48
- package/node_modules/@groove-dev/daemon/src/routes/agents.js +6 -83
- package/node_modules/@groove-dev/gui/dist/assets/{index-BcoF6_eF.js → index-Bxc0gU06.js} +232 -238
- package/node_modules/@groove-dev/gui/dist/assets/index-C0pztKBn.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +80 -95
- package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +2 -70
- package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +68 -66
- package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +4 -8
- package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +39 -31
- package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +66 -65
- package/node_modules/@groove-dev/gui/src/components/lab/preset-manager.jsx +17 -14
- package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +126 -127
- package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +10 -8
- package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +8 -8
- package/node_modules/@groove-dev/gui/src/lib/status.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +17 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +8 -1
- package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +13 -14
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +41 -10
- package/node_modules/@groove-dev/gui/src/views/models.jsx +57 -36
- package/node_modules/axios/CHANGELOG.md +260 -0
- package/node_modules/axios/README.md +595 -223
- package/node_modules/axios/dist/axios.js +1460 -1090
- package/node_modules/axios/dist/axios.js.map +1 -1
- package/node_modules/axios/dist/axios.min.js +3 -3
- package/node_modules/axios/dist/axios.min.js.map +1 -1
- package/node_modules/axios/dist/browser/axios.cjs +1560 -1132
- package/node_modules/axios/dist/browser/axios.cjs.map +1 -1
- package/node_modules/axios/dist/esm/axios.js +1557 -1128
- package/node_modules/axios/dist/esm/axios.js.map +1 -1
- package/node_modules/axios/dist/esm/axios.min.js +2 -2
- package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
- package/node_modules/axios/dist/node/axios.cjs +1594 -1057
- package/node_modules/axios/dist/node/axios.cjs.map +1 -1
- package/node_modules/axios/index.d.cts +40 -41
- package/node_modules/axios/index.d.ts +151 -227
- package/node_modules/axios/index.js +2 -0
- package/node_modules/axios/lib/adapters/adapters.js +4 -2
- package/node_modules/axios/lib/adapters/fetch.js +147 -16
- package/node_modules/axios/lib/adapters/http.js +306 -58
- package/node_modules/axios/lib/adapters/xhr.js +6 -2
- package/node_modules/axios/lib/core/Axios.js +7 -3
- package/node_modules/axios/lib/core/AxiosError.js +120 -34
- package/node_modules/axios/lib/core/AxiosHeaders.js +27 -25
- package/node_modules/axios/lib/core/buildFullPath.js +1 -1
- package/node_modules/axios/lib/core/dispatchRequest.js +19 -7
- package/node_modules/axios/lib/core/mergeConfig.js +21 -4
- package/node_modules/axios/lib/core/settle.js +7 -11
- package/node_modules/axios/lib/defaults/index.js +14 -9
- package/node_modules/axios/lib/env/data.js +1 -1
- package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +1 -2
- package/node_modules/axios/lib/helpers/buildURL.js +1 -1
- package/node_modules/axios/lib/helpers/cookies.js +14 -2
- package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +28 -1
- package/node_modules/axios/lib/helpers/formDataToJSON.js +3 -1
- package/node_modules/axios/lib/helpers/formDataToStream.js +3 -2
- package/node_modules/axios/lib/helpers/parseProtocol.js +1 -1
- package/node_modules/axios/lib/helpers/progressEventReducer.js +5 -5
- package/node_modules/axios/lib/helpers/resolveConfig.js +54 -18
- package/node_modules/axios/lib/helpers/shouldBypassProxy.js +74 -2
- package/node_modules/axios/lib/helpers/toFormData.js +10 -2
- package/node_modules/axios/lib/helpers/validator.js +3 -1
- package/node_modules/axios/lib/utils.js +33 -21
- package/node_modules/axios/package.json +17 -24
- package/node_modules/follow-redirects/README.md +7 -5
- package/node_modules/follow-redirects/index.js +24 -1
- package/node_modules/follow-redirects/package.json +1 -1
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/conversations.js +18 -48
- package/packages/daemon/src/routes/agents.js +6 -83
- package/packages/gui/dist/assets/{index-BcoF6_eF.js → index-Bxc0gU06.js} +232 -238
- package/packages/gui/dist/assets/index-C0pztKBn.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +80 -95
- package/packages/gui/src/components/agents/agent-panel.jsx +2 -70
- package/packages/gui/src/components/chat/chat-header.jsx +2 -0
- package/packages/gui/src/components/chat/chat-input.jsx +68 -66
- package/packages/gui/src/components/chat/chat-view.jsx +4 -8
- package/packages/gui/src/components/lab/chat-playground.jsx +39 -31
- package/packages/gui/src/components/lab/parameter-panel.jsx +66 -65
- package/packages/gui/src/components/lab/preset-manager.jsx +17 -14
- package/packages/gui/src/components/lab/runtime-config.jsx +126 -127
- package/packages/gui/src/components/lab/system-prompt-editor.jsx +10 -8
- package/packages/gui/src/components/ui/slider.jsx +8 -8
- package/packages/gui/src/lib/status.js +1 -0
- package/packages/gui/src/stores/groove.js +17 -0
- package/packages/gui/src/stores/slices/agents-slice.js +8 -1
- package/packages/gui/src/stores/slices/chat-slice.js +13 -14
- package/packages/gui/src/views/model-lab.jsx +41 -10
- package/packages/gui/src/views/models.jsx +57 -36
- package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.css +0 -1
- package/packages/gui/dist/assets/index-Dd7qhiEd.css +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
2
|
import { useState, useEffect, useRef } from 'react';
|
|
3
3
|
import { useGrooveStore } from '../../stores/groove';
|
|
4
|
+
import { SidebarSection } from '../../views/model-lab';
|
|
4
5
|
import { Button } from '../ui/button';
|
|
5
6
|
import { Badge } from '../ui/badge';
|
|
6
7
|
import { Input } from '../ui/input';
|
|
@@ -95,8 +96,8 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
95
96
|
<button
|
|
96
97
|
onClick={() => onSelect(runtime.id)}
|
|
97
98
|
className={cn(
|
|
98
|
-
'w-full flex items-center gap-2.5 px-2.5 py-2 text-left transition-colors cursor-pointer rounded
|
|
99
|
-
active ? 'bg-accent/8
|
|
99
|
+
'w-full flex items-center gap-2.5 px-2.5 py-2 text-left transition-colors cursor-pointer rounded',
|
|
100
|
+
active ? 'bg-accent/8 ring-1 ring-accent/20' : 'hover:bg-surface-2',
|
|
100
101
|
)}
|
|
101
102
|
>
|
|
102
103
|
<span className={cn(
|
|
@@ -104,10 +105,10 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
104
105
|
online ? 'bg-success' : runtime.status === 'error' ? 'bg-danger' : 'bg-text-4',
|
|
105
106
|
)} />
|
|
106
107
|
<div className="flex-1 min-w-0">
|
|
107
|
-
<div className={cn('text-
|
|
108
|
+
<div className={cn('text-[11px] font-sans font-medium truncate', active ? 'text-text-0' : 'text-text-2')}>
|
|
108
109
|
{RUNTIME_TYPES.find((t) => t.value === runtime.type)?.label || runtime.type}
|
|
109
110
|
</div>
|
|
110
|
-
<div className="text-
|
|
111
|
+
<div className="text-[10px] text-text-4 flex items-center gap-1.5">
|
|
111
112
|
<span className={cn('font-sans', online ? 'text-success' : 'text-danger')}>
|
|
112
113
|
{online ? 'Online' : 'Offline'}
|
|
113
114
|
</span>
|
|
@@ -116,7 +117,7 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
116
117
|
)}
|
|
117
118
|
</div>
|
|
118
119
|
</div>
|
|
119
|
-
<div className="flex items-center gap-
|
|
120
|
+
<div className="flex items-center gap-px flex-shrink-0">
|
|
120
121
|
{managed && online && (
|
|
121
122
|
<Tooltip content="Stop server">
|
|
122
123
|
<button
|
|
@@ -142,7 +143,7 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
142
143
|
onClick={(e) => { e.stopPropagation(); onTest(runtime.id); }}
|
|
143
144
|
className="p-1 text-text-4 hover:text-accent transition-colors cursor-pointer"
|
|
144
145
|
>
|
|
145
|
-
{testing === runtime.id ? <Loader2 size={
|
|
146
|
+
{testing === runtime.id ? <Loader2 size={10} className="animate-spin" /> : <RotateCcw size={10} />}
|
|
146
147
|
</button>
|
|
147
148
|
</Tooltip>
|
|
148
149
|
<Tooltip content="Remove runtime">
|
|
@@ -150,7 +151,7 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
150
151
|
onClick={(e) => { e.stopPropagation(); onRemove(runtime.id); }}
|
|
151
152
|
className="p-1 text-text-4 hover:text-danger transition-colors cursor-pointer"
|
|
152
153
|
>
|
|
153
|
-
<Trash2 size={
|
|
154
|
+
<Trash2 size={10} />
|
|
154
155
|
</button>
|
|
155
156
|
</Tooltip>
|
|
156
157
|
</div>
|
|
@@ -172,23 +173,30 @@ const BACKENDS = [
|
|
|
172
173
|
{ id: 'tgi', label: 'TGI', subtitle: 'HuggingFace, guided setup', autoLaunch: false },
|
|
173
174
|
];
|
|
174
175
|
|
|
175
|
-
function
|
|
176
|
-
|
|
176
|
+
function StatusBanner({ variant, icon: Icon, children }) {
|
|
177
|
+
const styles = {
|
|
178
|
+
success: 'bg-success/8 text-success',
|
|
179
|
+
danger: 'bg-danger/8 text-danger',
|
|
180
|
+
accent: 'bg-accent/8 text-accent',
|
|
181
|
+
warning: 'bg-warning/8 text-warning',
|
|
182
|
+
};
|
|
177
183
|
return (
|
|
178
|
-
<div className={cn(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
phase === 'error' && 'bg-danger/8 text-danger',
|
|
182
|
-
(phase === 'starting' || phase === 'checking') && 'bg-accent/8 text-accent',
|
|
183
|
-
)}>
|
|
184
|
-
{phase === 'starting' && <><Loader2 size={11} className="animate-spin" /> Starting server...</>}
|
|
185
|
-
{phase === 'checking' && <><Loader2 size={11} className="animate-spin" /> Checking...</>}
|
|
186
|
-
{phase === 'ready' && <><CheckCircle size={11} /> Server ready</>}
|
|
187
|
-
{phase === 'error' && <><AlertTriangle size={11} /> {error || 'Launch failed'}</>}
|
|
184
|
+
<div className={cn('flex items-center gap-2 px-2.5 py-2 text-[11px] font-sans rounded', styles[variant])}>
|
|
185
|
+
<Icon size={11} className={variant === 'accent' ? 'animate-spin' : ''} />
|
|
186
|
+
<span>{children}</span>
|
|
188
187
|
</div>
|
|
189
188
|
);
|
|
190
189
|
}
|
|
191
190
|
|
|
191
|
+
function LaunchStatus({ phase, error }) {
|
|
192
|
+
if (!phase) return null;
|
|
193
|
+
if (phase === 'starting') return <StatusBanner variant="accent" icon={Loader2}>Starting server...</StatusBanner>;
|
|
194
|
+
if (phase === 'checking') return <StatusBanner variant="accent" icon={Loader2}>Checking...</StatusBanner>;
|
|
195
|
+
if (phase === 'ready') return <StatusBanner variant="success" icon={CheckCircle}>Server ready</StatusBanner>;
|
|
196
|
+
if (phase === 'error') return <StatusBanner variant="danger" icon={AlertTriangle}>{error || 'Launch failed'}</StatusBanner>;
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
|
|
192
200
|
function getIncompatibilityReason(modelType, backendId) {
|
|
193
201
|
if (modelType === 'gguf' && backendId === 'mlx') return 'GGUF model — MLX needs MLX-format weights';
|
|
194
202
|
if (modelType === 'gguf' && (backendId === 'vllm' || backendId === 'tgi')) return 'GGUF model — needs standard HuggingFace weights';
|
|
@@ -289,22 +297,20 @@ export function LaunchModel() {
|
|
|
289
297
|
}
|
|
290
298
|
|
|
291
299
|
return (
|
|
292
|
-
<
|
|
293
|
-
<span className="text-2xs font-semibold font-sans text-text-3 uppercase tracking-wider">Launch Model</span>
|
|
294
|
-
|
|
300
|
+
<SidebarSection label="Launch Model" collapsible defaultOpen={false}>
|
|
295
301
|
{localModels.length === 0 ? (
|
|
296
|
-
<div className="py-
|
|
297
|
-
<HardDrive size={
|
|
298
|
-
<p className="text-
|
|
299
|
-
<p className="text-
|
|
302
|
+
<div className="py-6 text-center rounded-md bg-surface-1/50 border border-border-subtle">
|
|
303
|
+
<HardDrive size={16} className="mx-auto text-text-4 mb-2" />
|
|
304
|
+
<p className="text-[11px] text-text-3 font-sans">No downloaded models</p>
|
|
305
|
+
<p className="text-[10px] text-text-4 font-sans mt-0.5">Download models from the Models tab</p>
|
|
300
306
|
</div>
|
|
301
307
|
) : (
|
|
302
|
-
|
|
308
|
+
<div className="space-y-4">
|
|
303
309
|
<div className="relative">
|
|
304
310
|
<select
|
|
305
311
|
value={selectedModel || ''}
|
|
306
312
|
onChange={handleModelChange}
|
|
307
|
-
className="w-full h-
|
|
313
|
+
className="w-full h-9 px-2.5 pr-7 text-[11px] rounded bg-surface-1 border border-border text-text-0 font-sans appearance-none cursor-pointer focus:outline-none focus:ring-1 focus:ring-accent/50 focus:border-accent/50 transition-colors"
|
|
308
314
|
>
|
|
309
315
|
<option value="">Select a model</option>
|
|
310
316
|
{localModels.map((m) => {
|
|
@@ -318,75 +324,73 @@ export function LaunchModel() {
|
|
|
318
324
|
);
|
|
319
325
|
})}
|
|
320
326
|
</select>
|
|
321
|
-
<ChevronRight size={
|
|
327
|
+
<ChevronRight size={12} className="absolute right-2.5 top-1/2 -translate-y-1/2 text-text-4 pointer-events-none rotate-90" />
|
|
322
328
|
</div>
|
|
323
329
|
|
|
324
330
|
{selectedModel && (
|
|
325
|
-
<div className="space-y-
|
|
326
|
-
<
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
<
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
<div className="flex
|
|
344
|
-
<
|
|
345
|
-
{b.
|
|
346
|
-
|
|
347
|
-
|
|
331
|
+
<div className="space-y-4">
|
|
332
|
+
<div className="space-y-2">
|
|
333
|
+
<span className="text-[10px] font-semibold font-sans text-text-4 uppercase tracking-widest">Backend</span>
|
|
334
|
+
<div className="space-y-1 rounded-md bg-surface-1/50 border border-border-subtle p-2">
|
|
335
|
+
{backendsWithCompat.map((b) => (
|
|
336
|
+
<Tooltip key={b.id} content={b.reason} side="right">
|
|
337
|
+
<button
|
|
338
|
+
onClick={() => setSelectedBackend(b.id)}
|
|
339
|
+
className={cn(
|
|
340
|
+
'w-full flex items-center gap-2.5 px-2.5 py-2 text-left transition-colors cursor-pointer rounded',
|
|
341
|
+
selectedBackend === b.id ? 'bg-accent/10' : 'hover:bg-surface-3',
|
|
342
|
+
!b.compatible && 'opacity-40',
|
|
343
|
+
)}
|
|
344
|
+
>
|
|
345
|
+
<span className={cn(
|
|
346
|
+
'w-2 h-2 rounded-full border-[1.5px] flex-shrink-0 transition-colors',
|
|
347
|
+
selectedBackend === b.id ? 'border-accent bg-accent' : 'border-text-4',
|
|
348
|
+
)} />
|
|
349
|
+
<div className="flex-1 min-w-0">
|
|
350
|
+
<div className="flex items-center gap-1.5">
|
|
351
|
+
<span className={cn('text-[11px] font-sans font-medium', selectedBackend === b.id ? 'text-text-0' : 'text-text-2')}>
|
|
352
|
+
{b.label}
|
|
353
|
+
</span>
|
|
354
|
+
{b.compatible && b.recommended && <Badge variant="success" className="text-[9px]">Recommended</Badge>}
|
|
355
|
+
</div>
|
|
356
|
+
<div className="text-[10px] text-text-4 font-sans">{b.subtitle}</div>
|
|
348
357
|
</div>
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
))}
|
|
358
|
+
</button>
|
|
359
|
+
</Tooltip>
|
|
360
|
+
))}
|
|
361
|
+
</div>
|
|
354
362
|
</div>
|
|
355
363
|
|
|
356
364
|
{!isCompatible && (
|
|
357
|
-
<
|
|
358
|
-
|
|
359
|
-
{currentBackend?.reason}
|
|
360
|
-
</p>
|
|
365
|
+
<StatusBanner variant="warning" icon={AlertTriangle}>
|
|
366
|
+
{currentBackend?.reason}
|
|
361
367
|
{suggestion && (
|
|
362
|
-
<
|
|
363
|
-
Try <span className="font-mono font-medium">{suggestion.repoId}</span> instead
|
|
364
|
-
</p>
|
|
368
|
+
<> — try <span className="font-mono font-medium">{suggestion.repoId}</span></>
|
|
365
369
|
)}
|
|
366
|
-
</
|
|
370
|
+
</StatusBanner>
|
|
367
371
|
)}
|
|
368
372
|
|
|
369
373
|
{isCompatible && selectedBackend === 'llama-cpp' && (
|
|
370
|
-
<div
|
|
374
|
+
<div>
|
|
371
375
|
{llamaInstalled === null && (
|
|
372
|
-
<div className="flex items-center gap-2 text-
|
|
376
|
+
<div className="flex items-center gap-2 text-[11px] text-text-3 font-sans">
|
|
373
377
|
<Loader2 size={10} className="animate-spin" /> Checking llama-server...
|
|
374
378
|
</div>
|
|
375
379
|
)}
|
|
376
380
|
{llamaInstalled === true && (
|
|
377
|
-
<div className="flex items-center gap-2 text-
|
|
381
|
+
<div className="flex items-center gap-2 text-[11px] text-success font-sans">
|
|
378
382
|
<CheckCircle size={10} /> llama-server found
|
|
379
383
|
</div>
|
|
380
384
|
)}
|
|
381
385
|
{llamaInstalled === false && (
|
|
382
|
-
<div className="space-y-
|
|
383
|
-
<div className="flex items-center gap-2 text-
|
|
386
|
+
<div className="space-y-2">
|
|
387
|
+
<div className="flex items-center gap-2 text-[11px] text-danger font-sans">
|
|
384
388
|
<AlertTriangle size={10} /> llama-server not found
|
|
385
389
|
</div>
|
|
386
|
-
<code className="block text-
|
|
390
|
+
<code className="block text-[10px] font-mono text-text-3 bg-surface-2 px-2.5 py-1.5 rounded">brew install llama.cpp</code>
|
|
387
391
|
<button
|
|
388
392
|
onClick={checkLlama}
|
|
389
|
-
className="flex items-center gap-1.5 text-
|
|
393
|
+
className="flex items-center gap-1.5 text-[11px] font-sans text-accent hover:text-accent/80 transition-colors cursor-pointer"
|
|
390
394
|
>
|
|
391
395
|
<RotateCcw size={10} /> Recheck after install
|
|
392
396
|
</button>
|
|
@@ -399,44 +403,41 @@ export function LaunchModel() {
|
|
|
399
403
|
<div className="space-y-2">
|
|
400
404
|
{hasActiveAssistant && labAssistantBackend === selectedBackend ? (
|
|
401
405
|
<div className="space-y-2">
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
<><CheckCircle size={12} /> Setup complete</>
|
|
410
|
-
)}
|
|
411
|
-
</div>
|
|
406
|
+
{assistantRunning ? (
|
|
407
|
+
<StatusBanner variant="accent" icon={Loader2}>
|
|
408
|
+
Assistant is setting up {currentBackend?.label}...
|
|
409
|
+
</StatusBanner>
|
|
410
|
+
) : (
|
|
411
|
+
<StatusBanner variant="success" icon={CheckCircle}>Setup complete</StatusBanner>
|
|
412
|
+
)}
|
|
412
413
|
{!labAssistantMode && (
|
|
413
414
|
<button
|
|
414
415
|
onClick={() => setLabAssistantMode(true)}
|
|
415
|
-
className="w-full flex items-center justify-center gap-1.5
|
|
416
|
+
className="w-full flex items-center justify-center gap-1.5 h-8 text-[11px] font-sans font-medium text-text-1 bg-surface-2 hover:bg-surface-3 rounded transition-colors cursor-pointer"
|
|
416
417
|
>
|
|
417
418
|
View Assistant
|
|
418
419
|
</button>
|
|
419
420
|
)}
|
|
420
421
|
</div>
|
|
421
422
|
) : (
|
|
422
|
-
|
|
423
|
+
<div className="space-y-2">
|
|
423
424
|
<button
|
|
424
425
|
onClick={handleLaunchAssistant}
|
|
425
426
|
disabled={assistantLaunching}
|
|
426
427
|
className={cn(
|
|
427
|
-
'w-full flex items-center justify-center gap-1.5
|
|
428
|
+
'w-full flex items-center justify-center gap-1.5 h-8 text-[11px] font-sans font-medium rounded transition-colors cursor-pointer',
|
|
428
429
|
assistantLaunching ? 'bg-accent/20 text-accent' : 'bg-accent text-surface-0 hover:bg-accent/90',
|
|
429
430
|
)}
|
|
430
431
|
>
|
|
431
432
|
{assistantLaunching
|
|
432
|
-
? <><Loader2 size={
|
|
433
|
-
: <><Wrench size={
|
|
433
|
+
? <><Loader2 size={11} className="animate-spin" /> Starting Assistant...</>
|
|
434
|
+
: <><Wrench size={11} /> Setup {currentBackend?.label} with Assistant</>
|
|
434
435
|
}
|
|
435
436
|
</button>
|
|
436
|
-
<p className="text-
|
|
437
|
+
<p className="text-[10px] text-text-4 font-sans text-center">
|
|
437
438
|
An AI assistant will check your system and handle the installation.
|
|
438
439
|
</p>
|
|
439
|
-
|
|
440
|
+
</div>
|
|
440
441
|
)}
|
|
441
442
|
</div>
|
|
442
443
|
)}
|
|
@@ -444,22 +445,20 @@ export function LaunchModel() {
|
|
|
444
445
|
{isCompatible && currentBackend?.autoLaunch && (
|
|
445
446
|
<div className="space-y-2">
|
|
446
447
|
{serverRunning ? (
|
|
447
|
-
<
|
|
448
|
-
<CheckCircle size={12} /> Server Running
|
|
449
|
-
</div>
|
|
448
|
+
<StatusBanner variant="success" icon={CheckCircle}>Server Running</StatusBanner>
|
|
450
449
|
) : (
|
|
451
450
|
<button
|
|
452
451
|
disabled={!canLaunch}
|
|
453
452
|
onClick={handleLaunch}
|
|
454
453
|
className={cn(
|
|
455
|
-
'w-full flex items-center justify-center gap-1.5
|
|
454
|
+
'w-full flex items-center justify-center gap-1.5 h-8 text-[11px] font-sans font-medium rounded transition-colors cursor-pointer',
|
|
456
455
|
canLaunch ? 'bg-accent text-surface-0 hover:bg-accent/90' : 'bg-surface-3 text-text-4 cursor-not-allowed',
|
|
457
456
|
)}
|
|
458
457
|
>
|
|
459
458
|
{launching ? (
|
|
460
|
-
<><Loader2 size={
|
|
459
|
+
<><Loader2 size={11} className="animate-spin" /> Starting...</>
|
|
461
460
|
) : (
|
|
462
|
-
<><Play size={
|
|
461
|
+
<><Play size={11} /> Launch</>
|
|
463
462
|
)}
|
|
464
463
|
</button>
|
|
465
464
|
)}
|
|
@@ -468,9 +467,9 @@ export function LaunchModel() {
|
|
|
468
467
|
)}
|
|
469
468
|
</div>
|
|
470
469
|
)}
|
|
471
|
-
|
|
470
|
+
</div>
|
|
472
471
|
)}
|
|
473
|
-
</
|
|
472
|
+
</SidebarSection>
|
|
474
473
|
);
|
|
475
474
|
}
|
|
476
475
|
|
|
@@ -493,33 +492,35 @@ export function RuntimeConfig() {
|
|
|
493
492
|
}
|
|
494
493
|
|
|
495
494
|
return (
|
|
496
|
-
<
|
|
497
|
-
|
|
498
|
-
|
|
495
|
+
<SidebarSection
|
|
496
|
+
label="Runtimes"
|
|
497
|
+
collapsible
|
|
498
|
+
defaultOpen={false}
|
|
499
|
+
action={
|
|
499
500
|
<Tooltip content="Add runtime">
|
|
500
501
|
<button
|
|
501
502
|
onClick={() => setDialogOpen(true)}
|
|
502
503
|
className="p-1 text-text-4 hover:text-accent transition-colors cursor-pointer"
|
|
503
504
|
>
|
|
504
|
-
<Plus size={
|
|
505
|
+
<Plus size={12} />
|
|
505
506
|
</button>
|
|
506
507
|
</Tooltip>
|
|
507
|
-
|
|
508
|
-
|
|
508
|
+
}
|
|
509
|
+
>
|
|
509
510
|
{runtimes.length === 0 ? (
|
|
510
|
-
<div className="py-
|
|
511
|
-
<WifiOff size={
|
|
512
|
-
<p className="text-
|
|
511
|
+
<div className="py-6 text-center rounded-md bg-surface-1/50 border border-border-subtle">
|
|
512
|
+
<WifiOff size={16} className="mx-auto text-text-4 mb-2" />
|
|
513
|
+
<p className="text-[11px] text-text-3 font-sans">No runtimes configured</p>
|
|
513
514
|
<button
|
|
514
515
|
onClick={() => setDialogOpen(true)}
|
|
515
|
-
className="mt-2 flex items-center gap-1
|
|
516
|
+
className="mt-2 inline-flex items-center gap-1 px-2.5 py-1 text-[10px] font-sans text-accent hover:text-accent/80 transition-colors cursor-pointer"
|
|
516
517
|
>
|
|
517
|
-
<Plus size={
|
|
518
|
+
<Plus size={10} /> Add Runtime
|
|
518
519
|
</button>
|
|
519
520
|
</div>
|
|
520
521
|
) : (
|
|
521
522
|
<ScrollArea className="max-h-48">
|
|
522
|
-
<div className="space-y-
|
|
523
|
+
<div className="space-y-1 rounded-md bg-surface-1/50 border border-border-subtle p-2">
|
|
523
524
|
{runtimes.map((rt) => (
|
|
524
525
|
<RuntimeItem
|
|
525
526
|
key={rt.id}
|
|
@@ -538,7 +539,7 @@ export function RuntimeConfig() {
|
|
|
538
539
|
)}
|
|
539
540
|
|
|
540
541
|
<AddRuntimeDialog open={dialogOpen} onOpenChange={setDialogOpen} />
|
|
541
|
-
</
|
|
542
|
+
</SidebarSection>
|
|
542
543
|
);
|
|
543
544
|
}
|
|
544
545
|
|
|
@@ -560,7 +561,7 @@ export function RuntimeSection() {
|
|
|
560
561
|
|
|
561
562
|
if (!serverRunning || expanded) {
|
|
562
563
|
return (
|
|
563
|
-
<div className="space-y-
|
|
564
|
+
<div className="space-y-6">
|
|
564
565
|
<LaunchModel />
|
|
565
566
|
<RuntimeConfig />
|
|
566
567
|
</div>
|
|
@@ -568,24 +569,22 @@ export function RuntimeSection() {
|
|
|
568
569
|
}
|
|
569
570
|
|
|
570
571
|
return (
|
|
571
|
-
<div className="
|
|
572
|
-
<
|
|
573
|
-
|
|
574
|
-
<div className="
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
{activeModel || 'Ready'}{activeRt?.latency != null ? ` · ${Math.round(activeRt.latency)}ms` : ''}
|
|
578
|
-
</div>
|
|
572
|
+
<div className="flex items-center gap-2.5 px-2.5 py-2 rounded-md bg-surface-1/50 border border-border-subtle">
|
|
573
|
+
<span className="w-2 h-2 rounded-full bg-success flex-shrink-0" />
|
|
574
|
+
<div className="flex-1 min-w-0">
|
|
575
|
+
<div className="text-[11px] font-sans font-medium text-text-0 truncate">{runtimeLabel}</div>
|
|
576
|
+
<div className="text-[10px] text-text-4 font-sans truncate">
|
|
577
|
+
{activeModel || 'Ready'}{activeRt?.latency != null ? ` · ${Math.round(activeRt.latency)}ms` : ''}
|
|
579
578
|
</div>
|
|
580
|
-
<Tooltip content="Runtime settings">
|
|
581
|
-
<button
|
|
582
|
-
onClick={() => setExpanded(true)}
|
|
583
|
-
className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer"
|
|
584
|
-
>
|
|
585
|
-
<Settings2 size={13} />
|
|
586
|
-
</button>
|
|
587
|
-
</Tooltip>
|
|
588
579
|
</div>
|
|
580
|
+
<Tooltip content="Runtime settings">
|
|
581
|
+
<button
|
|
582
|
+
onClick={() => setExpanded(true)}
|
|
583
|
+
className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer"
|
|
584
|
+
>
|
|
585
|
+
<Settings2 size={12} />
|
|
586
|
+
</button>
|
|
587
|
+
</Tooltip>
|
|
589
588
|
</div>
|
|
590
589
|
);
|
|
591
590
|
}
|
|
@@ -25,7 +25,7 @@ export function SystemPromptEditor() {
|
|
|
25
25
|
const systemPrompt = useGrooveStore((s) => s.labSystemPrompt);
|
|
26
26
|
const setSystemPrompt = useGrooveStore((s) => s.setLabSystemPrompt);
|
|
27
27
|
const themeKey = useGrooveStore((s) => s.editorTheme);
|
|
28
|
-
const [collapsed, setCollapsed] = useState(
|
|
28
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
29
29
|
const containerRef = useRef(null);
|
|
30
30
|
const viewRef = useRef(null);
|
|
31
31
|
const themeCompartment = useRef(new Compartment());
|
|
@@ -87,20 +87,22 @@ export function SystemPromptEditor() {
|
|
|
87
87
|
}, [systemPrompt]);
|
|
88
88
|
|
|
89
89
|
return (
|
|
90
|
-
<div className="space-y-
|
|
90
|
+
<div className="space-y-3">
|
|
91
91
|
<button
|
|
92
92
|
onClick={() => setCollapsed(!collapsed)}
|
|
93
|
-
className="flex items-center gap-1.5 w-full cursor-pointer group"
|
|
93
|
+
className="flex items-center gap-1.5 w-full h-6 cursor-pointer group"
|
|
94
94
|
>
|
|
95
95
|
{collapsed ? (
|
|
96
|
-
<ChevronRight size={
|
|
96
|
+
<ChevronRight size={10} className="text-text-4 group-hover:text-text-2 transition-colors flex-shrink-0" />
|
|
97
97
|
) : (
|
|
98
|
-
<ChevronDown size={
|
|
98
|
+
<ChevronDown size={10} className="text-text-4 group-hover:text-text-2 transition-colors flex-shrink-0" />
|
|
99
99
|
)}
|
|
100
|
-
<span className="text-
|
|
100
|
+
<span className="text-[10px] font-semibold font-sans text-text-3 uppercase tracking-widest group-hover:text-text-2 transition-colors">
|
|
101
101
|
System Prompt
|
|
102
102
|
</span>
|
|
103
|
-
|
|
103
|
+
{charCount > 0 && (
|
|
104
|
+
<span className="text-[10px] font-mono text-text-4 ml-auto tabular-nums">{charCount}</span>
|
|
105
|
+
)}
|
|
104
106
|
</button>
|
|
105
107
|
|
|
106
108
|
<div className={cn(
|
|
@@ -109,7 +111,7 @@ export function SystemPromptEditor() {
|
|
|
109
111
|
)}>
|
|
110
112
|
<div
|
|
111
113
|
ref={containerRef}
|
|
112
|
-
className="h-full border border-border-subtle
|
|
114
|
+
className="h-full rounded-md bg-surface-1/50 border border-border-subtle overflow-hidden"
|
|
113
115
|
/>
|
|
114
116
|
</div>
|
|
115
117
|
</div>
|
|
@@ -10,13 +10,13 @@ export function TuningSlider({
|
|
|
10
10
|
const display = typeof fmt === 'function' ? fmt(value) : (typeof fmt === 'string' ? fmt : value);
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
|
-
<div className={cn('group flex items-center gap-2
|
|
14
|
-
<span className="text-
|
|
13
|
+
<div className={cn('group flex items-center gap-2.5 h-8', disabled && 'opacity-40 pointer-events-none', className)}>
|
|
14
|
+
<span className="text-[11px] text-text-2 font-sans w-[76px] shrink-0 truncate">{label}</span>
|
|
15
15
|
<div className="relative flex-1 flex items-center h-5">
|
|
16
16
|
<div className="absolute inset-y-0 flex items-center w-full pointer-events-none">
|
|
17
|
-
<div className="w-full h-
|
|
17
|
+
<div className="w-full h-[3px] rounded-full bg-surface-5">
|
|
18
18
|
<div
|
|
19
|
-
className="h-full rounded-full bg-accent/
|
|
19
|
+
className="h-full rounded-full bg-accent/60 group-hover:bg-accent transition-colors"
|
|
20
20
|
style={{ width: `${pct}%` }}
|
|
21
21
|
/>
|
|
22
22
|
</div>
|
|
@@ -30,20 +30,20 @@ export function TuningSlider({
|
|
|
30
30
|
disabled={disabled}
|
|
31
31
|
onChange={(e) => onChange(Number(e.target.value))}
|
|
32
32
|
className="relative w-full h-5 appearance-none bg-transparent cursor-pointer
|
|
33
|
-
[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-
|
|
33
|
+
[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-2.5 [&::-webkit-slider-thumb]:h-2.5
|
|
34
34
|
[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-accent
|
|
35
35
|
[&::-webkit-slider-thumb]:shadow-[0_0_0_2px_var(--color-surface-1)]
|
|
36
|
-
[&::-webkit-slider-thumb]:hover:
|
|
36
|
+
[&::-webkit-slider-thumb]:hover:shadow-[0_0_0_2px_var(--color-surface-1),0_0_6px_rgba(51,175,188,0.35)]
|
|
37
37
|
[&::-webkit-slider-thumb]:active:scale-110
|
|
38
38
|
[&::-webkit-slider-thumb]:transition-all
|
|
39
|
-
[&::-moz-range-thumb]:w-
|
|
39
|
+
[&::-moz-range-thumb]:w-2.5 [&::-moz-range-thumb]:h-2.5 [&::-moz-range-thumb]:rounded-full
|
|
40
40
|
[&::-moz-range-thumb]:bg-accent [&::-moz-range-thumb]:border-none
|
|
41
41
|
[&::-moz-range-thumb]:shadow-[0_0_0_2px_var(--color-surface-1)]
|
|
42
42
|
[&::-moz-range-track]:bg-transparent
|
|
43
43
|
disabled:cursor-not-allowed"
|
|
44
44
|
/>
|
|
45
45
|
</div>
|
|
46
|
-
<span className="text-
|
|
46
|
+
<span className="text-[11px] text-accent font-mono font-medium w-9 text-right shrink-0 tabular-nums">{display}</span>
|
|
47
47
|
</div>
|
|
48
48
|
);
|
|
49
49
|
}
|
|
@@ -91,6 +91,11 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
91
91
|
if (data) set({ subscription: { ...get().subscription, ...data } });
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
+
if (window.groove?.update?.onUpdateAvailable) {
|
|
95
|
+
window.groove.update.onUpdateAvailable((data) => {
|
|
96
|
+
set({ updateProgress: { percent: 0, version: data.version } });
|
|
97
|
+
});
|
|
98
|
+
}
|
|
94
99
|
if (window.groove?.update?.onUpdateProgress) {
|
|
95
100
|
window.groove.update.onUpdateProgress((data) => {
|
|
96
101
|
set({ updateProgress: data });
|
|
@@ -101,6 +106,18 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
101
106
|
set({ updateReady: data.version, updateModalOpen: true, updateProgress: null });
|
|
102
107
|
});
|
|
103
108
|
}
|
|
109
|
+
if (window.groove?.update?.getUpdateStatus) {
|
|
110
|
+
window.groove.update.getUpdateStatus().then((state) => {
|
|
111
|
+
if (!state) return;
|
|
112
|
+
if (state.downloaded) {
|
|
113
|
+
set({ updateReady: state.downloaded.version, updateProgress: null });
|
|
114
|
+
} else if (state.progress) {
|
|
115
|
+
set({ updateProgress: state.progress });
|
|
116
|
+
} else if (state.available) {
|
|
117
|
+
set({ updateProgress: { percent: 0, version: state.available.version } });
|
|
118
|
+
}
|
|
119
|
+
}).catch(() => {});
|
|
120
|
+
}
|
|
104
121
|
};
|
|
105
122
|
|
|
106
123
|
ws.onmessage = (event) => {
|
|
@@ -407,15 +407,22 @@ export const createAgentsSlice = (set, get) => ({
|
|
|
407
407
|
|
|
408
408
|
case 'read': {
|
|
409
409
|
if (tags.length === 0) { addSystemMsg('Usage: [read] #tag1 #tag2 ...'); return true; }
|
|
410
|
+
const userText = rest.replace(/#[\w/.-]+/g, '').trim();
|
|
410
411
|
get().addChatMessage(agentId, 'user', message, false);
|
|
411
412
|
const readBrief = await api.post('/keeper/pull', { tags });
|
|
412
413
|
if (readBrief?.brief) {
|
|
414
|
+
const memoryBlock = `\n\n---\nContext from memories (${tags.map(t => '#' + t).join(', ')}):\n\n${readBrief.brief}`;
|
|
415
|
+
set((s) => ({ thinkingAgents: new Set([...s.thinkingAgents, agentId]) }));
|
|
413
416
|
await api.post(`/agents/${encodeURIComponent(agentId)}/instruct`, {
|
|
414
|
-
message: `Here is context from my tagged memories:\n\n${readBrief.brief}`,
|
|
417
|
+
message: userText ? `${userText}${memoryBlock}` : `Here is context from my tagged memories:\n\n${readBrief.brief}`,
|
|
415
418
|
});
|
|
416
419
|
addSystemMsg(`Sent ${tags.map(t => '#' + t).join(', ')} to agent`);
|
|
417
420
|
} else {
|
|
418
421
|
addSystemMsg(`No memories found for ${tags.map(t => '#' + t).join(', ')}`);
|
|
422
|
+
if (userText) {
|
|
423
|
+
set((s) => ({ thinkingAgents: new Set([...s.thinkingAgents, agentId]) }));
|
|
424
|
+
await api.post(`/agents/${encodeURIComponent(agentId)}/instruct`, { message: userText });
|
|
425
|
+
}
|
|
419
426
|
}
|
|
420
427
|
return true;
|
|
421
428
|
}
|