Skip to content

Commit fa6dd2a

Browse files
committed
seperated questions to process with llm to increase accuracy per input
1 parent b820b4c commit fa6dd2a

File tree

1 file changed

+71
-66
lines changed

1 file changed

+71
-66
lines changed

conversion2025/mathpix_to_llm_to_in2lambda_to_JSON.ipynb

Lines changed: 71 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@
226226
" end_location = fig_name.index(\"?\")\n",
227227
" image_name = f\"{idx}_{fig_name[:end_location]}\"\n",
228228
" fig_info[\"local_path\"] = image_name\n",
229-
" fig_info[\"image\"].save(f\"{media_path}{fig_info['local_path']}\")\n",
229+
" fig_info[\"image\"].save(f\"{media_path}/{fig_info['local_path']}\")\n",
230230
"\n",
231231
"save_figures_to_path(figures)"
232232
]
@@ -248,6 +248,8 @@
248248
"source": [
249249
"def replace_figures_in_markdown(md_content, figures):\n",
250250
" #replace the image URLs in the markdown content with local paths\n",
251+
" # add pictureTag for Lmabda Feedback to recognise it as a picture\n",
252+
" md_content = md_content.replace(\"![]\", \"![pictureTag]\")\n",
251253
" for fig_name, fig_info in figures.items():\n",
252254
" md_content = md_content.replace(fig_info[\"url\"], fig_info[\"local_path\"])\n",
253255
" print(f\"Replaced {fig_info['url']} with {fig_info['local_path']} in markdown content.\")\n",
@@ -300,6 +302,7 @@
300302
" # full question and full solution\n",
301303
" question_content: str = Field(..., description=\"The content of the question.\")\n",
302304
" solution_content: str = Field(..., description=\"The content of the solution.\")\n",
305+
" images: list[str] = Field(..., description=\"A list of image URLs associated with the question.\")\n",
303306
"\n",
304307
"class AllQuestionsModel(BaseModel):\n",
305308
" name: str = Field(..., description=\"Title of the set\")\n",
@@ -313,7 +316,8 @@
313316
" 2. Identify the year of the tutorial, if mentioned. Otherwise, use \"0\".\n",
314317
" 3. Every character should match the original source exactly unless you're instructed to split content into fields, without adding escapes or modifications.\n",
315318
" 4. Look through the entire markdown:\n",
316-
" - Without ignoring any mentions of images, figures, or other media.\n",
319+
" - Do not neglect any images, figures, or other media mentioned in the question, do not alter or neglect the alt text and the image URL.\n",
320+
" - Leave the Image links and alt text within the question/solution, but also make a copy and place it into the `images` field.\n",
317321
" - Identify full Questions, place it into question_content\n",
318322
" - Identify the full Worked Solution for each full Question.\n",
319323
" - If the Worked Solution is not found, try to find the Answers associated with it instead.\n",
@@ -358,12 +362,6 @@
358362
" print(\"LLM response successfully parsed as JSON with questions.\")\n",
359363
" # For Pydantic v2, use model_dump() to convert the model to a dictionary.\n",
360364
" return parsed_output.model_dump()\n",
361-
" except ValidationError as ve:\n",
362-
" print(\"❌ Pydantic Validation Error:\")\n",
363-
" for error in ve.errors():\n",
364-
" print(f\" - {error['loc']}: {error['msg']}\")\n",
365-
" print(\"Raw LLM output:\")\n",
366-
" print(response.content)\n",
367365
" except Exception as e:\n",
368366
" print(\"Error parsing LLM response as JSON:\")\n",
369367
" print(\"Retrying...\")\n",
@@ -384,6 +382,7 @@
384382
" content: str = Field(..., description=\"Content of the question (no exercise title, no subquestions)\")\n",
385383
" parts: list[str] = Field(..., description=\"List of parts within the question (only the text, no numbering)\")\n",
386384
" parts_solutions: list[str] = Field(..., description=\"List of worked solutions for the question (no numbering or counting)\")\n",
385+
" images: list[str] = Field(..., description=\"List of image URLs associated with the question (no alt text, only URLs)\")\n",
387386
"\n",
388387
"class Set(BaseModel):\n",
389388
" name: str = Field(..., description=\"Title of the set\")\n",
@@ -398,15 +397,16 @@
398397
" Please follow these steps carefully:\n",
399398
" 1. Every character should match the original source exactly unless you're instructed to split content into fields, without adding escapes or modifications.\n",
400399
" 2. Use the same name and year.\n",
401-
" 3. For each question in questions:\n",
400+
" 3. Use the same list of images as in the input for each question.\n",
401+
" 4. For each question in questions:\n",
402402
" - Title is the only field where you are allowed to name it whatever you seem fit for the question.\n",
403-
" - Do not neglect any images, figures, or other media mentioned in the question.\n",
403+
" - Do not neglect any images, figures, or other media mentioned in the question, do not alter or neglect the alt text and the image URL.\n",
404404
" - Identify the stem and parts of the question, the parts may be obvious to find, like \"a)...\", \"b)...\", 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",
405405
" 1. The stem should be placed into the \"content\" field. Text in this field should be valid in the Milkdown editor. \n",
406406
" 2. the parts of the question (subquestions) should be placed into the \"parts\" field. Text in this field should be valid under Lexdown.\n",
407407
" 3. for each part, identify the worked solution/answer and place it into the \"parts_solutions\" field, if not found, leave as empty string, \"\". Text in this field should be valid under Lexdown.\n",
408-
" 4. Output only a valid, plain, raw JSON string matching the schema above, ready to parse immediately, with no code fence or extra text. Use plain newlines (not escaped as `\\n`).\n",
409-
" 5. The Text inside the JSON should be in Lexdown:\n",
408+
" 5. Output only a valid, plain, raw JSON string matching the schema above, ready to parse immediately, with no code fence or extra text. Use plain newlines (not escaped as `\\n`).\n",
409+
" 6. The Text inside the JSON should be in Lexdown:\n",
410410
" 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",
411411
" 2. Do not remove or collapse blank lines.\n",
412412
" 3. Do not escape characters like `\\n` or `\\\\`.\n",
@@ -441,49 +441,62 @@
441441
" If parsing fails, returns None.\n",
442442
" \"\"\"\n",
443443
" # Initialize the output parser with the Tutorial schema.\n",
444-
" parser = PydanticOutputParser(pydantic_object=Set)\n",
444+
" parser = PydanticOutputParser(pydantic_object=Set_Question)\n",
445445
"\n",
446-
" # Construct the prompt, appending the parser's format instructions.\n",
447-
" prompt = f\"\"\"\n",
448-
" Input markdown:\n",
449-
" ```markdown\n",
450-
" {questions_dict}\n",
451-
" ```\n",
452446
"\n",
453-
" Your task is to extract a JSON with the following structure exactly:\n",
454-
" {parser.get_format_instructions()}\n",
455-
"\n",
456-
" {llm_task_seperate_parts}\n",
457-
"\n",
458-
" Return the JSON now.\n",
459-
" \"\"\"\n",
460447
" \n",
461-
" # tries to call the LLM multiple times to ensure robustness.\n",
462-
" for i in range(3):\n",
463-
" \n",
464-
" # Call the LLM\n",
465-
" response = llm.invoke(prompt)\n",
466-
"\n",
467-
" # Debug: print the raw LLM response\n",
468-
" # print(\"Raw LLM Response:\")\n",
469-
" # print(response)\n",
448+
" questions_in_parts = []\n",
449+
" for question in questions_dict[\"questions\"]:\n",
450+
" passed = False\n",
451+
"\n",
452+
" for idx in range(3):\n",
453+
"\n",
454+
" # Construct the prompt, appending the parser's format instructions.\n",
455+
" prompt = f\"\"\"\n",
456+
" Your task is to extract a JSON with the following structure exactly:\n",
457+
" {parser.get_format_instructions()}\n",
458+
"\n",
459+
" {llm_task_seperate_parts}\n",
460+
"\n",
461+
" Input JSON:\n",
462+
" ```JSON\n",
463+
" {question}\n",
464+
" ```\n",
465+
"\n",
466+
" Return the JSON now.\n",
467+
" \"\"\"\n",
468+
" \n",
469+
" # Call the LLM\n",
470+
" response = llm.invoke(prompt)\n",
471+
"\n",
472+
" # Debug: print the raw LLM response\n",
473+
" # print(\"Raw LLM Response:\")\n",
474+
" # print(response)\n",
475+
"\n",
476+
" try:\n",
477+
" # Parse the response using the output parser.\n",
478+
" parsed_output = parser.parse(response.content)\n",
479+
" print(f\"LLM response successfully parsed question {idx}.\")\n",
480+
" # For Pydantic v2, use model_dump() to convert the model to a dictionary.\n",
481+
" passed = True\n",
482+
" break\n",
483+
" except Exception as e:\n",
484+
" print(\"Error parsing LLM response as JSON:\")\n",
485+
" print(\"Retrying...\")\n",
486+
" time.sleep(2)\n",
470487
"\n",
471-
" try:\n",
472-
" # Parse the response using the output parser.\n",
473-
" parsed_output = parser.parse(response.content)\n",
474-
" print(\"LLM response successfully parsed as JSON with parts.\")\n",
475-
" # For Pydantic v2, use model_dump() to convert the model to a dictionary.\n",
476-
" return parsed_output.model_dump()\n",
477-
" except ValidationError as ve:\n",
478-
" print(\"❌ Pydantic Validation Error:\")\n",
479-
" for error in ve.errors():\n",
480-
" print(f\" - {error['loc']}: {error['msg']}\")\n",
481-
" print(\"Raw LLM output:\")\n",
482-
" print(response.content)\n",
483-
" except Exception as e:\n",
484-
" print(\"Error parsing LLM response as JSON:\")\n",
485-
" print(\"Retrying...\")\n",
486-
" time.sleep(2)"
488+
" if not passed:\n",
489+
" raise Exception(\"Failed to parse LLM response as JSON after multiple attempts.\")\n",
490+
" \n",
491+
" questions_in_parts.append(parsed_output)\n",
492+
" \n",
493+
" return Set(\n",
494+
" name=questions_dict[\"name\"],\n",
495+
" year=questions_dict[\"year\"],\n",
496+
" questions=questions_in_parts\n",
497+
" ).model_dump()\n",
498+
" \n",
499+
" \n"
487500
]
488501
},
489502
{
@@ -522,6 +535,9 @@
522535
" 7. Blank lines:\n",
523536
" - Preserve all blank lines inside math blocks.\n",
524537
" - Outside math, follow the structure of the original input.\n",
538+
" 8. Alt text and image URLs:\n",
539+
" - Ensure that all image URLs and alt text are preserved as they appear in the original input.\n",
540+
" - The alt text must be `pictureTag`.\n",
525541
" 8. Output format:\n",
526542
" - Output a single valid JSON string.\n",
527543
" - Do not include any extra characters, explanations, or escaped formatting outside the JSON structure.\n",
@@ -570,12 +586,6 @@
570586
" print(\"LLM response successfully parsed as JSON with valid $$.\")\n",
571587
" # For Pydantic v2, use model_dump() to convert the model to a dictionary.\n",
572588
" return parsed_output.model_dump()\n",
573-
" except ValidationError as ve:\n",
574-
" print(\"❌ Pydantic Validation Error:\")\n",
575-
" for error in ve.errors():\n",
576-
" print(f\" - {error['loc']}: {error['msg']}\")\n",
577-
" print(\"Raw LLM output:\")\n",
578-
" print(response.content)\n",
579589
" except Exception as e:\n",
580590
" print(\"Error parsing textdown LLM response as JSON:\")\n",
581591
" print(\"Retrying...\")\n",
@@ -665,6 +675,8 @@
665675
"\n",
666676
"in2lambda_questions = []\n",
667677
"\n",
678+
"\n",
679+
"\n",
668680
"# Loop over all questions and question_answers and use in2lambda to create a JSON.\n",
669681
"for idx, question_dict in enumerate(questions, start=1):\n",
670682
" parts = []\n",
@@ -678,20 +690,13 @@
678690
" question = Question(\n",
679691
" title=question_dict.get(\"title\", f\"Question {idx}\"),\n",
680692
" main_text=question_dict.get(\"content\", \"\"),\n",
681-
" parts=parts\n",
693+
" parts=parts,\n",
694+
" images=[ f\"{media_path}/{img}\" for img in question_dict.get(\"images\", []) ]\n",
682695
" )\n",
683696
" in2lambda_questions.append(question)\n",
684697
"\n",
685698
"Module(in2lambda_questions).to_json(f\"{output_path}/out\")"
686699
]
687-
},
688-
{
689-
"cell_type": "code",
690-
"execution_count": null,
691-
"id": "24",
692-
"metadata": {},
693-
"outputs": [],
694-
"source": []
695700
}
696701
],
697702
"metadata": {

0 commit comments

Comments
 (0)