j-js commited on
Commit
70ad043
·
verified ·
1 Parent(s): 8a961f1

Update formatting.py

Browse files
Files changed (1) hide show
  1. formatting.py +95 -323
formatting.py CHANGED
@@ -1,228 +1,3 @@
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:
8
- if tone < 0.2:
9
- return ""
10
- if tone < 0.45:
11
- return "ok.."
12
- if tone < 0.75:
13
- return "Let’s work through it."
14
- return "You’ve got this — let’s solve it cleanly."
15
-
16
-
17
- def _normalize_key(text: str) -> str:
18
- text = (text or "").strip().lower()
19
- text = text.replace("’", "'")
20
- text = re.sub(r"\s+", " ", text)
21
- return text
22
-
23
-
24
- def _clean_lines(core: str) -> list[str]:
25
- lines = []
26
- for line in (core or "").splitlines():
27
- cleaned = line.strip()
28
- if cleaned:
29
- lines.append(cleaned)
30
- return lines
31
-
32
-
33
- def _dedupe_lines(lines: list[str]) -> list[str]:
34
- seen = set()
35
- output = []
36
- for line in lines:
37
- key = _normalize_key(line)
38
- if key and key not in seen:
39
- seen.add(key)
40
- output.append(line.strip())
41
- return output
42
-
43
-
44
- def _coerce_string(value: Any) -> str:
45
- return (value or "").strip() if isinstance(value, str) else ""
46
-
47
-
48
- def _coerce_list(value: Any) -> List[str]:
49
- if not value:
50
- return []
51
- if isinstance(value, list):
52
- return [str(v).strip() for v in value if str(v).strip()]
53
- if isinstance(value, tuple):
54
- return [str(v).strip() for v in value if str(v).strip()]
55
- if isinstance(value, str):
56
- text = value.strip()
57
- return [text] if text else []
58
- return []
59
-
60
-
61
- def _limit_steps(steps: List[str], verbosity: float, minimum: int = 1) -> List[str]:
62
- if not steps:
63
- return []
64
- if verbosity < 0.25:
65
- limit = minimum
66
- elif verbosity < 0.5:
67
- limit = max(minimum, 2)
68
- elif verbosity < 0.75:
69
- limit = max(minimum, 3)
70
- else:
71
- limit = max(minimum, 5)
72
- return steps[:limit]
73
-
74
-
75
- def _why_line(topic: str) -> str:
76
- topic = (topic or "").lower()
77
-
78
- if topic == "algebra":
79
- return "Why: algebra works by keeping the relationship balanced while undoing the operations attached to the variable."
80
- if topic == "percent":
81
- return "Why: percent questions depend on choosing the correct base before doing any calculation."
82
- if topic == "ratio":
83
- return "Why: ratio questions depend on preserving the comparison and using one shared scale factor."
84
- if topic == "probability":
85
- return "Why: probability compares successful outcomes to all possible outcomes."
86
- if topic == "statistics":
87
- return "Why: the right method depends on which summary measure the question actually asks for."
88
- if topic == "geometry":
89
- return "Why: geometry depends on the relationships between the parts of the figure."
90
- if topic == "number_theory":
91
- return "Why: number properties follow fixed rules about divisibility, factors, and remainders."
92
- return "Why: start with the structure of the problem before calculating."
93
-
94
-
95
- def _extract_topic_from_text(text: str, fallback: Optional[str] = None) -> str:
96
- low = (text or "").lower()
97
- if fallback:
98
- return fallback
99
- if any(word in low for word in ["equation", "variable", "isolate", "algebra"]):
100
- return "algebra"
101
- if any(word in low for word in ["percent", "percentage", "%"]):
102
- return "percent"
103
- if any(word in low for word in ["ratio", "proportion"]):
104
- return "ratio"
105
- if any(word in low for word in ["probability", "outcome", "chance", "odds"]):
106
- return "probability"
107
- if any(word in low for word in ["mean", "median", "average"]):
108
- return "statistics"
109
- if any(word in low for word in ["triangle", "circle", "angle", "area", "perimeter"]):
110
- return "geometry"
111
- if any(word in low for word in ["integer", "factor", "multiple", "prime", "remainder"]):
112
- return "number_theory"
113
- return "general"
114
-
115
-
116
- def _format_answer_mode(
117
- lines: List[str],
118
- topic: str,
119
- tone: float,
120
- verbosity: float,
121
- transparency: float,
122
- ) -> str:
123
- output: List[str] = []
124
- prefix = style_prefix(tone)
125
- if prefix:
126
- output.append(prefix)
127
- output.append("")
128
-
129
- limited = _limit_steps(lines, verbosity, minimum=2)
130
- if limited:
131
- output.append("Answer path:")
132
- if len(limited) >= 1:
133
- output.append(f"- What to identify: {limited[0]}")
134
- if len(limited) >= 2:
135
- output.append(f"- First move: {limited[1]}")
136
- if len(limited) >= 3:
137
- output.append(f"- Next step: {limited[2]}")
138
- for extra in limited[3:]:
139
- output.append(f"- Keep in mind: {extra}")
140
-
141
- if transparency >= 0.8:
142
- output.append("")
143
- output.append(_why_line(topic))
144
-
145
- return "\n".join(output).strip()
146
-
147
-
148
- def format_reply(
149
- core: str,
150
- tone: float,
151
- verbosity: float,
152
- transparency: float,
153
- help_mode: str,
154
- hint_stage: int = 0,
155
- topic: Optional[str] = None,
156
- ) -> str:
157
- prefix = style_prefix(tone)
158
- core = (core or "").strip()
159
-
160
- if not core:
161
- return prefix or "Start with the structure of the problem."
162
-
163
- lines = _dedupe_lines(_clean_lines(core))
164
- if not lines:
165
- return prefix or "Start with the structure of the problem."
166
-
167
- resolved_topic = _extract_topic_from_text(core, topic)
168
-
169
- if help_mode == "answer":
170
- return _format_answer_mode(lines, resolved_topic, tone, verbosity, transparency)
171
-
172
- shown = _limit_steps(lines, verbosity, minimum=1)
173
- output: List[str] = []
174
-
175
- if prefix:
176
- output.append(prefix)
177
- output.append("")
178
-
179
- if help_mode == "hint":
180
- output.append("Hint:")
181
- output.append(f"- {shown[0]}")
182
- if transparency >= 0.8:
183
- output.append("")
184
- output.append(_why_line(resolved_topic))
185
- return "\n".join(output).strip()
186
-
187
- if help_mode in {"instruction", "step_by_step", "walkthrough"}:
188
- label = "First step:" if help_mode == "instruction" else "Walkthrough:"
189
- output.append(label)
190
- for line in shown:
191
- output.append(f"- {line}")
192
- if transparency >= 0.8:
193
- output.append("")
194
- output.append(_why_line(resolved_topic))
195
- return "\n".join(output).strip()
196
-
197
- if help_mode in {"method", "explain", "concept", "definition"}:
198
- label = {
199
- "method": "Method:",
200
- "explain": "Explanation:",
201
- "concept": "Key idea:",
202
- "definition": "Key idea:",
203
- }[help_mode]
204
- output.append(label)
205
- for line in shown:
206
- output.append(f"- {line}")
207
- if transparency >= 0.75:
208
- output.append("")
209
- output.append(_why_line(resolved_topic))
210
- return "\n".join(output).strip()
211
-
212
- for line in shown:
213
- output.append(f"- {line}")
214
-
215
- if transparency >= 0.85:
216
- output.append("")
217
- output.append(_why_line(resolved_topic))
218
-
219
- return "\n".join(output).strip()
220
-
221
-
222
- def _get_scaffold(result: Any):
223
- return getattr(result, "scaffold", None)
224
-
225
-
226
  def _staged_scaffold_lines(
227
  result: Any,
228
  hint_stage: int,
@@ -248,77 +23,122 @@ def _staged_scaffold_lines(
248
  hint_ladder = _coerce_list(getattr(scaffold, "hint_ladder", []))
249
  key_operations = _coerce_list(getattr(scaffold, "key_operations", []))
250
 
251
- if concept and stage == 0 and transparency >= 0.75:
252
- output.append("Core idea:")
253
- output.append(f"- {concept}")
254
- output.append("")
 
 
255
 
256
- if ask:
257
- output.append("What to identify first:")
258
- output.append(f"- {ask}")
259
 
260
- if stage == 0:
261
- if first_move:
262
  output.append("")
263
- output.append("First move:")
264
- output.append(f"- {first_move}")
265
- elif hint_ladder:
 
 
266
  output.append("")
267
  output.append("First move:")
268
- output.append(f"- {hint_ladder[0]}")
269
- return output
270
 
271
- if setup_actions:
272
- output.append("")
273
- output.append("Set-up path:")
274
- for item in _limit_steps(setup_actions, verbosity, minimum=2 if stage >= 1 else 1):
275
- output.append(f"- {item}")
276
 
277
- if first_move:
278
- output.append("")
279
- output.append("First move:")
280
- output.append(f"- {first_move}")
 
281
 
282
- if stage == 1:
283
- if next_hint:
284
  output.append("")
285
- output.append("Next hint:")
286
- output.append(f"- {next_hint}")
287
- elif len(hint_ladder) >= 2:
 
 
 
 
 
 
 
 
288
  output.append("")
289
  output.append("Next hint:")
290
- output.append(f"- {hint_ladder[1]}")
 
 
 
 
 
 
 
291
  return output
292
 
293
- if intermediate_steps:
294
- output.append("")
295
- output.append("How to build it:")
296
- for item in _limit_steps(intermediate_steps, verbosity, minimum=2):
297
- output.append(f"- {item}")
298
 
299
- if next_hint:
300
- output.append("")
301
- output.append("Next hint:")
302
- output.append(f"- {next_hint}")
 
 
 
 
 
303
 
 
 
 
304
  if stage == 2:
 
 
 
 
 
 
 
 
 
 
 
305
  if variables_to_define:
306
  output.append("")
307
  output.append("Variables to define:")
308
  for item in variables_to_define[:2]:
309
  output.append(f"- {item}")
 
310
  if equations_to_form:
311
  output.append("")
312
  output.append("Equations to form:")
313
  for item in equations_to_form[:2]:
314
  output.append(f"- {item}")
315
- if key_operations:
316
- output.append("")
317
- output.append("Key operations:")
318
- for item in key_operations[:3]:
319
- output.append(f"- {item}")
320
  return output
321
 
 
 
 
 
 
 
 
 
 
 
 
 
322
  if variables_to_define:
323
  output.append("")
324
  output.append("Variables to define:")
@@ -337,63 +157,15 @@ def _staged_scaffold_lines(
337
  for item in key_operations[:3]:
338
  output.append(f"- {item}")
339
 
340
- if common_traps:
341
  output.append("")
342
- output.append("Watch out for:")
343
- for item in common_traps[:4]:
344
- output.append(f"- {item}")
345
-
346
- return output
347
-
348
-
349
- def format_explainer_response(
350
- result: Any,
351
- tone: float,
352
- verbosity: float,
353
- transparency: float,
354
- hint_stage: int = 0,
355
- ) -> str:
356
- if not result or not getattr(result, "understood", False):
357
- return "I can help explain what the question is asking, but I need the full wording of the question."
358
-
359
- output: List[str] = []
360
- prefix = style_prefix(tone)
361
- if prefix:
362
- output.append(prefix)
363
- output.append("")
364
-
365
- output.append("Question breakdown:")
366
- output.append("")
367
-
368
- summary = _coerce_string(getattr(result, "summary", ""))
369
- if summary:
370
- output.append(summary)
371
-
372
- scaffold_lines = _staged_scaffold_lines(
373
- result=result,
374
- hint_stage=hint_stage,
375
- verbosity=verbosity,
376
- transparency=transparency,
377
- )
378
- if scaffold_lines:
379
- if summary:
380
- output.append("")
381
- output.extend(scaffold_lines)
382
 
383
- teaching_points = _coerce_list(getattr(result, "teaching_points", []))
384
- if teaching_points and (verbosity >= 0.55 or hint_stage >= 2):
385
  output.append("")
386
- output.append("Key teaching points:")
387
- for item in _limit_steps(teaching_points, verbosity, minimum=2):
388
  output.append(f"- {item}")
389
 
390
- topic = _extract_topic_from_text(
391
- f"{summary} {' '.join(teaching_points)}",
392
- getattr(result, "topic", None),
393
- )
394
-
395
- if transparency >= 0.8:
396
- output.append("")
397
- output.append(_why_line(topic))
398
-
399
- return "\n".join(_dedupe_lines(output)).strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  def _staged_scaffold_lines(
2
  result: Any,
3
  hint_stage: int,
 
23
  hint_ladder = _coerce_list(getattr(scaffold, "hint_ladder", []))
24
  key_operations = _coerce_list(getattr(scaffold, "key_operations", []))
25
 
26
+ # STAGE 0 = full scaffold / walkthrough
27
+ if stage == 0:
28
+ if concept and transparency >= 0.75:
29
+ output.append("Core idea:")
30
+ output.append(f"- {concept}")
31
+ output.append("")
32
 
33
+ if ask:
34
+ output.append("What to identify first:")
35
+ output.append(f"- {ask}")
36
 
37
+ if setup_actions:
 
38
  output.append("")
39
+ output.append("Set-up path:")
40
+ for item in _limit_steps(setup_actions, verbosity, minimum=2):
41
+ output.append(f"- {item}")
42
+
43
+ if first_move or hint_ladder:
44
  output.append("")
45
  output.append("First move:")
46
+ output.append(f"- {first_move or hint_ladder[0]}")
 
47
 
48
+ if intermediate_steps:
49
+ output.append("")
50
+ output.append("How to build it:")
51
+ for item in _limit_steps(intermediate_steps, verbosity, minimum=2):
52
+ output.append(f"- {item}")
53
 
54
+ if variables_to_define:
55
+ output.append("")
56
+ output.append("Variables to define:")
57
+ for item in variables_to_define[:3]:
58
+ output.append(f"- {item}")
59
 
60
+ if equations_to_form:
 
61
  output.append("")
62
+ output.append("Equations to form:")
63
+ for item in equations_to_form[:3]:
64
+ output.append(f"- {item}")
65
+
66
+ if key_operations:
67
+ output.append("")
68
+ output.append("Key operations:")
69
+ for item in key_operations[:3]:
70
+ output.append(f"- {item}")
71
+
72
+ if next_hint or len(hint_ladder) >= 2:
73
  output.append("")
74
  output.append("Next hint:")
75
+ output.append(f"- {next_hint or hint_ladder[1]}")
76
+
77
+ if common_traps and verbosity >= 0.7:
78
+ output.append("")
79
+ output.append("Watch out for:")
80
+ for item in common_traps[:3]:
81
+ output.append(f"- {item}")
82
+
83
  return output
84
 
85
+ # STAGE 1 = short hint only
86
+ if stage == 1:
87
+ if ask:
88
+ output.append("What to identify first:")
89
+ output.append(f"- {ask}")
90
 
91
+ if first_move or hint_ladder:
92
+ output.append("")
93
+ output.append("First move:")
94
+ output.append(f"- {first_move or hint_ladder[0]}")
95
+
96
+ if next_hint or len(hint_ladder) >= 2:
97
+ output.append("")
98
+ output.append("Next hint:")
99
+ output.append(f"- {next_hint or hint_ladder[1]}")
100
 
101
+ return output
102
+
103
+ # STAGE 2 = deeper structure only
104
  if stage == 2:
105
+ if setup_actions:
106
+ output.append("Set-up path:")
107
+ for item in _limit_steps(setup_actions, verbosity, minimum=2):
108
+ output.append(f"- {item}")
109
+
110
+ if intermediate_steps:
111
+ output.append("")
112
+ output.append("How to build it:")
113
+ for item in _limit_steps(intermediate_steps, verbosity, minimum=2):
114
+ output.append(f"- {item}")
115
+
116
  if variables_to_define:
117
  output.append("")
118
  output.append("Variables to define:")
119
  for item in variables_to_define[:2]:
120
  output.append(f"- {item}")
121
+
122
  if equations_to_form:
123
  output.append("")
124
  output.append("Equations to form:")
125
  for item in equations_to_form[:2]:
126
  output.append(f"- {item}")
127
+
 
 
 
 
128
  return output
129
 
130
+ # STAGE 3+ = fuller help, still no answer reveal
131
+ if setup_actions:
132
+ output.append("Set-up path:")
133
+ for item in _limit_steps(setup_actions, verbosity, minimum=2):
134
+ output.append(f"- {item}")
135
+
136
+ if intermediate_steps:
137
+ output.append("")
138
+ output.append("How to build it:")
139
+ for item in _limit_steps(intermediate_steps, verbosity, minimum=2):
140
+ output.append(f"- {item}")
141
+
142
  if variables_to_define:
143
  output.append("")
144
  output.append("Variables to define:")
 
157
  for item in key_operations[:3]:
158
  output.append(f"- {item}")
159
 
160
+ if next_hint or len(hint_ladder) >= 2:
161
  output.append("")
162
+ output.append("Next hint:")
163
+ output.append(f"- {next_hint or hint_ladder[1]}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
+ if common_traps and verbosity >= 0.65:
 
166
  output.append("")
167
+ output.append("Watch out for:")
168
+ for item in common_traps[:3]:
169
  output.append(f"- {item}")
170
 
171
+ return output