{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Image Generation Prompt iteration\n",
    "\n",
    "This is based off of a tweet from [@ThorondorLLC](https://x.com/ThorondorLLC)\n",
    "\n",
    "Tweet is [here](https://x.com/ThorondorLLC/status/1880048546382221313)\n",
    "\n",
    "This will take an initial desired prompt, and iteratively refine it until the image generated matches the desired prompt.\n",
    "\n",
    "This is not DSPy prompt optimization as it is normally used, but it is a good example of how to use multimodal DSPy.\n",
    "\n",
    "A future upgrade would be to create a dataset of initial, final prompts to optimize the prompt generation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can install DSPy via:\n",
    "```bash\n",
    "pip install -U dspy\n",
    "```\n",
    "\n",
    "For this example, we'll use Flux Pro from FAL. You can get an API key [here](https://fal.com/flux-pro)\n",
    "\n",
    "We will also need to install Pillow and dotenv.\n",
    "```bash\n",
    "pip install fal-client pillow dotenv\n",
    "```\n",
    "\n",
    "\n",
    "Now, let's import the necessary libraries and set up the environment:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Optional\n",
    "#os.environ[\"FAL_API_KEY\"] = \"your_fal_api_key\"\n",
    "#os.environ[\"OPENAI_API_KEY\"] = \"your_openai_api_key\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/isaac/sd_optimizer/.venv/lib/python3.12/site-packages/pydantic/_internal/_config.py:345: UserWarning: Valid config keys have changed in V2:\n",
      "* 'fields' has been removed\n",
      "  warnings.warn(message, UserWarning)\n",
      "/Users/isaac/sd_optimizer/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "import dspy\n",
    "\n",
    "from PIL import Image\n",
    "from io import BytesIO\n",
    "import requests\n",
    "import fal_client\n",
    "\n",
    "from dotenv import load_dotenv\n",
    "load_dotenv()\n",
    "\n",
    "# import display\n",
    "from IPython.display import display\n",
    "\n",
    "lm = dspy.LM(model=\"gpt-4o-mini\", temperature=0.5)\n",
    "dspy.configure(lm=lm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_image(prompt):\n",
    "\n",
    "    request_id = fal_client.submit(\n",
    "        \"fal-ai/flux-pro/v1.1-ultra\",\n",
    "        arguments={\n",
    "            \"prompt\": prompt\n",
    "        },\n",
    "    ).request_id\n",
    "\n",
    "    result = fal_client.result(\"fal-ai/flux-pro/v1.1-ultra\", request_id)\n",
    "    url = result[\"images\"][0][\"url\"]\n",
    "\n",
    "    return dspy.Image(url)\n",
    "\n",
    "def display_image(image):\n",
    "    url = image.url\n",
    "    # download the image\n",
    "    response = requests.get(url)\n",
    "    image = Image.open(BytesIO(response.content))\n",
    "\n",
    "    # display at 25% of original size\n",
    "    display(image.resize((image.width // 4, image.height // 4)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration 1 of 5\n"
     ]
    },
    {
     "data": {
      "image/jpeg": "[encoded data, 99184 characters]",
      "image/png": "[encoded data, 1010276 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=688x384>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Feedback: The image depicts a peaceful autumn scene with people walking among colorful leaves, which aligns with the peaceful aspect of the prompt. However, it lacks any elements that convey tension, making it not fully representative of the desired prompt.\n",
      "Iteration 2 of 5\n"
     ]
    },
    {
     "data": {
      "image/jpeg": "[encoded data, 64464 characters]",
      "image/png": "[encoded data, 910692 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=688x384>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Feedback: The image depicts a serene autumn scene with vibrant foliage and a calm river, which aligns well with the idea of peace. However, it lacks explicit elements that suggest underlying tension, making it less effective in conveying both aspects of the desired prompt.\n",
      "Iteration 3 of 5\n"
     ]
    },
    {
     "data": {
      "image/jpeg": "[encoded data, 83612 characters]",
      "image/png": "[encoded data, 999032 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=688x384>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Feedback: The image depicts a serene autumn scene with warm colors and soft lighting, which aligns with the peaceful aspect of the desired prompt. However, it lacks elements that evoke tension or unease, making it not fully meet the requirement for a scene that is both peaceful and tense.\n",
      "Iteration 4 of 5\n"
     ]
    },
    {
     "data": {
      "image/jpeg": "[encoded data, 69256 characters]",
      "image/png": "[encoded data, 913636 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=688x384>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final prompt: A serene autumn scene with fog and shadows, capturing both peace and tension.\n"
     ]
    }
   ],
   "source": [
    "check_and_revise_prompt = dspy.Predict(\"desired_prompt: str, current_image: dspy.Image, current_prompt:str -> feedback:str, image_strictly_matches_desired_prompt: bool, revised_prompt: str\")\n",
    "\n",
    "initial_prompt = \"A scene that's both peaceful and tense\"\n",
    "current_prompt = initial_prompt\n",
    "\n",
    "max_iter = 5\n",
    "for i in range(max_iter):\n",
    "    print(f\"Iteration {i+1} of {max_iter}\")\n",
    "    current_image = generate_image(current_prompt)\n",
    "    result = check_and_revise_prompt(desired_prompt=initial_prompt, current_image=current_image, current_prompt=current_prompt)\n",
    "    display_image(current_image)\n",
    "    if result.image_strictly_matches_desired_prompt:\n",
    "        break\n",
    "    else:\n",
    "        current_prompt = result.revised_prompt\n",
    "        print(f\"Feedback: {result.feedback}\")\n",
    "        print(f\"Revised prompt: {result.revised_prompt}\")\n",
    "\n",
    "print(f\"Final prompt: {current_prompt}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\n",
      "\n",
      "\u001b[34m[2025-01-17T11:36:55.947579]\u001b[0m\n",
      "\n",
      "\u001b[31mSystem message:\u001b[0m\n",
      "\n",
      "Your input fields are:\n",
      "1. `desired_prompt` (str)\n",
      "2. `current_image` (Image)\n",
      "3. `current_prompt` (str)\n",
      "\n",
      "Your output fields are:\n",
      "1. `feedback` (str)\n",
      "2. `revised_prompt` (str)\n",
      "3. `image_strictly_matches_desired_prompt` (bool)\n",
      "\n",
      "All interactions will be structured in the following way, with the appropriate values filled in.\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "{desired_prompt}\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "{current_image}\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "{current_prompt}\n",
      "\n",
      "[[ ## feedback ## ]]\n",
      "{feedback}\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "{revised_prompt}\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "{image_strictly_matches_desired_prompt}        # note: the value you produce must be True or False\n",
      "\n",
      "[[ ## completed ## ]]\n",
      "\n",
      "In adhering to this structure, your objective is: \n",
      "        Given the fields `desired_prompt`, `current_image`, `current_prompt`, produce the fields `feedback`, `revised_prompt`, `image_strictly_matches_desired_prompt`.\n",
      "\n",
      "\n",
      "\u001b[31mUser message:\u001b[0m\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "A scene that's both peaceful and tense\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "\u001b[34m<image_url: https://fal.media/files/panda/HLKaiKFc_sN_fwBBN1C_2_1d1a72732e294452afb056a26d6b6c96.jpg>\u001b[0m\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "A scene that's both peaceful and tense\n",
      "\n",
      "Respond with the corresponding output fields, starting with the field `[[ ## feedback ## ]]`, then `[[ ## revised_prompt ## ]]`, then `[[ ## image_strictly_matches_desired_prompt ## ]]` (must be formatted as a valid Python bool), and then ending with the marker for `[[ ## completed ## ]]`.\n",
      "\n",
      "\n",
      "\u001b[31mResponse:\u001b[0m\n",
      "\n",
      "\u001b[32m[[ ## feedback ## ]]\n",
      "The image depicts a calm café scene with two individuals engaged in conversation, which conveys a sense of peace. However, the woman's posture and expression suggest an underlying tension, as she appears to be contemplative or distressed. This duality aligns well with the desired prompt of being both peaceful and tense.\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "A scene that captures the duality of peace and tension in a café setting.\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "True\n",
      "\n",
      "[[ ## completed ## ]]\u001b[0m\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\u001b[34m[2025-01-17T11:37:55.458433]\u001b[0m\n",
      "\n",
      "\u001b[31mSystem message:\u001b[0m\n",
      "\n",
      "Your input fields are:\n",
      "1. `desired_prompt` (str)\n",
      "2. `current_image` (Image)\n",
      "3. `current_prompt` (str)\n",
      "\n",
      "Your output fields are:\n",
      "1. `feedback` (str)\n",
      "2. `image_strictly_matches_desired_prompt` (bool)\n",
      "3. `revised_prompt` (str)\n",
      "\n",
      "All interactions will be structured in the following way, with the appropriate values filled in.\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "{desired_prompt}\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "{current_image}\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "{current_prompt}\n",
      "\n",
      "[[ ## feedback ## ]]\n",
      "{feedback}\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "{image_strictly_matches_desired_prompt}        # note: the value you produce must be True or False\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "{revised_prompt}\n",
      "\n",
      "[[ ## completed ## ]]\n",
      "\n",
      "In adhering to this structure, your objective is: \n",
      "        Given the fields `desired_prompt`, `current_image`, `current_prompt`, produce the fields `feedback`, `image_strictly_matches_desired_prompt`, `revised_prompt`.\n",
      "\n",
      "\n",
      "\u001b[31mUser message:\u001b[0m\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "A scene that's both peaceful and tense\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "\u001b[34m<image_url: https://fal.media/files/elephant/p3M00ca786Ov5ITX8nXUs_7b815b2087e54c1fbd798b9b9e0c5f5e.jpg>\u001b[0m\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "A scene that's both peaceful and tense\n",
      "\n",
      "Respond with the corresponding output fields, starting with the field `[[ ## feedback ## ]]`, then `[[ ## image_strictly_matches_desired_prompt ## ]]` (must be formatted as a valid Python bool), then `[[ ## revised_prompt ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.\n",
      "\n",
      "\n",
      "\u001b[31mResponse:\u001b[0m\n",
      "\n",
      "\u001b[32m[[ ## feedback ## ]]\n",
      "The image depicts a peaceful autumn scene with people walking among colorful leaves, which aligns with the peaceful aspect of the prompt. However, it lacks any elements that convey tension, making it not fully representative of the desired prompt.\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "False\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "A serene autumn scene with elements that suggest underlying tension\n",
      "\n",
      "[[ ## completed ## ]]\u001b[0m\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\u001b[34m[2025-01-17T11:38:24.032318]\u001b[0m\n",
      "\n",
      "\u001b[31mSystem message:\u001b[0m\n",
      "\n",
      "Your input fields are:\n",
      "1. `desired_prompt` (str)\n",
      "2. `current_image` (Image)\n",
      "3. `current_prompt` (str)\n",
      "\n",
      "Your output fields are:\n",
      "1. `feedback` (str)\n",
      "2. `image_strictly_matches_desired_prompt` (bool)\n",
      "3. `revised_prompt` (str)\n",
      "\n",
      "All interactions will be structured in the following way, with the appropriate values filled in.\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "{desired_prompt}\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "{current_image}\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "{current_prompt}\n",
      "\n",
      "[[ ## feedback ## ]]\n",
      "{feedback}\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "{image_strictly_matches_desired_prompt}        # note: the value you produce must be True or False\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "{revised_prompt}\n",
      "\n",
      "[[ ## completed ## ]]\n",
      "\n",
      "In adhering to this structure, your objective is: \n",
      "        Given the fields `desired_prompt`, `current_image`, `current_prompt`, produce the fields `feedback`, `image_strictly_matches_desired_prompt`, `revised_prompt`.\n",
      "\n",
      "\n",
      "\u001b[31mUser message:\u001b[0m\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "A scene that's both peaceful and tense\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "\u001b[34m<image_url: https://fal.media/files/monkey/QhPlYYa5tob38190Gtn0t_17e963cacc1542d5977031aff954c718.jpg>\u001b[0m\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "A serene autumn scene with elements that suggest underlying tension\n",
      "\n",
      "Respond with the corresponding output fields, starting with the field `[[ ## feedback ## ]]`, then `[[ ## image_strictly_matches_desired_prompt ## ]]` (must be formatted as a valid Python bool), then `[[ ## revised_prompt ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.\n",
      "\n",
      "\n",
      "\u001b[31mResponse:\u001b[0m\n",
      "\n",
      "\u001b[32m[[ ## feedback ## ]]\n",
      "The image depicts a serene autumn scene with vibrant foliage and a calm river, which aligns well with the idea of peace. However, it lacks explicit elements that suggest underlying tension, making it less effective in conveying both aspects of the desired prompt.\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "False\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "A serene autumn scene with elements that evoke a sense of unease or foreboding\n",
      "\n",
      "[[ ## completed ## ]]\u001b[0m\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\u001b[34m[2025-01-17T11:38:46.424883]\u001b[0m\n",
      "\n",
      "\u001b[31mSystem message:\u001b[0m\n",
      "\n",
      "Your input fields are:\n",
      "1. `desired_prompt` (str)\n",
      "2. `current_image` (Image)\n",
      "3. `current_prompt` (str)\n",
      "\n",
      "Your output fields are:\n",
      "1. `feedback` (str)\n",
      "2. `image_strictly_matches_desired_prompt` (bool)\n",
      "3. `revised_prompt` (str)\n",
      "\n",
      "All interactions will be structured in the following way, with the appropriate values filled in.\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "{desired_prompt}\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "{current_image}\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "{current_prompt}\n",
      "\n",
      "[[ ## feedback ## ]]\n",
      "{feedback}\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "{image_strictly_matches_desired_prompt}        # note: the value you produce must be True or False\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "{revised_prompt}\n",
      "\n",
      "[[ ## completed ## ]]\n",
      "\n",
      "In adhering to this structure, your objective is: \n",
      "        Given the fields `desired_prompt`, `current_image`, `current_prompt`, produce the fields `feedback`, `image_strictly_matches_desired_prompt`, `revised_prompt`.\n",
      "\n",
      "\n",
      "\u001b[31mUser message:\u001b[0m\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "A scene that's both peaceful and tense\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "\u001b[34m<image_url: https://fal.media/files/panda/kNBH7FF1uLk06nXonN0Zr_256e1bf2f71c4dcc8eb94027f09f3452.jpg>\u001b[0m\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "A serene autumn scene with elements that evoke a sense of unease or foreboding\n",
      "\n",
      "Respond with the corresponding output fields, starting with the field `[[ ## feedback ## ]]`, then `[[ ## image_strictly_matches_desired_prompt ## ]]` (must be formatted as a valid Python bool), then `[[ ## revised_prompt ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.\n",
      "\n",
      "\n",
      "\u001b[31mResponse:\u001b[0m\n",
      "\n",
      "\u001b[32m[[ ## feedback ## ]]\n",
      "The image depicts a serene autumn scene with warm colors and soft lighting, which aligns with the peaceful aspect of the desired prompt. However, it lacks elements that evoke tension or unease, making it not fully meet the requirement for a scene that is both peaceful and tense.\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "False\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "A serene autumn scene that includes subtle elements of tension or foreboding, such as dark shadows or an unsettling atmosphere.\n",
      "\n",
      "[[ ## completed ## ]]\u001b[0m\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\u001b[34m[2025-01-17T11:39:03.586839]\u001b[0m\n",
      "\n",
      "\u001b[31mSystem message:\u001b[0m\n",
      "\n",
      "Your input fields are:\n",
      "1. `desired_prompt` (str)\n",
      "2. `current_image` (Image)\n",
      "3. `current_prompt` (str)\n",
      "\n",
      "Your output fields are:\n",
      "1. `feedback` (str)\n",
      "2. `image_strictly_matches_desired_prompt` (bool)\n",
      "3. `revised_prompt` (str)\n",
      "\n",
      "All interactions will be structured in the following way, with the appropriate values filled in.\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "{desired_prompt}\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "{current_image}\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "{current_prompt}\n",
      "\n",
      "[[ ## feedback ## ]]\n",
      "{feedback}\n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "{image_strictly_matches_desired_prompt}        # note: the value you produce must be True or False\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "{revised_prompt}\n",
      "\n",
      "[[ ## completed ## ]]\n",
      "\n",
      "In adhering to this structure, your objective is: \n",
      "        Given the fields `desired_prompt`, `current_image`, `current_prompt`, produce the fields `feedback`, `image_strictly_matches_desired_prompt`, `revised_prompt`.\n",
      "\n",
      "\n",
      "\u001b[31mUser message:\u001b[0m\n",
      "\n",
      "[[ ## desired_prompt ## ]]\n",
      "A scene that's both peaceful and tense\n",
      "\n",
      "[[ ## current_image ## ]]\n",
      "\u001b[34m<image_url: https://fal.media/files/penguin/AXRlgHfIxbZ26Ipgd64Rn_d012474791904e70bd419945f7179980.jpg>\u001b[0m\n",
      "\n",
      "[[ ## current_prompt ## ]]\n",
      "A serene autumn scene that includes subtle elements of tension or foreboding, such as dark shadows or an unsettling atmosphere.\n",
      "\n",
      "Respond with the corresponding output fields, starting with the field `[[ ## feedback ## ]]`, then `[[ ## image_strictly_matches_desired_prompt ## ]]` (must be formatted as a valid Python bool), then `[[ ## revised_prompt ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.\n",
      "\n",
      "\n",
      "\u001b[31mResponse:\u001b[0m\n",
      "\n",
      "\u001b[32m[[ ## feedback ## ]]\n",
      "The image depicts a foggy forest scene with autumn leaves, creating a peaceful atmosphere. However, the overall mood is somewhat tense due to the fog and shadows, aligning well with the desired prompt of being both peaceful and tense. \n",
      "\n",
      "[[ ## image_strictly_matches_desired_prompt ## ]]\n",
      "True\n",
      "\n",
      "[[ ## revised_prompt ## ]]\n",
      "A serene autumn scene with fog and shadows, capturing both peace and tension.\n",
      "\n",
      "[[ ## completed ## ]]\u001b[0m\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "dspy.inspect_history(5)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
