|
294 | 294 | "outputs": [], |
295 | 295 | "source": [ |
296 | 296 | "# Set up the LLM via LangChain.\n", |
297 | | - "llm = ChatOpenAI(\n", |
298 | | - " model=os.environ['OPENAI_MODEL'],\n", |
| 297 | + "\n", |
| 298 | + "# Uses gpt-4.1-nano:\n", |
| 299 | + "# - a faster model\n", |
| 300 | + "# - less intelligent\n", |
| 301 | + "\n", |
| 302 | + "llm_nano = ChatOpenAI(\n", |
| 303 | + " model=\"gpt-4.1-nano\",\n", |
| 304 | + " api_key=os.environ[\"OPENAI_API_KEY\"],\n", |
| 305 | + " )\n", |
| 306 | + "\n", |
| 307 | + "# Uses gpt-4o-mini:\n", |
| 308 | + "# - more intelligent\n", |
| 309 | + "llm_mini = ChatOpenAI(\n", |
| 310 | + " model=\"gpt-4o-mini\",\n", |
299 | 311 | " api_key=os.environ[\"OPENAI_API_KEY\"],\n", |
300 | 312 | " )" |
301 | 313 | ] |
|
338 | 350 | " Return the markdown now.\n", |
339 | 351 | " \"\"\"\n", |
340 | 352 | "\n", |
341 | | - " response = llm.invoke(prompt)\n", |
| 353 | + " response = llm_nano.invoke(prompt)\n", |
342 | 354 | "\n", |
343 | 355 | " return response.content.strip()" |
344 | 356 | ] |
|
387 | 399 | " 5. Output only a valid, plain, raw JSON string matching the schema above, ready to parse immediately\n", |
388 | 400 | " - NO markdown code blocks, NO extra text, NO explanations.\n", |
389 | 401 | " - Use plain newlines (not escaped as `\\n`).\n", |
| 402 | + " - In JSON strings, backslashes must be escaped. Use \\\\\\\\ for LaTeX backslashes.\n", |
390 | 403 | " - Always have each field in the JSON, even if it is empty.\n", |
391 | 404 | " - Becareful that the last element of a list is not followed by a comma.\n", |
392 | | - " 6. The Text inside the JSON should be in Texdown:\n", |
| 405 | + " 6. The Text inside the JSON should be in Lexdown:\n", |
393 | 406 | " 1. preserving all LaTeX math delimiters (`$...$` and `$$...$$`) and all formatting exactly as in the input, without paraphrasing, summarizing, or simplifying any mathematical expressions or formulas.\n", |
394 | | - " 2. do not remove or collapse blank lines.\n", |
395 | | - " 3. Do not escape characters like `\\n` or `\\\\`.\n", |
| 407 | + " 2. Do not remove or collapse blank lines.\n", |
| 408 | + " 3. Do not escape characters like `\\n` or `\\\\` except for JSON requirements.\n", |
396 | 409 | " \"\"\"\n", |
397 | 410 | "\n", |
398 | 411 | "def extract_questions(doc_page_content: str) -> dict:\n", |
|
416 | 429 | " for attempt_idx in range(3):\n", |
417 | 430 | " \n", |
418 | 431 | " # Call the LLM\n", |
419 | | - " response = llm.invoke(prompt)\n", |
| 432 | + " response = llm_mini.invoke(prompt)\n", |
420 | 433 | "\n", |
421 | 434 | " # Debug: print the raw LLM response\n", |
422 | 435 | " # print(\"Raw LLM Response:\")\n", |
|
431 | 444 | " return parsed_output.model_dump()\n", |
432 | 445 | " except Exception as e:\n", |
433 | 446 | " print(\"Error parsing LLM response as JSON:\")\n", |
434 | | - " print(\"Outputted response:\\n\", response.content)\n", |
435 | 447 | " print(\"Retrying... Attempt No.\", attempt_idx + 1)\n", |
436 | | - " raise e\n", |
437 | 448 | " time.sleep(2)\n", |
438 | 449 | "\n", |
439 | 450 | " print(\"Final raw LLM Response:\")\n", |
|
509 | 520 | " - The parts may be obvious to find, like \"a)...\", \"b)...\", or, \"i)...\", \"ii)...\", etc, or they could be implied by the question itself. All question must have at least one part, if there is only one part.\n", |
510 | 521 | " 1. The stem should be placed into the \"content\" field. Text in this field should be valid in the Milkdown editor. \n", |
511 | 522 | " 2. the parts of the question (subquestions) should be placed into the \"parts\" field. Text in this field should be valid under Lexdown.\n", |
512 | | - " 5. Output only a valid, plain, raw JSON string matching the schema above, ready to parse immediately, NO markdown code blocks, NO extra text, NO explanations. Use plain newlines (not escaped as `\\n`).\n", |
| 523 | + " 5. Output only a valid, plain, raw JSON string matching the schema above, ready to parse immediately\n", |
| 524 | + " - NO markdown code blocks, NO extra text, NO explanations.\n", |
| 525 | + " - Use plain newlines (not escaped as `\\n`).\n", |
| 526 | + " - In JSON strings, backslashes must be escaped. Use \\\\\\\\ for LaTeX backslashes.\n", |
| 527 | + " - Always have each field in the JSON, even if it is empty.\n", |
| 528 | + " - Becareful that the last element of a list is not followed by a comma.\n", |
513 | 529 | " 6. The Text inside the JSON should be in Lexdown:\n", |
514 | 530 | " 1. preserving all LaTeX math delimiters (`$...$` and `$$...$$`) and all formatting exactly as in the input, without paraphrasing, summarizing, or simplifying any mathematical expressions or formulas.\n", |
515 | 531 | " 2. Do not remove or collapse blank lines.\n", |
516 | | - " 3. Do not escape characters like `\\n` or `\\\\`.\n", |
| 532 | + " 3. Do not escape characters like `\\n` or `\\\\` except for JSON requirements.\n", |
517 | 533 | " \"\"\"\n", |
518 | 534 | "\n", |
519 | 535 | "llm_task_seperate_parts_solution = \"\"\"\n", |
|
523 | 539 | " 2. Use the same list of images as in the input for each question.\n", |
524 | 540 | " 3. For each parts of the question (subquestions):\n", |
525 | 541 | " - Carefully try to find the solution for each part, and place it into the \"part_solution\" field. Otherwise, leave as empty string. Text in this field should be valid under Lexdown.\n", |
526 | | - " 5. Output only a valid, plain, raw JSON string matching the schema above, ready to parse immediately, NO markdown code blocks, NO extra text, NO explanations. Use plain newlines (not escaped as `\\n`).\n", |
527 | | - " 6. The Text inside the JSON should be in Lexdown:\n", |
| 542 | + " - Make sure that the solution is only for the particular part.\n", |
| 543 | + " 4. Output only a valid, plain, raw JSON string matching the schema above, ready to parse immediately\n", |
| 544 | + " - NO markdown code blocks, NO extra text, NO explanations.\n", |
| 545 | + " - Use plain newlines (not escaped as `\\n`).\n", |
| 546 | + " - In JSON strings, backslashes must be escaped. Use \\\\\\\\ for LaTeX backslashes.\n", |
| 547 | + " - Always have each field in the JSON, even if it is empty.\n", |
| 548 | + " - Becareful that the last element of a list is not followed by a comma.\n", |
| 549 | + " 5. The Text inside the JSON should be in Lexdown:\n", |
528 | 550 | " 1. preserving all LaTeX math delimiters (`$...$` and `$$...$$`) and all formatting exactly as in the input, without paraphrasing, summarizing, or simplifying any mathematical expressions or formulas.\n", |
529 | 551 | " 2. Do not remove or collapse blank lines.\n", |
530 | | - " 3. Do not escape characters like `\\n` or `\\\\`.\n", |
| 552 | + " 3. Do not escape characters like `\\n` or `\\\\` except for JSON requirements.\n", |
531 | 553 | " \"\"\"\n", |
532 | 554 | "\n", |
533 | 555 | "def extract_parts_question(questions_dict: dict) -> dict:\n", |
|
584 | 606 | " \"\"\"\n", |
585 | 607 | " \n", |
586 | 608 | " # Call the LLM\n", |
587 | | - " response = llm.invoke(prompt)\n", |
| 609 | + " response = llm_mini.invoke(prompt)\n", |
588 | 610 | "\n", |
589 | 611 | " # Debug: print the raw LLM response\n", |
590 | 612 | " # print(\"Raw LLM Response:\")\n", |
|
594 | 616 | " # Parse the response using the output parser.\n", |
595 | 617 | " parsed_output_parts = question_parser.parse(response.content)\n", |
596 | 618 | " print(f\"LLM response successfully parsed question {question_idx + 1}.\")\n", |
| 619 | + " print(parsed_output_parts.content)\n", |
597 | 620 | " # For Pydantic v2, use model_dump() to convert the model to a dictionary.\n", |
598 | 621 | " question_parse_success = True\n", |
599 | 622 | " break\n", |
|
630 | 653 | " \"\"\"\n", |
631 | 654 | " \n", |
632 | 655 | " # Call the LLM\n", |
633 | | - " response = llm.invoke(prompt)\n", |
| 656 | + " response = llm_mini.invoke(prompt)\n", |
634 | 657 | "\n", |
635 | 658 | " try:\n", |
636 | 659 | " # Parse the response using the output parser.\n", |
637 | 660 | " parsed_output_solution_part = solution_parser.parse(response.content)\n", |
638 | 661 | " print(f\"LLM response successfully parsed solution for part {part_idx + 1} of question {question_idx + 1}.\")\n", |
| 662 | + " print(response.content)\n", |
639 | 663 | " # For Pydantic v2, use model_dump() to convert the model to a dictionary.\n", |
640 | 664 | " solution_parse_success = True\n", |
641 | 665 | " break\n", |
|
688 | 712 | "\n", |
689 | 713 | "llm_task_expression_check = r\"\"\"\n", |
690 | 714 | " Look inside the structure, specifically the `part_text` and `part_solution` fields. Ensure that the JSON content follows these rules:\n", |
691 | | - " 1. No extra escaping: The JSON string must contain no literal `\\\\n`, `\\\\\\\\`, or unnecessary escape sequences unless they are explicitly present in the original input text.\n", |
692 | | - " 2. Careful to make the distinction between inline and display math, i.e. do not mess up the use of `$` and `$$`.\n", |
693 | | - " 3. Math delimiters: All LaTeX math commands and math macros must be fully enclosed within math delimiters — use `$...$` for inline math, and `$$...$$` for display math.\n", |
694 | | - " 4. Balanced delimiters:\n", |
| 715 | + " 1. JSON escaping: In JSON strings, backslashes must be escaped. Use \\\\\\\\ for LaTeX backslashes (e.g., \"$A \\\\\\\\cup B$\" not \"$A \\\\cup B$\").\n", |
| 716 | + " 2. Math delimiters: All LaTeX math commands and math macros must be fully enclosed within math delimiters — use `$...$` for inline math, and `$$...$$` for display math.\n", |
| 717 | + " 3. Balanced delimiters:\n", |
695 | 718 | " - All `$$` and `$` must be properly opened and closed.\n", |
696 | 719 | " - No unbalanced or partial math blocks.\n", |
697 | 720 | " 4. Display math formatting:\n", |
|
702 | 725 | " - `$...$` should not span multiple lines.\n", |
703 | 726 | " - Avoid using `$$` for short inline expressions.\n", |
704 | 727 | " 6. Preserve LaTeX syntax:\n", |
705 | | - " - All LaTeX commands, backslashes (`\\`), braces (`{}`, `[]`), and special characters must be preserved exactly as in the original input.\n", |
706 | | - " - Do not add or remove escaping.\n", |
| 728 | + " - All LaTeX commands, braces (`{}`, `[]`), and special characters must be preserved exactly as in the original input.\n", |
| 729 | + " - Remember: in JSON, use \\\\\\\\ for each LaTeX backslash.\n", |
707 | 730 | " 7. Blank lines:\n", |
708 | 731 | " - Preserve all blank lines inside math blocks.\n", |
709 | 732 | " - Outside math, follow the structure of the original input.\n", |
710 | 733 | " 8. Alt text and image URLs:\n", |
711 | 734 | " - Ensure that all image URLs and alt text are preserved as they appear in the original input.\n", |
712 | 735 | " - The alt text must be `pictureTag`.\n", |
713 | | - " 8. Output format:\n", |
| 736 | + " 9. Output format:\n", |
714 | 737 | " - Output a single valid JSON string.\n", |
715 | 738 | " - Do not include any extra characters, explanations, or escaped formatting outside the JSON structure.\n", |
| 739 | + " - No literal \\\\n sequences - use actual newlines in JSON strings.\n", |
716 | 740 | " \"\"\"\n", |
717 | 741 | "\n", |
718 | 742 | "\n", |
|
762 | 786 | " \"\"\"\n", |
763 | 787 | "\n", |
764 | 788 | " # Call the LLM\n", |
765 | | - " response = llm.invoke(validation_prompt)\n", |
| 789 | + " response = llm_nano.invoke(validation_prompt)\n", |
766 | 790 | "\n", |
767 | 791 | " try:\n", |
768 | 792 | " # Parse the response using the output parser.\n", |
|
826 | 850 | " dict: A dictionary containing the keys \"name\" and \"exercise\".\n", |
827 | 851 | " If parsing fails, returns None.\n", |
828 | 852 | " \"\"\"\n", |
829 | | - " # corrected_md_content = correct_mistakes_in_markdown(md_content)\n", |
830 | | - " # print(\"Markdown content corrected for spelling, grammar, and structure.\")\n", |
| 853 | + " corrected_md_content = correct_mistakes_in_markdown(md_content)\n", |
| 854 | + " print(\"Markdown content corrected for spelling, grammar, and structure.\")\n", |
831 | 855 | "\n", |
832 | | - " questions_dict = extract_questions(md_content)\n", |
| 856 | + " questions_dict = extract_questions(corrected_md_content)\n", |
833 | 857 | " print(\"successfully extracted the questions from the markdown. Now extracting the parts...\")\n", |
834 | 858 | "\n", |
835 | 859 | " extracted_dict = extract_parts_question(questions_dict)\n", |
|
0 commit comments