@lzhzzzzwill/cofos 1.3.4 → 1.3.6
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/README.md +7 -0
- package/bin/cofos.js +101 -5
- package/package.json +1 -1
- package/runtime/src/inference/student_adapter_inference.py +5 -0
package/README.md
CHANGED
|
@@ -71,6 +71,12 @@ Disable RAG for a session:
|
|
|
71
71
|
npx @lzhzzzzwill/cofos --no-rag
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
Show model reasoning scaffold when emitted:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx @lzhzzzzwill/cofos --show-reasoning
|
|
78
|
+
```
|
|
79
|
+
|
|
74
80
|
Ask once and exit, useful for scripts and smoke tests:
|
|
75
81
|
|
|
76
82
|
```bash
|
|
@@ -128,6 +134,7 @@ cofos \
|
|
|
128
134
|
| `--rebuild-pdf-index` | Force PDF parsing and BM25 rebuild |
|
|
129
135
|
| `--top-k` | Retrieval top-k |
|
|
130
136
|
| `-p`, `--prompt` | Ask one question and exit |
|
|
137
|
+
| `--show-reasoning` | Show emitted reasoning scaffold in dim text before the final answer |
|
|
131
138
|
| `--max-new-tokens` | Override generation length |
|
|
132
139
|
| `--no-rag` | Run without KG/BM25 retrieval |
|
|
133
140
|
|
package/bin/cofos.js
CHANGED
|
@@ -216,7 +216,7 @@ function createPlainUi() {
|
|
|
216
216
|
async function main() {
|
|
217
217
|
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
218
218
|
console.log("\n COFOS CLI — 9B + RAG chat (Willlzh/COFOS)\n");
|
|
219
|
-
console.log(" Usage: cofos [-p QUESTION] [--model PATH] [--config PATH] [--pdf-dir DIR] [--cache-dir DIR] [--top-k N] [--no-rag]\n");
|
|
219
|
+
console.log(" Usage: cofos [-p QUESTION] [--show-reasoning] [--model PATH] [--config PATH] [--pdf-dir DIR] [--cache-dir DIR] [--top-k N] [--no-rag]\n");
|
|
220
220
|
console.log(" Commands in chat:");
|
|
221
221
|
console.log(" /help Show this help");
|
|
222
222
|
console.log(" /exit Quit");
|
|
@@ -229,6 +229,7 @@ async function main() {
|
|
|
229
229
|
console.log(" --pdf-dir DIR Add PDFs from DIR as extra BM25 evidence");
|
|
230
230
|
console.log(" --cache-dir DIR Store model/data cache here (default: ./.cofos)");
|
|
231
231
|
console.log(" --max-new-tokens N Override generation length");
|
|
232
|
+
console.log(" --show-reasoning Show model reasoning scaffold in dim text when emitted");
|
|
232
233
|
console.log(" -p, --prompt TEXT Ask once and exit");
|
|
233
234
|
console.log(" --rebuild-pdf-index Force reparsing PDFs and rebuilding BM25\n");
|
|
234
235
|
console.log(" Multi-line: end a line with \\ to continue on the next line.\n");
|
|
@@ -245,6 +246,7 @@ async function main() {
|
|
|
245
246
|
let maxNewTokens = null;
|
|
246
247
|
let cacheDir = process.env.COFOS_CACHE_DIR ? resolve(process.env.COFOS_CACHE_DIR) : resolve(process.cwd(), ".cofos");
|
|
247
248
|
let rebuildPdfIndex = false;
|
|
249
|
+
let showReasoning = false;
|
|
248
250
|
let singlePrompt = null;
|
|
249
251
|
const backendArgs = [chatScript];
|
|
250
252
|
|
|
@@ -276,6 +278,8 @@ async function main() {
|
|
|
276
278
|
cacheDir = resolve(value(++i, arg));
|
|
277
279
|
} else if (arg === "--rebuild-pdf-index") {
|
|
278
280
|
rebuildPdfIndex = true;
|
|
281
|
+
} else if (arg === "--show-reasoning") {
|
|
282
|
+
showReasoning = true;
|
|
279
283
|
} else if (arg === "-p" || arg === "--prompt") {
|
|
280
284
|
singlePrompt = value(++i, arg);
|
|
281
285
|
} else if (arg === "--no-rag") {
|
|
@@ -311,6 +315,9 @@ async function main() {
|
|
|
311
315
|
HF_HUB_ETAG_TIMEOUT: process.env.HF_HUB_ETAG_TIMEOUT || "30",
|
|
312
316
|
HF_HUB_VERBOSITY: process.env.HF_HUB_VERBOSITY || "error",
|
|
313
317
|
TRANSFORMERS_VERBOSITY: process.env.TRANSFORMERS_VERBOSITY || "error",
|
|
318
|
+
PYTHONUNBUFFERED: process.env.PYTHONUNBUFFERED || "1",
|
|
319
|
+
PYTHONIOENCODING: process.env.PYTHONIOENCODING || "utf-8",
|
|
320
|
+
COFOS_SHOW_REASONING: showReasoning ? "1" : (process.env.COFOS_SHOW_REASONING || "0"),
|
|
314
321
|
};
|
|
315
322
|
const proc = spawn(py, backendArgs, {
|
|
316
323
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -346,6 +353,33 @@ async function main() {
|
|
|
346
353
|
}
|
|
347
354
|
}
|
|
348
355
|
|
|
356
|
+
function handleLocalOnly(rawInput) {
|
|
357
|
+
const input = String(rawInput || "").trim();
|
|
358
|
+
if (!input) return true;
|
|
359
|
+
if (input === "/") {
|
|
360
|
+
ui.print("Commands: " + SLASH_COMMANDS.map(({ cmd }) => cmd).join(" ") + "\nType a prefix such as /r or /to, then press Tab for completion.");
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
if (/^\/(exit|quit)$/.test(input)) { shutdown(); return true; }
|
|
364
|
+
if (/^\/(help)$/.test(input)) {
|
|
365
|
+
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.");
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
if (/^\/(cls)$/.test(input)) {
|
|
369
|
+
ui.clear();
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
if (/^\/(info)$/.test(input)) {
|
|
373
|
+
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 Status: ${ready ? "ready" : "loading"}\n Hist: ~/.cofos_history.jsonl\n`);
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
if (input.startsWith("/") && !/^\/(clear|save|topk)(?:\s|$)/.test(input) && !/^\/rag\s+(?:on|off)$/.test(input)) {
|
|
377
|
+
ui.print(`{red-fg}•{/red-fg} Unknown command: ${input}.\n${slashPrefixHint(input)}`);
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
349
383
|
function handle(rawInput) {
|
|
350
384
|
let input = String(rawInput || "");
|
|
351
385
|
if (input.endsWith("\\")) {
|
|
@@ -427,8 +461,14 @@ async function main() {
|
|
|
427
461
|
}
|
|
428
462
|
|
|
429
463
|
ui.setSubmitHandler((line) => {
|
|
464
|
+
if (handleLocalOnly(line)) {
|
|
465
|
+
if (!closed) ui.focus();
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
430
468
|
if (!ready || generating) {
|
|
431
469
|
inputQ.push(line);
|
|
470
|
+
ui.print(ready ? "• Queued until the current answer finishes." : "• Queued until the model is ready. Use /exit to quit while loading.");
|
|
471
|
+
if (!closed) ui.focus();
|
|
432
472
|
return;
|
|
433
473
|
}
|
|
434
474
|
const started = handle(line);
|
|
@@ -476,12 +516,68 @@ async function main() {
|
|
|
476
516
|
|
|
477
517
|
function streamPlainAssistant(text, final = false) {
|
|
478
518
|
if (ui.supportsReplace) return;
|
|
519
|
+
if (!showReasoning) {
|
|
520
|
+
if (plainAssistantMode === "undecided") {
|
|
521
|
+
if (!text && !final) return;
|
|
522
|
+
plainAssistantMode = "answer";
|
|
523
|
+
ui.append(`${GR}${B}◆${R} `);
|
|
524
|
+
}
|
|
525
|
+
if (text) ui.append(text);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
plainAssistantPending += text;
|
|
479
530
|
if (plainAssistantMode === "undecided") {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
531
|
+
const start = plainAssistantPending.trimStart();
|
|
532
|
+
if (!start && !final) return;
|
|
533
|
+
if (start.startsWith("<think>") || reasonRe.test(start)) {
|
|
534
|
+
plainAssistantMode = "reason";
|
|
535
|
+
plainReasonBuffer = start.replace(/^<think>\s*/i, "");
|
|
536
|
+
plainReasonPrinted = 0;
|
|
537
|
+
plainAssistantPending = "";
|
|
538
|
+
ui.append(`${G}-${R} ${G}`);
|
|
539
|
+
} else {
|
|
540
|
+
plainAssistantMode = "answer";
|
|
541
|
+
plainAssistantPending = start;
|
|
542
|
+
ui.append(`${GR}${B}◆${R} `);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (plainAssistantMode === "reason") {
|
|
547
|
+
plainReasonBuffer += plainAssistantPending;
|
|
548
|
+
plainAssistantPending = "";
|
|
549
|
+
const thinkEnd = plainReasonBuffer.indexOf("</think>");
|
|
550
|
+
const paraEnd = plainReasonBuffer.search(/\n\s*\n/);
|
|
551
|
+
const split = thinkEnd !== -1 ? thinkEnd : paraEnd;
|
|
552
|
+
if (split !== -1) {
|
|
553
|
+
const reason = plainReasonBuffer.slice(plainReasonPrinted, split).replace(/<\/think>/gi, "");
|
|
554
|
+
const restStart = thinkEnd !== -1 ? split + "</think>".length : split;
|
|
555
|
+
const rest = plainReasonBuffer.slice(restStart).trimStart();
|
|
556
|
+
if (reason) ui.append(reason);
|
|
557
|
+
ui.append(`${R}\n${GR}${B}◆${R} `);
|
|
558
|
+
plainAssistantMode = "answer";
|
|
559
|
+
plainAssistantPending = rest;
|
|
560
|
+
plainReasonBuffer = "";
|
|
561
|
+
plainReasonPrinted = 0;
|
|
562
|
+
} else {
|
|
563
|
+
const printableUntil = final ? plainReasonBuffer.length : Math.max(0, plainReasonBuffer.length - 2);
|
|
564
|
+
if (printableUntil > plainReasonPrinted) {
|
|
565
|
+
ui.append(plainReasonBuffer.slice(plainReasonPrinted, printableUntil));
|
|
566
|
+
plainReasonPrinted = printableUntil;
|
|
567
|
+
}
|
|
568
|
+
if (!final) return;
|
|
569
|
+
if (plainReasonPrinted < plainReasonBuffer.length) ui.append(plainReasonBuffer.slice(plainReasonPrinted));
|
|
570
|
+
plainReasonBuffer = "";
|
|
571
|
+
plainReasonPrinted = 0;
|
|
572
|
+
ui.append(R);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (plainAssistantMode === "answer" && plainAssistantPending) {
|
|
578
|
+
ui.append(plainAssistantPending);
|
|
579
|
+
plainAssistantPending = "";
|
|
483
580
|
}
|
|
484
|
-
if (text) ui.append(text);
|
|
485
581
|
}
|
|
486
582
|
|
|
487
583
|
|
package/package.json
CHANGED
|
@@ -489,6 +489,11 @@ class StudentAdapterInference:
|
|
|
489
489
|
@staticmethod
|
|
490
490
|
def _sanitize_stream(chunks: Any) -> Generator[str, None, None]:
|
|
491
491
|
"""Drop ``<think>`` blocks and leading labels from a text stream."""
|
|
492
|
+
if os.environ.get("COFOS_SHOW_REASONING", "").lower() in {"1", "true", "yes", "on"}:
|
|
493
|
+
for chunk in chunks:
|
|
494
|
+
yield chunk
|
|
495
|
+
return
|
|
496
|
+
|
|
492
497
|
buffer = ""
|
|
493
498
|
started = False
|
|
494
499
|
in_think = False
|