@lzhzzzzwill/cofos 1.2.2 → 1.2.4
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/bin/cofos.js +96 -10
- package/package.json +1 -1
package/bin/cofos.js
CHANGED
|
@@ -106,7 +106,8 @@ function render({ modelPath, cacheDir, pdfDir }) {
|
|
|
106
106
|
const SLASH_COMMANDS = [
|
|
107
107
|
{ cmd: "/help", desc: "Show help" },
|
|
108
108
|
{ cmd: "/exit", desc: "Quit" },
|
|
109
|
-
{ cmd: "/clear", desc: "Clear conversation history" },
|
|
109
|
+
{ cmd: "/clear", desc: "Clear page and conversation history" },
|
|
110
|
+
{ cmd: "/cls", desc: "Clear page only" },
|
|
110
111
|
{ cmd: "/save", desc: "Save this session history" },
|
|
111
112
|
{ cmd: "/info", desc: "Show model and RAG info" },
|
|
112
113
|
{ cmd: "/topk 5", desc: "Set retrieval top-k" },
|
|
@@ -169,8 +170,8 @@ function createTui() {
|
|
|
169
170
|
alwaysScroll: true,
|
|
170
171
|
keys: true,
|
|
171
172
|
vi: true,
|
|
172
|
-
mouse:
|
|
173
|
-
tags:
|
|
173
|
+
mouse: true,
|
|
174
|
+
tags: true,
|
|
174
175
|
scrollbar: { ch: " ", track: { bg: "black" }, style: { bg: "cyan" } },
|
|
175
176
|
});
|
|
176
177
|
|
|
@@ -207,10 +208,13 @@ function createTui() {
|
|
|
207
208
|
|
|
208
209
|
let buffer = "";
|
|
209
210
|
let onSubmit = () => {};
|
|
211
|
+
let onExit = () => {};
|
|
212
|
+
let submitting = false;
|
|
210
213
|
let suggestionMatches = [];
|
|
211
214
|
let selectedSuggestion = 0;
|
|
212
215
|
|
|
213
216
|
function append(text = "") {
|
|
217
|
+
const wasAtBottom = history.getScrollPerc() >= 98;
|
|
214
218
|
const data = String(text).replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
215
219
|
for (const part of data.split("\r")) {
|
|
216
220
|
if (part === "") continue;
|
|
@@ -223,7 +227,7 @@ function createTui() {
|
|
|
223
227
|
}
|
|
224
228
|
if (buffer.length > 250000) buffer = buffer.slice(-220000);
|
|
225
229
|
history.setContent(buffer);
|
|
226
|
-
history.setScrollPerc(100);
|
|
230
|
+
if (wasAtBottom) history.setScrollPerc(100);
|
|
227
231
|
screen.render();
|
|
228
232
|
}
|
|
229
233
|
|
|
@@ -268,6 +272,8 @@ function createTui() {
|
|
|
268
272
|
}
|
|
269
273
|
|
|
270
274
|
function submitInput() {
|
|
275
|
+
if (submitting) return;
|
|
276
|
+
submitting = true;
|
|
271
277
|
let value = input.getValue();
|
|
272
278
|
if (!suggestions.hidden && selectedCommand()) value = selectedCommand();
|
|
273
279
|
hideSuggestions();
|
|
@@ -275,9 +281,11 @@ function createTui() {
|
|
|
275
281
|
input.focus();
|
|
276
282
|
screen.render();
|
|
277
283
|
onSubmit(String(value || ""));
|
|
284
|
+
setTimeout(() => { submitting = false; }, 0);
|
|
278
285
|
}
|
|
279
286
|
|
|
280
287
|
input.key(["enter"], submitInput);
|
|
288
|
+
screen.key(["enter"], submitInput);
|
|
281
289
|
|
|
282
290
|
input.on("keypress", (_ch, key = {}) => {
|
|
283
291
|
if (["up", "down", "tab", "enter", "escape"].includes(key.name)) return;
|
|
@@ -308,7 +316,11 @@ function createTui() {
|
|
|
308
316
|
});
|
|
309
317
|
|
|
310
318
|
screen.key(["escape"], hideSuggestions);
|
|
311
|
-
screen.key(["
|
|
319
|
+
screen.key(["pageup"], () => { history.scroll(-Math.max(3, Math.floor(screen.height * 0.8))); screen.render(); });
|
|
320
|
+
screen.key(["pagedown"], () => { history.scroll(Math.max(3, Math.floor(screen.height * 0.8))); screen.render(); });
|
|
321
|
+
screen.key(["home"], () => { history.setScrollPerc(0); screen.render(); });
|
|
322
|
+
screen.key(["end"], () => { history.setScrollPerc(100); screen.render(); });
|
|
323
|
+
screen.key(["C-c"], () => onExit());
|
|
312
324
|
|
|
313
325
|
input.focus();
|
|
314
326
|
screen.render();
|
|
@@ -318,7 +330,10 @@ function createTui() {
|
|
|
318
330
|
input,
|
|
319
331
|
append,
|
|
320
332
|
print: (text = "") => append(String(text) + "\n"),
|
|
333
|
+
clear: () => { buffer = ""; history.setContent(""); history.setScrollPerc(100); screen.render(); },
|
|
321
334
|
setSubmitHandler: (fn) => { onSubmit = fn; },
|
|
335
|
+
setExitHandler: (fn) => { onExit = fn; },
|
|
336
|
+
focus: () => { input.focus(); screen.render(); },
|
|
322
337
|
setPrompt: (label) => { input.setLabel(` ${label} `); screen.render(); },
|
|
323
338
|
destroy: () => screen.destroy(),
|
|
324
339
|
};
|
|
@@ -423,7 +438,8 @@ function main() {
|
|
|
423
438
|
proc.stdout.setEncoding("utf-8");
|
|
424
439
|
proc.stderr.setEncoding("utf-8");
|
|
425
440
|
|
|
426
|
-
let buf = "", ready = false, generating = false, responseOpen = false, closed = false;
|
|
441
|
+
let buf = "", ready = false, generating = false, responseOpen = false, commandOpen = false, closed = false;
|
|
442
|
+
let responseMaybeReasoning = false, responseReasoning = false, responseBuffer = "";
|
|
427
443
|
const inputQ = [];
|
|
428
444
|
let mlBuf = [];
|
|
429
445
|
|
|
@@ -435,6 +451,7 @@ function main() {
|
|
|
435
451
|
ui.destroy();
|
|
436
452
|
}
|
|
437
453
|
|
|
454
|
+
ui.setExitHandler(shutdown);
|
|
438
455
|
process.on("SIGTERM", shutdown);
|
|
439
456
|
process.on("SIGINT", shutdown);
|
|
440
457
|
|
|
@@ -467,9 +484,22 @@ function main() {
|
|
|
467
484
|
ui.print(slashHint("/") + "\n Add local papers to RAG:\n cofos --pdf-dir ./new_pdfs\n cofos --pdf-dir ./new_pdfs --rebuild-pdf-index\n\n Multi-line: end a line with \\ to continue.");
|
|
468
485
|
return false;
|
|
469
486
|
}
|
|
487
|
+
if (/^\/(cls)$/.test(input)) {
|
|
488
|
+
ui.clear();
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
470
491
|
if (/^\/(clear)$/.test(input)) {
|
|
492
|
+
ui.clear();
|
|
471
493
|
mlBuf = [];
|
|
472
494
|
responseOpen = false;
|
|
495
|
+
commandOpen = true;
|
|
496
|
+
proc.stdin.write(input + "\n");
|
|
497
|
+
generating = true;
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
if (/^\/(topk)(?:\s+\d+)?$/.test(input) || /^\/rag\s+(?:on|off)$/.test(input)) {
|
|
501
|
+
responseOpen = false;
|
|
502
|
+
commandOpen = true;
|
|
473
503
|
proc.stdin.write(input + "\n");
|
|
474
504
|
generating = true;
|
|
475
505
|
return true;
|
|
@@ -478,8 +508,12 @@ function main() {
|
|
|
478
508
|
ui.print(`\n COFOS CLI — v${pkg.version}\n Model: ${modelPath} (Hugging Face)\n RAG: Willlzh/COFOS_data/runtime (KG + BM25) + optional --pdf-dir BM25 evidence\n Cache: ${cacheDir}\n HF: ${backendEnv.HF_HOME}\n Engine: StudentAdapterInference + transformers + PyTorch\n Hist: ~/.cofos_history.jsonl\n`);
|
|
479
509
|
return false;
|
|
480
510
|
}
|
|
511
|
+
if (input.startsWith("/")) {
|
|
512
|
+
ui.print(`{red-fg}•{/red-fg} Unknown command: ${input}. Press / then Tab for commands, or run /help.`);
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
481
515
|
|
|
482
|
-
ui.print(
|
|
516
|
+
ui.print("{yellow-fg}●{/yellow-fg} " + input);
|
|
483
517
|
responseOpen = false;
|
|
484
518
|
generating = true;
|
|
485
519
|
proc.stdin.write(input + "\n");
|
|
@@ -493,6 +527,39 @@ function main() {
|
|
|
493
527
|
|
|
494
528
|
proc.stderr.on("data", (chunk) => ui.append(chunk));
|
|
495
529
|
|
|
530
|
+
function appendAssistant(text) {
|
|
531
|
+
if (!text) return;
|
|
532
|
+
if (responseMaybeReasoning) {
|
|
533
|
+
responseBuffer += text;
|
|
534
|
+
const enough = responseBuffer.length > 80 || responseBuffer.includes("\n\n") || responseBuffer.includes("。") || responseBuffer.includes(".");
|
|
535
|
+
if (!enough) return;
|
|
536
|
+
const start = responseBuffer.trimStart();
|
|
537
|
+
responseReasoning = /^(The user|Okay[,,]|I need|We need|Based on the provided information[,,]? I can|用户)/i.test(start);
|
|
538
|
+
responseMaybeReasoning = false;
|
|
539
|
+
if (responseReasoning) {
|
|
540
|
+
ui.append("{gray-fg}- " + responseBuffer + "{/gray-fg}");
|
|
541
|
+
} else {
|
|
542
|
+
ui.append(responseBuffer);
|
|
543
|
+
}
|
|
544
|
+
responseBuffer = "";
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (responseReasoning) {
|
|
548
|
+
const end = text.indexOf("\n\n");
|
|
549
|
+
if (end !== -1) {
|
|
550
|
+
const reason = text.slice(0, end);
|
|
551
|
+
const answer = text.slice(end).replace(/^\s+/, "");
|
|
552
|
+
if (reason) ui.append("{gray-fg}" + reason + "{/gray-fg}");
|
|
553
|
+
responseReasoning = false;
|
|
554
|
+
if (answer) ui.append("\n{green-fg}{bold}◆{/bold}{/green-fg} " + answer);
|
|
555
|
+
} else {
|
|
556
|
+
ui.append("{gray-fg}" + text + "{/gray-fg}");
|
|
557
|
+
}
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
ui.append(text);
|
|
561
|
+
}
|
|
562
|
+
|
|
496
563
|
proc.stdout.on("data", (chunk) => {
|
|
497
564
|
buf += chunk;
|
|
498
565
|
if (!ready) {
|
|
@@ -507,6 +574,20 @@ function main() {
|
|
|
507
574
|
}
|
|
508
575
|
if (!generating) { buf = ""; return; }
|
|
509
576
|
|
|
577
|
+
if (commandOpen) {
|
|
578
|
+
const di = buf.indexOf(DONE);
|
|
579
|
+
if (di === -1) return;
|
|
580
|
+
const pre = buf.slice(0, di).trim();
|
|
581
|
+
if (pre) ui.print("{gray-fg}•{/gray-fg} " + pre);
|
|
582
|
+
buf = buf.slice(di + DONE.length);
|
|
583
|
+
if (buf[0] === "\n") buf = buf.slice(1);
|
|
584
|
+
generating = false;
|
|
585
|
+
commandOpen = false;
|
|
586
|
+
ui.focus();
|
|
587
|
+
drain();
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
|
|
510
591
|
if (!responseOpen) {
|
|
511
592
|
const ri = buf.indexOf(RESPONSE);
|
|
512
593
|
if (ri === -1) {
|
|
@@ -517,24 +598,29 @@ function main() {
|
|
|
517
598
|
buf = buf.slice(ri + RESPONSE.length);
|
|
518
599
|
if (buf[0] === "\n") buf = buf.slice(1);
|
|
519
600
|
responseOpen = true;
|
|
520
|
-
|
|
601
|
+
responseMaybeReasoning = true;
|
|
602
|
+
responseReasoning = false;
|
|
603
|
+
responseBuffer = "";
|
|
604
|
+
ui.append("{green-fg}{bold}◆{/bold}{/green-fg} ");
|
|
521
605
|
}
|
|
522
606
|
|
|
523
607
|
const di = buf.indexOf(DONE);
|
|
524
608
|
if (di !== -1) {
|
|
525
609
|
const pre = buf.slice(0, di);
|
|
526
|
-
if (pre)
|
|
610
|
+
if (pre) appendAssistant(pre);
|
|
527
611
|
buf = buf.slice(di + DONE.length);
|
|
528
612
|
if (buf[0] === "\n") buf = buf.slice(1);
|
|
613
|
+
if (responseBuffer) { appendAssistant("\n"); }
|
|
529
614
|
generating = false;
|
|
530
615
|
responseOpen = false;
|
|
531
616
|
ui.append("\n");
|
|
617
|
+
ui.focus();
|
|
532
618
|
drain();
|
|
533
619
|
} else {
|
|
534
620
|
const keep = DONE.length - 1;
|
|
535
621
|
if (buf.length > keep) {
|
|
536
622
|
const out = buf.slice(0, -keep);
|
|
537
|
-
if (out)
|
|
623
|
+
if (out) appendAssistant(out);
|
|
538
624
|
buf = buf.slice(-keep);
|
|
539
625
|
}
|
|
540
626
|
}
|