groove-dev 0.27.135 → 0.27.136
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 +32 -0
- package/node_modules/@groove-dev/daemon/src/journalist.js +14 -12
- package/node_modules/@groove-dev/daemon/src/model-lab.js +50 -67
- package/node_modules/@groove-dev/daemon/src/rotator.js +1 -1
- package/node_modules/@groove-dev/gui/dist/assets/{index-D4JZyMWH.js → index-BrZHF7pK.js} +7 -7
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +5 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +8 -8
- package/node_modules/@groove-dev/gui/src/components/editor/file-tree.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +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/api.js +32 -0
- package/packages/daemon/src/journalist.js +14 -12
- package/packages/daemon/src/model-lab.js +50 -67
- package/packages/daemon/src/rotator.js +1 -1
- package/packages/gui/dist/assets/{index-D4JZyMWH.js → index-BrZHF7pK.js} +7 -7
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +5 -1
- package/packages/gui/src/components/agents/agent-file-tree.jsx +1 -1
- package/packages/gui/src/components/editor/code-editor.jsx +8 -8
- package/packages/gui/src/components/editor/file-tree.jsx +1 -1
- package/packages/gui/src/components/lab/system-prompt-editor.jsx +3 -3
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
8
8
|
<title>Groove GUI</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-BrZHF7pK.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-26L3JoZv.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-DoBZjiHE.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-DRQdprYi.js">
|
|
@@ -575,7 +575,7 @@ export function AgentFeed({ agent }) {
|
|
|
575
575
|
}
|
|
576
576
|
});
|
|
577
577
|
}
|
|
578
|
-
}, [timeline.length]);
|
|
578
|
+
}, [timeline.length, sending, isThinking]);
|
|
579
579
|
|
|
580
580
|
async function handleFileSelect(e) {
|
|
581
581
|
const files = Array.from(e.target.files || []);
|
|
@@ -621,6 +621,10 @@ export function AgentFeed({ agent }) {
|
|
|
621
621
|
|
|
622
622
|
setInput('');
|
|
623
623
|
setSending(true);
|
|
624
|
+
isAtBottomRef.current = true;
|
|
625
|
+
requestAnimationFrame(() => {
|
|
626
|
+
if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
627
|
+
});
|
|
624
628
|
try {
|
|
625
629
|
if (mode === 'query') {
|
|
626
630
|
await queryAgent(agent.id, text);
|
|
@@ -135,7 +135,7 @@ function TreeEntry({ entry, depth, onOpen, expandedDirs, onToggleDir, onContextM
|
|
|
135
135
|
onDoubleClick={handleCtxMenu}
|
|
136
136
|
onContextMenu={handleCtxMenu}
|
|
137
137
|
className={cn(
|
|
138
|
-
'w-full flex items-center gap-1.5 py-[3px] text-
|
|
138
|
+
'w-full flex items-center gap-1.5 py-[3px] text-xs font-sans cursor-pointer',
|
|
139
139
|
'hover:bg-surface-4/50 transition-colors text-left',
|
|
140
140
|
isDragging && 'opacity-50',
|
|
141
141
|
isDragOver && 'bg-accent/15 ring-1 ring-accent/50 rounded',
|
|
@@ -27,10 +27,10 @@ const LANGS = {
|
|
|
27
27
|
const grooveHighlightStyle = HighlightStyle.define([
|
|
28
28
|
{ tag: t.keyword, color: '#b07fd5' },
|
|
29
29
|
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#d4d8e0' },
|
|
30
|
-
{ tag: [t.function(t.variableName), t.labelName], color: '#
|
|
31
|
-
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#
|
|
30
|
+
{ tag: [t.function(t.variableName), t.labelName], color: '#dcc9a0' },
|
|
31
|
+
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#d4a07a' },
|
|
32
32
|
{ tag: [t.definition(t.name), t.separator], color: '#bcc2cd' },
|
|
33
|
-
{ tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#
|
|
33
|
+
{ tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#e0c589' },
|
|
34
34
|
{ tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.special(t.string)], color: '#89b4c4' },
|
|
35
35
|
{ tag: [t.meta, t.comment], color: '#6e7681', fontStyle: 'italic' },
|
|
36
36
|
{ tag: t.strong, fontWeight: 'bold' },
|
|
@@ -38,13 +38,13 @@ const grooveHighlightStyle = HighlightStyle.define([
|
|
|
38
38
|
{ tag: t.strikethrough, textDecoration: 'line-through' },
|
|
39
39
|
{ tag: t.link, color: '#7ab0df', textDecoration: 'underline' },
|
|
40
40
|
{ tag: t.heading, fontWeight: '400', color: '#bcc2cd' },
|
|
41
|
-
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#
|
|
42
|
-
{ tag: [t.processingInstruction, t.string, t.inserted], color: '#
|
|
41
|
+
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#d4a07a' },
|
|
42
|
+
{ tag: [t.processingInstruction, t.string, t.inserted], color: '#95b2b8' },
|
|
43
43
|
{ tag: t.invalid, color: '#d4736e' },
|
|
44
|
-
{ tag: t.propertyName, color: '#
|
|
44
|
+
{ tag: t.propertyName, color: '#dcc9a0' },
|
|
45
45
|
{ tag: [t.tagName], color: '#d4736e' },
|
|
46
|
-
{ tag: t.attributeName, color: '#
|
|
47
|
-
{ tag: t.attributeValue, color: '#
|
|
46
|
+
{ tag: t.attributeName, color: '#e0c589' },
|
|
47
|
+
{ tag: t.attributeValue, color: '#95b2b8' },
|
|
48
48
|
]);
|
|
49
49
|
|
|
50
50
|
// Custom theme overrides to match our design tokens
|
|
@@ -130,7 +130,7 @@ function TreeNode({ entry, depth = 0, activePath, onFileClick, onDirToggle, expa
|
|
|
130
130
|
onDoubleClick={handleContextMenu}
|
|
131
131
|
onContextMenu={handleContextMenu}
|
|
132
132
|
className={cn(
|
|
133
|
-
'w-full flex items-center gap-1.5 py-[3px] text-
|
|
133
|
+
'w-full flex items-center gap-1.5 py-[3px] text-xs font-sans cursor-pointer',
|
|
134
134
|
'hover:bg-surface-5 transition-colors text-left select-none',
|
|
135
135
|
isActive && 'bg-accent/10 text-text-0',
|
|
136
136
|
!isActive && 'text-text-1',
|
|
@@ -23,14 +23,14 @@ const editorTheme = EditorView.theme({
|
|
|
23
23
|
const promptHighlightStyle = HighlightStyle.define([
|
|
24
24
|
{ tag: t.keyword, color: '#b07fd5' },
|
|
25
25
|
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#d4d8e0' },
|
|
26
|
-
{ tag: [t.function(t.variableName), t.labelName], color: '#
|
|
26
|
+
{ tag: [t.function(t.variableName), t.labelName], color: '#dcc9a0' },
|
|
27
27
|
{ tag: [t.meta, t.comment], color: '#6e7681', fontStyle: 'italic' },
|
|
28
28
|
{ tag: t.strong, fontWeight: 'bold' },
|
|
29
29
|
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
30
30
|
{ tag: t.link, color: '#7ab0df', textDecoration: 'underline' },
|
|
31
31
|
{ tag: t.heading, fontWeight: '400', color: '#bcc2cd' },
|
|
32
|
-
{ tag: [t.processingInstruction, t.string, t.inserted], color: '#
|
|
33
|
-
{ tag: [t.atom, t.bool], color: '#
|
|
32
|
+
{ tag: [t.processingInstruction, t.string, t.inserted], color: '#95b2b8' },
|
|
33
|
+
{ tag: [t.atom, t.bool], color: '#d4a07a' },
|
|
34
34
|
{ tag: t.invalid, color: '#d4736e' },
|
|
35
35
|
]);
|
|
36
36
|
|
|
@@ -153,7 +153,7 @@ export function BreadcrumbBar({
|
|
|
153
153
|
return (
|
|
154
154
|
<header
|
|
155
155
|
className={cn(
|
|
156
|
-
'h-
|
|
156
|
+
'h-12 flex-shrink-0 flex items-center gap-3 px-4 bg-surface-3 border-b border-border relative',
|
|
157
157
|
darwinDrag && 'pl-24 electron-drag electron-no-drag-children',
|
|
158
158
|
)}
|
|
159
159
|
>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.136",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama, any local model.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -124,6 +124,38 @@ export function createApi(app, daemon) {
|
|
|
124
124
|
res.json({ status: 'ok', uptime: process.uptime() });
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
+
// Debug: test fetch to llama-server from daemon runtime
|
|
128
|
+
app.get('/api/lab/debug-fetch', async (req, res) => {
|
|
129
|
+
const target = req.query.url || 'http://localhost:8081/v1/chat/completions';
|
|
130
|
+
const log = [];
|
|
131
|
+
try {
|
|
132
|
+
log.push(`fetch → ${target}`);
|
|
133
|
+
log.push(`node ${process.version}, electron ${process.versions.electron || 'N/A'}`);
|
|
134
|
+
const start = Date.now();
|
|
135
|
+
const r = await fetch(target, {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
headers: { 'Content-Type': 'application/json' },
|
|
138
|
+
body: JSON.stringify({ model: 'Qwen3-0.6B-Q8_0.gguf', messages: [{ role: 'user', content: 'Say ok' }], stream: true, max_tokens: 10 }),
|
|
139
|
+
signal: AbortSignal.timeout(10000),
|
|
140
|
+
});
|
|
141
|
+
log.push(`status=${r.status} in ${Date.now() - start}ms`);
|
|
142
|
+
const reader = r.body.getReader();
|
|
143
|
+
let chunks = 0;
|
|
144
|
+
while (chunks < 5) {
|
|
145
|
+
const { done, value } = await reader.read();
|
|
146
|
+
if (done) break;
|
|
147
|
+
chunks++;
|
|
148
|
+
log.push(`chunk ${chunks}: ${new TextDecoder().decode(value).slice(0, 120)}`);
|
|
149
|
+
}
|
|
150
|
+
reader.cancel();
|
|
151
|
+
log.push(`total chunks read: ${chunks}`);
|
|
152
|
+
res.json({ ok: true, log });
|
|
153
|
+
} catch (err) {
|
|
154
|
+
log.push(`ERROR: ${err.message}`);
|
|
155
|
+
res.json({ ok: false, log, error: err.message });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
127
159
|
// List all agents
|
|
128
160
|
app.get('/api/agents', (req, res) => {
|
|
129
161
|
res.json(daemon.registry.getAll());
|
|
@@ -853,9 +853,8 @@ export class Journalist {
|
|
|
853
853
|
const agentLog = filteredLogs[agent.id];
|
|
854
854
|
const entries = agentLog?.entries || [];
|
|
855
855
|
|
|
856
|
-
// Layer 7 memory: discoveries (pointer
|
|
857
|
-
const
|
|
858
|
-
const discoveryPointer = hasDiscoveries ? 'See .groove/memory/agent-discoveries.jsonl for error→fix pairs.' : '';
|
|
856
|
+
// Layer 7 memory: discoveries (inline, not pointer — agents lose context with pointers), constraints, specializations
|
|
857
|
+
const discoveries = this.daemon.memory?.getDiscoveriesMarkdown(agent.role, 10, 1500) || '';
|
|
859
858
|
const constraints = this.daemon.memory?.getConstraintsMarkdown(2000) || '';
|
|
860
859
|
const specialization = this.daemon.memory?.getSpecialization(agent.id);
|
|
861
860
|
const specLine = specialization?.avgQualityScore != null
|
|
@@ -872,7 +871,7 @@ export class Journalist {
|
|
|
872
871
|
const recentTools = entries
|
|
873
872
|
.filter((e) => e.type === 'tool' || e.type === 'error')
|
|
874
873
|
.slice(-5)
|
|
875
|
-
.map((e) => `- ${e.type === 'error' ? 'ERROR ' : ''}${e.tool}: ${(e.input || e.text || '').slice(0,
|
|
874
|
+
.map((e) => `- ${e.type === 'error' ? 'ERROR ' : ''}${e.tool}: ${(e.input || e.text || '').slice(0, 200)}`)
|
|
876
875
|
.join('\n');
|
|
877
876
|
|
|
878
877
|
// Try AI-synthesized session summary
|
|
@@ -909,7 +908,7 @@ export class Journalist {
|
|
|
909
908
|
const fallbackRecentTools = entries
|
|
910
909
|
.filter((e) => e.type === 'tool' || e.type === 'error')
|
|
911
910
|
.slice(-5)
|
|
912
|
-
.map((e) => `- ${e.type === 'error' ? 'ERROR ' : ''}${e.tool}: ${(e.input || '').slice(0,
|
|
911
|
+
.map((e) => `- ${e.type === 'error' ? 'ERROR ' : ''}${e.tool}: ${(e.input || '').slice(0, 200)}`)
|
|
913
912
|
.join('\n');
|
|
914
913
|
|
|
915
914
|
const fallbackParts = [];
|
|
@@ -923,8 +922,8 @@ export class Journalist {
|
|
|
923
922
|
// For quality_degradation rotations, drop user messages (already in session summary)
|
|
924
923
|
const includeUserMessages = options.reason !== 'quality_degradation';
|
|
925
924
|
|
|
926
|
-
// Cap Original Task to
|
|
927
|
-
const originalTask = agent.prompt ? agent.prompt.slice(0,
|
|
925
|
+
// Cap Original Task to 1000 chars — task descriptions for debugging can be long
|
|
926
|
+
const originalTask = agent.prompt ? agent.prompt.slice(0, 1000) + (agent.prompt.length > 1000 ? '…' : '') : '';
|
|
928
927
|
|
|
929
928
|
let brief = [
|
|
930
929
|
`# Handoff Brief — ${agent.name} (${agent.role})`,
|
|
@@ -934,10 +933,13 @@ export class Journalist {
|
|
|
934
933
|
`Rotation: ${options.reason || 'manual'}${options.qualityScore ? ` (quality: ${options.qualityScore}/100)` : ''} | Tokens: ${agent.tokensUsed}`,
|
|
935
934
|
specLine,
|
|
936
935
|
``,
|
|
937
|
-
|
|
936
|
+
// Priority order: session summary (contains unresolved errors) first,
|
|
937
|
+
// then constraints, then discoveries, then tools — so the most critical
|
|
938
|
+
// debugging context survives even if the brief hits the hard cap.
|
|
939
|
+
sessionSummary ? `## Session Summary\n\n${sessionSummary}\n` : '',
|
|
938
940
|
constraints ? `## Project Constraints (must follow)\n\n${constraints}\n` : '',
|
|
941
|
+
discoveries ? `## Known Issues & Fixes\n\n${discoveries}\n` : '',
|
|
939
942
|
recentTools ? `## Last 5 Tool Calls\n\n${recentTools}\n` : '',
|
|
940
|
-
sessionSummary ? `## Session Summary\n\n${sessionSummary}\n` : '',
|
|
941
943
|
includeUserMessages && conversationSummary ? `## Recent User Messages\n\n${conversationSummary}\n` : '',
|
|
942
944
|
recentChain ? `## Rotation History\n\n${recentChain}\n` : '',
|
|
943
945
|
originalTask ? `## Original Task\n\n${originalTask}\n` : '',
|
|
@@ -946,9 +948,9 @@ export class Journalist {
|
|
|
946
948
|
`Continue seamlessly — finish the work and deliver the output.`,
|
|
947
949
|
].filter(Boolean).join('\n');
|
|
948
950
|
|
|
949
|
-
// Hard cap:
|
|
950
|
-
if (brief.length >
|
|
951
|
-
brief = brief.slice(0,
|
|
951
|
+
// Hard cap: 8000 chars — enough for debugging context without overwhelming the new agent
|
|
952
|
+
if (brief.length > 8000) {
|
|
953
|
+
brief = brief.slice(0, 7950) + '\n\n[Brief truncated — see session logs for full context]';
|
|
952
954
|
}
|
|
953
955
|
|
|
954
956
|
return brief;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { resolve } from 'path';
|
|
5
5
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from 'fs';
|
|
6
6
|
import { randomUUID } from 'crypto';
|
|
7
|
+
import { Readable } from 'stream';
|
|
7
8
|
|
|
8
9
|
const RUNTIME_TYPES = ['ollama', 'vllm', 'llama-cpp', 'tgi', 'openai-compatible'];
|
|
9
10
|
const DEFAULT_OLLAMA_ENDPOINT = 'http://localhost:11434';
|
|
@@ -223,10 +224,9 @@ export class ModelLab {
|
|
|
223
224
|
...this._buildParameterBody(parameters || {}),
|
|
224
225
|
};
|
|
225
226
|
|
|
226
|
-
const endpoint =
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (rt.apiKey) headers['Authorization'] = `Bearer ${rt.apiKey}`;
|
|
227
|
+
const endpoint = rt.endpoint.replace('localhost', '127.0.0.1');
|
|
228
|
+
const reqHeaders = { 'Content-Type': 'application/json' };
|
|
229
|
+
if (rt.apiKey) reqHeaders['Authorization'] = `Bearer ${rt.apiKey}`;
|
|
230
230
|
|
|
231
231
|
const requestStart = Date.now();
|
|
232
232
|
let ttft = null;
|
|
@@ -236,85 +236,61 @@ export class ModelLab {
|
|
|
236
236
|
let generationStart = null;
|
|
237
237
|
let fullContent = '';
|
|
238
238
|
|
|
239
|
-
const resp = await fetch(endpoint
|
|
239
|
+
const resp = await fetch(`${endpoint}/v1/chat/completions`, {
|
|
240
240
|
method: 'POST',
|
|
241
|
-
headers,
|
|
241
|
+
headers: reqHeaders,
|
|
242
242
|
body: JSON.stringify(body),
|
|
243
243
|
signal: AbortSignal.timeout(300000),
|
|
244
244
|
});
|
|
245
245
|
|
|
246
246
|
if (!resp.ok) {
|
|
247
|
-
let
|
|
248
|
-
try {
|
|
249
|
-
throw new Error(
|
|
247
|
+
let errMsg = `HTTP ${resp.status}`;
|
|
248
|
+
try { const e = await resp.json(); errMsg = e.error?.message || errMsg; } catch { /* ignore */ }
|
|
249
|
+
throw new Error(errMsg);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
const
|
|
253
|
-
const decoder = new TextDecoder();
|
|
252
|
+
const nodeStream = Readable.fromWeb(resp.body);
|
|
254
253
|
let buffer = '';
|
|
255
254
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
fullContent += delta.content;
|
|
288
|
-
completionTokens++;
|
|
289
|
-
onEvent({ type: 'token', content: delta.content });
|
|
290
|
-
}
|
|
291
|
-
if (chunk.usage) {
|
|
292
|
-
promptTokens = chunk.usage.prompt_tokens || 0;
|
|
293
|
-
totalTokens = chunk.usage.total_tokens || 0;
|
|
294
|
-
if (chunk.usage.completion_tokens) {
|
|
295
|
-
completionTokens = chunk.usage.completion_tokens;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
} catch { /* skip malformed chunk */ }
|
|
299
|
-
}
|
|
255
|
+
for await (const chunk of nodeStream) {
|
|
256
|
+
buffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
257
|
+
const lines = buffer.split('\n');
|
|
258
|
+
buffer = lines.pop() || '';
|
|
259
|
+
|
|
260
|
+
for (const line of lines) {
|
|
261
|
+
const trimmed = line.trim();
|
|
262
|
+
if (!trimmed || !trimmed.startsWith('data: ')) continue;
|
|
263
|
+
const data = trimmed.slice(6);
|
|
264
|
+
if (data === '[DONE]') continue;
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
const parsed = JSON.parse(data);
|
|
268
|
+
const delta = parsed.choices?.[0]?.delta;
|
|
269
|
+
if (delta?.reasoning_content) {
|
|
270
|
+
if (ttft === null) { ttft = Date.now() - requestStart; generationStart = Date.now(); }
|
|
271
|
+
completionTokens++;
|
|
272
|
+
onEvent({ type: 'reasoning', content: delta.reasoning_content });
|
|
273
|
+
}
|
|
274
|
+
if (delta?.content) {
|
|
275
|
+
if (ttft === null) { ttft = Date.now() - requestStart; generationStart = Date.now(); }
|
|
276
|
+
fullContent += delta.content;
|
|
277
|
+
completionTokens++;
|
|
278
|
+
onEvent({ type: 'token', content: delta.content });
|
|
279
|
+
}
|
|
280
|
+
if (parsed.usage) {
|
|
281
|
+
promptTokens = parsed.usage.prompt_tokens || 0;
|
|
282
|
+
totalTokens = parsed.usage.total_tokens || 0;
|
|
283
|
+
if (parsed.usage.completion_tokens) completionTokens = parsed.usage.completion_tokens;
|
|
284
|
+
}
|
|
285
|
+
} catch { /* skip malformed chunk */ }
|
|
300
286
|
}
|
|
301
|
-
} finally {
|
|
302
|
-
reader.releaseLock();
|
|
303
287
|
}
|
|
304
288
|
|
|
305
289
|
const generationTime = generationStart ? Date.now() - generationStart : Date.now() - requestStart;
|
|
306
290
|
const tokensPerSec = generationTime > 0 ? (completionTokens / (generationTime / 1000)) : 0;
|
|
307
291
|
|
|
308
|
-
let memoryUsage = null;
|
|
309
|
-
if (rt.type === 'ollama') {
|
|
310
|
-
memoryUsage = await this.getOllamaMemoryUsage(rt.endpoint);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
292
|
if (sessionId) {
|
|
314
|
-
this._appendToSession(sessionId, messages, {
|
|
315
|
-
role: 'assistant',
|
|
316
|
-
content: fullContent,
|
|
317
|
-
});
|
|
293
|
+
this._appendToSession(sessionId, messages, { role: 'assistant', content: fullContent });
|
|
318
294
|
}
|
|
319
295
|
|
|
320
296
|
onEvent({
|
|
@@ -326,9 +302,16 @@ export class ModelLab {
|
|
|
326
302
|
promptTokens,
|
|
327
303
|
completionTokens,
|
|
328
304
|
generationTime,
|
|
329
|
-
memoryUsage,
|
|
305
|
+
memoryUsage: null,
|
|
330
306
|
},
|
|
331
307
|
});
|
|
308
|
+
|
|
309
|
+
if (rt.type === 'ollama') {
|
|
310
|
+
try {
|
|
311
|
+
const mem = await this.getOllamaMemoryUsage(rt.endpoint);
|
|
312
|
+
if (mem) onEvent({ type: 'memory', usage: mem });
|
|
313
|
+
} catch { /* ignore */ }
|
|
314
|
+
}
|
|
332
315
|
}
|
|
333
316
|
|
|
334
317
|
_buildParameterBody(params) {
|
|
@@ -343,7 +343,7 @@ export class Rotator extends EventEmitter {
|
|
|
343
343
|
reason: options.reason || 'manual',
|
|
344
344
|
oldTokens: agent.tokensUsed,
|
|
345
345
|
contextUsage: agent.contextUsage,
|
|
346
|
-
brief: brief.slice(0,
|
|
346
|
+
brief: brief.slice(0, 6000),
|
|
347
347
|
}, agent.workingDir, agent.teamId);
|
|
348
348
|
}
|
|
349
349
|
|