j-js commited on
Commit
5da21b7
·
verified ·
1 Parent(s): e614e8a

Update formatting.py

Browse files
Files changed (1) hide show
  1. formatting.py +249 -101
formatting.py CHANGED
@@ -1,7 +1,7 @@
1
  from __future__ import annotations
2
 
3
  import re
4
- from typing import Any, List
5
 
6
 
7
  def style_prefix(tone: float) -> str:
@@ -54,10 +54,12 @@ def _is_wrapper_line(line: str) -> bool:
54
  "you’ve got this — here’s what the question is really asking",
55
  "you've got this — here's what the question is really asking",
56
  "what to identify first",
 
57
  "set-up path",
58
  "setup path",
59
  "first move",
60
  "next hint",
 
61
  "watch out for",
62
  "variables to define",
63
  "equations to form",
@@ -67,6 +69,8 @@ def _is_wrapper_line(line: str) -> bool:
67
  "constraints or conditions",
68
  "key relationship",
69
  "concepts you will probably need",
 
 
70
  }
71
  return key in wrapper_lines
72
 
@@ -107,13 +111,10 @@ def _limit_lines_for_verbosity(lines: list[str], verbosity: float, help_mode: st
107
 
108
  if verbosity < 0.2:
109
  return lines[:1]
110
-
111
  if verbosity < 0.45:
112
  return lines[:2]
113
-
114
  if verbosity < 0.75:
115
  return lines[:3]
116
-
117
  return lines
118
 
119
 
@@ -181,6 +182,104 @@ def _tone_adjust_line(line: str, tone: float) -> str:
181
  return line
182
 
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  def _format_answer_mode(
185
  lines: List[str],
186
  topic: str,
@@ -239,7 +338,6 @@ def format_reply(core: str, tone: float, verbosity: float, transparency: float,
239
  return _format_answer_mode(lines, topic, tone, verbosity, transparency)
240
 
241
  lines = _limit_lines_for_verbosity(lines, verbosity, help_mode)
242
-
243
  if not lines:
244
  return prefix or "Start with the structure of the problem."
245
 
@@ -295,92 +393,24 @@ def format_reply(core: str, tone: float, verbosity: float, transparency: float,
295
  return "\n".join(output).strip()
296
 
297
 
298
- def _explainer_header(tone: float) -> str:
299
- if tone < 0.2:
300
- return "Question breakdown:"
301
- if tone < 0.45:
302
- return "Here’s what the question is really asking:"
303
- if tone < 0.75:
304
- return "Let’s break down what the question is asking:"
305
- return "You’ve got this — here’s what the question is really asking:"
306
-
307
-
308
- def _clean_section_items(items: List[str]) -> List[str]:
309
- cleaned = []
310
- seen = set()
311
-
312
- for item in items or []:
313
- text = (item or "").strip()
314
- if not text:
315
- continue
316
- if _is_wrapper_line(text):
317
- continue
318
- key = _normalize_key(text)
319
- if key in seen:
320
- continue
321
- seen.add(key)
322
- cleaned.append(text)
323
-
324
- return cleaned
325
-
326
-
327
- def _append_section(lines: List[str], title: str, items: List[str], limit: int) -> None:
328
- cleaned = _clean_section_items(items)
329
- if not cleaned:
330
- return
331
-
332
- lines.append("")
333
- lines.append(title)
334
- for item in cleaned[:limit]:
335
- lines.append(f"- {item}")
336
-
337
-
338
- def _coerce_string(value: Any) -> str:
339
- return (value or "").strip() if isinstance(value, str) else ""
340
-
341
-
342
- def _coerce_list(value: Any) -> List[str]:
343
- if not value:
344
- return []
345
- if isinstance(value, list):
346
- return [str(v).strip() for v in value if str(v).strip()]
347
- if isinstance(value, tuple):
348
- return [str(v).strip() for v in value if str(v).strip()]
349
- if isinstance(value, str):
350
- text = value.strip()
351
- return [text] if text else []
352
- return []
353
-
354
-
355
- def _get_scaffold(result: Any):
356
- return getattr(result, "scaffold", None)
357
-
358
-
359
- def _explainer_topic(result: Any) -> str:
360
- topic = _coerce_string(getattr(result, "topic", ""))
361
- if topic:
362
- return topic
363
-
364
- joined = " ".join(
365
- _coerce_list(getattr(result, "teaching_points", []))
366
- + _coerce_list(getattr(result, "givens", []))
367
- + _coerce_list(getattr(result, "relationships", []))
368
- )
369
- return _extract_topic_from_lines([joined]) if joined else "general"
370
-
371
-
372
- def _build_scaffold_sections(
373
  result: Any,
 
 
374
  verbosity: float,
375
  transparency: float,
 
 
 
376
  ) -> List[str]:
377
  output: List[str] = []
378
  scaffold = _get_scaffold(result)
379
-
380
  if scaffold is None:
381
  return output
382
 
383
  ask = _coerce_string(getattr(scaffold, "ask", ""))
 
 
384
  setup_actions = _coerce_list(getattr(scaffold, "setup_actions", []))
385
  intermediate_steps = _coerce_list(getattr(scaffold, "intermediate_steps", []))
386
  first_move = _coerce_string(getattr(scaffold, "first_move", ""))
@@ -388,9 +418,97 @@ def _build_scaffold_sections(
388
  variables_to_define = _coerce_list(getattr(scaffold, "variables_to_define", []))
389
  equations_to_form = _coerce_list(getattr(scaffold, "equations_to_form", []))
390
  common_traps = _coerce_list(getattr(scaffold, "common_traps", []))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
 
392
  if ask:
393
- output.append("")
394
  output.append("What to identify first:")
395
  output.append(f"- {ask}")
396
 
@@ -401,11 +519,12 @@ def _build_scaffold_sections(
401
  for item in setup_actions[:limit]:
402
  output.append(f"- {item}")
403
 
404
- if verbosity >= 0.55 and intermediate_steps:
 
405
  output.append("")
406
  output.append("How to build it:")
407
  limit = 2 if verbosity < 0.75 else 4
408
- for item in intermediate_steps[:limit]:
409
  output.append(f"- {item}")
410
 
411
  if first_move:
@@ -418,24 +537,39 @@ def _build_scaffold_sections(
418
  output.append("Next hint:")
419
  output.append(f"- {next_hint}")
420
 
421
- if verbosity >= 0.65 and variables_to_define:
 
 
 
 
 
 
422
  output.append("")
423
  output.append("Variables to define:")
424
  for item in variables_to_define[:3]:
425
  output.append(f"- {item}")
426
 
427
- if transparency >= 0.55 and equations_to_form:
428
  output.append("")
429
  output.append("Equations to form:")
430
  for item in equations_to_form[:3]:
431
  output.append(f"- {item}")
432
 
433
- if transparency >= 0.6 or verbosity >= 0.75:
434
- if common_traps:
435
- output.append("")
436
- output.append("Watch out for:")
437
- for item in common_traps[:4]:
438
- output.append(f"- {item}")
 
 
 
 
 
 
 
 
 
439
 
440
  return output
441
 
@@ -445,6 +579,11 @@ def format_explainer_response(
445
  tone: float,
446
  verbosity: float,
447
  transparency: float,
 
 
 
 
 
448
  ) -> str:
449
  if not result or not getattr(result, "understood", False):
450
  return "I can help explain what the question is asking, but I need the full wording of the question."
@@ -462,11 +601,20 @@ def format_explainer_response(
462
  if summary and not _is_wrapper_line(summary):
463
  output.append(summary)
464
 
465
- scaffold_sections = _build_scaffold_sections(result, verbosity, transparency)
 
 
 
 
 
 
 
 
 
466
  if scaffold_sections:
467
  output.extend(scaffold_sections)
468
 
469
- if teaching_points and verbosity >= 0.5:
470
  output.append("")
471
  output.append("Key teaching points:")
472
  limit = 2 if verbosity < 0.75 else 4
@@ -478,11 +626,11 @@ def format_explainer_response(
478
  output.append(plain)
479
 
480
  asks_for = _coerce_string(getattr(result, "asks_for", ""))
481
- if asks_for and transparency >= 0.3:
482
  output.append("")
483
  output.append(f"The question is asking for: {asks_for}")
484
 
485
- if verbosity >= 0.4:
486
  _append_section(
487
  output,
488
  "What the question gives you:",
@@ -490,7 +638,7 @@ def format_explainer_response(
490
  5 if verbosity >= 0.7 else 3,
491
  )
492
 
493
- if verbosity >= 0.65:
494
  _append_section(
495
  output,
496
  "Constraints or conditions:",
@@ -498,7 +646,7 @@ def format_explainer_response(
498
  5,
499
  )
500
 
501
- if transparency >= 0.45:
502
  _append_section(
503
  output,
504
  "Key relationship:",
@@ -506,7 +654,7 @@ def format_explainer_response(
506
  4,
507
  )
508
 
509
- if transparency >= 0.5:
510
  _append_section(
511
  output,
512
  "Concepts you will probably need:",
@@ -514,7 +662,7 @@ def format_explainer_response(
514
  4,
515
  )
516
 
517
- if transparency >= 0.55 or verbosity >= 0.7:
518
  _append_section(
519
  output,
520
  "Watch out for:",
@@ -523,7 +671,7 @@ def format_explainer_response(
523
  )
524
 
525
  strategy_hint = _coerce_string(getattr(result, "strategy_hint", ""))
526
- if strategy_hint and verbosity >= 0.35 and not _is_wrapper_line(strategy_hint):
527
  output.append("")
528
  output.append(f"Best starting move: {strategy_hint}")
529
 
 
1
  from __future__ import annotations
2
 
3
  import re
4
+ from typing import Any, List, Optional
5
 
6
 
7
  def style_prefix(tone: float) -> str:
 
54
  "you’ve got this — here’s what the question is really asking",
55
  "you've got this — here's what the question is really asking",
56
  "what to identify first",
57
+ "what to identify",
58
  "set-up path",
59
  "setup path",
60
  "first move",
61
  "next hint",
62
+ "next steps",
63
  "watch out for",
64
  "variables to define",
65
  "equations to form",
 
69
  "constraints or conditions",
70
  "key relationship",
71
  "concepts you will probably need",
72
+ "best starting move",
73
+ "final answer",
74
  }
75
  return key in wrapper_lines
76
 
 
111
 
112
  if verbosity < 0.2:
113
  return lines[:1]
 
114
  if verbosity < 0.45:
115
  return lines[:2]
 
116
  if verbosity < 0.75:
117
  return lines[:3]
 
118
  return lines
119
 
120
 
 
182
  return line
183
 
184
 
185
+ def _coerce_string(value: Any) -> str:
186
+ return (value or "").strip() if isinstance(value, str) else ""
187
+
188
+
189
+ def _coerce_list(value: Any) -> List[str]:
190
+ if not value:
191
+ return []
192
+ if isinstance(value, list):
193
+ return [str(v).strip() for v in value if str(v).strip()]
194
+ if isinstance(value, tuple):
195
+ return [str(v).strip() for v in value if str(v).strip()]
196
+ if isinstance(value, str):
197
+ text = value.strip()
198
+ return [text] if text else []
199
+ return []
200
+
201
+
202
+ def _clean_section_items(items: List[str]) -> List[str]:
203
+ cleaned = []
204
+ seen = set()
205
+
206
+ for item in items or []:
207
+ text = (item or "").strip()
208
+ if not text:
209
+ continue
210
+ if _is_wrapper_line(text):
211
+ continue
212
+ key = _normalize_key(text)
213
+ if key in seen:
214
+ continue
215
+ seen.add(key)
216
+ cleaned.append(text)
217
+
218
+ return cleaned
219
+
220
+
221
+ def _append_section(lines: List[str], title: str, items: List[str], limit: int) -> None:
222
+ cleaned = _clean_section_items(items)
223
+ if not cleaned:
224
+ return
225
+
226
+ lines.append("")
227
+ lines.append(title)
228
+ for item in cleaned[:limit]:
229
+ lines.append(f"- {item}")
230
+
231
+
232
+ def _get_scaffold(result: Any):
233
+ return getattr(result, "scaffold", None)
234
+
235
+
236
+ def _explainer_topic(result: Any) -> str:
237
+ topic = _coerce_string(getattr(result, "topic", ""))
238
+ if topic:
239
+ return topic
240
+
241
+ joined = " ".join(
242
+ _coerce_list(getattr(result, "teaching_points", []))
243
+ + _coerce_list(getattr(result, "givens", []))
244
+ + _coerce_list(getattr(result, "relationships", []))
245
+ )
246
+ return _extract_topic_from_lines([joined]) if joined else "general"
247
+
248
+
249
+ def _safe_solver_steps(steps: Optional[List[str]]) -> List[str]:
250
+ if not steps:
251
+ return []
252
+ banned = [
253
+ r"\bthe answer is\b",
254
+ r"\banswer:\b",
255
+ r"\bfinal answer\b",
256
+ r"\bx\s*=",
257
+ r"\by\s*=",
258
+ r"\bresult is\b",
259
+ r"\btherefore\b",
260
+ ]
261
+ cleaned: List[str] = []
262
+ for step in steps:
263
+ s = str(step or "").strip()
264
+ if not s:
265
+ continue
266
+ low = s.lower()
267
+ if any(re.search(p, low) for p in banned):
268
+ continue
269
+ cleaned.append(s)
270
+ return _dedupe_lines(cleaned)
271
+
272
+
273
+ def _explainer_header(tone: float) -> str:
274
+ if tone < 0.2:
275
+ return "Question breakdown:"
276
+ if tone < 0.45:
277
+ return "Here’s what the question is really asking:"
278
+ if tone < 0.75:
279
+ return "Let’s break down what the question is asking:"
280
+ return "You’ve got this — here’s what the question is really asking:"
281
+
282
+
283
  def _format_answer_mode(
284
  lines: List[str],
285
  topic: str,
 
338
  return _format_answer_mode(lines, topic, tone, verbosity, transparency)
339
 
340
  lines = _limit_lines_for_verbosity(lines, verbosity, help_mode)
 
341
  if not lines:
342
  return prefix or "Start with the structure of the problem."
343
 
 
393
  return "\n".join(output).strip()
394
 
395
 
396
+ def _build_progression_sections(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  result: Any,
398
+ help_mode: str,
399
+ hint_stage: int,
400
  verbosity: float,
401
  transparency: float,
402
+ solver_steps: Optional[List[str]] = None,
403
+ can_reveal_answer: bool = False,
404
+ final_answer: Optional[str] = None,
405
  ) -> List[str]:
406
  output: List[str] = []
407
  scaffold = _get_scaffold(result)
 
408
  if scaffold is None:
409
  return output
410
 
411
  ask = _coerce_string(getattr(scaffold, "ask", ""))
412
+ concept = _coerce_string(getattr(scaffold, "concept", ""))
413
+ target = _coerce_string(getattr(scaffold, "target", ""))
414
  setup_actions = _coerce_list(getattr(scaffold, "setup_actions", []))
415
  intermediate_steps = _coerce_list(getattr(scaffold, "intermediate_steps", []))
416
  first_move = _coerce_string(getattr(scaffold, "first_move", ""))
 
418
  variables_to_define = _coerce_list(getattr(scaffold, "variables_to_define", []))
419
  equations_to_form = _coerce_list(getattr(scaffold, "equations_to_form", []))
420
  common_traps = _coerce_list(getattr(scaffold, "common_traps", []))
421
+ hint_ladder = _coerce_list(getattr(scaffold, "hint_ladder", []))
422
+ key_operations = _coerce_list(getattr(scaffold, "key_operations", []))
423
+
424
+ safe_steps = _safe_solver_steps(solver_steps)
425
+
426
+ if help_mode == "definition":
427
+ stage = 0
428
+ elif help_mode in {"walkthrough", "step_by_step"}:
429
+ stage = 3
430
+ elif help_mode == "answer" and can_reveal_answer:
431
+ stage = 3
432
+ elif help_mode == "answer":
433
+ stage = max(2, hint_stage)
434
+ else:
435
+ stage = min(max(hint_stage, 0), 3)
436
+
437
+ if stage == 0:
438
+ if concept:
439
+ output.append(concept)
440
+ if ask:
441
+ output.append("")
442
+ output.append("What to identify:")
443
+ output.append(f"- {ask}")
444
+ if first_move:
445
+ output.append("")
446
+ output.append("First move:")
447
+ output.append(f"- {first_move}")
448
+ elif safe_steps:
449
+ output.append("")
450
+ output.append("First move:")
451
+ output.append(f"- {safe_steps[0]}")
452
+ return output
453
+
454
+ if stage == 1:
455
+ if ask:
456
+ output.append("What to identify:")
457
+ output.append(f"- {ask}")
458
+ if setup_actions:
459
+ output.append("")
460
+ output.append("Set-up path:")
461
+ for item in setup_actions[:2]:
462
+ output.append(f"- {item}")
463
+ if first_move:
464
+ output.append("")
465
+ output.append("First move:")
466
+ output.append(f"- {first_move}")
467
+ if next_hint:
468
+ output.append("")
469
+ output.append("Next hint:")
470
+ output.append(f"- {next_hint}")
471
+ elif safe_steps:
472
+ output.append("")
473
+ output.append("Next hint:")
474
+ output.append(f"- {safe_steps[0]}")
475
+ return output
476
+
477
+ if stage == 2:
478
+ if ask:
479
+ output.append("What to identify:")
480
+ output.append(f"- {ask}")
481
+ if target:
482
+ output.append("")
483
+ output.append(f"Target: {target}")
484
+ if setup_actions:
485
+ output.append("")
486
+ output.append("Set-up path:")
487
+ for item in setup_actions[: min(3, len(setup_actions))]:
488
+ output.append(f"- {item}")
489
+ progression_steps = intermediate_steps[:2] or safe_steps[:2]
490
+ if progression_steps:
491
+ output.append("")
492
+ output.append("Next steps:")
493
+ for item in progression_steps:
494
+ output.append(f"- {item}")
495
+ if next_hint:
496
+ output.append("")
497
+ output.append("Next hint:")
498
+ output.append(f"- {next_hint}")
499
+ if variables_to_define and transparency >= 0.45:
500
+ output.append("")
501
+ output.append("Variables to define:")
502
+ for item in variables_to_define[:2]:
503
+ output.append(f"- {item}")
504
+ if equations_to_form and transparency >= 0.5:
505
+ output.append("")
506
+ output.append("Equations to form:")
507
+ for item in equations_to_form[:2]:
508
+ output.append(f"- {item}")
509
+ return output
510
 
511
  if ask:
 
512
  output.append("What to identify first:")
513
  output.append(f"- {ask}")
514
 
 
519
  for item in setup_actions[:limit]:
520
  output.append(f"- {item}")
521
 
522
+ deep_steps = intermediate_steps or safe_steps
523
+ if deep_steps:
524
  output.append("")
525
  output.append("How to build it:")
526
  limit = 2 if verbosity < 0.75 else 4
527
+ for item in deep_steps[:limit]:
528
  output.append(f"- {item}")
529
 
530
  if first_move:
 
537
  output.append("Next hint:")
538
  output.append(f"- {next_hint}")
539
 
540
+ if hint_ladder and verbosity >= 0.65:
541
+ output.append("")
542
+ output.append("Hint ladder:")
543
+ for item in hint_ladder[:3]:
544
+ output.append(f"- {item}")
545
+
546
+ if variables_to_define and verbosity >= 0.65:
547
  output.append("")
548
  output.append("Variables to define:")
549
  for item in variables_to_define[:3]:
550
  output.append(f"- {item}")
551
 
552
+ if equations_to_form and transparency >= 0.55:
553
  output.append("")
554
  output.append("Equations to form:")
555
  for item in equations_to_form[:3]:
556
  output.append(f"- {item}")
557
 
558
+ if key_operations and transparency >= 0.55:
559
+ output.append("")
560
+ output.append("Key operations:")
561
+ for item in key_operations[:3]:
562
+ output.append(f"- {item}")
563
+
564
+ if (transparency >= 0.6 or verbosity >= 0.75) and common_traps:
565
+ output.append("")
566
+ output.append("Watch out for:")
567
+ for item in common_traps[:4]:
568
+ output.append(f"- {item}")
569
+
570
+ if help_mode == "answer" and can_reveal_answer and final_answer:
571
+ output.append("")
572
+ output.append(f"Final answer: {final_answer}")
573
 
574
  return output
575
 
 
579
  tone: float,
580
  verbosity: float,
581
  transparency: float,
582
+ help_mode: str = "explain",
583
+ hint_stage: int = 0,
584
+ solver_steps: Optional[List[str]] = None,
585
+ can_reveal_answer: bool = False,
586
+ final_answer: Optional[str] = None,
587
  ) -> str:
588
  if not result or not getattr(result, "understood", False):
589
  return "I can help explain what the question is asking, but I need the full wording of the question."
 
601
  if summary and not _is_wrapper_line(summary):
602
  output.append(summary)
603
 
604
+ scaffold_sections = _build_progression_sections(
605
+ result=result,
606
+ help_mode=help_mode,
607
+ hint_stage=hint_stage,
608
+ verbosity=verbosity,
609
+ transparency=transparency,
610
+ solver_steps=solver_steps,
611
+ can_reveal_answer=can_reveal_answer,
612
+ final_answer=final_answer,
613
+ )
614
  if scaffold_sections:
615
  output.extend(scaffold_sections)
616
 
617
+ if teaching_points and verbosity >= 0.55 and hint_stage >= 2:
618
  output.append("")
619
  output.append("Key teaching points:")
620
  limit = 2 if verbosity < 0.75 else 4
 
626
  output.append(plain)
627
 
628
  asks_for = _coerce_string(getattr(result, "asks_for", ""))
629
+ if asks_for and transparency >= 0.35 and hint_stage >= 2:
630
  output.append("")
631
  output.append(f"The question is asking for: {asks_for}")
632
 
633
+ if verbosity >= 0.45 and hint_stage >= 2:
634
  _append_section(
635
  output,
636
  "What the question gives you:",
 
638
  5 if verbosity >= 0.7 else 3,
639
  )
640
 
641
+ if verbosity >= 0.7 and hint_stage >= 3:
642
  _append_section(
643
  output,
644
  "Constraints or conditions:",
 
646
  5,
647
  )
648
 
649
+ if transparency >= 0.5 and hint_stage >= 2:
650
  _append_section(
651
  output,
652
  "Key relationship:",
 
654
  4,
655
  )
656
 
657
+ if transparency >= 0.55 and hint_stage >= 2:
658
  _append_section(
659
  output,
660
  "Concepts you will probably need:",
 
662
  4,
663
  )
664
 
665
+ if (transparency >= 0.6 or verbosity >= 0.75) and hint_stage >= 3:
666
  _append_section(
667
  output,
668
  "Watch out for:",
 
671
  )
672
 
673
  strategy_hint = _coerce_string(getattr(result, "strategy_hint", ""))
674
+ if strategy_hint and verbosity >= 0.35 and not _is_wrapper_line(strategy_hint) and hint_stage >= 1:
675
  output.append("")
676
  output.append(f"Best starting move: {strategy_hint}")
677