natureco-cli 5.25.0 → 5.27.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/CHANGELOG.md +19 -0
- package/package.json +1 -1
- package/src/commands/repl.js +14 -8
- package/src/tools/agentic-runner.js +54 -13
- package/src/tools/workflow.js +80 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NatureCo CLI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [5.27.0] - 2026-07-04 — "GEMINI DÜZELTMESİ + DÖNGÜ BİRLEŞTİRME (2 sağlayıcı E2E)"
|
|
6
|
+
|
|
7
|
+
### 🐛 Düzeltme / ✨ İyileştirme
|
|
8
|
+
- **Gemini artik calisiyor**: `supportsToolCalls()` Gemini'yi robust agentic-runner yoluna aldi. Neden: `gemini-2.5-flash` bir "thinking" modeli — plan-bazli yoldaki dusuk `max_tokens` (orn. 20) cagrilarinda tum butceyi ic dusunmede harcayip BOS donuyordu. Agentic yol (yuksek max_tokens + native tool_calls/XML parse) bunu cozer. (Kod yorumu zaten "Gemini desteklemez" diyordu ama uygulanmamisti — tutarsizlik giderildi.)
|
|
9
|
+
- **Dongu birlestirme (dogrulanmis)**: `agentic-runner` artik 2 farkli saglayici ailesinde E2E dogrulandi — **MiniMax** (native XML tool-call) + **Gemini** (OpenAI-compat, gercek key ile dosya olusturma + memory recall "Gencay"). Groq/Ollama/localhost da bu yolu kullanir. OpenAI/Anthropic native `tool_calls` yolunda kalir (standart, dokunulmadi).
|
|
10
|
+
|
|
11
|
+
Doğrulama: Gemini gercek key ile E2E (dosya olusturma + hafiza); **484 test yeşil**, ESLint temiz.
|
|
12
|
+
|
|
13
|
+
## [5.26.0] - 2026-07-04 — "CANLI STREAMING + KEŞFET→DÜZENLE→DOĞRULA"
|
|
14
|
+
|
|
15
|
+
### ✨ Yeni
|
|
16
|
+
- **Canlı streaming (TTY)**: yanit artik token token akarak gelir (Hermes/Claude Code hissi). SSE → tool-call/skill XML'ini gizleyen filtre → model-adi sanitizer ("Ben MiniMax"→"Ben {bot}") zinciri; kelime chunk sinirinda bolunse bile sizinti yok. Non-TTY (pipe/CI) bloklu yola duser. `makeStreamFilter` + `makeSanitizeStream` (birim testli).
|
|
17
|
+
- **Keşfet→düzenle→doğrula**: ajana `file_search` (glob), `list_dir` ve MEVCUT dosyayi hedefli degistiren `edit_file` eklendi; sistem prompt'u "once oku, sonra edit_file ile duzenle, sonra calistirip dogrula" akisini ogretir. E2E dogrulandi (dosyayi oku→edit→node ile calistir→sonucu raporla).
|
|
18
|
+
|
|
19
|
+
### 🐛 Düzeltme
|
|
20
|
+
- **Okuma araclari icerigi modele donmuyordu**: `read_file`/`list_dir`/`file_search`/`bash` sonucu ajana sadece "OK" olarak donuyordu → model icerigi goremeyip "okudum ama bos" diye takiliyordu. Feedback artik gercek ciktiyi (content/output/results) modele verir.
|
|
21
|
+
|
|
22
|
+
Doğrulama: MiniMax ile E2E (oku→edit→calistir; streaming XML/model-adi sizintisiz); **484 test yeşil**, ESLint temiz.
|
|
23
|
+
|
|
5
24
|
## [5.25.0] - 2026-07-04 — "PROVIDER-AGNOSTİK + STREAMING ALTYAPISI"
|
|
6
25
|
|
|
7
26
|
### ✨ İyileştirme
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.27.0",
|
|
4
4
|
"description": "OpenClaw'dan daha güvenli, daha hızlı, daha ucuz AI agent CLI. Multi-agent, self-evolving skills, audit log, maliyet optimizasyonu ve NatureCo platform-native.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"natureco": "bin/natureco.js"
|
package/src/commands/repl.js
CHANGED
|
@@ -1493,16 +1493,20 @@ async function startRepl(args) {
|
|
|
1493
1493
|
messages[0] = { role: 'system', content: systemPrompt, _internal: true };
|
|
1494
1494
|
|
|
1495
1495
|
// v5.13.0: Run workflow FIRST for every request
|
|
1496
|
-
|
|
1496
|
+
// v5.26: TTY'de canli streaming — spinner yerine yanit akarak gelir (workflow stdout'a akitir)
|
|
1497
|
+
const wantStream = !!(process.stdout && process.stdout.isTTY);
|
|
1498
|
+
if (!wantStream) process.stdout.write(tui.styled('\r 🔧 workflow... ', { color: tui.PALETTE.muted }));
|
|
1497
1499
|
const wfToolDefs = getToolDefs();
|
|
1498
1500
|
const recentHistory = messages.length > 1 ? messages.slice(-10) : [];
|
|
1499
|
-
const wfResult = await executeTool('workflow', { action: 'run', task: line, conversationHistory: recentHistory }, wfToolDefs);
|
|
1501
|
+
const wfResult = await executeTool('workflow', { action: 'run', task: line, conversationHistory: recentHistory, stream: wantStream }, wfToolDefs);
|
|
1500
1502
|
const wf = wfResult?.result || {};
|
|
1501
|
-
if (wf.
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1503
|
+
if (!wf.streamed) {
|
|
1504
|
+
if (wf.success !== false) {
|
|
1505
|
+
const loaded = wf.skillsLoaded && wf.skillsLoaded.length > 0 ? ` [skill: ${wf.skillsLoaded.join(', ')}]` : '';
|
|
1506
|
+
process.stdout.write(tui.styled(` ✓ workflow${loaded}\n`, { color: tui.PALETTE.success }));
|
|
1507
|
+
} else {
|
|
1508
|
+
process.stdout.write(tui.styled(' ✗ workflow\n', { color: tui.PALETTE.danger }));
|
|
1509
|
+
}
|
|
1506
1510
|
}
|
|
1507
1511
|
|
|
1508
1512
|
if (wf.passthrough && wf.reply !== undefined && wf.reply !== null) {
|
|
@@ -1522,7 +1526,9 @@ async function startRepl(args) {
|
|
|
1522
1526
|
fixedReply = fixedReply.replace(/Ben\s+GPT[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
1523
1527
|
fixedReply = fixedReply.replace(/Ben\s+Asistan[\s\w\.]*/gi, 'Ben ' + displayBotName);
|
|
1524
1528
|
fixedReply = fixedReply.replace(/\*\*(?:MiniMax|Claude|GPT|M2\.5|M2)[^\*]*\*\*/gi, '**' + displayBotName + '**');
|
|
1525
|
-
|
|
1529
|
+
// streamed ise yanit zaten canli basildi (sanitize edilerek) — tekrar basma
|
|
1530
|
+
if (wf.streamed) process.stdout.write('\n');
|
|
1531
|
+
else process.stdout.write('\n' + fixedReply + '\n');
|
|
1526
1532
|
messages.push({ role: 'assistant', content: fixedReply });
|
|
1527
1533
|
totalInputTokens += Math.ceil(line.length / 4);
|
|
1528
1534
|
totalOutputTokens += Math.ceil(fullReply.length / 4);
|
|
@@ -21,7 +21,7 @@ const os = require('os');
|
|
|
21
21
|
// icinde approvals politikasini uyguluyor (isSafeCommand → direkt; tehlikeli → red;
|
|
22
22
|
// digerleri → allowlist/full moda gore). Yani keyfi/yikici komut calismaz.
|
|
23
23
|
// Diger ~85 arac (discord, telegram, cron, browser...) bilerek DISARIDA.
|
|
24
|
-
const DEFAULT_ALLOWED = ['write_file', 'read_file', 'edit_file', 'skill_view', 'bash'];
|
|
24
|
+
const DEFAULT_ALLOWED = ['write_file', 'read_file', 'edit_file', 'skill_view', 'bash', 'file_search', 'list_dir'];
|
|
25
25
|
|
|
26
26
|
const TOOL_ALIASES = {
|
|
27
27
|
write_file: 'write_file', create_file: 'write_file', writefile: 'write_file', write: 'write_file', create: 'write_file', save_file: 'write_file', new_file: 'write_file',
|
|
@@ -29,6 +29,8 @@ const TOOL_ALIASES = {
|
|
|
29
29
|
edit_file: 'edit_file', editfile: 'edit_file', str_replace: 'edit_file', str_replace_editor: 'edit_file', replace_in_file: 'edit_file',
|
|
30
30
|
skill_view: 'skill_view', skillview: 'skill_view', load_skill: 'skill_view', view_skill: 'skill_view', skill: 'skill_view',
|
|
31
31
|
bash: 'bash', run_command: 'bash', shell: 'bash', shell_command: 'bash', exec: 'bash', run_terminal: 'bash', terminal: 'bash', run: 'bash', command: 'bash',
|
|
32
|
+
file_search: 'file_search', glob: 'file_search', find_files: 'file_search', search_files: 'file_search', find: 'file_search',
|
|
33
|
+
list_dir: 'list_dir', ls: 'list_dir', dir: 'list_dir', list_directory: 'list_dir', filesystem: 'list_dir',
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
function expandHome(p) {
|
|
@@ -136,6 +138,35 @@ function makeStreamFilter(onText, onTool) {
|
|
|
136
138
|
};
|
|
137
139
|
}
|
|
138
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Streaming model-adi temizleyici: canli akarken model kendi adini soylerse
|
|
143
|
+
* (MiniMax/Claude/GPT...) persona adiyla degistir. Kelime sinirinda (bosluk)
|
|
144
|
+
* emit eder — boylece bir chunk'ta "Mini", digerinde "Max" gelse bile kelime
|
|
145
|
+
* tam olusmadan gonderilmez, sizinti olmaz. onText'e temizlenmis metin gider.
|
|
146
|
+
*/
|
|
147
|
+
function makeSanitizeStream(botName, onText) {
|
|
148
|
+
const name = botName || 'Asistan';
|
|
149
|
+
let buf = '';
|
|
150
|
+
const clean = (s) => s
|
|
151
|
+
.replace(/\bMiniMaxi?\b/gi, name)
|
|
152
|
+
.replace(/\bChatGPT\b/gi, name)
|
|
153
|
+
.replace(/\bGPT-?[0-9.]*\b/gi, name)
|
|
154
|
+
.replace(/\bClaude(?:\s*[0-9.]+)?\b/gi, name)
|
|
155
|
+
.replace(/\bM2\.5\b/gi, name);
|
|
156
|
+
return {
|
|
157
|
+
push(chunk) {
|
|
158
|
+
buf += (chunk || '');
|
|
159
|
+
const lastWs = Math.max(buf.lastIndexOf(' '), buf.lastIndexOf('\n'), buf.lastIndexOf('\t'));
|
|
160
|
+
if (lastWs >= 0) {
|
|
161
|
+
const safe = buf.slice(0, lastWs + 1);
|
|
162
|
+
buf = buf.slice(lastWs + 1);
|
|
163
|
+
if (onText) onText(clean(safe));
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
end() { if (buf && onText) onText(clean(buf)); buf = ''; },
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
139
170
|
function sanitizeArgs(args) {
|
|
140
171
|
const a = { ...args };
|
|
141
172
|
if (typeof a.content === 'string' && a.content.length > 160) a.content = `[${a.content.length} chars]`;
|
|
@@ -143,6 +174,24 @@ function sanitizeArgs(args) {
|
|
|
143
174
|
return a;
|
|
144
175
|
}
|
|
145
176
|
|
|
177
|
+
// Modele geri beslenecek metin. KRITIK: read_file/list_dir/file_search/bash gibi
|
|
178
|
+
// araclarda modelin GORMESI gereken asil cikti (content/output/results/items) geri
|
|
179
|
+
// verilmeli — yoksa model "okudum ama icerik yok" diye takilir.
|
|
180
|
+
function buildFeedback(norm, res) {
|
|
181
|
+
if (typeof res === 'string') return `${norm} sonucu:\n` + res.slice(0, 2500);
|
|
182
|
+
const ok = res && res.success !== false;
|
|
183
|
+
if (!ok) return `${norm} HATA: ${(res && res.error) || 'bilinmeyen hata'}`;
|
|
184
|
+
if (norm === 'write_file') return `write_file OK: ${res.path || ''} (${res.size != null ? res.size : '?'} bytes)`;
|
|
185
|
+
let body = '';
|
|
186
|
+
if (res.content != null) body = String(res.content).slice(0, 2500);
|
|
187
|
+
else if (res.output != null) body = String(res.output).slice(0, 2000);
|
|
188
|
+
else if (res.stdout != null) body = String(res.stdout).slice(0, 2000);
|
|
189
|
+
else if (res.results != null) body = JSON.stringify(res.results).slice(0, 2000);
|
|
190
|
+
else if (res.items != null) body = JSON.stringify(res.items).slice(0, 2000);
|
|
191
|
+
const head = `${norm} OK` + (res.path ? ` (${res.path})` : '');
|
|
192
|
+
return body ? `${head}:\n${body}` : head;
|
|
193
|
+
}
|
|
194
|
+
|
|
146
195
|
/**
|
|
147
196
|
* Tek bir parse edilmis cagriyi calistir.
|
|
148
197
|
* Donus: { records: [...], feedback: '...' }
|
|
@@ -214,17 +263,9 @@ async function executeCall(call, opts = {}) {
|
|
|
214
263
|
}
|
|
215
264
|
try {
|
|
216
265
|
const res = await fn(args);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
feedback = `${norm} sonucu:\n` + res.slice(0, 1500);
|
|
221
|
-
} else {
|
|
222
|
-
const ok = res && res.success !== false;
|
|
223
|
-
status = ok ? 'done' : 'error';
|
|
224
|
-
feedback = ok
|
|
225
|
-
? `${norm} OK` + (res.path ? `: ${res.path} (${res.size != null ? res.size : '?'} bytes)` : (res.output ? ': ' + String(res.output).slice(0, 300) : ''))
|
|
226
|
-
: `${norm} HATA: ${res && res.error}`;
|
|
227
|
-
}
|
|
266
|
+
const ok = typeof res === 'string' ? true : (res && res.success !== false);
|
|
267
|
+
const status = ok ? 'done' : 'error';
|
|
268
|
+
const feedback = buildFeedback(norm, res);
|
|
228
269
|
records.push({ tool: norm, status, args: sanitizeArgs(args), result: res });
|
|
229
270
|
return { records, feedback };
|
|
230
271
|
} catch (e) {
|
|
@@ -278,4 +319,4 @@ async function runAgentic({ callModel, systemPrompt, historyMessages, task, tool
|
|
|
278
319
|
return { records: allRecords, reply: finalReply, iterations };
|
|
279
320
|
}
|
|
280
321
|
|
|
281
|
-
module.exports = { parseAgenticCalls, stripProtocolTokens, executeCall, runAgentic, expandHome, makeStreamFilter, TOOL_ALIASES, DEFAULT_ALLOWED };
|
|
322
|
+
module.exports = { parseAgenticCalls, stripProtocolTokens, executeCall, runAgentic, expandHome, makeStreamFilter, makeSanitizeStream, TOOL_ALIASES, DEFAULT_ALLOWED };
|
package/src/tools/workflow.js
CHANGED
|
@@ -117,6 +117,58 @@ function apiCall(providerUrl, apiKey, body) {
|
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
// Streaming (SSE) varyanti — onDelta(token) canli cagirilir; sonunda {content, toolCalls} doner.
|
|
121
|
+
// OpenAI-uyumlu delta formati (MiniMax chatcompletion_v2, Gemini /openai, OpenAI, ...).
|
|
122
|
+
function apiCallStream(providerUrl, apiKey, body, onDelta) {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
const base = providerUrl.replace(/\/+$/, '');
|
|
125
|
+
const endpoint = isMiniMax(base)
|
|
126
|
+
? base + '/v1/text/chatcompletion_v2'
|
|
127
|
+
: isGemini(base)
|
|
128
|
+
? base + '/openai/chat/completions'
|
|
129
|
+
: base + '/chat/completions';
|
|
130
|
+
const req = https.request(endpoint, {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
headers: { 'Authorization': 'Bearer ' + apiKey, 'Content-Type': 'application/json' },
|
|
133
|
+
timeout: 120000,
|
|
134
|
+
}, (res) => {
|
|
135
|
+
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
136
|
+
let data = '';
|
|
137
|
+
res.on('data', c => data += c);
|
|
138
|
+
res.on('end', () => reject(new Error('HTTP ' + res.statusCode + ': ' + data.slice(0, 300))));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
res.setEncoding('utf8');
|
|
142
|
+
let sseBuf = '';
|
|
143
|
+
let content = '';
|
|
144
|
+
const toolCalls = [];
|
|
145
|
+
res.on('data', (chunk) => {
|
|
146
|
+
sseBuf += chunk;
|
|
147
|
+
const parts = sseBuf.split('\n');
|
|
148
|
+
sseBuf = parts.pop(); // eksik son satiri buffer'da tut
|
|
149
|
+
for (const line of parts) {
|
|
150
|
+
const l = line.trim();
|
|
151
|
+
if (!l.startsWith('data:')) continue;
|
|
152
|
+
const payload = l.slice(5).trim();
|
|
153
|
+
if (!payload || payload === '[DONE]') continue;
|
|
154
|
+
try {
|
|
155
|
+
const parsed = JSON.parse(payload);
|
|
156
|
+
const delta = parsed.choices?.[0]?.delta;
|
|
157
|
+
if (!delta) continue;
|
|
158
|
+
if (delta.content) { content += delta.content; if (onDelta) onDelta(delta.content); }
|
|
159
|
+
if (Array.isArray(delta.tool_calls)) { for (const tc of delta.tool_calls) toolCalls.push(tc); }
|
|
160
|
+
} catch { /* eksik/parcali JSON — atla */ }
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
res.on('end', () => resolve({ content, toolCalls }));
|
|
164
|
+
});
|
|
165
|
+
req.on('error', reject);
|
|
166
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
|
|
167
|
+
req.write(JSON.stringify(body));
|
|
168
|
+
req.end();
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
120
172
|
async function workflow(params) {
|
|
121
173
|
const { action, task, steps, name, workflowId, regenerateStep, conversationHistory } = params;
|
|
122
174
|
const cfg = loadConfig();
|
|
@@ -149,8 +201,10 @@ async function workflow(params) {
|
|
|
149
201
|
// Non-tool-calling model tespiti
|
|
150
202
|
function supportsToolCalls() {
|
|
151
203
|
const url = (providerUrl || '').toLowerCase();
|
|
152
|
-
//
|
|
204
|
+
// Bu saglayicilar OpenAI-tarzi tool_calls JSON'unu guvenilir uretmiyor (ya native
|
|
205
|
+
// XML uretiyor ya da dusuk max_tokens'ta bos donuyor) → robust agentic-runner yolu.
|
|
153
206
|
if (url.includes('minimax')) return false;
|
|
207
|
+
if (url.includes('generativelanguage') || url.includes('gemini')) return false; // Gemini 2.5 "thinking" — plan yolundaki dusuk max_tokens'ta bos doner
|
|
154
208
|
if (url.includes('ollama')) return false;
|
|
155
209
|
if (url.includes('localhost')) return false;
|
|
156
210
|
if (url.includes('groq')) return false;
|
|
@@ -176,11 +230,15 @@ async function workflow(params) {
|
|
|
176
230
|
'\n\nKullanabilecegin araclar:',
|
|
177
231
|
'- write_file: dosya olustur/uzerine yaz. parametreler: path (TAM yol), content (dosyanin TAM icerigi). Kod/oyun/site isteniyorsa TUM icerigi content icine yaz, kisaltma.',
|
|
178
232
|
'- read_file: dosya oku. parametre: path',
|
|
179
|
-
'- edit_file: dosyada metin degistir. parametreler: path, old_string, new_string',
|
|
180
|
-
'-
|
|
233
|
+
'- edit_file: MEVCUT dosyada metin degistir. parametreler: path, old_string (birebir mevcut metin), new_string. Bir dosyanin bir kismini degistirirken write_file yerine bunu kullan (tum dosyayi yeniden yazma).',
|
|
234
|
+
'- file_search: glob ile dosya bul. parametre: pattern (orn. "**/*.js", "src/**/*.json").',
|
|
235
|
+
'- list_dir: dizin icerigini listele. parametre: path',
|
|
236
|
+
'- bash: kabuk komutu calistir (npm, git, node, python, test, ls, grep/findstr, mkdir...). parametre: command. Guvenli komutlar dogrudan calisir; yikici/tehlikeli komutlar guvenlik politikasiyla engellenir. Icerik aramasi icin grep/findstr kullan.',
|
|
181
237
|
'- skill_view: gorevle ilgili bir skill yukle. parametre: name',
|
|
182
238
|
'\nKurallar:',
|
|
183
|
-
'-
|
|
239
|
+
'- MEVCUT bir dosyayi degistirmeden ONCE read_file ile oku; sonra edit_file ile hedefli degisiklik yap (tum dosyayi write_file ile ezme).',
|
|
240
|
+
'- Bir seyi nerede oldugunu bilmiyorsan once file_search/list_dir/bash(grep) ile kesfet.',
|
|
241
|
+
'- Kod yazdiktan/degistirdikten sonra gerektiginde bash ile calistir/test et (orn. "node dosya.js", "npm test"); hata cikarsa duzelt.',
|
|
184
242
|
'- Birden fazla dosya gerekiyorsa her biri icin AYRI write_file cagir.',
|
|
185
243
|
'- Kullanici "masaustu"/"desktop" dediyse ve tam yol vermediyse dosyayi buraya yaz: ' + desktop,
|
|
186
244
|
'- Goreceli yol yerine TAM yol kullan.',
|
|
@@ -194,7 +252,21 @@ async function workflow(params) {
|
|
|
194
252
|
for (const hm of conversationHistory) { if (hm._internal) continue; historyMessages.push({ role: hm.role, content: hm.content || '' }); }
|
|
195
253
|
}
|
|
196
254
|
|
|
255
|
+
// Streaming: yalnizca TTY'de ve caller (repl) stream:true gecince. SSE token'lari
|
|
256
|
+
// XML-gizleyen filtre + model-adi sanitizer zincirinden gecip stdout'a canli akar.
|
|
257
|
+
const streamOn = params.stream === true && !!(process.stdout && process.stdout.isTTY);
|
|
258
|
+
const botName = cfg.botName || 'Asistan';
|
|
259
|
+
const { makeStreamFilter, makeSanitizeStream } = require('./agentic-runner');
|
|
260
|
+
|
|
197
261
|
async function callModel(msgs) {
|
|
262
|
+
if (streamOn) {
|
|
263
|
+
const sani = makeSanitizeStream(botName, t => process.stdout.write(t));
|
|
264
|
+
const filter = makeStreamFilter(t => sani.push(t), null);
|
|
265
|
+
const body = { model, stream: true, messages: msgs, temperature: 0.3, max_tokens: 16000 };
|
|
266
|
+
const out = await apiCallStream(providerUrl, providerApiKey, body, d => filter.push(d));
|
|
267
|
+
filter.end(); sani.end();
|
|
268
|
+
return { content: out.content || '', toolCalls: out.toolCalls || [] };
|
|
269
|
+
}
|
|
198
270
|
const body = { model, stream: false, messages: msgs, temperature: 0.3, max_tokens: 16000 };
|
|
199
271
|
const r = await apiCall(providerUrl, providerApiKey, body);
|
|
200
272
|
const msg = r.choices?.[0]?.message || {};
|
|
@@ -210,7 +282,9 @@ async function workflow(params) {
|
|
|
210
282
|
let finalReply = reply || '';
|
|
211
283
|
if (fileWrites.length > 0) {
|
|
212
284
|
const lines = fileWrites.map(r => ` ✓ ${path.basename((r.result && r.result.path) || (r.args && r.args.path) || '')} olusturuldu (${(r.result && r.result.size != null) ? r.result.size : '?'} bytes)`).join('\n');
|
|
213
|
-
|
|
285
|
+
const fileSummary = 'Dosya(lar):\n' + lines;
|
|
286
|
+
finalReply = (finalReply ? finalReply + '\n\n' : '') + fileSummary;
|
|
287
|
+
if (streamOn) process.stdout.write('\n\n' + fileSummary + '\n');
|
|
214
288
|
}
|
|
215
289
|
const done = records.filter(r => r.status === 'done').length;
|
|
216
290
|
return {
|
|
@@ -222,6 +296,7 @@ async function workflow(params) {
|
|
|
222
296
|
completedSteps: done,
|
|
223
297
|
results: records.map((r, i) => ({ step: i + 1, ...r })),
|
|
224
298
|
passthrough: true,
|
|
299
|
+
streamed: streamOn,
|
|
225
300
|
reply: finalReply || 'Tamamlandi.',
|
|
226
301
|
};
|
|
227
302
|
} catch (e) {
|