@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\n");
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 || "warning",
176
- TRANSFORMERS_VERBOSITY: process.env.TRANSFORMERS_VERBOSITY || "warning",
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 rl = createInterface({ input: process.stdin, terminal: true });
192
- rl.on("line", (l) => { inputQ.push(l); });
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 { rl.question(C + " cofos " + R, handle); } catch (_) {}
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
- try { rl.question(G + " ... " + R, handle); } catch (_) {}
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) { ask(); return; }
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
- ask(); return;
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
- ask(); return;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lzhzzzzwill/cofos",
3
- "version": "1.1.6",
3
+ "version": "1.1.9",
4
4
  "description": "COFOS 9B + RAG chat CLI with optional local PDF ingestion.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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: true
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", "warning")
28
- os.environ.setdefault("TRANSFORMERS_VERBOSITY", "warning")
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
- turns.append(turn)
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
- logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
467
- for noisy_logger in ("httpx", "httpcore", "huggingface_hub"):
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
- print(f"[chat] Generated in {gen_elapsed:.1f}s", file=sys.stderr, flush=True)
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
- logging.exception("Generation failed")
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