Kamocodes commited on
Commit
e27b2a0
·
verified ·
1 Parent(s): 17e6683

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +363 -582
app.py CHANGED
@@ -1,615 +1,396 @@
1
- import gradio as gr
 
2
  import json
3
- import re
4
- import random
5
  import time
6
- import os
7
- from transformers import pipeline
8
- from huggingface_hub import HfApi
 
 
9
 
10
- # Set constants
11
- DEFAULT_NUM_QUESTIONS = 3
12
- DEFAULT_DIFFICULTY = "Medium"
13
- MODEL_GENERATION = "facebook/opt-1.3b" # Free model for question generation
14
- MODEL_VERIFICATION = "gpt2-large" # Free model for verification
 
 
 
15
 
16
- # Initialize models (with low memory footprint)
17
- try:
18
- question_generator = pipeline("text-generation", model=MODEL_GENERATION, max_length=1000)
19
- question_verifier = pipeline("text-generation", model=MODEL_VERIFICATION, max_length=300)
20
- except Exception as e:
21
- print(f"Model loading error: {str(e)}. Will attempt to load on first use.")
22
- question_generator = None
23
- question_verifier = None
24
 
25
- # Calculus curriculum from James Stewart's textbooks
26
- calculus_curriculum = [
27
- {
28
- "chapter": "1. Functions and Models",
29
- "subchapters": [
30
- "1.1 Four Ways to Represent a Function",
31
- "1.2 Mathematical Models",
32
- "1.3 New Functions from Old Functions",
33
- "1.4 Exponential Functions",
34
- "1.5 Inverse Functions and Logarithms",
35
- "1.6 Parametric Curves"
36
- ],
37
- "key_formulas": [
38
- "Domain and Range",
39
- "Function composition: $(f \\circ g)(x) = f(g(x))$",
40
- "Exponential function: $f(x) = a^x$, where $a > 0$",
41
- "Natural exponential function: $f(x) = e^x$",
42
- "Logarithmic function: $f(x) = \\log_a(x)$, where $a > 0, a \\neq 1$",
43
- "Natural logarithm: $f(x) = \\ln(x)$"
44
- ]
45
- },
46
- {
47
- "chapter": "2. Limits and Derivatives",
48
- "subchapters": [
49
- "2.1 The Tangent and Velocity Problems",
50
- "2.2 The Limit of a Function",
51
- "2.3 Calculating Limits",
52
- "2.4 Continuity",
53
- "2.5 The Derivative",
54
- "2.6 The Derivative as a Function",
55
- "2.7 Derivatives of Trigonometric Functions",
56
- "2.8 The Chain Rule",
57
- "2.9 Implicit Differentiation",
58
- "2.10 Related Rates",
59
- "2.11 Linear Approximations and Differentials"
60
- ],
61
- "key_formulas": [
62
- "Limit Definition: $\\lim_{x \\to a} f(x) = L$",
63
- "Derivative Definition: $f'(x) = \\lim_{h \\to 0} \\frac{f(x+h) - f(x)}{h}$",
64
- "Power Rule: $\\frac{d}{dx}(x^n) = nx^{n-1}$",
65
- "Product Rule: $\\frac{d}{dx}[f(x)g(x)] = f'(x)g(x) + f(x)g'(x)$",
66
- "Quotient Rule: $\\frac{d}{dx}\\left[\\frac{f(x)}{g(x)}\\right] = \\frac{f'(x)g(x) - f(x)g'(x)}{[g(x)]^2}$",
67
- "Chain Rule: $\\frac{d}{dx}[f(g(x))] = f'(g(x)) \\cdot g'(x)$"
68
- ]
69
- },
70
- {
71
- "chapter": "3. Applications of Differentiation",
72
- "subchapters": [
73
- "3.1 Maximum and Minimum Values",
74
- "3.2 The Mean Value Theorem",
75
- "3.3 How Derivatives Affect the Shape of a Graph",
76
- "3.4 Indeterminate Forms and L'Hospital's Rule",
77
- "3.5 Summary of Curve Sketching",
78
- "3.6 Optimization Problems",
79
- "3.7 Newton's Method",
80
- "3.8 Antiderivatives"
81
- ],
82
- "key_formulas": [
83
- "Critical Points: $f'(x) = 0$ or $f'(x)$ is undefined",
84
- "Mean Value Theorem: If $f$ is continuous on $[a, b]$ and differentiable on $(a, b)$, then there exists a $c$ in $(a, b)$ such that $f'(c) = \\frac{f(b) - f(a)}{b - a}$",
85
- "Second Derivative Test: If $f'(c) = 0$ and $f''(c) > 0$, then $f$ has a local minimum at $c$",
86
- "L'Hospital's Rule: $\\lim_{x \\to a}\\frac{f(x)}{g(x)} = \\lim_{x \\to a}\\frac{f'(x)}{g'(x)}$",
87
- "Newton's Method: $x_{n+1} = x_n - \\frac{f(x_n)}{f'(x_n)}$"
88
- ]
89
- },
90
- {
91
- "chapter": "4. Integrals",
92
- "subchapters": [
93
- "4.1 Areas and Distances",
94
- "4.2 The Definite Integral",
95
- "4.3 The Fundamental Theorem of Calculus",
96
- "4.4 Indefinite Integrals and the Net Change Theorem",
97
- "4.5 The Substitution Rule"
98
- ],
99
- "key_formulas": [
100
- "Definite Integral: $\\int_a^b f(x)\\,dx = \\lim_{n \\to \\infty} \\sum_{i=1}^{n} f(x_i^*)\\Delta x$",
101
- "Fundamental Theorem of Calculus: $\\int_a^b f(x)\\,dx = F(b) - F(a)$ where $F'(x) = f(x)$",
102
- "Indefinite Integral: $\\int f(x)\\,dx = F(x) + C$ where $F'(x) = f(x)$",
103
- "Power Rule for Integration: $\\int x^n\\,dx = \\frac{x^{n+1}}{n+1} + C$ for $n \\neq -1$",
104
- "Substitution Rule: $\\int f(g(x))g'(x)\\,dx = \\int f(u)\\,du$ where $u = g(x)$"
105
- ]
106
- },
107
- {
108
- "chapter": "5. Applications of Integration",
109
- "subchapters": [
110
- "5.1 Areas Between Curves",
111
- "5.2 Volumes",
112
- "5.3 Volumes by Cylindrical Shells",
113
- "5.4 Work",
114
- "5.5 Average Value of a Function"
115
- ],
116
- "key_formulas": [
117
- "Area Between Curves: $\\int_a^b [f(x) - g(x)]\\,dx$ where $f(x) \\geq g(x)$",
118
- "Volume by Disk Method: $V = \\pi\\int_a^b [R(x)]^2\\,dx$",
119
- "Volume by Washer Method: $V = \\pi\\int_a^b [(R(x))^2 - (r(x))^2]\\,dx$",
120
- "Volume by Cylindrical Shells: $V = 2\\pi\\int_a^b xf(x)\\,dx$ for rotation about y-axis",
121
- "Average Value of $f$ on $[a,b]$: $f_{avg} = \\frac{1}{b-a}\\int_a^b f(x)\\,dx$",
122
- "Work: $W = \\int_a^b F(x)\\,dx$"
123
- ]
124
- },
125
- {
126
- "chapter": "6. Techniques of Integration",
127
- "subchapters": [
128
- "6.1 Integration by Parts",
129
- "6.2 Trigonometric Integrals",
130
- "6.3 Trigonometric Substitution",
131
- "6.4 Integration of Rational Functions by Partial Fractions",
132
- "6.5 Strategy for Integration",
133
- "6.6 Approximate Integration",
134
- "6.7 Improper Integrals"
135
- ],
136
- "key_formulas": [
137
- "Integration by Parts: $\\int u\\,dv = uv - \\int v\\,du$",
138
- "Trigonometric Integrals: $\\int \\sin^n x \\cos^m x\\,dx$ (various formulas)",
139
- "Trig Substitution: $x = a\\sin\\theta$ for $\\sqrt{a^2-x^2}$, $x = a\\tan\\theta$ for $\\sqrt{a^2+x^2}$",
140
- "Partial Fractions: $\\frac{P(x)}{Q(x)} = \\frac{A}{(x-a)} + \\frac{B}{(x-a)^2} + \\frac{Cx+D}{x^2+bx+c} + ...$",
141
- "Improper Integrals: $\\int_a^{\\infty} f(x)\\,dx = \\lim_{t \\to \\infty} \\int_a^t f(x)\\,dx$"
142
- ]
143
- },
144
- {
145
- "chapter": "7. Differential Equations",
146
- "subchapters": [
147
- "7.1 Modeling with Differential Equations",
148
- "7.2 Direction Fields and Euler's Method",
149
- "7.3 Separable Equations",
150
- "7.4 Models for Population Growth",
151
- "7.5 Linear Equations",
152
- "7.6 Predator-Prey Systems"
153
- ],
154
- "key_formulas": [
155
- "General form of a first-order differential equation: $\\frac{dy}{dx} = f(x, y)$",
156
- "Separable equation: $\\frac{dy}{dx} = g(x)h(y)$ → $\\int \\frac{1}{h(y)}dy = \\int g(x)dx + C$",
157
- "First-order linear differential equation: $\\frac{dy}{dx} + P(x)y = Q(x)$",
158
- "Integrating factor method: Multiply by $e^{\\int P(x)dx}$",
159
- "Euler's Method: $y_{n+1} = y_n + hf(x_n, y_n)$"
160
- ]
161
- },
162
- {
163
- "chapter": "8. Infinite Sequences and Series",
164
- "subchapters": [
165
- "8.1 Sequences",
166
- "8.2 Series",
167
- "8.3 The Integral Test and Estimates of Sums",
168
- "8.4 The Comparison Tests",
169
- "8.5 Alternating Series",
170
- "8.6 Absolute Convergence and the Ratio and Root Tests",
171
- "8.7 Strategy for Testing Series",
172
- "8.8 Power Series",
173
- "8.9 Representations of Functions as Power Series",
174
- "8.10 Taylor and Maclaurin Series"
175
- ],
176
- "key_formulas": [
177
- "Geometric Series: $\\sum_{n=0}^{\\infty} ar^n = \\frac{a}{1-r}$ if $|r| < 1$",
178
- "Taylor Series: $f(x) = \\sum_{n=0}^{\\infty} \\frac{f^{(n)}(a)}{n!}(x-a)^n$",
179
- "Maclaurin Series: $f(x) = \\sum_{n=0}^{\\infty} \\frac{f^{(n)}(0)}{n!}x^n$",
180
- "Common Maclaurin Series: $e^x = \\sum_{n=0}^{\\infty} \\frac{x^n}{n!}$, $\\sin(x) = \\sum_{n=0}^{\\infty} \\frac{(-1)^n}{(2n+1)!}x^{2n+1}$",
181
- "Ratio Test: $\\lim_{n \\to \\infty} |\\frac{a_{n+1}}{a_n}| < 1$ implies convergence"
182
- ]
183
- },
184
- {
185
- "chapter": "9. Parametric Equations and Polar Coordinates",
186
- "subchapters": [
187
- "9.1 Parametric Curves",
188
- "9.2 Calculus with Parametric Curves",
189
- "9.3 Polar Coordinates",
190
- "9.4 Areas and Lengths in Polar Coordinates",
191
- "9.5 Conic Sections"
192
- ],
193
- "key_formulas": [
194
- "Parametric curve: $x = f(t)$, $y = g(t)$",
195
- "Arc length of parametric curve: $L = \\int_a^b \\sqrt{[f'(t)]^2 + [g'(t)]^2}\\,dt$",
196
- "Polar to rectangular coordinates: $x = r\\cos\\theta$, $y = r\\sin\\theta$",
197
- "Rectangular to polar coordinates: $r = \\sqrt{x^2 + y^2}$, $\\theta = \\arctan(\\frac{y}{x})$",
198
- "Area in polar coordinates: $A = \\frac{1}{2}\\int_{\\alpha}^{\\beta} [r(\\theta)]^2\\,d\\theta$"
199
- ]
200
- },
201
- {
202
- "chapter": "10. Vectors and the Geometry of Space",
203
- "subchapters": [
204
- "10.1 Three-Dimensional Coordinate Systems",
205
- "10.2 Vectors",
206
- "10.3 The Dot Product",
207
- "10.4 The Cross Product",
208
- "10.5 Equations of Lines and Planes",
209
- "10.6 Cylinders and Quadric Surfaces"
210
- ],
211
- "key_formulas": [
212
- "Dot Product: $\\vec{a} \\cdot \\vec{b} = |\\vec{a}||\\vec{b}|\\cos\\theta$",
213
- "Cross Product: $\\vec{a} \\times \\vec{b} = |\\vec{a}||\\vec{b}|\\sin\\theta\\,\\vec{n}$",
214
- "Equation of a line: $\\vec{r} = \\vec{r_0} + t\\vec{v}$",
215
- "Equation of a plane: $\\vec{n} \\cdot (\\vec{r} - \\vec{r_0}) = 0$ or $ax + by + cz + d = 0$",
216
- "Distance from point to plane: $d = \\frac{|ax_0 + by_0 + cz_0 + d|}{\\sqrt{a^2 + b^2 + c^2}}$"
217
- ]
218
- },
219
- {
220
- "chapter": "11. Vector Functions",
221
- "subchapters": [
222
- "11.1 Vector Functions and Space Curves",
223
- "11.2 Derivatives and Integrals of Vector Functions",
224
- "11.3 Arc Length and Curvature",
225
- "11.4 Motion in Space: Velocity and Acceleration"
226
- ],
227
- "key_formulas": [
228
- "Vector function: $\\vec{r}(t) = x(t)\\vec{i} + y(t)\\vec{j} + z(t)\\vec{k}$",
229
- "Derivative of vector function: $\\vec{r}'(t) = x'(t)\\vec{i} + y'(t)\\vec{j} + z'(t)\\vec{k}$",
230
- "Arc length: $L = \\int_a^b |\\vec{r}'(t)|\\,dt$",
231
- "Unit tangent vector: $\\vec{T}(t) = \\frac{\\vec{r}'(t)}{|\\vec{r}'(t)|}$",
232
- "Curvature: $\\kappa = \\frac{|\\vec{T}'(t)|}{|\\vec{r}'(t)|}$",
233
- "Acceleration: $\\vec{a}(t) = \\vec{r}''(t)$"
234
- ]
235
- },
236
- {
237
- "chapter": "12. Partial Derivatives",
238
- "subchapters": [
239
- "12.1 Functions of Several Variables",
240
- "12.2 Limits and Continuity",
241
- "12.3 Partial Derivatives",
242
- "12.4 Tangent Planes and Linear Approximations",
243
- "12.5 The Chain Rule",
244
- "12.6 Directional Derivatives and the Gradient Vector",
245
- "12.7 Maximum and Minimum Values",
246
- "12.8 Lagrange Multipliers"
247
- ],
248
- "key_formulas": [
249
- "Partial derivative: $\\frac{\\partial f}{\\partial x}(x_0, y_0)$",
250
- "Gradient: $\\nabla f = \\frac{\\partial f}{\\partial x}\\vec{i} + \\frac{\\partial f}{\\partial y}\\vec{j} + \\frac{\\partial f}{\\partial z}\\vec{k}$",
251
- "Directional derivative: $D_\\vec{u}f = \\nabla f \\cdot \\vec{u}$",
252
- "Tangent plane: $z - z_0 = f_x(x_0, y_0)(x - x_0) + f_y(x_0, y_0)(y - y_0)$",
253
- "Chain Rule: $\\frac{dz}{dt} = \\frac{\\partial z}{\\partial x}\\frac{dx}{dt} + \\frac{\\partial z}{\\partial y}\\frac{dy}{dt}$"
254
- ]
255
- },
256
- {
257
- "chapter": "13. Multiple Integrals",
258
- "subchapters": [
259
- "13.1 Double Integrals over Rectangles",
260
- "13.2 Iterated Integrals",
261
- "13.3 Double Integrals over General Regions",
262
- "13.4 Double Integrals in Polar Coordinates",
263
- "13.5 Applications of Double Integrals",
264
- "13.6 Triple Integrals",
265
- "13.7 Triple Integrals in Cylindrical Coordinates",
266
- "13.8 Triple Integrals in Spherical Coordinates",
267
- "13.9 Change of Variables in Multiple Integrals"
268
- ],
269
- "key_formulas": [
270
- "Double integral: $\\iint_R f(x,y)\\,dA = \\int_a^b \\int_c^d f(x,y)\\,dy\\,dx$",
271
- "Area in polar coordinates: $\\iint_R f(r,\\theta)\\,dA = \\int_{\\alpha}^{\\beta} \\int_{h_1(\\theta)}^{h_2(\\theta)} f(r,\\theta)\\,r\\,dr\\,d\\theta$",
272
- "Triple integral: $\\iiint_E f(x,y,z)\\,dV$",
273
- "Cylindrical coordinates: $\\iiint_E f(x,y,z)\\,dV = \\iiint_E f(r\\cos\\theta, r\\sin\\theta, z)\\,r\\,dr\\,d\\theta\\,dz$",
274
- "Spherical coordinates: $\\iiint_E f(x,y,z)\\,dV = \\iiint_E f(\\rho\\sin\\phi\\cos\\theta, \\rho\\sin\\phi\\sin\\theta, \\rho\\cos\\phi)\\,\\rho^2\\sin\\phi\\,d\\rho\\,d\\phi\\,d\\theta$"
275
- ]
276
- },
277
- {
278
- "chapter": "14. Vector Calculus",
279
- "subchapters": [
280
- "14.1 Vector Fields",
281
- "14.2 Line Integrals",
282
- "14.3 The Fundamental Theorem for Line Integrals",
283
- "14.4 Green's Theorem",
284
- "14.5 Curl and Divergence",
285
- "14.6 Surface Integrals",
286
- "14.7 Stokes' Theorem",
287
- "14.8 The Divergence Theorem"
288
- ],
289
- "key_formulas": [
290
- "Line integral of scalar function: $\\int_C f(x,y,z)\\,ds = \\int_a^b f(\\vec{r}(t))|\\vec{r}'(t)|\\,dt$",
291
- "Line integral of vector field: $\\int_C \\vec{F} \\cdot d\\vec{r} = \\int_a^b \\vec{F}(\\vec{r}(t)) \\cdot \\vec{r}'(t)\\,dt$",
292
- "Green's Theorem: $\\oint_C (P\\,dx + Q\\,dy) = \\iint_D (\\frac{\\partial Q}{\\partial x} - \\frac{\\partial P}{\\partial y})\\,dA$",
293
- "Divergence: $\\text{div}\\,\\vec{F} = \\nabla \\cdot \\vec{F} = \\frac{\\partial P}{\\partial x} + \\frac{\\partial Q}{\\partial y} + \\frac{\\partial R}{\\partial z}$",
294
- "Curl: $\\text{curl}\\,\\vec{F} = \\nabla \\times \\vec{F}$",
295
- "Stokes' Theorem: $\\int_S (\\nabla \\times \\vec{F}) \\cdot d\\vec{S} = \\oint_C \\vec{F} \\cdot d\\vec{r}$",
296
- "Divergence Theorem: $\\iiint_E (\\nabla \\cdot \\vec{F})\\,dV = \\iint_{\\partial E} \\vec{F} \\cdot d\\vec{S}$"
297
- ]
298
- }
299
- ]
300
-
301
- def load_models_if_needed():
302
- """Ensures models are loaded when needed"""
303
- global question_generator, question_verifier
304
 
305
- if question_generator is None:
306
- try:
307
- question_generator = pipeline("text-generation", model=MODEL_GENERATION, max_length=1000)
308
- except Exception as e:
309
- return f"Error loading question generator: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
 
311
- if question_verifier is None:
312
- try:
313
- question_verifier = pipeline("text-generation", model=MODEL_VERIFICATION, max_length=300)
314
- except Exception as e:
315
- return f"Error loading question verifier: {str(e)}"
 
316
 
317
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
 
319
- def get_chapter_summary(chapter_idx, subchapter_idx=None):
320
- """Get summary of selected chapter and subchapter"""
321
- if chapter_idx < 0 or chapter_idx >= len(calculus_curriculum):
322
- return "Invalid chapter selection."
323
-
324
- chapter = calculus_curriculum[chapter_idx]
325
 
326
- if subchapter_idx is None or subchapter_idx < 0 or subchapter_idx >= len(chapter["subchapters"]):
327
- # Return chapter summary only
328
- summary = f"# {chapter['chapter']}\n\n"
329
- summary += "## Key Formulas\n"
330
- for formula in chapter.get("key_formulas", []):
331
- summary += f"- {formula}\n"
332
- return summary
333
-
334
- # Return specific subchapter
335
- subchapter = chapter["subchapters"][subchapter_idx]
336
- summary = f"# {chapter['chapter']}\n## {subchapter}\n\n"
337
- summary += "### Key Formulas\n"
338
- for formula in chapter.get("key_formulas", []):
339
- summary += f"- {formula}\n"
340
-
341
- return summary
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
- def generate_question_prompt(chapter, subchapter, difficulty, num_questions=3):
344
- """Generate a prompt for the model to create questions"""
345
- prompt = f"""Create {num_questions} university-level mathematics questions about {subchapter} from {chapter} at {difficulty} difficulty.
346
 
347
  For each question:
348
- 1. Write a clear, university-level calculus problem that requires understanding of the concepts and techniques.
349
- 2. Include a step-by-step solution showing all work and mathematical reasoning.
350
- 3. Provide the final answer.
351
-
352
- Format your response exactly as follows:
353
 
354
- ## Question 1
355
- [Question text]
356
-
357
- ### Solution
358
- Step 1: [First step of solution]
359
- Step 2: [Second step]
360
- ...
361
-
362
- ### Answer
363
- [Final answer]
364
-
365
- ## Question 2
366
  ...
 
367
 
368
- Make sure all mathematics is correct and your solution steps are clear and logical.
369
  """
370
- return prompt
371
-
372
- def verify_question(question_text, solution_text):
373
- """Verify if the question and solution are mathematically sound"""
374
- error_msg = load_models_if_needed()
375
- if error_msg:
376
- return False, error_msg
377
-
378
- verification_prompt = f"""Verify if this calculus question and solution are mathematically correct:
379
-
380
- Question: {question_text}
381
 
382
- Solution: {solution_text}
383
-
384
- Is the solution mathematically correct? Answer Yes or No and briefly explain why."""
385
-
386
- try:
387
- # Get verification response
388
- verification = question_verifier(verification_prompt, max_length=300)[0]['generated_text']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
 
390
- # Check response for indication that the solution is correct
391
- if "yes" in verification.lower() and "incorrect" not in verification.lower() and "error" not in verification.lower():
392
- return True, "Verification passed"
393
- else:
394
- # Extract the explanation for why it's incorrect
395
- explanation = verification.split("No")[1] if "No" in verification else "Unable to determine specific issue"
396
- return False, f"Verification failed: {explanation}"
397
- except Exception as e:
398
- return False, f"Error during verification: {str(e)}"
399
-
400
- def generate_questions(chapter_index, subchapter_index, difficulty, num_questions):
401
- """Generate mathematics questions based on chapter/subchapter"""
402
- error_msg = load_models_if_needed()
403
- if error_msg:
404
- return f"## Error Loading Models\n\n{error_msg}\n\nPlease try again later or contact the administrator."
405
-
406
- # Get chapter and subchapter information
407
- if chapter_index < 0 or chapter_index >= len(calculus_curriculum):
408
- return "Please select a valid chapter."
409
 
410
- chapter = calculus_curriculum[chapter_index]
411
-
412
- if subchapter_index < 0 or subchapter_index >= len(chapter["subchapters"]):
413
- return "Please select a valid subchapter."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
 
415
- subchapter = chapter["subchapters"][subchapter_index]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
 
417
- # Generate prompt for the model
418
- prompt = generate_question_prompt(chapter["chapter"], subchapter, difficulty, num_questions)
 
 
 
 
 
 
 
419
 
420
- try:
421
- # Generate questions
422
- result = question_generator(prompt, max_length=1500, do_sample=True,
423
- temperature=0.7, top_p=0.85, num_return_sequences=1)[0]['generated_text']
424
 
425
- # Extract generated questions and solutions
426
- result = result.replace(prompt, "")
 
 
427
 
428
- # Basic formatting fixes
429
- result = re.sub(r'#+\s*Question', '## Question', result)
430
- result = re.sub(r'#+\s*Solution', '### Solution', result)
431
- result = re.sub(r'#+\s*Answer', '### Answer', result)
432
 
433
- # Check if we got properly formatted output
434
- if "## Question" not in result:
435
- # Fallback to template questions for the topic
436
- result = generate_fallback_questions(chapter["chapter"], subchapter, num_questions)
437
 
438
- # Add chapter summary at the top
439
- summary = get_chapter_summary(chapter_index, subchapter_index)
440
- result = f"{summary}\n\n# Generated Questions\n\n{result}"
 
 
 
 
 
 
 
 
 
 
 
 
 
441
 
442
- return result
 
 
443
 
444
- except Exception as e:
445
- return f"Error generating questions: {str(e)}\n\nPlease try again or select a different topic."
446
-
447
- def generate_fallback_questions(chapter, subchapter, num_questions):
448
- """Generate fallback questions when model generation fails"""
449
- # Basic templates for different calculus topics
450
- if "Limits" in chapter or "Limits" in subchapter:
451
- questions = [
452
- {
453
- "question": "Evaluate the limit: $\\lim_{x \\to 2} \\frac{x^3 - 8}{x - 2}$",
454
- "solution": "Step 1: Note that this is an indeterminate form (0/0) when x = 2.\n" +
455
- "Step 2: Factor the numerator: $x^3 - 8 = (x - 2)(x^2 + 2x + 4)$\n" +
456
- "Step 3: Simplify: $\\lim_{x \\to 2} \\frac{(x - 2)(x^2 + 2x + 4)}{x - 2} = \\lim_{x \\to 2} (x^2 + 2x + 4)$\n" +
457
- "Step 4: Evaluate by direct substitution: $2^2 + 2(2) + 4 = 4 + 4 + 4 = 12$",
458
- "answer": "12"
459
- },
460
- {
461
- "question": "Find the limit: $\\lim_{x \\to 0} \\frac{\\sin(3x)}{x}$",
462
- "solution": "Step 1: Rewrite using the limit property $\\lim_{x \\to 0} \\frac{\\sin x}{x} = 1$\n" +
463
- "Step 2: $\\lim_{x \\to 0} \\frac{\\sin(3x)}{x} = \\lim_{x \\to 0} 3 \\cdot \\frac{\\sin(3x)}{3x}$\n" +
464
- "Step 3: Apply the limit property: $3 \\cdot \\lim_{x \\to 0} \\frac{\\sin(3x)}{3x} = 3 \\cdot 1 = 3$",
465
- "answer": "3"
466
- }
467
- ]
468
- elif "Derivative" in chapter or "Derivative" in subchapter:
469
- questions = [
470
- {
471
- "question": "Find the derivative of $f(x) = x^3\\ln(x) - \\frac{x^3}{3}$",
472
- "solution": "Step 1: Use the product rule on $x^3\\ln(x)$\n" +
473
- "$\\frac{d}{dx}[x^3\\ln(x)] = x^3 \\cdot \\frac{1}{x} + \\ln(x) \\cdot 3x^2$\n" +
474
- "$= x^2 + 3x^2\\ln(x)$\n" +
475
- "Step 2: Find the derivative of $\\frac{x^3}{3}$\n" +
476
- "$\\frac{d}{dx}[\\frac{x^3}{3}] = \\frac{3x^2}{3} = x^2$\n" +
477
- "Step 3: Combine the results\n" +
478
- "$f'(x) = x^2 + 3x^2\\ln(x) - x^2 = 3x^2\\ln(x)$",
479
- "answer": "$f'(x) = 3x^2\\ln(x)$"
480
- }
481
- ]
482
- elif "Integration" in chapter or "Integral" in chapter or "Integration" in subchapter or "Integral" in subchapter:
483
- questions = [
484
- {
485
- "question": "Evaluate the integral: $\\int x^2\\ln(x) dx$",
486
- "solution": "Step 1: Use integration by parts with $u = \\ln(x)$ and $dv = x^2 dx$\n" +
487
- "Then $du = \\frac{1}{x}dx$ and $v = \\frac{x^3}{3}$\n" +
488
- "Step 2: Apply the formula $\\int u dv = uv - \\int v du$\n" +
489
- "$\\int x^2\\ln(x) dx = \\ln(x) \\cdot \\frac{x^3}{3} - \\int \\frac{x^3}{3} \\cdot \\frac{1}{x} dx$\n" +
490
- "$= \\frac{x^3\\ln(x)}{3} - \\frac{1}{3}\\int x^2 dx$\n" +
491
- "$= \\frac{x^3\\ln(x)}{3} - \\frac{1}{3} \\cdot \\frac{x^3}{3} + C$\n" +
492
- "$= \\frac{x^3\\ln(x)}{3} - \\frac{x^3}{9} + C$",
493
- "answer": "$\\frac{x^3\\ln(x)}{3} - \\frac{x^3}{9} + C$"
494
- }
495
- ]
496
- else:
497
- # Generic calculus questions
498
- questions = [
499
- {
500
- "question": "Find the critical points of $f(x) = x^3 - 6x^2 + 12x + 5$ and determine their nature.",
501
- "solution": "Step 1: Find the derivative: $f'(x) = 3x^2 - 12x + 12$\n" +
502
- "Step 2: Set $f'(x) = 0$ and solve: $3x^2 - 12x + 12 = 0$\n" +
503
- "Step 3: Simplify: $x^2 - 4x + 4 = 0$\n" +
504
- "Step 4: Factor: $(x - 2)^2 = 0$\n" +
505
- "Step 5: Therefore $x = 2$ is a critical point (with multiplicity 2)\n" +
506
- "Step 6: Find the second derivative: $f''(x) = 6x - 12$\n" +
507
- "Step 7: Evaluate at $x = 2$: $f''(2) = 6(2) - 12 = 0$\n" +
508
- "Step 8: Since $f''(2) = 0$, the second derivative test is inconclusive\n" +
509
- "Step 9: Checking $f'(x)$ around $x = 2$:\n" +
510
- "For $x < 2$, $f'(x) < 0$ and for $x > 2$, $f'(x) > 0$\n" +
511
- "Step 10: Therefore, $x = 2$ is a point of inflection",
512
- "answer": "$x = 2$ is a critical point and an inflection point"
513
- }
514
- ]
515
-
516
- # Get a random subset of questions or duplicate if we need more
517
- result_questions = []
518
- for i in range(num_questions):
519
- idx = i % len(questions)
520
- q = questions[idx]
521
- result_questions.append({
522
- "id": i+1,
523
- "question": q["question"],
524
- "solution": q["solution"],
525
- "answer": q["answer"]
526
- })
527
-
528
- # Format the output
529
- result = ""
530
- for q in result_questions:
531
- result += f"## Question {q['id']}\n{q['question']}\n\n"
532
- result += f"### Solution\n{q['solution']}\n\n"
533
- result += f"### Answer\n{q['answer']}\n\n"
534
-
535
- return result
536
-
537
- def on_chapter_change(chapter_index):
538
- """Update subchapter dropdown based on selected chapter"""
539
- if chapter_index < 0 or chapter_index >= len(calculus_curriculum):
540
- return gr.Dropdown(choices=[], value=None)
541
-
542
- subchapters = calculus_curriculum[chapter_index]["subchapters"]
543
- return gr.Dropdown(choices=subchapters, value=subchapters[0] if subchapters else None)
544
 
545
- def create_interface():
546
- """Create the Gradio interface"""
547
- # Extract chapter titles for dropdown
548
- chapters = [chapter["chapter"] for chapter in calculus_curriculum]
549
 
550
- with gr.Blocks(title="Calculus Question Generator", theme=gr.themes.Soft()) as demo:
551
- gr.Markdown("# 🧮 Calculus Question Generator")
552
- gr.Markdown("Generate university-level calculus questions with step-by-step solutions.")
553
-
554
- with gr.Row():
555
- with gr.Column(scale=2):
556
- chapter_dropdown = gr.Dropdown(
557
- choices=chapters,
558
- value=chapters[0] if chapters else None,
559
- label="Chapter",
560
- info="Select a chapter from Stewart's Calculus"
561
- )
562
-
563
- subchapter_dropdown = gr.Dropdown(
564
- choices=calculus_curriculum[0]["subchapters"] if calculus_curriculum else [],
565
- value=calculus_curriculum[0]["subchapters"][0] if calculus_curriculum and calculus_curriculum[0]["subchapters"] else None,
566
- label="Subchapter",
567
- info="Select a specific subchapter"
568
- )
569
-
570
- with gr.Row():
571
- num_questions = gr.Slider(
572
- minimum=1,
573
- maximum=5,
574
- value=DEFAULT_NUM_QUESTIONS,
575
- step=1,
576
- label="Number of Questions"
577
- )
578
-
579
- difficulty = gr.Dropdown(
580
- choices=["Easy", "Medium", "Hard", "Advanced"],
581
- value=DEFAULT_DIFFICULTY,
582
- label="Difficulty Level"
583
- )
584
-
585
- generate_button = gr.Button("Generate Questions", variant="primary")
586
-
587
- output = gr.Markdown(label="Generated Questions & Solutions")
588
-
589
- # Handle chapter selection change
590
- chapter_dropdown.change(
591
- fn=on_chapter_change,
592
- inputs=[chapter_dropdown],
593
- outputs=[subchapter_dropdown]
594
- )
595
-
596
- # Handle generate button click
597
- generate_button.click(
598
- fn=generate_questions,
599
- inputs=[
600
- gr.State(lambda: chapters.index(chapter_dropdown.value) if chapter_dropdown.value in chapters else 0),
601
- gr.State(lambda: calculus_curriculum[chapters.index(chapter_dropdown.value) if chapter_dropdown.value in chapters else 0]["subchapters"].index(subchapter_dropdown.value) if subchapter_dropdown.value in calculus_curriculum[chapters.index(chapter_dropdown.value) if chapter_dropdown.value in chapters else 0]["subchapters"] else 0),
602
- difficulty,
603
- num_questions
604
- ],
605
- outputs=[output]
606
- )
607
-
608
- gr.Markdown("---")
609
- gr.Markdown("Created by Kamagelo Mosia | Based on James Stewart's Calculus curriculum")
610
 
611
- return demo
 
 
 
612
 
613
- # Create and launch the interface
614
- demo = create_interface()
615
- demo.launch()
 
1
+ import os
2
+ import torch
3
  import json
 
 
4
  import time
5
+ import logging
6
+ from datetime import datetime
7
+ from threading import Thread
8
+ from queue import Queue
9
+ from transformers import AutoTokenizer, AutoModelForCausalLM
10
 
11
+ # Configuration
12
+ PRIMARY_MODEL = "TinyLlama/TinyLlama-1.1B-Chat-v1.0" # First model to try
13
+ SECONDARY_MODEL = "facebook/opt-1.3b" # More powerful backup model
14
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
15
+ BATCH_SIZE = 5 # Process 5 chapters at a time
16
+ MAX_RETRIES = 3
17
+ OUTPUT_DIR = "calculus_textbook_output"
18
+ LOG_FILE = "textbook_generation.log"
19
 
20
+ # Setup logging
21
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
22
+ logging.basicConfig(
23
+ filename=os.path.join(OUTPUT_DIR, LOG_FILE),
24
+ level=logging.INFO,
25
+ format='%(asctime)s - %(levelname)s - %(message)s'
26
+ )
 
27
 
28
+ class ModelManager:
29
+ """Manages loading and switching between language models for text generation."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ def __init__(self):
32
+ self.models = {}
33
+ self.tokenizers = {}
34
+ self.current_model = None
35
+
36
+ def load_model(self, model_name):
37
+ """Load a model and its tokenizer if not already loaded."""
38
+ if model_name not in self.models:
39
+ try:
40
+ logging.info(f"Loading model: {model_name}")
41
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
42
+ model = AutoModelForCausalLM.from_pretrained(
43
+ model_name,
44
+ torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32,
45
+ device_map="auto" if DEVICE == "cuda" else None
46
+ )
47
+ model.eval()
48
+
49
+ self.models[model_name] = model
50
+ self.tokenizers[model_name] = tokenizer
51
+ logging.info(f"Successfully loaded model: {model_name}")
52
+ return True
53
+ except Exception as e:
54
+ logging.error(f"Failed to load model {model_name}: {str(e)}")
55
+ return False
56
+ return True
57
 
58
+ def set_current_model(self, model_name):
59
+ """Set the current model to use for generation."""
60
+ if model_name not in self.models and not self.load_model(model_name):
61
+ return False
62
+ self.current_model = model_name
63
+ return True
64
 
65
+ def generate_text(self, prompt, max_length=1024):
66
+ """Generate text using the current model."""
67
+ if not self.current_model:
68
+ raise ValueError("No model selected. Call set_current_model first.")
69
+
70
+ model = self.models[self.current_model]
71
+ tokenizer = self.tokenizers[self.current_model]
72
+
73
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
74
+
75
+ # Generate with some randomness for creativity
76
+ with torch.no_grad():
77
+ outputs = model.generate(
78
+ **inputs,
79
+ max_length=max_length,
80
+ temperature=0.7,
81
+ top_p=0.9,
82
+ do_sample=True,
83
+ pad_token_id=tokenizer.eos_token_id
84
+ )
85
+
86
+ response = tokenizer.decode(outputs[0], skip_special_tokens=True)
87
+ # Extract only the generated part
88
+ generated_text = response[len(tokenizer.decode(inputs['input_ids'][0], skip_special_tokens=True)):].strip()
89
+ return generated_text
90
 
91
+ class CalculusTextbookGenerator:
92
+ """Generates a complete calculus textbook with questions and solutions."""
 
 
 
 
93
 
94
+ def __init__(self):
95
+ self.model_manager = ModelManager()
96
+ self.textbook_data = self.create_initial_textbook_structure()
97
+
98
+ def create_initial_textbook_structure(self):
99
+ """Create the initial structure of the calculus textbook."""
100
+ return {
101
+ "books": [
102
+ {
103
+ "name": "Calculus 1: Early Transcendentals",
104
+ "details": "Introduction to single-variable calculus including limits, derivatives, and basic integration techniques.",
105
+ "chapters": [
106
+ {
107
+ "chapterTitle": "Chapter 6: Applications of Integration",
108
+ "subChapters": [
109
+ "6.1: Areas Between Curves",
110
+ "6.2: Volumes",
111
+ "6.3: Volumes by Cylindrical Shells",
112
+ "6.4: Work",
113
+ "6.5: Average Value of a Function"
114
+ ],
115
+ "questions": [] # Will be filled with generated questions
116
+ },
117
+ {
118
+ "chapterTitle": "Chapter 8: Further Applications of Integration",
119
+ "subChapters": [
120
+ "8.1: Arc Length",
121
+ "8.2: Area of a Surface of Revolution",
122
+ "8.3: Applications to Physics and Engineering",
123
+ "8.4: Applications to Economics and Biology",
124
+ "8.5: Probability"
125
+ ],
126
+ "questions": []
127
+ },
128
+ {
129
+ "chapterTitle": "Chapter 9: Differential Equations",
130
+ "subChapters": [
131
+ "9.1: Modeling with Differential Equations",
132
+ "9.2: Direction Fields and Euler's Method",
133
+ "9.3: Separable Equations",
134
+ "9.4: Models for Population Growth",
135
+ "9.5: Linear Equations",
136
+ "9.6: Predator–Prey Systems"
137
+ ],
138
+ "questions": []
139
+ },
140
+ {
141
+ "chapterTitle": "Chapter 10: Parametric Equations and Polar Coordinates",
142
+ "subChapters": [
143
+ "10.1: Curves Defined by Parametric Equations",
144
+ "10.2: Calculus with Parametric Curves",
145
+ "10.3: Polar Coordinates",
146
+ "10.4: Calculus in Polar Coordinates",
147
+ "10.5: Conic Sections",
148
+ "10.6: Conic Sections in Polar Coordinates"
149
+ ],
150
+ "questions": []
151
+ },
152
+ {
153
+ "chapterTitle": "Chapter 11: Sequences, Series, and Power Series",
154
+ "subChapters": [
155
+ "11.1: Sequences",
156
+ "11.2: Series",
157
+ "11.3: The Integral Test and Estimates of Sums",
158
+ "11.4: The Comparison Tests",
159
+ "11.5: Alternating Series and Absolute Convergence",
160
+ "11.6: The Ratio and Root Tests",
161
+ "11.7: Power Series"
162
+ ],
163
+ "questions": []
164
+ }
165
+ ]
166
+ },
167
+ {
168
+ "name": "Calculus 2: Advanced Concepts",
169
+ "details": "Advances into series, sequences, techniques of integration, and vector calculus.",
170
+ "chapters": [
171
+ {
172
+ "chapterTitle": "Chapter 12: Vectors and the Geometry of Space",
173
+ "subChapters": [
174
+ "12.1: Three-Dimensional Coordinate Systems",
175
+ "12.2: Vectors",
176
+ "12.3: The Dot Product",
177
+ "12.4: The Cross Product",
178
+ "12.5: Equations of Lines and Planes",
179
+ "12.6: Cylinders and Quadric Surfaces"
180
+ ],
181
+ "questions": []
182
+ },
183
+ {
184
+ "chapterTitle": "Chapter 13: Vector Functions",
185
+ "subChapters": [
186
+ "13.1: Vector Functions and Space Curves",
187
+ "13.2: Derivatives and Integrals of Vector Functions",
188
+ "13.3: Arc Length and Curvature",
189
+ "13.4: Motion in Space: Velocity and Acceleration"
190
+ ],
191
+ "questions": []
192
+ },
193
+ {
194
+ "chapterTitle": "Chapter 14: Partial Derivatives",
195
+ "subChapters": [
196
+ "14.1: Functions of Several Variables",
197
+ "14.2: Limits and Continuity",
198
+ "14.3: Partial Derivatives",
199
+ "14.4: Tangent Planes and Linear Approximation",
200
+ "14.5: The Chain Rule"
201
+ ],
202
+ "questions": []
203
+ }
204
+ ]
205
+ }
206
+ ]
207
+ }
208
+
209
+ def generate_question_set(self, chapter_title, subchapter_titles, num_questions=3):
210
+ """Generate a set of questions with step-by-step solutions for a chapter."""
211
+
212
+ # Try the primary model first
213
+ self.model_manager.set_current_model(PRIMARY_MODEL)
214
+
215
+ prompt = f"""Create {num_questions} calculus questions with detailed step-by-step solutions for:
216
+ {chapter_title}
217
 
218
+ The questions should cover these subchapters:
219
+ {', '.join(subchapter_titles)}
 
220
 
221
  For each question:
222
+ 1. Write a clear, university-level calculus problem
223
+ 2. Provide a comprehensive step-by-step solution with all math steps shown
224
+ 3. Include a final answer
 
 
225
 
226
+ Format each question as:
227
+ QUESTION: [Problem statement]
228
+ SOLUTION:
229
+ Step 1: [First step with explanation]
230
+ Step 2: [Next step]
 
 
 
 
 
 
 
231
  ...
232
+ Final Answer: [The solution]
233
 
234
+ Make sure to use proper mathematical notation and include a variety of question types.
235
  """
 
 
 
 
 
 
 
 
 
 
 
236
 
237
+ try:
238
+ generated_content = self.model_manager.generate_text(prompt, max_length=2048)
239
+
240
+ # Check if the content looks good
241
+ if len(generated_content) < 200 or "QUESTION" not in generated_content:
242
+ # Try the secondary model if the primary one gave poor results
243
+ logging.warning(f"Primary model gave insufficient results for {chapter_title}. Trying secondary model.")
244
+ self.model_manager.set_current_model(SECONDARY_MODEL)
245
+ generated_content = self.model_manager.generate_text(prompt, max_length=2048)
246
+
247
+ # Parse the generated content into question objects
248
+ questions = self.parse_questions(generated_content)
249
+
250
+ if not questions or len(questions) == 0:
251
+ logging.warning(f"Failed to parse any questions from content for {chapter_title}")
252
+ return []
253
+
254
+ return questions
255
+
256
+ except Exception as e:
257
+ logging.error(f"Error generating questions for {chapter_title}: {str(e)}")
258
+ return []
259
+
260
+ def parse_questions(self, content):
261
+ """Parse the generated content into structured question objects."""
262
+ questions = []
263
 
264
+ # Split by "QUESTION:" or similar markers
265
+ parts = content.split("QUESTION:")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
+ for i, part in enumerate(parts):
268
+ if i == 0:
269
+ continue # Skip the first part (before the first QUESTION:)
270
+
271
+ try:
272
+ # Split into question and solution
273
+ if "SOLUTION:" in part:
274
+ question_text, solution = part.split("SOLUTION:", 1)
275
+ else:
276
+ # Try alternative formats
277
+ for marker in ["Solution:", "STEPS:", "Steps:"]:
278
+ if marker in part:
279
+ question_text, solution = part.split(marker, 1)
280
+ break
281
+ else:
282
+ question_text = part
283
+ solution = ""
284
+
285
+ questions.append({
286
+ "question": question_text.strip(),
287
+ "solution": solution.strip()
288
+ })
289
+ except Exception as e:
290
+ logging.error(f"Error parsing question {i}: {str(e)}")
291
+ continue
292
+
293
+ return questions
294
 
295
+ def worker_function(self, queue, results):
296
+ """Worker thread function to process chapters from queue."""
297
+ while True:
298
+ item = queue.get()
299
+ if item is None: # None signals to exit
300
+ queue.task_done()
301
+ break
302
+
303
+ book_idx, chapter_idx, chapter = item
304
+ chapter_title = chapter["chapterTitle"]
305
+ subchapters = chapter.get("subChapters", [])
306
+
307
+ logging.info(f"Processing: {chapter_title}")
308
+
309
+ # Try to generate questions with retries
310
+ for attempt in range(MAX_RETRIES):
311
+ try:
312
+ questions = self.generate_question_set(chapter_title, subchapters, num_questions=4)
313
+ if questions:
314
+ # Save the questions to the chapter
315
+ self.textbook_data["books"][book_idx]["chapters"][chapter_idx]["questions"] = questions
316
+
317
+ logging.info(f"✓ Generated {len(questions)} questions for {chapter_title}")
318
+ break # Success, exit retry loop
319
+ else:
320
+ logging.warning(f"No questions generated for {chapter_title} on attempt {attempt+1}")
321
+
322
+ except Exception as e:
323
+ logging.error(f"Attempt {attempt+1}/{MAX_RETRIES} failed for {chapter_title}: {str(e)}")
324
+ time.sleep(2) # Wait before retrying
325
+
326
+ # Save current state to file
327
+ self.save_current_state()
328
+ queue.task_done()
329
 
330
+ def save_current_state(self):
331
+ """Save the current state of the textbook generation."""
332
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
333
+ with open(os.path.join(OUTPUT_DIR, f"textbook_state_{timestamp}.json"), "w") as f:
334
+ json.dump(self.textbook_data, f, indent=2)
335
+
336
+ # Also save to a fixed filename for the latest state
337
+ with open(os.path.join(OUTPUT_DIR, "textbook_latest.json"), "w") as f:
338
+ json.dump(self.textbook_data, f, indent=2)
339
 
340
+ def process_in_batches(self):
341
+ """Process all chapters in batches."""
342
+ queue = Queue()
 
343
 
344
+ # Queue all chapters for processing
345
+ for book_idx, book in enumerate(self.textbook_data["books"]):
346
+ for chapter_idx, chapter in enumerate(book["chapters"]):
347
+ queue.put((book_idx, chapter_idx, chapter))
348
 
349
+ # Create and start worker thread
350
+ worker = Thread(target=self.worker_function, args=(queue, None))
351
+ worker.daemon = True # Allow the program to exit even if the thread is running
352
+ worker.start()
353
 
354
+ # Process in batches
355
+ total_chapters = queue.qsize()
356
+ processed = 0
 
357
 
358
+ while processed < total_chapters:
359
+ # Wait for the batch to be processed
360
+ start_size = queue.qsize()
361
+ batch_size = min(BATCH_SIZE, start_size)
362
+
363
+ logging.info(f"Processing batch of {batch_size} chapters. {start_size} remaining.")
364
+
365
+ # Wait until this batch is done
366
+ while queue.qsize() > start_size - batch_size:
367
+ time.sleep(2)
368
+
369
+ processed += batch_size
370
+ logging.info(f"Batch complete. {processed}/{total_chapters} chapters processed.")
371
+
372
+ # Save current state
373
+ self.save_current_state()
374
 
375
+ # Signal worker to exit
376
+ queue.put(None)
377
+ worker.join()
378
 
379
+ # Save final state
380
+ self.save_current_state()
381
+ logging.info("All chapters processed. Textbook generation complete.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
 
383
+ def main():
384
+ start_time = datetime.now()
385
+ logging.info(f"Starting textbook generation at {start_time}")
 
386
 
387
+ generator = CalculusTextbookGenerator()
388
+ generator.process_in_batches()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
 
390
+ end_time = datetime.now()
391
+ duration = end_time - start_time
392
+ logging.info(f"Textbook generation completed in {duration}")
393
+ logging.info(f"Final textbook saved to {os.path.join(OUTPUT_DIR, 'textbook_latest.json')}")
394
 
395
+ if __name__ == "__main__":
396
+ main()