groove-dev 0.27.142 → 0.27.144
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/api.js +1086 -6532
- package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
- package/node_modules/@groove-dev/daemon/src/index.js +3 -0
- package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
- package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
- package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
- package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
- package/node_modules/@groove-dev/daemon/src/process.js +2 -2
- package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
- package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
- package/node_modules/@groove-dev/daemon/src/routes/agents.js +889 -0
- package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
- package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
- package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
- package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
- package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
- package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
- package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
- package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
- package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
- package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
- package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
- package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
- package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
- package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
- package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BcoF6_eF.js +1012 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.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/{packages/gui/src/app.jsx → node_modules/@groove-dev/gui/src/App.jsx} +0 -2
- package/node_modules/@groove-dev/gui/src/app.css +35 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +144 -31
- package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
- package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
- package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
- package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
- package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
- package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
- package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
- package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
- package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
- package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/editor/selection-menu.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
- package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
- package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +195 -14
- package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +286 -102
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
- package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
- package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
- package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
- package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
- package/node_modules/@groove-dev/gui/src/lib/status.js +24 -24
- package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +34 -3144
- package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +452 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +227 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +17 -6
- package/node_modules/@groove-dev/gui/src/views/models.jsx +410 -509
- package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
- package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +1086 -6532
- package/packages/daemon/src/gateways/manager.js +35 -1
- package/packages/daemon/src/index.js +3 -0
- package/packages/daemon/src/journalist.js +23 -13
- package/packages/daemon/src/mlx-server.js +365 -0
- package/packages/daemon/src/model-lab.js +308 -12
- package/packages/daemon/src/pm.js +1 -1
- package/packages/daemon/src/process.js +2 -2
- package/packages/daemon/src/providers/local.js +36 -8
- package/packages/daemon/src/registry.js +21 -5
- package/packages/daemon/src/routes/agents.js +889 -0
- package/packages/daemon/src/routes/coordination.js +318 -0
- package/packages/daemon/src/routes/files.js +751 -0
- package/packages/daemon/src/routes/integrations.js +485 -0
- package/packages/daemon/src/routes/network.js +1784 -0
- package/packages/daemon/src/routes/providers.js +755 -0
- package/packages/daemon/src/routes/schedules.js +110 -0
- package/packages/daemon/src/routes/teams.js +650 -0
- package/packages/daemon/src/scheduler.js +456 -24
- package/packages/daemon/src/teams.js +1 -1
- package/packages/daemon/src/validate.js +38 -1
- package/packages/daemon/templates/mlx-setup.json +12 -0
- package/packages/daemon/templates/tgi-setup.json +1 -1
- package/packages/daemon/templates/vllm-setup.json +1 -1
- package/packages/gui/dist/assets/index-BcoF6_eF.js +1012 -0
- package/packages/gui/dist/assets/index-Dd7qhiEd.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/{node_modules/@groove-dev/gui/src/app.jsx → packages/gui/src/App.jsx} +0 -2
- package/packages/gui/src/app.css +35 -0
- package/packages/gui/src/components/agents/agent-config.jsx +1 -128
- package/packages/gui/src/components/agents/agent-feed.jsx +144 -31
- package/packages/gui/src/components/agents/agent-node.jsx +8 -13
- package/packages/gui/src/components/agents/code-review.jsx +159 -122
- package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
- package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
- package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
- package/packages/gui/src/components/automations/automation-card.jsx +274 -0
- package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
- package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
- package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
- package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
- package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
- package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
- package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
- package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
- package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
- package/packages/gui/src/components/dashboard/token-chart.jsx +4 -4
- package/packages/gui/src/components/editor/selection-menu.jsx +2 -0
- package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
- package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
- package/packages/gui/src/components/lab/parameter-panel.jsx +195 -14
- package/packages/gui/src/components/lab/runtime-config.jsx +286 -102
- package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
- package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
- package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
- package/packages/gui/src/components/network/network-health.jsx +2 -2
- package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
- package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
- package/packages/gui/src/components/ui/sheet.jsx +5 -2
- package/packages/gui/src/lib/cron.js +64 -0
- package/packages/gui/src/lib/status.js +24 -24
- package/packages/gui/src/lib/theme-hex.js +1 -0
- package/packages/gui/src/stores/groove.js +34 -3144
- package/packages/gui/src/stores/helpers.js +10 -0
- package/packages/gui/src/stores/slices/agents-slice.js +452 -0
- package/packages/gui/src/stores/slices/automations-slice.js +96 -0
- package/packages/gui/src/stores/slices/chat-slice.js +227 -0
- package/packages/gui/src/stores/slices/editor-slice.js +285 -0
- package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
- package/packages/gui/src/stores/slices/network-slice.js +361 -0
- package/packages/gui/src/stores/slices/preview-slice.js +109 -0
- package/packages/gui/src/stores/slices/providers-slice.js +897 -0
- package/packages/gui/src/stores/slices/teams-slice.js +413 -0
- package/packages/gui/src/stores/slices/ui-slice.js +98 -0
- package/packages/gui/src/views/agents.jsx +5 -5
- package/packages/gui/src/views/dashboard.jsx +12 -13
- package/packages/gui/src/views/marketplace.jsx +191 -3
- package/packages/gui/src/views/model-lab.jsx +17 -6
- package/packages/gui/src/views/models.jsx +410 -509
- package/packages/gui/src/views/network.jsx +3 -3
- package/packages/gui/src/views/settings.jsx +81 -94
- package/packages/gui/src/views/teams.jsx +40 -483
- package/SECURITY_SWEEP.md +0 -228
- package/TRAINING_DATA_v4.md +0 -6
- package/node_modules/@groove-dev/gui/dist/assets/index-Bjd91ufV.js +0 -984
- package/node_modules/@groove-dev/gui/dist/assets/index-BqdwIFn4.css +0 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -322
- package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
- package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
- package/packages/gui/dist/assets/index-Bjd91ufV.js +0 -984
- package/packages/gui/dist/assets/index-BqdwIFn4.css +0 -1
- package/packages/gui/src/components/agents/agent-chat.jsx +0 -322
- package/packages/gui/src/views/preview.jsx +0 -6
- package/packages/gui/src/views/subscription-panel.jsx +0 -327
- package/test.py +0 -571
|
@@ -4,29 +4,29 @@ import { Tooltip } from '../ui/tooltip';
|
|
|
4
4
|
import { cn } from '../../lib/cn';
|
|
5
5
|
import { fmtNum } from '../../lib/format';
|
|
6
6
|
import { HEX, hexAlpha } from '../../lib/theme-hex';
|
|
7
|
-
import { Zap, Clock, Cpu, Hash, Timer, Link } from 'lucide-react';
|
|
7
|
+
import { Zap, Clock, Cpu, Hash, Timer, Link, MessageSquare, TrendingUp, ArrowUpRight, ArrowDownRight, Layers } from 'lucide-react';
|
|
8
8
|
|
|
9
9
|
function TtftGauge({ value }) {
|
|
10
10
|
if (value == null) {
|
|
11
11
|
return (
|
|
12
|
-
<div className="text-center py-
|
|
12
|
+
<div className="text-center py-3">
|
|
13
13
|
<div className="text-2xl font-mono font-bold text-text-4 tabular-nums">--</div>
|
|
14
|
-
<div className="text-2xs text-text-4 font-sans mt-
|
|
14
|
+
<div className="text-2xs text-text-4 font-sans mt-0.5">TTFT</div>
|
|
15
15
|
</div>
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
18
|
const color = value < 200 ? 'text-success' : value < 500 ? 'text-warning' : 'text-danger';
|
|
19
19
|
return (
|
|
20
|
-
<div className="text-center py-
|
|
20
|
+
<div className="text-center py-3">
|
|
21
21
|
<div className={cn('text-2xl font-mono font-bold tabular-nums', color)}>
|
|
22
22
|
{Math.round(value)}
|
|
23
23
|
</div>
|
|
24
|
-
<div className="text-2xs text-text-3 font-sans mt-
|
|
24
|
+
<div className="text-2xs text-text-3 font-sans mt-0.5">ms TTFT</div>
|
|
25
25
|
</div>
|
|
26
26
|
);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function Sparkline({ data, width = 140, height =
|
|
29
|
+
function Sparkline({ data, width = 140, height = 28, color = HEX.accent }) {
|
|
30
30
|
if (!data || data.length < 2) {
|
|
31
31
|
return <div className="flex-shrink-0" style={{ width, height }} />;
|
|
32
32
|
}
|
|
@@ -54,11 +54,11 @@ function Sparkline({ data, width = 140, height = 32, color = HEX.accent }) {
|
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function MetricRow({ icon: Icon, label, value, unit, tooltip }) {
|
|
57
|
+
function MetricRow({ icon: Icon, label, value, unit, tooltip, accent }) {
|
|
58
58
|
const content = (
|
|
59
|
-
<div className="flex items-center justify-between py-
|
|
59
|
+
<div className="flex items-center justify-between py-1.5">
|
|
60
60
|
<div className="flex items-center gap-2">
|
|
61
|
-
<Icon size={11} className=
|
|
61
|
+
<Icon size={11} className={cn('flex-shrink-0', accent ? 'text-accent' : 'text-text-4')} />
|
|
62
62
|
<span className="text-2xs text-text-3 font-sans">{label}</span>
|
|
63
63
|
</div>
|
|
64
64
|
<span className="text-xs font-mono font-medium text-text-1 tabular-nums">
|
|
@@ -69,48 +69,203 @@ function MetricRow({ icon: Icon, label, value, unit, tooltip }) {
|
|
|
69
69
|
return tooltip ? <Tooltip content={tooltip}>{content}</Tooltip> : content;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function SectionLabel({ children }) {
|
|
73
|
+
return (
|
|
74
|
+
<span className="text-2xs font-semibold font-sans text-text-4 uppercase tracking-wider">{children}</span>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function CapacityBar({ used, total, label }) {
|
|
79
|
+
if (!total) return null;
|
|
80
|
+
const pct = Math.min((used / total) * 100, 100);
|
|
81
|
+
const color = pct < 50 ? 'bg-success' : pct < 80 ? 'bg-warning' : 'bg-danger';
|
|
82
|
+
return (
|
|
83
|
+
<div className="space-y-1">
|
|
84
|
+
<div className="flex items-center justify-between">
|
|
85
|
+
<span className="text-2xs text-text-3 font-sans">{label}</span>
|
|
86
|
+
<span className="text-2xs font-mono text-text-2 tabular-nums">{Math.round(pct)}%</span>
|
|
87
|
+
</div>
|
|
88
|
+
<div className="h-1.5 bg-surface-3 rounded-full overflow-hidden">
|
|
89
|
+
<div className={cn('h-full rounded-full transition-all duration-300', color)} style={{ width: `${pct}%` }} />
|
|
90
|
+
</div>
|
|
91
|
+
<div className="text-2xs font-mono text-text-4 tabular-nums">
|
|
92
|
+
{fmtNum(used)} / {fmtNum(total)}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function SparklineSection({ icon: Icon, label, value, unit, data, color = HEX.accent }) {
|
|
99
|
+
return (
|
|
100
|
+
<div className="space-y-1.5">
|
|
101
|
+
<div className="flex items-center justify-between">
|
|
102
|
+
<div className="flex items-center gap-1.5">
|
|
103
|
+
<Icon size={11} className="text-accent" />
|
|
104
|
+
<span className="text-2xs text-text-3 font-sans">{label}</span>
|
|
105
|
+
</div>
|
|
106
|
+
<span className="text-sm font-mono font-bold text-text-0 tabular-nums">
|
|
107
|
+
{value != null ? value : '--'}{unit && value != null ? <span className="text-text-4 ml-0.5 text-xs">{unit}</span> : ''}
|
|
108
|
+
</span>
|
|
109
|
+
</div>
|
|
110
|
+
<Sparkline data={data} color={color} />
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
72
115
|
export function MetricsPanel() {
|
|
73
116
|
const metrics = useGrooveStore((s) => s.labMetrics);
|
|
74
117
|
const activeRuntime = useGrooveStore((s) => s.labActiveRuntime);
|
|
118
|
+
const activeSession = useGrooveStore((s) => s.labActiveSession);
|
|
119
|
+
const sessions = useGrooveStore((s) => s.labSessions);
|
|
120
|
+
|
|
121
|
+
const session = sessions.find((s) => s.id === activeSession);
|
|
122
|
+
const messageCount = session?.messages?.length || 0;
|
|
123
|
+
const generationCount = metrics.generationCount || 0;
|
|
124
|
+
const avgTps = metrics.tokensPerSecHistory.length > 0
|
|
125
|
+
? (metrics.tokensPerSecHistory.reduce((a, b) => a + (b || 0), 0) / metrics.tokensPerSecHistory.length)
|
|
126
|
+
: null;
|
|
127
|
+
const avgTtft = metrics.ttftHistory?.length > 0
|
|
128
|
+
? (metrics.ttftHistory.reduce((a, b) => a + (b || 0), 0) / metrics.ttftHistory.length)
|
|
129
|
+
: null;
|
|
130
|
+
const peakTps = metrics.tokensPerSecHistory.length > 0
|
|
131
|
+
? Math.max(...metrics.tokensPerSecHistory.filter((v) => v != null))
|
|
132
|
+
: null;
|
|
133
|
+
|
|
134
|
+
const sessionDuration = metrics.sessionStartTime
|
|
135
|
+
? Math.round((Date.now() - metrics.sessionStartTime) / 1000)
|
|
136
|
+
: null;
|
|
137
|
+
|
|
138
|
+
function fmtDuration(secs) {
|
|
139
|
+
if (secs == null) return null;
|
|
140
|
+
if (secs < 60) return `${secs}s`;
|
|
141
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m ${secs % 60}s`;
|
|
142
|
+
return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
|
|
143
|
+
}
|
|
75
144
|
|
|
76
145
|
return (
|
|
77
|
-
<div className="space-y-
|
|
78
|
-
{/*
|
|
146
|
+
<div className="space-y-4">
|
|
147
|
+
{/* Performance */}
|
|
79
148
|
<TtftGauge value={metrics.ttft} />
|
|
80
149
|
|
|
150
|
+
<SparklineSection
|
|
151
|
+
icon={Zap}
|
|
152
|
+
label="Tokens/sec"
|
|
153
|
+
value={metrics.tokensPerSec != null ? metrics.tokensPerSec.toFixed(1) : null}
|
|
154
|
+
data={metrics.tokensPerSecHistory}
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
{metrics.ttftHistory?.length > 1 && (
|
|
158
|
+
<SparklineSection
|
|
159
|
+
icon={Clock}
|
|
160
|
+
label="TTFT Trend"
|
|
161
|
+
value={metrics.ttft != null ? `${Math.round(metrics.ttft)}` : null}
|
|
162
|
+
unit="ms"
|
|
163
|
+
data={metrics.ttftHistory}
|
|
164
|
+
color={HEX.warning || HEX.accent}
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
167
|
+
|
|
81
168
|
<div className="h-px bg-border-subtle" />
|
|
82
169
|
|
|
83
|
-
{/*
|
|
84
|
-
<div className="space-y-
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
170
|
+
{/* Token Usage */}
|
|
171
|
+
<div className="space-y-1">
|
|
172
|
+
<SectionLabel>Tokens</SectionLabel>
|
|
173
|
+
<MetricRow
|
|
174
|
+
icon={Hash}
|
|
175
|
+
label="Total"
|
|
176
|
+
value={metrics.totalTokens > 0 ? fmtNum(metrics.totalTokens) : null}
|
|
177
|
+
tooltip="Total tokens generated this session"
|
|
178
|
+
/>
|
|
179
|
+
{metrics.promptTokens > 0 && (
|
|
180
|
+
<MetricRow
|
|
181
|
+
icon={ArrowUpRight}
|
|
182
|
+
label="Prompt"
|
|
183
|
+
value={fmtNum(metrics.promptTokens)}
|
|
184
|
+
tooltip="Input/prompt tokens"
|
|
185
|
+
/>
|
|
186
|
+
)}
|
|
187
|
+
{metrics.completionTokens > 0 && (
|
|
188
|
+
<MetricRow
|
|
189
|
+
icon={ArrowDownRight}
|
|
190
|
+
label="Completion"
|
|
191
|
+
value={fmtNum(metrics.completionTokens)}
|
|
192
|
+
tooltip="Output/completion tokens"
|
|
193
|
+
/>
|
|
194
|
+
)}
|
|
95
195
|
</div>
|
|
96
196
|
|
|
97
197
|
<div className="h-px bg-border-subtle" />
|
|
98
198
|
|
|
99
|
-
{/* Stats */}
|
|
100
|
-
<div className="space-y-
|
|
199
|
+
{/* Session Stats */}
|
|
200
|
+
<div className="space-y-1">
|
|
201
|
+
<SectionLabel>Session</SectionLabel>
|
|
202
|
+
<MetricRow
|
|
203
|
+
icon={MessageSquare}
|
|
204
|
+
label="Messages"
|
|
205
|
+
value={messageCount > 0 ? messageCount : null}
|
|
206
|
+
tooltip="Messages in current session"
|
|
207
|
+
/>
|
|
208
|
+
<MetricRow
|
|
209
|
+
icon={Layers}
|
|
210
|
+
label="Generations"
|
|
211
|
+
value={generationCount > 0 ? generationCount : null}
|
|
212
|
+
tooltip="Number of model generations"
|
|
213
|
+
/>
|
|
214
|
+
{avgTps != null && (
|
|
215
|
+
<MetricRow
|
|
216
|
+
icon={TrendingUp}
|
|
217
|
+
label="Avg TPS"
|
|
218
|
+
value={avgTps.toFixed(1)}
|
|
219
|
+
tooltip="Average tokens/sec across session"
|
|
220
|
+
/>
|
|
221
|
+
)}
|
|
222
|
+
{avgTtft != null && (
|
|
223
|
+
<MetricRow
|
|
224
|
+
icon={Clock}
|
|
225
|
+
label="Avg TTFT"
|
|
226
|
+
value={`${Math.round(avgTtft)}`}
|
|
227
|
+
unit="ms"
|
|
228
|
+
tooltip="Average time to first token"
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
{peakTps != null && (
|
|
232
|
+
<MetricRow
|
|
233
|
+
icon={Zap}
|
|
234
|
+
label="Peak TPS"
|
|
235
|
+
value={peakTps.toFixed(1)}
|
|
236
|
+
accent
|
|
237
|
+
tooltip="Highest tokens/sec this session"
|
|
238
|
+
/>
|
|
239
|
+
)}
|
|
240
|
+
<MetricRow
|
|
241
|
+
icon={Timer}
|
|
242
|
+
label="Duration"
|
|
243
|
+
value={fmtDuration(sessionDuration)}
|
|
244
|
+
tooltip="Time since first generation"
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<div className="h-px bg-border-subtle" />
|
|
249
|
+
|
|
250
|
+
{/* Resources */}
|
|
251
|
+
<div className="space-y-1">
|
|
252
|
+
<SectionLabel>Resources</SectionLabel>
|
|
101
253
|
<MetricRow
|
|
102
254
|
icon={Cpu}
|
|
103
255
|
label="Memory"
|
|
104
256
|
value={metrics.memory != null ? `${(metrics.memory / 1024 / 1024).toFixed(0)}` : null}
|
|
105
257
|
unit="MB"
|
|
106
|
-
tooltip="GPU/CPU memory usage"
|
|
107
|
-
/>
|
|
108
|
-
<MetricRow
|
|
109
|
-
icon={Hash}
|
|
110
|
-
label="Total Tokens"
|
|
111
|
-
value={metrics.totalTokens > 0 ? fmtNum(metrics.totalTokens) : null}
|
|
112
|
-
tooltip="Total tokens generated this session"
|
|
258
|
+
tooltip="Current GPU/CPU memory usage"
|
|
113
259
|
/>
|
|
260
|
+
{metrics.peakMemory != null && metrics.peakMemory > 0 && (
|
|
261
|
+
<MetricRow
|
|
262
|
+
icon={Cpu}
|
|
263
|
+
label="Peak Memory"
|
|
264
|
+
value={`${(metrics.peakMemory / 1024 / 1024).toFixed(0)}`}
|
|
265
|
+
unit="MB"
|
|
266
|
+
tooltip="Peak memory usage this session"
|
|
267
|
+
/>
|
|
268
|
+
)}
|
|
114
269
|
<MetricRow
|
|
115
270
|
icon={Timer}
|
|
116
271
|
label="Gen Time"
|
|
@@ -1,24 +1,151 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { useGrooveStore } from '../../stores/groove';
|
|
3
4
|
import { TuningSlider } from '../ui/slider';
|
|
4
5
|
import { Tooltip } from '../ui/tooltip';
|
|
5
|
-
import { RotateCcw } from 'lucide-react';
|
|
6
|
+
import { RotateCcw, Brain, Braces, ChevronRight, Dices, X, Plus } from 'lucide-react';
|
|
7
|
+
import { cn } from '../../lib/cn';
|
|
6
8
|
|
|
7
9
|
const DEFAULTS = {
|
|
8
|
-
temperature: 0.7, topP: 0.9, topK: 40, repeatPenalty: 1.1,
|
|
10
|
+
temperature: 0.7, topP: 0.9, topK: 40, minP: 0, repeatPenalty: 1.1,
|
|
9
11
|
maxTokens: 2048, frequencyPenalty: 0, presencePenalty: 0,
|
|
12
|
+
thinking: false, seed: null, stopSequences: [], jsonMode: false,
|
|
10
13
|
};
|
|
11
14
|
|
|
12
|
-
const
|
|
15
|
+
const SAMPLING_SLIDERS = [
|
|
13
16
|
{ key: 'temperature', label: 'Temperature', min: 0, max: 2, step: 0.01 },
|
|
14
17
|
{ key: 'topP', label: 'Top P', min: 0, max: 1, step: 0.01 },
|
|
18
|
+
{ key: 'minP', label: 'Min P', min: 0, max: 1, step: 0.01 },
|
|
15
19
|
{ key: 'topK', label: 'Top K', min: 0, max: 200, step: 1 },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const PENALTY_SLIDERS = [
|
|
16
23
|
{ key: 'repeatPenalty', label: 'Repeat Penalty', min: 0, max: 2, step: 0.01 },
|
|
17
|
-
{ key: 'maxTokens', label: 'Max Tokens', min: 1, max: 32768, step: 1, format: (v) => v >= 1000 ? `${(v / 1000).toFixed(1)}k` : v },
|
|
18
24
|
{ key: 'frequencyPenalty', label: 'Freq Penalty', min: 0, max: 2, step: 0.01 },
|
|
19
25
|
{ key: 'presencePenalty', label: 'Pres Penalty', min: 0, max: 2, step: 0.01 },
|
|
20
26
|
];
|
|
21
27
|
|
|
28
|
+
function ParamGroup({ title, defaultOpen = true, children }) {
|
|
29
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
30
|
+
return (
|
|
31
|
+
<div>
|
|
32
|
+
<button
|
|
33
|
+
onClick={() => setOpen(!open)}
|
|
34
|
+
className="w-full flex items-center gap-1.5 py-1.5 text-left cursor-pointer group"
|
|
35
|
+
>
|
|
36
|
+
<ChevronRight
|
|
37
|
+
size={10}
|
|
38
|
+
className={cn('text-text-4 transition-transform duration-150 flex-shrink-0', open && 'rotate-90')}
|
|
39
|
+
/>
|
|
40
|
+
<span className="text-2xs font-semibold text-text-4 font-sans uppercase tracking-wider">{title}</span>
|
|
41
|
+
</button>
|
|
42
|
+
{open && <div className="space-y-0.5 pb-1">{children}</div>}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function ToggleRow({ icon: Icon, label, active, onClick, description }) {
|
|
48
|
+
return (
|
|
49
|
+
<Tooltip content={description} side="right">
|
|
50
|
+
<button
|
|
51
|
+
onClick={onClick}
|
|
52
|
+
className={cn(
|
|
53
|
+
'w-full flex items-center gap-2 px-2 py-1.5 rounded-sm transition-colors cursor-pointer text-left',
|
|
54
|
+
active ? 'bg-accent/8' : 'hover:bg-surface-3',
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
<Icon size={12} className={cn(active ? 'text-accent' : 'text-text-4')} />
|
|
58
|
+
<span className="flex-1 text-2xs font-sans text-text-2">{label}</span>
|
|
59
|
+
<span className={cn('text-2xs font-mono', active ? 'text-accent' : 'text-text-4')}>
|
|
60
|
+
{active ? 'ON' : 'OFF'}
|
|
61
|
+
</span>
|
|
62
|
+
</button>
|
|
63
|
+
</Tooltip>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function SeedInput({ value, onChange }) {
|
|
68
|
+
function handleRandomize() {
|
|
69
|
+
onChange(Math.floor(Math.random() * 2147483647));
|
|
70
|
+
}
|
|
71
|
+
return (
|
|
72
|
+
<div className="flex items-center gap-1.5 px-2 py-1">
|
|
73
|
+
<span className="text-2xs text-text-3 font-sans flex-shrink-0 w-12">Seed</span>
|
|
74
|
+
<input
|
|
75
|
+
type="number"
|
|
76
|
+
min={0}
|
|
77
|
+
value={value ?? ''}
|
|
78
|
+
onChange={(e) => onChange(e.target.value === '' ? null : parseInt(e.target.value, 10))}
|
|
79
|
+
placeholder="Random"
|
|
80
|
+
className="flex-1 min-w-0 h-6 px-1.5 text-2xs font-mono bg-surface-1 border border-border rounded-sm text-text-1 placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent tabular-nums"
|
|
81
|
+
/>
|
|
82
|
+
<Tooltip content="Randomize seed">
|
|
83
|
+
<button onClick={handleRandomize} className="p-0.5 text-text-4 hover:text-accent transition-colors cursor-pointer">
|
|
84
|
+
<Dices size={12} />
|
|
85
|
+
</button>
|
|
86
|
+
</Tooltip>
|
|
87
|
+
{value != null && (
|
|
88
|
+
<button onClick={() => onChange(null)} className="p-0.5 text-text-4 hover:text-danger transition-colors cursor-pointer">
|
|
89
|
+
<X size={10} />
|
|
90
|
+
</button>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function StopSequenceInput({ sequences, onChange }) {
|
|
97
|
+
const [input, setInput] = useState('');
|
|
98
|
+
|
|
99
|
+
function handleAdd() {
|
|
100
|
+
const val = input.trim();
|
|
101
|
+
if (!val || sequences.length >= 10 || sequences.includes(val)) return;
|
|
102
|
+
onChange([...sequences, val]);
|
|
103
|
+
setInput('');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function handleRemove(idx) {
|
|
107
|
+
onChange(sequences.filter((_, i) => i !== idx));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function handleKeyDown(e) {
|
|
111
|
+
if (e.key === 'Enter') { e.preventDefault(); handleAdd(); }
|
|
112
|
+
if (e.key === 'Backspace' && !input && sequences.length) handleRemove(sequences.length - 1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div className="px-2 py-1 space-y-1.5">
|
|
117
|
+
<span className="text-2xs text-text-3 font-sans">Stop Sequences</span>
|
|
118
|
+
{sequences.length > 0 && (
|
|
119
|
+
<div className="flex flex-wrap gap-1">
|
|
120
|
+
{sequences.map((s, i) => (
|
|
121
|
+
<span key={i} className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-surface-3 rounded text-2xs font-mono text-text-2">
|
|
122
|
+
{s.length > 12 ? s.slice(0, 12) + '...' : s}
|
|
123
|
+
<button onClick={() => handleRemove(i)} className="text-text-4 hover:text-danger cursor-pointer"><X size={8} /></button>
|
|
124
|
+
</span>
|
|
125
|
+
))}
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
<div className="flex gap-1">
|
|
129
|
+
<input
|
|
130
|
+
value={input}
|
|
131
|
+
onChange={(e) => setInput(e.target.value)}
|
|
132
|
+
onKeyDown={handleKeyDown}
|
|
133
|
+
placeholder="Add sequence..."
|
|
134
|
+
maxLength={100}
|
|
135
|
+
className="flex-1 min-w-0 h-6 px-1.5 text-2xs font-mono bg-surface-1 border border-border rounded-sm text-text-1 placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent"
|
|
136
|
+
/>
|
|
137
|
+
<button
|
|
138
|
+
onClick={handleAdd}
|
|
139
|
+
disabled={!input.trim() || sequences.length >= 10}
|
|
140
|
+
className="p-1 text-text-4 hover:text-accent disabled:opacity-30 transition-colors cursor-pointer"
|
|
141
|
+
>
|
|
142
|
+
<Plus size={12} />
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
22
149
|
export function ParameterPanel() {
|
|
23
150
|
const parameters = useGrooveStore((s) => s.labParameters);
|
|
24
151
|
const setParameter = useGrooveStore((s) => s.setLabParameter);
|
|
@@ -42,18 +169,72 @@ export function ParameterPanel() {
|
|
|
42
169
|
</button>
|
|
43
170
|
</Tooltip>
|
|
44
171
|
</div>
|
|
45
|
-
|
|
172
|
+
|
|
173
|
+
{/* Mode toggles */}
|
|
174
|
+
<div className="space-y-px pb-1">
|
|
175
|
+
<ToggleRow
|
|
176
|
+
icon={Brain}
|
|
177
|
+
label="Thinking"
|
|
178
|
+
active={parameters.thinking}
|
|
179
|
+
onClick={() => setParameter('thinking', !parameters.thinking)}
|
|
180
|
+
description="Chain-of-thought for supported models (Qwen3, etc.)"
|
|
181
|
+
/>
|
|
182
|
+
<ToggleRow
|
|
183
|
+
icon={Braces}
|
|
184
|
+
label="JSON Mode"
|
|
185
|
+
active={parameters.jsonMode}
|
|
186
|
+
onClick={() => setParameter('jsonMode', !parameters.jsonMode)}
|
|
187
|
+
description="Constrain output to valid JSON"
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
{/* Sampling group */}
|
|
192
|
+
<ParamGroup title="Sampling">
|
|
193
|
+
{SAMPLING_SLIDERS.map((s) => (
|
|
194
|
+
<TuningSlider
|
|
195
|
+
key={s.key}
|
|
196
|
+
label={s.label}
|
|
197
|
+
value={parameters[s.key]}
|
|
198
|
+
onChange={(v) => setParameter(s.key, v)}
|
|
199
|
+
min={s.min}
|
|
200
|
+
max={s.max}
|
|
201
|
+
step={s.step}
|
|
202
|
+
/>
|
|
203
|
+
))}
|
|
204
|
+
</ParamGroup>
|
|
205
|
+
|
|
206
|
+
{/* Generation group */}
|
|
207
|
+
<ParamGroup title="Generation">
|
|
46
208
|
<TuningSlider
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
formatValue={s.format}
|
|
209
|
+
label="Max Tokens"
|
|
210
|
+
value={parameters.maxTokens}
|
|
211
|
+
onChange={(v) => setParameter('maxTokens', v)}
|
|
212
|
+
min={1}
|
|
213
|
+
max={32768}
|
|
214
|
+
step={1}
|
|
215
|
+
formatValue={(v) => v >= 1000 ? `${(v / 1000).toFixed(1)}k` : v}
|
|
55
216
|
/>
|
|
56
|
-
|
|
217
|
+
<SeedInput value={parameters.seed} onChange={(v) => setParameter('seed', v)} />
|
|
218
|
+
<StopSequenceInput
|
|
219
|
+
sequences={parameters.stopSequences || []}
|
|
220
|
+
onChange={(v) => setParameter('stopSequences', v)}
|
|
221
|
+
/>
|
|
222
|
+
</ParamGroup>
|
|
223
|
+
|
|
224
|
+
{/* Penalties group — collapsed by default */}
|
|
225
|
+
<ParamGroup title="Penalties" defaultOpen={false}>
|
|
226
|
+
{PENALTY_SLIDERS.map((s) => (
|
|
227
|
+
<TuningSlider
|
|
228
|
+
key={s.key}
|
|
229
|
+
label={s.label}
|
|
230
|
+
value={parameters[s.key]}
|
|
231
|
+
onChange={(v) => setParameter(s.key, v)}
|
|
232
|
+
min={s.min}
|
|
233
|
+
max={s.max}
|
|
234
|
+
step={s.step}
|
|
235
|
+
/>
|
|
236
|
+
))}
|
|
237
|
+
</ParamGroup>
|
|
57
238
|
</div>
|
|
58
239
|
);
|
|
59
240
|
}
|