@noobdemon/noob-cli 1.9.7 → 1.9.9
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/package.json +1 -1
- package/src/agent.js +3 -0
- package/src/repl.js +25 -29
- package/src/tokens.js +22 -0
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -513,6 +513,9 @@ export async function runAgent({ history, model, signal, onTool, onStatus, onDel
|
|
|
513
513
|
|
|
514
514
|
const system = buildSystem(history, extraToolsDoc, goal, recentSessions);
|
|
515
515
|
const message = buildUserMessage(history);
|
|
516
|
+
// Cập nhật context tokens realtime — đếm system + message sau khi build xong.
|
|
517
|
+
// Mỗi iteration (tool call, steer) history thay đổi → size thay đổi → cần update.
|
|
518
|
+
tokenMeter?.setContext(countTokens(system) + countTokens(message));
|
|
516
519
|
tokenMeter?.addInput(countTokens(message));
|
|
517
520
|
onStatus?.("thinking");
|
|
518
521
|
onDelta?.({ type: "step-start" });
|
package/src/repl.js
CHANGED
|
@@ -5,7 +5,7 @@ import chalk from "chalk";
|
|
|
5
5
|
import { createTui } from "./tui.js";
|
|
6
6
|
import { runAgent, maybeSummarize } from "./agent.js";
|
|
7
7
|
import { runSubAgent, spawnAgentToolsDoc, MAX_SUBAGENT_DEPTH } from "./subagent.js";
|
|
8
|
-
import { TokenMeter } from "./tokens.js";
|
|
8
|
+
import { TokenMeter, countMessages, CONTEXT_WINDOW } from "./tokens.js";
|
|
9
9
|
import { stream, usage, ApiError, resetMemoryToken } from "./api.js";
|
|
10
10
|
import { runTool, describe, DESTRUCTIVE, addRoot, removeRoot, listRoots, OutOfScopeError, nearestExistingDir } from "./tools.js";
|
|
11
11
|
import { MODELS, PROVIDERS, findModel, providerColor, DEFAULT_MODEL } from "./models.js";
|
|
@@ -1186,7 +1186,7 @@ NGUYÊN TẮC:
|
|
|
1186
1186
|
// Nhờ vậy người dùng LUÔN thấy đồng hồ + token đang chạy, kể cả khi treo chờ y/n.
|
|
1187
1187
|
const tickMeta = () => {
|
|
1188
1188
|
const elapsed = ((Date.now() - t0) / 1000).toFixed(0);
|
|
1189
|
-
tui.setMeta(`${elapsed}s · ${tokenMeter.
|
|
1189
|
+
tui.setMeta(`${elapsed}s · ${tokenMeter.formatWithPct()}`);
|
|
1190
1190
|
};
|
|
1191
1191
|
const tick = (label) => {
|
|
1192
1192
|
tui.status(c.dim(`${label}…`));
|
|
@@ -1237,6 +1237,8 @@ NGUYÊN TẮC:
|
|
|
1237
1237
|
? text + `\n\n[File người dùng nhắc tới bằng @: ${files.join(", ")} — đọc bằng read_file nếu cần.]`
|
|
1238
1238
|
: text;
|
|
1239
1239
|
state.history.push({ role: "user", content });
|
|
1240
|
+
// Tính context tokens realtime — đếm system prompt + history trước khi gửi.
|
|
1241
|
+
tokenMeter.setContext(countMessages(state.history));
|
|
1240
1242
|
if (process.stdin.isTTY && !state.steerHintShown) {
|
|
1241
1243
|
console.log(c.dim(" " + t.steerHint));
|
|
1242
1244
|
state.steerHintShown = true;
|
|
@@ -1341,34 +1343,28 @@ NGUYÊN TẮC:
|
|
|
1341
1343
|
} finally {
|
|
1342
1344
|
abort = null;
|
|
1343
1345
|
tui.setBusy(false);
|
|
1344
|
-
//
|
|
1345
|
-
//
|
|
1346
|
-
//
|
|
1346
|
+
// Auto-compact dựa trên context tokens thay vì chars.
|
|
1347
|
+
// 80% context window (160k tokens) → auto compact.
|
|
1348
|
+
// 70% (140k tokens) → cảnh báo mạnh.
|
|
1349
|
+
// 60% (120k tokens) → nhắc nhẹ.
|
|
1347
1350
|
try {
|
|
1348
|
-
const
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
);
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
// giữ model chạy mượt khi user mải làm việc, không để phiên phình mãi.
|
|
1355
|
-
// Dùng cờ _autoCompacting chống re-entrant (nếu compact lâu, lượt sau
|
|
1356
|
-
// tới trước khi xong thì bỏ qua).
|
|
1357
|
-
if (totalChars > 240000 && !state._autoCompacting) {
|
|
1351
|
+
const totalTokens = countMessages(state.history);
|
|
1352
|
+
tokenMeter.setContext(totalTokens);
|
|
1353
|
+
const k = Math.round(totalTokens / 1000);
|
|
1354
|
+
const pct = Math.round((totalTokens / CONTEXT_WINDOW) * 100);
|
|
1355
|
+
// Mốc 3 (≥80% — 160k tokens): TỰ ĐỘNG compact.
|
|
1356
|
+
if (totalTokens >= CONTEXT_WINDOW * 0.8 && !state._autoCompacting) {
|
|
1358
1357
|
state._autoCompacting = true;
|
|
1359
|
-
console.log(c.accent(
|
|
1358
|
+
console.log(c.accent(` ⚡ ${t.autoCompactTrigger(k)} (${pct}% context)`));
|
|
1360
1359
|
tui.setBusy(true, t.compactRunning);
|
|
1361
1360
|
try {
|
|
1362
1361
|
const ok = await maybeSummarize(state.history, { model: state.model, force: true });
|
|
1363
1362
|
tui.setBusy(false);
|
|
1364
1363
|
if (ok) {
|
|
1365
|
-
const
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
);
|
|
1369
|
-
const aK = Math.round(afterChars / 1000);
|
|
1370
|
-
const pct = totalChars > 0 ? Math.round(((totalChars - afterChars) / totalChars) * 100) : 0;
|
|
1371
|
-
console.log(c.ok(" " + t.autoCompactDone(k, aK, pct)));
|
|
1364
|
+
const afterTokens = countMessages(state.history);
|
|
1365
|
+
const aK = Math.round(afterTokens / 1000);
|
|
1366
|
+
const saved = totalTokens > 0 ? Math.round(((totalTokens - afterTokens) / totalTokens) * 100) : 0;
|
|
1367
|
+
console.log(c.ok(` ${t.autoCompactDone(k, aK, saved)} (${Math.round((afterTokens / CONTEXT_WINDOW) * 100)}% context)`));
|
|
1372
1368
|
state._longSessionWarned = false;
|
|
1373
1369
|
persist();
|
|
1374
1370
|
} else {
|
|
@@ -1380,13 +1376,13 @@ NGUYÊN TẮC:
|
|
|
1380
1376
|
} finally {
|
|
1381
1377
|
state._autoCompacting = false;
|
|
1382
1378
|
}
|
|
1383
|
-
} else if (
|
|
1384
|
-
// Mốc 2 (
|
|
1385
|
-
console.log(c.err(
|
|
1379
|
+
} else if (totalTokens >= CONTEXT_WINDOW * 0.7) {
|
|
1380
|
+
// Mốc 2 (≥70% — 140k tokens): cảnh báo mạnh.
|
|
1381
|
+
console.log(c.err(` ⚠ ${t.veryLongSession(k)} (${pct}% context)`));
|
|
1386
1382
|
state._longSessionWarned = true;
|
|
1387
|
-
} else if (
|
|
1388
|
-
// Mốc 1 (120k
|
|
1389
|
-
console.log(c.dim(
|
|
1383
|
+
} else if (totalTokens >= CONTEXT_WINDOW * 0.6 && !state._longSessionWarned) {
|
|
1384
|
+
// Mốc 1 (≥60% — 120k tokens): nhắc nhẹ một lần.
|
|
1385
|
+
console.log(c.dim(` ⓘ ${t.longSession(k)} (${pct}% context)`));
|
|
1390
1386
|
state._longSessionWarned = true;
|
|
1391
1387
|
}
|
|
1392
1388
|
} catch {}
|
package/src/tokens.js
CHANGED
|
@@ -32,11 +32,16 @@ export function countMessages(messages = []) {
|
|
|
32
32
|
// window đủ rộng (256 chars) để qua mọi ranh giới token thực tế của cl100k_base
|
|
33
33
|
// (token dài nhất ~ vài chục byte).
|
|
34
34
|
const TAIL_WINDOW = 256;
|
|
35
|
+
// Context window tối đa của model (200k tokens). Dùng để tính % usage realtime.
|
|
36
|
+
export const CONTEXT_WINDOW = 200000;
|
|
35
37
|
|
|
36
38
|
export class TokenMeter {
|
|
37
39
|
constructor() {
|
|
38
40
|
this.input = 0;
|
|
39
41
|
this.output = 0;
|
|
42
|
+
// Context size (tokens) hiện tại của messages sắp gửi lên API.
|
|
43
|
+
// Được set từ repl.js trước mỗi lượt gọi, cập nhật realtime.
|
|
44
|
+
this.contextTokens = 0;
|
|
40
45
|
// Phần đầu output đã "commit" — không đụng vào nữa.
|
|
41
46
|
this._committedChars = 0; // số ký tự đã đẩy qua khỏi tail window
|
|
42
47
|
this._committedTokens = 0; // tổng token tương ứng đã cộng vào this.output
|
|
@@ -92,9 +97,26 @@ export class TokenMeter {
|
|
|
92
97
|
const fmt = (n) => (n >= 1000 ? (n / 1000).toFixed(1) + "k" : String(n));
|
|
93
98
|
return `↑${fmt(this.input)} ↓${fmt(this.output)} (${fmt(this.total)})`;
|
|
94
99
|
}
|
|
100
|
+
// Định dạng kèm context usage: "↑1.2k ↓340 (1.5k) · ctx 45%".
|
|
101
|
+
formatWithPct() {
|
|
102
|
+
const base = this.format();
|
|
103
|
+
const pct = this.contextPct();
|
|
104
|
+
if (pct === null) return base;
|
|
105
|
+
return `${base} · ctx ${pct}%`;
|
|
106
|
+
}
|
|
107
|
+
// Phần trăm context window đã dùng (0–100). Trả null nếu chưa có data.
|
|
108
|
+
contextPct() {
|
|
109
|
+
if (!this.contextTokens) return null;
|
|
110
|
+
return Math.min(100, Math.round((this.contextTokens / CONTEXT_WINDOW) * 100));
|
|
111
|
+
}
|
|
112
|
+
// Đặt context size (tokens) — gọi từ repl.js trước mỗi lượt API.
|
|
113
|
+
setContext(n) {
|
|
114
|
+
this.contextTokens = Math.max(0, n | 0);
|
|
115
|
+
}
|
|
95
116
|
reset() {
|
|
96
117
|
this.input = 0;
|
|
97
118
|
this.output = 0;
|
|
119
|
+
this.contextTokens = 0;
|
|
98
120
|
this._committedChars = 0;
|
|
99
121
|
this._committedTokens = 0;
|
|
100
122
|
this._tail = "";
|