@lzhzzzzwill/cofos 1.1.6 → 1.1.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/bin/cofos.js
CHANGED
|
@@ -162,7 +162,10 @@ function main() {
|
|
|
162
162
|
if (!existsSync(chatScript)) die(`Missing script: ${chatScript}`);
|
|
163
163
|
ensureDeps(py);
|
|
164
164
|
|
|
165
|
-
console.log(" Loading COFOS 9B + RAG backend from Hugging Face … first run may take a while
|
|
165
|
+
console.log(" Loading COFOS 9B + RAG backend from Hugging Face … first run may take a while");
|
|
166
|
+
if (pdfDir) console.log(" Local PDF RAG: enabled for this session");
|
|
167
|
+
else console.log(" Tip: add local papers with `cofos --pdf-dir ./new_pdfs`");
|
|
168
|
+
console.log("");
|
|
166
169
|
|
|
167
170
|
const backendEnv = {
|
|
168
171
|
...process.env,
|
|
@@ -172,8 +175,8 @@ function main() {
|
|
|
172
175
|
HF_HUB_ENABLE_HF_TRANSFER: process.env.HF_HUB_ENABLE_HF_TRANSFER || "0",
|
|
173
176
|
HF_HUB_DOWNLOAD_TIMEOUT: process.env.HF_HUB_DOWNLOAD_TIMEOUT || "120",
|
|
174
177
|
HF_HUB_ETAG_TIMEOUT: process.env.HF_HUB_ETAG_TIMEOUT || "30",
|
|
175
|
-
HF_HUB_VERBOSITY: process.env.HF_HUB_VERBOSITY || "
|
|
176
|
-
TRANSFORMERS_VERBOSITY: process.env.TRANSFORMERS_VERBOSITY || "
|
|
178
|
+
HF_HUB_VERBOSITY: process.env.HF_HUB_VERBOSITY || "error",
|
|
179
|
+
TRANSFORMERS_VERBOSITY: process.env.TRANSFORMERS_VERBOSITY || "error",
|
|
177
180
|
};
|
|
178
181
|
const proc = spawn(py, backendArgs, {
|
|
179
182
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -188,8 +191,18 @@ function main() {
|
|
|
188
191
|
|
|
189
192
|
let buf = "", ready = false, generating = false, responseOpen = false, closed = false;
|
|
190
193
|
const inputQ = [];
|
|
191
|
-
const
|
|
192
|
-
|
|
194
|
+
const mainPrompt = C + " cofos " + R;
|
|
195
|
+
const contPrompt = G + " ... " + R;
|
|
196
|
+
const rl = createInterface({
|
|
197
|
+
input: process.stdin,
|
|
198
|
+
output: process.stdout,
|
|
199
|
+
terminal: true,
|
|
200
|
+
prompt: mainPrompt,
|
|
201
|
+
});
|
|
202
|
+
rl.on("line", (l) => {
|
|
203
|
+
if (!ready || generating) inputQ.push(l);
|
|
204
|
+
else if (!handle(l)) ask();
|
|
205
|
+
});
|
|
193
206
|
|
|
194
207
|
// Forward Python stderr (status / progress messages) to our stderr.
|
|
195
208
|
proc.stderr.on("data", (chunk) => { process.stderr.write(chunk); });
|
|
@@ -275,9 +288,12 @@ function main() {
|
|
|
275
288
|
ask();
|
|
276
289
|
}
|
|
277
290
|
|
|
278
|
-
function ask() {
|
|
291
|
+
function ask(prompt = mainPrompt) {
|
|
279
292
|
if (closed) return;
|
|
280
|
-
try {
|
|
293
|
+
try {
|
|
294
|
+
rl.setPrompt(prompt);
|
|
295
|
+
rl.prompt();
|
|
296
|
+
} catch (_) {}
|
|
281
297
|
}
|
|
282
298
|
|
|
283
299
|
/* ─── multi-line accumulator ─── */
|
|
@@ -287,8 +303,8 @@ function main() {
|
|
|
287
303
|
// Multi-line continuation: lines ending with "\"
|
|
288
304
|
if (input.endsWith("\\")) {
|
|
289
305
|
mlBuf.push(input.slice(0, -1));
|
|
290
|
-
|
|
291
|
-
return;
|
|
306
|
+
ask(contPrompt);
|
|
307
|
+
return true;
|
|
292
308
|
}
|
|
293
309
|
if (mlBuf.length > 0) {
|
|
294
310
|
mlBuf.push(input);
|
|
@@ -297,20 +313,20 @@ function main() {
|
|
|
297
313
|
}
|
|
298
314
|
|
|
299
315
|
input = input.trim();
|
|
300
|
-
if (!input)
|
|
316
|
+
if (!input) return false;
|
|
301
317
|
|
|
302
318
|
// Local-only commands (don't hit the backend).
|
|
303
|
-
if (/^\/(exit|quit)$/.test(input)) { proc.stdin.write("/exit\n"); proc.stdin.end(); return; }
|
|
319
|
+
if (/^\/(exit|quit)$/.test(input)) { proc.stdin.write("/exit\n"); proc.stdin.end(); return true; }
|
|
304
320
|
if (/^\/(help)$/.test(input)) {
|
|
305
|
-
console.log("\n Commands:\n /help Show help\n /exit Quit\n /clear Clear conversation history\n /save Persist conversation history\n /info Model and RAG info\n /topk N Set retrieval top-k\n /rag on|off Enable/disable RAG\n\n Multi-line: end a line with \\ to continue.\n");
|
|
306
|
-
|
|
321
|
+
console.log("\n Commands:\n /help Show help\n /exit Quit\n /clear Clear conversation history\n /save Persist conversation history\n /info Model and RAG info\n /topk N Set retrieval top-k\n /rag on|off Enable/disable RAG\n\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.\n");
|
|
322
|
+
return false;
|
|
307
323
|
}
|
|
308
324
|
if (/^\/(clear)$/.test(input)) {
|
|
309
325
|
mlBuf = [];
|
|
310
326
|
responseOpen = false;
|
|
311
327
|
proc.stdin.write(input + "\n");
|
|
312
328
|
generating = true;
|
|
313
|
-
return;
|
|
329
|
+
return true;
|
|
314
330
|
}
|
|
315
331
|
if (/^\/(info)$/.test(input)) {
|
|
316
332
|
console.log(`\n COFOS CLI — v${pkg.version}`);
|
|
@@ -320,7 +336,7 @@ function main() {
|
|
|
320
336
|
console.log(` HF: ${backendEnv.HF_HOME}`);
|
|
321
337
|
console.log(" Engine: StudentAdapterInference + transformers + PyTorch");
|
|
322
338
|
console.log(" Hist: ~/.cofos_history.jsonl\n");
|
|
323
|
-
|
|
339
|
+
return false;
|
|
324
340
|
}
|
|
325
341
|
|
|
326
342
|
// Forward to backend. Echo a stable user turn so pasted/submitted questions remain visible in the transcript.
|
|
@@ -328,6 +344,7 @@ function main() {
|
|
|
328
344
|
responseOpen = false;
|
|
329
345
|
generating = true;
|
|
330
346
|
proc.stdin.write(input + "\n");
|
|
347
|
+
return true;
|
|
331
348
|
}
|
|
332
349
|
}
|
|
333
350
|
|
package/package.json
CHANGED
|
@@ -42,7 +42,7 @@ generation:
|
|
|
42
42
|
temperature: 0.2
|
|
43
43
|
top_p: 0.9
|
|
44
44
|
max_new_tokens: 1024
|
|
45
|
-
do_sample:
|
|
45
|
+
do_sample: false
|
|
46
46
|
repetition_penalty: 1.08
|
|
47
47
|
num_beams: 1
|
|
48
48
|
no_repeat_ngram_size: 0
|
|
@@ -51,6 +51,7 @@ generation:
|
|
|
51
51
|
refine_answers: false
|
|
52
52
|
stream_refinement: false
|
|
53
53
|
refinement_max_new_tokens: 512
|
|
54
|
+
remove_invalid_values: true
|
|
54
55
|
data_processing:
|
|
55
56
|
chunk_size: 768
|
|
56
57
|
chunk_overlap: 50
|
|
@@ -104,3 +104,16 @@ student_answer_refinement_user_prompt: 'Question: {question}
|
|
|
104
104
|
checklist, or explanation of how you rewrote it.
|
|
105
105
|
|
|
106
106
|
'
|
|
107
|
+
interaction:
|
|
108
|
+
greetings:
|
|
109
|
+
hi: Hello! What would you like to know about COFs or reactive oxygen species?
|
|
110
|
+
hello: Hello! What would you like to know about COFs or reactive oxygen species?
|
|
111
|
+
hey: Hello! What would you like to know about COFs or reactive oxygen species?
|
|
112
|
+
你好: 你好!你想了解 COF、光催化还是活性氧相关问题?
|
|
113
|
+
您好: 您好!你想了解 COF、光催化还是活性氧相关问题?
|
|
114
|
+
嗨: 你好!你想了解 COF、光催化还是活性氧相关问题?
|
|
115
|
+
在吗: 在的。你想了解哪方面的 COF 或活性氧问题?
|
|
116
|
+
thanks: You're welcome.
|
|
117
|
+
thank you: You're welcome.
|
|
118
|
+
谢谢: 不客气。
|
|
119
|
+
感谢: 不客气。
|
|
@@ -181,6 +181,7 @@ class StudentAdapterInference:
|
|
|
181
181
|
self.num_beams = int(generation_config.get("num_beams", 1))
|
|
182
182
|
self.no_repeat_ngram_size = int(generation_config.get("no_repeat_ngram_size", 0))
|
|
183
183
|
self.min_new_tokens = int(generation_config.get("min_new_tokens", 0))
|
|
184
|
+
self.remove_invalid_values = bool(generation_config.get("remove_invalid_values", True))
|
|
184
185
|
self.history_max_turns = int(generation_config.get("history_max_turns", 4))
|
|
185
186
|
self.refine_answers = bool(generation_config.get("refine_answers", True))
|
|
186
187
|
self.stream_refinement = bool(generation_config.get("stream_refinement", True))
|
|
@@ -283,6 +284,7 @@ class StudentAdapterInference:
|
|
|
283
284
|
"repetition_penalty": self.repetition_penalty,
|
|
284
285
|
"eos_token_id": self.tokenizer.eos_token_id,
|
|
285
286
|
"pad_token_id": self.tokenizer.pad_token_id,
|
|
287
|
+
"remove_invalid_values": self.remove_invalid_values,
|
|
286
288
|
}
|
|
287
289
|
if self.do_sample:
|
|
288
290
|
kwargs["temperature"] = self.temperature
|
|
@@ -475,6 +477,7 @@ class StudentAdapterInference:
|
|
|
475
477
|
buffer = ""
|
|
476
478
|
started = False
|
|
477
479
|
in_think = False
|
|
480
|
+
awaiting_initial_answer = True
|
|
478
481
|
|
|
479
482
|
for chunk in chunks:
|
|
480
483
|
buffer += chunk
|
|
@@ -488,6 +491,16 @@ class StudentAdapterInference:
|
|
|
488
491
|
break
|
|
489
492
|
buffer = buffer[end + len("</think>") :]
|
|
490
493
|
in_think = False
|
|
494
|
+
awaiting_initial_answer = False
|
|
495
|
+
continue
|
|
496
|
+
|
|
497
|
+
# Some tokenizers suppress the opening <think> marker but still
|
|
498
|
+
# stream the reasoning text followed by </think>. At the start
|
|
499
|
+
# of an answer, drop everything before that closing marker too.
|
|
500
|
+
end_without_start = buffer.find("</think>")
|
|
501
|
+
if awaiting_initial_answer and end_without_start != -1:
|
|
502
|
+
buffer = buffer[end_without_start + len("</think>") :]
|
|
503
|
+
awaiting_initial_answer = False
|
|
491
504
|
continue
|
|
492
505
|
|
|
493
506
|
start = buffer.find("<think>")
|
package/scripts/chat.py
CHANGED
|
@@ -24,8 +24,8 @@ os.environ.setdefault("HF_HUB_DISABLE_XET", "1")
|
|
|
24
24
|
os.environ.setdefault("HF_HUB_ENABLE_HF_TRANSFER", "0")
|
|
25
25
|
os.environ.setdefault("HF_HUB_DOWNLOAD_TIMEOUT", "120")
|
|
26
26
|
os.environ.setdefault("HF_HUB_ETAG_TIMEOUT", "30")
|
|
27
|
-
os.environ.setdefault("HF_HUB_VERBOSITY", "
|
|
28
|
-
os.environ.setdefault("TRANSFORMERS_VERBOSITY", "
|
|
27
|
+
os.environ.setdefault("HF_HUB_VERBOSITY", "error")
|
|
28
|
+
os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error")
|
|
29
29
|
|
|
30
30
|
HISTORY_FILE = os.path.join(os.path.expanduser("~"), ".cofos_history.jsonl")
|
|
31
31
|
HISTORY_MAX_TURNS = 20
|
|
@@ -373,6 +373,14 @@ def ensure_runtime_data(project_root: Path, config: dict) -> None:
|
|
|
373
373
|
print("[chat] Runtime data ready.", file=sys.stderr, flush=True)
|
|
374
374
|
|
|
375
375
|
|
|
376
|
+
|
|
377
|
+
def _clean_history_content(role: str, content: str) -> str:
|
|
378
|
+
content = content.replace("COF-ROS-Reasoner", "COFOS")
|
|
379
|
+
if role == "assistant":
|
|
380
|
+
content = re.sub(r"(?is)<think>.*?</think>", "", content).strip()
|
|
381
|
+
content = re.sub(r"(?is)^.*?</think>", "", content).strip()
|
|
382
|
+
return content
|
|
383
|
+
|
|
376
384
|
def _load_history() -> list:
|
|
377
385
|
"""Load recent conversation turns from the persistent history file.
|
|
378
386
|
|
|
@@ -398,7 +406,9 @@ def _load_history() -> list:
|
|
|
398
406
|
and turn.get("role") in {"user", "assistant"}
|
|
399
407
|
and isinstance(turn.get("content"), str)
|
|
400
408
|
):
|
|
401
|
-
|
|
409
|
+
cleaned = _clean_history_content(turn["role"], turn["content"])
|
|
410
|
+
if cleaned:
|
|
411
|
+
turns.append({"role": turn["role"], "content": cleaned})
|
|
402
412
|
except OSError:
|
|
403
413
|
return []
|
|
404
414
|
|
|
@@ -463,14 +473,19 @@ def main() -> None:
|
|
|
463
473
|
import yaml
|
|
464
474
|
from inference.student_adapter_inference import StudentAdapterInference, StudentRAGContext
|
|
465
475
|
|
|
466
|
-
|
|
467
|
-
|
|
476
|
+
debug = os.environ.get("COFOS_DEBUG", "").lower() in {"1", "true", "yes", "on"}
|
|
477
|
+
log_level = logging.INFO if debug else logging.ERROR
|
|
478
|
+
logging.basicConfig(
|
|
479
|
+
level=log_level,
|
|
480
|
+
format="%(asctime)s - %(levelname)s - %(message)s",
|
|
481
|
+
force=True,
|
|
482
|
+
)
|
|
483
|
+
for noisy_logger in ("httpx", "httpcore", "huggingface_hub", "transformers"):
|
|
468
484
|
logging.getLogger(noisy_logger).setLevel(logging.WARNING)
|
|
469
485
|
|
|
470
486
|
config_path = default_config_path(runtime_root) if args.config == DEFAULT_CONFIG else resolve_project_path(runtime_root, args.config)
|
|
471
487
|
model_path = resolve_project_path(runtime_root, args.model_path)
|
|
472
488
|
|
|
473
|
-
debug = os.environ.get("COFOS_DEBUG", "").lower() in {"1", "true", "yes", "on"}
|
|
474
489
|
if debug:
|
|
475
490
|
print(f"[chat] Runtime root: {runtime_root}", file=sys.stderr, flush=True)
|
|
476
491
|
print(f"[chat] Cache root: {runtime_cache_root()}", file=sys.stderr, flush=True)
|
|
@@ -571,7 +586,8 @@ def main() -> None:
|
|
|
571
586
|
answer = "".join(answer_parts)
|
|
572
587
|
gen_elapsed = time.time() - t_gen
|
|
573
588
|
print("\n===DONE===", flush=True)
|
|
574
|
-
|
|
589
|
+
if os.environ.get("COFOS_DEBUG", "").lower() in {"1", "true", "yes", "on"}:
|
|
590
|
+
print(f"[chat] Generated in {gen_elapsed:.1f}s", file=sys.stderr, flush=True)
|
|
575
591
|
|
|
576
592
|
# Persist both turns only once the answer completed, so an
|
|
577
593
|
# interrupted generation cannot leave a dangling user turn behind.
|
|
@@ -580,7 +596,8 @@ def main() -> None:
|
|
|
580
596
|
_save_turn("user", question)
|
|
581
597
|
_save_turn("assistant", answer)
|
|
582
598
|
except Exception as exc:
|
|
583
|
-
|
|
599
|
+
if os.environ.get("COFOS_DEBUG", "").lower() in {"1", "true", "yes", "on"}:
|
|
600
|
+
logging.exception("Generation failed")
|
|
584
601
|
print(f"\n[COFOS error] {exc}")
|
|
585
602
|
print("===DONE===", flush=True)
|
|
586
603
|
|