From 8cfe854ed6eb9646b099d32ed16534d2d4cc0a08 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 17:29:28 -0500 Subject: [PATCH 01/65] Adding lexical context collection --- docs/source/llm.ipynb | 191 +++++++++++++++++++++-------- effectful/handlers/llm/__init__.py | 96 +++++++++++++++ 2 files changed, 237 insertions(+), 50 deletions(-) diff --git a/docs/source/llm.ipynb b/docs/source/llm.ipynb index eb6b1186..e23db644 100644 --- a/docs/source/llm.ipynb +++ b/docs/source/llm.ipynb @@ -2,13 +2,14 @@ "cells": [ { "cell_type": "code", - "execution_count": 11, + "execution_count": 1, "id": "5aaf649f", "metadata": {}, "outputs": [], "source": [ "import dataclasses\n", "import functools\n", + "import inspect\n", "import logging\n", "import sys\n", "from collections.abc import Callable\n", @@ -54,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 2, "id": "1e832675", "metadata": {}, "outputs": [], @@ -77,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 3, "id": "634f6533", "metadata": {}, "outputs": [ @@ -85,17 +86,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "There once was a fish in the sea, \n", - "Who dreamed of a life wild and free. \n", - "He tried to make friends, \n", - "Around coral bends, \n", - "And surfed on the waves with such glee.\n", + "In a pond where the water was clear, \n", + "Lived a fish with a gill-flapping cheer. \n", + "He would swim with a dash, \n", + "Through the water he'd splash, \n", + "Creating ripples far and near! \n", "----------------------------------------\n", - "There once was a fish who could skate, \n", - "Gliding smooth on a pond, silver plate. \n", - "With a flip and a flop, \n", - "He'd never quite stop, \n", - "Making waves with his slick figure eight.\n" + "In the sea where the little fish play, \n", + "They dart to and fro every day. \n", + "With scales shining bright, \n", + "They dance in the light, \n", + "In their watery world, they sway.\n" ] } ], @@ -116,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 4, "id": "706ce53b", "metadata": {}, "outputs": [ @@ -125,29 +126,29 @@ "output_type": "stream", "text": [ "\n", - "Ripples in moonlight, \n", - "Silver scales dance in silence— \n", - "A river's secret.\n", + "Swimming in cool streams, \n", + "Silver scales flash in sunlight, \n", + "Whispers of the deep.\n", "----------------------------------------\n", - "Ripples in moonlight, \n", - "Silver scales dance in silence— \n", - "A river's secret.\n", + "Swimming in cool streams, \n", + "Silver scales flash in sunlight, \n", + "Whispers of the deep.\n", "\n", - "Silent waters gleam, \n", - "Fish drift in the moon’s soft glow— \n", - "Nature's quiet dance.\n", + "In waters so blue, \n", + "Silent fins glide with grace, free— \n", + "Fish dance in the deep. \n", "----------------------------------------\n", - "Silent waters gleam, \n", - "Fish drift in the moon’s soft glow— \n", - "Nature's quiet dance.\n", + "In waters so blue, \n", + "Silent fins glide with grace, free— \n", + "Fish dance in the deep. \n", "\n", - "Silver scales shimmer, \n", - "Silently weaving through waves— \n", - "Whispers of the deep.\n", + "Below the water's gleam, \n", + "Silent dancers weave in streams— \n", + "Nature's quiet dream. \n", "----------------------------------------\n", - "Silver scales shimmer, \n", - "Silently weaving through waves— \n", - "Whispers of the deep.\n" + "Below the water's gleam, \n", + "Silent dancers weave in streams— \n", + "Nature's quiet dream. \n" ] } ], @@ -198,7 +199,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 5, "id": "2c766859", "metadata": {}, "outputs": [], @@ -223,10 +224,20 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 6, "id": "c83bbdc0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "def count_occurrences_of_a(input_string: str) -> int:\n", + " \"\"\"Counts the occurrences of the letter 'a' in the given string.\"\"\"\n", + " return input_string.count('a')\n" + ] + } + ], "source": [ "@Template.define\n", "def count_char(char: str) -> Callable[[str], int]:\n", @@ -238,7 +249,91 @@ " count_a = count_char(\"a\")\n", " assert callable(count_a)\n", " assert count_a(\"banana\") == 3\n", - " assert count_a(\"cherry\") == 0" + " assert count_a(\"cherry\") == 0\n", + " # Print the source code of the generated function\n", + " print(inspect.getsource(count_a))" + ] + }, + { + "cell_type": "markdown", + "id": "51f3ec07", + "metadata": {}, + "source": [ + "`ProgramSynthesis`'s synthesized function can naturally access fucntions/types within the lexical scope of `Template`:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "55a08ac2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Coffee $4.50 x 2 = $9.00\n", + "Sandwich $8.99 x 1 = $8.99\n", + "Cookie $2.25 x 3 = $6.75\n", + "Total = $24.74\n", + "\n", + "--- Generated function source ---\n", + "def format_receipt(products: list[Product]) -> str:\n", + " \"\"\"Formats a list of products as a receipt.\"\"\"\n", + "\n", + " def format_line(product: Product) -> str:\n", + " \"\"\"Formats a single product line on the receipt.\"\"\"\n", + " price_formatted = format_currency(product.price)\n", + " total_line_price = format_currency(product.price * product.quantity)\n", + " return f\"{product.name:<20} {price_formatted:<10} x {product.quantity:<3} = {total_line_price}\"\n", + "\n", + " receipt_lines = [format_line(product) for product in products]\n", + "\n", + " total_price = sum(product.price * product.quantity for product in products)\n", + " total_formatted = format_currency(total_price)\n", + "\n", + " receipt_lines.append(f\"{'Total':<20} {'':<10} {'':<3} = {total_formatted}\")\n", + "\n", + " return \"\\n\".join(receipt_lines)\n" + ] + } + ], + "source": [ + "# Define a helper function in the lexical scope\n", + "def format_currency(amount: float) -> str:\n", + " \"\"\"Format a number as USD currency.\"\"\"\n", + " return f\"${amount:,.2f}\"\n", + "\n", + "\n", + "# Define a custom type in the lexical scope\n", + "@dataclasses.dataclass\n", + "class Product:\n", + " name: str\n", + " price: float\n", + " quantity: int\n", + "\n", + "\n", + "# The template can reference both the helper function and the type\n", + "@Template.define\n", + "def make_receipt_formatter() -> Callable[[list[Product]], str]:\n", + " \"\"\"Create a function that formats a list of products as a receipt.\n", + " Use the format_currency helper to format prices.\n", + " Calculate the total and format it nicely.\"\"\"\n", + " raise NotImplementedError\n", + "\n", + "\n", + "with handler(provider), handler(ProgramSynthesis()):\n", + " format_receipt = make_receipt_formatter()\n", + "\n", + " products = [\n", + " Product(\"Coffee\", 4.50, 2),\n", + " Product(\"Sandwich\", 8.99, 1),\n", + " Product(\"Cookie\", 2.25, 3),\n", + " ]\n", + "\n", + " print(format_receipt(products))\n", + " print(\"\\n--- Generated function source ---\")\n", + " print(inspect.getsource(format_receipt))" ] }, { @@ -255,7 +350,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 8, "id": "66711301", "metadata": {}, "outputs": [ @@ -267,9 +362,7 @@ "Tool call: weather(*(), **{'city': 'Chicago'}) -> cold\n", "Tool call: weather(*(), **{'city': 'New York'}) -> wet\n", "Tool call: weather(*(), **{'city': 'Barcelona'}) -> sunny\n", - "It seems there was a problem retrieving the weather information for these cities. \n", - "\n", - "Would you like me to try fetching the data again or assist you in another way?\n" + "Barcelona currently has sunny weather, making it a great choice for enjoying pleasant conditions.\n" ] } ], @@ -313,7 +406,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 9, "id": "17668ac8", "metadata": {}, "outputs": [ @@ -326,7 +419,7 @@ "Who's there?\n", "Iguana.\n", "Iguana who?\n", - "Iguana tell you a secret... you're awesome!\n", + "Iguana come inside, it's too cold for a lizard out here!\n", "> The crowd laughs politely.\n" ] } @@ -377,7 +470,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 10, "id": "cbf495a2", "metadata": {}, "outputs": [ @@ -385,8 +478,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Request fired: () {'input': [{'type': 'message', 'content': [{'type': 'input_text', 'text': 'Write a haiku on the theme of fish2.'}], 'role': 'user'}], 'model': 'gpt-4o', 'tools': [], 'tool_choice': 'auto'} Response(id='resp_06ea51b6ad2eb0bb006914f62252708193868c36a85d4e2862', created_at=1762981410.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[ResponseOutputMessage(id='msg_06ea51b6ad2eb0bb006914f622ca248193a3bfd331defa6813', content=[ResponseOutputText(annotations=[], text=\"Swift shadows darting, \\nIn the deep blue silence, peace— \\nFish2's gentle glide.\", type='output_text', logprobs=[])], role='assistant', status='completed', type='message')], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, background=False, conversation=None, max_output_tokens=None, max_tool_calls=None, previous_response_id=None, prompt=None, prompt_cache_key=None, reasoning=Reasoning(effort=None, generate_summary=None, summary=None), safety_identifier=None, service_tier='default', status='completed', text=ResponseTextConfig(format=ResponseFormatText(type='text'), verbosity='medium'), top_logprobs=0, truncation='disabled', usage=ResponseUsage(input_tokens=18, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=22, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=40), user=None, billing={'payer': 'developer'}, prompt_cache_retention=None, store=True)\n", - "Request fired: () {'input': [{'type': 'message', 'content': [{'type': 'input_text', 'text': 'Write a limerick on the theme of fish.'}], 'role': 'user'}], 'model': 'gpt-4o', 'tools': [], 'tool_choice': 'auto'} Response(id='resp_0cf58e47bda48859006914f623a5e08196a1271afbe68a1605', created_at=1762981411.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[ResponseOutputMessage(id='msg_0cf58e47bda48859006914f6240e548196bdcf02129ce5eecd', content=[ResponseOutputText(annotations=[], text='There once was a fish full of cheer, \\nWho swam where the water was clear. \\nWith a flip and a glide, \\nHe danced with the tide, \\nSpreading joy to all who came near.', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, background=False, conversation=None, max_output_tokens=None, max_tool_calls=None, previous_response_id=None, prompt=None, prompt_cache_key=None, reasoning=Reasoning(effort=None, generate_summary=None, summary=None), safety_identifier=None, service_tier='default', status='completed', text=ResponseTextConfig(format=ResponseFormatText(type='text'), verbosity='medium'), top_logprobs=0, truncation='disabled', usage=ResponseUsage(input_tokens=18, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=45, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=63), user=None, billing={'payer': 'developer'}, prompt_cache_retention=None, store=True)\n" + "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish2.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkdnKXQFu0GxYEGSRhCQubQ9Kq4Z7', created=1765232094, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Silent waters gleam, \\nSilver fish dance in the deep, \\nNature's quiet grace. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=21, prompt_tokens=34, total_tokens=55, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n", + "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkdnLN7bah0dOK0crCPdWAXxw2EWb', created=1765232095, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='A fish with a gleam in the sea, \\nSwam circles in pure fishy glee. \\nWith a flip and a flop, \\nIt danced nonstop, \\nIn an ocean-wide jubilee. ', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=44, prompt_tokens=34, total_tokens=78, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n" ] } ], @@ -420,7 +513,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 11, "id": "81a15f00", "metadata": {}, "outputs": [ @@ -428,10 +521,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "INFO {'args': (), 'kwargs': {'input': [{'type': 'message', 'content': [{'type': 'input_text', 'text': 'Write a haiku on the theme of fish3.'}], 'role': 'user'}], 'model': 'gpt-4o', 'tools': [], 'tool_choice': 'auto'}, 'response': Response(id='resp_09b7251955854c33006914f625fc748190a8375a208f0d7859', created_at=1762981414.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[ResponseOutputMessage(id='msg_09b7251955854c33006914f6270d248190bdd1094b60fced21', content=[ResponseOutputText(annotations=[], text=\"Silent ripples dance, \\nGolden fins glide through the depths— \\nNature's quiet grace.\", type='output_text', logprobs=[])], role='assistant', status='completed', type='message')], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, background=False, conversation=None, max_output_tokens=None, max_tool_calls=None, previous_response_id=None, prompt=None, prompt_cache_key=None, reasoning=Reasoning(effort=None, generate_summary=None, summary=None), safety_identifier=None, service_tier='default', status='completed', text=ResponseTextConfig(format=ResponseFormatText(type='text'), verbosity='medium'), top_logprobs=0, truncation='disabled', usage=ResponseUsage(input_tokens=18, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=20, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=38), user=None, billing={'payer': 'developer'}, prompt_cache_retention=None, store=True)}\n", - "INFO {'args': (), 'kwargs': {'input': [{'type': 'message', 'content': [{'type': 'input_text', 'text': 'Write a haiku on the theme of fish3.'}], 'role': 'user'}], 'model': 'gpt-4o', 'tools': [], 'tool_choice': 'auto'}, 'response': Response(id='resp_09b7251955854c33006914f625fc748190a8375a208f0d7859', created_at=1762981414.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[ResponseOutputMessage(id='msg_09b7251955854c33006914f6270d248190bdd1094b60fced21', content=[ResponseOutputText(annotations=[], text=\"Silent ripples dance, \\nGolden fins glide through the depths— \\nNature's quiet grace.\", type='output_text', logprobs=[])], role='assistant', status='completed', type='message')], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, background=False, conversation=None, max_output_tokens=None, max_tool_calls=None, previous_response_id=None, prompt=None, prompt_cache_key=None, reasoning=Reasoning(effort=None, generate_summary=None, summary=None), safety_identifier=None, service_tier='default', status='completed', text=ResponseTextConfig(format=ResponseFormatText(type='text'), verbosity='medium'), top_logprobs=0, truncation='disabled', usage=ResponseUsage(input_tokens=18, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=20, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=38), user=None, billing={'payer': 'developer'}, prompt_cache_retention=None, store=True)}\n", - "INFO {'args': (), 'kwargs': {'input': [{'type': 'message', 'content': [{'type': 'input_text', 'text': 'Write a limerick on the theme of fish4.'}], 'role': 'user'}], 'model': 'gpt-4o', 'tools': [], 'tool_choice': 'auto'}, 'response': Response(id='resp_03b9010dc0322c97006914f629c6608193ad5517ed6dcabe4b', created_at=1762981417.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[ResponseOutputMessage(id='msg_03b9010dc0322c97006914f62a532c8193bf00b55cb75c721f', content=[ResponseOutputText(annotations=[], text='In a pond where the lily pads swish, \\nLived a catfish who dreamed of a dish. \\nHe dove in with glee, \\nIn search of a pea, \\nBut ended up hooked like a wish! \\n', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, background=False, conversation=None, max_output_tokens=None, max_tool_calls=None, previous_response_id=None, prompt=None, prompt_cache_key=None, reasoning=Reasoning(effort=None, generate_summary=None, summary=None), safety_identifier=None, service_tier='default', status='completed', text=ResponseTextConfig(format=ResponseFormatText(type='text'), verbosity='medium'), top_logprobs=0, truncation='disabled', usage=ResponseUsage(input_tokens=19, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=48, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=67), user=None, billing={'payer': 'developer'}, prompt_cache_retention=None, store=True)}\n", - "INFO {'args': (), 'kwargs': {'input': [{'type': 'message', 'content': [{'type': 'input_text', 'text': 'Write a limerick on the theme of fish4.'}], 'role': 'user'}], 'model': 'gpt-4o', 'tools': [], 'tool_choice': 'auto'}, 'response': Response(id='resp_03b9010dc0322c97006914f629c6608193ad5517ed6dcabe4b', created_at=1762981417.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[ResponseOutputMessage(id='msg_03b9010dc0322c97006914f62a532c8193bf00b55cb75c721f', content=[ResponseOutputText(annotations=[], text='In a pond where the lily pads swish, \\nLived a catfish who dreamed of a dish. \\nHe dove in with glee, \\nIn search of a pea, \\nBut ended up hooked like a wish! \\n', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, background=False, conversation=None, max_output_tokens=None, max_tool_calls=None, previous_response_id=None, prompt=None, prompt_cache_key=None, reasoning=Reasoning(effort=None, generate_summary=None, summary=None), safety_identifier=None, service_tier='default', status='completed', text=ResponseTextConfig(format=ResponseFormatText(type='text'), verbosity='medium'), top_logprobs=0, truncation='disabled', usage=ResponseUsage(input_tokens=19, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=48, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=67), user=None, billing={'payer': 'developer'}, prompt_cache_retention=None, store=True)}\n" + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkdnNfHBwmiYxss90ETayFP4ngJYh', created=1765232097, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Silver scales gleam bright, \\nIn the coral reef's soft glow, \\nOcean's quiet dance. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=23, prompt_tokens=34, total_tokens=57, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish4.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkdnO8RXwmGeCm1Noam5CjS1qeDOI', created=1765232098, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"A fish named Lou swam with great flair, \\nThrough waters deep, without a care. \\nWith scales that shined bright, \\nIn the moon's gentle light, \\nHe danced in the ocean, quite rare. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=47, prompt_tokens=35, total_tokens=82, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n" ] } ], diff --git a/effectful/handlers/llm/__init__.py b/effectful/handlers/llm/__init__.py index 0cdbdd0e..3cb23042 100644 --- a/effectful/handlers/llm/__init__.py +++ b/effectful/handlers/llm/__init__.py @@ -1,17 +1,100 @@ import dataclasses import functools import inspect +import textwrap +import types from collections.abc import Callable, Iterable +from typing import Any from effectful.ops.syntax import defop from effectful.ops.types import NotHandled, Operation +def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: + """Collect all symbols from the caller's lexical context. + + Returns a dict mapping names to (source_code/repr, object) tuples. + Captures everything except: + - Private/dunder names (starting with _) + - Modules + """ + lexical_context = {**frame.f_globals, **frame.f_locals} + + collected: dict[str, tuple[str, Any]] = {} + for name, obj in lexical_context.items(): + # Skip private/dunder names + if name.startswith("_"): + continue + + # Skip modules + if isinstance(obj, types.ModuleType): + continue + + # Get source/repr for the object + source = _get_source_for_object(obj, name) + if source is not None: + collected[name] = (source, obj) + + return collected + + +def _get_source_for_object(obj: Any, name: str) -> str | None: + """Get source code or representation for an object. + + Returns a string representation suitable for including in a prompt. + """ + # For functions, try to get source + if isinstance(obj, types.FunctionType): + try: + return textwrap.dedent(inspect.getsource(obj)).strip() + except OSError: + # Fallback for functions without source (e.g., defined in REPL) + doc = obj.__doc__ or "No docstring" + return f"# \n# {doc}" + + # For classes/types + if isinstance(obj, type): + try: + return textwrap.dedent(inspect.getsource(obj)).strip() + except OSError: + # Fallback for classes without source + doc = obj.__doc__ or "No docstring" + return f"# \n# {doc}" + + # For generic aliases (list[int], Callable[[str], int], etc.) + if hasattr(obj, "__origin__"): + return f"{name} = {obj}" + + # For callable instances (objects with __call__) + if callable(obj): + obj_type = type(obj) + try: + return textwrap.dedent(inspect.getsource(obj_type)).strip() + except OSError: + doc = getattr(obj, "__doc__", None) or "No docstring" + return f"# \n# {doc}" + + # For dataclass instances, show the instance + if dataclasses.is_dataclass(obj) and not isinstance(obj, type): + return f"{name} = {obj!r}" + + try: + repr_str = repr(obj) + if len(repr_str) > 500: + return f"{name} = <{type(obj).__name__}>" + return f"{name} = {repr_str}" + except Exception: + return f"{name} = <{type(obj).__name__}>" + + @dataclasses.dataclass(frozen=True) class Template[**P, T]: __signature__: inspect.Signature __prompt_template__: str tools: tuple[Operation, ...] + lexical_context: dict[str, tuple[str, Any]] = dataclasses.field( + default_factory=dict + ) @defop def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: @@ -23,8 +106,20 @@ def __get__(self, instance, _owner): else: return self + def get_lexical_context_source(self) -> str: + """Return the source code of all captured lexical symbols.""" + return "\n\n".join(source for source, _ in self.lexical_context.values()) + @classmethod def define(cls, _func=None, *, tools: Iterable[Operation] = ()): + # Capture caller's frame to collect lexical context + caller_frame = inspect.currentframe() + assert caller_frame is not None + caller_frame = caller_frame.f_back + assert caller_frame is not None + + lexical_ctx = _collect_lexical_context(caller_frame) + def decorator(body: Callable[P, T]): if not body.__doc__: raise ValueError("Expected a docstring on body") @@ -33,6 +128,7 @@ def decorator(body: Callable[P, T]): __signature__=inspect.signature(body), __prompt_template__=body.__doc__, tools=tuple(tools), + lexical_context=lexical_ctx, ) if _func is None: From 4122acca807b9f86fd7214034a4e4cc327b0c651 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 17:34:17 -0500 Subject: [PATCH 02/65] Allow model input to refer to anything within the lexical context --- effectful/handlers/llm/providers.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/effectful/handlers/llm/providers.py b/effectful/handlers/llm/providers.py index 29241eec..289d845e 100644 --- a/effectful/handlers/llm/providers.py +++ b/effectful/handlers/llm/providers.py @@ -392,11 +392,22 @@ def format_model_input[**P, T]( """Format a template applied to arguments into a sequence of input messages. + Lexical context items can be referenced in the prompt template using + {name} syntax. For functions/classes, the source code is inserted. + For other values, the repr is inserted. """ bound_args = template.__signature__.bind(*args, **kwargs) bound_args.apply_defaults() + + format_args = {} + + for name, (source, obj) in template.lexical_context.items(): + format_args[name] = source + + format_args.update(bound_args.arguments) + prompt = _OpenAIPromptFormatter().format_as_messages( - template.__prompt_template__, **bound_args.arguments + template.__prompt_template__, **format_args ) # Note: The OpenAI api only seems to accept images in the 'user' role. The From 28fc2c92d5b98155730bee7e0bbbf9a52f919e48 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 17:48:53 -0500 Subject: [PATCH 03/65] Different handling between representable object and other types when referred to in Template --- effectful/handlers/llm/providers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/effectful/handlers/llm/providers.py b/effectful/handlers/llm/providers.py index 289d845e..9c426caa 100644 --- a/effectful/handlers/llm/providers.py +++ b/effectful/handlers/llm/providers.py @@ -402,7 +402,11 @@ def format_model_input[**P, T]( format_args = {} for name, (source, obj) in template.lexical_context.items(): - format_args[name] = source + # If object is a dataclass, we want to insert the repr of the instance + if dataclasses.is_dataclass(obj) and not isinstance(obj, type): + format_args[name] = repr(obj) + else: # type, function, callable, etc. + format_args[name] = source format_args.update(bound_args.arguments) From 126cab4a7e71abf347cb3640342392bb86093946 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 19:26:14 -0500 Subject: [PATCH 04/65] More edge case handling --- effectful/handlers/llm/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/effectful/handlers/llm/__init__.py b/effectful/handlers/llm/__init__.py index 3cb23042..aa98edfa 100644 --- a/effectful/handlers/llm/__init__.py +++ b/effectful/handlers/llm/__init__.py @@ -47,7 +47,7 @@ def _get_source_for_object(obj: Any, name: str) -> str | None: if isinstance(obj, types.FunctionType): try: return textwrap.dedent(inspect.getsource(obj)).strip() - except OSError: + except (OSError, TypeError): # Fallback for functions without source (e.g., defined in REPL) doc = obj.__doc__ or "No docstring" return f"# \n# {doc}" @@ -56,8 +56,7 @@ def _get_source_for_object(obj: Any, name: str) -> str | None: if isinstance(obj, type): try: return textwrap.dedent(inspect.getsource(obj)).strip() - except OSError: - # Fallback for classes without source + except (OSError, TypeError): doc = obj.__doc__ or "No docstring" return f"# \n# {doc}" @@ -70,7 +69,7 @@ def _get_source_for_object(obj: Any, name: str) -> str | None: obj_type = type(obj) try: return textwrap.dedent(inspect.getsource(obj_type)).strip() - except OSError: + except (OSError, TypeError): doc = getattr(obj, "__doc__", None) or "No docstring" return f"# \n# {doc}" From bec5e064fdbf1f85ca80383f7b5b170250c5471a Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 19:31:19 -0500 Subject: [PATCH 05/65] More edge case handling --- effectful/handlers/llm/__init__.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/effectful/handlers/llm/__init__.py b/effectful/handlers/llm/__init__.py index aa98edfa..2976cb23 100644 --- a/effectful/handlers/llm/__init__.py +++ b/effectful/handlers/llm/__init__.py @@ -22,15 +22,6 @@ def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: collected: dict[str, tuple[str, Any]] = {} for name, obj in lexical_context.items(): - # Skip private/dunder names - if name.startswith("_"): - continue - - # Skip modules - if isinstance(obj, types.ModuleType): - continue - - # Get source/repr for the object source = _get_source_for_object(obj, name) if source is not None: collected[name] = (source, obj) From a768749412bee0ba1c826c575468006d739fd943 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 19:32:42 -0500 Subject: [PATCH 06/65] More edge case handling --- effectful/handlers/llm/providers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/effectful/handlers/llm/providers.py b/effectful/handlers/llm/providers.py index 9c426caa..b6d4a2d8 100644 --- a/effectful/handlers/llm/providers.py +++ b/effectful/handlers/llm/providers.py @@ -405,6 +405,8 @@ def format_model_input[**P, T]( # If object is a dataclass, we want to insert the repr of the instance if dataclasses.is_dataclass(obj) and not isinstance(obj, type): format_args[name] = repr(obj) + elif isinstance(obj, (int, float, str, bytes, bool, tuple, list, set, dict)): + format_args[name] = repr(obj) else: # type, function, callable, etc. format_args[name] = source From aabb0b757f54bc813a5c234fc72700452ca304c1 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Thu, 4 Dec 2025 14:25:37 -0500 Subject: [PATCH 07/65] Adding mypy check and test --- effectful/handlers/llm/synthesis.py | 274 +++++++++++++++++++++++++++- tests/test_handlers_llm.py | 111 ++++++++++- 2 files changed, 378 insertions(+), 7 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 3a77441b..e57a5a47 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,10 +1,16 @@ import ast import collections.abc import dataclasses +import inspect import linecache +import os import re +import tempfile import textwrap import typing +from typing import get_args, get_origin, get_type_hints + +from mypy import api as mypy_api from effectful.handlers.llm import Template from effectful.ops.semantics import fwd @@ -19,13 +25,246 @@ def __init__(self, message, code=None): self.code = code +def collect_type_sources(t: type, seen: set[type] | None = None) -> dict[type, str]: + """Recursively collect source code for a type and all its dependencies. + + This function walks through a type annotation (including generic types like + Callable[[Person], Order]) and extracts source code for all user-defined + types it references. + + Args: + t: The type to analyze + seen: Set of already-processed types (to avoid infinite recursion) + + Returns: + A dict mapping types to their source code strings + """ + if seen is None: + seen = set() + + sources: dict[type, str] = {} + + # Handle generic types (e.g., Callable[[X], Y], list[X], Optional[X]) + origin = get_origin(t) + if origin is not None: + # process type arguments recursively + for arg in get_args(t): + if isinstance(arg, type): + sources.update(collect_type_sources(arg, seen)) + elif isinstance(arg, list): + # handle Callable[[P1, P2], R] where args is a list + for inner_arg in arg: + if isinstance(inner_arg, type): + sources.update(collect_type_sources(inner_arg, seen)) + return sources + + # Skip non-types, already-seen types, and builtins + if not isinstance(t, type) or t in seen: + return sources + if t.__module__ == "builtins": + return sources + + seen.add(t) + + # Try to get source code for this type, since there might be exceptions... + try: + source = inspect.getsource(t) + sources[t] = source + except (OSError, TypeError): + # Can't get source (built-in, C extension, dynamically created, etc.) + return sources + + # recursive descenting + try: + hints = get_type_hints(t) + for hint in hints.values(): + sources.update(collect_type_sources(hint, seen)) + except Exception: + pass + + # For dataclasses, also check field types explicitly + if dataclasses.is_dataclass(t): + for field in dataclasses.fields(t): + field_type = field.type + if isinstance(field_type, type): + sources.update(collect_type_sources(field_type, seen)) + elif isinstance(field_type, str): + # Forward reference as string - skip for now + pass + else: + # Could be a generic type + sources.update(collect_type_sources(field_type, seen)) + + # check base classes (excluding object) + for base in t.__bases__: + if base is not object: + sources.update(collect_type_sources(base, seen)) + + return sources + + +def format_type_context(sources: dict[type, str]) -> str: + """Format collected type sources into a context string for the prompt. + + Args: + sources: Dict mapping types to their source code + + Returns: + A formatted string containing all type definitions + """ + if not sources: + return "" + + parts = [] + for source in sources.values(): + # Clean up the source (dedent if needed) + cleaned = textwrap.dedent(source).strip() + parts.append(cleaned) + + return "\n\n".join(parts) + + +def _format_type_for_annotation(t: type) -> str: + """Format a type for use in a type annotation string. + + Handles Callable types from collections.abc and typing module. + """ + origin = get_origin(t) + + if origin is not None: + # handle generic types like Callable[[X], Y], list[X], etc. + args = get_args(t) + + # get the origin name - handle collections.abc.Callable -> Callable + if hasattr(origin, "__name__"): + origin_name = origin.__name__ + else: + origin_name = str(origin).split(".")[-1] + + if origin_name == "Callable" and args: + # Format as Callable[[P1, P2], R] + param_types = args[0] + return_type = args[-1] + + if param_types is ...: + params_str = "..." + else: + params_str = ( + "[" + + ", ".join(_format_type_for_annotation(p) for p in param_types) + + "]" + ) + + ret_str = _format_type_for_annotation(return_type) + return f"Callable[{params_str}, {ret_str}]" + else: + # Generic type like list[X], dict[K, V] + args_str = ", ".join(_format_type_for_annotation(a) for a in args) + return f"{origin_name}[{args_str}]" + + # Simple type + if hasattr(t, "__name__"): + return t.__name__ + return str(t) + + +def run_mypy_check( + code: str, + type_sources: dict[type, str], + expected_type: type, +) -> tuple[bool, str]: + """Run mypy on generated code to verify type correctness. + + Args: + code: The generated function code + type_sources: Dict mapping types to their source code + expected_type: The expected Callable type for the function + + Returns: + A tuple of (success: bool, error_message: str) + """ + + # Build the full source file with imports, type definitions, and generated code + source_parts = [] + + # Add common imports + source_parts.append("from __future__ import annotations") + source_parts.append("from typing import *") + source_parts.append("from collections.abc import Callable") + source_parts.append("import dataclasses") + source_parts.append("from dataclasses import dataclass") + source_parts.append("") + + # Add type definitions + for type_source in type_sources.values(): + cleaned = textwrap.dedent(type_source).strip() + source_parts.append(cleaned) + source_parts.append("") + + # Add the generated code + source_parts.append(textwrap.dedent(code).strip()) + source_parts.append("") + + # Add a type assertion to verify the function matches the expected type + # Extract function name from code + try: + module_ast = ast.parse(code) + last_decl = module_ast.body[-1] + if isinstance(last_decl, ast.FunctionDef): + func_name = last_decl.name + # Format the expected type for annotation (using imported names) + type_annotation = _format_type_for_annotation(expected_type) + source_parts.append(f"_check: {type_annotation} = {func_name}") + except Exception: + pass + + full_source = "\n".join(source_parts) + + # Write to temp file and run mypy + with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: + f.write(full_source) + temp_path = f.name + + try: + # Run mypy with strict settings + result = mypy_api.run( + [ + temp_path, + "--no-error-summary", + "--no-pretty", + "--hide-error-context", + "--no-color-output", + ] + ) + stdout, stderr, exit_code = result + + if exit_code != 0: + # Filter out the temp file path from error messages for cleaner output + error_msg = stdout.replace(temp_path, "") + return False, error_msg.strip() + + return True, "" + finally: + os.unlink(temp_path) + + class ProgramSynthesis(ObjectInterpretation): """Provides a `template` handler to instruct the LLM to generate code of the right form and with the right type. """ - def _parse_and_eval[T](self, t: type[T], content: str) -> T: + def __init__(self, type_check: bool = False): + """Initialize the program synthesis handler. + + Args: + type_check: Whether to verify the function signature matches the expected type. + """ + self.type_check = type_check + + def _parse_and_eval[T]( + self, t: type[T], content: str, type_sources: dict[type, str] + ) -> T: pattern = r"(.*?)" code_content = re.search(pattern, content, re.DOTALL) if code_content is None: @@ -51,8 +290,16 @@ def _parse_and_eval[T](self, t: type[T], content: str) -> T: # register into linecache linecache.cache[filename] = (len(source_code), None, lines, filename) - # TODO: assert callable type compatibility + # Build globals dict with type definitions available for exec gs: dict = {} + for typ in type_sources: + gs[typ.__name__] = typ + + if self.type_check: + success, error_msg = run_mypy_check(code, type_sources, t) + if not success: + raise SynthesisError(f"Type check failed:\n{error_msg}", content) + try: code_obj = compile(source_code, filename, "exec") exec(code_obj, gs) @@ -65,23 +312,37 @@ def _parse_and_eval[T](self, t: type[T], content: str) -> T: def _call(self, template, *args, **kwargs) -> None: ret_type = template.__signature__.return_annotation origin = typing.get_origin(ret_type) - ret_type = ret_type if origin is None else origin + ret_type_origin = ret_type if origin is None else origin - if not (issubclass(ret_type, collections.abc.Callable)): # type: ignore[arg-type] + if not (issubclass(ret_type_origin, collections.abc.Callable)): # type: ignore[arg-type] return fwd() + # Collect source code for all types referenced in the signature + type_sources = collect_type_sources(ret_type) + type_context = format_type_context(type_sources) + + # Build the type definitions section if there are custom types + type_defs_section = "" + if type_context: + type_defs_section = f""" + +{textwrap.indent(type_context, " ")} + +""" + prompt_ext = textwrap.dedent(f""" Generate a Python function satisfying the following specification and type signature. {template.__prompt_template__} {str(ret_type)} - +{type_defs_section} 1. Produce one block of Python code. 2. Do not include usage examples. 3. Return your response in tags. 4. Do not return your response in markdown blocks. 5. Your output function def must be the final statement in the code block. + 6. Do not redefine any types from - they are already available. """).strip() @@ -95,6 +356,7 @@ def _call(self, template, *args, **kwargs) -> None: **kwargs, ) - functional = self._parse_and_eval(ret_type, response) + # Pass full ret_type (with type args) for proper type checking + functional = self._parse_and_eval(ret_type, response, type_sources) return functional diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index 2531ef0d..af382b0d 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -1,7 +1,17 @@ +import inspect +import textwrap from collections.abc import Callable +from dataclasses import dataclass + +import pytest from effectful.handlers.llm import Template -from effectful.handlers.llm.synthesis import ProgramSynthesis +from effectful.handlers.llm.synthesis import ( + ProgramSynthesis, + SynthesisError, + collect_type_sources, + format_type_context, +) from effectful.ops.semantics import handler from effectful.ops.syntax import ObjectInterpretation, implements @@ -78,6 +88,18 @@ def count_char(char: str) -> Callable[[str], int]: raise NotImplementedError +@dataclass +class Person: + name: str + age: int + + +@Template.define +def make_greeter(style: str) -> Callable[[Person], str]: + """Create a greeting function for a person with the given style.""" + raise NotImplementedError + + # Unit tests def test_limerick(): """Test the limerick template returns a string.""" @@ -115,3 +137,90 @@ def count_occurrences(s): assert callable(count_a) assert count_a("banana") == 3 assert count_a("cherry") == 0 + + +def test_collect_type_sources(): + """Test the format_type_context function.""" + type_sources = collect_type_sources(Person) + assert type_sources == {Person: inspect.getsource(Person)} + + +def test_format_type_context(): + """Test the format_type_context function.""" + type_sources = {Person: inspect.getsource(Person)} + print(format_type_context(type_sources)) + print(textwrap.dedent(inspect.getsource(Person)).strip()) + assert ( + format_type_context(type_sources) + == textwrap.dedent(inspect.getsource(Person)).strip() + ) + + +def test_count_char_with_program_synthesis_type_check(): + """Test the count_char template with program synthesis and type checking.""" + mock_code = """ +def count_occurrences(s: str) -> int: + return s.count('a') +""" + mock_provider = SingleResponseLLMProvider(mock_code) + + with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): + count_a = count_char("a") + assert callable(count_a) + assert count_a("banana") == 3 + assert count_a("cherry") == 0 + + +def test_program_synthesis_type_check_catches_wrong_return_type(): + """Test that type checking catches functions with wrong return type.""" + # Invalid code: returns None instead of int + mock_code = """ +def count_occurrences(s: str) -> None: + pass +""" + mock_provider = SingleResponseLLMProvider(mock_code) + + with pytest.raises(SynthesisError, match="Type check failed"): + with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): + count_char("a") + + +def test_program_synthesis_type_check_catches_wrong_param_type(): + """Test that type checking catches functions with wrong parameter type.""" + # Invalid code: takes int instead of str + mock_code = """ +def count_occurrences(s: int) -> int: + return 42 +""" + mock_provider = SingleResponseLLMProvider(mock_code) + + with pytest.raises(SynthesisError, match="Type check failed"): + with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): + count_char("a") + + +def test_make_greeter_with_program_synthesis_custom_type_check(): + """Test program synthesis with custom type (Person) in the signature.""" + mock_code = """ +def greet(person: Person) -> str: + return f"Hello, {person.name}!" +""" + mock_provider = SingleResponseLLMProvider(mock_code) + + with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): + greeter = make_greeter("formal") + assert callable(greeter) + + +def test_make_greeter_with_program_synthesis_custom_type_check_error(): + """Test that type checking catches wrong custom type in parameter.""" + # Invalid code: takes str instead of Person + mock_code = """ +def greet(person: str) -> str: + return f"Hello, {person}!" +""" + mock_provider = SingleResponseLLMProvider(mock_code) + + with pytest.raises(SynthesisError, match="Type check failed"): + with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): + make_greeter("formal") From 55cd2831d201410fbfcc5e6e4bbc11b5d4706d92 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Thu, 4 Dec 2025 15:25:38 -0500 Subject: [PATCH 08/65] SynthesizedFunction for constrained decoding, adapt the tests --- effectful/handlers/llm/synthesis.py | 334 ++++++++++++++++------------ tests/test_handlers_llm.py | 139 +++++++----- 2 files changed, 273 insertions(+), 200 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index e57a5a47..64414af2 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,16 +1,14 @@ -import ast import collections.abc import dataclasses import inspect import linecache -import os -import re import tempfile import textwrap import typing +from mypy import api as mypy_api from typing import get_args, get_origin, get_type_hints -from mypy import api as mypy_api +import pydantic from effectful.handlers.llm import Template from effectful.ops.semantics import fwd @@ -25,81 +23,117 @@ def __init__(self, message, code=None): self.code = code -def collect_type_sources(t: type, seen: set[type] | None = None) -> dict[type, str]: - """Recursively collect source code for a type and all its dependencies. +class SynthesizedFunction(pydantic.BaseModel): + """Structured output for function synthesis. + + The LLM provides the function name and body, while the signature + (parameter types and return type) is prescribed by the prompt. + """ + + function_name: str + body: str # The indented function body (implementation) - This function walks through a type annotation (including generic types like - Callable[[Person], Order]) and extracts source code for all user-defined - types it references. + +def collect_referenced_types(t: type, seen: set[type] | None = None) -> set[type]: + """Collect all non-builtin types referenced in a type annotation. + + Walks through a type annotation (including generic types like + Callable[[Person], Order]) and collects all user-defined types. Args: t: The type to analyze seen: Set of already-processed types (to avoid infinite recursion) Returns: - A dict mapping types to their source code strings + A set of non-builtin types referenced in the annotation """ if seen is None: seen = set() - sources: dict[type, str] = {} + types: set[type] = set() # Handle generic types (e.g., Callable[[X], Y], list[X], Optional[X]) origin = get_origin(t) if origin is not None: - # process type arguments recursively for arg in get_args(t): if isinstance(arg, type): - sources.update(collect_type_sources(arg, seen)) + types.update(collect_referenced_types(arg, seen)) elif isinstance(arg, list): - # handle Callable[[P1, P2], R] where args is a list + # Handle Callable[[P1, P2], R] where args is a list for inner_arg in arg: if isinstance(inner_arg, type): - sources.update(collect_type_sources(inner_arg, seen)) - return sources + types.update(collect_referenced_types(inner_arg, seen)) + return types # Skip non-types, already-seen types, and builtins if not isinstance(t, type) or t in seen: - return sources + return types if t.__module__ == "builtins": - return sources + return types seen.add(t) + types.add(t) - # Try to get source code for this type, since there might be exceptions... - try: - source = inspect.getsource(t) - sources[t] = source - except (OSError, TypeError): - # Can't get source (built-in, C extension, dynamically created, etc.) - return sources - - # recursive descenting + # Recursively process type hints from annotations try: hints = get_type_hints(t) for hint in hints.values(): - sources.update(collect_type_sources(hint, seen)) + types.update(collect_referenced_types(hint, seen)) except Exception: pass - # For dataclasses, also check field types explicitly + # For dataclasses, also check field types if dataclasses.is_dataclass(t): for field in dataclasses.fields(t): field_type = field.type if isinstance(field_type, type): - sources.update(collect_type_sources(field_type, seen)) - elif isinstance(field_type, str): - # Forward reference as string - skip for now - pass - else: - # Could be a generic type - sources.update(collect_type_sources(field_type, seen)) + types.update(collect_referenced_types(field_type, seen)) + elif not isinstance(field_type, str): + types.update(collect_referenced_types(field_type, seen)) - # check base classes (excluding object) + # Check base classes (excluding object) for base in t.__bases__: if base is not object: - sources.update(collect_type_sources(base, seen)) + types.update(collect_referenced_types(base, seen)) + + return types + + +def get_type_imports(types: set[type]) -> list[str]: + """Get import statements for a set of types using inspect.getmodule. + + Args: + types: Set of types to generate imports for + Returns: + List of import statement strings + """ + imports = [] + for t in types: + module = inspect.getmodule(t) + if module is None or module.__name__ == "builtins": + continue + imports.append(f"from {module.__name__} import {t.__name__}") + return imports + + +def collect_type_sources(t: type) -> dict[type, str]: + """Collect source code for all types referenced in a type annotation. + + Args: + t: The type to analyze + + Returns: + A dict mapping types to their source code strings + """ + types = collect_referenced_types(t) + sources: dict[type, str] = {} + for typ in types: + try: + sources[typ] = inspect.getsource(typ) + except (OSError, TypeError): + # Can't get source (built-in, C extension, dynamically created, etc.) + pass return sources @@ -168,68 +202,64 @@ def _format_type_for_annotation(t: type) -> str: return str(t) +def _format_param_signature(callable_type: type) -> str: + """Format the parameter signature from a Callable type. + + E.g., Callable[[str, int], bool] -> "arg0: str, arg1: int" + """ + args = get_args(callable_type) + if not args: + return "" + + param_types = args[0] + if param_types is ...: + return "*args, **kwargs" + + params = [] + for i, param_type in enumerate(param_types): + type_str = _format_type_for_annotation(param_type) + params.append(f"arg{i}: {type_str}") + + return ", ".join(params) + + +def _format_return_type(callable_type: type) -> str: + """Extract and format the return type from a Callable type.""" + args = get_args(callable_type) + if not args: + return "Any" + + return_type = args[-1] + return _format_type_for_annotation(return_type) + + def run_mypy_check( code: str, - type_sources: dict[type, str], - expected_type: type, + referenced_types: set[type], ) -> tuple[bool, str]: """Run mypy on generated code to verify type correctness. Args: code: The generated function code - type_sources: Dict mapping types to their source code - expected_type: The expected Callable type for the function + referenced_types: Set of types referenced in the signature Returns: A tuple of (success: bool, error_message: str) """ - - # Build the full source file with imports, type definitions, and generated code - source_parts = [] - - # Add common imports - source_parts.append("from __future__ import annotations") - source_parts.append("from typing import *") - source_parts.append("from collections.abc import Callable") - source_parts.append("import dataclasses") - source_parts.append("from dataclasses import dataclass") - source_parts.append("") - - # Add type definitions - for type_source in type_sources.values(): - cleaned = textwrap.dedent(type_source).strip() - source_parts.append(cleaned) - source_parts.append("") - - # Add the generated code + source_parts = get_type_imports(referenced_types) source_parts.append(textwrap.dedent(code).strip()) - source_parts.append("") - - # Add a type assertion to verify the function matches the expected type - # Extract function name from code - try: - module_ast = ast.parse(code) - last_decl = module_ast.body[-1] - if isinstance(last_decl, ast.FunctionDef): - func_name = last_decl.name - # Format the expected type for annotation (using imported names) - type_annotation = _format_type_for_annotation(expected_type) - source_parts.append(f"_check: {type_annotation} = {func_name}") - except Exception: - pass full_source = "\n".join(source_parts) - # Write to temp file and run mypy - with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: + with tempfile.NamedTemporaryFile( + mode="w", suffix=".py", delete_on_close=False + ) as f: f.write(full_source) - temp_path = f.name + f.close() # Close so mypy can read it - try: - # Run mypy with strict settings result = mypy_api.run( [ - temp_path, + f.name, "--no-error-summary", "--no-pretty", "--hide-error-context", @@ -239,74 +269,84 @@ def run_mypy_check( stdout, stderr, exit_code = result if exit_code != 0: - # Filter out the temp file path from error messages for cleaner output - error_msg = stdout.replace(temp_path, "") + error_msg = stdout.replace(f.name, "") return False, error_msg.strip() return True, "" - finally: - os.unlink(temp_path) class ProgramSynthesis(ObjectInterpretation): """Provides a `template` handler to instruct the LLM to generate code of the right form and with the right type. - """ def __init__(self, type_check: bool = False): """Initialize the program synthesis handler. Args: - type_check: Whether to verify the function signature matches the expected type. + type_check: Whether to run mypy to verify the generated code. + Even with constrained decoding, this can catch errors + in the function body implementation. """ self.type_check = type_check - def _parse_and_eval[T]( - self, t: type[T], content: str, type_sources: dict[type, str] - ) -> T: - pattern = r"(.*?)" - code_content = re.search(pattern, content, re.DOTALL) - if code_content is None: - raise SynthesisError(" tags not found", content) - code = code_content.group(1) - - try: - module_ast = ast.parse(code) - except SyntaxError as exc: - raise SynthesisError("failed to parse", content) from exc - - if not isinstance(module_ast, ast.Module): - raise SynthesisError("not a module", content) + def _build_function( + self, + result: SynthesizedFunction, + callable_type: type, + referenced_types: set[type], + ) -> typing.Callable: + """Build and execute a function from the structured synthesis result. - last_decl = module_ast.body[-1] - if not isinstance(last_decl, ast.FunctionDef): - raise SynthesisError("last definition not a function", content) + Args: + result: The structured output from the LLM + callable_type: The expected Callable type (e.g., Callable[[str], int]) + referenced_types: Set of types referenced in the signature - source_code = textwrap.dedent(code) + Returns: + The synthesized callable function + """ + # Build the function with prescribed signature + param_sig = _format_param_signature(callable_type) + return_type = _format_return_type(callable_type) + func_name = result.function_name + + # Ensure body is properly indented + body = result.body + if not body.startswith(" ") and not body.startswith("\t"): + # Indent the body if not already indented + body = textwrap.indent(body, " ") + + # Construct the full function code + code = f"def {func_name}({param_sig}) -> {return_type}:\n{body}" + + # Register in linecache for better tracebacks + source_code = code lines = code.splitlines(keepends=True) filename = f"" - - # register into linecache linecache.cache[filename] = (len(source_code), None, lines, filename) - # Build globals dict with type definitions available for exec - gs: dict = {} - for typ in type_sources: - gs[typ.__name__] = typ - + # Optional mypy type checking if self.type_check: - success, error_msg = run_mypy_check(code, type_sources, t) + success, error_msg = run_mypy_check(code, referenced_types) if not success: - raise SynthesisError(f"Type check failed:\n{error_msg}", content) + raise SynthesisError(f"Type check failed:\n{error_msg}", code) + + # Build globals dict by importing types from their original modules + gs: dict = {} + for typ in referenced_types: + module = inspect.getmodule(typ) + if module is not None: + # Import the type from its module - this brings in all dependencies + gs[typ.__name__] = typ try: code_obj = compile(source_code, filename, "exec") exec(code_obj, gs) except Exception as exc: - raise SynthesisError("evaluation failed", content) from exc + raise SynthesisError("evaluation failed", code) from exc - return gs[last_decl.name] + return gs[func_name] @implements(Template.__call__) def _call(self, template, *args, **kwargs) -> None: @@ -317,46 +357,58 @@ def _call(self, template, *args, **kwargs) -> None: if not (issubclass(ret_type_origin, collections.abc.Callable)): # type: ignore[arg-type] return fwd() - # Collect source code for all types referenced in the signature + # Collect all types referenced in the signature + referenced_types = collect_referenced_types(ret_type) + + # Get type sources for the prompt (to show LLM the type definitions) type_sources = collect_type_sources(ret_type) type_context = format_type_context(type_sources) + # Build the prescribed signature + param_sig = _format_param_signature(ret_type) + return_type_str = _format_return_type(ret_type) + # Build the type definitions section if there are custom types type_defs_section = "" if type_context: type_defs_section = f""" - -{textwrap.indent(type_context, " ")} - +The following types are available: + +```python +{type_context} +``` """ prompt_ext = textwrap.dedent(f""" - Generate a Python function satisfying the following specification and type signature. - - {template.__prompt_template__} - {str(ret_type)} -{type_defs_section} - - 1. Produce one block of Python code. - 2. Do not include usage examples. - 3. Return your response in tags. - 4. Do not return your response in markdown blocks. - 5. Your output function def must be the final statement in the code block. - 6. Do not redefine any types from - they are already available. - + Implement a Python function with the following specification. + + **Specification:** {template.__prompt_template__} + + **Required signature:** + ```python + def ({param_sig}) -> {return_type_str}: + + ``` + {type_defs_section} + **Instructions:** + 1. Choose a descriptive function name. + 2. Implement only the function body. + 3. The parameter types and return type are fixed as shown above. + 4. Do not redefine any of the provided types. """).strip() - response = fwd( + # Use structured output - the LLM returns JSON with function_name and body + response: SynthesizedFunction = fwd( dataclasses.replace( template, __prompt_template__=prompt_ext, - __signature__=template.__signature__.replace(return_annotation=str), + __signature__=template.__signature__.replace( + return_annotation=SynthesizedFunction + ), ), *args, **kwargs, ) - # Pass full ret_type (with type args) for proper type checking - functional = self._parse_and_eval(ret_type, response, type_sources) - - return functional + # Build and return the function using imports instead of source injection + return self._build_function(response, ret_type, referenced_types) diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index af382b0d..038ea48e 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -9,6 +9,7 @@ from effectful.handlers.llm.synthesis import ( ProgramSynthesis, SynthesisError, + SynthesizedFunction, collect_type_sources, format_type_context, ) @@ -126,11 +127,12 @@ def test_primes_decode_int(): def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" - mock_code = """ -def count_occurrences(s): - return s.count('a') -""" - mock_provider = SingleResponseLLMProvider(mock_code) + # Structured response: LLM provides function name and body + mock_response = SynthesizedFunction( + function_name="count_occurrences", + body=" return arg0.count('a')", + ) + mock_provider = SingleResponseLLMProvider(mock_response) with handler(mock_provider), handler(ProgramSynthesis()): count_a = count_char("a") @@ -140,7 +142,7 @@ def count_occurrences(s): def test_collect_type_sources(): - """Test the format_type_context function.""" + """Test the collect_type_sources function.""" type_sources = collect_type_sources(Person) assert type_sources == {Person: inspect.getsource(Person)} @@ -148,79 +150,98 @@ def test_collect_type_sources(): def test_format_type_context(): """Test the format_type_context function.""" type_sources = {Person: inspect.getsource(Person)} - print(format_type_context(type_sources)) - print(textwrap.dedent(inspect.getsource(Person)).strip()) assert ( format_type_context(type_sources) == textwrap.dedent(inspect.getsource(Person)).strip() ) -def test_count_char_with_program_synthesis_type_check(): - """Test the count_char template with program synthesis and type checking.""" - mock_code = """ -def count_occurrences(s: str) -> int: - return s.count('a') -""" - mock_provider = SingleResponseLLMProvider(mock_code) +def test_count_char_with_typed_body(): + """Test program synthesis constructs function with correct prescribed types.""" + # The body just needs to implement the logic - types are prescribed + mock_response = SynthesizedFunction( + function_name="count_chars", + body=" return arg0.count('x')", + ) + mock_provider = SingleResponseLLMProvider(mock_response) - with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): - count_a = count_char("a") - assert callable(count_a) - assert count_a("banana") == 3 - assert count_a("cherry") == 0 + with handler(mock_provider), handler(ProgramSynthesis()): + count_x = count_char("x") + assert callable(count_x) + # Verify the function works + assert count_x("xylophone") == 1 + assert count_x("xxx") == 3 -def test_program_synthesis_type_check_catches_wrong_return_type(): - """Test that type checking catches functions with wrong return type.""" - # Invalid code: returns None instead of int - mock_code = """ -def count_occurrences(s: str) -> None: - pass -""" - mock_provider = SingleResponseLLMProvider(mock_code) +def test_make_greeter_with_program_synthesis(): + """Test program synthesis with custom type (Person) in the signature.""" + mock_response = SynthesizedFunction( + function_name="greet_person", + body=' return f"Hello, {arg0.name}!"', + ) + mock_provider = SingleResponseLLMProvider(mock_response) - with pytest.raises(SynthesisError, match="Type check failed"): - with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): - count_char("a") + with handler(mock_provider), handler(ProgramSynthesis()): + greeter = make_greeter("formal") + assert callable(greeter) + # Test the generated function works with the custom type + person = Person(name="Alice", age=30) + assert greeter(person) == "Hello, Alice!" -def test_program_synthesis_type_check_catches_wrong_param_type(): - """Test that type checking catches functions with wrong parameter type.""" - # Invalid code: takes int instead of str - mock_code = """ -def count_occurrences(s: int) -> int: - return 42 -""" - mock_provider = SingleResponseLLMProvider(mock_code) +def test_program_synthesis_invalid_body(): + """Test that synthesis fails when body has syntax errors.""" + mock_response = SynthesizedFunction( + function_name="bad_func", + body=" return this is not valid python", + ) + mock_provider = SingleResponseLLMProvider(mock_response) - with pytest.raises(SynthesisError, match="Type check failed"): - with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): + with pytest.raises(SynthesisError, match="evaluation failed"): + with handler(mock_provider), handler(ProgramSynthesis()): count_char("a") -def test_make_greeter_with_program_synthesis_custom_type_check(): - """Test program synthesis with custom type (Person) in the signature.""" - mock_code = """ -def greet(person: Person) -> str: - return f"Hello, {person.name}!" -""" - mock_provider = SingleResponseLLMProvider(mock_code) +def test_program_synthesis_runtime_error(): + """Test that synthesis fails when body raises runtime error on compile.""" + mock_response = SynthesizedFunction( + function_name="bad_func", + body=" return undefined_variable", + ) + mock_provider = SingleResponseLLMProvider(mock_response) + # This should compile fine but fail at runtime when called + with handler(mock_provider), handler(ProgramSynthesis()): + func = count_char("a") + # The function is created, but calling it will fail + with pytest.raises(NameError): + func("test") + + +def test_program_synthesis_with_type_check(): + """Test program synthesis with optional mypy type checking enabled.""" + mock_response = SynthesizedFunction( + function_name="count_chars", + body=" return arg0.count('a')", + ) + mock_provider = SingleResponseLLMProvider(mock_response) + + # With type_check=True, mypy verifies the generated code with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): - greeter = make_greeter("formal") - assert callable(greeter) + count_a = count_char("a") + assert callable(count_a) + assert count_a("banana") == 3 -def test_make_greeter_with_program_synthesis_custom_type_check_error(): - """Test that type checking catches wrong custom type in parameter.""" - # Invalid code: takes str instead of Person - mock_code = """ -def greet(person: str) -> str: - return f"Hello, {person}!" -""" - mock_provider = SingleResponseLLMProvider(mock_code) +def test_program_synthesis_type_check_catches_body_errors(): + """Test that type checking catches type errors in the function body.""" + # Body returns wrong type (str instead of int) + mock_response = SynthesizedFunction( + function_name="bad_return", + body=' return "not an int"', + ) + mock_provider = SingleResponseLLMProvider(mock_response) with pytest.raises(SynthesisError, match="Type check failed"): with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): - make_greeter("formal") + count_char("a") From a23292663b69738aa5245e591c2d6bb5fd4f2df6 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Thu, 4 Dec 2025 15:25:56 -0500 Subject: [PATCH 09/65] Linting --- effectful/handlers/llm/synthesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 64414af2..1eec6014 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -5,10 +5,10 @@ import tempfile import textwrap import typing -from mypy import api as mypy_api from typing import get_args, get_origin, get_type_hints import pydantic +from mypy import api as mypy_api from effectful.handlers.llm import Template from effectful.ops.semantics import fwd From 61a943280899073f8e093620a2a6e67daba6d5da Mon Sep 17 00:00:00 2001 From: datvo06 Date: Thu, 4 Dec 2025 15:29:20 -0500 Subject: [PATCH 10/65] Let LLM generate param names --- effectful/handlers/llm/synthesis.py | 66 ++++++++++++++++++++--------- tests/test_handlers_llm.py | 19 ++++++--- 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 1eec6014..4e313fd9 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -26,11 +26,12 @@ def __init__(self, message, code=None): class SynthesizedFunction(pydantic.BaseModel): """Structured output for function synthesis. - The LLM provides the function name and body, while the signature - (parameter types and return type) is prescribed by the prompt. + The LLM provides the function name, parameter names, and body. + The parameter types and return type are prescribed by the prompt. """ function_name: str + param_names: list[str] # Names for each parameter (in order) body: str # The indented function body (implementation) @@ -202,23 +203,41 @@ def _format_type_for_annotation(t: type) -> str: return str(t) -def _format_param_signature(callable_type: type) -> str: - """Format the parameter signature from a Callable type. +def _get_param_types(callable_type: type) -> list[type] | None: + """Extract parameter types from a Callable type. - E.g., Callable[[str, int], bool] -> "arg0: str, arg1: int" + Returns None if the callable uses ellipsis (...) for params. """ args = get_args(callable_type) if not args: - return "" + return [] param_types = args[0] if param_types is ...: + return None + + return list(param_types) + + +def _format_param_signature( + callable_type: type, param_names: list[str] | None = None +) -> str: + """Format the parameter signature from a Callable type. + + E.g., Callable[[str, int], bool] with names ["text", "count"] + -> "text: str, count: int" + """ + param_types = _get_param_types(callable_type) + if param_types is None: return "*args, **kwargs" + if not param_types: + return "" params = [] for i, param_type in enumerate(param_types): type_str = _format_type_for_annotation(param_type) - params.append(f"arg{i}: {type_str}") + name = param_names[i] if param_names and i < len(param_names) else f"arg{i}" + params.append(f"{name}: {type_str}") return ", ".join(params) @@ -306,8 +325,8 @@ def _build_function( Returns: The synthesized callable function """ - # Build the function with prescribed signature - param_sig = _format_param_signature(callable_type) + # Build the function with prescribed types and LLM-provided names + param_sig = _format_param_signature(callable_type, result.param_names) return_type = _format_return_type(callable_type) func_name = result.function_name @@ -364,10 +383,20 @@ def _call(self, template, *args, **kwargs) -> None: type_sources = collect_type_sources(ret_type) type_context = format_type_context(type_sources) - # Build the prescribed signature - param_sig = _format_param_signature(ret_type) + # Get parameter types and return type for the prompt + param_types = _get_param_types(ret_type) return_type_str = _format_return_type(ret_type) + # Format parameter types for display + if param_types is None: + param_types_str = "*args, **kwargs" + elif not param_types: + param_types_str = "(no parameters)" + else: + param_types_str = ", ".join( + _format_type_for_annotation(t) for t in param_types + ) + # Build the type definitions section if there are custom types type_defs_section = "" if type_context: @@ -384,17 +413,16 @@ def _call(self, template, *args, **kwargs) -> None: **Specification:** {template.__prompt_template__} - **Required signature:** - ```python - def ({param_sig}) -> {return_type_str}: - - ``` + **Required types:** + - Parameter types (in order): {param_types_str} + - Return type: {return_type_str} {type_defs_section} **Instructions:** 1. Choose a descriptive function name. - 2. Implement only the function body. - 3. The parameter types and return type are fixed as shown above. - 4. Do not redefine any of the provided types. + 2. Choose descriptive parameter names (one for each parameter type). + 3. Implement the function body. + 4. The parameter types and return type are fixed as shown above. + 5. Do not redefine any of the provided types. """).strip() # Use structured output - the LLM returns JSON with function_name and body diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index 038ea48e..d7567bf1 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -127,10 +127,11 @@ def test_primes_decode_int(): def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" - # Structured response: LLM provides function name and body + # Structured response: LLM provides function name, param names, and body mock_response = SynthesizedFunction( function_name="count_occurrences", - body=" return arg0.count('a')", + param_names=["text"], + body=" return text.count('a')", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -158,10 +159,11 @@ def test_format_type_context(): def test_count_char_with_typed_body(): """Test program synthesis constructs function with correct prescribed types.""" - # The body just needs to implement the logic - types are prescribed + # The body uses the LLM-provided parameter name mock_response = SynthesizedFunction( function_name="count_chars", - body=" return arg0.count('x')", + param_names=["s"], + body=" return s.count('x')", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -177,7 +179,8 @@ def test_make_greeter_with_program_synthesis(): """Test program synthesis with custom type (Person) in the signature.""" mock_response = SynthesizedFunction( function_name="greet_person", - body=' return f"Hello, {arg0.name}!"', + param_names=["person"], + body=' return f"Hello, {person.name}!"', ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -193,6 +196,7 @@ def test_program_synthesis_invalid_body(): """Test that synthesis fails when body has syntax errors.""" mock_response = SynthesizedFunction( function_name="bad_func", + param_names=["x"], body=" return this is not valid python", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -206,6 +210,7 @@ def test_program_synthesis_runtime_error(): """Test that synthesis fails when body raises runtime error on compile.""" mock_response = SynthesizedFunction( function_name="bad_func", + param_names=["x"], body=" return undefined_variable", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -222,7 +227,8 @@ def test_program_synthesis_with_type_check(): """Test program synthesis with optional mypy type checking enabled.""" mock_response = SynthesizedFunction( function_name="count_chars", - body=" return arg0.count('a')", + param_names=["text"], + body=" return text.count('a')", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -238,6 +244,7 @@ def test_program_synthesis_type_check_catches_body_errors(): # Body returns wrong type (str instead of int) mock_response = SynthesizedFunction( function_name="bad_return", + param_names=["text"], body=' return "not an int"', ) mock_provider = SingleResponseLLMProvider(mock_response) From 6135eeee8eede42d8649b4c9d3b597d552e12f0b Mon Sep 17 00:00:00 2001 From: datvo06 Date: Thu, 4 Dec 2025 15:30:22 -0500 Subject: [PATCH 11/65] Pydantic field annotation --- effectful/handlers/llm/synthesis.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 4e313fd9..3bc57e5a 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -9,6 +9,7 @@ import pydantic from mypy import api as mypy_api +from pydantic import Field from effectful.handlers.llm import Template from effectful.ops.semantics import fwd @@ -30,9 +31,11 @@ class SynthesizedFunction(pydantic.BaseModel): The parameter types and return type are prescribed by the prompt. """ - function_name: str - param_names: list[str] # Names for each parameter (in order) - body: str # The indented function body (implementation) + function_name: str = Field(..., description="The name of the function") + param_names: list[str] = Field( + ..., description="The names of the parameters (in order)" + ) + body: str = Field(..., description="The indented function body (implementation)") def collect_referenced_types(t: type, seen: set[type] | None = None) -> set[type]: From cb3cb07922927b8eb4ce31e38bb0011be4a87272 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 22:08:54 -0500 Subject: [PATCH 12/65] Merging notebook --- docs/source/llm.ipynb | 124 ++++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/docs/source/llm.ipynb b/docs/source/llm.ipynb index e23db644..48798b96 100644 --- a/docs/source/llm.ipynb +++ b/docs/source/llm.ipynb @@ -86,17 +86,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "In a pond where the water was clear, \n", - "Lived a fish with a gill-flapping cheer. \n", - "He would swim with a dash, \n", - "Through the water he'd splash, \n", - "Creating ripples far and near! \n", + "In the deep sea where fish like to play, \n", + "A school of bright scales swim all day. \n", + "With a flick and a swish, \n", + "They dance like a wish, \n", + "In the ocean's grand cabaret.\n", "----------------------------------------\n", - "In the sea where the little fish play, \n", - "They dart to and fro every day. \n", - "With scales shining bright, \n", - "They dance in the light, \n", - "In their watery world, they sway.\n" + "In a pond where the cool waters swish, \n", + "Lived a carp with a whimsical wish. \n", + "He dreamt of the sea, \n", + "Where he'd swim wild and free, \n", + "In a world that's unfathomably fish!\n" ] } ], @@ -126,29 +126,31 @@ "output_type": "stream", "text": [ "\n", - "Swimming in cool streams, \n", - "Silver scales flash in sunlight, \n", + "Glistening scales shine, \n", + "Dancing through the azure waves, \n", "Whispers of the deep.\n", "----------------------------------------\n", - "Swimming in cool streams, \n", - "Silver scales flash in sunlight, \n", + "Glistening scales shine, \n", + "Dancing through the azure waves, \n", "Whispers of the deep.\n", "\n", - "In waters so blue, \n", - "Silent fins glide with grace, free— \n", - "Fish dance in the deep. \n", + "Silver scales shimmer, \n", + "In the cool, deep blue they glide— \n", + "Silent water dance. \n", "----------------------------------------\n", - "In waters so blue, \n", - "Silent fins glide with grace, free— \n", - "Fish dance in the deep. \n", + "Silver scales shimmer, \n", + "In the cool, deep blue they glide— \n", + "Silent water dance. \n", + "\n", + "Silver scales shimmer, \n", + "Underneath the waves they glide, \n", + "Silent streams of dreams. \n", "\n", - "Below the water's gleam, \n", - "Silent dancers weave in streams— \n", - "Nature's quiet dream. \n", "----------------------------------------\n", - "Below the water's gleam, \n", - "Silent dancers weave in streams— \n", - "Nature's quiet dream. \n" + "Silver scales shimmer, \n", + "Underneath the waves they glide, \n", + "Silent streams of dreams. \n", + "\n" ] } ], @@ -232,8 +234,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "def count_occurrences_of_a(input_string: str) -> int:\n", - " \"\"\"Counts the occurrences of the letter 'a' in the given string.\"\"\"\n", + "def count_a_occurrences(input_string: str) -> int:\n", + " \"\"\"\n", + " Counts the occurrences of the letter 'a' in the given string.\n", + "\n", + " :param input_string: The string to be searched.\n", + " :return: The number of times 'a' appears in the string.\n", + " \"\"\"\n", " return input_string.count('a')\n" ] } @@ -264,7 +271,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 7, "id": "55a08ac2", "metadata": {}, "outputs": [ @@ -272,27 +279,36 @@ "name": "stdout", "output_type": "stream", "text": [ - "Coffee $4.50 x 2 = $9.00\n", - "Sandwich $8.99 x 1 = $8.99\n", - "Cookie $2.25 x 3 = $6.75\n", - "Total = $24.74\n", + "Receipt:\n", + "----------------------------------------\n", + "Coffee x2 @ $4.50 each: $9.00\n", + "Sandwich x1 @ $8.99 each: $8.99\n", + "Cookie x3 @ $2.25 each: $6.75\n", + "----------------------------------------\n", + "Total: $24.74\n", "\n", "--- Generated function source ---\n", - "def format_receipt(products: list[Product]) -> str:\n", - " \"\"\"Formats a list of products as a receipt.\"\"\"\n", + "def format_receipt(products: list) -> str:\n", + " \"\"\"\n", + " Format a list of products as a receipt.\n", "\n", - " def format_line(product: Product) -> str:\n", - " \"\"\"Formats a single product line on the receipt.\"\"\"\n", - " price_formatted = format_currency(product.price)\n", - " total_line_price = format_currency(product.price * product.quantity)\n", - " return f\"{product.name:<20} {price_formatted:<10} x {product.quantity:<3} = {total_line_price}\"\n", + " :param products: A list of Product objects.\n", + " :return: A formatted receipt as a string.\n", + " \"\"\"\n", + " receipt_lines = []\n", + " total_amount = 0.0\n", + " receipt_lines.append(\"Receipt:\")\n", + " receipt_lines.append(\"----------------------------------------\")\n", "\n", - " receipt_lines = [format_line(product) for product in products]\n", + " # Iterate over products to create receipt lines\n", + " for product in products:\n", + " line_total = product.price * product.quantity\n", + " total_amount += line_total\n", + " receipt_lines.append(f\"{product.name} x{product.quantity} @ {format_currency(product.price)} each: {format_currency(line_total)}\")\n", "\n", - " total_price = sum(product.price * product.quantity for product in products)\n", - " total_formatted = format_currency(total_price)\n", - "\n", - " receipt_lines.append(f\"{'Total':<20} {'':<10} {'':<3} = {total_formatted}\")\n", + " receipt_lines.append(\"----------------------------------------\")\n", + " # Adding the total amount\n", + " receipt_lines.append(f\"Total: {format_currency(total_amount)}\")\n", "\n", " return \"\\n\".join(receipt_lines)\n" ] @@ -324,16 +340,16 @@ "\n", "with handler(provider), handler(ProgramSynthesis()):\n", " format_receipt = make_receipt_formatter()\n", - "\n", + " \n", " products = [\n", " Product(\"Coffee\", 4.50, 2),\n", " Product(\"Sandwich\", 8.99, 1),\n", " Product(\"Cookie\", 2.25, 3),\n", " ]\n", - "\n", + " \n", " print(format_receipt(products))\n", " print(\"\\n--- Generated function source ---\")\n", - " print(inspect.getsource(format_receipt))" + " print(inspect.getsource(format_receipt))\n" ] }, { @@ -362,7 +378,7 @@ "Tool call: weather(*(), **{'city': 'Chicago'}) -> cold\n", "Tool call: weather(*(), **{'city': 'New York'}) -> wet\n", "Tool call: weather(*(), **{'city': 'Barcelona'}) -> sunny\n", - "Barcelona currently has sunny weather, making it a great choice for enjoying pleasant conditions.\n" + "Based on the current weather conditions, Barcelona has good weather, as it is sunny.\n" ] } ], @@ -419,7 +435,7 @@ "Who's there?\n", "Iguana.\n", "Iguana who?\n", - "Iguana come inside, it's too cold for a lizard out here!\n", + "Iguana be your friend, but I'm too busy basking in the sun!\n", "> The crowd laughs politely.\n" ] } @@ -478,8 +494,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish2.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkdnKXQFu0GxYEGSRhCQubQ9Kq4Z7', created=1765232094, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Silent waters gleam, \\nSilver fish dance in the deep, \\nNature's quiet grace. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=21, prompt_tokens=34, total_tokens=55, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n", - "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkdnLN7bah0dOK0crCPdWAXxw2EWb', created=1765232095, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='A fish with a gleam in the sea, \\nSwam circles in pure fishy glee. \\nWith a flip and a flop, \\nIt danced nonstop, \\nIn an ocean-wide jubilee. ', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=44, prompt_tokens=34, total_tokens=78, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n" + "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish2.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkdYT7IPx1Q5ExG610ZEYSik1ImTF', created=1765231173, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the depths they glide, \\nSilent scales in sunlit dance, \\nWhere the waters hide. ', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=22, prompt_tokens=34, total_tokens=56, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n", + "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkdYUNHH3udvmHLdijDeaSsLvcTi9', created=1765231174, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"A fish from the blue ocean deep, \\nSwam up where the little waves leap. \\nHe danced with delight, \\nIn the sun's golden light, \\nThen dived back for a long, dreamy sleep. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=46, prompt_tokens=34, total_tokens=80, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n" ] } ], @@ -521,8 +537,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkdnNfHBwmiYxss90ETayFP4ngJYh', created=1765232097, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Silver scales gleam bright, \\nIn the coral reef's soft glow, \\nOcean's quiet dance. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=23, prompt_tokens=34, total_tokens=57, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish4.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkdnO8RXwmGeCm1Noam5CjS1qeDOI', created=1765232098, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"A fish named Lou swam with great flair, \\nThrough waters deep, without a care. \\nWith scales that shined bright, \\nIn the moon's gentle light, \\nHe danced in the ocean, quite rare. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=47, prompt_tokens=35, total_tokens=82, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n" + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkdYWrh2iVIJJFHKXL6yRwcHU6vu1', created=1765231176, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Swimming in clear streams, \\nGraceful scales in sunlit dance, \\nWhispers of the deep.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=22, prompt_tokens=34, total_tokens=56, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish4.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkdYXpWEqJ3jmd9RNaUo4RHdAs2Ic', created=1765231177, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the ocean where fish explore, \\nSwam a fish named Finn by the shore. \\nHe said with a swish, \\n\"I wish I were a dish,\" \\nBut found his dreams were quite a bore. ', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=46, prompt_tokens=35, total_tokens=81, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n" ] } ], From 40413c50e1898776817eecd234d328cffc56e8b9 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Thu, 4 Dec 2025 15:36:02 -0500 Subject: [PATCH 13/65] Linting --- effectful/handlers/llm/synthesis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 3bc57e5a..41f7aff9 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -5,6 +5,7 @@ import tempfile import textwrap import typing +from collections.abc import Callable from typing import get_args, get_origin, get_type_hints import pydantic @@ -371,7 +372,7 @@ def _build_function( return gs[func_name] @implements(Template.__call__) - def _call(self, template, *args, **kwargs) -> None: + def _call(self, template, *args, **kwargs) -> Callable: ret_type = template.__signature__.return_annotation origin = typing.get_origin(ret_type) ret_type_origin = ret_type if origin is None else origin From 4d7867b320dfe4f4bfa8e307fe8fab9d05384930 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Fri, 5 Dec 2025 11:33:59 -0500 Subject: [PATCH 14/65] Fix minor formatting for type context --- effectful/handlers/llm/synthesis.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 41f7aff9..f6c2f712 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -402,13 +402,15 @@ def _call(self, template, *args, **kwargs) -> Callable: ) # Build the type definitions section if there are custom types + # Escape curly braces in type source code to avoid format string issues type_defs_section = "" if type_context: + escaped_type_context = type_context.replace("{", "{{").replace("}", "}}") type_defs_section = f""" The following types are available: ```python -{type_context} +{escaped_type_context} ``` """ From ce8ebcff1f3c005a90bc7311c7dd16b39a585529 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Fri, 5 Dec 2025 11:38:32 -0500 Subject: [PATCH 15/65] Update llm dependencies to include mypy --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index ac502509..2260193a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ jax = ["jax", "dm-tree"] numpyro = ["numpyro>=0.19", "dm-tree"] llm = [ "litellm", + "mypy", "pillow", "pydantic", ] From 246b9804e11c015aeb4aab800a0922ee89b27134 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Fri, 5 Dec 2025 11:40:25 -0500 Subject: [PATCH 16/65] More comprehensive error message --- effectful/handlers/llm/synthesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index f6c2f712..25f3b404 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -367,7 +367,7 @@ def _build_function( code_obj = compile(source_code, filename, "exec") exec(code_obj, gs) except Exception as exc: - raise SynthesisError("evaluation failed", code) from exc + raise SynthesisError(f"evaluation failed: {exc!r}, source code: {source_code}", code) from exc return gs[func_name] From 90d933c33462cc509b463280da148d6209353705 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Fri, 5 Dec 2025 11:44:23 -0500 Subject: [PATCH 17/65] linting --- effectful/handlers/llm/synthesis.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 25f3b404..24964351 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -367,7 +367,9 @@ def _build_function( code_obj = compile(source_code, filename, "exec") exec(code_obj, gs) except Exception as exc: - raise SynthesisError(f"evaluation failed: {exc!r}, source code: {source_code}", code) from exc + raise SynthesisError( + f"evaluation failed: {exc!r}, source code: {source_code}", code + ) from exc return gs[func_name] From ac2c953d44b268ca85aed5a076aecb483237153f Mon Sep 17 00:00:00 2001 From: datvo06 Date: Fri, 5 Dec 2025 11:55:44 -0500 Subject: [PATCH 18/65] More comprehensive import --- effectful/handlers/llm/synthesis.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 24964351..ae1d724f 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,3 +1,4 @@ +import collections import collections.abc import dataclasses import inspect @@ -57,17 +58,17 @@ def collect_referenced_types(t: type, seen: set[type] | None = None) -> set[type types: set[type] = set() - # Handle generic types (e.g., Callable[[X], Y], list[X], Optional[X]) + # Handle generic types (e.g., Callable[[X], Y], list[X], dict[str, Item]) origin = get_origin(t) if origin is not None: for arg in get_args(t): - if isinstance(arg, type): - types.update(collect_referenced_types(arg, seen)) - elif isinstance(arg, list): - # Handle Callable[[P1, P2], R] where args is a list + if isinstance(arg, list): + # Handle Callable[[P1, P2], R] where first arg is a list of param types for inner_arg in arg: - if isinstance(inner_arg, type): - types.update(collect_referenced_types(inner_arg, seen)) + types.update(collect_referenced_types(inner_arg, seen)) + elif arg is not ...: + # Recursively process all type arguments (including generic aliases) + types.update(collect_referenced_types(arg, seen)) return types # Skip non-types, already-seen types, and builtins @@ -360,7 +361,6 @@ def _build_function( for typ in referenced_types: module = inspect.getmodule(typ) if module is not None: - # Import the type from its module - this brings in all dependencies gs[typ.__name__] = typ try: From 403a46d496e03403833caca518b377557ca6abf5 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 22:10:09 -0500 Subject: [PATCH 19/65] Merging lexical context --- effectful/handlers/llm/__init__.py | 48 +++++++ effectful/handlers/llm/synthesis.py | 189 +++++++++++++++++++++------- tests/test_handlers_llm.py | 112 +++++++++++++---- 3 files changed, 279 insertions(+), 70 deletions(-) diff --git a/effectful/handlers/llm/__init__.py b/effectful/handlers/llm/__init__.py index 2976cb23..634cc250 100644 --- a/effectful/handlers/llm/__init__.py +++ b/effectful/handlers/llm/__init__.py @@ -10,6 +10,7 @@ from effectful.ops.types import NotHandled, Operation +<<<<<<< HEAD def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: """Collect all symbols from the caller's lexical context. @@ -24,11 +25,33 @@ def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: for name, obj in lexical_context.items(): source = _get_source_for_object(obj, name) if source is not None: +======= +def _collect_lexical_functions(frame) -> dict[str, tuple[str, types.FunctionType]]: + """Collect functions from the caller's lexical context. + + Returns a dict mapping function names to (source_code, function_object) tuples. + """ + lexical_context = {**frame.f_globals, **frame.f_locals} + current_module_name = frame.f_globals.get("__name__", "__main__") + + collected: dict[str, tuple[str, types.FunctionType]] = {} + for name, obj in lexical_context.items(): + if ( + isinstance(obj, types.FunctionType) + and getattr(obj, "__module__", None) == current_module_name + ): + try: + source = textwrap.dedent(inspect.getsource(obj)).strip() + except OSError: + # Fallback for functions without source (e.g., defined in REPL) + source = f"# \n# {obj.__doc__ or 'No docstring'}" +>>>>>>> 5c2d51c (Collecting lexical context) collected[name] = (source, obj) return collected +<<<<<<< HEAD def _get_source_for_object(obj: Any, name: str) -> str | None: """Get source code or representation for an object. @@ -77,12 +100,18 @@ def _get_source_for_object(obj: Any, name: str) -> str | None: return f"{name} = <{type(obj).__name__}>" +======= +>>>>>>> 5c2d51c (Collecting lexical context) @dataclasses.dataclass(frozen=True) class Template[**P, T]: __signature__: inspect.Signature __prompt_template__: str tools: tuple[Operation, ...] +<<<<<<< HEAD lexical_context: dict[str, tuple[str, Any]] = dataclasses.field( +======= + lexical_functions: dict[str, tuple[str, types.FunctionType]] = dataclasses.field( +>>>>>>> 5c2d51c (Collecting lexical context) default_factory=dict ) @@ -97,8 +126,19 @@ def __get__(self, instance, _owner): return self def get_lexical_context_source(self) -> str: +<<<<<<< HEAD """Return the source code of all captured lexical symbols.""" return "\n\n".join(source for source, _ in self.lexical_context.values()) +======= + """Return the source code of all captured lexical functions.""" + return "\n\n".join(source for source, _ in self.lexical_functions.values()) + + def get_lexical_function(self, name: str) -> types.FunctionType | None: + """Get a captured lexical function by name.""" + if name in self.lexical_functions: + return self.lexical_functions[name][1] + return None +>>>>>>> 5c2d51c (Collecting lexical context) @classmethod def define(cls, _func=None, *, tools: Iterable[Operation] = ()): @@ -108,7 +148,11 @@ def define(cls, _func=None, *, tools: Iterable[Operation] = ()): caller_frame = caller_frame.f_back assert caller_frame is not None +<<<<<<< HEAD lexical_ctx = _collect_lexical_context(caller_frame) +======= + lexical_funcs = _collect_lexical_functions(caller_frame) +>>>>>>> 5c2d51c (Collecting lexical context) def decorator(body: Callable[P, T]): if not body.__doc__: @@ -118,7 +162,11 @@ def decorator(body: Callable[P, T]): __signature__=inspect.signature(body), __prompt_template__=body.__doc__, tools=tuple(tools), +<<<<<<< HEAD lexical_context=lexical_ctx, +======= + lexical_functions=lexical_funcs, +>>>>>>> 5c2d51c (Collecting lexical context) ) if _func is None: diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index ae1d724f..a19f4cfc 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -26,18 +26,21 @@ def __init__(self, message, code=None): self.code = code -class SynthesizedFunction(pydantic.BaseModel): +class SynthesizedModule(pydantic.BaseModel): """Structured output for function synthesis. - The LLM provides the function name, parameter names, and body. - The parameter types and return type are prescribed by the prompt. + The LLM provides a complete Python module and the name of the + entry-point function to return. """ - function_name: str = Field(..., description="The name of the function") - param_names: list[str] = Field( - ..., description="The names of the parameters (in order)" + function_name: str = Field( + ..., + description="The name of the main function that satisfies the specification", + ) + module_code: str = Field( + ..., + description="Complete Python module code including the function and any helpers", ) - body: str = Field(..., description="The indented function body (implementation)") def collect_referenced_types(t: type, seen: set[type] | None = None) -> set[type]: @@ -316,45 +319,35 @@ def __init__(self, type_check: bool = False): def _build_function( self, - result: SynthesizedFunction, + result: SynthesizedModule, callable_type: type, referenced_types: set[type], + lexical_functions: dict[str, tuple[str, typing.Callable]] | None = None, ) -> typing.Callable: - """Build and execute a function from the structured synthesis result. + """Build and execute a function from the synthesized module. Args: - result: The structured output from the LLM + result: The structured output from the LLM containing module code callable_type: The expected Callable type (e.g., Callable[[str], int]) referenced_types: Set of types referenced in the signature + lexical_functions: Dict of lexical functions available in scope Returns: The synthesized callable function """ - # Build the function with prescribed types and LLM-provided names - param_sig = _format_param_signature(callable_type, result.param_names) - return_type = _format_return_type(callable_type) func_name = result.function_name - - # Ensure body is properly indented - body = result.body - if not body.startswith(" ") and not body.startswith("\t"): - # Indent the body if not already indented - body = textwrap.indent(body, " ") - - # Construct the full function code - code = f"def {func_name}({param_sig}) -> {return_type}:\n{body}" + module_code = textwrap.dedent(result.module_code).strip() # Register in linecache for better tracebacks - source_code = code - lines = code.splitlines(keepends=True) - filename = f"" - linecache.cache[filename] = (len(source_code), None, lines, filename) + lines = module_code.splitlines(keepends=True) + filename = f"" + linecache.cache[filename] = (len(module_code), None, lines, filename) # Optional mypy type checking if self.type_check: - success, error_msg = run_mypy_check(code, referenced_types) + success, error_msg = run_mypy_check(module_code, referenced_types) if not success: - raise SynthesisError(f"Type check failed:\n{error_msg}", code) + raise SynthesisError(f"Type check failed:\n{error_msg}", module_code) # Build globals dict by importing types from their original modules gs: dict = {} @@ -363,15 +356,104 @@ def _build_function( if module is not None: gs[typ.__name__] = typ + # Add lexical functions from the template's captured context + if lexical_functions: + for name, (_, func) in lexical_functions.items(): + gs[name] = func + try: - code_obj = compile(source_code, filename, "exec") + code_obj = compile(module_code, filename, "exec") exec(code_obj, gs) except Exception as exc: raise SynthesisError( - f"evaluation failed: {exc!r}, source code: {source_code}", code + f"evaluation failed: {exc!r}, source code: {module_code}", module_code ) from exc - return gs[func_name] + # Find and validate the function + if func_name not in gs: + raise SynthesisError( + f"Function '{func_name}' not found in generated module. " + f"Available names: {[k for k in gs.keys() if not k.startswith('_')]}", + module_code, + ) + + func = gs[func_name] + if not callable(func): + raise SynthesisError( + f"'{func_name}' is not callable (got {type(func).__name__})", + module_code, + ) + + # Verify the function signature matches the expected type + self._verify_signature(func, callable_type, module_code) + + return func + + def _verify_signature( + self, func: typing.Callable, callable_type: type, code: str + ) -> None: + """Verify that the function signature matches the expected Callable type. + + Args: + func: The synthesized function + callable_type: The expected Callable type + code: The source code (for error messages) + + Raises: + SynthesisError: If the signature doesn't match + """ + expected_param_types = _get_param_types(callable_type) + + sig = inspect.signature(func) + actual_params = list(sig.parameters.values()) + + # Check parameter count (skip if Callable[..., R]) + if expected_param_types is not None: + if len(actual_params) != len(expected_param_types): + raise SynthesisError( + f"Parameter count mismatch: expected {len(expected_param_types)}, " + f"got {len(actual_params)}", + code, + ) + + # Get type hints for the function + hints = typing.get_type_hints(func) + + # Check parameter types (skip if Callable[..., R]) + if expected_param_types is not None: + for i, (param, expected_type) in enumerate( + zip(actual_params, expected_param_types) + ): + actual_type = hints.get(param.name) + if actual_type is None: + raise SynthesisError( + f"Parameter '{param.name}' missing type annotation", code + ) + # Note: We do a simple equality check here. For more complex type + # compatibility, we'd need a proper type checker. + if actual_type != expected_type: + raise SynthesisError( + f"Parameter '{param.name}' type mismatch: " + f"expected {expected_type}, got {actual_type}", + code, + ) + + # Check return type + expected_return_type = get_args(callable_type)[-1] if get_args(callable_type) else None + if expected_return_type is not None: + actual_return_type = hints.get("return") + if actual_return_type is None: + raise SynthesisError( + f"Function missing return type annotation. " + f"Expected: {_format_type_for_annotation(expected_return_type)}", + code, + ) + if actual_return_type != expected_return_type: + raise SynthesisError( + f"Return type mismatch: expected {_format_type_for_annotation(expected_return_type)}, " + f"got {_format_type_for_annotation(actual_return_type)}", + code, + ) @implements(Template.__call__) def _call(self, template, *args, **kwargs) -> Callable: @@ -389,6 +471,10 @@ def _call(self, template, *args, **kwargs) -> Callable: type_sources = collect_type_sources(ret_type) type_context = format_type_context(type_sources) + # Get lexical functions from the template's captured context + lexical_functions = getattr(template, "lexical_functions", {}) + lexical_context = template.get_lexical_context_source() if lexical_functions else "" + # Get parameter types and return type for the prompt param_types = _get_param_types(ret_type) return_type_str = _format_return_type(ret_type) @@ -414,32 +500,47 @@ def _call(self, template, *args, **kwargs) -> Callable: ```python {escaped_type_context} ``` +""" + + # Build the helper functions section if there are lexical functions + helper_functions_section = "" + if lexical_context: + escaped_lexical_context = lexical_context.replace("{", "{{").replace("}", "}}") + helper_functions_section = f""" +The following helper functions are available for you to use: + +```python +{escaped_lexical_context} +``` """ prompt_ext = textwrap.dedent(f""" - Implement a Python function with the following specification. + Implement a Python module containing a function with the following specification. **Specification:** {template.__prompt_template__} - **Required types:** + **Required signature for the main function:** - Parameter types (in order): {param_types_str} - Return type: {return_type_str} - {type_defs_section} + {type_defs_section}{helper_functions_section} **Instructions:** - 1. Choose a descriptive function name. - 2. Choose descriptive parameter names (one for each parameter type). - 3. Implement the function body. - 4. The parameter types and return type are fixed as shown above. - 5. Do not redefine any of the provided types. + 1. Write a complete Python module with the main function and any helper functions/classes you need. + 2. Choose a descriptive name for the main function. + 3. Choose descriptive parameter names (one for each parameter type). + 4. The main function's parameter types and return type must match exactly as specified above. + 5. Do not redefine any of the provided types - they are already imported. + 6. Do not include import statements - all necessary types and helpers are pre-imported. + 7. You may define additional helper functions, classes, or constants in the module. + 8. You may use any of the helper functions provided above. """).strip() - # Use structured output - the LLM returns JSON with function_name and body - response: SynthesizedFunction = fwd( + # Use structured output - the LLM returns JSON with function_name and module_code + response: SynthesizedModule = fwd( dataclasses.replace( template, __prompt_template__=prompt_ext, __signature__=template.__signature__.replace( - return_annotation=SynthesizedFunction + return_annotation=SynthesizedModule ), ), *args, @@ -447,4 +548,6 @@ def _call(self, template, *args, **kwargs) -> Callable: ) # Build and return the function using imports instead of source injection - return self._build_function(response, ret_type, referenced_types) + return self._build_function( + response, ret_type, referenced_types, lexical_functions + ) diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index d7567bf1..187e46bc 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -9,7 +9,7 @@ from effectful.handlers.llm.synthesis import ( ProgramSynthesis, SynthesisError, - SynthesizedFunction, + SynthesizedModule, collect_type_sources, format_type_context, ) @@ -127,11 +127,13 @@ def test_primes_decode_int(): def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" - # Structured response: LLM provides function name, param names, and body - mock_response = SynthesizedFunction( + # Structured response: LLM provides function name and full module code + mock_response = SynthesizedModule( function_name="count_occurrences", - param_names=["text"], - body=" return text.count('a')", + module_code=""" +def count_occurrences(text: str) -> int: + return text.count('a') +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -159,11 +161,13 @@ def test_format_type_context(): def test_count_char_with_typed_body(): """Test program synthesis constructs function with correct prescribed types.""" - # The body uses the LLM-provided parameter name - mock_response = SynthesizedFunction( + # The module uses the LLM-provided parameter name + mock_response = SynthesizedModule( function_name="count_chars", - param_names=["s"], - body=" return s.count('x')", + module_code=""" +def count_chars(s: str) -> int: + return s.count('x') +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -177,10 +181,12 @@ def test_count_char_with_typed_body(): def test_make_greeter_with_program_synthesis(): """Test program synthesis with custom type (Person) in the signature.""" - mock_response = SynthesizedFunction( + mock_response = SynthesizedModule( function_name="greet_person", - param_names=["person"], - body=' return f"Hello, {person.name}!"', + module_code=""" +def greet_person(person: Person) -> str: + return f"Hello, {person.name}!" +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -193,11 +199,13 @@ def test_make_greeter_with_program_synthesis(): def test_program_synthesis_invalid_body(): - """Test that synthesis fails when body has syntax errors.""" - mock_response = SynthesizedFunction( + """Test that synthesis fails when module has syntax errors.""" + mock_response = SynthesizedModule( function_name="bad_func", - param_names=["x"], - body=" return this is not valid python", + module_code=""" +def bad_func(x: str) -> int: + return this is not valid python +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -207,11 +215,13 @@ def test_program_synthesis_invalid_body(): def test_program_synthesis_runtime_error(): - """Test that synthesis fails when body raises runtime error on compile.""" - mock_response = SynthesizedFunction( + """Test that synthesis fails when module raises runtime error when called.""" + mock_response = SynthesizedModule( function_name="bad_func", - param_names=["x"], - body=" return undefined_variable", + module_code=""" +def bad_func(x: str) -> int: + return undefined_variable +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -225,10 +235,12 @@ def test_program_synthesis_runtime_error(): def test_program_synthesis_with_type_check(): """Test program synthesis with optional mypy type checking enabled.""" - mock_response = SynthesizedFunction( + mock_response = SynthesizedModule( function_name="count_chars", - param_names=["text"], - body=" return text.count('a')", + module_code=""" +def count_chars(text: str) -> int: + return text.count('a') +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -241,14 +253,60 @@ def test_program_synthesis_with_type_check(): def test_program_synthesis_type_check_catches_body_errors(): """Test that type checking catches type errors in the function body.""" - # Body returns wrong type (str instead of int) - mock_response = SynthesizedFunction( + # Module returns wrong type (str instead of int) + mock_response = SynthesizedModule( function_name="bad_return", - param_names=["text"], - body=' return "not an int"', + module_code=""" +def bad_return(text: str) -> int: + return "not an int" +""", ) mock_provider = SingleResponseLLMProvider(mock_response) with pytest.raises(SynthesisError, match="Type check failed"): with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): count_char("a") + + +# Helper function for lexical scope test - defined at module level +def double_count(text: str, char: str) -> int: + """Count occurrences of a character and double it.""" + return text.count(char) * 2 + + +# Template that captures the lexical function above +@Template.define +def make_double_counter(char: str) -> Callable[[str], int]: + """Create a function that counts occurrences of '{char}' and doubles the result. + Use the double_count helper function.""" + raise NotImplementedError + + +def test_program_synthesis_with_lexical_function(): + """Test that synthesized code can use functions from the lexical scope.""" + # The synthesized code uses the double_count helper from lexical scope + mock_response = SynthesizedModule( + function_name="count_and_double", + module_code=""" +def count_and_double(text: str) -> int: + return double_count(text, 'a') +""", + ) + mock_provider = SingleResponseLLMProvider(mock_response) + + with handler(mock_provider), handler(ProgramSynthesis()): + counter = make_double_counter("a") + assert callable(counter) + # "banana" has 3 'a's, doubled = 6 + assert counter("banana") == 6 + # "cherry" has 0 'a's, doubled = 0 + assert counter("cherry") == 0 + + +def test_program_synthesis_lexical_function_in_prompt(): + """Test that lexical functions are included in the template's context.""" + # Verify the template captured the lexical function + assert "double_count" in make_double_counter.lexical_functions + source, func = make_double_counter.lexical_functions["double_count"] + assert "Count occurrences of a character and double it" in source + assert func is double_count From bce39c9fc2aa87be2772e2899218335648556c03 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 22:10:39 -0500 Subject: [PATCH 20/65] Merging lexical context --- effectful/handlers/llm/__init__.py | 94 +++++++++++-- effectful/handlers/llm/synthesis.py | 209 ++++++++++++++++++++++------ tests/test_handlers_llm.py | 13 +- 3 files changed, 258 insertions(+), 58 deletions(-) diff --git a/effectful/handlers/llm/__init__.py b/effectful/handlers/llm/__init__.py index 634cc250..91b77495 100644 --- a/effectful/handlers/llm/__init__.py +++ b/effectful/handlers/llm/__init__.py @@ -10,6 +10,7 @@ from effectful.ops.types import NotHandled, Operation +<<<<<<< HEAD <<<<<<< HEAD def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: """Collect all symbols from the caller's lexical context. @@ -28,25 +29,48 @@ def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: ======= def _collect_lexical_functions(frame) -> dict[str, tuple[str, types.FunctionType]]: """Collect functions from the caller's lexical context. +======= +def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: + """Collect functions and types from the caller's lexical context. +>>>>>>> 297e9e7 (Function signature reformatting) - Returns a dict mapping function names to (source_code, function_object) tuples. + Returns a dict mapping names to (source_code, object) tuples. + Captures both functions and classes defined in the current module. """ lexical_context = {**frame.f_globals, **frame.f_locals} current_module_name = frame.f_globals.get("__name__", "__main__") - collected: dict[str, tuple[str, types.FunctionType]] = {} + collected: dict[str, tuple[str, Any]] = {} for name, obj in lexical_context.items(): - if ( - isinstance(obj, types.FunctionType) - and getattr(obj, "__module__", None) == current_module_name - ): - try: - source = textwrap.dedent(inspect.getsource(obj)).strip() - except OSError: - # Fallback for functions without source (e.g., defined in REPL) + # Skip private/dunder names + if name.startswith("_"): + continue + + # Check if it's a function or class from the current module + is_function = isinstance(obj, types.FunctionType) + is_class = isinstance(obj, type) + + if not (is_function or is_class): + continue + + if getattr(obj, "__module__", None) != current_module_name: + continue + + try: + source = textwrap.dedent(inspect.getsource(obj)).strip() + except OSError: + # Fallback for objects without source (e.g., defined in REPL) + if is_function: source = f"# \n# {obj.__doc__ or 'No docstring'}" +<<<<<<< HEAD >>>>>>> 5c2d51c (Collecting lexical context) collected[name] = (source, obj) +======= + else: + source = f"# \n# {obj.__doc__ or 'No docstring'}" + + collected[name] = (source, obj) +>>>>>>> 297e9e7 (Function signature reformatting) return collected @@ -107,14 +131,37 @@ class Template[**P, T]: __signature__: inspect.Signature __prompt_template__: str tools: tuple[Operation, ...] +<<<<<<< HEAD <<<<<<< HEAD lexical_context: dict[str, tuple[str, Any]] = dataclasses.field( ======= lexical_functions: dict[str, tuple[str, types.FunctionType]] = dataclasses.field( >>>>>>> 5c2d51c (Collecting lexical context) +======= + lexical_context: dict[str, tuple[str, Any]] = dataclasses.field( +>>>>>>> 297e9e7 (Function signature reformatting) default_factory=dict ) + # Backwards compatibility alias + @property + def lexical_functions(self) -> dict[str, tuple[str, types.FunctionType]]: + """Get only the functions from lexical context (backwards compatibility).""" + return { + name: (source, obj) + for name, (source, obj) in self.lexical_context.items() + if isinstance(obj, types.FunctionType) + } + + @property + def lexical_types(self) -> dict[str, tuple[str, type]]: + """Get only the types/classes from lexical context.""" + return { + name: (source, obj) + for name, (source, obj) in self.lexical_context.items() + if isinstance(obj, type) + } + @defop def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: raise NotHandled @@ -126,17 +173,32 @@ def __get__(self, instance, _owner): return self def get_lexical_context_source(self) -> str: +<<<<<<< HEAD <<<<<<< HEAD """Return the source code of all captured lexical symbols.""" return "\n\n".join(source for source, _ in self.lexical_context.values()) ======= """Return the source code of all captured lexical functions.""" return "\n\n".join(source for source, _ in self.lexical_functions.values()) +======= + """Return the source code of all captured lexical symbols.""" + return "\n\n".join(source for source, _ in self.lexical_context.values()) +>>>>>>> 297e9e7 (Function signature reformatting) def get_lexical_function(self, name: str) -> types.FunctionType | None: """Get a captured lexical function by name.""" - if name in self.lexical_functions: - return self.lexical_functions[name][1] + if name in self.lexical_context: + obj = self.lexical_context[name][1] + if isinstance(obj, types.FunctionType): + return obj + return None + + def get_lexical_type(self, name: str) -> type | None: + """Get a captured lexical type by name.""" + if name in self.lexical_context: + obj = self.lexical_context[name][1] + if isinstance(obj, type): + return obj return None >>>>>>> 5c2d51c (Collecting lexical context) @@ -148,11 +210,15 @@ def define(cls, _func=None, *, tools: Iterable[Operation] = ()): caller_frame = caller_frame.f_back assert caller_frame is not None +<<<<<<< HEAD <<<<<<< HEAD lexical_ctx = _collect_lexical_context(caller_frame) ======= lexical_funcs = _collect_lexical_functions(caller_frame) >>>>>>> 5c2d51c (Collecting lexical context) +======= + lexical_ctx = _collect_lexical_context(caller_frame) +>>>>>>> 297e9e7 (Function signature reformatting) def decorator(body: Callable[P, T]): if not body.__doc__: @@ -162,11 +228,15 @@ def decorator(body: Callable[P, T]): __signature__=inspect.signature(body), __prompt_template__=body.__doc__, tools=tuple(tools), +<<<<<<< HEAD <<<<<<< HEAD lexical_context=lexical_ctx, ======= lexical_functions=lexical_funcs, >>>>>>> 5c2d51c (Collecting lexical context) +======= + lexical_context=lexical_ctx, +>>>>>>> 297e9e7 (Function signature reformatting) ) if _func is None: diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index a19f4cfc..60ccadc3 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -29,8 +29,8 @@ def __init__(self, message, code=None): class SynthesizedModule(pydantic.BaseModel): """Structured output for function synthesis. - The LLM provides a complete Python module and the name of the - entry-point function to return. + The LLM provides a complete Python module and the name of the main function. + We extract the function, re-format it with prescribed types, and verify with mypy. """ function_name: str = Field( @@ -260,6 +260,80 @@ def _format_return_type(callable_type: type) -> str: return _format_type_for_annotation(return_type) +def _types_match(expected: type, actual: type) -> bool: + """Check if two types match, handling cross-module generic types. + + This is needed because `list[__main__.Product]` != `list[Product]` even + when they refer to the same class, due to how generic aliases are compared. + + Also handles cases where LLM uses bare type (list) instead of generic (list[X]). + + Args: + expected: The expected type (from caller's context) + actual: The actual type (from exec'd code) + + Returns: + True if the types are structurally equivalent + """ + # Direct equality check (covers most cases) + if expected == actual: + return True + + # Same class by identity + if expected is actual: + return True + + # For generic types, compare origin and args recursively + expected_origin = get_origin(expected) + actual_origin = get_origin(actual) + + # Handle case where one is generic and one is bare type + # e.g., list[Product] vs list - accept if origins match + if expected_origin is not None and actual_origin is None: + # expected is generic (list[X]), actual is bare (list) + # Accept if actual is the origin of expected + if actual is expected_origin: + return True + # Also check by name for cross-module cases + if isinstance(actual, type) and hasattr(expected_origin, "__name__"): + if actual.__name__ == expected_origin.__name__: + return True + + if actual_origin is not None and expected_origin is None: + # actual is generic (list[X]), expected is bare (list) + if expected is actual_origin: + return True + if isinstance(expected, type) and hasattr(actual_origin, "__name__"): + if expected.__name__ == actual_origin.__name__: + return True + + if expected_origin is not None and actual_origin is not None: + # Both are generic - origins must match + if expected_origin is not actual_origin: + # Check by name for cross-module cases + if not ( + hasattr(expected_origin, "__name__") + and hasattr(actual_origin, "__name__") + and expected_origin.__name__ == actual_origin.__name__ + ): + return False + + # Compare type arguments recursively + expected_args = get_args(expected) + actual_args = get_args(actual) + + if len(expected_args) != len(actual_args): + return False + + return all(_types_match(e, a) for e, a in zip(expected_args, actual_args)) + + # For non-generic types, compare by name (handles cross-module cases) + if isinstance(expected, type) and isinstance(actual, type): + return expected.__name__ == actual.__name__ + + return False + + def run_mypy_check( code: str, referenced_types: set[type], @@ -322,28 +396,88 @@ def _build_function( result: SynthesizedModule, callable_type: type, referenced_types: set[type], - lexical_functions: dict[str, tuple[str, typing.Callable]] | None = None, + lexical_context: dict[str, tuple[str, typing.Any]] | None = None, ) -> typing.Callable: """Build and execute a function from the synthesized module. + Extracts the function from LLM's module, re-formats with prescribed signature, + and optionally verifies with mypy. + Args: result: The structured output from the LLM containing module code callable_type: The expected Callable type (e.g., Callable[[str], int]) referenced_types: Set of types referenced in the signature - lexical_functions: Dict of lexical functions available in scope + lexical_context: Dict of lexical context (functions/types) available in scope Returns: The synthesized callable function """ + import ast + func_name = result.function_name - module_code = textwrap.dedent(result.module_code).strip() + original_module_code = textwrap.dedent(result.module_code).strip() + + # Parse the module to extract function body and helpers + try: + tree = ast.parse(original_module_code) + except SyntaxError as exc: + raise SynthesisError( + f"Syntax error in generated code: {exc}", original_module_code + ) from exc + + # Find the target function and separate helpers + target_func_node = None + helper_nodes = [] + for node in tree.body: + if isinstance(node, ast.FunctionDef) and node.name == func_name: + target_func_node = node + else: + helper_nodes.append(node) + + if target_func_node is None: + raise SynthesisError( + f"Function '{func_name}' not found in generated module.", + original_module_code, + ) + + # Extract function body using AST - the body starts at first statement's line + all_lines = original_module_code.splitlines() + + # The body starts at the first statement in the function + if target_func_node.body: + body_start_line = target_func_node.body[0].lineno - 1 + body_end_line = target_func_node.end_lineno + body_lines = all_lines[body_start_line:body_end_line] + body = "\n".join(body_lines) + else: + # Empty function body (just pass or docstring) + body = " pass" + + # Get parameter names from the original function + param_names = [arg.arg for arg in target_func_node.args.args] + + # Build helper code from non-function-def nodes + helper_code = "" + if helper_nodes: + helper_parts = [] + for node in helper_nodes: + start = node.lineno - 1 + end = node.end_lineno + helper_parts.append("\n".join(all_lines[start:end])) + helper_code = "\n\n".join(helper_parts) + "\n\n" + + # Construct the function with PRESCRIBED types and extracted param names + param_sig = _format_param_signature(callable_type, param_names) + return_type = _format_return_type(callable_type) + func_code = f"def {func_name}({param_sig}) -> {return_type}:\n{body}" + module_code = helper_code + func_code # Register in linecache for better tracebacks lines = module_code.splitlines(keepends=True) filename = f"" linecache.cache[filename] = (len(module_code), None, lines, filename) - # Optional mypy type checking + # Optional mypy type checking - now with guaranteed correct signature if self.type_check: success, error_msg = run_mypy_check(module_code, referenced_types) if not success: @@ -356,10 +490,10 @@ def _build_function( if module is not None: gs[typ.__name__] = typ - # Add lexical functions from the template's captured context - if lexical_functions: - for name, (_, func) in lexical_functions.items(): - gs[name] = func + # Add lexical context (functions and types) from the template's captured context + if lexical_context: + for name, (_, obj) in lexical_context.items(): + gs[name] = obj try: code_obj = compile(module_code, filename, "exec") @@ -369,25 +503,14 @@ def _build_function( f"evaluation failed: {exc!r}, source code: {module_code}", module_code ) from exc - # Find and validate the function if func_name not in gs: raise SynthesisError( - f"Function '{func_name}' not found in generated module. " + f"Function '{func_name}' not found after execution. " f"Available names: {[k for k in gs.keys() if not k.startswith('_')]}", module_code, ) - func = gs[func_name] - if not callable(func): - raise SynthesisError( - f"'{func_name}' is not callable (got {type(func).__name__})", - module_code, - ) - - # Verify the function signature matches the expected type - self._verify_signature(func, callable_type, module_code) - - return func + return gs[func_name] def _verify_signature( self, func: typing.Callable, callable_type: type, code: str @@ -429,9 +552,8 @@ def _verify_signature( raise SynthesisError( f"Parameter '{param.name}' missing type annotation", code ) - # Note: We do a simple equality check here. For more complex type - # compatibility, we'd need a proper type checker. - if actual_type != expected_type: + # Use structural comparison to handle cross-module generic types + if not _types_match(expected_type, actual_type): raise SynthesisError( f"Parameter '{param.name}' type mismatch: " f"expected {expected_type}, got {actual_type}", @@ -439,7 +561,9 @@ def _verify_signature( ) # Check return type - expected_return_type = get_args(callable_type)[-1] if get_args(callable_type) else None + expected_return_type = ( + get_args(callable_type)[-1] if get_args(callable_type) else None + ) if expected_return_type is not None: actual_return_type = hints.get("return") if actual_return_type is None: @@ -448,7 +572,8 @@ def _verify_signature( f"Expected: {_format_type_for_annotation(expected_return_type)}", code, ) - if actual_return_type != expected_return_type: + # Use structural comparison to handle cross-module generic types + if not _types_match(expected_return_type, actual_return_type): raise SynthesisError( f"Return type mismatch: expected {_format_type_for_annotation(expected_return_type)}, " f"got {_format_type_for_annotation(actual_return_type)}", @@ -471,9 +596,11 @@ def _call(self, template, *args, **kwargs) -> Callable: type_sources = collect_type_sources(ret_type) type_context = format_type_context(type_sources) - # Get lexical functions from the template's captured context - lexical_functions = getattr(template, "lexical_functions", {}) - lexical_context = template.get_lexical_context_source() if lexical_functions else "" + # Get lexical context (functions and types) from the template's captured context + lexical_context = getattr(template, "lexical_context", {}) + lexical_context_source = ( + template.get_lexical_context_source() if lexical_context else "" + ) # Get parameter types and return type for the prompt param_types = _get_param_types(ret_type) @@ -502,12 +629,14 @@ def _call(self, template, *args, **kwargs) -> Callable: ``` """ - # Build the helper functions section if there are lexical functions - helper_functions_section = "" - if lexical_context: - escaped_lexical_context = lexical_context.replace("{", "{{").replace("}", "}}") - helper_functions_section = f""" -The following helper functions are available for you to use: + # Build the lexical context section (helper functions and types) + lexical_context_section = "" + if lexical_context_source: + escaped_lexical_context = lexical_context_source.replace("{", "{{").replace( + "}", "}}" + ) + lexical_context_section = f""" +The following helper functions and types are available for you to use: ```python {escaped_lexical_context} @@ -522,7 +651,7 @@ def _call(self, template, *args, **kwargs) -> Callable: **Required signature for the main function:** - Parameter types (in order): {param_types_str} - Return type: {return_type_str} - {type_defs_section}{helper_functions_section} + {type_defs_section}{lexical_context_section} **Instructions:** 1. Write a complete Python module with the main function and any helper functions/classes you need. 2. Choose a descriptive name for the main function. @@ -531,7 +660,7 @@ def _call(self, template, *args, **kwargs) -> Callable: 5. Do not redefine any of the provided types - they are already imported. 6. Do not include import statements - all necessary types and helpers are pre-imported. 7. You may define additional helper functions, classes, or constants in the module. - 8. You may use any of the helper functions provided above. + 8. You may use any of the helper functions and types provided above. """).strip() # Use structured output - the LLM returns JSON with function_name and module_code @@ -549,5 +678,5 @@ def _call(self, template, *args, **kwargs) -> Callable: # Build and return the function using imports instead of source injection return self._build_function( - response, ret_type, referenced_types, lexical_functions + response, ret_type, referenced_types, lexical_context ) diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index 187e46bc..894a36bb 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -127,7 +127,7 @@ def test_primes_decode_int(): def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" - # Structured response: LLM provides function name and full module code + # LLM provides full module, we extract and re-format with prescribed types mock_response = SynthesizedModule( function_name="count_occurrences", module_code=""" @@ -161,11 +161,11 @@ def test_format_type_context(): def test_count_char_with_typed_body(): """Test program synthesis constructs function with correct prescribed types.""" - # The module uses the LLM-provided parameter name + # LLM can use any types, we re-format with prescribed types mock_response = SynthesizedModule( function_name="count_chars", module_code=""" -def count_chars(s: str) -> int: +def count_chars(s): # LLM might not use correct types return s.count('x') """, ) @@ -209,7 +209,7 @@ def bad_func(x: str) -> int: ) mock_provider = SingleResponseLLMProvider(mock_response) - with pytest.raises(SynthesisError, match="evaluation failed"): + with pytest.raises(SynthesisError, match="Syntax error"): with handler(mock_provider), handler(ProgramSynthesis()): count_char("a") @@ -253,7 +253,7 @@ def count_chars(text: str) -> int: def test_program_synthesis_type_check_catches_body_errors(): """Test that type checking catches type errors in the function body.""" - # Module returns wrong type (str instead of int) + # Body returns wrong type (str instead of int) - mypy will catch this mock_response = SynthesizedModule( function_name="bad_return", module_code=""" @@ -288,7 +288,8 @@ def test_program_synthesis_with_lexical_function(): mock_response = SynthesizedModule( function_name="count_and_double", module_code=""" -def count_and_double(text: str) -> int: +def count_and_double(text): + # Uses double_count from lexical scope return double_count(text, 'a') """, ) From 64ecc637eeb471b47849803b46c840165b424787 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 22:16:00 -0500 Subject: [PATCH 21/65] Merging lexical context --- effectful/handlers/llm/__init__.py | 118 ----------------------------- 1 file changed, 118 deletions(-) diff --git a/effectful/handlers/llm/__init__.py b/effectful/handlers/llm/__init__.py index 91b77495..2976cb23 100644 --- a/effectful/handlers/llm/__init__.py +++ b/effectful/handlers/llm/__init__.py @@ -10,8 +10,6 @@ from effectful.ops.types import NotHandled, Operation -<<<<<<< HEAD -<<<<<<< HEAD def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: """Collect all symbols from the caller's lexical context. @@ -26,56 +24,11 @@ def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: for name, obj in lexical_context.items(): source = _get_source_for_object(obj, name) if source is not None: -======= -def _collect_lexical_functions(frame) -> dict[str, tuple[str, types.FunctionType]]: - """Collect functions from the caller's lexical context. -======= -def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: - """Collect functions and types from the caller's lexical context. ->>>>>>> 297e9e7 (Function signature reformatting) - - Returns a dict mapping names to (source_code, object) tuples. - Captures both functions and classes defined in the current module. - """ - lexical_context = {**frame.f_globals, **frame.f_locals} - current_module_name = frame.f_globals.get("__name__", "__main__") - - collected: dict[str, tuple[str, Any]] = {} - for name, obj in lexical_context.items(): - # Skip private/dunder names - if name.startswith("_"): - continue - - # Check if it's a function or class from the current module - is_function = isinstance(obj, types.FunctionType) - is_class = isinstance(obj, type) - - if not (is_function or is_class): - continue - - if getattr(obj, "__module__", None) != current_module_name: - continue - - try: - source = textwrap.dedent(inspect.getsource(obj)).strip() - except OSError: - # Fallback for objects without source (e.g., defined in REPL) - if is_function: - source = f"# \n# {obj.__doc__ or 'No docstring'}" -<<<<<<< HEAD ->>>>>>> 5c2d51c (Collecting lexical context) collected[name] = (source, obj) -======= - else: - source = f"# \n# {obj.__doc__ or 'No docstring'}" - - collected[name] = (source, obj) ->>>>>>> 297e9e7 (Function signature reformatting) return collected -<<<<<<< HEAD def _get_source_for_object(obj: Any, name: str) -> str | None: """Get source code or representation for an object. @@ -124,44 +77,15 @@ def _get_source_for_object(obj: Any, name: str) -> str | None: return f"{name} = <{type(obj).__name__}>" -======= ->>>>>>> 5c2d51c (Collecting lexical context) @dataclasses.dataclass(frozen=True) class Template[**P, T]: __signature__: inspect.Signature __prompt_template__: str tools: tuple[Operation, ...] -<<<<<<< HEAD -<<<<<<< HEAD lexical_context: dict[str, tuple[str, Any]] = dataclasses.field( -======= - lexical_functions: dict[str, tuple[str, types.FunctionType]] = dataclasses.field( ->>>>>>> 5c2d51c (Collecting lexical context) -======= - lexical_context: dict[str, tuple[str, Any]] = dataclasses.field( ->>>>>>> 297e9e7 (Function signature reformatting) default_factory=dict ) - # Backwards compatibility alias - @property - def lexical_functions(self) -> dict[str, tuple[str, types.FunctionType]]: - """Get only the functions from lexical context (backwards compatibility).""" - return { - name: (source, obj) - for name, (source, obj) in self.lexical_context.items() - if isinstance(obj, types.FunctionType) - } - - @property - def lexical_types(self) -> dict[str, tuple[str, type]]: - """Get only the types/classes from lexical context.""" - return { - name: (source, obj) - for name, (source, obj) in self.lexical_context.items() - if isinstance(obj, type) - } - @defop def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: raise NotHandled @@ -173,34 +97,8 @@ def __get__(self, instance, _owner): return self def get_lexical_context_source(self) -> str: -<<<<<<< HEAD -<<<<<<< HEAD """Return the source code of all captured lexical symbols.""" return "\n\n".join(source for source, _ in self.lexical_context.values()) -======= - """Return the source code of all captured lexical functions.""" - return "\n\n".join(source for source, _ in self.lexical_functions.values()) -======= - """Return the source code of all captured lexical symbols.""" - return "\n\n".join(source for source, _ in self.lexical_context.values()) ->>>>>>> 297e9e7 (Function signature reformatting) - - def get_lexical_function(self, name: str) -> types.FunctionType | None: - """Get a captured lexical function by name.""" - if name in self.lexical_context: - obj = self.lexical_context[name][1] - if isinstance(obj, types.FunctionType): - return obj - return None - - def get_lexical_type(self, name: str) -> type | None: - """Get a captured lexical type by name.""" - if name in self.lexical_context: - obj = self.lexical_context[name][1] - if isinstance(obj, type): - return obj - return None ->>>>>>> 5c2d51c (Collecting lexical context) @classmethod def define(cls, _func=None, *, tools: Iterable[Operation] = ()): @@ -210,15 +108,7 @@ def define(cls, _func=None, *, tools: Iterable[Operation] = ()): caller_frame = caller_frame.f_back assert caller_frame is not None -<<<<<<< HEAD -<<<<<<< HEAD lexical_ctx = _collect_lexical_context(caller_frame) -======= - lexical_funcs = _collect_lexical_functions(caller_frame) ->>>>>>> 5c2d51c (Collecting lexical context) -======= - lexical_ctx = _collect_lexical_context(caller_frame) ->>>>>>> 297e9e7 (Function signature reformatting) def decorator(body: Callable[P, T]): if not body.__doc__: @@ -228,15 +118,7 @@ def decorator(body: Callable[P, T]): __signature__=inspect.signature(body), __prompt_template__=body.__doc__, tools=tuple(tools), -<<<<<<< HEAD -<<<<<<< HEAD - lexical_context=lexical_ctx, -======= - lexical_functions=lexical_funcs, ->>>>>>> 5c2d51c (Collecting lexical context) -======= lexical_context=lexical_ctx, ->>>>>>> 297e9e7 (Function signature reformatting) ) if _func is None: From 04d5622aca80b828517ec2e73004c374cc58eca8 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 22:16:11 -0500 Subject: [PATCH 22/65] Lint --- docs/source/llm.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/llm.ipynb b/docs/source/llm.ipynb index 48798b96..19a062c0 100644 --- a/docs/source/llm.ipynb +++ b/docs/source/llm.ipynb @@ -340,16 +340,16 @@ "\n", "with handler(provider), handler(ProgramSynthesis()):\n", " format_receipt = make_receipt_formatter()\n", - " \n", + "\n", " products = [\n", " Product(\"Coffee\", 4.50, 2),\n", " Product(\"Sandwich\", 8.99, 1),\n", " Product(\"Cookie\", 2.25, 3),\n", " ]\n", - " \n", + "\n", " print(format_receipt(products))\n", " print(\"\\n--- Generated function source ---\")\n", - " print(inspect.getsource(format_receipt))\n" + " print(inspect.getsource(format_receipt))" ] }, { From e3741aa8229d65911514cb91a7a6ef09cd079470 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 8 Dec 2025 22:30:08 -0500 Subject: [PATCH 23/65] More refractoring to use the lexical context instead of collected type sources --- effectful/handlers/llm/synthesis.py | 356 +++------------------------- tests/test_handlers_llm.py | 23 +- 2 files changed, 37 insertions(+), 342 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 60ccadc3..34384f7f 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,3 +1,4 @@ +import ast import collections import collections.abc import dataclasses @@ -7,7 +8,7 @@ import textwrap import typing from collections.abc import Callable -from typing import get_args, get_origin, get_type_hints +from typing import Any, get_args, get_origin import pydantic from mypy import api as mypy_api @@ -43,130 +44,22 @@ class SynthesizedModule(pydantic.BaseModel): ) -def collect_referenced_types(t: type, seen: set[type] | None = None) -> set[type]: - """Collect all non-builtin types referenced in a type annotation. +def _get_imports_from_lexical_context( + lexical_context: dict[str, tuple[str, Any]], +) -> list[str]: + """Generate import statements for types in the lexical context. - Walks through a type annotation (including generic types like - Callable[[Person], Order]) and collects all user-defined types. - - Args: - t: The type to analyze - seen: Set of already-processed types (to avoid infinite recursion) - - Returns: - A set of non-builtin types referenced in the annotation - """ - if seen is None: - seen = set() - - types: set[type] = set() - - # Handle generic types (e.g., Callable[[X], Y], list[X], dict[str, Item]) - origin = get_origin(t) - if origin is not None: - for arg in get_args(t): - if isinstance(arg, list): - # Handle Callable[[P1, P2], R] where first arg is a list of param types - for inner_arg in arg: - types.update(collect_referenced_types(inner_arg, seen)) - elif arg is not ...: - # Recursively process all type arguments (including generic aliases) - types.update(collect_referenced_types(arg, seen)) - return types - - # Skip non-types, already-seen types, and builtins - if not isinstance(t, type) or t in seen: - return types - if t.__module__ == "builtins": - return types - - seen.add(t) - types.add(t) - - # Recursively process type hints from annotations - try: - hints = get_type_hints(t) - for hint in hints.values(): - types.update(collect_referenced_types(hint, seen)) - except Exception: - pass - - # For dataclasses, also check field types - if dataclasses.is_dataclass(t): - for field in dataclasses.fields(t): - field_type = field.type - if isinstance(field_type, type): - types.update(collect_referenced_types(field_type, seen)) - elif not isinstance(field_type, str): - types.update(collect_referenced_types(field_type, seen)) - - # Check base classes (excluding object) - for base in t.__bases__: - if base is not object: - types.update(collect_referenced_types(base, seen)) - - return types - - -def get_type_imports(types: set[type]) -> list[str]: - """Get import statements for a set of types using inspect.getmodule. - - Args: - types: Set of types to generate imports for - - Returns: - List of import statement strings + Only generates imports for types/classes that have a proper module. """ imports = [] - for t in types: - module = inspect.getmodule(t) - if module is None or module.__name__ == "builtins": - continue - imports.append(f"from {module.__name__} import {t.__name__}") + for name, (_, obj) in lexical_context.items(): + if isinstance(obj, type): + module = inspect.getmodule(obj) + if module is not None and module.__name__ not in ("builtins", "__main__"): + imports.append(f"from {module.__name__} import {name}") return imports -def collect_type_sources(t: type) -> dict[type, str]: - """Collect source code for all types referenced in a type annotation. - - Args: - t: The type to analyze - - Returns: - A dict mapping types to their source code strings - """ - types = collect_referenced_types(t) - sources: dict[type, str] = {} - for typ in types: - try: - sources[typ] = inspect.getsource(typ) - except (OSError, TypeError): - # Can't get source (built-in, C extension, dynamically created, etc.) - pass - return sources - - -def format_type_context(sources: dict[type, str]) -> str: - """Format collected type sources into a context string for the prompt. - - Args: - sources: Dict mapping types to their source code - - Returns: - A formatted string containing all type definitions - """ - if not sources: - return "" - - parts = [] - for source in sources.values(): - # Clean up the source (dedent if needed) - cleaned = textwrap.dedent(source).strip() - parts.append(cleaned) - - return "\n\n".join(parts) - - def _format_type_for_annotation(t: type) -> str: """Format a type for use in a type annotation string. @@ -260,95 +153,21 @@ def _format_return_type(callable_type: type) -> str: return _format_type_for_annotation(return_type) -def _types_match(expected: type, actual: type) -> bool: - """Check if two types match, handling cross-module generic types. - - This is needed because `list[__main__.Product]` != `list[Product]` even - when they refer to the same class, due to how generic aliases are compared. - - Also handles cases where LLM uses bare type (list) instead of generic (list[X]). - - Args: - expected: The expected type (from caller's context) - actual: The actual type (from exec'd code) - - Returns: - True if the types are structurally equivalent - """ - # Direct equality check (covers most cases) - if expected == actual: - return True - - # Same class by identity - if expected is actual: - return True - - # For generic types, compare origin and args recursively - expected_origin = get_origin(expected) - actual_origin = get_origin(actual) - - # Handle case where one is generic and one is bare type - # e.g., list[Product] vs list - accept if origins match - if expected_origin is not None and actual_origin is None: - # expected is generic (list[X]), actual is bare (list) - # Accept if actual is the origin of expected - if actual is expected_origin: - return True - # Also check by name for cross-module cases - if isinstance(actual, type) and hasattr(expected_origin, "__name__"): - if actual.__name__ == expected_origin.__name__: - return True - - if actual_origin is not None and expected_origin is None: - # actual is generic (list[X]), expected is bare (list) - if expected is actual_origin: - return True - if isinstance(expected, type) and hasattr(actual_origin, "__name__"): - if expected.__name__ == actual_origin.__name__: - return True - - if expected_origin is not None and actual_origin is not None: - # Both are generic - origins must match - if expected_origin is not actual_origin: - # Check by name for cross-module cases - if not ( - hasattr(expected_origin, "__name__") - and hasattr(actual_origin, "__name__") - and expected_origin.__name__ == actual_origin.__name__ - ): - return False - - # Compare type arguments recursively - expected_args = get_args(expected) - actual_args = get_args(actual) - - if len(expected_args) != len(actual_args): - return False - - return all(_types_match(e, a) for e, a in zip(expected_args, actual_args)) - - # For non-generic types, compare by name (handles cross-module cases) - if isinstance(expected, type) and isinstance(actual, type): - return expected.__name__ == actual.__name__ - - return False - - def run_mypy_check( code: str, - referenced_types: set[type], + lexical_context: dict[str, tuple[str, Any]], ) -> tuple[bool, str]: """Run mypy on generated code to verify type correctness. Args: code: The generated function code - referenced_types: Set of types referenced in the signature + lexical_context: Lexical context containing types for imports Returns: A tuple of (success: bool, error_message: str) """ - source_parts = get_type_imports(referenced_types) - source_parts.append(textwrap.dedent(code).strip()) + imports = _get_imports_from_lexical_context(lexical_context) + source_parts = imports + [textwrap.dedent(code).strip()] full_source = "\n".join(source_parts) @@ -395,8 +214,7 @@ def _build_function( self, result: SynthesizedModule, callable_type: type, - referenced_types: set[type], - lexical_context: dict[str, tuple[str, typing.Any]] | None = None, + lexical_context: dict[str, tuple[str, Any]], ) -> typing.Callable: """Build and execute a function from the synthesized module. @@ -406,14 +224,11 @@ def _build_function( Args: result: The structured output from the LLM containing module code callable_type: The expected Callable type (e.g., Callable[[str], int]) - referenced_types: Set of types referenced in the signature lexical_context: Dict of lexical context (functions/types) available in scope Returns: The synthesized callable function """ - import ast - func_name = result.function_name original_module_code = textwrap.dedent(result.module_code).strip() @@ -477,23 +292,16 @@ def _build_function( filename = f"" linecache.cache[filename] = (len(module_code), None, lines, filename) - # Optional mypy type checking - now with guaranteed correct signature + # Optional mypy type checking - uses lexical context for imports if self.type_check: - success, error_msg = run_mypy_check(module_code, referenced_types) + success, error_msg = run_mypy_check(module_code, lexical_context) if not success: raise SynthesisError(f"Type check failed:\n{error_msg}", module_code) - # Build globals dict by importing types from their original modules + # Build globals dict from lexical context (all functions, types, etc.) gs: dict = {} - for typ in referenced_types: - module = inspect.getmodule(typ) - if module is not None: - gs[typ.__name__] = typ - - # Add lexical context (functions and types) from the template's captured context - if lexical_context: - for name, (_, obj) in lexical_context.items(): - gs[name] = obj + for name, (_, obj) in lexical_context.items(): + gs[name] = obj try: code_obj = compile(module_code, filename, "exec") @@ -512,74 +320,6 @@ def _build_function( return gs[func_name] - def _verify_signature( - self, func: typing.Callable, callable_type: type, code: str - ) -> None: - """Verify that the function signature matches the expected Callable type. - - Args: - func: The synthesized function - callable_type: The expected Callable type - code: The source code (for error messages) - - Raises: - SynthesisError: If the signature doesn't match - """ - expected_param_types = _get_param_types(callable_type) - - sig = inspect.signature(func) - actual_params = list(sig.parameters.values()) - - # Check parameter count (skip if Callable[..., R]) - if expected_param_types is not None: - if len(actual_params) != len(expected_param_types): - raise SynthesisError( - f"Parameter count mismatch: expected {len(expected_param_types)}, " - f"got {len(actual_params)}", - code, - ) - - # Get type hints for the function - hints = typing.get_type_hints(func) - - # Check parameter types (skip if Callable[..., R]) - if expected_param_types is not None: - for i, (param, expected_type) in enumerate( - zip(actual_params, expected_param_types) - ): - actual_type = hints.get(param.name) - if actual_type is None: - raise SynthesisError( - f"Parameter '{param.name}' missing type annotation", code - ) - # Use structural comparison to handle cross-module generic types - if not _types_match(expected_type, actual_type): - raise SynthesisError( - f"Parameter '{param.name}' type mismatch: " - f"expected {expected_type}, got {actual_type}", - code, - ) - - # Check return type - expected_return_type = ( - get_args(callable_type)[-1] if get_args(callable_type) else None - ) - if expected_return_type is not None: - actual_return_type = hints.get("return") - if actual_return_type is None: - raise SynthesisError( - f"Function missing return type annotation. " - f"Expected: {_format_type_for_annotation(expected_return_type)}", - code, - ) - # Use structural comparison to handle cross-module generic types - if not _types_match(expected_return_type, actual_return_type): - raise SynthesisError( - f"Return type mismatch: expected {_format_type_for_annotation(expected_return_type)}, " - f"got {_format_type_for_annotation(actual_return_type)}", - code, - ) - @implements(Template.__call__) def _call(self, template, *args, **kwargs) -> Callable: ret_type = template.__signature__.return_annotation @@ -589,18 +329,8 @@ def _call(self, template, *args, **kwargs) -> Callable: if not (issubclass(ret_type_origin, collections.abc.Callable)): # type: ignore[arg-type] return fwd() - # Collect all types referenced in the signature - referenced_types = collect_referenced_types(ret_type) - - # Get type sources for the prompt (to show LLM the type definitions) - type_sources = collect_type_sources(ret_type) - type_context = format_type_context(type_sources) - - # Get lexical context (functions and types) from the template's captured context + # Get lexical context - contains all functions, types, and values from definition site lexical_context = getattr(template, "lexical_context", {}) - lexical_context_source = ( - template.get_lexical_context_source() if lexical_context else "" - ) # Get parameter types and return type for the prompt param_types = _get_param_types(ret_type) @@ -616,30 +346,16 @@ def _call(self, template, *args, **kwargs) -> Callable: _format_type_for_annotation(t) for t in param_types ) - # Build the type definitions section if there are custom types - # Escape curly braces in type source code to avoid format string issues - type_defs_section = "" - if type_context: - escaped_type_context = type_context.replace("{", "{{").replace("}", "}}") - type_defs_section = f""" -The following types are available: - -```python -{escaped_type_context} -``` -""" - - # Build the lexical context section (helper functions and types) - lexical_context_section = "" - if lexical_context_source: - escaped_lexical_context = lexical_context_source.replace("{", "{{").replace( - "}", "}}" - ) - lexical_context_section = f""" -The following helper functions and types are available for you to use: + # Include the full lexical context - all functions, types, values available to synthesized code + context_section = "" + if lexical_context: + context_source = template.get_lexical_context_source() + escaped_context = context_source.replace("{", "{{").replace("}", "}}") + context_section = f""" +The following types, functions, and values are available: ```python -{escaped_lexical_context} +{escaped_context} ``` """ @@ -651,7 +367,7 @@ def _call(self, template, *args, **kwargs) -> Callable: **Required signature for the main function:** - Parameter types (in order): {param_types_str} - Return type: {return_type_str} - {type_defs_section}{lexical_context_section} + {context_section} **Instructions:** 1. Write a complete Python module with the main function and any helper functions/classes you need. 2. Choose a descriptive name for the main function. @@ -660,7 +376,7 @@ def _call(self, template, *args, **kwargs) -> Callable: 5. Do not redefine any of the provided types - they are already imported. 6. Do not include import statements - all necessary types and helpers are pre-imported. 7. You may define additional helper functions, classes, or constants in the module. - 8. You may use any of the helper functions and types provided above. + 8. You may use any of the helper functions and types from the context above. """).strip() # Use structured output - the LLM returns JSON with function_name and module_code @@ -676,7 +392,5 @@ def _call(self, template, *args, **kwargs) -> Callable: **kwargs, ) - # Build and return the function using imports instead of source injection - return self._build_function( - response, ret_type, referenced_types, lexical_context - ) + # Build and return the function using lexical context for exec globals + return self._build_function(response, ret_type, lexical_context) diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index 894a36bb..c32e9124 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -1,5 +1,3 @@ -import inspect -import textwrap from collections.abc import Callable from dataclasses import dataclass @@ -10,8 +8,6 @@ ProgramSynthesis, SynthesisError, SynthesizedModule, - collect_type_sources, - format_type_context, ) from effectful.ops.semantics import handler from effectful.ops.syntax import ObjectInterpretation, implements @@ -144,21 +140,6 @@ def count_occurrences(text: str) -> int: assert count_a("cherry") == 0 -def test_collect_type_sources(): - """Test the collect_type_sources function.""" - type_sources = collect_type_sources(Person) - assert type_sources == {Person: inspect.getsource(Person)} - - -def test_format_type_context(): - """Test the format_type_context function.""" - type_sources = {Person: inspect.getsource(Person)} - assert ( - format_type_context(type_sources) - == textwrap.dedent(inspect.getsource(Person)).strip() - ) - - def test_count_char_with_typed_body(): """Test program synthesis constructs function with correct prescribed types.""" # LLM can use any types, we re-format with prescribed types @@ -307,7 +288,7 @@ def count_and_double(text): def test_program_synthesis_lexical_function_in_prompt(): """Test that lexical functions are included in the template's context.""" # Verify the template captured the lexical function - assert "double_count" in make_double_counter.lexical_functions - source, func = make_double_counter.lexical_functions["double_count"] + assert "double_count" in make_double_counter.lexical_context + source, func = make_double_counter.lexical_context["double_count"] assert "Count occurrences of a character and double it" in source assert func is double_count From 362e773a9d3cee495dd948b7fd91846f751681f4 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 9 Dec 2025 10:23:22 -0500 Subject: [PATCH 24/65] Bring constrained decoding back to synthesized function --- effectful/handlers/llm/synthesis.py | 122 ++++++++++------------------ tests/test_handlers_llm.py | 95 +++++++++++----------- 2 files changed, 92 insertions(+), 125 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 34384f7f..beacce71 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,4 +1,3 @@ -import ast import collections import collections.abc import dataclasses @@ -27,20 +26,28 @@ def __init__(self, message, code=None): self.code = code -class SynthesizedModule(pydantic.BaseModel): +class SynthesizedFunction(pydantic.BaseModel): """Structured output for function synthesis. - The LLM provides a complete Python module and the name of the main function. - We extract the function, re-format it with prescribed types, and verify with mypy. + LLM provides helper code freely, but main function structure is constrained. + We reconstruct the main function with prescribed types from the signature. """ + helper_code: str = Field( + default="", + description="Optional helper functions, classes, or constants (no imports needed), will be executed before the main function", + ) function_name: str = Field( ..., - description="The name of the main function that satisfies the specification", + description="The name of the main function", + ) + param_names: list[str] = Field( + ..., + description="Parameter names in order (must match number of parameter types)", ) - module_code: str = Field( + body: str = Field( ..., - description="Complete Python module code including the function and any helpers", + description="The indented function body including return statement", ) @@ -212,17 +219,16 @@ def __init__(self, type_check: bool = False): def _build_function( self, - result: SynthesizedModule, + result: SynthesizedFunction, callable_type: type, lexical_context: dict[str, tuple[str, Any]], ) -> typing.Callable: - """Build and execute a function from the synthesized module. + """Build and execute a function from the structured synthesis result. - Extracts the function from LLM's module, re-formats with prescribed signature, - and optionally verifies with mypy. + Combines helper code with a reconstructed main function using prescribed types. Args: - result: The structured output from the LLM containing module code + result: The structured output from the LLM callable_type: The expected Callable type (e.g., Callable[[str], int]) lexical_context: Dict of lexical context (functions/types) available in scope @@ -230,62 +236,21 @@ def _build_function( The synthesized callable function """ func_name = result.function_name - original_module_code = textwrap.dedent(result.module_code).strip() - - # Parse the module to extract function body and helpers - try: - tree = ast.parse(original_module_code) - except SyntaxError as exc: - raise SynthesisError( - f"Syntax error in generated code: {exc}", original_module_code - ) from exc - # Find the target function and separate helpers - target_func_node = None - helper_nodes = [] - for node in tree.body: - if isinstance(node, ast.FunctionDef) and node.name == func_name: - target_func_node = node - else: - helper_nodes.append(node) + # Ensure body is properly indented + body = result.body + if body and not body.startswith(" ") and not body.startswith("\t"): + body = textwrap.indent(body, " ") - if target_func_node is None: - raise SynthesisError( - f"Function '{func_name}' not found in generated module.", - original_module_code, - ) - - # Extract function body using AST - the body starts at first statement's line - all_lines = original_module_code.splitlines() - - # The body starts at the first statement in the function - if target_func_node.body: - body_start_line = target_func_node.body[0].lineno - 1 - body_end_line = target_func_node.end_lineno - body_lines = all_lines[body_start_line:body_end_line] - body = "\n".join(body_lines) - else: - # Empty function body (just pass or docstring) - body = " pass" - - # Get parameter names from the original function - param_names = [arg.arg for arg in target_func_node.args.args] - - # Build helper code from non-function-def nodes - helper_code = "" - if helper_nodes: - helper_parts = [] - for node in helper_nodes: - start = node.lineno - 1 - end = node.end_lineno - helper_parts.append("\n".join(all_lines[start:end])) - helper_code = "\n\n".join(helper_parts) + "\n\n" - - # Construct the function with PRESCRIBED types and extracted param names - param_sig = _format_param_signature(callable_type, param_names) + param_sig = _format_param_signature(callable_type, result.param_names) return_type = _format_return_type(callable_type) func_code = f"def {func_name}({param_sig}) -> {return_type}:\n{body}" - module_code = helper_code + func_code + + helper_code = textwrap.dedent(result.helper_code).strip() + if helper_code: + module_code = helper_code + "\n\n" + func_code + else: + module_code = func_code # Register in linecache for better tracebacks lines = module_code.splitlines(keepends=True) @@ -306,10 +271,12 @@ def _build_function( try: code_obj = compile(module_code, filename, "exec") exec(code_obj, gs) - except Exception as exc: + except SyntaxError as exc: raise SynthesisError( - f"evaluation failed: {exc!r}, source code: {module_code}", module_code + f"Syntax error in generated code: {exc}", module_code ) from exc + except Exception as exc: + raise SynthesisError(f"Evaluation failed: {exc!r}", module_code) from exc if func_name not in gs: raise SynthesisError( @@ -360,32 +327,29 @@ def _call(self, template, *args, **kwargs) -> Callable: """ prompt_ext = textwrap.dedent(f""" - Implement a Python module containing a function with the following specification. + Implement a Python function with the following specification. **Specification:** {template.__prompt_template__} - **Required signature for the main function:** + **Required signature:** - Parameter types (in order): {param_types_str} - Return type: {return_type_str} {context_section} **Instructions:** - 1. Write a complete Python module with the main function and any helper functions/classes you need. - 2. Choose a descriptive name for the main function. - 3. Choose descriptive parameter names (one for each parameter type). - 4. The main function's parameter types and return type must match exactly as specified above. - 5. Do not redefine any of the provided types - they are already imported. - 6. Do not include import statements - all necessary types and helpers are pre-imported. - 7. You may define additional helper functions, classes, or constants in the module. - 8. You may use any of the helper functions and types from the context above. + 1. Choose a descriptive function name. + 2. Choose descriptive parameter names (one for each parameter type). + 3. Implement the function body with a return statement. + 4. If needed, include helper functions/classes/constants in helper_code. + 5. Do not redefine provided types - they are already available. + 6. Do not include import statements. """).strip() - # Use structured output - the LLM returns JSON with function_name and module_code - response: SynthesizedModule = fwd( + response: SynthesizedFunction = fwd( dataclasses.replace( template, __prompt_template__=prompt_ext, __signature__=template.__signature__.replace( - return_annotation=SynthesizedModule + return_annotation=SynthesizedFunction ), ), *args, diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index c32e9124..48711b59 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -7,7 +7,7 @@ from effectful.handlers.llm.synthesis import ( ProgramSynthesis, SynthesisError, - SynthesizedModule, + SynthesizedFunction, ) from effectful.ops.semantics import handler from effectful.ops.syntax import ObjectInterpretation, implements @@ -123,13 +123,10 @@ def test_primes_decode_int(): def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" - # LLM provides full module, we extract and re-format with prescribed types - mock_response = SynthesizedModule( + mock_response = SynthesizedFunction( function_name="count_occurrences", - module_code=""" -def count_occurrences(text: str) -> int: - return text.count('a') -""", + param_names=["text"], + body=" return text.count('a')", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -142,13 +139,10 @@ def count_occurrences(text: str) -> int: def test_count_char_with_typed_body(): """Test program synthesis constructs function with correct prescribed types.""" - # LLM can use any types, we re-format with prescribed types - mock_response = SynthesizedModule( + mock_response = SynthesizedFunction( function_name="count_chars", - module_code=""" -def count_chars(s): # LLM might not use correct types - return s.count('x') -""", + param_names=["s"], + body=" return s.count('x')", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -162,12 +156,10 @@ def count_chars(s): # LLM might not use correct types def test_make_greeter_with_program_synthesis(): """Test program synthesis with custom type (Person) in the signature.""" - mock_response = SynthesizedModule( + mock_response = SynthesizedFunction( function_name="greet_person", - module_code=""" -def greet_person(person: Person) -> str: - return f"Hello, {person.name}!" -""", + param_names=["person"], + body=' return f"Hello, {person.name}!"', ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -180,13 +172,11 @@ def greet_person(person: Person) -> str: def test_program_synthesis_invalid_body(): - """Test that synthesis fails when module has syntax errors.""" - mock_response = SynthesizedModule( + """Test that synthesis fails when body has syntax errors.""" + mock_response = SynthesizedFunction( function_name="bad_func", - module_code=""" -def bad_func(x: str) -> int: - return this is not valid python -""", + param_names=["x"], + body=" return this is not valid python", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -196,13 +186,11 @@ def bad_func(x: str) -> int: def test_program_synthesis_runtime_error(): - """Test that synthesis fails when module raises runtime error when called.""" - mock_response = SynthesizedModule( + """Test that synthesis fails when body raises runtime error when called.""" + mock_response = SynthesizedFunction( function_name="bad_func", - module_code=""" -def bad_func(x: str) -> int: - return undefined_variable -""", + param_names=["x"], + body=" return undefined_variable", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -216,12 +204,10 @@ def bad_func(x: str) -> int: def test_program_synthesis_with_type_check(): """Test program synthesis with optional mypy type checking enabled.""" - mock_response = SynthesizedModule( + mock_response = SynthesizedFunction( function_name="count_chars", - module_code=""" -def count_chars(text: str) -> int: - return text.count('a') -""", + param_names=["text"], + body=" return text.count('a')", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -235,12 +221,10 @@ def count_chars(text: str) -> int: def test_program_synthesis_type_check_catches_body_errors(): """Test that type checking catches type errors in the function body.""" # Body returns wrong type (str instead of int) - mypy will catch this - mock_response = SynthesizedModule( + mock_response = SynthesizedFunction( function_name="bad_return", - module_code=""" -def bad_return(text: str) -> int: - return "not an int" -""", + param_names=["text"], + body=' return "not an int"', ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -266,13 +250,10 @@ def make_double_counter(char: str) -> Callable[[str], int]: def test_program_synthesis_with_lexical_function(): """Test that synthesized code can use functions from the lexical scope.""" # The synthesized code uses the double_count helper from lexical scope - mock_response = SynthesizedModule( + mock_response = SynthesizedFunction( function_name="count_and_double", - module_code=""" -def count_and_double(text): - # Uses double_count from lexical scope - return double_count(text, 'a') -""", + param_names=["text"], + body=" return double_count(text, 'a')", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -292,3 +273,25 @@ def test_program_synthesis_lexical_function_in_prompt(): source, func = make_double_counter.lexical_context["double_count"] assert "Count occurrences of a character and double it" in source assert func is double_count + + +def test_program_synthesis_with_helper_code(): + """Test that helper_code is included and executed before the main function.""" + mock_response = SynthesizedFunction( + helper_code=""" +def multiply_by_three(n: int) -> int: + return n * 3 +""", + function_name="count_and_triple", + param_names=["text"], + body=" return multiply_by_three(text.count('a'))", + ) + mock_provider = SingleResponseLLMProvider(mock_response) + + with handler(mock_provider), handler(ProgramSynthesis()): + counter = count_char("a") + assert callable(counter) + # "banana" has 3 'a's, tripled = 9 + assert counter("banana") == 9 + # "aardvark" has 3 'a's, tripled = 9 + assert counter("aardvark") == 9 From 81a993bfa359e990cb6833206583d262b03aebd3 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 9 Dec 2025 11:28:18 -0500 Subject: [PATCH 25/65] Remove redundant format signature and rely on mypy to check for type consistency --- docs/source/llm.ipynb | 133 ++++++++++----------- effectful/handlers/llm/synthesis.py | 173 ++++------------------------ tests/test_handlers_llm.py | 94 ++++++++------- 3 files changed, 132 insertions(+), 268 deletions(-) diff --git a/docs/source/llm.ipynb b/docs/source/llm.ipynb index 19a062c0..06f129c7 100644 --- a/docs/source/llm.ipynb +++ b/docs/source/llm.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 12, "id": "5aaf649f", "metadata": {}, "outputs": [], @@ -55,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 13, "id": "1e832675", "metadata": {}, "outputs": [], @@ -78,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 14, "id": "634f6533", "metadata": {}, "outputs": [ @@ -86,17 +86,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "In the deep sea where fish like to play, \n", - "A school of bright scales swim all day. \n", - "With a flick and a swish, \n", - "They dance like a wish, \n", - "In the ocean's grand cabaret.\n", + "There once was a fish in the sea, \n", + "Who dreamed of a life fancy-free. \n", + "With a flip and a splash, \n", + "It made quite a dash, \n", + "And danced with the waves full of glee.\n", "----------------------------------------\n", - "In a pond where the cool waters swish, \n", - "Lived a carp with a whimsical wish. \n", - "He dreamt of the sea, \n", - "Where he'd swim wild and free, \n", - "In a world that's unfathomably fish!\n" + "In the sea, a fish took a dive, \n", + "Through waters it felt so alive. \n", + "With scales that did shimmer, \n", + "It danced with a glimmer, \n", + "In its oceanic jive, it would thrive.\n" ] } ], @@ -117,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 15, "id": "706ce53b", "metadata": {}, "outputs": [ @@ -126,31 +126,29 @@ "output_type": "stream", "text": [ "\n", - "Glistening scales shine, \n", - "Dancing through the azure waves, \n", - "Whispers of the deep.\n", + "Silver scales glisten, \n", + "Beneath the rippling waves' light, \n", + "Silent currents dance. \n", "----------------------------------------\n", - "Glistening scales shine, \n", - "Dancing through the azure waves, \n", - "Whispers of the deep.\n", + "Silver scales glisten, \n", + "Beneath the rippling waves' light, \n", + "Silent currents dance. \n", "\n", "Silver scales shimmer, \n", - "In the cool, deep blue they glide— \n", - "Silent water dance. \n", + "Silent currents' gentle dance, \n", + "Depths of azure dreams.\n", "----------------------------------------\n", "Silver scales shimmer, \n", - "In the cool, deep blue they glide— \n", - "Silent water dance. \n", - "\n", - "Silver scales shimmer, \n", - "Underneath the waves they glide, \n", - "Silent streams of dreams. \n", + "Silent currents' gentle dance, \n", + "Depths of azure dreams.\n", "\n", + "Silent waters glide, \n", + "Stars flicker on scaled mirrors— \n", + "Fish weave through moonlight.\n", "----------------------------------------\n", - "Silver scales shimmer, \n", - "Underneath the waves they glide, \n", - "Silent streams of dreams. \n", - "\n" + "Silent waters glide, \n", + "Stars flicker on scaled mirrors— \n", + "Fish weave through moonlight.\n" ] } ], @@ -201,7 +199,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 16, "id": "2c766859", "metadata": {}, "outputs": [], @@ -226,7 +224,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 17, "id": "c83bbdc0", "metadata": {}, "outputs": [ @@ -234,14 +232,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "def count_a_occurrences(input_string: str) -> int:\n", - " \"\"\"\n", - " Counts the occurrences of the letter 'a' in the given string.\n", - "\n", - " :param input_string: The string to be searched.\n", - " :return: The number of times 'a' appears in the string.\n", - " \"\"\"\n", - " return input_string.count('a')\n" + "def count_a(s: str) -> int:\n", + " return s.count('a')\n" ] } ], @@ -271,7 +263,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 23, "id": "55a08ac2", "metadata": {}, "outputs": [ @@ -279,36 +271,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "Receipt:\n", - "----------------------------------------\n", - "Coffee x2 @ $4.50 each: $9.00\n", - "Sandwich x1 @ $8.99 each: $8.99\n", - "Cookie x3 @ $2.25 each: $6.75\n", - "----------------------------------------\n", + "Coffee x2: $9.00\n", + "Sandwich x1: $8.99\n", + "Cookie x3: $6.75\n", + "-\n", "Total: $24.74\n", "\n", "--- Generated function source ---\n", - "def format_receipt(products: list) -> str:\n", - " \"\"\"\n", - " Format a list of products as a receipt.\n", - "\n", - " :param products: A list of Product objects.\n", - " :return: A formatted receipt as a string.\n", - " \"\"\"\n", + "def format_receipt(products):\n", + " \"\"\"Format a list of products as a receipt, calculating the total.\"\"\"\n", " receipt_lines = []\n", - " total_amount = 0.0\n", - " receipt_lines.append(\"Receipt:\")\n", - " receipt_lines.append(\"----------------------------------------\")\n", + " total = 0.0\n", "\n", - " # Iterate over products to create receipt lines\n", " for product in products:\n", " line_total = product.price * product.quantity\n", - " total_amount += line_total\n", - " receipt_lines.append(f\"{product.name} x{product.quantity} @ {format_currency(product.price)} each: {format_currency(line_total)}\")\n", + " total += line_total\n", + " receipt_lines.append(f\"{product.name} x{product.quantity}: {format_currency(line_total)}\")\n", "\n", - " receipt_lines.append(\"----------------------------------------\")\n", - " # Adding the total amount\n", - " receipt_lines.append(f\"Total: {format_currency(total_amount)}\")\n", + " receipt_lines.append(\"-\")\n", + " receipt_lines.append(f\"Total: {format_currency(total)}\")\n", "\n", " return \"\\n\".join(receipt_lines)\n" ] @@ -366,7 +347,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 19, "id": "66711301", "metadata": {}, "outputs": [ @@ -378,7 +359,7 @@ "Tool call: weather(*(), **{'city': 'Chicago'}) -> cold\n", "Tool call: weather(*(), **{'city': 'New York'}) -> wet\n", "Tool call: weather(*(), **{'city': 'Barcelona'}) -> sunny\n", - "Based on the current weather conditions, Barcelona has good weather, as it is sunny.\n" + "I suggest Barcelona, as it's currently experiencing sunny weather.\n" ] } ], @@ -422,7 +403,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 20, "id": "17668ac8", "metadata": {}, "outputs": [ @@ -435,7 +416,7 @@ "Who's there?\n", "Iguana.\n", "Iguana who?\n", - "Iguana be your friend, but I'm too busy basking in the sun!\n", + "Iguana see you laugh at this joke!\n", "> The crowd laughs politely.\n" ] } @@ -486,7 +467,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 21, "id": "cbf495a2", "metadata": {}, "outputs": [ @@ -494,8 +475,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish2.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkdYT7IPx1Q5ExG610ZEYSik1ImTF', created=1765231173, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the depths they glide, \\nSilent scales in sunlit dance, \\nWhere the waters hide. ', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=22, prompt_tokens=34, total_tokens=56, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n", - "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkdYUNHH3udvmHLdijDeaSsLvcTi9', created=1765231174, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"A fish from the blue ocean deep, \\nSwam up where the little waves leap. \\nHe danced with delight, \\nIn the sun's golden light, \\nThen dived back for a long, dreamy sleep. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=46, prompt_tokens=34, total_tokens=80, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n" + "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish2.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-CkulmuIOxeFwBSPsXa4XoKxkMHZfM', created=1765297346, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"In clear waters glide, \\nFish dance with the silver tide, \\nNature's grace displayed. \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=21, prompt_tokens=34, total_tokens=55, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n", + "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish.'}], 'role': 'user'}], 'response_format': None, 'tools': []} ModelResponse(id='chatcmpl-Ckulnzrzz7DPbKTXHFiRbRHRLd8cj', created=1765297347, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"In the ocean, where cool waters swish, \\nSwam a trout with one unusual wish. \\nHe'd jump and he'd dive, \\nIn the sea, he'd jive, \\nDreaming one day he'd become a big dish!\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=49, prompt_tokens=34, total_tokens=83, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n" ] } ], @@ -529,7 +510,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 22, "id": "81a15f00", "metadata": {}, "outputs": [ @@ -537,8 +518,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkdYWrh2iVIJJFHKXL6yRwcHU6vu1', created=1765231176, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Swimming in clear streams, \\nGraceful scales in sunlit dance, \\nWhispers of the deep.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=22, prompt_tokens=34, total_tokens=56, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish4.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkdYXpWEqJ3jmd9RNaUo4RHdAs2Ic', created=1765231177, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the ocean where fish explore, \\nSwam a fish named Finn by the shore. \\nHe said with a swish, \\n\"I wish I were a dish,\" \\nBut found his dreams were quite a bore. ', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=46, prompt_tokens=35, total_tokens=81, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n" + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkulpNRsjCfUrGQynAXzDqbtxISZG', created=1765297349, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Glimmering scales shine, \\nSilent beneath the water, \\nDancing with the tide.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=21, prompt_tokens=34, total_tokens=55, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkulpNRsjCfUrGQynAXzDqbtxISZG', created=1765297349, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Glimmering scales shine, \\nSilent beneath the water, \\nDancing with the tide.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=21, prompt_tokens=34, total_tokens=55, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish4.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkulqYZYYbO4Ml4EOA3EkeQhYdUty', created=1765297350, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"In the deep ocean where fish thrive, \\nA skittish shark took a long dive. \\nHe met a bright clown, \\nWore scales like a gown, \\nTogether they'd swim and jive! \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=44, prompt_tokens=35, total_tokens=79, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish4.'}], 'role': 'user'}], 'response_format': None, 'tools': []}, 'response': ModelResponse(id='chatcmpl-CkulqYZYYbO4Ml4EOA3EkeQhYdUty', created=1765297350, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"In the deep ocean where fish thrive, \\nA skittish shark took a long dive. \\nHe met a bright clown, \\nWore scales like a gown, \\nTogether they'd swim and jive! \", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=44, prompt_tokens=35, total_tokens=79, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n" ] } ], diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index beacce71..714678de 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -7,7 +7,7 @@ import textwrap import typing from collections.abc import Callable -from typing import Any, get_args, get_origin +from typing import Any import pydantic from mypy import api as mypy_api @@ -29,25 +29,17 @@ def __init__(self, message, code=None): class SynthesizedFunction(pydantic.BaseModel): """Structured output for function synthesis. - LLM provides helper code freely, but main function structure is constrained. - We reconstruct the main function with prescribed types from the signature. + LLM provides a complete module and the name of the main function. + We add a type assertion to verify the function matches the expected signature. """ - helper_code: str = Field( - default="", - description="Optional helper functions, classes, or constants (no imports needed), will be executed before the main function", - ) function_name: str = Field( ..., - description="The name of the main function", - ) - param_names: list[str] = Field( - ..., - description="Parameter names in order (must match number of parameter types)", + description="The name of the main function that satisfies the specification", ) - body: str = Field( + module_code: str = Field( ..., - description="The indented function body including return statement", + description="Complete Python module code (no imports needed)", ) @@ -67,99 +59,6 @@ def _get_imports_from_lexical_context( return imports -def _format_type_for_annotation(t: type) -> str: - """Format a type for use in a type annotation string. - - Handles Callable types from collections.abc and typing module. - """ - origin = get_origin(t) - - if origin is not None: - # handle generic types like Callable[[X], Y], list[X], etc. - args = get_args(t) - - # get the origin name - handle collections.abc.Callable -> Callable - if hasattr(origin, "__name__"): - origin_name = origin.__name__ - else: - origin_name = str(origin).split(".")[-1] - - if origin_name == "Callable" and args: - # Format as Callable[[P1, P2], R] - param_types = args[0] - return_type = args[-1] - - if param_types is ...: - params_str = "..." - else: - params_str = ( - "[" - + ", ".join(_format_type_for_annotation(p) for p in param_types) - + "]" - ) - - ret_str = _format_type_for_annotation(return_type) - return f"Callable[{params_str}, {ret_str}]" - else: - # Generic type like list[X], dict[K, V] - args_str = ", ".join(_format_type_for_annotation(a) for a in args) - return f"{origin_name}[{args_str}]" - - # Simple type - if hasattr(t, "__name__"): - return t.__name__ - return str(t) - - -def _get_param_types(callable_type: type) -> list[type] | None: - """Extract parameter types from a Callable type. - - Returns None if the callable uses ellipsis (...) for params. - """ - args = get_args(callable_type) - if not args: - return [] - - param_types = args[0] - if param_types is ...: - return None - - return list(param_types) - - -def _format_param_signature( - callable_type: type, param_names: list[str] | None = None -) -> str: - """Format the parameter signature from a Callable type. - - E.g., Callable[[str, int], bool] with names ["text", "count"] - -> "text: str, count: int" - """ - param_types = _get_param_types(callable_type) - if param_types is None: - return "*args, **kwargs" - if not param_types: - return "" - - params = [] - for i, param_type in enumerate(param_types): - type_str = _format_type_for_annotation(param_type) - name = param_names[i] if param_names and i < len(param_names) else f"arg{i}" - params.append(f"{name}: {type_str}") - - return ", ".join(params) - - -def _format_return_type(callable_type: type) -> str: - """Extract and format the return type from a Callable type.""" - args = get_args(callable_type) - if not args: - return "Any" - - return_type = args[-1] - return _format_type_for_annotation(return_type) - - def run_mypy_check( code: str, lexical_context: dict[str, tuple[str, Any]], @@ -173,7 +72,9 @@ def run_mypy_check( Returns: A tuple of (success: bool, error_message: str) """ - imports = _get_imports_from_lexical_context(lexical_context) + # Always include collections.abc for Callable type assertions + imports = ["import collections.abc"] + imports.extend(_get_imports_from_lexical_context(lexical_context)) source_parts = imports + [textwrap.dedent(code).strip()] full_source = "\n".join(source_parts) @@ -223,9 +124,10 @@ def _build_function( callable_type: type, lexical_context: dict[str, tuple[str, Any]], ) -> typing.Callable: - """Build and execute a function from the structured synthesis result. + """Build and execute a function from the synthesized module. - Combines helper code with a reconstructed main function using prescribed types. + Executes the LLM's module code as-is and optionally verifies + the function matches the expected type using a type assertion. Args: result: The structured output from the LLM @@ -236,30 +138,20 @@ def _build_function( The synthesized callable function """ func_name = result.function_name + module_code = textwrap.dedent(result.module_code).strip() - # Ensure body is properly indented - body = result.body - if body and not body.startswith(" ") and not body.startswith("\t"): - body = textwrap.indent(body, " ") - - param_sig = _format_param_signature(callable_type, result.param_names) - return_type = _format_return_type(callable_type) - func_code = f"def {func_name}({param_sig}) -> {return_type}:\n{body}" - - helper_code = textwrap.dedent(result.helper_code).strip() - if helper_code: - module_code = helper_code + "\n\n" + func_code - else: - module_code = func_code + # Add type assertion for mypy checking (use repr for the type) + type_assertion = f"\n\n_: {repr(callable_type)} = {func_name}" + code_with_assertion = module_code + type_assertion # Register in linecache for better tracebacks lines = module_code.splitlines(keepends=True) filename = f"" linecache.cache[filename] = (len(module_code), None, lines, filename) - # Optional mypy type checking - uses lexical context for imports + # Optional mypy type checking with type assertion if self.type_check: - success, error_msg = run_mypy_check(module_code, lexical_context) + success, error_msg = run_mypy_check(code_with_assertion, lexical_context) if not success: raise SynthesisError(f"Type check failed:\n{error_msg}", module_code) @@ -299,20 +191,6 @@ def _call(self, template, *args, **kwargs) -> Callable: # Get lexical context - contains all functions, types, and values from definition site lexical_context = getattr(template, "lexical_context", {}) - # Get parameter types and return type for the prompt - param_types = _get_param_types(ret_type) - return_type_str = _format_return_type(ret_type) - - # Format parameter types for display - if param_types is None: - param_types_str = "*args, **kwargs" - elif not param_types: - param_types_str = "(no parameters)" - else: - param_types_str = ", ".join( - _format_type_for_annotation(t) for t in param_types - ) - # Include the full lexical context - all functions, types, values available to synthesized code context_section = "" if lexical_context: @@ -331,17 +209,14 @@ def _call(self, template, *args, **kwargs) -> Callable: **Specification:** {template.__prompt_template__} - **Required signature:** - - Parameter types (in order): {param_types_str} - - Return type: {return_type_str} + **Required signature:** {repr(ret_type)} {context_section} **Instructions:** - 1. Choose a descriptive function name. - 2. Choose descriptive parameter names (one for each parameter type). - 3. Implement the function body with a return statement. - 4. If needed, include helper functions/classes/constants in helper_code. - 5. Do not redefine provided types - they are already available. - 6. Do not include import statements. + 1. Write a complete Python module with the function. + 2. Choose descriptive function and parameter names. + 3. You may include helper functions/classes/constants. + 4. Do not redefine provided types - they are already available. + 5. Do not include import statements. """).strip() response: SynthesizedFunction = fwd( diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index 48711b59..8db8923a 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -125,8 +125,10 @@ def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" mock_response = SynthesizedFunction( function_name="count_occurrences", - param_names=["text"], - body=" return text.count('a')", + module_code=""" +def count_occurrences(text: str) -> int: + return text.count('a') +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -137,19 +139,20 @@ def test_count_char_with_program_synthesis(): assert count_a("cherry") == 0 -def test_count_char_with_typed_body(): - """Test program synthesis constructs function with correct prescribed types.""" +def test_count_char_with_untyped_function(): + """Test program synthesis works even when LLM omits type annotations.""" mock_response = SynthesizedFunction( function_name="count_chars", - param_names=["s"], - body=" return s.count('x')", + module_code=""" +def count_chars(s): + return s.count('x') +""", ) mock_provider = SingleResponseLLMProvider(mock_response) with handler(mock_provider), handler(ProgramSynthesis()): count_x = count_char("x") assert callable(count_x) - # Verify the function works assert count_x("xylophone") == 1 assert count_x("xxx") == 3 @@ -158,25 +161,28 @@ def test_make_greeter_with_program_synthesis(): """Test program synthesis with custom type (Person) in the signature.""" mock_response = SynthesizedFunction( function_name="greet_person", - param_names=["person"], - body=' return f"Hello, {person.name}!"', + module_code=""" +def greet_person(person: Person) -> str: + return f"Hello, {person.name}!" +""", ) mock_provider = SingleResponseLLMProvider(mock_response) with handler(mock_provider), handler(ProgramSynthesis()): greeter = make_greeter("formal") assert callable(greeter) - # Test the generated function works with the custom type person = Person(name="Alice", age=30) assert greeter(person) == "Hello, Alice!" -def test_program_synthesis_invalid_body(): - """Test that synthesis fails when body has syntax errors.""" +def test_program_synthesis_invalid_code(): + """Test that synthesis fails when module has syntax errors.""" mock_response = SynthesizedFunction( function_name="bad_func", - param_names=["x"], - body=" return this is not valid python", + module_code=""" +def bad_func(x): + return this is not valid python +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -186,45 +192,48 @@ def test_program_synthesis_invalid_body(): def test_program_synthesis_runtime_error(): - """Test that synthesis fails when body raises runtime error when called.""" + """Test that runtime errors propagate when calling the function.""" mock_response = SynthesizedFunction( function_name="bad_func", - param_names=["x"], - body=" return undefined_variable", + module_code=""" +def bad_func(x): + return undefined_variable +""", ) mock_provider = SingleResponseLLMProvider(mock_response) - # This should compile fine but fail at runtime when called with handler(mock_provider), handler(ProgramSynthesis()): func = count_char("a") - # The function is created, but calling it will fail with pytest.raises(NameError): func("test") def test_program_synthesis_with_type_check(): - """Test program synthesis with optional mypy type checking enabled.""" + """Test program synthesis with mypy type checking via type assertion.""" mock_response = SynthesizedFunction( function_name="count_chars", - param_names=["text"], - body=" return text.count('a')", + module_code=""" +def count_chars(text: str) -> int: + return text.count('a') +""", ) mock_provider = SingleResponseLLMProvider(mock_response) - # With type_check=True, mypy verifies the generated code with handler(mock_provider), handler(ProgramSynthesis(type_check=True)): count_a = count_char("a") assert callable(count_a) assert count_a("banana") == 3 -def test_program_synthesis_type_check_catches_body_errors(): - """Test that type checking catches type errors in the function body.""" - # Body returns wrong type (str instead of int) - mypy will catch this +def test_program_synthesis_type_check_catches_signature_mismatch(): + """Test that type checking catches when function signature doesn't match.""" + # Function returns str but expected int - type assertion will fail mock_response = SynthesizedFunction( function_name="bad_return", - param_names=["text"], - body=' return "not an int"', + module_code=""" +def bad_return(text: str) -> str: + return "not an int" +""", ) mock_provider = SingleResponseLLMProvider(mock_response) @@ -249,49 +258,46 @@ def make_double_counter(char: str) -> Callable[[str], int]: def test_program_synthesis_with_lexical_function(): """Test that synthesized code can use functions from the lexical scope.""" - # The synthesized code uses the double_count helper from lexical scope mock_response = SynthesizedFunction( function_name="count_and_double", - param_names=["text"], - body=" return double_count(text, 'a')", + module_code=""" +def count_and_double(text: str) -> int: + return double_count(text, 'a') +""", ) mock_provider = SingleResponseLLMProvider(mock_response) with handler(mock_provider), handler(ProgramSynthesis()): counter = make_double_counter("a") assert callable(counter) - # "banana" has 3 'a's, doubled = 6 - assert counter("banana") == 6 - # "cherry" has 0 'a's, doubled = 0 + assert counter("banana") == 6 # 3 'a's doubled assert counter("cherry") == 0 def test_program_synthesis_lexical_function_in_prompt(): """Test that lexical functions are included in the template's context.""" - # Verify the template captured the lexical function assert "double_count" in make_double_counter.lexical_context source, func = make_double_counter.lexical_context["double_count"] assert "Count occurrences of a character and double it" in source assert func is double_count -def test_program_synthesis_with_helper_code(): - """Test that helper_code is included and executed before the main function.""" +def test_program_synthesis_with_helper_in_module(): + """Test that module can include helper functions.""" mock_response = SynthesizedFunction( - helper_code=""" + function_name="count_and_triple", + module_code=""" def multiply_by_three(n: int) -> int: return n * 3 + +def count_and_triple(text: str) -> int: + return multiply_by_three(text.count('a')) """, - function_name="count_and_triple", - param_names=["text"], - body=" return multiply_by_three(text.count('a'))", ) mock_provider = SingleResponseLLMProvider(mock_response) with handler(mock_provider), handler(ProgramSynthesis()): counter = count_char("a") assert callable(counter) - # "banana" has 3 'a's, tripled = 9 - assert counter("banana") == 9 - # "aardvark" has 3 'a's, tripled = 9 + assert counter("banana") == 9 # 3 'a's tripled assert counter("aardvark") == 9 From a2ada49e78e7733d0281011620517d5820002723 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 15 Dec 2025 17:20:12 -0500 Subject: [PATCH 26/65] Revert change --- effectful/handlers/llm/__init__.py | 155 ++++++++++++++--------------- 1 file changed, 73 insertions(+), 82 deletions(-) diff --git a/effectful/handlers/llm/__init__.py b/effectful/handlers/llm/__init__.py index 2976cb23..fbcb8593 100644 --- a/effectful/handlers/llm/__init__.py +++ b/effectful/handlers/llm/__init__.py @@ -1,90 +1,70 @@ +from __future__ import annotations + import dataclasses import functools import inspect -import textwrap import types -from collections.abc import Callable, Iterable +from collections import ChainMap +from collections.abc import Callable, Iterable, Mapping from typing import Any +from effectful.ops.semantics import evaluate from effectful.ops.syntax import defop from effectful.ops.types import NotHandled, Operation -def _collect_lexical_context(frame) -> dict[str, tuple[str, Any]]: - """Collect all symbols from the caller's lexical context. +class LexicalContext(ChainMap): + """ChainMap subclass for Template lexical scope. - Returns a dict mapping names to (source_code/repr, object) tuples. - Captures everything except: - - Private/dunder names (starting with _) - - Modules + This avoids recursive evaluation of circular Template references. """ - lexical_context = {**frame.f_globals, **frame.f_locals} - - collected: dict[str, tuple[str, Any]] = {} - for name, obj in lexical_context.items(): - source = _get_source_for_object(obj, name) - if source is not None: - collected[name] = (source, obj) - return collected + pass -def _get_source_for_object(obj: Any, name: str) -> str | None: - """Get source code or representation for an object. - - Returns a string representation suitable for including in a prompt. - """ - # For functions, try to get source - if isinstance(obj, types.FunctionType): - try: - return textwrap.dedent(inspect.getsource(obj)).strip() - except (OSError, TypeError): - # Fallback for functions without source (e.g., defined in REPL) - doc = obj.__doc__ or "No docstring" - return f"# \n# {doc}" - - # For classes/types - if isinstance(obj, type): - try: - return textwrap.dedent(inspect.getsource(obj)).strip() - except (OSError, TypeError): - doc = obj.__doc__ or "No docstring" - return f"# \n# {doc}" - - # For generic aliases (list[int], Callable[[str], int], etc.) - if hasattr(obj, "__origin__"): - return f"{name} = {obj}" - - # For callable instances (objects with __call__) - if callable(obj): - obj_type = type(obj) - try: - return textwrap.dedent(inspect.getsource(obj_type)).strip() - except (OSError, TypeError): - doc = getattr(obj, "__doc__", None) or "No docstring" - return f"# \n# {doc}" - - # For dataclass instances, show the instance - if dataclasses.is_dataclass(obj) and not isinstance(obj, type): - return f"{name} = {obj!r}" - - try: - repr_str = repr(obj) - if len(repr_str) > 500: - return f"{name} = <{type(obj).__name__}>" - return f"{name} = {repr_str}" - except Exception: - return f"{name} = <{type(obj).__name__}>" +@evaluate.register(LexicalContext) +def _evaluate_lexical_context(expr: LexicalContext, **kwargs) -> LexicalContext: + return expr @dataclasses.dataclass(frozen=True) class Template[**P, T]: - __signature__: inspect.Signature __prompt_template__: str - tools: tuple[Operation, ...] - lexical_context: dict[str, tuple[str, Any]] = dataclasses.field( - default_factory=dict - ) + __signature__: inspect.Signature + __context__: Mapping[str, Any] + __name__: str + + @staticmethod + def _get_excluded_operations() -> frozenset[Operation]: + """Get the set of internal operations to exclude from auto-capture.""" + from effectful.handlers.llm import providers + from effectful.ops import semantics + + excluded: set[Operation] = set() + for module in (providers, semantics): + for name in dir(module): + obj = getattr(module, name) + if isinstance(obj, Operation): + excluded.add(obj) + return frozenset(excluded) + + @property + def tools(self) -> tuple[Operation | Template, ...]: + """Operations and Templates available as tools. Auto-capture from lexical context.""" + excluded_ops = self._get_excluded_operations() + result: list[Operation | Template] = [] + # ChainMap.items() respects shadowing (locals shadow globals) + for name, obj in self.__context__.items(): + if name.startswith("_") or obj in result: + continue + if isinstance(obj, Operation): + # Exclude internal operations from providers and semantics modules + if obj in excluded_ops: + continue + result.append(obj) + elif isinstance(obj, Template): + result.append(obj) + return tuple(result) @defop def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: @@ -96,29 +76,40 @@ def __get__(self, instance, _owner): else: return self - def get_lexical_context_source(self) -> str: - """Return the source code of all captured lexical symbols.""" - return "\n\n".join(source for source, _ in self.lexical_context.values()) - @classmethod - def define(cls, _func=None, *, tools: Iterable[Operation] = ()): - # Capture caller's frame to collect lexical context - caller_frame = inspect.currentframe() - assert caller_frame is not None - caller_frame = caller_frame.f_back - assert caller_frame is not None - - lexical_ctx = _collect_lexical_context(caller_frame) + def define( + cls, + _func=None, + *, + tools: Iterable[Operation | Template] | str | None = None, + ): + """Define a prompt template. + + Args: + tools: Tools to expose to the LLM: + - None (default): no tools + - "auto": auto-capture from lexical scope + - list: explicit list of Operations/Templates + """ + frame: types.FrameType = inspect.currentframe().f_back # type: ignore + globals_proxy: types.MappingProxyType[str, Any] = types.MappingProxyType( + frame.f_globals + ) + locals_proxy: types.MappingProxyType[str, Any] = types.MappingProxyType( + frame.f_locals + ) + # LexicalContext: locals first (shadow globals), then globals + context = LexicalContext(locals_proxy, globals_proxy) # type: ignore[arg-type] def decorator(body: Callable[P, T]): if not body.__doc__: raise ValueError("Expected a docstring on body") return cls( - __signature__=inspect.signature(body), __prompt_template__=body.__doc__, - tools=tuple(tools), - lexical_context=lexical_ctx, + __signature__=inspect.signature(body), + __name__=body.__name__, + __context__=context, ) if _func is None: From 80d03531cfd9bd10ddabf43640b8e4c03e36f9e2 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:04:15 -0500 Subject: [PATCH 27/65] removing lexical context docstring --- effectful/handlers/llm/providers.py | 4 - effectful/handlers/llm/synthesis.py | 10 ++- tests/test_handlers_llm.py | 116 +++++++++++++--------------- 3 files changed, 62 insertions(+), 68 deletions(-) diff --git a/effectful/handlers/llm/providers.py b/effectful/handlers/llm/providers.py index 97a12c3f..b3fb7e05 100644 --- a/effectful/handlers/llm/providers.py +++ b/effectful/handlers/llm/providers.py @@ -446,10 +446,6 @@ def format_model_input[**P, T]( ) -> list[Any]: """Format a template applied to arguments into a sequence of input messages. - - Lexical context items can be referenced in the prompt template using - {name} syntax. For functions/classes, the source code is inserted. - For other values, the repr is inserted. """ bound_args = template.__signature__.bind(*args, **kwargs) bound_args.apply_defaults() diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 43cde032..0319ebf0 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -113,7 +113,11 @@ def get_lexical_context_source(template: Template) -> str: sig = inspect.signature(obj) doc = obj.__doc__ or "" first_line = doc.split("\n")[0].strip() if doc else "" - source = f"def {name}{sig}: ... # {first_line}" if first_line else f"def {name}{sig}: ..." + source = ( + f"def {name}{sig}: ... # {first_line}" + if first_line + else f"def {name}{sig}: ..." + ) except (ValueError, TypeError): source = f"def {name}(...): ..." @@ -122,7 +126,9 @@ def get_lexical_context_source(template: Template) -> str: try: sig = obj.__signature__ doc = obj.__prompt_template__.split("\n")[0].strip() - source = f"def {name}{sig}: ... # {doc}" if doc else f"def {name}{sig}: ..." + source = ( + f"def {name}{sig}: ... # {doc}" if doc else f"def {name}{sig}: ..." + ) except (ValueError, TypeError, AttributeError): source = f"def {name}(...): ..." diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index fdcee5cb..62f1ec19 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -3,22 +3,16 @@ import pytest -import pytest - from effectful.handlers.llm import Template -<<<<<<< HEAD +from effectful.handlers.llm.providers import RetryLLMHandler from effectful.handlers.llm.synthesis import ( ProgramSynthesis, SynthesisError, SynthesizedFunction, ) from effectful.ops.semantics import handler -======= -from effectful.handlers.llm.providers import RetryLLMHandler -from effectful.handlers.llm.synthesis import ProgramSynthesis -from effectful.ops.semantics import NotHandled, handler ->>>>>>> 931d5071d3f386a224cf46c103ca1905fa3c12df from effectful.ops.syntax import ObjectInterpretation, implements +from effectful.ops.types import NotHandled class MockLLMProvider[T](ObjectInterpretation): @@ -68,6 +62,37 @@ def _call[**P]( return self.response +class FailingThenSucceedingProvider[T](ObjectInterpretation): + """Mock provider that fails a specified number of times before succeeding.""" + + def __init__( + self, + fail_count: int, + success_response: T, + exception_factory: Callable[[], Exception], + ): + """Initialize the provider. + + Args: + fail_count: Number of times to fail before succeeding + success_response: Response to return after failures + exception_factory: Factory function that creates exceptions to raise + """ + self.fail_count = fail_count + self.success_response = success_response + self.exception_factory = exception_factory + self.call_count = 0 + + @implements(Template.__call__) + def _call[**P]( + self, template: Template[P, T], *args: P.args, **kwargs: P.kwargs + ) -> T: + self.call_count += 1 + if self.call_count <= self.fail_count: + raise self.exception_factory() + return self.success_response + + # Test templates from the notebook examples @Template.define def limerick(theme: str) -> str: @@ -118,6 +143,24 @@ def make_greeter(style: str) -> Callable[[Person], str]: raise NotImplementedError +# Helper function for lexical scope test - defined at module level +def double_count(text: str, char: str) -> int: + """Count occurrences of a character and double it.""" + return text.count(char) * 2 + + +# Template that captures the lexical function above +@Template.define +def make_double_counter(char: str) -> Callable[[str], int]: + """Create a function that counts occurrences of '{char}' and doubles the result. + Use the double_count helper function.""" + raise NotImplementedError + + +# Module-level variable for shadowing test +shadow_test_value = "global" + + # Unit tests def test_limerick(): """Test the limerick template returns a string.""" @@ -160,7 +203,6 @@ def count_occurrences(text: str) -> int: assert count_a("cherry") == 0 -<<<<<<< HEAD def test_count_char_with_untyped_function(): """Test program synthesis works even when LLM omits type annotations.""" mock_response = SynthesizedFunction( @@ -264,20 +306,6 @@ def bad_return(text: str) -> str: count_char("a") -# Helper function for lexical scope test - defined at module level -def double_count(text: str, char: str) -> int: - """Count occurrences of a character and double it.""" - return text.count(char) * 2 - - -# Template that captures the lexical function above -@Template.define -def make_double_counter(char: str) -> Callable[[str], int]: - """Create a function that counts occurrences of '{char}' and doubles the result. - Use the double_count helper function.""" - raise NotImplementedError - - def test_program_synthesis_with_lexical_function(): """Test that synthesized code can use functions from the lexical scope.""" mock_response = SynthesizedFunction( @@ -296,11 +324,10 @@ def count_and_double(text: str) -> int: assert counter("cherry") == 0 -def test_program_synthesis_lexical_function_in_prompt(): +def test_program_synthesis_lexical_function_in_context(): """Test that lexical functions are included in the template's context.""" - assert "double_count" in make_double_counter.lexical_context - source, func = make_double_counter.lexical_context["double_count"] - assert "Count occurrences of a character and double it" in source + assert "double_count" in make_double_counter.__context__ + func = make_double_counter.__context__["double_count"] assert func is double_count @@ -323,36 +350,6 @@ def count_and_triple(text: str) -> int: assert callable(counter) assert counter("banana") == 9 # 3 'a's tripled assert counter("aardvark") == 9 -======= -class FailingThenSucceedingProvider[T](ObjectInterpretation): - """Mock provider that fails a specified number of times before succeeding.""" - - def __init__( - self, - fail_count: int, - success_response: T, - exception_factory: Callable[[], Exception], - ): - """Initialize the provider. - - Args: - fail_count: Number of times to fail before succeeding - success_response: Response to return after failures - exception_factory: Factory function that creates exceptions to raise - """ - self.fail_count = fail_count - self.success_response = success_response - self.exception_factory = exception_factory - self.call_count = 0 - - @implements(Template.__call__) - def _call[**P]( - self, template: Template[P, T], *args: P.args, **kwargs: P.kwargs - ) -> T: - self.call_count += 1 - if self.call_count <= self.fail_count: - raise self.exception_factory() - return self.success_response def test_retry_handler_succeeds_after_failures(): @@ -519,10 +516,6 @@ def test_mutually_recursive_templates(): assert mutual_b in mutual_b.tools -# Module-level variable for shadowing test -shadow_test_value = "global" - - def test_lexical_context_shadowing(): """Test that local variables shadow global variables in lexical context.""" # Local shadows global @@ -551,4 +544,3 @@ def template_sees_global() -> str: # Should see the global value (no local shadow in this scope) assert "shadow_test_value" in template_sees_global.__context__ assert template_sees_global.__context__["shadow_test_value"] == "global" ->>>>>>> 931d5071d3f386a224cf46c103ca1905fa3c12df From 79f07a4093eb3ee443ddeb608727ce8f7489b29d Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:24:20 -0500 Subject: [PATCH 28/65] Using Encodable as interface and resolve merge conflicts --- effectful/handlers/llm/synthesis.py | 284 ++++++++++++++++++---------- 1 file changed, 183 insertions(+), 101 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 0319ebf0..472dca2c 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import collections import collections.abc import dataclasses @@ -9,10 +11,12 @@ from collections.abc import Callable import pydantic +from litellm import OpenAIMessageContentListBlock from mypy import api as mypy_api from pydantic import Field from effectful.handlers.llm import LexicalContext, Template +from effectful.handlers.llm.encoding import EncodableAs, type_to_encodable_type from effectful.ops.semantics import fwd from effectful.ops.syntax import ObjectInterpretation, implements @@ -25,11 +29,15 @@ def __init__(self, message, code=None): self.code = code -class SynthesizedFunction(pydantic.BaseModel): +@type_to_encodable_type.register(collections.abc.Callable) +class SynthesizedFunction( + pydantic.BaseModel, + EncodableAs[Callable, "SynthesizedFunction"], +): """Structured output for function synthesis. - LLM provides a complete module and the name of the main function. - We add a type assertion to verify the function matches the expected signature. + Encodes a Callable to a serializable representation (function name + module code). + Decodes by executing the module code and returning the named function. """ function_name: str = Field( @@ -41,101 +49,190 @@ class SynthesizedFunction(pydantic.BaseModel): description="Complete Python module code (no imports needed)", ) + t: typing.ClassVar[type[SynthesizedFunction]] -def _get_imports_from_lexical_context( - lexical_context: LexicalContext, -) -> list[str]: - """Generate import statements for types in the lexical context. + @classmethod + def encode( + cls, vl: Callable, context: LexicalContext | None = None + ) -> SynthesizedFunction: + """Encode a Callable to a SynthesizedFunction. - Only generates imports for types/classes that have a proper module. - """ - imports = [] - for name, obj in lexical_context.items(): - if isinstance(obj, type): - module = inspect.getmodule(obj) - if module is not None and module.__name__ not in ("builtins", "__main__"): - imports.append(f"from {module.__name__} import {name}") - return imports + Extracts the function name and source code. + Optionally captures lexical context from the function's globals. + """ + func_name = vl.__name__ + try: + source = inspect.getsource(vl) + except (OSError, TypeError): + # If we can't get source, create a minimal representation + try: + sig = inspect.signature(vl) + source = f"def {func_name}{sig}:\n pass # Source unavailable" + except (ValueError, TypeError): + source = f"def {func_name}(...):\n pass # Source unavailable" + return cls(function_name=func_name, module_code=textwrap.dedent(source).strip()) -def get_lexical_context_source(template: Template) -> str: - """Generate Python source code representations of items in the lexical context. + @classmethod + def decode( + cls, vl: SynthesizedFunction, context: LexicalContext | None = None + ) -> Callable: + """Decode a SynthesizedFunction to a Callable. - This provides the LLM with information about types, functions, and values - that are available for use in synthesized code. + Executes the module code and returns the named function. + The module code becomes the function's lexical context, + optionally augmented with provided context. + """ + func_name = vl.function_name + module_code = textwrap.dedent(vl.module_code).strip() - Args: - template: The template whose lexical context to examine + # Start with provided context or empty dict + exec_globals: dict[str, typing.Any] = dict(context) if context else {} - Returns: - A string containing Python source code or type signatures + try: + code_obj = compile(module_code, "", "exec") + exec(code_obj, exec_globals) + except SyntaxError as exc: + raise SynthesisError( + f"Syntax error in generated code: {exc}", module_code + ) from exc + except Exception as exc: + raise SynthesisError(f"Evaluation failed: {exc!r}", module_code) from exc + + if func_name not in exec_globals: + raise SynthesisError( + f"Function '{func_name}' not found after execution. " + f"Available names: {[k for k in exec_globals.keys() if not k.startswith('_')]}", + module_code, + ) + + return exec_globals[func_name] + + @classmethod + def serialize(cls, vl: SynthesizedFunction) -> list[OpenAIMessageContentListBlock]: + return [{"type": "text", "text": vl.model_dump_json()}] + + +SynthesizedFunction.t = SynthesizedFunction + + +@type_to_encodable_type.register(LexicalContext) +class EncodableLexicalContext( + EncodableAs[LexicalContext, str], +): + """Encodes LexicalContext to Python source code representation. + + encode: Extracts source code/signatures for types, functions, and values. + decode: Executes source code to reconstruct a lexical context. """ - from effectful.ops.types import Operation - sources: list[str] = [] - seen_names: set[str] = set() + t: typing.ClassVar[type[str]] = str - for name, obj in template.__context__.items(): - # Skip private/dunder names and duplicates - if name.startswith("_") or name in seen_names: - continue - seen_names.add(name) + @classmethod + def encode(cls, context: LexicalContext) -> str: + """Generate Python source code representations of items in the lexical context.""" + from effectful.ops.types import Operation - # Skip modules and common builtins - if isinstance(obj, types.ModuleType): - continue + sources: list[str] = [] + seen_names: set[str] = set() - source = None + for name, obj in context.items(): + # Skip private/dunder names and duplicates + if name.startswith("_") or name in seen_names: + continue + seen_names.add(name) - # Try to get source for classes (including dataclasses) - if isinstance(obj, type): - try: - source = inspect.getsource(obj) - except (OSError, TypeError): - # Fall back to showing class signature - source = f"class {name}: ..." + # Skip modules and common builtins + if isinstance(obj, types.ModuleType): + continue - # Handle functions - elif callable(obj) and not isinstance(obj, (Operation, Template)): - try: - source = inspect.getsource(obj) - except (OSError, TypeError): - # Fall back to showing function signature + source = None + + # Try to get source for classes (including dataclasses) + if isinstance(obj, type): + try: + source = inspect.getsource(obj) + except (OSError, TypeError): + # Fall back to showing class signature + source = f"class {name}: ..." + + # Handle functions + elif callable(obj) and not isinstance(obj, (Operation, Template)): + try: + source = inspect.getsource(obj) + except (OSError, TypeError): + # Fall back to showing function signature + try: + sig = inspect.signature(obj) + source = f"def {name}{sig}: ..." + except (ValueError, TypeError): + source = f"def {name}(...): ..." + + # Handle Operations - show as function signatures + elif isinstance(obj, Operation): try: sig = inspect.signature(obj) - source = f"def {name}{sig}: ..." + doc = obj.__doc__ or "" + first_line = doc.split("\n")[0].strip() if doc else "" + source = ( + f"def {name}{sig}: ... # {first_line}" + if first_line + else f"def {name}{sig}: ..." + ) except (ValueError, TypeError): source = f"def {name}(...): ..." - # Handle Operations - show as function signatures - elif isinstance(obj, Operation): - try: - sig = inspect.signature(obj) - doc = obj.__doc__ or "" - first_line = doc.split("\n")[0].strip() if doc else "" - source = ( - f"def {name}{sig}: ... # {first_line}" - if first_line - else f"def {name}{sig}: ..." - ) - except (ValueError, TypeError): - source = f"def {name}(...): ..." + # Handle Templates - show as function signatures + elif isinstance(obj, Template): + try: + sig = obj.__signature__ + doc = obj.__prompt_template__.split("\n")[0].strip() + source = ( + f"def {name}{sig}: ... # {doc}" + if doc + else f"def {name}{sig}: ..." + ) + except (ValueError, TypeError, AttributeError): + source = f"def {name}(...): ..." - # Handle Templates - show as function signatures - elif isinstance(obj, Template): - try: - sig = obj.__signature__ - doc = obj.__prompt_template__.split("\n")[0].strip() - source = ( - f"def {name}{sig}: ... # {doc}" if doc else f"def {name}{sig}: ..." - ) - except (ValueError, TypeError, AttributeError): - source = f"def {name}(...): ..." + if source: + sources.append(textwrap.dedent(source).strip()) + + return "\n\n".join(sources) - if source: - sources.append(textwrap.dedent(source).strip()) + @classmethod + def decode(cls, source: str) -> LexicalContext: + """Execute source code to reconstruct a lexical context.""" + exec_globals: dict[str, typing.Any] = {} + try: + code_obj = compile(source, "", "exec") + exec(code_obj, exec_globals) + except SyntaxError: + # Source may be stubs/signatures that can't be executed + pass + except Exception: + pass + return LexicalContext(exec_globals) + + @classmethod + def serialize(cls, source: str) -> list[OpenAIMessageContentListBlock]: + return [{"type": "text", "text": source}] - return "\n\n".join(sources) + +def _get_imports_from_lexical_context( + lexical_context: LexicalContext, +) -> list[str]: + """Generate import statements for types in the lexical context. + + Only generates imports for types/classes that have a proper module. + """ + imports = [] + for name, obj in lexical_context.items(): + if isinstance(obj, type): + module = inspect.getmodule(obj) + if module is not None and module.__name__ not in ("builtins", "__main__"): + imports.append(f"from {module.__name__} import {name}") + return imports def run_mypy_check( @@ -205,8 +302,8 @@ def _build_function( ) -> Callable: """Build and execute a function from the synthesized module. - Executes the LLM's module code as-is and optionally verifies - the function matches the expected type using a type assertion. + Uses SynthesizedFunction.decode with the template's lexical context. + Optionally runs mypy type checking if enabled. Args: result: The structured output from the LLM @@ -216,29 +313,14 @@ def _build_function( Returns: The synthesized callable function """ - func_name = result.function_name - module_code = textwrap.dedent(result.module_code).strip() - - # Create a new dictionary for the exec globals - exec_globals = dict(lexical_context) - try: - code_obj = compile(module_code, "", "exec") - exec(code_obj, exec_globals) - except SyntaxError as exc: - raise SynthesisError( - f"Syntax error in generated code: {exc}", module_code - ) from exc - except Exception as exc: - raise SynthesisError(f"Evaluation failed: {exc!r}", module_code) from exc - - if func_name not in lexical_context: - raise SynthesisError( - f"Function '{func_name}' not found after execution. " - f"Available names: {[k for k in exec_globals.keys() if not k.startswith('_')]}", - module_code, - ) + if self.type_check: + success, error_msg = run_mypy_check(result.module_code, lexical_context) + if not success: + raise SynthesisError( + f"Type check failed:\n{error_msg}", result.module_code + ) - return exec_globals[func_name] + return SynthesizedFunction.decode(result, context=lexical_context) @implements(Template.__call__) def _call(self, template, *args, **kwargs) -> Callable: @@ -250,7 +332,7 @@ def _call(self, template, *args, **kwargs) -> Callable: return fwd() # Include the full lexical context - all functions, types, values available to synthesized code - context_source = get_lexical_context_source(template) + context_source = EncodableLexicalContext.encode(template.__context__) escaped_context = context_source.replace("{", "{{").replace("}", "}}") context_section = f""" The following types, functions, and values are available: From d2abb9ef44a447734426be92ef2af88723391eec Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:28:00 -0500 Subject: [PATCH 29/65] Linting --- docs/source/llm.ipynb | 105 ++++++++++++++-------------- effectful/handlers/llm/synthesis.py | 4 +- 2 files changed, 52 insertions(+), 57 deletions(-) diff --git a/docs/source/llm.ipynb b/docs/source/llm.ipynb index f7f8e1c5..1c6c1295 100644 --- a/docs/source/llm.ipynb +++ b/docs/source/llm.ipynb @@ -56,7 +56,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "id": "1e832675", "metadata": {}, "outputs": [], @@ -79,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "id": "634f6533", "metadata": {}, "outputs": [ @@ -87,17 +87,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "In the ocean so deep and so swish, \n", - "Swam a curious gold-colored fish. \n", - "With a flick of its tail, \n", - "It set off to sail, \n", - "In search of a dream and a wish.\n", + "In a brook where the swift currents swish, \n", + "Lived a trout with a silvery wish. \n", + "He dreamt of the sea, \n", + "Where big fish swim free, \n", + "Not just meals on a fisherman's dish!\n", "----------------------------------------\n", - "In the ocean where fish like to play, \n", - "They swim and they glide all the day. \n", - "With scales shining bright, \n", - "They bring such delight, \n", - "In the waters, they dance and display.\n" + "In the deep blue sea swam a fish, \n", + "Whose scales were silver, quite swish. \n", + "He danced with delight, \n", + "In the shimmering light, \n", + "His life full of bubbles and bliss.\n" ] } ], @@ -118,7 +118,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "id": "706ce53b", "metadata": {}, "outputs": [ @@ -127,37 +127,29 @@ "output_type": "stream", "text": [ "\n", - "Silent waters dance, \n", - "Silver flash beneath the waves— \n", - "Fish in quiet grace.\n", + "In tranquil waters, \n", + "Silver scales weave through sunbeams, \n", + "Whispers of the deep.\n", "----------------------------------------\n", - "Silent waters dance, \n", - "Silver flash beneath the waves— \n", - "Fish in quiet grace.\n", + "In tranquil waters, \n", + "Silver scales weave through sunbeams, \n", + "Whispers of the deep.\n", "\n", - "Fish swim silently, \n", - "In the embrace of water, \n", - "Nature's dance alive.\n", + "Fish dart through the sea, \n", + "Silent waves whisper secrets, \n", + "Nature's dance unveiled. \n", "----------------------------------------\n", - "Fish swim silently, \n", - "In the embrace of water, \n", - "Nature's dance alive.\n", - "\n", + "Fish dart through the sea, \n", + "Silent waves whisper secrets, \n", + "Nature's dance unveiled. \n", "\n", - "\u001b[1;31mGive Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new\u001b[0m\n", - "LiteLLM.Info: If you need to debug this error, use `litellm._turn_on_debug()'.\n", - "\n", - "Here's a haiku on the theme of fish3:\n", - "\n", - "In the deep blue sea, \n", - "Silent swimmers glide with grace, \n", - "Whispers of the tide.\n", + "In the silver lake, \n", + "Fish swim through liquid moonlight, \n", + "Nature's silent grace.\n", "----------------------------------------\n", - "Here is a haiku on the theme of fish3:\n", - "\n", - "In the clear water, \n", - "Graceful fins weave through the sea, \n", - "Whispers of the deep.\n" + "In the silver lake, \n", + "Fish swim through liquid moonlight, \n", + "Nature's silent grace.\n" ] } ], @@ -208,7 +200,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "id": "2c766859", "metadata": {}, "outputs": [], @@ -233,17 +225,22 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "id": "c83bbdc0", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "def count_a_occurrences(input_string: str) -> int:\n", - " return input_string.count('a')\n", - "\n" + "ename": "OSError", + "evalue": "could not get source code", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mOSError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 11\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m count_a(\u001b[33m\"\u001b[39m\u001b[33mcherry\u001b[39m\u001b[33m\"\u001b[39m) == \u001b[32m0\u001b[39m\n\u001b[32m 12\u001b[39m \u001b[38;5;66;03m# Print the source code of the generated function\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43minspect\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgetsource\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcount_a\u001b[49m\u001b[43m)\u001b[49m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1285\u001b[39m, in \u001b[36mgetsource\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1279\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgetsource\u001b[39m(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1280\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Return the text of the source code for an object.\u001b[39;00m\n\u001b[32m 1281\u001b[39m \n\u001b[32m 1282\u001b[39m \u001b[33;03m The argument may be a module, class, method, function, traceback, frame,\u001b[39;00m\n\u001b[32m 1283\u001b[39m \u001b[33;03m or code object. The source code is returned as a single string. An\u001b[39;00m\n\u001b[32m 1284\u001b[39m \u001b[33;03m OSError is raised if the source code cannot be retrieved.\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1285\u001b[39m lines, lnum = \u001b[43mgetsourcelines\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mobject\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 1286\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m.join(lines)\n", + "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1267\u001b[39m, in \u001b[36mgetsourcelines\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1259\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Return a list of source lines and starting line number for an object.\u001b[39;00m\n\u001b[32m 1260\u001b[39m \n\u001b[32m 1261\u001b[39m \u001b[33;03mThe argument may be a module, class, method, function, traceback, frame,\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1264\u001b[39m \u001b[33;03moriginal source file the first line of code was found. An OSError is\u001b[39;00m\n\u001b[32m 1265\u001b[39m \u001b[33;03mraised if the source code cannot be retrieved.\"\"\"\u001b[39;00m\n\u001b[32m 1266\u001b[39m \u001b[38;5;28mobject\u001b[39m = unwrap(\u001b[38;5;28mobject\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m1267\u001b[39m lines, lnum = \u001b[43mfindsource\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mobject\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 1269\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m istraceback(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1270\u001b[39m \u001b[38;5;28mobject\u001b[39m = \u001b[38;5;28mobject\u001b[39m.tb_frame\n", + "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1096\u001b[39m, in \u001b[36mfindsource\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1094\u001b[39m lines = linecache.getlines(file)\n\u001b[32m 1095\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m lines:\n\u001b[32m-> \u001b[39m\u001b[32m1096\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m(\u001b[33m'\u001b[39m\u001b[33mcould not get source code\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 1098\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m ismodule(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1099\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m lines, \u001b[32m0\u001b[39m\n", + "\u001b[31mOSError\u001b[39m: could not get source code" ] } ], @@ -277,7 +274,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "66711301", "metadata": {}, "outputs": [ @@ -333,7 +330,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "17668ac8", "metadata": {}, "outputs": [ @@ -397,7 +394,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "cbf495a2", "metadata": {}, "outputs": [ @@ -440,7 +437,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "81a15f00", "metadata": {}, "outputs": [ @@ -490,7 +487,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "78a4bf44", "metadata": {}, "outputs": [ @@ -573,7 +570,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "4334d07a", "metadata": {}, "outputs": [ @@ -655,7 +652,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "39b2b225", "metadata": {}, "outputs": [ diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 472dca2c..c8783c8d 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -49,8 +49,6 @@ class SynthesizedFunction( description="Complete Python module code (no imports needed)", ) - t: typing.ClassVar[type[SynthesizedFunction]] - @classmethod def encode( cls, vl: Callable, context: LexicalContext | None = None @@ -126,7 +124,7 @@ class EncodableLexicalContext( decode: Executes source code to reconstruct a lexical context. """ - t: typing.ClassVar[type[str]] = str + t = str @classmethod def encode(cls, context: LexicalContext) -> str: From e3374f6955bd57bed3c2ba2173885a2b0a259a85 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:29:09 -0500 Subject: [PATCH 30/65] Attaching source code to the function --- effectful/handlers/llm/synthesis.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index c8783c8d..65ab0fdf 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -104,7 +104,12 @@ def decode( module_code, ) - return exec_globals[func_name] + func = exec_globals[func_name] + # Attach source code to the function for later retrieval + # (inspect.getsource won't work on dynamically generated functions) + func.__source__ = module_code + func.__synthesized__ = vl + return func @classmethod def serialize(cls, vl: SynthesizedFunction) -> list[OpenAIMessageContentListBlock]: From 535d9fc39fe259a6aa8d307871d49712ac0229c3 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:34:10 -0500 Subject: [PATCH 31/65] Attaching source code to the function --- docs/source/llm.ipynb | 172 ++++++++++++++++++++++------ effectful/handlers/llm/synthesis.py | 30 ++--- 2 files changed, 152 insertions(+), 50 deletions(-) diff --git a/docs/source/llm.ipynb b/docs/source/llm.ipynb index 1c6c1295..70d63e47 100644 --- a/docs/source/llm.ipynb +++ b/docs/source/llm.ipynb @@ -87,17 +87,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "In a brook where the swift currents swish, \n", - "Lived a trout with a silvery wish. \n", - "He dreamt of the sea, \n", - "Where big fish swim free, \n", - "Not just meals on a fisherman's dish!\n", + "There once was a fish in a brook, \n", + "Who fancied the way that he shook, \n", + "He'd wiggle and glide, \n", + "While swimming with pride, \n", + "And read tales in a watery nook.\n", "----------------------------------------\n", - "In the deep blue sea swam a fish, \n", - "Whose scales were silver, quite swish. \n", - "He danced with delight, \n", - "In the shimmering light, \n", - "His life full of bubbles and bliss.\n" + "There once was a fish in a brook, \n", + "Who fancied himself quite a cook. \n", + "With a splash and a wish, \n", + "He'd plate up a dish, \n", + "And read recipes from a book! \n" ] } ], @@ -127,29 +127,33 @@ "output_type": "stream", "text": [ "\n", - "In tranquil waters, \n", - "Silver scales weave through sunbeams, \n", - "Whispers of the deep.\n", + "In cool water's flow, \n", + "Fish glide, silver scales shimmer, \n", + "Silent depths below.\n", "----------------------------------------\n", - "In tranquil waters, \n", - "Silver scales weave through sunbeams, \n", - "Whispers of the deep.\n", + "In cool water's flow, \n", + "Fish glide, silver scales shimmer, \n", + "Silent depths below.\n", "\n", - "Fish dart through the sea, \n", - "Silent waves whisper secrets, \n", - "Nature's dance unveiled. \n", + "Here's a haiku on the theme of fish2:\n", + "\n", + "Swimming in the sea, \n", + "Graceful fins beneath the tide, \n", + "Silent scales glide by.\n", "----------------------------------------\n", - "Fish dart through the sea, \n", - "Silent waves whisper secrets, \n", - "Nature's dance unveiled. \n", + "Here's a haiku on the theme of fish2:\n", + "\n", + "Swimming in the sea, \n", + "Graceful fins beneath the tide, \n", + "Silent scales glide by.\n", "\n", - "In the silver lake, \n", - "Fish swim through liquid moonlight, \n", - "Nature's silent grace.\n", + "In the clear blue stream, \n", + "Silver scales flicker with light, \n", + "Gentle ripples dance.\n", "----------------------------------------\n", - "In the silver lake, \n", - "Fish swim through liquid moonlight, \n", - "Nature's silent grace.\n" + "In the clear blue stream, \n", + "Silver scales flicker with light, \n", + "Gentle ripples dance.\n" ] } ], @@ -230,17 +234,113 @@ "metadata": {}, "outputs": [ { - "ename": "OSError", - "evalue": "could not get source code", + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[1;31mGive Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new\u001b[0m\n", + "LiteLLM.Info: If you need to debug this error, use `litellm._turn_on_debug()'.\n", + "\n" + ] + }, + { + "ename": "APIConnectionError", + "evalue": "litellm.APIConnectionError: APIConnectionError: OpenAIException - Cannot generate a JsonSchema for core_schema.IsInstanceSchema ()", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mOSError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 11\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m count_a(\u001b[33m\"\u001b[39m\u001b[33mcherry\u001b[39m\u001b[33m\"\u001b[39m) == \u001b[32m0\u001b[39m\n\u001b[32m 12\u001b[39m \u001b[38;5;66;03m# Print the source code of the generated function\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43minspect\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgetsource\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcount_a\u001b[49m\u001b[43m)\u001b[49m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1285\u001b[39m, in \u001b[36mgetsource\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1279\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgetsource\u001b[39m(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1280\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Return the text of the source code for an object.\u001b[39;00m\n\u001b[32m 1281\u001b[39m \n\u001b[32m 1282\u001b[39m \u001b[33;03m The argument may be a module, class, method, function, traceback, frame,\u001b[39;00m\n\u001b[32m 1283\u001b[39m \u001b[33;03m or code object. The source code is returned as a single string. An\u001b[39;00m\n\u001b[32m 1284\u001b[39m \u001b[33;03m OSError is raised if the source code cannot be retrieved.\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1285\u001b[39m lines, lnum = \u001b[43mgetsourcelines\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mobject\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 1286\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m.join(lines)\n", - "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1267\u001b[39m, in \u001b[36mgetsourcelines\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1259\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Return a list of source lines and starting line number for an object.\u001b[39;00m\n\u001b[32m 1260\u001b[39m \n\u001b[32m 1261\u001b[39m \u001b[33;03mThe argument may be a module, class, method, function, traceback, frame,\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1264\u001b[39m \u001b[33;03moriginal source file the first line of code was found. An OSError is\u001b[39;00m\n\u001b[32m 1265\u001b[39m \u001b[33;03mraised if the source code cannot be retrieved.\"\"\"\u001b[39;00m\n\u001b[32m 1266\u001b[39m \u001b[38;5;28mobject\u001b[39m = unwrap(\u001b[38;5;28mobject\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m1267\u001b[39m lines, lnum = \u001b[43mfindsource\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mobject\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 1269\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m istraceback(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1270\u001b[39m \u001b[38;5;28mobject\u001b[39m = \u001b[38;5;28mobject\u001b[39m.tb_frame\n", - "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1096\u001b[39m, in \u001b[36mfindsource\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1094\u001b[39m lines = linecache.getlines(file)\n\u001b[32m 1095\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m lines:\n\u001b[32m-> \u001b[39m\u001b[32m1096\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m(\u001b[33m'\u001b[39m\u001b[33mcould not get source code\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 1098\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m ismodule(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1099\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m lines, \u001b[32m0\u001b[39m\n", - "\u001b[31mOSError\u001b[39m: could not get source code" + "\u001b[31mPydanticInvalidForJsonSchema\u001b[39m Traceback (most recent call last)", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/main.py:1327\u001b[39m, in \u001b[36mcompletion\u001b[39m\u001b[34m(model, messages, timeout, temperature, top_p, n, stream, stream_options, stop, max_completion_tokens, max_tokens, modalities, prediction, audio, presence_penalty, frequency_penalty, logit_bias, user, reasoning_effort, verbosity, response_format, seed, tools, tool_choice, logprobs, top_logprobs, parallel_tool_calls, web_search_options, deployment_id, extra_headers, safety_identifier, service_tier, functions, function_call, base_url, api_version, api_key, model_list, thinking, shared_session, **kwargs)\u001b[39m\n\u001b[32m 1289\u001b[39m optional_param_args = {\n\u001b[32m 1290\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mfunctions\u001b[39m\u001b[33m\"\u001b[39m: functions,\n\u001b[32m 1291\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mfunction_call\u001b[39m\u001b[33m\"\u001b[39m: function_call,\n\u001b[32m (...)\u001b[39m\u001b[32m 1325\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mallowed_openai_params\u001b[39m\u001b[33m\"\u001b[39m: kwargs.get(\u001b[33m\"\u001b[39m\u001b[33mallowed_openai_params\u001b[39m\u001b[33m\"\u001b[39m),\n\u001b[32m 1326\u001b[39m }\n\u001b[32m-> \u001b[39m\u001b[32m1327\u001b[39m optional_params = \u001b[43mget_optional_params\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1328\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43moptional_param_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mnon_default_params\u001b[49m\n\u001b[32m 1329\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1330\u001b[39m processed_non_default_params = pre_process_non_default_params(\n\u001b[32m 1331\u001b[39m model=model,\n\u001b[32m 1332\u001b[39m passed_params=optional_param_args,\n\u001b[32m (...)\u001b[39m\u001b[32m 1338\u001b[39m provider_config=provider_config,\n\u001b[32m 1339\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/utils.py:3392\u001b[39m, in \u001b[36mget_optional_params\u001b[39m\u001b[34m(model, functions, function_call, temperature, top_p, n, stream, stream_options, stop, max_tokens, max_completion_tokens, modalities, prediction, audio, presence_penalty, frequency_penalty, logit_bias, user, custom_llm_provider, response_format, seed, tools, tool_choice, max_retries, logprobs, top_logprobs, extra_headers, api_version, parallel_tool_calls, drop_params, allowed_openai_params, reasoning_effort, verbosity, additional_drop_params, messages, thinking, web_search_options, safety_identifier, **kwargs)\u001b[39m\n\u001b[32m 3391\u001b[39m special_params = passed_params.pop(\u001b[33m\"\u001b[39m\u001b[33mkwargs\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m3392\u001b[39m non_default_params = \u001b[43mpre_process_non_default_params\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 3393\u001b[39m \u001b[43m \u001b[49m\u001b[43mpassed_params\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpassed_params\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3394\u001b[39m \u001b[43m \u001b[49m\u001b[43mspecial_params\u001b[49m\u001b[43m=\u001b[49m\u001b[43mspecial_params\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3395\u001b[39m \u001b[43m \u001b[49m\u001b[43mcustom_llm_provider\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcustom_llm_provider\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3396\u001b[39m \u001b[43m \u001b[49m\u001b[43madditional_drop_params\u001b[49m\u001b[43m=\u001b[49m\u001b[43madditional_drop_params\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3397\u001b[39m \u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3398\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 3399\u001b[39m optional_params = pre_process_optional_params(\n\u001b[32m 3400\u001b[39m passed_params=passed_params,\n\u001b[32m 3401\u001b[39m non_default_params=non_default_params,\n\u001b[32m 3402\u001b[39m custom_llm_provider=custom_llm_provider,\n\u001b[32m 3403\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/utils.py:3196\u001b[39m, in \u001b[36mpre_process_non_default_params\u001b[39m\u001b[34m(passed_params, special_params, custom_llm_provider, additional_drop_params, model, remove_sensitive_keys, add_provider_specific_params, provider_config)\u001b[39m\n\u001b[32m 3195\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m3196\u001b[39m non_default_params[\u001b[33m\"\u001b[39m\u001b[33mresponse_format\u001b[39m\u001b[33m\"\u001b[39m] = \u001b[43mtype_to_response_format_param\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 3197\u001b[39m \u001b[43m \u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m=\u001b[49m\u001b[43mnon_default_params\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mresponse_format\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[32m 3198\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 3200\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mtools\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m non_default_params \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\n\u001b[32m 3201\u001b[39m non_default_params, \u001b[38;5;28mlist\u001b[39m\n\u001b[32m 3202\u001b[39m ): \u001b[38;5;66;03m# fixes https://github.com/BerriAI/litellm/issues/4933\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/llms/base_llm/base_utils.py:198\u001b[39m, in \u001b[36mtype_to_response_format_param\u001b[39m\u001b[34m(response_format, ref_template)\u001b[39m\n\u001b[32m 197\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m198\u001b[39m schema = \u001b[43m_pydantic\u001b[49m\u001b[43m.\u001b[49m\u001b[43mto_strict_json_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 200\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m {\n\u001b[32m 201\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m\"\u001b[39m: \u001b[33m\"\u001b[39m\u001b[33mjson_schema\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 202\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mjson_schema\u001b[39m\u001b[33m\"\u001b[39m: {\n\u001b[32m (...)\u001b[39m\u001b[32m 206\u001b[39m },\n\u001b[32m 207\u001b[39m }\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/openai/lib/_pydantic.py:18\u001b[39m, in \u001b[36mto_strict_json_schema\u001b[39m\u001b[34m(model)\u001b[39m\n\u001b[32m 17\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m inspect.isclass(model) \u001b[38;5;129;01mand\u001b[39;00m is_basemodel_type(model):\n\u001b[32m---> \u001b[39m\u001b[32m18\u001b[39m schema = \u001b[43mmodel_json_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 19\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m (\u001b[38;5;129;01mnot\u001b[39;00m PYDANTIC_V1) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(model, pydantic.TypeAdapter):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/openai/_compat.py:177\u001b[39m, in \u001b[36mmodel_json_schema\u001b[39m\u001b[34m(model)\u001b[39m\n\u001b[32m 176\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m model.schema() \u001b[38;5;66;03m# pyright: ignore[reportDeprecated]\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m177\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmodel\u001b[49m\u001b[43m.\u001b[49m\u001b[43mmodel_json_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/main.py:576\u001b[39m, in \u001b[36mBaseModel.model_json_schema\u001b[39m\u001b[34m(cls, by_alias, ref_template, schema_generator, mode, union_format)\u001b[39m\n\u001b[32m 556\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Generates a JSON schema for a model class.\u001b[39;00m\n\u001b[32m 557\u001b[39m \n\u001b[32m 558\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 574\u001b[39m \u001b[33;03m The JSON schema for the given model class.\u001b[39;00m\n\u001b[32m 575\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m576\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmodel_json_schema\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 577\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 578\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[43m \u001b[49m\u001b[43mref_template\u001b[49m\u001b[43m=\u001b[49m\u001b[43mref_template\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 580\u001b[39m \u001b[43m \u001b[49m\u001b[43munion_format\u001b[49m\u001b[43m=\u001b[49m\u001b[43munion_format\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 581\u001b[39m \u001b[43m \u001b[49m\u001b[43mschema_generator\u001b[49m\u001b[43m=\u001b[49m\u001b[43mschema_generator\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 582\u001b[39m \u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 583\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:2542\u001b[39m, in \u001b[36mmodel_json_schema\u001b[39m\u001b[34m(cls, by_alias, ref_template, union_format, schema_generator, mode)\u001b[39m\n\u001b[32m 2541\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28mcls\u001b[39m.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), \u001b[33m'\u001b[39m\u001b[33mthis is a bug! please report it\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m-> \u001b[39m\u001b[32m2542\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mschema_generator_instance\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgenerate\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__pydantic_core_schema__\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmode\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:415\u001b[39m, in \u001b[36mGenerateJsonSchema.generate\u001b[39m\u001b[34m(self, schema, mode)\u001b[39m\n\u001b[32m 409\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m PydanticUserError(\n\u001b[32m 410\u001b[39m \u001b[33m'\u001b[39m\u001b[33mThis JSON schema generator has already been used to generate a JSON schema. \u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m 411\u001b[39m \u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mYou must create a new instance of \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(\u001b[38;5;28mself\u001b[39m).\u001b[34m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m to generate a new JSON schema.\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 412\u001b[39m code=\u001b[33m'\u001b[39m\u001b[33mjson-schema-already-used\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 413\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m415\u001b[39m json_schema: JsonSchemaValue = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 416\u001b[39m json_ref_counts = \u001b[38;5;28mself\u001b[39m.get_json_ref_counts(json_schema)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:556\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..new_handler_func\u001b[39m\u001b[34m(schema_or_field, current_handler, js_modify_function)\u001b[39m\n\u001b[32m 551\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mnew_handler_func\u001b[39m(\n\u001b[32m 552\u001b[39m schema_or_field: CoreSchemaOrField,\n\u001b[32m 553\u001b[39m current_handler: GetJsonSchemaHandler = current_handler,\n\u001b[32m 554\u001b[39m js_modify_function: GetJsonSchemaFunction = js_modify_function,\n\u001b[32m 555\u001b[39m ) -> JsonSchemaValue:\n\u001b[32m--> \u001b[39m\u001b[32m556\u001b[39m json_schema = \u001b[43mjs_modify_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcurrent_handler\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 557\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema_or_field):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/main.py:852\u001b[39m, in \u001b[36mBaseModel.__get_pydantic_json_schema__\u001b[39m\u001b[34m(cls, core_schema, handler)\u001b[39m\n\u001b[32m 835\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Hook into generating the model's JSON schema.\u001b[39;00m\n\u001b[32m 836\u001b[39m \n\u001b[32m 837\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 850\u001b[39m \u001b[33;03m A JSON schema, as a Python object.\u001b[39;00m\n\u001b[32m 851\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m852\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1604\u001b[39m, in \u001b[36mGenerateJsonSchema.model_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1603\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._config_wrapper_stack.push(config):\n\u001b[32m-> \u001b[39m\u001b[32m1604\u001b[39m json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1606\u001b[39m \u001b[38;5;28mself\u001b[39m._update_class_schema(json_schema, \u001b[38;5;28mcls\u001b[39m, config)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1717\u001b[39m, in \u001b[36mGenerateJsonSchema.model_fields_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1716\u001b[39m named_required_fields.extend(\u001b[38;5;28mself\u001b[39m._name_required_computed_fields(schema.get(\u001b[33m'\u001b[39m\u001b[33mcomputed_fields\u001b[39m\u001b[33m'\u001b[39m, [])))\n\u001b[32m-> \u001b[39m\u001b[32m1717\u001b[39m json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_named_required_fields_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnamed_required_fields\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1718\u001b[39m extras_schema = schema.get(\u001b[33m'\u001b[39m\u001b[33mextras_schema\u001b[39m\u001b[33m'\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1508\u001b[39m, in \u001b[36mGenerateJsonSchema._named_required_fields_schema\u001b[39m\u001b[34m(self, named_required_fields)\u001b[39m\n\u001b[32m 1507\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1508\u001b[39m field_json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield\u001b[49m\u001b[43m)\u001b[49m.copy()\n\u001b[32m 1509\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m PydanticOmit:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1576\u001b[39m, in \u001b[36mGenerateJsonSchema.model_field_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1568\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Generates a JSON schema that matches a schema that defines a model field.\u001b[39;00m\n\u001b[32m 1569\u001b[39m \n\u001b[32m 1570\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1574\u001b[39m \u001b[33;03m The generated JSON schema.\u001b[39;00m\n\u001b[32m 1575\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1576\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:556\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..new_handler_func\u001b[39m\u001b[34m(schema_or_field, current_handler, js_modify_function)\u001b[39m\n\u001b[32m 551\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mnew_handler_func\u001b[39m(\n\u001b[32m 552\u001b[39m schema_or_field: CoreSchemaOrField,\n\u001b[32m 553\u001b[39m current_handler: GetJsonSchemaHandler = current_handler,\n\u001b[32m 554\u001b[39m js_modify_function: GetJsonSchemaFunction = js_modify_function,\n\u001b[32m 555\u001b[39m ) -> JsonSchemaValue:\n\u001b[32m--> \u001b[39m\u001b[32m556\u001b[39m json_schema = \u001b[43mjs_modify_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcurrent_handler\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 557\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema_or_field):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/main.py:852\u001b[39m, in \u001b[36mBaseModel.__get_pydantic_json_schema__\u001b[39m\u001b[34m(cls, core_schema, handler)\u001b[39m\n\u001b[32m 835\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Hook into generating the model's JSON schema.\u001b[39;00m\n\u001b[32m 836\u001b[39m \n\u001b[32m 837\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 850\u001b[39m \u001b[33;03m A JSON schema, as a Python object.\u001b[39;00m\n\u001b[32m 851\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m852\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1604\u001b[39m, in \u001b[36mGenerateJsonSchema.model_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1603\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._config_wrapper_stack.push(config):\n\u001b[32m-> \u001b[39m\u001b[32m1604\u001b[39m json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1606\u001b[39m \u001b[38;5;28mself\u001b[39m._update_class_schema(json_schema, \u001b[38;5;28mcls\u001b[39m, config)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1717\u001b[39m, in \u001b[36mGenerateJsonSchema.model_fields_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1716\u001b[39m named_required_fields.extend(\u001b[38;5;28mself\u001b[39m._name_required_computed_fields(schema.get(\u001b[33m'\u001b[39m\u001b[33mcomputed_fields\u001b[39m\u001b[33m'\u001b[39m, [])))\n\u001b[32m-> \u001b[39m\u001b[32m1717\u001b[39m json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_named_required_fields_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnamed_required_fields\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1718\u001b[39m extras_schema = schema.get(\u001b[33m'\u001b[39m\u001b[33mextras_schema\u001b[39m\u001b[33m'\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1508\u001b[39m, in \u001b[36mGenerateJsonSchema._named_required_fields_schema\u001b[39m\u001b[34m(self, named_required_fields)\u001b[39m\n\u001b[32m 1507\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1508\u001b[39m field_json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield\u001b[49m\u001b[43m)\u001b[49m.copy()\n\u001b[32m 1509\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m PydanticOmit:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1576\u001b[39m, in \u001b[36mGenerateJsonSchema.model_field_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1568\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Generates a JSON schema that matches a schema that defines a model field.\u001b[39;00m\n\u001b[32m 1569\u001b[39m \n\u001b[32m 1570\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1574\u001b[39m \u001b[33;03m The generated JSON schema.\u001b[39;00m\n\u001b[32m 1575\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1576\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:2009\u001b[39m, in \u001b[36mGenerateJsonSchema.custom_error_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 2001\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Generates a JSON schema that matches a schema that defines a custom error.\u001b[39;00m\n\u001b[32m 2002\u001b[39m \n\u001b[32m 2003\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 2007\u001b[39m \u001b[33;03m The generated JSON schema.\u001b[39;00m\n\u001b[32m 2008\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m2009\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:921\u001b[39m, in \u001b[36mGenerateJsonSchema.is_instance_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 911\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Handles JSON schema generation for a core schema that checks if a value is an instance of a class.\u001b[39;00m\n\u001b[32m 912\u001b[39m \n\u001b[32m 913\u001b[39m \u001b[33;03mUnless overridden in a subclass, this raises an error.\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 919\u001b[39m \u001b[33;03m The generated JSON schema.\u001b[39;00m\n\u001b[32m 920\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m921\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandle_invalid_for_json_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43mf\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mcore_schema.IsInstanceSchema (\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcls\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[33;43m)\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:2436\u001b[39m, in \u001b[36mGenerateJsonSchema.handle_invalid_for_json_schema\u001b[39m\u001b[34m(self, schema, error_info)\u001b[39m\n\u001b[32m 2435\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mhandle_invalid_for_json_schema\u001b[39m(\u001b[38;5;28mself\u001b[39m, schema: CoreSchemaOrField, error_info: \u001b[38;5;28mstr\u001b[39m) -> JsonSchemaValue:\n\u001b[32m-> \u001b[39m\u001b[32m2436\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m PydanticInvalidForJsonSchema(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mCannot generate a JsonSchema for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00merror_info\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n", + "\u001b[31mPydanticInvalidForJsonSchema\u001b[39m: Cannot generate a JsonSchema for core_schema.IsInstanceSchema ()\n\nFor further information visit https://errors.pydantic.dev/2.12/u/invalid-for-json-schema", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[31mAPIConnectionError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m 4\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m NotHandled\n\u001b[32m 7\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler(provider), handler(ProgramSynthesis()):\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m count_a = \u001b[43mcount_char\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43ma\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 9\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mcallable\u001b[39m(count_a)\n\u001b[32m 10\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m count_a(\u001b[33m\"\u001b[39m\u001b[33mbanana\u001b[39m\u001b[33m\"\u001b[39m) == \u001b[32m3\u001b[39m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:490\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m490\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__apply__\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:488\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m self_handler(*args, **kwargs)\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 490\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__apply__(\u001b[38;5;28mself\u001b[39m, *args, **kwargs)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:539\u001b[39m, in \u001b[36m__apply__\u001b[39m\u001b[34m(op, *args, **kwargs)\u001b[39m\n\u001b[32m 510\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__apply__\u001b[39m[**A, B](op: Operation[A, B], *args: A.args, **kwargs: A.kwargs) -> B:\n\u001b[32m 511\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Apply ``op`` to ``args``, ``kwargs`` in interpretation ``intp``.\u001b[39;00m\n\u001b[32m 512\u001b[39m \n\u001b[32m 513\u001b[39m \u001b[33;03m Handling :func:`Operation.__apply__` changes the evaluation strategy of terms.\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 537\u001b[39m \n\u001b[32m 538\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m539\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mop\u001b[49m\u001b[43m.\u001b[49m\u001b[43m__default_rule__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:335\u001b[39m, in \u001b[36mOperation.__default_rule__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 333\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 334\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m335\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 336\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mNotImplementedError\u001b[39;00m:\n\u001b[32m 337\u001b[39m warnings.warn(\n\u001b[32m 338\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mOperations should raise effectful.ops.types.NotHandled instead of NotImplementedError.\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 339\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m,\n\u001b[32m 340\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:451\u001b[39m, in \u001b[36mOperation.__get__.._instance_op\u001b[39m\u001b[34m(instance, *args, **kwargs)\u001b[39m\n\u001b[32m 447\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 448\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_instance_op\u001b[39m(instance, *args, **kwargs):\n\u001b[32m 449\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01meffectful\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mops\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01msyntax\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m defdata\n\u001b[32m--> \u001b[39m\u001b[32m451\u001b[39m default_result = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43minstance\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 452\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[32m 453\u001b[39m \u001b[38;5;28misinstance\u001b[39m(default_result, Term)\n\u001b[32m 454\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m default_result.op \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28mself\u001b[39m\n\u001b[32m (...)\u001b[39m\u001b[32m 460\u001b[39m \u001b[38;5;66;03m# so that the instance-specific operation reappears\u001b[39;00m\n\u001b[32m 461\u001b[39m \u001b[38;5;66;03m# in the final term and is therefore visible to evaluate()\u001b[39;00m\n\u001b[32m 462\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m defdata(\n\u001b[32m 463\u001b[39m \u001b[38;5;28mself\u001b[39m.\u001b[34m__get__\u001b[39m(default_result.args[\u001b[32m0\u001b[39m]),\n\u001b[32m 464\u001b[39m *default_result.args[\u001b[32m1\u001b[39m:],\n\u001b[32m 465\u001b[39m **default_result.kwargs,\n\u001b[32m 466\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:485\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 483\u001b[39m self_handler = intp.get(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 484\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m self_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mself_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:70\u001b[39m, in \u001b[36m_set_prompt..bound_body\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 68\u001b[39m next_cont = get_interpretation().get(prompt, prompt.__default_rule__)\n\u001b[32m 69\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({prompt: handler({prompt: next_cont})(cont)}):\n\u001b[32m---> \u001b[39m\u001b[32m70\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mbody\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/synthesis.py:363\u001b[39m, in \u001b[36mProgramSynthesis._call\u001b[39m\u001b[34m(self, template, *args, **kwargs)\u001b[39m\n\u001b[32m 340\u001b[39m context_section = \u001b[33mf\u001b[39m\u001b[33m\"\"\"\u001b[39m\n\u001b[32m 341\u001b[39m \u001b[33mThe following types, functions, and values are available:\u001b[39m\n\u001b[32m 342\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 345\u001b[39m \u001b[33m```\u001b[39m\n\u001b[32m 346\u001b[39m \u001b[33m\"\"\"\u001b[39m\n\u001b[32m 348\u001b[39m prompt_ext = textwrap.dedent(\u001b[33mf\u001b[39m\u001b[33m\"\"\"\u001b[39m\n\u001b[32m 349\u001b[39m \u001b[33m Implement a Python function with the following specification.\u001b[39m\n\u001b[32m 350\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 360\u001b[39m \u001b[33m 5. Do not include import statements.\u001b[39m\n\u001b[32m 361\u001b[39m \u001b[33m \u001b[39m\u001b[33m\"\"\"\u001b[39m).strip()\n\u001b[32m--> \u001b[39m\u001b[32m363\u001b[39m response: SynthesizedFunction = \u001b[43mfwd\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 364\u001b[39m \u001b[43m \u001b[49m\u001b[43mdataclasses\u001b[49m\u001b[43m.\u001b[49m\u001b[43mreplace\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 365\u001b[39m \u001b[43m \u001b[49m\u001b[43mtemplate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 366\u001b[39m \u001b[43m \u001b[49m\u001b[43m__prompt_template__\u001b[49m\u001b[43m=\u001b[49m\u001b[43mprompt_ext\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 367\u001b[39m \u001b[43m \u001b[49m\u001b[43m__signature__\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtemplate\u001b[49m\u001b[43m.\u001b[49m\u001b[43m__signature__\u001b[49m\u001b[43m.\u001b[49m\u001b[43mreplace\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 368\u001b[39m \u001b[43m \u001b[49m\u001b[43mreturn_annotation\u001b[49m\u001b[43m=\u001b[49m\u001b[43mSynthesizedFunction\u001b[49m\n\u001b[32m 369\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 370\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 371\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 372\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 373\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 375\u001b[39m \u001b[38;5;66;03m# Build and return the function using lexical context for exec globals\u001b[39;00m\n\u001b[32m 376\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._build_function(response, ret_type, template.__context__)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:485\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 483\u001b[39m self_handler = intp.get(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 484\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m self_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mself_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n", + "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py:81\u001b[39m, in \u001b[36mContextDecorator.__call__..inner\u001b[39m\u001b[34m(*args, **kwds)\u001b[39m\n\u001b[32m 78\u001b[39m \u001b[38;5;129m@wraps\u001b[39m(func)\n\u001b[32m 79\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minner\u001b[39m(*args, **kwds):\n\u001b[32m 80\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._recreate_cm():\n\u001b[32m---> \u001b[39m\u001b[32m81\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:45\u001b[39m, in \u001b[36m_restore_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 42\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 43\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 44\u001b[39m a, k = (a, k) \u001b[38;5;28;01mif\u001b[39;00m a \u001b[38;5;129;01mor\u001b[39;00m k \u001b[38;5;28;01melse\u001b[39;00m _get_args()\n\u001b[32m---> \u001b[39m\u001b[32m45\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:70\u001b[39m, in \u001b[36m_set_prompt..bound_body\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 68\u001b[39m next_cont = get_interpretation().get(prompt, prompt.__default_rule__)\n\u001b[32m 69\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({prompt: handler({prompt: next_cont})(cont)}):\n\u001b[32m---> \u001b[39m\u001b[32m70\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mbody\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/providers.py:490\u001b[39m, in \u001b[36mLiteLLMProvider._call\u001b[39m\u001b[34m(self, template, *args, **kwargs)\u001b[39m\n\u001b[32m 485\u001b[39m \u001b[38;5;129m@implements\u001b[39m(Template.\u001b[34m__call__\u001b[39m)\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_call\u001b[39m[**P, T](\n\u001b[32m 487\u001b[39m \u001b[38;5;28mself\u001b[39m, template: Template[P, T], *args: P.args, **kwargs: P.kwargs\n\u001b[32m 488\u001b[39m ) -> T:\n\u001b[32m 489\u001b[39m model_input = format_model_input(template, *args, **kwargs)\n\u001b[32m--> \u001b[39m\u001b[32m490\u001b[39m resp = \u001b[43mcompute_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtemplate\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodel_input\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 491\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m decode_response(template, resp)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:490\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m490\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__apply__\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:488\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m self_handler(*args, **kwargs)\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 490\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__apply__(\u001b[38;5;28mself\u001b[39m, *args, **kwargs)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:539\u001b[39m, in \u001b[36m__apply__\u001b[39m\u001b[34m(op, *args, **kwargs)\u001b[39m\n\u001b[32m 510\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__apply__\u001b[39m[**A, B](op: Operation[A, B], *args: A.args, **kwargs: A.kwargs) -> B:\n\u001b[32m 511\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Apply ``op`` to ``args``, ``kwargs`` in interpretation ``intp``.\u001b[39;00m\n\u001b[32m 512\u001b[39m \n\u001b[32m 513\u001b[39m \u001b[33;03m Handling :func:`Operation.__apply__` changes the evaluation strategy of terms.\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 537\u001b[39m \n\u001b[32m 538\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m539\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mop\u001b[49m\u001b[43m.\u001b[49m\u001b[43m__default_rule__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:335\u001b[39m, in \u001b[36mOperation.__default_rule__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 333\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 334\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m335\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 336\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mNotImplementedError\u001b[39;00m:\n\u001b[32m 337\u001b[39m warnings.warn(\n\u001b[32m 338\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mOperations should raise effectful.ops.types.NotHandled instead of NotImplementedError.\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 339\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m,\n\u001b[32m 340\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/providers.py:386\u001b[39m, in \u001b[36mcompute_response\u001b[39m\u001b[34m(template, model_input)\u001b[39m\n\u001b[32m 384\u001b[39m \u001b[38;5;66;03m# loop based on: https://cookbook.openai.com/examples/reasoning_function_calls\u001b[39;00m\n\u001b[32m 385\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m386\u001b[39m response: ModelResponse = \u001b[43mcompletion\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 387\u001b[39m \u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmodel_input\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 388\u001b[39m \u001b[43m \u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpydantic\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcreate_model\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 389\u001b[39m \u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mResponse\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m=\u001b[49m\u001b[43mresponse_encoding_type\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m__config__\u001b[49m\u001b[43m=\u001b[49m\u001b[43m{\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mextra\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mforbid\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m}\u001b[49m\n\u001b[32m 390\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 391\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mresponse_encoding_type\u001b[49m\n\u001b[32m 392\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m 393\u001b[39m \u001b[43m \u001b[49m\u001b[43mtools\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtool_schemas\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 394\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 396\u001b[39m choice: Choices = typing.cast(Choices, response.choices[\u001b[32m0\u001b[39m])\n\u001b[32m 397\u001b[39m message: Message = choice.message\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:485\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 483\u001b[39m self_handler = intp.get(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 484\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m self_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mself_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:70\u001b[39m, in \u001b[36m_set_prompt..bound_body\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 68\u001b[39m next_cont = get_interpretation().get(prompt, prompt.__default_rule__)\n\u001b[32m 69\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({prompt: handler({prompt: next_cont})(cont)}):\n\u001b[32m---> \u001b[39m\u001b[32m70\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mbody\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/providers.py:483\u001b[39m, in \u001b[36mLiteLLMProvider._completion\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 481\u001b[39m \u001b[38;5;129m@implements\u001b[39m(completion)\n\u001b[32m 482\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_completion\u001b[39m(\u001b[38;5;28mself\u001b[39m, *args, **kwargs):\n\u001b[32m--> \u001b[39m\u001b[32m483\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfwd\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmodel_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m \u001b[49m\u001b[43m|\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:485\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 483\u001b[39m self_handler = intp.get(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 484\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m self_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mself_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n", + "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py:81\u001b[39m, in \u001b[36mContextDecorator.__call__..inner\u001b[39m\u001b[34m(*args, **kwds)\u001b[39m\n\u001b[32m 78\u001b[39m \u001b[38;5;129m@wraps\u001b[39m(func)\n\u001b[32m 79\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minner\u001b[39m(*args, **kwds):\n\u001b[32m 80\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._recreate_cm():\n\u001b[32m---> \u001b[39m\u001b[32m81\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:45\u001b[39m, in \u001b[36m_restore_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 42\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 43\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 44\u001b[39m a, k = (a, k) \u001b[38;5;28;01mif\u001b[39;00m a \u001b[38;5;129;01mor\u001b[39;00m k \u001b[38;5;28;01melse\u001b[39;00m _get_args()\n\u001b[32m---> \u001b[39m\u001b[32m45\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:335\u001b[39m, in \u001b[36mOperation.__default_rule__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 333\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 334\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m335\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 336\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mNotImplementedError\u001b[39;00m:\n\u001b[32m 337\u001b[39m warnings.warn(\n\u001b[32m 338\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mOperations should raise effectful.ops.types.NotHandled instead of NotImplementedError.\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 339\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m,\n\u001b[32m 340\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/providers.py:225\u001b[39m, in \u001b[36mcompletion\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 221\u001b[39m \u001b[38;5;129m@defop\u001b[39m\n\u001b[32m 222\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(litellm.completion)\n\u001b[32m 223\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcompletion\u001b[39m(*args, **kwargs) -> Any:\n\u001b[32m 224\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Low-level LLM request. Handlers may log/modify requests and delegate via fwd().\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m225\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mlitellm\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcompletion\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/utils.py:1382\u001b[39m, in \u001b[36mclient..wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 1378\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m logging_obj:\n\u001b[32m 1379\u001b[39m logging_obj.failure_handler(\n\u001b[32m 1380\u001b[39m e, traceback_exception, start_time, end_time\n\u001b[32m 1381\u001b[39m ) \u001b[38;5;66;03m# DO NOT MAKE THREADED - router retry fallback relies on this!\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1382\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m e\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/utils.py:1251\u001b[39m, in \u001b[36mclient..wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 1249\u001b[39m print_verbose(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mError while checking max token limit: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 1250\u001b[39m \u001b[38;5;66;03m# MODEL CALL\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1251\u001b[39m result = \u001b[43moriginal_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1252\u001b[39m end_time = datetime.datetime.now()\n\u001b[32m 1253\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _is_streaming_request(\n\u001b[32m 1254\u001b[39m kwargs=kwargs,\n\u001b[32m 1255\u001b[39m call_type=call_type,\n\u001b[32m 1256\u001b[39m ):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/main.py:3842\u001b[39m, in \u001b[36mcompletion\u001b[39m\u001b[34m(model, messages, timeout, temperature, top_p, n, stream, stream_options, stop, max_completion_tokens, max_tokens, modalities, prediction, audio, presence_penalty, frequency_penalty, logit_bias, user, reasoning_effort, verbosity, response_format, seed, tools, tool_choice, logprobs, top_logprobs, parallel_tool_calls, web_search_options, deployment_id, extra_headers, safety_identifier, service_tier, functions, function_call, base_url, api_version, api_key, model_list, thinking, shared_session, **kwargs)\u001b[39m\n\u001b[32m 3839\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m response\n\u001b[32m 3840\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 3841\u001b[39m \u001b[38;5;66;03m## Map to OpenAI Exception\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m3842\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[43mexception_type\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 3843\u001b[39m \u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3844\u001b[39m \u001b[43m \u001b[49m\u001b[43mcustom_llm_provider\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcustom_llm_provider\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3845\u001b[39m \u001b[43m \u001b[49m\u001b[43moriginal_exception\u001b[49m\u001b[43m=\u001b[49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3846\u001b[39m \u001b[43m \u001b[49m\u001b[43mcompletion_kwargs\u001b[49m\u001b[43m=\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3847\u001b[39m \u001b[43m \u001b[49m\u001b[43mextra_kwargs\u001b[49m\u001b[43m=\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3848\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/litellm_core_utils/exception_mapping_utils.py:2333\u001b[39m, in \u001b[36mexception_type\u001b[39m\u001b[34m(model, original_exception, custom_llm_provider, completion_kwargs, extra_kwargs)\u001b[39m\n\u001b[32m 2331\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(e, error_type):\n\u001b[32m 2332\u001b[39m \u001b[38;5;28msetattr\u001b[39m(e, \u001b[33m\"\u001b[39m\u001b[33mlitellm_response_headers\u001b[39m\u001b[33m\"\u001b[39m, litellm_response_headers)\n\u001b[32m-> \u001b[39m\u001b[32m2333\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m e \u001b[38;5;66;03m# it's already mapped\u001b[39;00m\n\u001b[32m 2334\u001b[39m raised_exc = APIConnectionError(\n\u001b[32m 2335\u001b[39m message=\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[33m\"\u001b[39m.format(original_exception, traceback.format_exc()),\n\u001b[32m 2336\u001b[39m llm_provider=\u001b[33m\"\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 2337\u001b[39m model=\u001b[33m\"\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 2338\u001b[39m )\n\u001b[32m 2339\u001b[39m \u001b[38;5;28msetattr\u001b[39m(raised_exc, \u001b[33m\"\u001b[39m\u001b[33mlitellm_response_headers\u001b[39m\u001b[33m\"\u001b[39m, litellm_response_headers)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/litellm_core_utils/exception_mapping_utils.py:574\u001b[39m, in \u001b[36mexception_type\u001b[39m\u001b[34m(model, original_exception, custom_llm_provider, completion_kwargs, extra_kwargs)\u001b[39m\n\u001b[32m 563\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m APIError(\n\u001b[32m 564\u001b[39m status_code=original_exception.status_code,\n\u001b[32m 565\u001b[39m message=\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mAPIError: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mexception_provider\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m - \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmessage\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 569\u001b[39m litellm_debug_info=extra_information,\n\u001b[32m 570\u001b[39m )\n\u001b[32m 571\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 572\u001b[39m \u001b[38;5;66;03m# if no status code then it is an APIConnectionError: https://github.com/openai/openai-python#handling-errors\u001b[39;00m\n\u001b[32m 573\u001b[39m \u001b[38;5;66;03m# exception_mapping_worked = True\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m574\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m APIConnectionError(\n\u001b[32m 575\u001b[39m message=\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mAPIConnectionError: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mexception_provider\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m - \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmessage\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 576\u001b[39m llm_provider=custom_llm_provider,\n\u001b[32m 577\u001b[39m model=model,\n\u001b[32m 578\u001b[39m litellm_debug_info=extra_information,\n\u001b[32m 579\u001b[39m request=httpx.Request(\n\u001b[32m 580\u001b[39m method=\u001b[33m\"\u001b[39m\u001b[33mPOST\u001b[39m\u001b[33m\"\u001b[39m, url=\u001b[33m\"\u001b[39m\u001b[33mhttps://api.openai.com/v1/\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 581\u001b[39m ),\n\u001b[32m 582\u001b[39m )\n\u001b[32m 583\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m (\n\u001b[32m 584\u001b[39m custom_llm_provider == \u001b[33m\"\u001b[39m\u001b[33manthropic\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 585\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m custom_llm_provider == \u001b[33m\"\u001b[39m\u001b[33manthropic_text\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 586\u001b[39m ): \u001b[38;5;66;03m# one of the anthropics\u001b[39;00m\n\u001b[32m 587\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mprompt is too long\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m error_str \u001b[38;5;129;01mor\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mprompt: length\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m error_str:\n", + "\u001b[31mAPIConnectionError\u001b[39m: litellm.APIConnectionError: APIConnectionError: OpenAIException - Cannot generate a JsonSchema for core_schema.IsInstanceSchema ()" ] } ], diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 65ab0fdf..2e956739 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -29,15 +29,10 @@ def __init__(self, message, code=None): self.code = code -@type_to_encodable_type.register(collections.abc.Callable) -class SynthesizedFunction( - pydantic.BaseModel, - EncodableAs[Callable, "SynthesizedFunction"], -): +class SynthesizedFunction(pydantic.BaseModel): """Structured output for function synthesis. - Encodes a Callable to a serializable representation (function name + module code). - Decodes by executing the module code and returning the named function. + Pydantic model representing synthesized code with function name and module code. """ function_name: str = Field( @@ -49,6 +44,15 @@ class SynthesizedFunction( description="Complete Python module code (no imports needed)", ) + +@type_to_encodable_type.register(collections.abc.Callable) +class EncodableSynthesizedFunction( + EncodableAs[Callable, SynthesizedFunction], +): + """Encodes Callable to SynthesizedFunction and vice versa.""" + + t = SynthesizedFunction + @classmethod def encode( cls, vl: Callable, context: LexicalContext | None = None @@ -56,7 +60,6 @@ def encode( """Encode a Callable to a SynthesizedFunction. Extracts the function name and source code. - Optionally captures lexical context from the function's globals. """ func_name = vl.__name__ try: @@ -69,7 +72,9 @@ def encode( except (ValueError, TypeError): source = f"def {func_name}(...):\n pass # Source unavailable" - return cls(function_name=func_name, module_code=textwrap.dedent(source).strip()) + return SynthesizedFunction( + function_name=func_name, module_code=textwrap.dedent(source).strip() + ) @classmethod def decode( @@ -116,9 +121,6 @@ def serialize(cls, vl: SynthesizedFunction) -> list[OpenAIMessageContentListBloc return [{"type": "text", "text": vl.model_dump_json()}] -SynthesizedFunction.t = SynthesizedFunction - - @type_to_encodable_type.register(LexicalContext) class EncodableLexicalContext( EncodableAs[LexicalContext, str], @@ -305,7 +307,7 @@ def _build_function( ) -> Callable: """Build and execute a function from the synthesized module. - Uses SynthesizedFunction.decode with the template's lexical context. + Uses EncodableSynthesizedFunction.decode with the template's lexical context. Optionally runs mypy type checking if enabled. Args: @@ -323,7 +325,7 @@ def _build_function( f"Type check failed:\n{error_msg}", result.module_code ) - return SynthesizedFunction.decode(result, context=lexical_context) + return EncodableSynthesizedFunction.decode(result, context=lexical_context) @implements(Template.__call__) def _call(self, template, *args, **kwargs) -> Callable: From 3d1074cda77f11bec41cae4b8d0a7fb2e18034ce Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:37:50 -0500 Subject: [PATCH 32/65] Attaching source code to the function --- docs/source/llm.ipynb | 176 +++++++--------------------- effectful/handlers/llm/synthesis.py | 24 +++- 2 files changed, 62 insertions(+), 138 deletions(-) diff --git a/docs/source/llm.ipynb b/docs/source/llm.ipynb index 70d63e47..a99cbd06 100644 --- a/docs/source/llm.ipynb +++ b/docs/source/llm.ipynb @@ -87,17 +87,19 @@ "name": "stdout", "output_type": "stream", "text": [ - "There once was a fish in a brook, \n", - "Who fancied the way that he shook, \n", - "He'd wiggle and glide, \n", - "While swimming with pride, \n", - "And read tales in a watery nook.\n", + "Here's a limerick on the theme of fish:\n", + "\n", + "There once was a fish in the sea, \n", + "Who fancied itself fancy-free. \n", + "It swam with such grace, \n", + "In a watery space, \n", + "And danced with the waves in glee.\n", "----------------------------------------\n", - "There once was a fish in a brook, \n", - "Who fancied himself quite a cook. \n", - "With a splash and a wish, \n", - "He'd plate up a dish, \n", - "And read recipes from a book! \n" + "In the ocean where fish like to play, \n", + "A goldfish swam fast every day. \n", + "With a flick of its fin, \n", + "It wore a big grin, \n", + "And danced in a bubbly ballet.\n" ] } ], @@ -127,33 +129,33 @@ "output_type": "stream", "text": [ "\n", - "In cool water's flow, \n", - "Fish glide, silver scales shimmer, \n", - "Silent depths below.\n", + "In the deep blue sea, \n", + "Silent fins softly whisper, \n", + "Life's dance in water.\n", "----------------------------------------\n", - "In cool water's flow, \n", - "Fish glide, silver scales shimmer, \n", - "Silent depths below.\n", + "In the deep blue sea, \n", + "Silent fins softly whisper, \n", + "Life's dance in water.\n", "\n", - "Here's a haiku on the theme of fish2:\n", - "\n", - "Swimming in the sea, \n", - "Graceful fins beneath the tide, \n", - "Silent scales glide by.\n", + "Fish beneath waves swim, \n", + "Silent gliders in the deep, \n", + "Dreams in water's flow. \n", "----------------------------------------\n", - "Here's a haiku on the theme of fish2:\n", + "Fish beneath waves swim, \n", + "Silent gliders in the deep, \n", + "Dreams in water's flow. \n", "\n", - "Swimming in the sea, \n", - "Graceful fins beneath the tide, \n", - "Silent scales glide by.\n", + "Here's a haiku on the theme of fish:\n", "\n", - "In the clear blue stream, \n", - "Silver scales flicker with light, \n", - "Gentle ripples dance.\n", + "In the clear stream's flow, \n", + "Gliding fins slice through sunlight, \n", + "Silent depths below.\n", "----------------------------------------\n", - "In the clear blue stream, \n", - "Silver scales flicker with light, \n", - "Gentle ripples dance.\n" + "Here's a haiku on the theme of fish:\n", + "\n", + "In the clear stream's flow, \n", + "Gliding fins slice through sunlight, \n", + "Silent depths below.\n" ] } ], @@ -234,113 +236,17 @@ "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\u001b[1;31mGive Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new\u001b[0m\n", - "LiteLLM.Info: If you need to debug this error, use `litellm._turn_on_debug()'.\n", - "\n" - ] - }, - { - "ename": "APIConnectionError", - "evalue": "litellm.APIConnectionError: APIConnectionError: OpenAIException - Cannot generate a JsonSchema for core_schema.IsInstanceSchema ()", + "ename": "OSError", + "evalue": "could not get source code", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mPydanticInvalidForJsonSchema\u001b[39m Traceback (most recent call last)", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/main.py:1327\u001b[39m, in \u001b[36mcompletion\u001b[39m\u001b[34m(model, messages, timeout, temperature, top_p, n, stream, stream_options, stop, max_completion_tokens, max_tokens, modalities, prediction, audio, presence_penalty, frequency_penalty, logit_bias, user, reasoning_effort, verbosity, response_format, seed, tools, tool_choice, logprobs, top_logprobs, parallel_tool_calls, web_search_options, deployment_id, extra_headers, safety_identifier, service_tier, functions, function_call, base_url, api_version, api_key, model_list, thinking, shared_session, **kwargs)\u001b[39m\n\u001b[32m 1289\u001b[39m optional_param_args = {\n\u001b[32m 1290\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mfunctions\u001b[39m\u001b[33m\"\u001b[39m: functions,\n\u001b[32m 1291\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mfunction_call\u001b[39m\u001b[33m\"\u001b[39m: function_call,\n\u001b[32m (...)\u001b[39m\u001b[32m 1325\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mallowed_openai_params\u001b[39m\u001b[33m\"\u001b[39m: kwargs.get(\u001b[33m\"\u001b[39m\u001b[33mallowed_openai_params\u001b[39m\u001b[33m\"\u001b[39m),\n\u001b[32m 1326\u001b[39m }\n\u001b[32m-> \u001b[39m\u001b[32m1327\u001b[39m optional_params = \u001b[43mget_optional_params\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1328\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43moptional_param_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mnon_default_params\u001b[49m\n\u001b[32m 1329\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1330\u001b[39m processed_non_default_params = pre_process_non_default_params(\n\u001b[32m 1331\u001b[39m model=model,\n\u001b[32m 1332\u001b[39m passed_params=optional_param_args,\n\u001b[32m (...)\u001b[39m\u001b[32m 1338\u001b[39m provider_config=provider_config,\n\u001b[32m 1339\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/utils.py:3392\u001b[39m, in \u001b[36mget_optional_params\u001b[39m\u001b[34m(model, functions, function_call, temperature, top_p, n, stream, stream_options, stop, max_tokens, max_completion_tokens, modalities, prediction, audio, presence_penalty, frequency_penalty, logit_bias, user, custom_llm_provider, response_format, seed, tools, tool_choice, max_retries, logprobs, top_logprobs, extra_headers, api_version, parallel_tool_calls, drop_params, allowed_openai_params, reasoning_effort, verbosity, additional_drop_params, messages, thinking, web_search_options, safety_identifier, **kwargs)\u001b[39m\n\u001b[32m 3391\u001b[39m special_params = passed_params.pop(\u001b[33m\"\u001b[39m\u001b[33mkwargs\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m3392\u001b[39m non_default_params = \u001b[43mpre_process_non_default_params\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 3393\u001b[39m \u001b[43m \u001b[49m\u001b[43mpassed_params\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpassed_params\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3394\u001b[39m \u001b[43m \u001b[49m\u001b[43mspecial_params\u001b[49m\u001b[43m=\u001b[49m\u001b[43mspecial_params\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3395\u001b[39m \u001b[43m \u001b[49m\u001b[43mcustom_llm_provider\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcustom_llm_provider\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3396\u001b[39m \u001b[43m \u001b[49m\u001b[43madditional_drop_params\u001b[49m\u001b[43m=\u001b[49m\u001b[43madditional_drop_params\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3397\u001b[39m \u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3398\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 3399\u001b[39m optional_params = pre_process_optional_params(\n\u001b[32m 3400\u001b[39m passed_params=passed_params,\n\u001b[32m 3401\u001b[39m non_default_params=non_default_params,\n\u001b[32m 3402\u001b[39m custom_llm_provider=custom_llm_provider,\n\u001b[32m 3403\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/utils.py:3196\u001b[39m, in \u001b[36mpre_process_non_default_params\u001b[39m\u001b[34m(passed_params, special_params, custom_llm_provider, additional_drop_params, model, remove_sensitive_keys, add_provider_specific_params, provider_config)\u001b[39m\n\u001b[32m 3195\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m3196\u001b[39m non_default_params[\u001b[33m\"\u001b[39m\u001b[33mresponse_format\u001b[39m\u001b[33m\"\u001b[39m] = \u001b[43mtype_to_response_format_param\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 3197\u001b[39m \u001b[43m \u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m=\u001b[49m\u001b[43mnon_default_params\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mresponse_format\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[32m 3198\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 3200\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mtools\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m non_default_params \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\n\u001b[32m 3201\u001b[39m non_default_params, \u001b[38;5;28mlist\u001b[39m\n\u001b[32m 3202\u001b[39m ): \u001b[38;5;66;03m# fixes https://github.com/BerriAI/litellm/issues/4933\u001b[39;00m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/llms/base_llm/base_utils.py:198\u001b[39m, in \u001b[36mtype_to_response_format_param\u001b[39m\u001b[34m(response_format, ref_template)\u001b[39m\n\u001b[32m 197\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m198\u001b[39m schema = \u001b[43m_pydantic\u001b[49m\u001b[43m.\u001b[49m\u001b[43mto_strict_json_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 200\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m {\n\u001b[32m 201\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m\"\u001b[39m: \u001b[33m\"\u001b[39m\u001b[33mjson_schema\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 202\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mjson_schema\u001b[39m\u001b[33m\"\u001b[39m: {\n\u001b[32m (...)\u001b[39m\u001b[32m 206\u001b[39m },\n\u001b[32m 207\u001b[39m }\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/openai/lib/_pydantic.py:18\u001b[39m, in \u001b[36mto_strict_json_schema\u001b[39m\u001b[34m(model)\u001b[39m\n\u001b[32m 17\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m inspect.isclass(model) \u001b[38;5;129;01mand\u001b[39;00m is_basemodel_type(model):\n\u001b[32m---> \u001b[39m\u001b[32m18\u001b[39m schema = \u001b[43mmodel_json_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 19\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m (\u001b[38;5;129;01mnot\u001b[39;00m PYDANTIC_V1) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(model, pydantic.TypeAdapter):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/openai/_compat.py:177\u001b[39m, in \u001b[36mmodel_json_schema\u001b[39m\u001b[34m(model)\u001b[39m\n\u001b[32m 176\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m model.schema() \u001b[38;5;66;03m# pyright: ignore[reportDeprecated]\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m177\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmodel\u001b[49m\u001b[43m.\u001b[49m\u001b[43mmodel_json_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/main.py:576\u001b[39m, in \u001b[36mBaseModel.model_json_schema\u001b[39m\u001b[34m(cls, by_alias, ref_template, schema_generator, mode, union_format)\u001b[39m\n\u001b[32m 556\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Generates a JSON schema for a model class.\u001b[39;00m\n\u001b[32m 557\u001b[39m \n\u001b[32m 558\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 574\u001b[39m \u001b[33;03m The JSON schema for the given model class.\u001b[39;00m\n\u001b[32m 575\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m576\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmodel_json_schema\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 577\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 578\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[43m \u001b[49m\u001b[43mref_template\u001b[49m\u001b[43m=\u001b[49m\u001b[43mref_template\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 580\u001b[39m \u001b[43m \u001b[49m\u001b[43munion_format\u001b[49m\u001b[43m=\u001b[49m\u001b[43munion_format\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 581\u001b[39m \u001b[43m \u001b[49m\u001b[43mschema_generator\u001b[49m\u001b[43m=\u001b[49m\u001b[43mschema_generator\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 582\u001b[39m \u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 583\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:2542\u001b[39m, in \u001b[36mmodel_json_schema\u001b[39m\u001b[34m(cls, by_alias, ref_template, union_format, schema_generator, mode)\u001b[39m\n\u001b[32m 2541\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28mcls\u001b[39m.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), \u001b[33m'\u001b[39m\u001b[33mthis is a bug! please report it\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m-> \u001b[39m\u001b[32m2542\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mschema_generator_instance\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgenerate\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__pydantic_core_schema__\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmode\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:415\u001b[39m, in \u001b[36mGenerateJsonSchema.generate\u001b[39m\u001b[34m(self, schema, mode)\u001b[39m\n\u001b[32m 409\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m PydanticUserError(\n\u001b[32m 410\u001b[39m \u001b[33m'\u001b[39m\u001b[33mThis JSON schema generator has already been used to generate a JSON schema. \u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m 411\u001b[39m \u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mYou must create a new instance of \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(\u001b[38;5;28mself\u001b[39m).\u001b[34m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m to generate a new JSON schema.\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 412\u001b[39m code=\u001b[33m'\u001b[39m\u001b[33mjson-schema-already-used\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 413\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m415\u001b[39m json_schema: JsonSchemaValue = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 416\u001b[39m json_ref_counts = \u001b[38;5;28mself\u001b[39m.get_json_ref_counts(json_schema)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:556\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..new_handler_func\u001b[39m\u001b[34m(schema_or_field, current_handler, js_modify_function)\u001b[39m\n\u001b[32m 551\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mnew_handler_func\u001b[39m(\n\u001b[32m 552\u001b[39m schema_or_field: CoreSchemaOrField,\n\u001b[32m 553\u001b[39m current_handler: GetJsonSchemaHandler = current_handler,\n\u001b[32m 554\u001b[39m js_modify_function: GetJsonSchemaFunction = js_modify_function,\n\u001b[32m 555\u001b[39m ) -> JsonSchemaValue:\n\u001b[32m--> \u001b[39m\u001b[32m556\u001b[39m json_schema = \u001b[43mjs_modify_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcurrent_handler\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 557\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema_or_field):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/main.py:852\u001b[39m, in \u001b[36mBaseModel.__get_pydantic_json_schema__\u001b[39m\u001b[34m(cls, core_schema, handler)\u001b[39m\n\u001b[32m 835\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Hook into generating the model's JSON schema.\u001b[39;00m\n\u001b[32m 836\u001b[39m \n\u001b[32m 837\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 850\u001b[39m \u001b[33;03m A JSON schema, as a Python object.\u001b[39;00m\n\u001b[32m 851\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m852\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1604\u001b[39m, in \u001b[36mGenerateJsonSchema.model_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1603\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._config_wrapper_stack.push(config):\n\u001b[32m-> \u001b[39m\u001b[32m1604\u001b[39m json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1606\u001b[39m \u001b[38;5;28mself\u001b[39m._update_class_schema(json_schema, \u001b[38;5;28mcls\u001b[39m, config)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1717\u001b[39m, in \u001b[36mGenerateJsonSchema.model_fields_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1716\u001b[39m named_required_fields.extend(\u001b[38;5;28mself\u001b[39m._name_required_computed_fields(schema.get(\u001b[33m'\u001b[39m\u001b[33mcomputed_fields\u001b[39m\u001b[33m'\u001b[39m, [])))\n\u001b[32m-> \u001b[39m\u001b[32m1717\u001b[39m json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_named_required_fields_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnamed_required_fields\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1718\u001b[39m extras_schema = schema.get(\u001b[33m'\u001b[39m\u001b[33mextras_schema\u001b[39m\u001b[33m'\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1508\u001b[39m, in \u001b[36mGenerateJsonSchema._named_required_fields_schema\u001b[39m\u001b[34m(self, named_required_fields)\u001b[39m\n\u001b[32m 1507\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1508\u001b[39m field_json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield\u001b[49m\u001b[43m)\u001b[49m.copy()\n\u001b[32m 1509\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m PydanticOmit:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1576\u001b[39m, in \u001b[36mGenerateJsonSchema.model_field_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1568\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Generates a JSON schema that matches a schema that defines a model field.\u001b[39;00m\n\u001b[32m 1569\u001b[39m \n\u001b[32m 1570\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1574\u001b[39m \u001b[33;03m The generated JSON schema.\u001b[39;00m\n\u001b[32m 1575\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1576\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:556\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..new_handler_func\u001b[39m\u001b[34m(schema_or_field, current_handler, js_modify_function)\u001b[39m\n\u001b[32m 551\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mnew_handler_func\u001b[39m(\n\u001b[32m 552\u001b[39m schema_or_field: CoreSchemaOrField,\n\u001b[32m 553\u001b[39m current_handler: GetJsonSchemaHandler = current_handler,\n\u001b[32m 554\u001b[39m js_modify_function: GetJsonSchemaFunction = js_modify_function,\n\u001b[32m 555\u001b[39m ) -> JsonSchemaValue:\n\u001b[32m--> \u001b[39m\u001b[32m556\u001b[39m json_schema = \u001b[43mjs_modify_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcurrent_handler\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 557\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema_or_field):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/main.py:852\u001b[39m, in \u001b[36mBaseModel.__get_pydantic_json_schema__\u001b[39m\u001b[34m(cls, core_schema, handler)\u001b[39m\n\u001b[32m 835\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Hook into generating the model's JSON schema.\u001b[39;00m\n\u001b[32m 836\u001b[39m \n\u001b[32m 837\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 850\u001b[39m \u001b[33;03m A JSON schema, as a Python object.\u001b[39;00m\n\u001b[32m 851\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m852\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1604\u001b[39m, in \u001b[36mGenerateJsonSchema.model_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1603\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._config_wrapper_stack.push(config):\n\u001b[32m-> \u001b[39m\u001b[32m1604\u001b[39m json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1606\u001b[39m \u001b[38;5;28mself\u001b[39m._update_class_schema(json_schema, \u001b[38;5;28mcls\u001b[39m, config)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1717\u001b[39m, in \u001b[36mGenerateJsonSchema.model_fields_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1716\u001b[39m named_required_fields.extend(\u001b[38;5;28mself\u001b[39m._name_required_computed_fields(schema.get(\u001b[33m'\u001b[39m\u001b[33mcomputed_fields\u001b[39m\u001b[33m'\u001b[39m, [])))\n\u001b[32m-> \u001b[39m\u001b[32m1717\u001b[39m json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_named_required_fields_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnamed_required_fields\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1718\u001b[39m extras_schema = schema.get(\u001b[33m'\u001b[39m\u001b[33mextras_schema\u001b[39m\u001b[33m'\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1508\u001b[39m, in \u001b[36mGenerateJsonSchema._named_required_fields_schema\u001b[39m\u001b[34m(self, named_required_fields)\u001b[39m\n\u001b[32m 1507\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1508\u001b[39m field_json_schema = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfield\u001b[49m\u001b[43m)\u001b[49m.copy()\n\u001b[32m 1509\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m PydanticOmit:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:1576\u001b[39m, in \u001b[36mGenerateJsonSchema.model_field_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 1568\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Generates a JSON schema that matches a schema that defines a model field.\u001b[39;00m\n\u001b[32m 1569\u001b[39m \n\u001b[32m 1570\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1574\u001b[39m \u001b[33;03m The generated JSON schema.\u001b[39;00m\n\u001b[32m 1575\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1576\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:2009\u001b[39m, in \u001b[36mGenerateJsonSchema.custom_error_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 2001\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Generates a JSON schema that matches a schema that defines a custom error.\u001b[39;00m\n\u001b[32m 2002\u001b[39m \n\u001b[32m 2003\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 2007\u001b[39m \u001b[33;03m The generated JSON schema.\u001b[39;00m\n\u001b[32m 2008\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m2009\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mgenerate_inner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mschema\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:578\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 576\u001b[39m current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(\u001b[38;5;28mself\u001b[39m, new_handler_func)\n\u001b[32m--> \u001b[39m\u001b[32m578\u001b[39m json_schema = \u001b[43mcurrent_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 579\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _core_utils.is_core_schema(schema):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py:37\u001b[39m, in \u001b[36mGenerateJsonSchemaHandler.__call__\u001b[39m\u001b[34m(self, core_schema)\u001b[39m\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcore_schema\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:511\u001b[39m, in \u001b[36mGenerateJsonSchema.generate_inner..handler_func\u001b[39m\u001b[34m(schema_or_field)\u001b[39m\n\u001b[32m 510\u001b[39m generate_for_schema_type = \u001b[38;5;28mself\u001b[39m._schema_type_to_method[schema_or_field[\u001b[33m'\u001b[39m\u001b[33mtype\u001b[39m\u001b[33m'\u001b[39m]]\n\u001b[32m--> \u001b[39m\u001b[32m511\u001b[39m json_schema = \u001b[43mgenerate_for_schema_type\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema_or_field\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 512\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:921\u001b[39m, in \u001b[36mGenerateJsonSchema.is_instance_schema\u001b[39m\u001b[34m(self, schema)\u001b[39m\n\u001b[32m 911\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Handles JSON schema generation for a core schema that checks if a value is an instance of a class.\u001b[39;00m\n\u001b[32m 912\u001b[39m \n\u001b[32m 913\u001b[39m \u001b[33;03mUnless overridden in a subclass, this raises an error.\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 919\u001b[39m \u001b[33;03m The generated JSON schema.\u001b[39;00m\n\u001b[32m 920\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m921\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mhandle_invalid_for_json_schema\u001b[49m\u001b[43m(\u001b[49m\u001b[43mschema\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43mf\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mcore_schema.IsInstanceSchema (\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mschema\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcls\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[33;43m)\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/json_schema.py:2436\u001b[39m, in \u001b[36mGenerateJsonSchema.handle_invalid_for_json_schema\u001b[39m\u001b[34m(self, schema, error_info)\u001b[39m\n\u001b[32m 2435\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mhandle_invalid_for_json_schema\u001b[39m(\u001b[38;5;28mself\u001b[39m, schema: CoreSchemaOrField, error_info: \u001b[38;5;28mstr\u001b[39m) -> JsonSchemaValue:\n\u001b[32m-> \u001b[39m\u001b[32m2436\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m PydanticInvalidForJsonSchema(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mCannot generate a JsonSchema for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00merror_info\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n", - "\u001b[31mPydanticInvalidForJsonSchema\u001b[39m: Cannot generate a JsonSchema for core_schema.IsInstanceSchema ()\n\nFor further information visit https://errors.pydantic.dev/2.12/u/invalid-for-json-schema", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001b[31mAPIConnectionError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m 4\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m NotHandled\n\u001b[32m 7\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler(provider), handler(ProgramSynthesis()):\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m count_a = \u001b[43mcount_char\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43ma\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 9\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mcallable\u001b[39m(count_a)\n\u001b[32m 10\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m count_a(\u001b[33m\"\u001b[39m\u001b[33mbanana\u001b[39m\u001b[33m\"\u001b[39m) == \u001b[32m3\u001b[39m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:490\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m490\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__apply__\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:488\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m self_handler(*args, **kwargs)\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 490\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__apply__(\u001b[38;5;28mself\u001b[39m, *args, **kwargs)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:539\u001b[39m, in \u001b[36m__apply__\u001b[39m\u001b[34m(op, *args, **kwargs)\u001b[39m\n\u001b[32m 510\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__apply__\u001b[39m[**A, B](op: Operation[A, B], *args: A.args, **kwargs: A.kwargs) -> B:\n\u001b[32m 511\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Apply ``op`` to ``args``, ``kwargs`` in interpretation ``intp``.\u001b[39;00m\n\u001b[32m 512\u001b[39m \n\u001b[32m 513\u001b[39m \u001b[33;03m Handling :func:`Operation.__apply__` changes the evaluation strategy of terms.\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 537\u001b[39m \n\u001b[32m 538\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m539\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mop\u001b[49m\u001b[43m.\u001b[49m\u001b[43m__default_rule__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:335\u001b[39m, in \u001b[36mOperation.__default_rule__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 333\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 334\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m335\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 336\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mNotImplementedError\u001b[39;00m:\n\u001b[32m 337\u001b[39m warnings.warn(\n\u001b[32m 338\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mOperations should raise effectful.ops.types.NotHandled instead of NotImplementedError.\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 339\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m,\n\u001b[32m 340\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:451\u001b[39m, in \u001b[36mOperation.__get__.._instance_op\u001b[39m\u001b[34m(instance, *args, **kwargs)\u001b[39m\n\u001b[32m 447\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 448\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_instance_op\u001b[39m(instance, *args, **kwargs):\n\u001b[32m 449\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01meffectful\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mops\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01msyntax\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m defdata\n\u001b[32m--> \u001b[39m\u001b[32m451\u001b[39m default_result = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43minstance\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 452\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[32m 453\u001b[39m \u001b[38;5;28misinstance\u001b[39m(default_result, Term)\n\u001b[32m 454\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m default_result.op \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28mself\u001b[39m\n\u001b[32m (...)\u001b[39m\u001b[32m 460\u001b[39m \u001b[38;5;66;03m# so that the instance-specific operation reappears\u001b[39;00m\n\u001b[32m 461\u001b[39m \u001b[38;5;66;03m# in the final term and is therefore visible to evaluate()\u001b[39;00m\n\u001b[32m 462\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m defdata(\n\u001b[32m 463\u001b[39m \u001b[38;5;28mself\u001b[39m.\u001b[34m__get__\u001b[39m(default_result.args[\u001b[32m0\u001b[39m]),\n\u001b[32m 464\u001b[39m *default_result.args[\u001b[32m1\u001b[39m:],\n\u001b[32m 465\u001b[39m **default_result.kwargs,\n\u001b[32m 466\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:485\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 483\u001b[39m self_handler = intp.get(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 484\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m self_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mself_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:70\u001b[39m, in \u001b[36m_set_prompt..bound_body\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 68\u001b[39m next_cont = get_interpretation().get(prompt, prompt.__default_rule__)\n\u001b[32m 69\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({prompt: handler({prompt: next_cont})(cont)}):\n\u001b[32m---> \u001b[39m\u001b[32m70\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mbody\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/synthesis.py:363\u001b[39m, in \u001b[36mProgramSynthesis._call\u001b[39m\u001b[34m(self, template, *args, **kwargs)\u001b[39m\n\u001b[32m 340\u001b[39m context_section = \u001b[33mf\u001b[39m\u001b[33m\"\"\"\u001b[39m\n\u001b[32m 341\u001b[39m \u001b[33mThe following types, functions, and values are available:\u001b[39m\n\u001b[32m 342\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 345\u001b[39m \u001b[33m```\u001b[39m\n\u001b[32m 346\u001b[39m \u001b[33m\"\"\"\u001b[39m\n\u001b[32m 348\u001b[39m prompt_ext = textwrap.dedent(\u001b[33mf\u001b[39m\u001b[33m\"\"\"\u001b[39m\n\u001b[32m 349\u001b[39m \u001b[33m Implement a Python function with the following specification.\u001b[39m\n\u001b[32m 350\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 360\u001b[39m \u001b[33m 5. Do not include import statements.\u001b[39m\n\u001b[32m 361\u001b[39m \u001b[33m \u001b[39m\u001b[33m\"\"\"\u001b[39m).strip()\n\u001b[32m--> \u001b[39m\u001b[32m363\u001b[39m response: SynthesizedFunction = \u001b[43mfwd\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 364\u001b[39m \u001b[43m \u001b[49m\u001b[43mdataclasses\u001b[49m\u001b[43m.\u001b[49m\u001b[43mreplace\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 365\u001b[39m \u001b[43m \u001b[49m\u001b[43mtemplate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 366\u001b[39m \u001b[43m \u001b[49m\u001b[43m__prompt_template__\u001b[49m\u001b[43m=\u001b[49m\u001b[43mprompt_ext\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 367\u001b[39m \u001b[43m \u001b[49m\u001b[43m__signature__\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtemplate\u001b[49m\u001b[43m.\u001b[49m\u001b[43m__signature__\u001b[49m\u001b[43m.\u001b[49m\u001b[43mreplace\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 368\u001b[39m \u001b[43m \u001b[49m\u001b[43mreturn_annotation\u001b[49m\u001b[43m=\u001b[49m\u001b[43mSynthesizedFunction\u001b[49m\n\u001b[32m 369\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 370\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 371\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 372\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 373\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 375\u001b[39m \u001b[38;5;66;03m# Build and return the function using lexical context for exec globals\u001b[39;00m\n\u001b[32m 376\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._build_function(response, ret_type, template.__context__)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:485\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 483\u001b[39m self_handler = intp.get(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 484\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m self_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mself_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n", - "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py:81\u001b[39m, in \u001b[36mContextDecorator.__call__..inner\u001b[39m\u001b[34m(*args, **kwds)\u001b[39m\n\u001b[32m 78\u001b[39m \u001b[38;5;129m@wraps\u001b[39m(func)\n\u001b[32m 79\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minner\u001b[39m(*args, **kwds):\n\u001b[32m 80\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._recreate_cm():\n\u001b[32m---> \u001b[39m\u001b[32m81\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:45\u001b[39m, in \u001b[36m_restore_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 42\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 43\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 44\u001b[39m a, k = (a, k) \u001b[38;5;28;01mif\u001b[39;00m a \u001b[38;5;129;01mor\u001b[39;00m k \u001b[38;5;28;01melse\u001b[39;00m _get_args()\n\u001b[32m---> \u001b[39m\u001b[32m45\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:70\u001b[39m, in \u001b[36m_set_prompt..bound_body\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 68\u001b[39m next_cont = get_interpretation().get(prompt, prompt.__default_rule__)\n\u001b[32m 69\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({prompt: handler({prompt: next_cont})(cont)}):\n\u001b[32m---> \u001b[39m\u001b[32m70\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mbody\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/providers.py:490\u001b[39m, in \u001b[36mLiteLLMProvider._call\u001b[39m\u001b[34m(self, template, *args, **kwargs)\u001b[39m\n\u001b[32m 485\u001b[39m \u001b[38;5;129m@implements\u001b[39m(Template.\u001b[34m__call__\u001b[39m)\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_call\u001b[39m[**P, T](\n\u001b[32m 487\u001b[39m \u001b[38;5;28mself\u001b[39m, template: Template[P, T], *args: P.args, **kwargs: P.kwargs\n\u001b[32m 488\u001b[39m ) -> T:\n\u001b[32m 489\u001b[39m model_input = format_model_input(template, *args, **kwargs)\n\u001b[32m--> \u001b[39m\u001b[32m490\u001b[39m resp = \u001b[43mcompute_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtemplate\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodel_input\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 491\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m decode_response(template, resp)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:490\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m490\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__apply__\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:488\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m self_handler(*args, **kwargs)\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 490\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__apply__(\u001b[38;5;28mself\u001b[39m, *args, **kwargs)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:539\u001b[39m, in \u001b[36m__apply__\u001b[39m\u001b[34m(op, *args, **kwargs)\u001b[39m\n\u001b[32m 510\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__apply__\u001b[39m[**A, B](op: Operation[A, B], *args: A.args, **kwargs: A.kwargs) -> B:\n\u001b[32m 511\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Apply ``op`` to ``args``, ``kwargs`` in interpretation ``intp``.\u001b[39;00m\n\u001b[32m 512\u001b[39m \n\u001b[32m 513\u001b[39m \u001b[33;03m Handling :func:`Operation.__apply__` changes the evaluation strategy of terms.\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 537\u001b[39m \n\u001b[32m 538\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m539\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mop\u001b[49m\u001b[43m.\u001b[49m\u001b[43m__default_rule__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:335\u001b[39m, in \u001b[36mOperation.__default_rule__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 333\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 334\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m335\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 336\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mNotImplementedError\u001b[39;00m:\n\u001b[32m 337\u001b[39m warnings.warn(\n\u001b[32m 338\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mOperations should raise effectful.ops.types.NotHandled instead of NotImplementedError.\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 339\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m,\n\u001b[32m 340\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/providers.py:386\u001b[39m, in \u001b[36mcompute_response\u001b[39m\u001b[34m(template, model_input)\u001b[39m\n\u001b[32m 384\u001b[39m \u001b[38;5;66;03m# loop based on: https://cookbook.openai.com/examples/reasoning_function_calls\u001b[39;00m\n\u001b[32m 385\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m386\u001b[39m response: ModelResponse = \u001b[43mcompletion\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 387\u001b[39m \u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmodel_input\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 388\u001b[39m \u001b[43m \u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpydantic\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcreate_model\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 389\u001b[39m \u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mResponse\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m=\u001b[49m\u001b[43mresponse_encoding_type\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m__config__\u001b[49m\u001b[43m=\u001b[49m\u001b[43m{\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mextra\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mforbid\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m}\u001b[49m\n\u001b[32m 390\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 391\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mresponse_encoding_type\u001b[49m\n\u001b[32m 392\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m 393\u001b[39m \u001b[43m \u001b[49m\u001b[43mtools\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtool_schemas\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 394\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 396\u001b[39m choice: Choices = typing.cast(Choices, response.choices[\u001b[32m0\u001b[39m])\n\u001b[32m 397\u001b[39m message: Message = choice.message\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:485\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 483\u001b[39m self_handler = intp.get(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 484\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m self_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mself_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:70\u001b[39m, in \u001b[36m_set_prompt..bound_body\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 68\u001b[39m next_cont = get_interpretation().get(prompt, prompt.__default_rule__)\n\u001b[32m 69\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({prompt: handler({prompt: next_cont})(cont)}):\n\u001b[32m---> \u001b[39m\u001b[32m70\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mbody\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/providers.py:483\u001b[39m, in \u001b[36mLiteLLMProvider._completion\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 481\u001b[39m \u001b[38;5;129m@implements\u001b[39m(completion)\n\u001b[32m 482\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_completion\u001b[39m(\u001b[38;5;28mself\u001b[39m, *args, **kwargs):\n\u001b[32m--> \u001b[39m\u001b[32m483\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfwd\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmodel_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m \u001b[49m\u001b[43m|\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:485\u001b[39m, in \u001b[36mOperation.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 483\u001b[39m self_handler = intp.get(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m 484\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m self_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mself_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m args \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(args[\u001b[32m0\u001b[39m], Operation) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m args[\u001b[32m0\u001b[39m].__apply__:\n\u001b[32m 487\u001b[39m \u001b[38;5;66;03m# Prevent infinite recursion when calling self.apply directly\u001b[39;00m\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.__default__(*args, **kwargs)\n", - "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py:81\u001b[39m, in \u001b[36mContextDecorator.__call__..inner\u001b[39m\u001b[34m(*args, **kwds)\u001b[39m\n\u001b[32m 78\u001b[39m \u001b[38;5;129m@wraps\u001b[39m(func)\n\u001b[32m 79\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minner\u001b[39m(*args, **kwds):\n\u001b[32m 80\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._recreate_cm():\n\u001b[32m---> \u001b[39m\u001b[32m81\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:45\u001b[39m, in \u001b[36m_restore_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 42\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 43\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 44\u001b[39m a, k = (a, k) \u001b[38;5;28;01mif\u001b[39;00m a \u001b[38;5;129;01mor\u001b[39;00m k \u001b[38;5;28;01melse\u001b[39;00m _get_args()\n\u001b[32m---> \u001b[39m\u001b[32m45\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/internals/runtime.py:56\u001b[39m, in \u001b[36m_save_args.._cont_wrapper\u001b[39m\u001b[34m(*a, **k)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(fn)\n\u001b[32m 54\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_cont_wrapper\u001b[39m(*a: P.args, **k: P.kwargs) -> T:\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m handler({_get_args: \u001b[38;5;28;01mlambda\u001b[39;00m: (a, k)}):\n\u001b[32m---> \u001b[39m\u001b[32m56\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mk\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/ops/types.py:335\u001b[39m, in \u001b[36mOperation.__default_rule__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 333\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 334\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m335\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__default__\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 336\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mNotImplementedError\u001b[39;00m:\n\u001b[32m 337\u001b[39m warnings.warn(\n\u001b[32m 338\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mOperations should raise effectful.ops.types.NotHandled instead of NotImplementedError.\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 339\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m,\n\u001b[32m 340\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/effectful/handlers/llm/providers.py:225\u001b[39m, in \u001b[36mcompletion\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 221\u001b[39m \u001b[38;5;129m@defop\u001b[39m\n\u001b[32m 222\u001b[39m \u001b[38;5;129m@functools\u001b[39m.wraps(litellm.completion)\n\u001b[32m 223\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcompletion\u001b[39m(*args, **kwargs) -> Any:\n\u001b[32m 224\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Low-level LLM request. Handlers may log/modify requests and delegate via fwd().\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m225\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mlitellm\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcompletion\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/utils.py:1382\u001b[39m, in \u001b[36mclient..wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 1378\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m logging_obj:\n\u001b[32m 1379\u001b[39m logging_obj.failure_handler(\n\u001b[32m 1380\u001b[39m e, traceback_exception, start_time, end_time\n\u001b[32m 1381\u001b[39m ) \u001b[38;5;66;03m# DO NOT MAKE THREADED - router retry fallback relies on this!\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1382\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m e\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/utils.py:1251\u001b[39m, in \u001b[36mclient..wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 1249\u001b[39m print_verbose(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mError while checking max token limit: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 1250\u001b[39m \u001b[38;5;66;03m# MODEL CALL\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1251\u001b[39m result = \u001b[43moriginal_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1252\u001b[39m end_time = datetime.datetime.now()\n\u001b[32m 1253\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m _is_streaming_request(\n\u001b[32m 1254\u001b[39m kwargs=kwargs,\n\u001b[32m 1255\u001b[39m call_type=call_type,\n\u001b[32m 1256\u001b[39m ):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/main.py:3842\u001b[39m, in \u001b[36mcompletion\u001b[39m\u001b[34m(model, messages, timeout, temperature, top_p, n, stream, stream_options, stop, max_completion_tokens, max_tokens, modalities, prediction, audio, presence_penalty, frequency_penalty, logit_bias, user, reasoning_effort, verbosity, response_format, seed, tools, tool_choice, logprobs, top_logprobs, parallel_tool_calls, web_search_options, deployment_id, extra_headers, safety_identifier, service_tier, functions, function_call, base_url, api_version, api_key, model_list, thinking, shared_session, **kwargs)\u001b[39m\n\u001b[32m 3839\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m response\n\u001b[32m 3840\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 3841\u001b[39m \u001b[38;5;66;03m## Map to OpenAI Exception\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m3842\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[43mexception_type\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 3843\u001b[39m \u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3844\u001b[39m \u001b[43m \u001b[49m\u001b[43mcustom_llm_provider\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcustom_llm_provider\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3845\u001b[39m \u001b[43m \u001b[49m\u001b[43moriginal_exception\u001b[49m\u001b[43m=\u001b[49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3846\u001b[39m \u001b[43m \u001b[49m\u001b[43mcompletion_kwargs\u001b[49m\u001b[43m=\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3847\u001b[39m \u001b[43m \u001b[49m\u001b[43mextra_kwargs\u001b[49m\u001b[43m=\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3848\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/litellm_core_utils/exception_mapping_utils.py:2333\u001b[39m, in \u001b[36mexception_type\u001b[39m\u001b[34m(model, original_exception, custom_llm_provider, completion_kwargs, extra_kwargs)\u001b[39m\n\u001b[32m 2331\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(e, error_type):\n\u001b[32m 2332\u001b[39m \u001b[38;5;28msetattr\u001b[39m(e, \u001b[33m\"\u001b[39m\u001b[33mlitellm_response_headers\u001b[39m\u001b[33m\"\u001b[39m, litellm_response_headers)\n\u001b[32m-> \u001b[39m\u001b[32m2333\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m e \u001b[38;5;66;03m# it's already mapped\u001b[39;00m\n\u001b[32m 2334\u001b[39m raised_exc = APIConnectionError(\n\u001b[32m 2335\u001b[39m message=\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[33m\"\u001b[39m.format(original_exception, traceback.format_exc()),\n\u001b[32m 2336\u001b[39m llm_provider=\u001b[33m\"\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 2337\u001b[39m model=\u001b[33m\"\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 2338\u001b[39m )\n\u001b[32m 2339\u001b[39m \u001b[38;5;28msetattr\u001b[39m(raised_exc, \u001b[33m\"\u001b[39m\u001b[33mlitellm_response_headers\u001b[39m\u001b[33m\"\u001b[39m, litellm_response_headers)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Marc/effectful/.venv/lib/python3.12/site-packages/litellm/litellm_core_utils/exception_mapping_utils.py:574\u001b[39m, in \u001b[36mexception_type\u001b[39m\u001b[34m(model, original_exception, custom_llm_provider, completion_kwargs, extra_kwargs)\u001b[39m\n\u001b[32m 563\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m APIError(\n\u001b[32m 564\u001b[39m status_code=original_exception.status_code,\n\u001b[32m 565\u001b[39m message=\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mAPIError: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mexception_provider\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m - \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmessage\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 569\u001b[39m litellm_debug_info=extra_information,\n\u001b[32m 570\u001b[39m )\n\u001b[32m 571\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 572\u001b[39m \u001b[38;5;66;03m# if no status code then it is an APIConnectionError: https://github.com/openai/openai-python#handling-errors\u001b[39;00m\n\u001b[32m 573\u001b[39m \u001b[38;5;66;03m# exception_mapping_worked = True\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m574\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m APIConnectionError(\n\u001b[32m 575\u001b[39m message=\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mAPIConnectionError: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mexception_provider\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m - \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmessage\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 576\u001b[39m llm_provider=custom_llm_provider,\n\u001b[32m 577\u001b[39m model=model,\n\u001b[32m 578\u001b[39m litellm_debug_info=extra_information,\n\u001b[32m 579\u001b[39m request=httpx.Request(\n\u001b[32m 580\u001b[39m method=\u001b[33m\"\u001b[39m\u001b[33mPOST\u001b[39m\u001b[33m\"\u001b[39m, url=\u001b[33m\"\u001b[39m\u001b[33mhttps://api.openai.com/v1/\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 581\u001b[39m ),\n\u001b[32m 582\u001b[39m )\n\u001b[32m 583\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m (\n\u001b[32m 584\u001b[39m custom_llm_provider == \u001b[33m\"\u001b[39m\u001b[33manthropic\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 585\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m custom_llm_provider == \u001b[33m\"\u001b[39m\u001b[33manthropic_text\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 586\u001b[39m ): \u001b[38;5;66;03m# one of the anthropics\u001b[39;00m\n\u001b[32m 587\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mprompt is too long\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m error_str \u001b[38;5;129;01mor\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mprompt: length\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m error_str:\n", - "\u001b[31mAPIConnectionError\u001b[39m: litellm.APIConnectionError: APIConnectionError: OpenAIException - Cannot generate a JsonSchema for core_schema.IsInstanceSchema ()" + "\u001b[31mOSError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 11\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m count_a(\u001b[33m\"\u001b[39m\u001b[33mcherry\u001b[39m\u001b[33m\"\u001b[39m) == \u001b[32m0\u001b[39m\n\u001b[32m 12\u001b[39m \u001b[38;5;66;03m# Print the source code of the generated function\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43minspect\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgetsource\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcount_a\u001b[49m\u001b[43m)\u001b[49m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1285\u001b[39m, in \u001b[36mgetsource\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1279\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgetsource\u001b[39m(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1280\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Return the text of the source code for an object.\u001b[39;00m\n\u001b[32m 1281\u001b[39m \n\u001b[32m 1282\u001b[39m \u001b[33;03m The argument may be a module, class, method, function, traceback, frame,\u001b[39;00m\n\u001b[32m 1283\u001b[39m \u001b[33;03m or code object. The source code is returned as a single string. An\u001b[39;00m\n\u001b[32m 1284\u001b[39m \u001b[33;03m OSError is raised if the source code cannot be retrieved.\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1285\u001b[39m lines, lnum = \u001b[43mgetsourcelines\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mobject\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 1286\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m.join(lines)\n", + "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1267\u001b[39m, in \u001b[36mgetsourcelines\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1259\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Return a list of source lines and starting line number for an object.\u001b[39;00m\n\u001b[32m 1260\u001b[39m \n\u001b[32m 1261\u001b[39m \u001b[33;03mThe argument may be a module, class, method, function, traceback, frame,\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1264\u001b[39m \u001b[33;03moriginal source file the first line of code was found. An OSError is\u001b[39;00m\n\u001b[32m 1265\u001b[39m \u001b[33;03mraised if the source code cannot be retrieved.\"\"\"\u001b[39;00m\n\u001b[32m 1266\u001b[39m \u001b[38;5;28mobject\u001b[39m = unwrap(\u001b[38;5;28mobject\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m1267\u001b[39m lines, lnum = \u001b[43mfindsource\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mobject\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 1269\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m istraceback(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1270\u001b[39m \u001b[38;5;28mobject\u001b[39m = \u001b[38;5;28mobject\u001b[39m.tb_frame\n", + "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1096\u001b[39m, in \u001b[36mfindsource\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1094\u001b[39m lines = linecache.getlines(file)\n\u001b[32m 1095\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m lines:\n\u001b[32m-> \u001b[39m\u001b[32m1096\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m(\u001b[33m'\u001b[39m\u001b[33mcould not get source code\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 1098\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m ismodule(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1099\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m lines, \u001b[32m0\u001b[39m\n", + "\u001b[31mOSError\u001b[39m: could not get source code" ] } ], diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 2e956739..a978148d 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -4,6 +4,7 @@ import collections.abc import dataclasses import inspect +import linecache import tempfile import textwrap import types @@ -76,6 +77,9 @@ def encode( function_name=func_name, module_code=textwrap.dedent(source).strip() ) + # Counter for unique filenames + _decode_counter: typing.ClassVar[int] = 0 + @classmethod def decode( cls, vl: SynthesizedFunction, context: LexicalContext | None = None @@ -89,11 +93,26 @@ def decode( func_name = vl.function_name module_code = textwrap.dedent(vl.module_code).strip() + # Create a unique filename and register source with linecache + # This allows inspect.getsource() to work on the generated function + cls._decode_counter += 1 + filename = f"" + lines = module_code.splitlines(keepends=True) + # Ensure last line has newline for linecache + if lines and not lines[-1].endswith("\n"): + lines[-1] += "\n" + linecache.cache[filename] = ( + len(module_code), + None, + lines, + filename, + ) + # Start with provided context or empty dict exec_globals: dict[str, typing.Any] = dict(context) if context else {} try: - code_obj = compile(module_code, "", "exec") + code_obj = compile(module_code, filename, "exec") exec(code_obj, exec_globals) except SyntaxError as exc: raise SynthesisError( @@ -110,8 +129,7 @@ def decode( ) func = exec_globals[func_name] - # Attach source code to the function for later retrieval - # (inspect.getsource won't work on dynamically generated functions) + # Also attach source code directly for convenience func.__source__ = module_code func.__synthesized__ = vl return func From a36659823db06da99dfb87ba73095ed230de45b1 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:42:50 -0500 Subject: [PATCH 33/65] Factor out program synthesis test to be less flaky --- docs/source/llm.ipynb | 225 ++++++++++-------- tests/test_handlers_llm_provider.py | 38 +-- tests/test_handlers_llm_provider_synthesis.py | 128 ++++++++++ 3 files changed, 261 insertions(+), 130 deletions(-) create mode 100644 tests/test_handlers_llm_provider_synthesis.py diff --git a/docs/source/llm.ipynb b/docs/source/llm.ipynb index a99cbd06..51aa8c46 100644 --- a/docs/source/llm.ipynb +++ b/docs/source/llm.ipynb @@ -87,19 +87,19 @@ "name": "stdout", "output_type": "stream", "text": [ - "Here's a limerick on the theme of fish:\n", - "\n", - "There once was a fish in the sea, \n", - "Who fancied itself fancy-free. \n", - "It swam with such grace, \n", - "In a watery space, \n", - "And danced with the waves in glee.\n", + "In the ocean, a fish swam with glee, \n", + "Darting 'round coral, wild and free. \n", + "With scales shining bright, \n", + "It danced in the light, \n", + "A marvel for all eyes to see.\n", "----------------------------------------\n", - "In the ocean where fish like to play, \n", - "A goldfish swam fast every day. \n", - "With a flick of its fin, \n", - "It wore a big grin, \n", - "And danced in a bubbly ballet.\n" + "Here is a limerick on the theme of fish:\n", + "\n", + "In the ocean, a fish did flip, \n", + "Dancing wildly, giving a dip. \n", + "With scales all agleam, \n", + "In a silvery beam, \n", + "It glided with hardly a slip.\n" ] } ], @@ -129,33 +129,33 @@ "output_type": "stream", "text": [ "\n", - "In the deep blue sea, \n", - "Silent fins softly whisper, \n", - "Life's dance in water.\n", + "In water's embrace, \n", + "Silver scales shimmer and glide, \n", + "Silent currents flow.\n", "----------------------------------------\n", - "In the deep blue sea, \n", - "Silent fins softly whisper, \n", - "Life's dance in water.\n", + "In water's embrace, \n", + "Silver scales shimmer and glide, \n", + "Silent currents flow.\n", "\n", - "Fish beneath waves swim, \n", - "Silent gliders in the deep, \n", - "Dreams in water's flow. \n", + "Fish swim silently, \n", + "Beneath the sunlit ripples, \n", + "Whispers of the deep. \n", "----------------------------------------\n", - "Fish beneath waves swim, \n", - "Silent gliders in the deep, \n", - "Dreams in water's flow. \n", + "Fish swim silently, \n", + "Beneath the sunlit ripples, \n", + "Whispers of the deep. \n", "\n", - "Here's a haiku on the theme of fish:\n", + "Here's a haiku on the theme of fish3:\n", "\n", - "In the clear stream's flow, \n", - "Gliding fins slice through sunlight, \n", - "Silent depths below.\n", + "In gentle streams flow, \n", + "Fish dance with the water's grace, \n", + "Nature's quiet show.\n", "----------------------------------------\n", - "Here's a haiku on the theme of fish:\n", + "Here's a haiku on the theme of fish3:\n", "\n", - "In the clear stream's flow, \n", - "Gliding fins slice through sunlight, \n", - "Silent depths below.\n" + "In gentle streams flow, \n", + "Fish dance with the water's grace, \n", + "Nature's quiet show.\n" ] } ], @@ -236,17 +236,22 @@ "metadata": {}, "outputs": [ { - "ename": "OSError", - "evalue": "could not get source code", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mOSError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 11\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m count_a(\u001b[33m\"\u001b[39m\u001b[33mcherry\u001b[39m\u001b[33m\"\u001b[39m) == \u001b[32m0\u001b[39m\n\u001b[32m 12\u001b[39m \u001b[38;5;66;03m# Print the source code of the generated function\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43minspect\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgetsource\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcount_a\u001b[49m\u001b[43m)\u001b[49m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1285\u001b[39m, in \u001b[36mgetsource\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1279\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mgetsource\u001b[39m(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1280\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Return the text of the source code for an object.\u001b[39;00m\n\u001b[32m 1281\u001b[39m \n\u001b[32m 1282\u001b[39m \u001b[33;03m The argument may be a module, class, method, function, traceback, frame,\u001b[39;00m\n\u001b[32m 1283\u001b[39m \u001b[33;03m or code object. The source code is returned as a single string. An\u001b[39;00m\n\u001b[32m 1284\u001b[39m \u001b[33;03m OSError is raised if the source code cannot be retrieved.\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1285\u001b[39m lines, lnum = \u001b[43mgetsourcelines\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mobject\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 1286\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m.join(lines)\n", - "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1267\u001b[39m, in \u001b[36mgetsourcelines\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1259\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Return a list of source lines and starting line number for an object.\u001b[39;00m\n\u001b[32m 1260\u001b[39m \n\u001b[32m 1261\u001b[39m \u001b[33;03mThe argument may be a module, class, method, function, traceback, frame,\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1264\u001b[39m \u001b[33;03moriginal source file the first line of code was found. An OSError is\u001b[39;00m\n\u001b[32m 1265\u001b[39m \u001b[33;03mraised if the source code cannot be retrieved.\"\"\"\u001b[39;00m\n\u001b[32m 1266\u001b[39m \u001b[38;5;28mobject\u001b[39m = unwrap(\u001b[38;5;28mobject\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m1267\u001b[39m lines, lnum = \u001b[43mfindsource\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mobject\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 1269\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m istraceback(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1270\u001b[39m \u001b[38;5;28mobject\u001b[39m = \u001b[38;5;28mobject\u001b[39m.tb_frame\n", - "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/inspect.py:1096\u001b[39m, in \u001b[36mfindsource\u001b[39m\u001b[34m(object)\u001b[39m\n\u001b[32m 1094\u001b[39m lines = linecache.getlines(file)\n\u001b[32m 1095\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m lines:\n\u001b[32m-> \u001b[39m\u001b[32m1096\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m(\u001b[33m'\u001b[39m\u001b[33mcould not get source code\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 1098\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m ismodule(\u001b[38;5;28mobject\u001b[39m):\n\u001b[32m 1099\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m lines, \u001b[32m0\u001b[39m\n", - "\u001b[31mOSError\u001b[39m: could not get source code" + "name": "stdout", + "output_type": "stream", + "text": [ + "def count_a_occurrences(input_string: str) -> int:\n", + " \"\"\"\n", + " Count the occurrences of the character 'a' in a given string.\n", + "\n", + " :param input_string: The string in which to count 'a' occurrences.\n", + " :return: The number of times 'a' appears in the input string.\n", + " \"\"\"\n", + " count = 0\n", + " for char in input_string:\n", + " if char == 'a':\n", + " count += 1\n", + " return count\n", + "\n" ] } ], @@ -280,7 +285,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "66711301", "metadata": {}, "outputs": [ @@ -292,7 +297,7 @@ "Tool call: weather(*(), **{'city': 'Chicago'}) -> cold\n", "Tool call: weather(*(), **{'city': 'New York'}) -> wet\n", "Tool call: weather(*(), **{'city': 'Barcelona'}) -> sunny\n", - "Among the cities checked, Barcelona has good weather, as it is currently sunny.\n" + "Based on the weather information, Barcelona currently has good weather, described as \"sunny.\"\n" ] } ], @@ -336,7 +341,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "17668ac8", "metadata": {}, "outputs": [ @@ -347,10 +352,10 @@ "> You are onstage at a comedy club. You tell the following joke:\n", "Knock knock.\n", "Who's there?\n", - "Liz.\n", - "Liz who?\n", - "Liz-ard you curious who's at the door?\n", - "> The crowd laughs politely.\n" + "Lizard.\n", + "Lizard who?\n", + "Lizard be the funniest joke you've heard yet!\n", + "> The crowd stares in stony silence.\n" ] } ], @@ -400,7 +405,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "cbf495a2", "metadata": {}, "outputs": [ @@ -408,8 +413,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish2. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]} ModelResponse(id='chatcmpl-CnANzyeB958opw15SxIJ5GLG5eCI8', created=1765834031, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the gentle stream, \\nSilver scales shimmer and dance, \\nQuietly they glide. ', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=20, prompt_tokens=364, total_tokens=384, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n", - "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]} ModelResponse(id='chatcmpl-CnAO05Uemhl4BA8dIUcQyqoKIyOvk', created=1765834032, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the sea where the waves gently swish, \\nLived a fish with a hopeful wish. \\nHe dreamed of the skies, \\nTo soar and to rise, \\nBut alas, he remained just a fish. ', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=46, prompt_tokens=364, total_tokens=410, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n" + "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish2. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]} ModelResponse(id='chatcmpl-CnUE7fbXtVjJgIODaUDGK8gtwmWor', created=1765910299, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"In the deep blue sea, \\nSilver scales gently shimmer— \\nNature's quiet dance.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=19, prompt_tokens=364, total_tokens=383, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n", + "Request fired: () {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]} ModelResponse(id='chatcmpl-CnUE8vMEssF1FzFejbIOcxhnjZiDK', created=1765910300, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the ocean, there swam a bright fish, \\nWhose colors would swirl and swish. \\nHe danced through the sea, \\nSo wild and so free, \\nA sight that fulfilled every wish.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=44, prompt_tokens=364, total_tokens=408, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')\n" ] } ], @@ -443,7 +448,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "81a15f00", "metadata": {}, "outputs": [ @@ -451,11 +456,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOCov9x47s8Jj0K2oGyrB21h9dM', created=1765834044, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"theme\":\"fish\"}', name='haiku_no_cache'), id='call_8gQN3B78H2aZzIdOZPhEqPqy', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=17, prompt_tokens=364, total_tokens=381, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOD4UrpELPotqSt3s76CnJGu6FB', created=1765834045, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the quiet stream, \\nSilver scales shimmer with grace, \\nFish dance in moonlight.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=20, prompt_tokens=363, total_tokens=383, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUE9dXHuFAfKrcdrwc7CRY6H2vz8', created=1765910301, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"theme\":\"fish3\"}', name='haiku_no_cache'), id='call_qslYzCSecwT5pFjoGHwzwi50', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=364, total_tokens=382, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUE96ToBoMlPV1OyIvlnWNFNGVBo', created=1765910301, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"theme\":\"fish\"}', name='haiku_no_cache'), id='call_OZxMIjFhLozcK6mjbbSwlGpK', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=17, prompt_tokens=364, total_tokens=381, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEAZwKsJqw6SVTCIVNxx6dq69SO', created=1765910302, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Silent waters glide, \\nSilver scales flicker below— \\nWhispers of the deep.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=19, prompt_tokens=363, total_tokens=382, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'haiku_no_cache', 'args': (), 'kwargs': {'theme': 'fish'}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3. Do not use any tools.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{\"theme\":\"fish\"}', 'name': 'haiku_no_cache'}, 'id': 'call_8gQN3B78H2aZzIdOZPhEqPqy', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_8gQN3B78H2aZzIdOZPhEqPqy', 'name': 'haiku_no_cache', 'content': [{'type': 'text', 'text': 'In the quiet stream, \\nSilver scales shimmer with grace, \\nFish dance in moonlight.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOEXNCJJEDIOBwwR8PaKYXoqOCs', created=1765834046, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the quiet stream, \\nSilver scales shimmer with grace, \\nFish dance in moonlight.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=20, prompt_tokens=410, total_tokens=430, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish4. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOEfHrN8jTkm6tIJ7EzgXP9bo2d', created=1765834046, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=45, prompt_tokens=365, total_tokens=410, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n" + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3. Do not use any tools.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{\"theme\":\"fish\"}', 'name': 'haiku_no_cache'}, 'id': 'call_OZxMIjFhLozcK6mjbbSwlGpK', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_OZxMIjFhLozcK6mjbbSwlGpK', 'name': 'haiku_no_cache', 'content': [{'type': 'text', 'text': 'Silent waters glide, \\nSilver scales flicker below— \\nWhispers of the deep.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEA73rIuQD7tQWwNwvMlolNgoRO', created=1765910302, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Here is a haiku on the theme of fish:\\n\\nSilent waters glide, \\nSilver scales flicker below— \\nWhispers of the deep.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=30, prompt_tokens=409, total_tokens=439, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'tool': 'haiku_no_cache', 'args': (), 'kwargs': {'theme': 'fish3'}}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a haiku on the theme of fish3. Do not use any tools.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{\"theme\":\"fish3\"}', 'name': 'haiku_no_cache'}, 'id': 'call_qslYzCSecwT5pFjoGHwzwi50', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_qslYzCSecwT5pFjoGHwzwi50', 'name': 'haiku_no_cache', 'content': [{'type': 'text', 'text': 'Here is a haiku on the theme of fish:\\n\\nSilent waters glide, \\nSilver scales flicker below— \\nWhispers of the deep.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEBG8aYhVGdEoMD6f1XUvs2NyBd', created=1765910303, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Here is a haiku on the theme of fish:\\n\\nSilent waters glide, \\nSilver scales flicker below— \\nWhispers of the deep.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=30, prompt_tokens=421, total_tokens=451, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a limerick on the theme of fish4. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUECinXFKY0SYBZuze4iP2R9DSJL', created=1765910304, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=41, prompt_tokens=365, total_tokens=406, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n" ] } ], @@ -493,7 +501,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "78a4bf44", "metadata": {}, "outputs": [ @@ -501,20 +509,46 @@ "name": "stdout", "output_type": "stream", "text": [ - "Sub-templates available to write_story: [Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': ..., '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': ..., '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='limerick'), Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': ..., 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': ..., 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='haiku_no_cache'), Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': ..., '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': ..., '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='primes'), Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': ..., 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': ..., 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='count_char'), Operation(cities, () -> list[str]), Operation(weather, (city: str) -> str), Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': ..., 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': ..., 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='vacation'), Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': ..., 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': ..., 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='write_joke'), Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': ..., 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': ..., 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='rate_joke'), Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': ..., 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': ..., 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='story_with_moral'), Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': ..., 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': ..., 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='story_funny'), Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': ...}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': 'In the ocean where fishies do play, \\nA big whale came swimming one day. \\nWith a splash and a dive, \\nHe felt so alive, \\nChasing fish in the blue, gleaming bay.', '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i5': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', '_i6': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i7': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i8': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i9': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i10': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i11': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '_i12': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i13': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i14': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i15': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': ...})), __name__='write_story')]\n", + "Sub-templates available to write_story: [Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': ..., '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': ..., '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='limerick'), Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': ..., 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': ..., 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='haiku_no_cache'), Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': ..., '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': ..., '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='primes'), Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': ..., 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': ..., 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='count_char'), Operation(cities, () -> list[str]), Operation(weather, (city: str) -> str), Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': ..., 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': ..., 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='vacation'), Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': ..., 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': ..., 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='write_joke'), Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': ..., 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': ..., 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='rate_joke'), Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': ..., 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': ..., 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='story_with_moral'), Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': ..., 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': ..., 'write_story': Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_story')})), __name__='story_funny'), Template(__prompt_template__=\"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", __signature__= str>, __context__=LexicalContext(mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': ...}), mappingproxy({'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': , '__builtins__': , '_ih': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], '_oh': {}, '_dh': [PosixPath('/Users/datnguyenthanh/Marc/effectful')], 'In': ['', 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))'], 'Out': {}, 'get_ipython': >, 'exit': , 'quit': , 'open': , '_': \"In the sea, a fish named Four, \\nSwam circles, but wanted more. \\nHe found a big dish, \\nOf delightful fish, \\nNow he's the happiest on the ocean floor!\", '__': '', '___': '', '__vsc_ipynb_file__': '/Users/datnguyenthanh/Marc/effectful/docs/source/llm.ipynb', '_i': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', '_ii': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', '_iii': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', '_i1': 'import dataclasses\\nimport functools\\nimport inspect\\nimport logging\\nimport sys\\nfrom collections.abc import Callable\\n\\nfrom effectful.handlers.llm import Template\\nfrom effectful.handlers.llm.providers import (\\n CacheLLMRequestHandler,\\n LiteLLMProvider,\\n LLMLoggingHandler,\\n RetryLLMHandler,\\n completion,\\n tool_call,\\n)\\nfrom effectful.handlers.llm.synthesis import ProgramSynthesis\\nfrom effectful.ops.semantics import NotHandled, fwd, handler\\nfrom effectful.ops.syntax import defop\\n\\nprovider = LiteLLMProvider()', 'dataclasses': , 'functools': , 'inspect': , 'logging': , 'sys': , 'Callable': , 'Template': , 'CacheLLMRequestHandler': , 'LiteLLMProvider': , 'LLMLoggingHandler': , 'RetryLLMHandler': , 'completion': Operation(completion, (model: str, messages: List = [], timeout: Union[float, str, openai.Timeout, NoneType] = None, temperature: Optional[float] = None, top_p: Optional[float] = None, n: Optional[int] = None, stream: Optional[bool] = None, stream_options: Optional[dict] = None, stop=None, max_completion_tokens: Optional[int] = None, max_tokens: Optional[int] = None, modalities: Optional[List[Literal['text', 'audio']]] = None, prediction: Optional[openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam] = None, audio: Optional[openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam] = None, presence_penalty: Optional[float] = None, frequency_penalty: Optional[float] = None, logit_bias: Optional[dict] = None, user: Optional[str] = None, reasoning_effort: Optional[Literal['none', 'minimal', 'low', 'medium', 'high', 'default']] = None, verbosity: Optional[Literal['low', 'medium', 'high']] = None, response_format: Union[dict, Type[pydantic.main.BaseModel], NoneType] = None, seed: Optional[int] = None, tools: Optional[List] = None, tool_choice: Union[str, dict, NoneType] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, parallel_tool_calls: Optional[bool] = None, web_search_options: Optional[litellm.types.llms.openai.OpenAIWebSearchOptions] = None, deployment_id=None, extra_headers: Optional[dict] = None, safety_identifier: Optional[str] = None, service_tier: Optional[str] = None, functions: Optional[List] = None, function_call: Optional[str] = None, base_url: Optional[str] = None, api_version: Optional[str] = None, api_key: Optional[str] = None, model_list: Optional[list] = None, thinking: Optional[litellm.types.llms.anthropic.AnthropicThinkingParam] = None, shared_session: Optional[ForwardRef('ClientSession')] = None, **kwargs) -> Union[litellm.types.utils.ModelResponse, litellm.litellm_core_utils.streaming_handler.CustomStreamWrapper]), 'tool_call': Operation(tool_call, (template: effectful.handlers.llm.Template, tool: Union[effectful.ops.types.Operation[..., T], effectful.handlers.llm.Template[..., T]], *args, **kwargs) -> T), 'ProgramSynthesis': , 'NotHandled': , 'fwd': Operation(fwd, (*args, **kwargs) -> Any), 'handler': , 'defop': , 'provider': , '_i2': '@Template.define\\ndef limerick(theme: str) -> str:\\n \"\"\"Write a limerick on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled', 'limerick': Template(__prompt_template__='Write a limerick on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='limerick'), '_i3': 'with handler(provider):\\n print(limerick(\"fish\"))\\n print(\"-\" * 40)\\n print(limerick(\"fish\"))', '_i4': '@functools.cache\\n@Template.define\\ndef haiku(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef haiku_no_cache(theme: str) -> str:\\n \"\"\"Write a haiku on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nprint()\\nwith handler(provider):\\n print(haiku(\"fish\"))\\n print(\"-\" * 40)\\n print(haiku(\"fish\"))\\n\\nprint()\\ncache_handler1 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler1):\\n print(haiku_no_cache(\"fish2\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish2\"))\\n\\nprint()\\ncache_handler2 = CacheLLMRequestHandler()\\nwith handler(provider), handler(cache_handler2):\\n print(haiku_no_cache(\"fish3\"))\\n print(\"-\" * 40)\\n print(haiku_no_cache(\"fish3\"))', 'haiku': , 'haiku_no_cache': Template(__prompt_template__='Write a haiku on the theme of {theme}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='haiku_no_cache'), 'cache_handler1': , 'cache_handler2': , '_i5': '@Template.define\\ndef primes(first_digit: int) -> int:\\n \"\"\"Give a prime number with {first_digit} as the first digit. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider):\\n assert type(primes(6)) is int', 'primes': Template(__prompt_template__='Give a prime number with {first_digit} as the first digit. Do not use any tools.', __signature__= int>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='primes'), '_i6': '@Template.define\\ndef count_char(char: str) -> Callable[[str], int]:\\n \"\"\"Write a function which takes a string and counts the occurrances of \\'{char}\\'. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\nwith handler(provider), handler(ProgramSynthesis()):\\n count_a = count_char(\"a\")\\n assert callable(count_a)\\n assert count_a(\"banana\") == 3\\n assert count_a(\"cherry\") == 0\\n # Print the source code of the generated function\\n print(inspect.getsource(count_a))', 'count_char': Template(__prompt_template__=\"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", __signature__= collections.abc.Callable[[str], int]>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='count_char'), 'count_a': , '_i7': '@defop\\ndef cities() -> list[str]:\\n return [\"Chicago\", \"New York\", \"Barcelona\"]\\n\\n\\n@defop\\ndef weather(city: str) -> str:\\n status = {\"Chicago\": \"cold\", \"New York\": \"wet\", \"Barcelona\": \"sunny\"}\\n return status.get(city, \"unknown\")\\n\\n\\n@Template.define # cities and weather auto-captured from lexical scope\\ndef vacation() -> str:\\n \"\"\"Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.\"\"\"\\n raise NotHandled\\n\\n\\ndef log_tool_call(_, tool, *args, **kwargs):\\n result = fwd()\\n print(f\"Tool call: {tool}(*{args}, **{kwargs}) -> {result}\")\\n return result\\n\\n\\nwith handler(provider), handler({tool_call: log_tool_call}):\\n print(vacation())', 'cities': Operation(cities, () -> list[str]), 'weather': Operation(weather, (city: str) -> str), 'vacation': Template(__prompt_template__='Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='vacation'), 'log_tool_call': , '_i8': '@dataclasses.dataclass\\nclass KnockKnockJoke:\\n whos_there: str\\n punchline: str\\n\\n\\n@Template.define\\ndef write_joke(theme: str) -> KnockKnockJoke:\\n \"\"\"Write a knock-knock joke on the theme of {theme}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef rate_joke(joke: KnockKnockJoke) -> bool:\\n \"\"\"Decide if {joke} is funny or not. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\ndef do_comedy():\\n joke = write_joke(\"lizards\")\\n print(\"> You are onstage at a comedy club. You tell the following joke:\")\\n print(\\n f\"Knock knock.\\\\nWho\\'s there?\\\\n{joke.whos_there}.\\\\n{joke.whos_there} who?\\\\n{joke.punchline}\"\\n )\\n if rate_joke(joke):\\n print(\"> The crowd laughs politely.\")\\n else:\\n print(\"> The crowd stares in stony silence.\")\\n\\n\\nwith handler(provider):\\n do_comedy()', 'KnockKnockJoke': , 'write_joke': Template(__prompt_template__='Write a knock-knock joke on the theme of {theme}. Do not use any tools.', __signature__= __main__.KnockKnockJoke>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='write_joke'), 'rate_joke': Template(__prompt_template__='Decide if {joke} is funny or not. Do not use any tools.', __signature__= bool>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='rate_joke'), 'do_comedy': , '_i9': 'def log_llm(*args, **kwargs):\\n result = fwd()\\n print(\"Request fired: \", args, kwargs, result)\\n return result\\n\\n\\n# Avoid cache\\ntry:\\n haiku.cache_clear()\\nexcept Exception:\\n pass\\n\\n# Put completion handler innermost so it has highest precedence during the call\\nwith handler(provider), handler({completion: log_llm}):\\n _ = haiku(\"fish2\")\\n _ = limerick(\"fish\") # or use haiku(\"fish-2\") to avoid cache', 'log_llm': , '_i10': '# 1. Create a logger\\nlogger = logging.getLogger(\"effectful.llm\")\\nlogger.setLevel(logging.INFO)\\nlog_handler = logging.StreamHandler(sys.stdout)\\nlog_handler.setFormatter(logging.Formatter(\"%(levelname)s %(payload)s\"))\\nlogger.addHandler(log_handler)\\n# 2. Pass it to the handler\\nllm_logger = LLMLoggingHandler(logger=logger) # can also be LLMLoggingHandler()\\n\\n# Avoid cache for demonstration\\ntry:\\n haiku.cache_clear()\\n limerick.cache_clear()\\nexcept Exception:\\n pass\\n\\nwith handler(provider), handler(llm_logger):\\n _ = haiku(\"fish3\")\\n _ = limerick(\"fish4\")', 'logger': , 'log_handler': , 'llm_logger': , '_i11': '# Sub-templates for different story styles\\n@Template.define\\ndef story_with_moral(topic: str) -> str:\\n \"\"\"Write a short story about {topic} and end with a moral lesson. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n@Template.define\\ndef story_funny(topic: str) -> str:\\n \"\"\"Write a funny, humorous story about {topic}. Do not use any tools.\"\"\"\\n raise NotHandled\\n\\n\\n# Main orchestrator template - has access to sub-templates\\n@Template.define\\ndef write_story(topic: str, style: str) -> str:\\n \"\"\"Write a story about {topic} in the style: {style}.\\n Available styles: \\'moral\\' for a story with a lesson, \\'funny\\' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"\"\"\\n raise NotHandled\\n\\n\\n# Verify sub-templates are captured in write_story\\'s lexical context\\nassert story_with_moral in write_story.tools\\nassert story_funny in write_story.tools\\nprint(\"Sub-templates available to write_story:\", list(write_story.tools))\\n\\nwith handler(provider), handler(llm_logger):\\n print(\"=== Story with moral ===\")\\n print(write_story(\"a curious cat\", \"moral\"))\\n print()\\n print(\"=== Funny story ===\")\\n print(write_story(\"a curious cat\", \"funny\"))', 'story_with_moral': Template(__prompt_template__='Write a short story about {topic} and end with a moral lesson. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_with_moral'), 'story_funny': Template(__prompt_template__='Write a funny, humorous story about {topic}. Do not use any tools.', __signature__= str>, __context__=LexicalContext(mappingproxy({...}), mappingproxy({...})), __name__='story_funny'), 'write_story': ...})), __name__='write_story')]\n", "=== Story with moral ===\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': \"Write a story about a curious cat in the style: moral.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOJJzdrkeZmPdyh1h0cMSFZlFJE', created=1765834051, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"topic\":\"a curious cat\"}', name='story_with_moral'), id='call_nJMDv3AxDTvyxxoDKXAzH4aB', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=560, total_tokens=578, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a short story about a curious cat and end with a moral lesson. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOJnGjxZtPLcZ9ekNXtCUneEitd', created=1765834051, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='In a quaint village nestled between rolling hills and whispering streams, there lived a cat named Whiskers. Whiskers was not an ordinary cat; his sleek, shiny coat gleamed under the sun, and his eyes sparkled with a clever curiosity that set him apart. His inquisitive nature drove him to explore every nook and cranny of the village, always seeking new adventures.\\n\\nOne day, while wandering near the woods, Whiskers stumbled upon a mysterious path he had never seen before. The path was lined with wildflowers and arched by towering trees that seemed to stretch on forever. Intrigued, Whiskers decided to follow it to see where it would lead.\\n\\nThe further he ventured, the stranger the path became. He encountered bubbling brooks, frogs that croaked like they were sharing secrets, and birds that sang unfamiliar melodies. Despite the eerie feeling curling around his paws, Whiskers pressed on.\\n\\nAfter what felt like hours, he arrived at a clearing with a peculiar sight: a large cage in the center with a small bird trapped inside. The bird chirped desperately, its tiny eyes pleading for help. Whiskers, though naturally inclined to chase birds, felt a tug of compassion watching the helpless creature.\\n\\nUsing his sharp claws, Whiskers carefully picked at the lock until it clicked open. The bird flapped its wings gratefully and soared into the sky, singing a joyful tune. Whiskers watched it disappear among the clouds, a warm feeling blossoming in his chest.\\n\\nContent with his good deed, Whiskers made his way back home, sticking to the original path. As he lay in his favorite sun-dappled spot on the porch, he reflected on his adventure.\\n\\nThe moral of the story is: Curiosity is a beautiful thing that leads to new discoveries, but it must be guided by kindness and the courage to act, for it is in helping others that we find our true purpose.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=395, prompt_tokens=528, total_tokens=923, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': \"Write a story about a curious cat in the style: moral.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEDqkyey8hGyUP8N4pApvKotzCg', created=1765910305, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"topic\":\"a curious cat\"}', name='story_with_moral'), id='call_VSAs7DmCe4maSEmL5vuI55Qt', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=560, total_tokens=578, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a short story about a curious cat and end with a moral lesson. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEDpLxPDuwMHW8vcASn4N8aWY0x', created=1765910305, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Once upon a time in a quaint little village, there lived a cat named Whiskers. Whiskers was no ordinary cat; he was curious beyond measure. Every nook and cranny of the village had been explored by his nimble paws, and every villager knew of his adventurous spirit.\\n\\nOne day, as Whiskers roamed the cobblestone streets, a glint of something shiny caught his eye. Nestled between two large stones at the base of a dilapidated wall was a curious object — a small, silver key. Whiskers, being the inquisitive feline that he was, decided to investigate further.\\n\\nHe carefully pawed the key from its hiding spot and began to search for what it might unlock. Whiskers explored every door, gate, and chest he could find, but none yielded to the mysterious key. Just as he was about to give up, he noticed an old, overgrown path leading to the back of the village where a forgotten garden lay in ruins.\\n\\nWith the silver key dangling from his mouth, Whiskers bounded down the path. At the end, hidden behind a curtain of ivy, stood an iron gate, rusted and long unused. With a determined leap, Whiskers stretched and placed the key into the rusty lock. To his surprise, the lock clicked open, and the gate creaked ajar.\\n\\nBeyond the gate was an enchanting garden, overgrown but full of beauty. Whiskers marveled at the vibrant flowers and the gentle bubbling brook that meandered through the garden. Birds chirped a melodious tune, and butterflies danced in the air. It was a secret paradise, long forgotten by the villagers.\\n\\nWhiskers spent hours exploring the garden, every corner holding another surprise. As the sun began to set, casting a golden hue over the garden, Whiskers realized it was time to return to the village. He knew he had discovered something special, something that even the humans had forgotten.\\n\\nThe next day, he led the villagers to the garden, where they were astonished by its hidden beauty. The villagers decided to restore the garden, turning it into a communal space for all to enjoy. Thanks to Whiskers' curiosity, the village now had a place of wonder and relaxation.\\n\\nThe moral of the story is: Curiosity, when pursued mindfully, can lead to wonderful discoveries and opportunities that enrich both your life and those of others.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=495, prompt_tokens=528, total_tokens=1023, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'story_with_moral', 'args': (), 'kwargs': {'topic': 'a curious cat'}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': \"Write a story about a curious cat in the style: moral.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{\"topic\":\"a curious cat\"}', 'name': 'story_with_moral'}, 'id': 'call_nJMDv3AxDTvyxxoDKXAzH4aB', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_nJMDv3AxDTvyxxoDKXAzH4aB', 'name': 'story_with_moral', 'content': [{'type': 'text', 'text': 'In a quaint village nestled between rolling hills and whispering streams, there lived a cat named Whiskers. Whiskers was not an ordinary cat; his sleek, shiny coat gleamed under the sun, and his eyes sparkled with a clever curiosity that set him apart. His inquisitive nature drove him to explore every nook and cranny of the village, always seeking new adventures.\\n\\nOne day, while wandering near the woods, Whiskers stumbled upon a mysterious path he had never seen before. The path was lined with wildflowers and arched by towering trees that seemed to stretch on forever. Intrigued, Whiskers decided to follow it to see where it would lead.\\n\\nThe further he ventured, the stranger the path became. He encountered bubbling brooks, frogs that croaked like they were sharing secrets, and birds that sang unfamiliar melodies. Despite the eerie feeling curling around his paws, Whiskers pressed on.\\n\\nAfter what felt like hours, he arrived at a clearing with a peculiar sight: a large cage in the center with a small bird trapped inside. The bird chirped desperately, its tiny eyes pleading for help. Whiskers, though naturally inclined to chase birds, felt a tug of compassion watching the helpless creature.\\n\\nUsing his sharp claws, Whiskers carefully picked at the lock until it clicked open. The bird flapped its wings gratefully and soared into the sky, singing a joyful tune. Whiskers watched it disappear among the clouds, a warm feeling blossoming in his chest.\\n\\nContent with his good deed, Whiskers made his way back home, sticking to the original path. As he lay in his favorite sun-dappled spot on the porch, he reflected on his adventure.\\n\\nThe moral of the story is: Curiosity is a beautiful thing that leads to new discoveries, but it must be guided by kindness and the courage to act, for it is in helping others that we find our true purpose.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOQIJsyohvBlsBGz9cztpetifUP', created=1765834058, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Here's a story about a curious cat named Whiskers who embarks on an adventure that teaches him an important lesson about kindness and courage. Whiskers' curiosity leads him to explore a mysterious path where he eventually discovers a trapped bird. Instead of succumbing to his natural instincts, Whiskers chooses to help the bird, freeing it from its cage. Through this act of compassion, Whiskers learns that while curiosity can lead to new discoveries, it is the courage to act with kindness that truly defines us.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=105, prompt_tokens=982, total_tokens=1087, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "Here's a story about a curious cat named Whiskers who embarks on an adventure that teaches him an important lesson about kindness and courage. Whiskers' curiosity leads him to explore a mysterious path where he eventually discovers a trapped bird. Instead of succumbing to his natural instincts, Whiskers chooses to help the bird, freeing it from its cage. Through this act of compassion, Whiskers learns that while curiosity can lead to new discoveries, it is the courage to act with kindness that truly defines us.\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': \"Write a story about a curious cat in the style: moral.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{\"topic\":\"a curious cat\"}', 'name': 'story_with_moral'}, 'id': 'call_VSAs7DmCe4maSEmL5vuI55Qt', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_VSAs7DmCe4maSEmL5vuI55Qt', 'name': 'story_with_moral', 'content': [{'type': 'text', 'text': \"Once upon a time in a quaint little village, there lived a cat named Whiskers. Whiskers was no ordinary cat; he was curious beyond measure. Every nook and cranny of the village had been explored by his nimble paws, and every villager knew of his adventurous spirit.\\n\\nOne day, as Whiskers roamed the cobblestone streets, a glint of something shiny caught his eye. Nestled between two large stones at the base of a dilapidated wall was a curious object — a small, silver key. Whiskers, being the inquisitive feline that he was, decided to investigate further.\\n\\nHe carefully pawed the key from its hiding spot and began to search for what it might unlock. Whiskers explored every door, gate, and chest he could find, but none yielded to the mysterious key. Just as he was about to give up, he noticed an old, overgrown path leading to the back of the village where a forgotten garden lay in ruins.\\n\\nWith the silver key dangling from his mouth, Whiskers bounded down the path. At the end, hidden behind a curtain of ivy, stood an iron gate, rusted and long unused. With a determined leap, Whiskers stretched and placed the key into the rusty lock. To his surprise, the lock clicked open, and the gate creaked ajar.\\n\\nBeyond the gate was an enchanting garden, overgrown but full of beauty. Whiskers marveled at the vibrant flowers and the gentle bubbling brook that meandered through the garden. Birds chirped a melodious tune, and butterflies danced in the air. It was a secret paradise, long forgotten by the villagers.\\n\\nWhiskers spent hours exploring the garden, every corner holding another surprise. As the sun began to set, casting a golden hue over the garden, Whiskers realized it was time to return to the village. He knew he had discovered something special, something that even the humans had forgotten.\\n\\nThe next day, he led the villagers to the garden, where they were astonished by its hidden beauty. The villagers decided to restore the garden, turning it into a communal space for all to enjoy. Thanks to Whiskers' curiosity, the village now had a place of wonder and relaxation.\\n\\nThe moral of the story is: Curiosity, when pursued mindfully, can lead to wonderful discoveries and opportunities that enrich both your life and those of others.\"}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEOPrpbgOHb7HX300i0JtULBTfY', created=1765910316, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Once upon a time in a quaint little village, there lived a cat named Whiskers. Whiskers was no ordinary cat; he was curious beyond measure. Every nook and cranny of the village had been explored by his nimble paws, and every villager knew of his adventurous spirit.\\n\\nOne day, as Whiskers roamed the cobblestone streets, a glint of something shiny caught his eye. Nestled between two large stones at the base of a dilapidated wall was a curious object — a small, silver key. Whiskers, being the inquisitive feline that he was, decided to investigate further.\\n\\nHe carefully pawed the key from its hiding spot and began to search for what it might unlock. Whiskers explored every door, gate, and chest he could find, but none yielded to the mysterious key. Just as he was about to give up, he noticed an old, overgrown path leading to the back of the village where a forgotten garden lay in ruins.\\n\\nWith the silver key dangling from his mouth, Whiskers bounded down the path. At the end, hidden behind a curtain of ivy, stood an iron gate, rusted and long unused. With a determined leap, Whiskers stretched and placed the key into the rusty lock. To his surprise, the lock clicked open, and the gate creaked ajar.\\n\\nBeyond the gate was an enchanting garden, overgrown but full of beauty. Whiskers marveled at the vibrant flowers and the gentle bubbling brook that meandered through the garden. Birds chirped a melodious tune, and butterflies danced in the air. It was a secret paradise, long forgotten by the villagers.\\n\\nWhiskers spent hours exploring the garden, every corner holding another surprise. As the sun began to set, casting a golden hue over the garden, Whiskers realized it was time to return to the village. He knew he had discovered something special, something that even the humans had forgotten.\\n\\nThe next day, he led the villagers to the garden, where they were astonished by its hidden beauty. The villagers decided to restore the garden, turning it into a communal space for all to enjoy. Thanks to Whiskers' curiosity, the village now had a place of wonder and relaxation.\\n\\nThe moral of the story is: Curiosity, when pursued mindfully, can lead to wonderful discoveries and opportunities that enrich both your life and those of others.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=495, prompt_tokens=1082, total_tokens=1577, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "Once upon a time in a quaint little village, there lived a cat named Whiskers. Whiskers was no ordinary cat; he was curious beyond measure. Every nook and cranny of the village had been explored by his nimble paws, and every villager knew of his adventurous spirit.\n", + "\n", + "One day, as Whiskers roamed the cobblestone streets, a glint of something shiny caught his eye. Nestled between two large stones at the base of a dilapidated wall was a curious object — a small, silver key. Whiskers, being the inquisitive feline that he was, decided to investigate further.\n", + "\n", + "He carefully pawed the key from its hiding spot and began to search for what it might unlock. Whiskers explored every door, gate, and chest he could find, but none yielded to the mysterious key. Just as he was about to give up, he noticed an old, overgrown path leading to the back of the village where a forgotten garden lay in ruins.\n", + "\n", + "With the silver key dangling from his mouth, Whiskers bounded down the path. At the end, hidden behind a curtain of ivy, stood an iron gate, rusted and long unused. With a determined leap, Whiskers stretched and placed the key into the rusty lock. To his surprise, the lock clicked open, and the gate creaked ajar.\n", + "\n", + "Beyond the gate was an enchanting garden, overgrown but full of beauty. Whiskers marveled at the vibrant flowers and the gentle bubbling brook that meandered through the garden. Birds chirped a melodious tune, and butterflies danced in the air. It was a secret paradise, long forgotten by the villagers.\n", + "\n", + "Whiskers spent hours exploring the garden, every corner holding another surprise. As the sun began to set, casting a golden hue over the garden, Whiskers realized it was time to return to the village. He knew he had discovered something special, something that even the humans had forgotten.\n", + "\n", + "The next day, he led the villagers to the garden, where they were astonished by its hidden beauty. The villagers decided to restore the garden, turning it into a communal space for all to enjoy. Thanks to Whiskers' curiosity, the village now had a place of wonder and relaxation.\n", + "\n", + "The moral of the story is: Curiosity, when pursued mindfully, can lead to wonderful discoveries and opportunities that enrich both your life and those of others.\n", "\n", "=== Funny story ===\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': \"Write a story about a curious cat in the style: funny.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOTXQYpAjE6AhrSHrREbvtgiSzs', created=1765834061, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"topic\":\"a curious cat\"}', name='story_funny'), id='call_zMjPfWzaDFKuswF7HiHFFu4R', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=17, prompt_tokens=560, total_tokens=577, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a funny, humorous story about a curious cat. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOUficJNRttQKM2b9t8qJDzfXnQ', created=1765834062, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Once upon a time in the quaint little town of Whiskerfield, there lived a particularly curious cat named Whiskers. Now, Whiskers wasn't your average feline; he had a knack for getting himself into the most bizarre situations, much to the amusement of the townsfolk.\\n\\nOne sunny morning, as Whiskers ventured out of his cozy basket, he noticed a peculiar shiny object gleaming in the garden. It was unlike anything he had ever seen before – a mix between a large spoon and a tiny satellite dish. His curiosity piqued, Whiskers approached with his usual stealth, attempting to decipher this mysterious contraption.\\n\\nUnbeknownst to Whiskers, the shiny object was none other than the town's new state-of-the-art bird feeder, designed with reflective surfaces to keep the squirrels away. But to Whiskers, it was the most intriguing puzzle he'd ever encountered. With his tail twitching like a metronome, Whiskers pounced at the feeder, only to collide with its slippery surface and land unceremoniously on his back, paws in the air.\\n\\nUndeterred by his clumsy introduction, Whiskers began his investigation with fervor. He circled the feeder, pawing at it and meowing loudly, as if expecting a response. The neighborhood birds watched from a safe distance, chirping in a chorus that resembled laughter. Whiskers, paying no mind to his feathered audience, was determined to unlock the secrets of this shiny beacon.\\n\\nAs noon approached, Whiskers, now slightly exasperated and hungry, decided to enlist the help of his best friend, Rover the golden retriever. Rover, although quite good-natured, wasn't exactly the brains of their operation, but he was always up for an adventure. With wagging tails and determined purrs, the duo devised a plan. Rover would use his weight to tip the feeder, while Whiskers would keep an eye out for any unexpected critters.\\n\\nThe plan was in motion. Rover, in his typical bounding style, lunged at the feeder, causing it to wobble precariously. Just as it began to tip, a sudden gust of wind swung the contraption in a whirlwind of seeds and reflections. Whiskers and Rover, caught in the midst of this flying feast, found themselves covered in birdseed, with Whiskers' fur boasting a collection of tiny sunflower hats.\\n\\nAs they sat there, bewildered and giggling in their own peculiar way, the townsfolk couldn't help but chuckle at the antics of Whiskers and Rover. Even the birds stopped their fluttering to admire the spectacle. From that day on, the bird feeder was not just a source of food for the birds, but also a stage for Whiskerfield's most unexpected entertainment duo.\\n\\nAnd so, Whiskers the curious cat learned an important lesson: sometimes, curiosity might not uncover the mysteries you expect, but it certainly creates the most memorable adventures. And as for Rover, well, he just loved being part of the fun, birdseed hats and all.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=632, prompt_tokens=524, total_tokens=1156, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': \"Write a story about a curious cat in the style: funny.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUESoVAhiOnofpdjEJ9kfxosCVCr', created=1765910320, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"topic\":\"a curious cat\"}', name='story_funny'), id='call_y9nl1iqWc1CJwphS7L1dN4H5', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=17, prompt_tokens=560, total_tokens=577, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Write a funny, humorous story about a curious cat. Do not use any tools.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUESKUUgAC77rItOvG1Sh227kCId', created=1765910320, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Once upon a time in the quaint little village of Whiskerville, lived a curious cat named Oliver. Oliver wasn\\'t just any ordinary cat; he was a feline with an insatiable appetite for adventure and perhaps, just as importantly, for snacks.\\n\\nOne sunny morning, as the villagers went about their daily routines, Oliver noticed something peculiar. A new aroma wafted through the air—a delightful mix of fish and chicken, with just a hint of something exotic. Perplexed and intrigued, Oliver decided it was his duty to investigate. After all, curiosity didn\\'t just kill the cat; it fed it delicious snacks.\\n\\nHis nose led him to the local bakery, which had recently started experimenting with \"gourmet pet pastries.\" As Oliver tiptoed inside, unnoticed by the busy baker, he spotted an array of treats on a shelf. The pièce de résistance was a cake that read \"Happy Birthday Max\" in bold, colorful icing.\\n\\nOliver, assuming this \"Max\" character could afford to miss one slice (or perhaps the entire cake), climbed onto the shelf. With expert precision, he nibbled around the edges, savoring each bite like the connoisseur he was. In his mind, he imagined the villagers applauding his refined palate and heralding him as \"Oliver, the Culinary Cat.\"\\n\\nJust then, the baker returned and realized what had happened. \"Oh no! Max\\'s birthday cake!\" she exclaimed, spotting Oliver mid-feast. Oliver, caught in the act, adopted his most innocent \"I\\'m just a cute kitty\" face, but the baker was less than impressed.\\n\\nIn a moment of inspiration—or perhaps desperation—Oliver decided to make a quick escape. He knocked over a bag of flour, creating a cloud of white, giving him just enough cover to dart out the back door and make his getaway. The baker, though exasperated, couldn\\'t help but chuckle as Oliver, now a ghostly white from the flour, trotted down the street with the last remnants of the cake still sticking to his whiskers.\\n\\nFrom that day on, Oliver earned a new nickname among the villagers: \"The Cake Bandit of Whiskerville.\" Despite the fiasco, he was more revered than ever, proving once again that even the cheekiest of cats could win hearts—and snacks—with a touch of humor and a sprinkle of charm.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=476, prompt_tokens=524, total_tokens=1000, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'story_funny', 'args': (), 'kwargs': {'topic': 'a curious cat'}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': \"Write a story about a curious cat in the style: funny.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{\"topic\":\"a curious cat\"}', 'name': 'story_funny'}, 'id': 'call_zMjPfWzaDFKuswF7HiHFFu4R', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_zMjPfWzaDFKuswF7HiHFFu4R', 'name': 'story_funny', 'content': [{'type': 'text', 'text': \"Once upon a time in the quaint little town of Whiskerfield, there lived a particularly curious cat named Whiskers. Now, Whiskers wasn't your average feline; he had a knack for getting himself into the most bizarre situations, much to the amusement of the townsfolk.\\n\\nOne sunny morning, as Whiskers ventured out of his cozy basket, he noticed a peculiar shiny object gleaming in the garden. It was unlike anything he had ever seen before – a mix between a large spoon and a tiny satellite dish. His curiosity piqued, Whiskers approached with his usual stealth, attempting to decipher this mysterious contraption.\\n\\nUnbeknownst to Whiskers, the shiny object was none other than the town's new state-of-the-art bird feeder, designed with reflective surfaces to keep the squirrels away. But to Whiskers, it was the most intriguing puzzle he'd ever encountered. With his tail twitching like a metronome, Whiskers pounced at the feeder, only to collide with its slippery surface and land unceremoniously on his back, paws in the air.\\n\\nUndeterred by his clumsy introduction, Whiskers began his investigation with fervor. He circled the feeder, pawing at it and meowing loudly, as if expecting a response. The neighborhood birds watched from a safe distance, chirping in a chorus that resembled laughter. Whiskers, paying no mind to his feathered audience, was determined to unlock the secrets of this shiny beacon.\\n\\nAs noon approached, Whiskers, now slightly exasperated and hungry, decided to enlist the help of his best friend, Rover the golden retriever. Rover, although quite good-natured, wasn't exactly the brains of their operation, but he was always up for an adventure. With wagging tails and determined purrs, the duo devised a plan. Rover would use his weight to tip the feeder, while Whiskers would keep an eye out for any unexpected critters.\\n\\nThe plan was in motion. Rover, in his typical bounding style, lunged at the feeder, causing it to wobble precariously. Just as it began to tip, a sudden gust of wind swung the contraption in a whirlwind of seeds and reflections. Whiskers and Rover, caught in the midst of this flying feast, found themselves covered in birdseed, with Whiskers' fur boasting a collection of tiny sunflower hats.\\n\\nAs they sat there, bewildered and giggling in their own peculiar way, the townsfolk couldn't help but chuckle at the antics of Whiskers and Rover. Even the birds stopped their fluttering to admire the spectacle. From that day on, the bird feeder was not just a source of food for the birds, but also a stage for Whiskerfield's most unexpected entertainment duo.\\n\\nAnd so, Whiskers the curious cat learned an important lesson: sometimes, curiosity might not uncover the mysteries you expect, but it certainly creates the most memorable adventures. And as for Rover, well, he just loved being part of the fun, birdseed hats and all.\"}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOfnzkwylzEgoQjoqCoqso1JcbO', created=1765834073, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Here's a funny story about a curious cat named Whiskers. In the quaint town of Whiskerfield, Whiskers is known for getting into bizarre situations. One day, he discovers a shiny bird feeder and mistakes it for a mysterious contraption. Despite several comedic mishaps, including enlisting the help of Rover the golden retriever, Whiskers ends up covered in birdseed, providing entertainment for the entire town. Through his antics, Whiskers realizes that while curiosity might not solve mysteries, it sure makes for adventurous tales.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=111, prompt_tokens=1217, total_tokens=1328, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "Here's a funny story about a curious cat named Whiskers. In the quaint town of Whiskerfield, Whiskers is known for getting into bizarre situations. One day, he discovers a shiny bird feeder and mistakes it for a mysterious contraption. Despite several comedic mishaps, including enlisting the help of Rover the golden retriever, Whiskers ends up covered in birdseed, providing entertainment for the entire town. Through his antics, Whiskers realizes that while curiosity might not solve mysteries, it sure makes for adventurous tales.\n" + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': \"Write a story about a curious cat in the style: funny.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\"}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{\"topic\":\"a curious cat\"}', 'name': 'story_funny'}, 'id': 'call_y9nl1iqWc1CJwphS7L1dN4H5', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_y9nl1iqWc1CJwphS7L1dN4H5', 'name': 'story_funny', 'content': [{'type': 'text', 'text': 'Once upon a time in the quaint little village of Whiskerville, lived a curious cat named Oliver. Oliver wasn\\'t just any ordinary cat; he was a feline with an insatiable appetite for adventure and perhaps, just as importantly, for snacks.\\n\\nOne sunny morning, as the villagers went about their daily routines, Oliver noticed something peculiar. A new aroma wafted through the air—a delightful mix of fish and chicken, with just a hint of something exotic. Perplexed and intrigued, Oliver decided it was his duty to investigate. After all, curiosity didn\\'t just kill the cat; it fed it delicious snacks.\\n\\nHis nose led him to the local bakery, which had recently started experimenting with \"gourmet pet pastries.\" As Oliver tiptoed inside, unnoticed by the busy baker, he spotted an array of treats on a shelf. The pièce de résistance was a cake that read \"Happy Birthday Max\" in bold, colorful icing.\\n\\nOliver, assuming this \"Max\" character could afford to miss one slice (or perhaps the entire cake), climbed onto the shelf. With expert precision, he nibbled around the edges, savoring each bite like the connoisseur he was. In his mind, he imagined the villagers applauding his refined palate and heralding him as \"Oliver, the Culinary Cat.\"\\n\\nJust then, the baker returned and realized what had happened. \"Oh no! Max\\'s birthday cake!\" she exclaimed, spotting Oliver mid-feast. Oliver, caught in the act, adopted his most innocent \"I\\'m just a cute kitty\" face, but the baker was less than impressed.\\n\\nIn a moment of inspiration—or perhaps desperation—Oliver decided to make a quick escape. He knocked over a bag of flour, creating a cloud of white, giving him just enough cover to dart out the back door and make his getaway. The baker, though exasperated, couldn\\'t help but chuckle as Oliver, now a ghostly white from the flour, trotted down the street with the last remnants of the cake still sticking to his whiskers.\\n\\nFrom that day on, Oliver earned a new nickname among the villagers: \"The Cake Bandit of Whiskerville.\" Despite the fiasco, he was more revered than ever, proving once again that even the cheekiest of cats could win hearts—and snacks—with a touch of humor and a sprinkle of charm.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEfS0n3oGVWVXE4YCfLUqgp6ZvA', created=1765910333, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Once upon a time in the quaint little village of Whiskerville, lived a curious cat named Oliver. Oliver wasn\\'t just any ordinary cat; he was a feline with an insatiable appetite for adventure and perhaps, just as importantly, for snacks.\\n\\nOne sunny morning, as the villagers went about their daily routines, Oliver noticed something peculiar. A new aroma wafted through the air—a delightful mix of fish and chicken, with just a hint of something exotic. Perplexed and intrigued, Oliver decided it was his duty to investigate. After all, curiosity didn\\'t just kill the cat; it fed it delicious snacks.\\n\\nHis nose led him to the local bakery, which had recently started experimenting with \"gourmet pet pastries.\" As Oliver tiptoed inside, unnoticed by the busy baker, he spotted an array of treats on a shelf. The pièce de résistance was a cake that read \"Happy Birthday Max\" in bold, colorful icing.\\n\\nOliver, assuming this \"Max\" character could afford to miss one slice (or perhaps the entire cake), climbed onto the shelf. With expert precision, he nibbled around the edges, savoring each bite like the connoisseur he was. In his mind, he imagined the villagers applauding his refined palate and heralding him as \"Oliver, the Culinary Cat.\"\\n\\nJust then, the baker returned and realized what had happened. \"Oh no! Max\\'s birthday cake!\" she exclaimed, spotting Oliver mid-feast. Oliver, caught in the act, adopted his most innocent \"I\\'m just a cute kitty\" face, but the baker was less than impressed.\\n\\nIn a moment of inspiration—or perhaps desperation—Oliver decided to make a quick escape. He knocked over a bag of flour, creating a cloud of white, giving him just enough cover to dart out the back door and make his getaway. The baker, though exasperated, couldn\\'t help but chuckle as Oliver, now a ghostly white from the flour, trotted down the street with the last remnants of the cake still sticking to his whiskers.\\n\\nFrom that day on, Oliver earned a new nickname among the villagers: \"The Cake Bandit of Whiskerville.\" Despite the fiasco, he was more revered than ever, proving once again that even the cheekiest of cats could win hearts—and snacks—with a touch of humor and a sprinkle of charm.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=476, prompt_tokens=1061, total_tokens=1537, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "Once upon a time in the quaint little village of Whiskerville, lived a curious cat named Oliver. Oliver wasn't just any ordinary cat; he was a feline with an insatiable appetite for adventure and perhaps, just as importantly, for snacks.\n", + "\n", + "One sunny morning, as the villagers went about their daily routines, Oliver noticed something peculiar. A new aroma wafted through the air—a delightful mix of fish and chicken, with just a hint of something exotic. Perplexed and intrigued, Oliver decided it was his duty to investigate. After all, curiosity didn't just kill the cat; it fed it delicious snacks.\n", + "\n", + "His nose led him to the local bakery, which had recently started experimenting with \"gourmet pet pastries.\" As Oliver tiptoed inside, unnoticed by the busy baker, he spotted an array of treats on a shelf. The pièce de résistance was a cake that read \"Happy Birthday Max\" in bold, colorful icing.\n", + "\n", + "Oliver, assuming this \"Max\" character could afford to miss one slice (or perhaps the entire cake), climbed onto the shelf. With expert precision, he nibbled around the edges, savoring each bite like the connoisseur he was. In his mind, he imagined the villagers applauding his refined palate and heralding him as \"Oliver, the Culinary Cat.\"\n", + "\n", + "Just then, the baker returned and realized what had happened. \"Oh no! Max's birthday cake!\" she exclaimed, spotting Oliver mid-feast. Oliver, caught in the act, adopted his most innocent \"I'm just a cute kitty\" face, but the baker was less than impressed.\n", + "\n", + "In a moment of inspiration—or perhaps desperation—Oliver decided to make a quick escape. He knocked over a bag of flour, creating a cloud of white, giving him just enough cover to dart out the back door and make his getaway. The baker, though exasperated, couldn't help but chuckle as Oliver, now a ghostly white from the flour, trotted down the street with the last remnants of the cake still sticking to his whiskers.\n", + "\n", + "From that day on, Oliver earned a new nickname among the villagers: \"The Cake Bandit of Whiskerville.\" Despite the fiasco, he was more revered than ever, proving once again that even the cheekiest of cats could win hearts—and snacks—with a touch of humor and a sprinkle of charm.\n" ] } ], @@ -576,7 +610,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "4334d07a", "metadata": {}, "outputs": [ @@ -584,36 +618,39 @@ "name": "stdout", "output_type": "stream", "text": [ - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOpUS8BAytmElgOX8fLDSSym8TP', created=1765834083, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_T6RgNagFWflpLfddbDdAhy7e', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOqUGCwcplfEVlflhfHFeoN0vmV', created=1765834084, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_1zL43TYBfRd82z76Ww3VtZ10', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOrjGy3cKfupoXxoUpyh5g3Rg2i', created=1765834085, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_QIhDdQlUVyQb4xL3bp9mA3Ln', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOrSd4OYuyZMTYJQEH8nAuKmbII', created=1765834085, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_ndnD0MTgx5kCh0RQloWTEMDO', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOs7Deh2JdFir6jSkT2tvgXUwAD', created=1765834086, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_zZZ2qOKtJOSK0NQR05Es8GCT', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOsEkupw6S2qV7dAVO6IXlv48fE', created=1765834086, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_SROFh6GD7MXpKjEjuCKfwsoR', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOtOfJnpaXYL7B3vdnHZKo7CfTQ', created=1765834087, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_OfaqegdBaNqhYXbmtMSfq8E3', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOu2KvgpurkvKS5js70BrYPZ6ei', created=1765834088, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_G1XWytSwDBOJXd5DbTnAgegd', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOuyGE1IdeXQAUdAihmAEuou0Nm', created=1765834088, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e819e3438b', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='unstable_service'), id='call_yefiR7zjd3zhSaL4hO2IlVPz', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=11, prompt_tokens=553, total_tokens=564, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_yefiR7zjd3zhSaL4hO2IlVPz', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_yefiR7zjd3zhSaL4hO2IlVPz', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 1/3. Please retry.'}\"}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOv3DETvRpajf5oFobKAex8uI4c', created=1765834089, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='unstable_service'), id='call_WKh4jx4XtF95mgUTv8uAXlLE', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=11, prompt_tokens=596, total_tokens=607, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_yefiR7zjd3zhSaL4hO2IlVPz', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_yefiR7zjd3zhSaL4hO2IlVPz', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 1/3. Please retry.'}\"}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_WKh4jx4XtF95mgUTv8uAXlLE', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_WKh4jx4XtF95mgUTv8uAXlLE', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 2/3. Please retry.'}\"}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOwAICzWpytXVphXANfC6ix6Iv6', created=1765834090, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='unstable_service'), id='call_g20DJy4DQvADfZGwqnNClZf7', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=11, prompt_tokens=639, total_tokens=650, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEmIONHwhLsMjK9u9cpJsqWBbgm', created=1765910340, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_nxj3sHvWKBpDCcXfbgDLocNc', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEmLBPSZv4j9Zi1is0ah7lGjyph', created=1765910340, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_BjcrjUeLyEvFzdBHZBbNhpAK', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEnub7d0H00fi77NHiEGLSQ1QdR', created=1765910341, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_3dSgdzKP3RpqtIEZKIeGHbaR', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEnKN3i31lLg13CFtZfZq8EOJOe', created=1765910341, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_zh4sXEge4m5oKeys7uPLMH8S', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEoU6nXlyGn7ibLzyZcgdNwLc1v', created=1765910342, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_jo5PH2nFHCUkhPZ5bT7u12bR', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEoeeRn9IkGRg6q51BgDbiIoeeF', created=1765910342, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_ta6xQ0Xyor6swXp9nU0NB4OR', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEpij6TnFbVNCS2dZp8DP9sW5ZQ', created=1765910343, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_3gdzkbFYZoU0t1mFRwbN8a0L', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEqdb1YjTu1hi1mtxgpEmWFy3dc', created=1765910344, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_qvlLtrZwpJYooqZSK4aZanTo', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUErM2EeRDop0LmxffT1h9FQVFaC', created=1765910345, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='fetch_data'), id='call_vum27pk9btnhr9Mih0R13mbS', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=10, prompt_tokens=553, total_tokens=563, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUErFocEMQZD38QineLbN9jwfmBW', created=1765910345, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='unstable_service'), id='call_KmH8evW9BwoEJ65sRNOTiijH', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=11, prompt_tokens=553, total_tokens=564, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_KmH8evW9BwoEJ65sRNOTiijH', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_KmH8evW9BwoEJ65sRNOTiijH', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 1/3. Please retry.'}\"}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEsXalp23viabH6Ypx9igTyB0vE', created=1765910346, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='unstable_service'), id='call_KfYFUQBLLB0sWfl74HMUBivj', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=11, prompt_tokens=596, total_tokens=607, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_KmH8evW9BwoEJ65sRNOTiijH', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_KmH8evW9BwoEJ65sRNOTiijH', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 1/3. Please retry.'}\"}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_KfYFUQBLLB0sWfl74HMUBivj', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_KfYFUQBLLB0sWfl74HMUBivj', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 2/3. Please retry.'}\"}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEsefSAxCp4UN3CQHL4sW2Nt5Kx', created=1765910346, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{}', name='unstable_service'), id='call_E4INtSQ08RbjluqMPNHtueH9', type='function')], function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=11, prompt_tokens=639, total_tokens=650, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'unstable_service', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_yefiR7zjd3zhSaL4hO2IlVPz', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_yefiR7zjd3zhSaL4hO2IlVPz', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 1/3. Please retry.'}\"}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_WKh4jx4XtF95mgUTv8uAXlLE', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_WKh4jx4XtF95mgUTv8uAXlLE', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 2/3. Please retry.'}\"}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_g20DJy4DQvADfZGwqnNClZf7', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_g20DJy4DQvADfZGwqnNClZf7', 'name': 'unstable_service', 'content': [{'type': 'text', 'text': \"{ 'status': 'ok', 'data': [1, 2, 3] }\"}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOwp4xrlzgidgfQPxc56WoCermB', created=1765834090, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='The data fetched from the unstable service is: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=20, prompt_tokens=679, total_tokens=699, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_KmH8evW9BwoEJ65sRNOTiijH', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_KmH8evW9BwoEJ65sRNOTiijH', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 1/3. Please retry.'}\"}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_KfYFUQBLLB0sWfl74HMUBivj', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_KfYFUQBLLB0sWfl74HMUBivj', 'name': 'unstable_service', 'content': \"{'status': 'failure', 'exception': 'Service unavailable! Attempt 2/3. Please retry.'}\"}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'unstable_service'}, 'id': 'call_E4INtSQ08RbjluqMPNHtueH9', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_E4INtSQ08RbjluqMPNHtueH9', 'name': 'unstable_service', 'content': [{'type': 'text', 'text': \"{ 'status': 'ok', 'data': [1, 2, 3] }\"}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEte02Ozt3mvzBr7xwcTPQcfWo8', created=1765910347, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='Successfully fetched data: [1, 2, 3]', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=14, prompt_tokens=679, total_tokens=693, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_vum27pk9btnhr9Mih0R13mbS', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_vum27pk9btnhr9Mih0R13mbS', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'Successfully fetched data: [1, 2, 3]'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEtyzAH1YfRKGgjks5iTqxXJwd2', created=1765910347, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=584, total_tokens=602, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_G1XWytSwDBOJXd5DbTnAgegd', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_G1XWytSwDBOJXd5DbTnAgegd', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'The data fetched from the unstable service is: `[1, 2, 3]`.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOxj5IZQ85aFjs4jCuNolWoY9OL', created=1765834091, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='The data fetched from the unstable service is: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=20, prompt_tokens=590, total_tokens=610, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_qvlLtrZwpJYooqZSK4aZanTo', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_qvlLtrZwpJYooqZSK4aZanTo', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data: \\\\([1, 2, 3]\\\\).'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEut9xAQEbMXYYTOiQohhkBfMbO', created=1765910348, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e819e3438b', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=588, total_tokens=606, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_OfaqegdBaNqhYXbmtMSfq8E3', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_OfaqegdBaNqhYXbmtMSfq8E3', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'The data fetched from the unstable service is: `[1, 2, 3]`.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOx62IhQMgrL2JUgPzFMn7RImks', created=1765834091, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data from the unstable service: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=21, prompt_tokens=590, total_tokens=611, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_3gdzkbFYZoU0t1mFRwbN8a0L', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_3gdzkbFYZoU0t1mFRwbN8a0L', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data: \\\\([1, 2, 3]\\\\).'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEv0gJ4TYYParxk5wTbOIWYs137', created=1765910349, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=588, total_tokens=606, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_SROFh6GD7MXpKjEjuCKfwsoR', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_SROFh6GD7MXpKjEjuCKfwsoR', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data from the unstable service: `[1, 2, 3]`.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAOyXctxr4R0RMfvWHsU5AYqAIOY', created=1765834092, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data from the unstable service, and the data is: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=26, prompt_tokens=591, total_tokens=617, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_ta6xQ0Xyor6swXp9nU0NB4OR', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_ta6xQ0Xyor6swXp9nU0NB4OR', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data: \\\\([1, 2, 3]\\\\).'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEvhnjGnCv0QKuYsiG86e4l3PJk', created=1765910349, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=588, total_tokens=606, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_zZZ2qOKtJOSK0NQR05Es8GCT', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_zZZ2qOKtJOSK0NQR05Es8GCT', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data from the unstable service, and the data is: `[1, 2, 3]`.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAP0wwqVQC9MyAl9zWVe7zzCHxAC', created=1765834094, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data from the unstable service, and the data is: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=26, prompt_tokens=596, total_tokens=622, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_jo5PH2nFHCUkhPZ5bT7u12bR', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_jo5PH2nFHCUkhPZ5bT7u12bR', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data: \\\\([1, 2, 3]\\\\).'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEwaQPwBxZP3HRfZLhObtpWoV7o', created=1765910350, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=588, total_tokens=606, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_ndnD0MTgx5kCh0RQloWTEMDO', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_ndnD0MTgx5kCh0RQloWTEMDO', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data from the unstable service, and the data is: `[1, 2, 3]`.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAP0MVlZ9RfdBubfjSXaW6MoAdrL', created=1765834094, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data from the unstable service, and the data is: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=26, prompt_tokens=596, total_tokens=622, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_zh4sXEge4m5oKeys7uPLMH8S', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_zh4sXEge4m5oKeys7uPLMH8S', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data: \\\\([1, 2, 3]\\\\).'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUExvTpaTUSQyUbDgGJouRpGW2JK', created=1765910351, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=588, total_tokens=606, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_QIhDdQlUVyQb4xL3bp9mA3Ln', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_QIhDdQlUVyQb4xL3bp9mA3Ln', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data from the unstable service, and the data is: `[1, 2, 3]`.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAP1erUfmc0ilFRVBJTR0bcGcrmx', created=1765834095, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data from the unstable service, and the data is: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=26, prompt_tokens=596, total_tokens=622, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_3dSgdzKP3RpqtIEZKIeGHbaR', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_3dSgdzKP3RpqtIEZKIeGHbaR', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data: \\\\([1, 2, 3]\\\\).'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUExXiP6j9PfzMFM1KYP29Ao7bh7', created=1765910351, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=588, total_tokens=606, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_1zL43TYBfRd82z76Ww3VtZ10', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_1zL43TYBfRd82z76Ww3VtZ10', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data from the unstable service, and the data is: `[1, 2, 3]`.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAP2E2oVkYct8MOs2A5fRhpclikB', created=1765834096, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully retrieved the data from the unstable service: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=21, prompt_tokens=596, total_tokens=617, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_BjcrjUeLyEvFzdBHZBbNhpAK', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_BjcrjUeLyEvFzdBHZBbNhpAK', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data: \\\\([1, 2, 3]\\\\).'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEyNJL6tqPyn6gGsDJtcVbc8Vg8', created=1765910352, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=588, total_tokens=606, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "INFO {'tool': 'fetch_data', 'args': (), 'kwargs': {}}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_T6RgNagFWflpLfddbDdAhy7e', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_T6RgNagFWflpLfddbDdAhy7e', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully retrieved the data from the unstable service: `[1, 2, 3]`.'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAP233bZtdUk1k1OCTmZHwHE9jN2', created=1765834096, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully retrieved the data from the unstable service: `[1, 2, 3]`.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=21, prompt_tokens=591, total_tokens=612, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "Result: I successfully retrieved the data from the unstable service: `[1, 2, 3]`. Retries: 3\n" + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Use the unstable_service tool to fetch data.'}], 'role': 'user'}, {'content': None, 'role': 'assistant', 'tool_calls': [{'function': {'arguments': '{}', 'name': 'fetch_data'}, 'id': 'call_nxj3sHvWKBpDCcXfbgDLocNc', 'type': 'function'}], 'function_call': None, 'provider_specific_fields': {'refusal': None}, 'annotations': []}, {'role': 'tool', 'tool_call_id': 'call_nxj3sHvWKBpDCcXfbgDLocNc', 'name': 'fetch_data', 'content': [{'type': 'text', 'text': 'I successfully fetched the data: \\\\([1, 2, 3]\\\\).'}]}], 'response_format': None, 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEy8aws4YfPFggmpXtfpUlwVtYf', created=1765910352, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='I successfully fetched the data: \\\\([1, 2, 3]\\\\).', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=18, prompt_tokens=588, total_tokens=606, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "Result: I successfully fetched the data: \\([1, 2, 3]\\). Retries: 3\n" ] } ], @@ -658,7 +695,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "39b2b225", "metadata": {}, "outputs": [ @@ -666,10 +703,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Give a rating for Die Hard. The explanation MUST include the numeric score. Do not use any tools.'}], 'role': 'user'}], 'response_format': , 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'give_rating_for_movie', 'description': 'Give a rating for {movie_name}. The explanation MUST include the numeric score. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'movie_name': {'title': 'Movie Name', 'type': 'string'}}, 'required': ['movie_name'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAPBMGNLhpSkzfqzv3kQfvYaTcmO', created=1765834105, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='{\"value\":{\"score\":9,\"explanation\":\"Die Hard is a classic action film that set a high standard for the genre, known for its thrilling action sequences and memorable performances, particularly by Bruce Willis as John McClane. The film\\'s strong plot, engaging characters, and suspenseful pacing contribute to its enduring popularity. Given its influential status and entertainment value, Die Hard deserves a high rating of 9 out of 10.\"}}', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=92, prompt_tokens=681, total_tokens=773, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", - "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Retry generating the following prompt: Give a rating for Die Hard. The explanation MUST include the numeric score. Do not use any tools.\\n\\nError from previous generation:\\n```\\nTraceback (most recent call last):\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/handlers/llm/providers.py\", line 347, in _retry_completion\\n return fwd(current_template, *args, **kwargs)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/ops/types.py\", line 485, in __call__\\n return self_handler(*args, **kwargs)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py\", line 81, in inner\\n return func(*args, **kwds)\\n ^^^^^^^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/internals/runtime.py\", line 45, in _cont_wrapper\\n return fn(*a, **k)\\n ^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/internals/runtime.py\", line 56, in _cont_wrapper\\n return fn(*a, **k)\\n ^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/internals/runtime.py\", line 70, in bound_body\\n return body(*a, **k)\\n ^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/internals/runtime.py\", line 56, in _cont_wrapper\\n return fn(*a, **k)\\n ^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/handlers/llm/providers.py\", line 492, in _call\\n return decode_response(template, resp)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/handlers/llm/providers.py\", line 436, in decode_response\\n result = Result.model_validate_json(result_str)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/main.py\", line 766, in model_validate_json\\n return cls.__pydantic_validator__.validate_json(\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\npydantic_core._pydantic_core.ValidationError: 1 validation error for Result\\nvalue.score\\n score must be 1–5, got 9 [type=invalid_score, input_value=9, input_type=int]\\n```'}], 'role': 'user'}], 'response_format': , 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'give_rating_for_movie', 'description': 'Give a rating for {movie_name}. The explanation MUST include the numeric score. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'movie_name': {'title': 'Movie Name', 'type': 'string'}}, 'required': ['movie_name'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnAPDokOZODkN8VxElAwlcaUZdLuL', created=1765834107, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_83554c687e', choices=[Choices(finish_reason='stop', index=0, message=Message(content='{\"value\":{\"score\":5,\"explanation\":\"Die Hard is an iconic action film that has become a staple of the genre. With its engaging plot, intense action sequences, and a memorable performance by Bruce Willis, it easily earns a score of 5 out of 5. The film\\'s blend of suspense and humor sets it apart, making it a must-watch for action movie enthusiasts.\"}}', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=83, prompt_tokens=1268, total_tokens=1351, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Give a rating for Die Hard. The explanation MUST include the numeric score. Do not use any tools.'}], 'role': 'user'}], 'response_format': , 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'give_rating_for_movie', 'description': 'Give a rating for {movie_name}. The explanation MUST include the numeric score. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'movie_name': {'title': 'Movie Name', 'type': 'string'}}, 'required': ['movie_name'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUEzoCPoZVdVvxcSYKXBd7GnpypB', created=1765910353, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='{\"value\":{\"score\":9,\"explanation\":\"Die Hard is a quintessential action film that has set the standard for its genre with its thrilling storyline, charismatic lead performance by Bruce Willis, and iconic villain played by Alan Rickman. Its blend of intense action sequences and clever humor makes it a favorite among audiences, earning it a rating of 9 out of 10.\"}}', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=79, prompt_tokens=681, total_tokens=760, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", + "INFO {'args': (), 'kwargs': {'messages': [{'type': 'message', 'content': [{'type': 'text', 'text': 'Retry generating the following prompt: Give a rating for Die Hard. The explanation MUST include the numeric score. Do not use any tools.\\n\\nError from previous generation:\\n```\\nTraceback (most recent call last):\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/handlers/llm/providers.py\", line 347, in _retry_completion\\n return fwd(current_template, *args, **kwargs)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/ops/types.py\", line 485, in __call__\\n return self_handler(*args, **kwargs)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py\", line 81, in inner\\n return func(*args, **kwds)\\n ^^^^^^^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/internals/runtime.py\", line 45, in _cont_wrapper\\n return fn(*a, **k)\\n ^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/internals/runtime.py\", line 56, in _cont_wrapper\\n return fn(*a, **k)\\n ^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/internals/runtime.py\", line 70, in bound_body\\n return body(*a, **k)\\n ^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/internals/runtime.py\", line 56, in _cont_wrapper\\n return fn(*a, **k)\\n ^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/handlers/llm/providers.py\", line 491, in _call\\n return decode_response(template, resp)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/effectful/handlers/llm/providers.py\", line 436, in decode_response\\n result = Result.model_validate_json(result_str)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/Users/datnguyenthanh/Marc/effectful/.venv/lib/python3.12/site-packages/pydantic/main.py\", line 766, in model_validate_json\\n return cls.__pydantic_validator__.validate_json(\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\npydantic_core._pydantic_core.ValidationError: 1 validation error for Result\\nvalue.score\\n score must be 1–5, got 9 [type=invalid_score, input_value=9, input_type=int]\\n```'}], 'role': 'user'}], 'response_format': , 'tools': [{'type': 'function', 'function': {'name': 'limerick', 'description': 'Write a limerick on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'haiku_no_cache', 'description': 'Write a haiku on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'primes', 'description': 'Give a prime number with {first_digit} as the first digit. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'first_digit': {'title': 'First Digit', 'type': 'integer'}}, 'required': ['first_digit'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'count_char', 'description': \"Write a function which takes a string and counts the occurrances of '{char}'. Do not use any tools.\", 'parameters': {'additionalProperties': False, 'properties': {'char': {'title': 'Char', 'type': 'string'}}, 'required': ['char'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'cities', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'weather', 'description': '', 'parameters': {'additionalProperties': False, 'properties': {'city': {'title': 'City', 'type': 'string'}}, 'required': ['city'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'vacation', 'description': 'Use the provided tools to suggest a city that has good weather. Use only the `cities` and `weather` tools provided.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_joke', 'description': 'Write a knock-knock joke on the theme of {theme}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'theme': {'title': 'Theme', 'type': 'string'}}, 'required': ['theme'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'rate_joke', 'description': 'Decide if {joke} is funny or not. Do not use any tools.', 'parameters': {'$defs': {'KnockKnockJoke': {'properties': {'whos_there': {'title': 'Whos There', 'type': 'string'}, 'punchline': {'title': 'Punchline', 'type': 'string'}}, 'required': ['whos_there', 'punchline'], 'title': 'KnockKnockJoke', 'type': 'object', 'additionalProperties': False}}, 'additionalProperties': False, 'properties': {'joke': {'$ref': '#/$defs/KnockKnockJoke'}}, 'required': ['joke'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_with_moral', 'description': 'Write a short story about {topic} and end with a moral lesson. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'story_funny', 'description': 'Write a funny, humorous story about {topic}. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}, 'required': ['topic'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'write_story', 'description': \"Write a story about {topic} in the style: {style}.\\n Available styles: 'moral' for a story with a lesson, 'funny' for humor. Use story_funny for humor, story_with_moral for a story with a lesson.\", 'parameters': {'additionalProperties': False, 'properties': {'topic': {'title': 'Topic', 'type': 'string'}, 'style': {'title': 'Style', 'type': 'string'}}, 'required': ['topic', 'style'], 'title': 'Params', 'type': 'object'}, 'strict': True}}, {'type': 'function', 'function': {'name': 'unstable_service', 'description': 'Fetch data from an unstable external service. May require retries.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'fetch_data', 'description': 'Use the unstable_service tool to fetch data.', 'parameters': {'additionalProperties': False, 'properties': {}, 'title': 'Params', 'type': 'object', 'required': []}, 'strict': True}}, {'type': 'function', 'function': {'name': 'give_rating_for_movie', 'description': 'Give a rating for {movie_name}. The explanation MUST include the numeric score. Do not use any tools.', 'parameters': {'additionalProperties': False, 'properties': {'movie_name': {'title': 'Movie Name', 'type': 'string'}}, 'required': ['movie_name'], 'title': 'Params', 'type': 'object'}, 'strict': True}}]}, 'response': ModelResponse(id='chatcmpl-CnUF1YnTVQv55Uk38dPyOrbY4xRJW', created=1765910355, model='gpt-4o-2024-08-06', object='chat.completion', system_fingerprint='fp_e413f45763', choices=[Choices(finish_reason='stop', index=0, message=Message(content='{\"value\":{\"score\":5,\"explanation\":\"Die Hard is a classic action film that has stood the test of time. It\\'s praised for its engaging storyline, memorable characters, and thrilling action sequences. Although it\\'s a great film deserving of a high score, given the rating scale, it receives a 5 for being one of the best examples of the action genre, known for Bruce Willis’s iconic performance and its blend of excitement and humor.\"}}', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'refusal': None}, annotations=[]), provider_specific_fields={})], usage=Usage(completion_tokens=93, prompt_tokens=1268, total_tokens=1361, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)), service_tier='default')}\n", "Score: 5/5\n", - "Explanation: Die Hard is an iconic action film that has become a staple of the genre. With its engaging plot, intense action sequences, and a memorable performance by Bruce Willis, it easily earns a score of 5 out of 5. The film's blend of suspense and humor sets it apart, making it a must-watch for action movie enthusiasts.\n" + "Explanation: Die Hard is a classic action film that has stood the test of time. It's praised for its engaging storyline, memorable characters, and thrilling action sequences. Although it's a great film deserving of a high score, given the rating scale, it receives a 5 for being one of the best examples of the action genre, known for Bruce Willis’s iconic performance and its blend of excitement and humor.\n" ] } ], diff --git a/tests/test_handlers_llm_provider.py b/tests/test_handlers_llm_provider.py index ac51bebd..0447d79d 100644 --- a/tests/test_handlers_llm_provider.py +++ b/tests/test_handlers_llm_provider.py @@ -1,13 +1,11 @@ """Tests for LLM handlers and providers. -This module tests the functionality from build/main.py and build/llm.py, -breaking down individual components like LiteLLMProvider, LLMLoggingHandler, -ProgramSynthesis, and sampling strategies. +This module tests the functionality of LiteLLMProvider, LLMLoggingHandler, +and related LLM components. """ import functools import logging import os -from collections.abc import Callable from enum import Enum import pytest @@ -21,7 +19,6 @@ LLMLoggingHandler, completion, ) -from effectful.handlers.llm.synthesis import ProgramSynthesis, SynthesisError from effectful.ops.semantics import fwd, handler from effectful.ops.syntax import ObjectInterpretation, implements from effectful.ops.types import NotHandled @@ -117,16 +114,6 @@ def generate_number(max_value: int) -> int: raise NotImplementedError -@Template.define -def create_function(char: str) -> Callable[[str], int]: - """Create a function that counts occurrences of the character '{char}' in a string. - Do not use any tools. - - Return as a code block with the last definition being the function. - """ - raise NotHandled - - class TestLiteLLMProvider: """Tests for LiteLLMProvider basic functionality.""" @@ -239,27 +226,6 @@ def test_custom_logger(self, caplog): ) -class TestProgramSynthesis: - """Tests for ProgramSynthesis handler functionality.""" - - @requires_openai - @retry_on_error(error=SynthesisError, n=3) - def test_generates_callable(self): - """Test ProgramSynthesis handler generates executable code.""" - with ( - handler(LiteLLMProvider(model_name="gpt-4o-mini")), - handler(ProgramSynthesis()), - handler(LimitLLMCallsHandler(max_calls=1)), - ): - count_func = create_function("a") - - assert callable(count_func) - # Test the generated function - assert count_func("banana") == 3 - assert count_func("cherry") == 0 - assert count_func("aardvark") == 3 - - def smiley_face() -> Image.Image: bmp = [ "00000000", diff --git a/tests/test_handlers_llm_provider_synthesis.py b/tests/test_handlers_llm_provider_synthesis.py new file mode 100644 index 00000000..3a36d155 --- /dev/null +++ b/tests/test_handlers_llm_provider_synthesis.py @@ -0,0 +1,128 @@ +"""Tests for LLM program synthesis functionality.""" + +from __future__ import annotations + +import functools +import inspect +import os +from collections.abc import Callable + +import pytest + +from effectful.handlers.llm import Template +from effectful.handlers.llm.providers import LiteLLMProvider, completion +from effectful.handlers.llm.synthesis import ProgramSynthesis, SynthesisError +from effectful.ops.semantics import fwd, handler +from effectful.ops.syntax import ObjectInterpretation, implements +from effectful.ops.types import NotHandled + +# Check for API keys +HAS_OPENAI_KEY = "OPENAI_API_KEY" in os.environ and os.environ["OPENAI_API_KEY"] + +requires_openai = pytest.mark.skipif( + not HAS_OPENAI_KEY, reason="OPENAI_API_KEY environment variable not set" +) + + +def retry_on_error(error: type[Exception], n: int): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + for i in range(n): + try: + return func(*args, **kwargs) + except error as e: + if i < n - 1: + continue + raise e + + return wrapper + + return decorator + + +class LimitLLMCallsHandler(ObjectInterpretation): + max_calls: int + no_calls: int = 0 + + def __init__(self, max_calls: int): + self.max_calls = max_calls + + @implements(completion) + def _completion(self, *args, **kwargs): + if self.no_calls >= self.max_calls: + raise RuntimeError( + f"Test used too many requests (max_calls = {self.max_calls})" + ) + self.no_calls += 1 + return fwd() + + +@Template.define +def create_function(char: str) -> Callable[[str], int]: + """Create a function that counts occurrences of the character '{char}' in a string. + Do not use any tools. + + Return as a code block with the last definition being the function. + """ + raise NotHandled + + +class TestProgramSynthesis: + """Tests for ProgramSynthesis handler functionality.""" + + @requires_openai + @retry_on_error(error=SynthesisError, n=3) + def test_generates_callable(self): + """Test ProgramSynthesis handler generates executable code.""" + with ( + handler(LiteLLMProvider(model_name="gpt-4o-mini")), + handler(ProgramSynthesis()), + handler(LimitLLMCallsHandler(max_calls=1)), + ): + count_func = create_function("a") + + assert callable(count_func) + # Test the generated function + assert count_func("banana") == 3 + assert count_func("cherry") == 0 + assert count_func("aardvark") == 3 + + @requires_openai + @retry_on_error(error=SynthesisError, n=3) + def test_inspect_getsource_works(self): + """Test that inspect.getsource() works on synthesized functions.""" + with ( + handler(LiteLLMProvider(model_name="gpt-4o-mini")), + handler(ProgramSynthesis()), + handler(LimitLLMCallsHandler(max_calls=1)), + ): + count_func = create_function("x") + + # inspect.getsource should work + source = inspect.getsource(count_func) + assert isinstance(source, str) + assert len(source) > 0 + assert "def" in source + + # __source__ attribute should also be available + assert hasattr(count_func, "__source__") + assert count_func.__source__ == source.rstrip("\n") + + @requires_openai + @retry_on_error(error=SynthesisError, n=3) + def test_synthesized_attribute(self): + """Test that __synthesized__ attribute is attached to generated functions.""" + from effectful.handlers.llm.synthesis import SynthesizedFunction + + with ( + handler(LiteLLMProvider(model_name="gpt-4o-mini")), + handler(ProgramSynthesis()), + handler(LimitLLMCallsHandler(max_calls=1)), + ): + count_func = create_function("z") + + assert hasattr(count_func, "__synthesized__") + assert isinstance(count_func.__synthesized__, SynthesizedFunction) + assert count_func.__synthesized__.function_name == count_func.__name__ + From aa4a98c4b0bb0670d809af44a3d4ab2544650814 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:45:05 -0500 Subject: [PATCH 34/65] Linting --- tests/test_handlers_llm_provider_synthesis.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_handlers_llm_provider_synthesis.py b/tests/test_handlers_llm_provider_synthesis.py index 3a36d155..346c2364 100644 --- a/tests/test_handlers_llm_provider_synthesis.py +++ b/tests/test_handlers_llm_provider_synthesis.py @@ -125,4 +125,3 @@ def test_synthesized_attribute(self): assert hasattr(count_func, "__synthesized__") assert isinstance(count_func.__synthesized__, SynthesizedFunction) assert count_func.__synthesized__.function_name == count_func.__name__ - From ad4d603987f3c32d920737ee12c8e6a2259cd7e0 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:50:56 -0500 Subject: [PATCH 35/65] Factor out image LLM test for less flaky --- tests/test_handlers_llm_provider.py | 37 ------------ tests/test_handlers_llm_provider_image.py | 74 +++++++++++++++++++++++ 2 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 tests/test_handlers_llm_provider_image.py diff --git a/tests/test_handlers_llm_provider.py b/tests/test_handlers_llm_provider.py index 0447d79d..f3415e7a 100644 --- a/tests/test_handlers_llm_provider.py +++ b/tests/test_handlers_llm_provider.py @@ -9,7 +9,6 @@ from enum import Enum import pytest -from PIL import Image from pydantic import BaseModel, Field from pydantic.dataclasses import dataclass @@ -21,7 +20,6 @@ ) from effectful.ops.semantics import fwd, handler from effectful.ops.syntax import ObjectInterpretation, implements -from effectful.ops.types import NotHandled # Check for API keys HAS_OPENAI_KEY = "OPENAI_API_KEY" in os.environ and os.environ["OPENAI_API_KEY"] @@ -226,41 +224,6 @@ def test_custom_logger(self, caplog): ) -def smiley_face() -> Image.Image: - bmp = [ - "00000000", - "00100100", - "00100100", - "00000000", - "01000010", - "00111100", - "00000000", - "00000000", - ] - - img = Image.new("1", (8, 8)) - for y, row in enumerate(bmp): - for x, c in enumerate(row): - img.putpixel((x, y), 1 if c == "1" else 0) - return img - - -@Template.define -def categorise_image(image: Image.Image) -> str: - """Return a description of the following image. Do not use any tools. - {image}""" - raise NotHandled - - -@requires_openai -def test_image_input(): - with ( - handler(LiteLLMProvider(model_name="gpt-4o")), - handler(LimitLLMCallsHandler(max_calls=3)), - ): - assert any("smile" in categorise_image(smiley_face()) for _ in range(3)) - - class BookReview(BaseModel): """A book review with rating and summary.""" diff --git a/tests/test_handlers_llm_provider_image.py b/tests/test_handlers_llm_provider_image.py new file mode 100644 index 00000000..772b46e2 --- /dev/null +++ b/tests/test_handlers_llm_provider_image.py @@ -0,0 +1,74 @@ +"""Tests for LLM image input functionality.""" + +from __future__ import annotations + +import os + +import pytest +from PIL import Image + +from effectful.handlers.llm import Template +from effectful.handlers.llm.providers import LiteLLMProvider, completion +from effectful.ops.semantics import fwd, handler +from effectful.ops.syntax import ObjectInterpretation, implements +from effectful.ops.types import NotHandled + +# Check for API keys +HAS_OPENAI_KEY = "OPENAI_API_KEY" in os.environ and os.environ["OPENAI_API_KEY"] + +requires_openai = pytest.mark.skipif( + not HAS_OPENAI_KEY, reason="OPENAI_API_KEY environment variable not set" +) + + +class LimitLLMCallsHandler(ObjectInterpretation): + max_calls: int + no_calls: int = 0 + + def __init__(self, max_calls: int): + self.max_calls = max_calls + + @implements(completion) + def _completion(self, *args, **kwargs): + if self.no_calls >= self.max_calls: + raise RuntimeError( + f"Test used too many requests (max_calls = {self.max_calls})" + ) + self.no_calls += 1 + return fwd() + + +def smiley_face() -> Image.Image: + bmp = [ + "00000000", + "00100100", + "00100100", + "00000000", + "01000010", + "00111100", + "00000000", + "00000000", + ] + + img = Image.new("1", (8, 8)) + for y, row in enumerate(bmp): + for x, c in enumerate(row): + img.putpixel((x, y), 1 if c == "1" else 0) + return img + + +@Template.define +def categorise_image(image: Image.Image) -> str: + """Return a description of the following image. Do not use any tools. + {image}""" + raise NotHandled + + +@requires_openai +def test_image_input(): + with ( + handler(LiteLLMProvider(model_name="gpt-4o")), + handler(LimitLLMCallsHandler(max_calls=3)), + ): + assert any("smile" in categorise_image(smiley_face()).lower() for _ in range(3)) + From 7aaa1730c0af1245ef5d2c6b1c7c069f3c8a7ea2 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 13:53:19 -0500 Subject: [PATCH 36/65] Linting --- tests/test_handlers_llm_provider_image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_handlers_llm_provider_image.py b/tests/test_handlers_llm_provider_image.py index 772b46e2..8e53aa84 100644 --- a/tests/test_handlers_llm_provider_image.py +++ b/tests/test_handlers_llm_provider_image.py @@ -71,4 +71,3 @@ def test_image_input(): handler(LimitLLMCallsHandler(max_calls=3)), ): assert any("smile" in categorise_image(smiley_face()).lower() for _ in range(3)) - From 8dd6f16a183075a90fcfd487e0565cd29f9bd54f Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 14:02:52 -0500 Subject: [PATCH 37/65] Include factored out test --- .github/workflows/test_llm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_llm.yml b/.github/workflows/test_llm.yml index 8856d9f5..b4af7d73 100644 --- a/.github/workflows/test_llm.yml +++ b/.github/workflows/test_llm.yml @@ -31,4 +31,4 @@ jobs: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: | - uv run pytest tests/test_handlers_llm_provider.py -v --tb=short + uv run pytest tests/test_handlers_llm_provider*.py -v --tb=short From 8f8c6145579c462e81ae95e0c33b561505372521 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 14:32:07 -0500 Subject: [PATCH 38/65] Fix TypeError weak reference to str --- effectful/handlers/llm/encoding.py | 9 ++++ effectful/handlers/llm/synthesis.py | 42 +++++++++++++++++-- tests/test_handlers_llm_provider_synthesis.py | 13 +++--- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/effectful/handlers/llm/encoding.py b/effectful/handlers/llm/encoding.py index fe8b7e21..8f657d81 100644 --- a/effectful/handlers/llm/encoding.py +++ b/effectful/handlers/llm/encoding.py @@ -53,6 +53,9 @@ class Encodable[T](EncodableAs[T, type]): def type_to_encodable_type[T]( __dispatch: Callable[[type[T]], Callable[..., Encodable[T]]], ty: type[T] ) -> Encodable[T]: + # Handle string annotation "str" from `from __future__ import annotations` + if ty == "str": + return _type_encodable_type_str(str) origin_ty = typing.get_origin(ty) or ty return __dispatch(origin_ty)(ty) @@ -73,6 +76,12 @@ def decode(cls, vl: T) -> T: return typing.cast(Encodable[T], BaseEncodable()) +# NOTE: Register str explicitly to avoid WeakKeyDictionary cache issues with singledispatch +@type_to_encodable_type.register(str) +def _type_encodable_type_str[T](ty: type[T]) -> Encodable[T]: + return _type_encodable_type_base(ty) + + @type_to_encodable_type.register(pydantic.BaseModel) def _type_encodable_type_pydantic_base_model[T: pydantic.BaseModel]( ty: type[T], diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index a978148d..a781dbde 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -351,7 +351,16 @@ def _call(self, template, *args, **kwargs) -> Callable: origin = typing.get_origin(ret_type) ret_type_origin = ret_type if origin is None else origin - if not (issubclass(ret_type_origin, collections.abc.Callable)): # type: ignore[arg-type] + # Check if return type is Callable - handle both class and typing special forms + # Also handle string annotations (from __future__ import annotations) + if isinstance(ret_type_origin, str): + is_callable = "Callable" in ret_type_origin + else: + is_callable = ret_type_origin is collections.abc.Callable or ( + isinstance(ret_type_origin, type) + and issubclass(ret_type_origin, collections.abc.Callable) + ) + if not is_callable: return fwd() # Include the full lexical context - all functions, types, values available to synthesized code @@ -392,5 +401,32 @@ def _call(self, template, *args, **kwargs) -> Callable: **kwargs, ) - # Build and return the function using lexical context for exec globals - return self._build_function(response, ret_type, template.__context__) + # Build the function using lexical context for exec globals + synthesized_func = self._build_function(response, ret_type, template.__context__) + + # Check if the synthesized function should be called with template args + # or if it's already the result (matches the return type directly) + try: + synth_sig = inspect.signature(synthesized_func) + template_sig = template.__signature__ + + # If synthesized function has same parameter names as template, + # it's a factory function - call it with the args + synth_params = set(synth_sig.parameters.keys()) + template_params = set(template_sig.parameters.keys()) + + if synth_params == template_params: + # Factory function - call it to get the actual callable + result = synthesized_func(*args, **kwargs) + # Copy synthesis metadata to the result if it's callable + if callable(result): + if hasattr(synthesized_func, "__source__"): + result.__source__ = synthesized_func.__source__ + if hasattr(synthesized_func, "__synthesized__"): + result.__synthesized__ = synthesized_func.__synthesized__ + return result + except (ValueError, TypeError): + pass + + # Otherwise, the synthesized function IS the result + return synthesized_func diff --git a/tests/test_handlers_llm_provider_synthesis.py b/tests/test_handlers_llm_provider_synthesis.py index 346c2364..97843283 100644 --- a/tests/test_handlers_llm_provider_synthesis.py +++ b/tests/test_handlers_llm_provider_synthesis.py @@ -99,15 +99,15 @@ def test_inspect_getsource_works(self): ): count_func = create_function("x") - # inspect.getsource should work + # inspect.getsource() should work on the synthesized function source = inspect.getsource(count_func) assert isinstance(source, str) assert len(source) > 0 assert "def" in source - - # __source__ attribute should also be available + + # __source__ attribute should also be available with full module code assert hasattr(count_func, "__source__") - assert count_func.__source__ == source.rstrip("\n") + assert "def" in count_func.__source__ @requires_openai @retry_on_error(error=SynthesisError, n=3) @@ -124,4 +124,7 @@ def test_synthesized_attribute(self): assert hasattr(count_func, "__synthesized__") assert isinstance(count_func.__synthesized__, SynthesizedFunction) - assert count_func.__synthesized__.function_name == count_func.__name__ + # The synthesized module code should be present + assert "def" in count_func.__synthesized__.module_code + # The function should work correctly + assert count_func("pizza") == 2 # two 'z's in "pizza" From 8b0251c7ce0870ed1e1b6e8deec03b1403ac4676 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 14:45:51 -0500 Subject: [PATCH 39/65] Fix misisng collections import --- effectful/handlers/llm/encoding.py | 3 --- effectful/handlers/llm/synthesis.py | 25 ++++--------------- tests/test_handlers_llm_provider_image.py | 2 -- tests/test_handlers_llm_provider_synthesis.py | 2 -- 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/effectful/handlers/llm/encoding.py b/effectful/handlers/llm/encoding.py index 8f657d81..e1183935 100644 --- a/effectful/handlers/llm/encoding.py +++ b/effectful/handlers/llm/encoding.py @@ -53,9 +53,6 @@ class Encodable[T](EncodableAs[T, type]): def type_to_encodable_type[T]( __dispatch: Callable[[type[T]], Callable[..., Encodable[T]]], ty: type[T] ) -> Encodable[T]: - # Handle string annotation "str" from `from __future__ import annotations` - if ty == "str": - return _type_encodable_type_str(str) origin_ty = typing.get_origin(ty) or ty return __dispatch(origin_ty)(ty) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index a781dbde..3f5707c8 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import collections import collections.abc import dataclasses @@ -93,8 +91,6 @@ def decode( func_name = vl.function_name module_code = textwrap.dedent(vl.module_code).strip() - # Create a unique filename and register source with linecache - # This allows inspect.getsource() to work on the generated function cls._decode_counter += 1 filename = f"" lines = module_code.splitlines(keepends=True) @@ -109,7 +105,10 @@ def decode( ) # Start with provided context or empty dict - exec_globals: dict[str, typing.Any] = dict(context) if context else {} + # Include collections module for type hints in synthesized code + exec_globals: dict[str, typing.Any] = {"collections": collections} + if context: + exec_globals.update(context) try: code_obj = compile(module_code, filename, "exec") @@ -352,14 +351,7 @@ def _call(self, template, *args, **kwargs) -> Callable: ret_type_origin = ret_type if origin is None else origin # Check if return type is Callable - handle both class and typing special forms - # Also handle string annotations (from __future__ import annotations) - if isinstance(ret_type_origin, str): - is_callable = "Callable" in ret_type_origin - else: - is_callable = ret_type_origin is collections.abc.Callable or ( - isinstance(ret_type_origin, type) - and issubclass(ret_type_origin, collections.abc.Callable) - ) + is_callable = ret_type_origin is collections.abc.Callable if not is_callable: return fwd() @@ -404,21 +396,15 @@ def _call(self, template, *args, **kwargs) -> Callable: # Build the function using lexical context for exec globals synthesized_func = self._build_function(response, ret_type, template.__context__) - # Check if the synthesized function should be called with template args - # or if it's already the result (matches the return type directly) try: synth_sig = inspect.signature(synthesized_func) template_sig = template.__signature__ - # If synthesized function has same parameter names as template, - # it's a factory function - call it with the args synth_params = set(synth_sig.parameters.keys()) template_params = set(template_sig.parameters.keys()) if synth_params == template_params: - # Factory function - call it to get the actual callable result = synthesized_func(*args, **kwargs) - # Copy synthesis metadata to the result if it's callable if callable(result): if hasattr(synthesized_func, "__source__"): result.__source__ = synthesized_func.__source__ @@ -428,5 +414,4 @@ def _call(self, template, *args, **kwargs) -> Callable: except (ValueError, TypeError): pass - # Otherwise, the synthesized function IS the result return synthesized_func diff --git a/tests/test_handlers_llm_provider_image.py b/tests/test_handlers_llm_provider_image.py index 8e53aa84..d2b40255 100644 --- a/tests/test_handlers_llm_provider_image.py +++ b/tests/test_handlers_llm_provider_image.py @@ -1,7 +1,5 @@ """Tests for LLM image input functionality.""" -from __future__ import annotations - import os import pytest diff --git a/tests/test_handlers_llm_provider_synthesis.py b/tests/test_handlers_llm_provider_synthesis.py index 97843283..10b4c0aa 100644 --- a/tests/test_handlers_llm_provider_synthesis.py +++ b/tests/test_handlers_llm_provider_synthesis.py @@ -1,7 +1,5 @@ """Tests for LLM program synthesis functionality.""" -from __future__ import annotations - import functools import inspect import os From 5a6a8ea9f715a8fac4c1d4824bf3dbe1534f6174 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 14:46:01 -0500 Subject: [PATCH 40/65] Linting --- effectful/handlers/llm/synthesis.py | 12 +++++++----- tests/test_handlers_llm_provider_synthesis.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 3f5707c8..135200f5 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -394,15 +394,17 @@ def _call(self, template, *args, **kwargs) -> Callable: ) # Build the function using lexical context for exec globals - synthesized_func = self._build_function(response, ret_type, template.__context__) - + synthesized_func = self._build_function( + response, ret_type, template.__context__ + ) + try: synth_sig = inspect.signature(synthesized_func) template_sig = template.__signature__ - + synth_params = set(synth_sig.parameters.keys()) template_params = set(template_sig.parameters.keys()) - + if synth_params == template_params: result = synthesized_func(*args, **kwargs) if callable(result): @@ -413,5 +415,5 @@ def _call(self, template, *args, **kwargs) -> Callable: return result except (ValueError, TypeError): pass - + return synthesized_func diff --git a/tests/test_handlers_llm_provider_synthesis.py b/tests/test_handlers_llm_provider_synthesis.py index 10b4c0aa..bbc8dfc2 100644 --- a/tests/test_handlers_llm_provider_synthesis.py +++ b/tests/test_handlers_llm_provider_synthesis.py @@ -102,7 +102,7 @@ def test_inspect_getsource_works(self): assert isinstance(source, str) assert len(source) > 0 assert "def" in source - + # __source__ attribute should also be available with full module code assert hasattr(count_func, "__source__") assert "def" in count_func.__source__ From 53b2b950ec15ea885ca7b3763e791d2966497faa Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 15:12:44 -0500 Subject: [PATCH 41/65] Add immutable lexical context test --- tests/test_handlers_llm_provider_synthesis.py | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/tests/test_handlers_llm_provider_synthesis.py b/tests/test_handlers_llm_provider_synthesis.py index bbc8dfc2..0ab01fce 100644 --- a/tests/test_handlers_llm_provider_synthesis.py +++ b/tests/test_handlers_llm_provider_synthesis.py @@ -75,7 +75,7 @@ def test_generates_callable(self): """Test ProgramSynthesis handler generates executable code.""" with ( handler(LiteLLMProvider(model_name="gpt-4o-mini")), - handler(ProgramSynthesis()), + handler(ProgramSynthesis(type_check=True)), handler(LimitLLMCallsHandler(max_calls=1)), ): count_func = create_function("a") @@ -92,7 +92,7 @@ def test_inspect_getsource_works(self): """Test that inspect.getsource() works on synthesized functions.""" with ( handler(LiteLLMProvider(model_name="gpt-4o-mini")), - handler(ProgramSynthesis()), + handler(ProgramSynthesis(type_check=True)), handler(LimitLLMCallsHandler(max_calls=1)), ): count_func = create_function("x") @@ -115,7 +115,7 @@ def test_synthesized_attribute(self): with ( handler(LiteLLMProvider(model_name="gpt-4o-mini")), - handler(ProgramSynthesis()), + handler(ProgramSynthesis(type_check=True)), handler(LimitLLMCallsHandler(max_calls=1)), ): count_func = create_function("z") @@ -126,3 +126,38 @@ def test_synthesized_attribute(self): assert "def" in count_func.__synthesized__.module_code # The function should work correctly assert count_func("pizza") == 2 # two 'z's in "pizza" + + def test_immutable_context(self): + """Test that synthesis does not pollute the original lexical context.""" + from effectful.handlers.llm import LexicalContext + from effectful.handlers.llm.synthesis import ( + EncodableSynthesizedFunction, + SynthesizedFunction, + ) + + # Create a context with known contents + original_context = LexicalContext({"helper": lambda x: x * 2}) + original_keys = set(original_context.keys()) + + # Synthesize a function that defines new names + synth = SynthesizedFunction( + function_name="my_func", + module_code=""" +def internal_helper(x): + return x + 1 + +def my_func(n): + return internal_helper(n) * 2 +""", + ) + + # Decode with the context + func = EncodableSynthesizedFunction.decode(synth, context=original_context) + + # Verify the function works + assert func(5) == 12 # (5 + 1) * 2 + + # Verify original context was NOT polluted with new definitions + assert set(original_context.keys()) == original_keys + assert "my_func" not in original_context + assert "internal_helper" not in original_context From 324f23c88746124f7376b7b1807a42fefe61b5b9 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 16:03:07 -0500 Subject: [PATCH 42/65] Initial Class Synthesis Attempt --- effectful/handlers/llm/synthesis.py | 13 +- effectful/handlers/llm/type_synthesis.py | 262 ++++++++++++++++++ ...t_handlers_llm_provider_class_synthesis.py | 146 ++++++++++ 3 files changed, 418 insertions(+), 3 deletions(-) create mode 100644 effectful/handlers/llm/type_synthesis.py create mode 100644 tests/test_handlers_llm_provider_class_synthesis.py diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 135200f5..3a3854f7 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -350,9 +350,8 @@ def _call(self, template, *args, **kwargs) -> Callable: origin = typing.get_origin(ret_type) ret_type_origin = ret_type if origin is None else origin - # Check if return type is Callable - handle both class and typing special forms - is_callable = ret_type_origin is collections.abc.Callable - if not is_callable: + # Check if return type is Callable + if ret_type_origin is not collections.abc.Callable: return fwd() # Include the full lexical context - all functions, types, values available to synthesized code @@ -366,6 +365,14 @@ def _call(self, template, *args, **kwargs) -> Callable: ``` """ + return self._handle_callable_synthesis( + template, ret_type, context_section, *args, **kwargs + ) + + def _handle_callable_synthesis( + self, template, ret_type, context_section: str, *args, **kwargs + ) -> Callable: + """Handle synthesis of Callable return types.""" prompt_ext = textwrap.dedent(f""" Implement a Python function with the following specification. diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py new file mode 100644 index 00000000..6e586ec9 --- /dev/null +++ b/effectful/handlers/llm/type_synthesis.py @@ -0,0 +1,262 @@ +"""Type/class synthesis for LLM-generated code.""" + +import collections +import collections.abc +import dataclasses +import inspect +import linecache +import textwrap +import typing + +import pydantic +from litellm import OpenAIMessageContentListBlock +from pydantic import Field + +from effectful.handlers.llm import LexicalContext, Template +from effectful.handlers.llm.encoding import EncodableAs, type_to_encodable_type +from effectful.handlers.llm.synthesis import ( + EncodableLexicalContext, + SynthesisError, + run_mypy_check, +) +from effectful.ops.semantics import fwd +from effectful.ops.syntax import ObjectInterpretation, implements + + +class SynthesizedType(pydantic.BaseModel): + """Structured output for type/class synthesis. + + Pydantic model representing synthesized class code with type name and module code. + """ + + type_name: str = Field( + ..., + description="The name of the class that satisfies the specification", + ) + module_code: str = Field( + ..., + description="Complete Python module code with the class definition (no imports needed)", + ) + + +@type_to_encodable_type.register(type) +class EncodableSynthesizedType( + EncodableAs[type, SynthesizedType], +): + """Encodes type to SynthesizedType and vice versa.""" + + t = SynthesizedType + + @classmethod + def encode(cls, vl: type, context: LexicalContext | None = None) -> SynthesizedType: + """Encode a type to a SynthesizedType. + + Extracts the type name and source code. + """ + type_name = vl.__name__ + try: + source = inspect.getsource(vl) + except (OSError, TypeError): + # If we can't get source, create a minimal representation + source = f"class {type_name}: pass # Source unavailable" + + return SynthesizedType( + type_name=type_name, module_code=textwrap.dedent(source).strip() + ) + + # Counter for unique filenames + _decode_counter: typing.ClassVar[int] = 0 + + @classmethod + def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> type: + """Decode a SynthesizedType to a type. + + Executes the module code and returns the named class. + The module code becomes the class's definition context, + optionally augmented with provided context. + """ + type_name = vl.type_name + module_code = textwrap.dedent(vl.module_code).strip() + + # Create a unique filename and register source with linecache + # This allows inspect.getsource() to work on the generated class + cls._decode_counter += 1 + filename = f"" + lines = module_code.splitlines(keepends=True) + # Ensure last line has newline for linecache + if lines and not lines[-1].endswith("\n"): + lines[-1] += "\n" + linecache.cache[filename] = ( + len(module_code), + None, + lines, + filename, + ) + + # Start with provided context or empty dict + # Include collections module for type hints in synthesized code + exec_globals: dict[str, typing.Any] = {"collections": collections} + if context: + exec_globals.update(context) + + try: + code_obj = compile(module_code, filename, "exec") + exec(code_obj, exec_globals) + except SyntaxError as exc: + raise SynthesisError( + f"Syntax error in generated code: {exc}", module_code + ) from exc + except Exception as exc: + raise SynthesisError(f"Evaluation failed: {exc!r}", module_code) from exc + + if type_name not in exec_globals: + raise SynthesisError( + f"Type '{type_name}' not found after execution. " + f"Available names: {[k for k in exec_globals.keys() if not k.startswith('_')]}", + module_code, + ) + + synthesized_type = exec_globals[type_name] + + if not isinstance(synthesized_type, type): + raise SynthesisError( + f"'{type_name}' is not a type, got {type(synthesized_type).__name__}", + module_code, + ) + + # Attach source code directly for convenience + synthesized_type.__source__ = module_code # type: ignore[attr-defined] + synthesized_type.__synthesized__ = vl # type: ignore[attr-defined] + return synthesized_type + + @classmethod + def serialize(cls, vl: SynthesizedType) -> list[OpenAIMessageContentListBlock]: + return [{"type": "text", "text": vl.model_dump_json()}] + + +class TypeSynthesis(ObjectInterpretation): + """Provides a `template` handler to instruct the LLM to generate a class/type + that inherits from a specified base type. + """ + + def __init__(self, type_check: bool = False): + """Initialize the type synthesis handler. + + Args: + type_check: Whether to run mypy to verify the generated code. + """ + self.type_check = type_check + + def _build_type( + self, + result: SynthesizedType, + base_type: type, + lexical_context: LexicalContext, + ) -> type: + """Build and execute a type from the synthesized module. + + Uses EncodableSynthesizedType.decode with the template's lexical context. + Optionally runs mypy type checking if enabled. + Validates that the synthesized type is a subclass of base_type. + + Args: + result: The structured output from the LLM + base_type: The expected base type (e.g., Animal for type[Animal]) + lexical_context: Dict of lexical context (functions/types) available in scope + + Returns: + The synthesized type (class) + """ + if self.type_check: + success, error_msg = run_mypy_check(result.module_code, lexical_context) + if not success: + raise SynthesisError( + f"Type check failed:\n{error_msg}", result.module_code + ) + + synthesized_type = EncodableSynthesizedType.decode( + result, context=lexical_context + ) + + # Validate that synthesized type inherits from base_type + if not issubclass(synthesized_type, base_type): + raise SynthesisError( + f"Synthesized type '{synthesized_type.__name__}' does not inherit from '{base_type.__name__}'", + result.module_code, + ) + + return synthesized_type + + @implements(Template.__call__) + def _call(self, template, *args, **kwargs) -> type: + ret_type = template.__signature__.return_annotation + origin = typing.get_origin(ret_type) + ret_type_origin = ret_type if origin is None else origin + + # Check if return type is type[BaseClass] + if ret_type_origin is not type: + return fwd() + + # Extract the base type from type[BaseClass] + type_args = typing.get_args(ret_type) + if not type_args: + raise SynthesisError( + "Type synthesis requires a base type, e.g., type[Animal]. " + "Got bare 'type' without a type parameter.", + None, + ) + + base_type = type_args[0] + + # Verify base type is in lexical context + base_type_name = base_type.__name__ + if base_type_name not in template.__context__: + raise SynthesisError( + f"Base type '{base_type_name}' must be in the template's lexical context.", + None, + ) + + # Include the full lexical context + context_source = EncodableLexicalContext.encode(template.__context__) + escaped_context = context_source.replace("{", "{{").replace("}", "}}") + context_section = f""" +The following types, functions, and values are available: + +```python +{escaped_context} +``` +""" + + prompt_ext = textwrap.dedent(f""" + Implement a Python class with the following specification. + + **Specification:** {template.__prompt_template__} + + **Required:** The class must inherit from `{base_type_name}` and implement its interface. + {context_section} + **Instructions:** + 1. Write a complete Python module with the class definition. + 2. Choose a descriptive class name. + 3. The class MUST inherit from `{base_type_name}`. + 4. Implement all required methods from the base class. + 5. You may include helper functions/classes/constants. + 6. Do not redefine provided types - they are already available. + 7. Do not include import statements. + """).strip() + + response: SynthesizedType = fwd( + dataclasses.replace( + template, + __prompt_template__=prompt_ext, + __signature__=template.__signature__.replace( + return_annotation=SynthesizedType + ), + ), + *args, + **kwargs, + ) + + # Build the type using lexical context for exec globals + synthesized_type = self._build_type(response, base_type, template.__context__) + + return synthesized_type diff --git a/tests/test_handlers_llm_provider_class_synthesis.py b/tests/test_handlers_llm_provider_class_synthesis.py new file mode 100644 index 00000000..74e905b1 --- /dev/null +++ b/tests/test_handlers_llm_provider_class_synthesis.py @@ -0,0 +1,146 @@ +"""Tests for LLM type/class synthesis functionality.""" + +import functools +import os + +import pytest + +from effectful.handlers.llm import Template +from effectful.handlers.llm.providers import LiteLLMProvider, completion +from effectful.handlers.llm.synthesis import SynthesisError +from effectful.handlers.llm.type_synthesis import TypeSynthesis +from effectful.ops.semantics import fwd, handler +from effectful.ops.syntax import ObjectInterpretation, implements +from effectful.ops.types import NotHandled + +# Check for API keys +HAS_OPENAI_KEY = "OPENAI_API_KEY" in os.environ and os.environ["OPENAI_API_KEY"] + +requires_openai = pytest.mark.skipif( + not HAS_OPENAI_KEY, reason="OPENAI_API_KEY environment variable not set" +) + + +def retry_on_error(error: type[Exception], n: int): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + for i in range(n): + try: + return func(*args, **kwargs) + except error as e: + if i < n - 1: + continue + raise e + + return wrapper + + return decorator + + +class LimitLLMCallsHandler(ObjectInterpretation): + max_calls: int + no_calls: int = 0 + + def __init__(self, max_calls: int): + self.max_calls = max_calls + + @implements(completion) + def _completion(self, *args, **kwargs): + if self.no_calls >= self.max_calls: + raise RuntimeError( + f"Test used too many requests (max_calls = {self.max_calls})" + ) + self.no_calls += 1 + return fwd() + + +# Base class for type synthesis tests +class Animal: + """Base class for animals.""" + + def speak(self) -> str: + raise NotImplementedError + + def move(self) -> str: + raise NotImplementedError + + +@Template.define +def create_animal(behavior: str) -> type[Animal]: + """Create an Animal subclass with the specified behavior: {behavior} + + The class should implement speak() and move() methods. + Do not use any tools. + """ + raise NotHandled + + +class TestTypeSynthesis: + """Tests for type (class) synthesis functionality.""" + + @requires_openai + @retry_on_error(error=SynthesisError, n=3) + def test_generates_subtype(self): + """Test that TypeSynthesis can generate a subtype of a base class.""" + with ( + handler(LiteLLMProvider(model_name="gpt-4o-mini")), + handler(TypeSynthesis()), + handler(LimitLLMCallsHandler(max_calls=1)), + ): + DogClass = create_animal("a dog that barks and walks") + + # Verify it's a type + assert isinstance(DogClass, type) + # Verify it inherits from Animal + assert issubclass(DogClass, Animal) + + # Create an instance and test methods + dog = DogClass() + speak_result = dog.speak() + move_result = dog.move() + + assert isinstance(speak_result, str) + assert isinstance(move_result, str) + + @requires_openai + @retry_on_error(error=SynthesisError, n=3) + def test_synthesized_type_has_source(self): + """Test that synthesized types have __source__ attribute.""" + with ( + handler(LiteLLMProvider(model_name="gpt-4o-mini")), + handler(TypeSynthesis()), + handler(LimitLLMCallsHandler(max_calls=1)), + ): + CatClass = create_animal("a cat that meows and prowls") + + assert hasattr(CatClass, "__source__") + assert "class" in CatClass.__source__ + assert hasattr(CatClass, "__synthesized__") + + def test_type_synthesis_requires_base_in_context(self): + """Test that type synthesis fails if base type is not in lexical context.""" + import dataclasses + + from effectful.handlers.llm import LexicalContext + + # Create a template with Animal in return type but NOT in context + @Template.define + def create_orphan() -> type[Animal]: + """Create an animal.""" + raise NotHandled + + # Create a modified template with empty context + orphan_template = dataclasses.replace( + create_orphan, + __context__=LexicalContext({}), # Empty context - no Animal + ) + + with pytest.raises( + SynthesisError, match="must be in the template's lexical context" + ): + with ( + handler(LiteLLMProvider(model_name="gpt-4o-mini")), + handler(TypeSynthesis()), + ): + orphan_template() From 62c649a22a204ab1c2fa8dbd8e32fbdbc27e344e Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 16:18:38 -0500 Subject: [PATCH 43/65] Readding type check mypy --- effectful/handlers/llm/synthesis.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 135200f5..14984c0f 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -260,22 +260,31 @@ def _get_imports_from_lexical_context( def run_mypy_check( code: str, lexical_context: LexicalContext, + function_name: str | None = None, + expected_type: type | None = None, ) -> tuple[bool, str]: """Run mypy on generated code to verify type correctness. Args: code: The generated function code lexical_context: Lexical context containing types for imports + function_name: Name of the function to type-check + expected_type: Expected Callable type (e.g., Callable[[str], int]) Returns: A tuple of (success: bool, error_message: str) """ - # Always include collections.abc for Callable type assertions - imports = ["import collections.abc"] + imports = ["from typing import Callable", "import collections.abc"] imports.extend(_get_imports_from_lexical_context(lexical_context)) - source_parts = imports + [textwrap.dedent(code).strip()] - full_source = "\n".join(source_parts) + # Build full module with the generated code + module_parts = imports + ["", textwrap.dedent(code).strip()] + + # Add type assertion if expected_type is provided + if function_name and expected_type: + module_parts.append(f"_: {repr(expected_type)} = {function_name}") + + full_source = "\n".join(module_parts) with tempfile.NamedTemporaryFile( mode="w", suffix=".py", delete_on_close=False @@ -336,7 +345,12 @@ def _build_function( The synthesized callable function """ if self.type_check: - success, error_msg = run_mypy_check(result.module_code, lexical_context) + success, error_msg = run_mypy_check( + result.module_code, + lexical_context, + function_name=result.function_name, + expected_type=callable_type, + ) if not success: raise SynthesisError( f"Type check failed:\n{error_msg}", result.module_code From 23309f180c1baa0c4870d0148bf300849ef717fa Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 16:36:11 -0500 Subject: [PATCH 44/65] Update synthesis prompt so that it will not misunderstand and return a function that create function --- effectful/handlers/llm/synthesis.py | 72 ++++++++++++++--------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 14984c0f..b3efe0ae 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -328,7 +328,6 @@ def __init__(self, type_check: bool = False): def _build_function( self, result: SynthesizedFunction, - callable_type: type, lexical_context: LexicalContext, ) -> Callable: """Build and execute a function from the synthesized module. @@ -338,26 +337,37 @@ def _build_function( Args: result: The structured output from the LLM - callable_type: The expected Callable type (e.g., Callable[[str], int]) lexical_context: Dict of lexical context (functions/types) available in scope Returns: The synthesized callable function """ - if self.type_check: - success, error_msg = run_mypy_check( - result.module_code, - lexical_context, - function_name=result.function_name, - expected_type=callable_type, - ) - if not success: - raise SynthesisError( - f"Type check failed:\n{error_msg}", result.module_code - ) - return EncodableSynthesizedFunction.decode(result, context=lexical_context) + def _run_type_check( + self, + result: SynthesizedFunction, + expected_type: type | None, + lexical_context: LexicalContext, + ) -> None: + """Run mypy type checking on the synthesized code. + + Args: + result: The structured output from the LLM + expected_type: The expected Callable type, or None to skip type assertion + lexical_context: Dict of lexical context for imports + """ + success, error_msg = run_mypy_check( + result.module_code, + lexical_context, + function_name=result.function_name if expected_type else None, + expected_type=expected_type, + ) + if not success: + raise SynthesisError( + f"Type check failed:\n{error_msg}", result.module_code + ) + @implements(Template.__call__) def _call(self, template, *args, **kwargs) -> Callable: ret_type = template.__signature__.return_annotation @@ -389,10 +399,11 @@ def _call(self, template, *args, **kwargs) -> Callable: {context_section} **Instructions:** 1. Write a complete Python module with the function. - 2. Choose descriptive function and parameter names. - 3. You may include helper functions/classes/constants. - 4. Do not redefine provided types - they are already available. - 5. Do not include import statements. + 2. The main function MUST have the exact signature shown above. + 3. Do NOT create a factory function - implement the function directly. + 4. You may include helper functions/classes/constants. + 5. Do not redefine provided types - they are already available. + 6. Do not include import statements. """).strip() response: SynthesizedFunction = fwd( @@ -408,26 +419,11 @@ def _call(self, template, *args, **kwargs) -> Callable: ) # Build the function using lexical context for exec globals - synthesized_func = self._build_function( - response, ret_type, template.__context__ - ) + synthesized_func = self._build_function(response, template.__context__) + + # Run type check if enabled + if self.type_check: + self._run_type_check(response, ret_type, template.__context__) - try: - synth_sig = inspect.signature(synthesized_func) - template_sig = template.__signature__ - - synth_params = set(synth_sig.parameters.keys()) - template_params = set(template_sig.parameters.keys()) - - if synth_params == template_params: - result = synthesized_func(*args, **kwargs) - if callable(result): - if hasattr(synthesized_func, "__source__"): - result.__source__ = synthesized_func.__source__ - if hasattr(synthesized_func, "__synthesized__"): - result.__synthesized__ = synthesized_func.__synthesized__ - return result - except (ValueError, TypeError): - pass return synthesized_func From 54b72ea0ec167b6013aa071c05c22c7cc601152e Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 16:44:36 -0500 Subject: [PATCH 45/65] Update --- effectful/handlers/llm/synthesis.py | 5 +---- tests/test_handlers_llm_provider_synthesis.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index b3efe0ae..8c0b45b2 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -364,9 +364,7 @@ def _run_type_check( expected_type=expected_type, ) if not success: - raise SynthesisError( - f"Type check failed:\n{error_msg}", result.module_code - ) + raise SynthesisError(f"Type check failed:\n{error_msg}", result.module_code) @implements(Template.__call__) def _call(self, template, *args, **kwargs) -> Callable: @@ -425,5 +423,4 @@ def _call(self, template, *args, **kwargs) -> Callable: if self.type_check: self._run_type_check(response, ret_type, template.__context__) - return synthesized_func diff --git a/tests/test_handlers_llm_provider_synthesis.py b/tests/test_handlers_llm_provider_synthesis.py index 0ab01fce..238a98ef 100644 --- a/tests/test_handlers_llm_provider_synthesis.py +++ b/tests/test_handlers_llm_provider_synthesis.py @@ -59,7 +59,7 @@ def _completion(self, *args, **kwargs): @Template.define def create_function(char: str) -> Callable[[str], int]: """Create a function that counts occurrences of the character '{char}' in a string. - Do not use any tools. + Do not use any tools and implement this function directly. Return as a code block with the last definition being the function. """ From 59ecdd09212c46987a5217289818882632e93b4d Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 16:54:47 -0500 Subject: [PATCH 46/65] Update prompt to avoid factory generation again --- effectful/handlers/llm/synthesis.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 8c0b45b2..62289f06 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -393,15 +393,24 @@ def _call(self, template, *args, **kwargs) -> Callable: **Specification:** {template.__prompt_template__} - **Required signature:** {repr(ret_type)} + **Required function signature:** {repr(ret_type)} {context_section} - **Instructions:** - 1. Write a complete Python module with the function. - 2. The main function MUST have the exact signature shown above. - 3. Do NOT create a factory function - implement the function directly. + **Critical Instructions:** + 1. The function you write MUST have EXACTLY this signature: {repr(ret_type)} + 2. Any values mentioned in the specification (like specific characters or strings) should be hardcoded directly in the function body, NOT as parameters. + 3. Do NOT create a wrapper or factory function. Write the function directly. 4. You may include helper functions/classes/constants. 5. Do not redefine provided types - they are already available. 6. Do not include import statements. + + Example: If asked to "count occurrences of 'a'" with signature Callable[[str], int], write: + def count_a(text: str) -> int: + return text.count('a') + NOT: + def make_counter(char: str) -> Callable[[str], int]: + def inner(text: str) -> int: + return text.count(char) + return inner """).strip() response: SynthesizedFunction = fwd( From ae62b24145784fa052bf0477196823aefa3fb729 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 20:40:23 -0500 Subject: [PATCH 47/65] Remove duplicated import --- ...t_handlers_llm_provider_class_synthesis.py | 51 +++---------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/tests/test_handlers_llm_provider_class_synthesis.py b/tests/test_handlers_llm_provider_class_synthesis.py index 74e905b1..5693f708 100644 --- a/tests/test_handlers_llm_provider_class_synthesis.py +++ b/tests/test_handlers_llm_provider_class_synthesis.py @@ -1,60 +1,21 @@ """Tests for LLM type/class synthesis functionality.""" -import functools -import os - import pytest from effectful.handlers.llm import Template -from effectful.handlers.llm.providers import LiteLLMProvider, completion +from effectful.handlers.llm.providers import LiteLLMProvider from effectful.handlers.llm.synthesis import SynthesisError from effectful.handlers.llm.type_synthesis import TypeSynthesis -from effectful.ops.semantics import fwd, handler -from effectful.ops.syntax import ObjectInterpretation, implements +from effectful.ops.semantics import handler from effectful.ops.types import NotHandled -# Check for API keys -HAS_OPENAI_KEY = "OPENAI_API_KEY" in os.environ and os.environ["OPENAI_API_KEY"] - -requires_openai = pytest.mark.skipif( - not HAS_OPENAI_KEY, reason="OPENAI_API_KEY environment variable not set" +from .test_handlers_llm_provider import ( + LimitLLMCallsHandler, + requires_openai, + retry_on_error, ) -def retry_on_error(error: type[Exception], n: int): - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - for i in range(n): - try: - return func(*args, **kwargs) - except error as e: - if i < n - 1: - continue - raise e - - return wrapper - - return decorator - - -class LimitLLMCallsHandler(ObjectInterpretation): - max_calls: int - no_calls: int = 0 - - def __init__(self, max_calls: int): - self.max_calls = max_calls - - @implements(completion) - def _completion(self, *args, **kwargs): - if self.no_calls >= self.max_calls: - raise RuntimeError( - f"Test used too many requests (max_calls = {self.max_calls})" - ) - self.no_calls += 1 - return fwd() - - # Base class for type synthesis tests class Animal: """Base class for animals.""" From 7ac9471cf41a0252e0631f40e3c2d5594afb42e4 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 16 Dec 2025 20:40:45 -0500 Subject: [PATCH 48/65] Remove unnecesary redefinition --- tests/test_handlers_llm_provider_synthesis.py | 52 +++---------------- 1 file changed, 6 insertions(+), 46 deletions(-) diff --git a/tests/test_handlers_llm_provider_synthesis.py b/tests/test_handlers_llm_provider_synthesis.py index 238a98ef..db86ea00 100644 --- a/tests/test_handlers_llm_provider_synthesis.py +++ b/tests/test_handlers_llm_provider_synthesis.py @@ -1,61 +1,21 @@ """Tests for LLM program synthesis functionality.""" -import functools import inspect -import os from collections.abc import Callable -import pytest - from effectful.handlers.llm import Template -from effectful.handlers.llm.providers import LiteLLMProvider, completion +from effectful.handlers.llm.providers import LiteLLMProvider from effectful.handlers.llm.synthesis import ProgramSynthesis, SynthesisError -from effectful.ops.semantics import fwd, handler -from effectful.ops.syntax import ObjectInterpretation, implements +from effectful.ops.semantics import handler from effectful.ops.types import NotHandled -# Check for API keys -HAS_OPENAI_KEY = "OPENAI_API_KEY" in os.environ and os.environ["OPENAI_API_KEY"] - -requires_openai = pytest.mark.skipif( - not HAS_OPENAI_KEY, reason="OPENAI_API_KEY environment variable not set" +from .test_handlers_llm_provider import ( + LimitLLMCallsHandler, + requires_openai, + retry_on_error, ) -def retry_on_error(error: type[Exception], n: int): - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - for i in range(n): - try: - return func(*args, **kwargs) - except error as e: - if i < n - 1: - continue - raise e - - return wrapper - - return decorator - - -class LimitLLMCallsHandler(ObjectInterpretation): - max_calls: int - no_calls: int = 0 - - def __init__(self, max_calls: int): - self.max_calls = max_calls - - @implements(completion) - def _completion(self, *args, **kwargs): - if self.no_calls >= self.max_calls: - raise RuntimeError( - f"Test used too many requests (max_calls = {self.max_calls})" - ) - self.no_calls += 1 - return fwd() - - @Template.define def create_function(char: str) -> Callable[[str], int]: """Create a function that counts occurrences of the character '{char}' in a string. From b034aa8fb77ce3ece705ad04f3babe117d0b2f87 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 10:42:03 -0500 Subject: [PATCH 49/65] Working inspect get source for type --- effectful/handlers/llm/type_synthesis.py | 38 ++++++++++++++----- ...t_handlers_llm_provider_class_synthesis.py | 20 ++++++++-- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py index 6e586ec9..71649645 100644 --- a/effectful/handlers/llm/type_synthesis.py +++ b/effectful/handlers/llm/type_synthesis.py @@ -5,7 +5,9 @@ import dataclasses import inspect import linecache +import sys import textwrap +import types import typing import pydantic @@ -76,12 +78,20 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t optionally augmented with provided context. """ type_name = vl.type_name - module_code = textwrap.dedent(vl.module_code).strip() + module_code = textwrap.dedent(vl.module_code).strip() + "\n" # Create a unique filename and register source with linecache # This allows inspect.getsource() to work on the generated class cls._decode_counter += 1 - filename = f"" + # NOTE: adding source to class is more tricky + # because for function func.__code__.co_filename (set by compile(..., filename, "exec")) is set automatically + # We have to do this manually for class (set module name) for inspect.getsource() to work + module_name = ( + f"_llm_effectful_synthesized_types.{type_name}.{cls._decode_counter}" + ) + filename = f"" + + # Register source for inspect/linecache lines = module_code.splitlines(keepends=True) # Ensure last line has newline for linecache if lines and not lines[-1].endswith("\n"): @@ -93,15 +103,22 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t filename, ) - # Start with provided context or empty dict - # Include collections module for type hints in synthesized code - exec_globals: dict[str, typing.Any] = {"collections": collections} + # Create a real module and put it to sys.modules + mod = types.ModuleType(module_name) + mod.__file__ = filename + sys.modules[module_name] = mod # type: ignore[attr-defined] + + # globals = module.__dict__ + context + g = mod.__dict__ + g.update({"collections": collections}) if context: - exec_globals.update(context) + g.update(context) + g.update({"__name__": module_name, "__file__": filename}) + g.setdefault("__package__", module_name.rpartition(".")[0]) try: code_obj = compile(module_code, filename, "exec") - exec(code_obj, exec_globals) + exec(code_obj, g, g) except SyntaxError as exc: raise SynthesisError( f"Syntax error in generated code: {exc}", module_code @@ -109,14 +126,14 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t except Exception as exc: raise SynthesisError(f"Evaluation failed: {exc!r}", module_code) from exc - if type_name not in exec_globals: + if type_name not in g: raise SynthesisError( f"Type '{type_name}' not found after execution. " - f"Available names: {[k for k in exec_globals.keys() if not k.startswith('_')]}", + f"Available names: {[k for k in g.keys() if not k.startswith('_')]}", module_code, ) - synthesized_type = exec_globals[type_name] + synthesized_type = g[type_name] if not isinstance(synthesized_type, type): raise SynthesisError( @@ -127,6 +144,7 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t # Attach source code directly for convenience synthesized_type.__source__ = module_code # type: ignore[attr-defined] synthesized_type.__synthesized__ = vl # type: ignore[attr-defined] + synthesized_type.__module__ = module_name return synthesized_type @classmethod diff --git a/tests/test_handlers_llm_provider_class_synthesis.py b/tests/test_handlers_llm_provider_class_synthesis.py index 5693f708..eb914c0c 100644 --- a/tests/test_handlers_llm_provider_class_synthesis.py +++ b/tests/test_handlers_llm_provider_class_synthesis.py @@ -1,9 +1,15 @@ """Tests for LLM type/class synthesis functionality.""" +import ast +import inspect +import linecache +import logging +import sys + import pytest from effectful.handlers.llm import Template -from effectful.handlers.llm.providers import LiteLLMProvider +from effectful.handlers.llm.providers import LiteLLMProvider, LLMLoggingHandler from effectful.handlers.llm.synthesis import SynthesisError from effectful.handlers.llm.type_synthesis import TypeSynthesis from effectful.ops.semantics import handler @@ -68,16 +74,24 @@ def test_generates_subtype(self): @retry_on_error(error=SynthesisError, n=3) def test_synthesized_type_has_source(self): """Test that synthesized types have __source__ attribute.""" + logger = logging.getLogger("effectful.llm") + logger.setLevel(logging.INFO) + log_handler = logging.StreamHandler(sys.stdout) + log_handler.setFormatter(logging.Formatter("%(levelname)s %(payload)s")) + logger.addHandler(log_handler) + llm_logger = LLMLoggingHandler(logger=logger) with ( handler(LiteLLMProvider(model_name="gpt-4o-mini")), handler(TypeSynthesis()), handler(LimitLLMCallsHandler(max_calls=1)), + handler(llm_logger), ): CatClass = create_animal("a cat that meows and prowls") - assert hasattr(CatClass, "__source__") - assert "class" in CatClass.__source__ + source = inspect.getsource(CatClass) assert hasattr(CatClass, "__synthesized__") + assert hasattr(CatClass, "__source__") + assert source == CatClass.__source__ def test_type_synthesis_requires_base_in_context(self): """Test that type synthesis fails if base type is not in lexical context.""" From 090249177f3965017921835708ee95bd525ee03c Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 10:48:31 -0500 Subject: [PATCH 50/65] Lint --- tests/test_handlers_llm_provider_class_synthesis.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_handlers_llm_provider_class_synthesis.py b/tests/test_handlers_llm_provider_class_synthesis.py index eb914c0c..4b4e43e3 100644 --- a/tests/test_handlers_llm_provider_class_synthesis.py +++ b/tests/test_handlers_llm_provider_class_synthesis.py @@ -1,8 +1,6 @@ """Tests for LLM type/class synthesis functionality.""" -import ast import inspect -import linecache import logging import sys From f85bd430eac9cb917c5e745a9a29b615934bbed1 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 11:05:07 -0500 Subject: [PATCH 51/65] Fix python 3.13+ compatibility --- effectful/handlers/llm/type_synthesis.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py index 71649645..d6631c89 100644 --- a/effectful/handlers/llm/type_synthesis.py +++ b/effectful/handlers/llm/type_synthesis.py @@ -1,5 +1,6 @@ """Type/class synthesis for LLM-generated code.""" +import ast import collections import collections.abc import dataclasses @@ -141,6 +142,17 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t module_code, ) + # Find the line number of the class definition for Python 3.13+ compatibility + # (inspect.getsource looks for __firstlineno__ on classes) + try: + tree = ast.parse(module_code) + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef) and node.name == type_name: + synthesized_type.__firstlineno__ = node.lineno # type: ignore[attr-defined] + break + except SyntaxError: + pass + # Attach source code directly for convenience synthesized_type.__source__ = module_code # type: ignore[attr-defined] synthesized_type.__synthesized__ = vl # type: ignore[attr-defined] From dfd22ebcd5516c6a95a304d4dcf55bf25a1f9879 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 11:06:58 -0500 Subject: [PATCH 52/65] Lint --- effectful/handlers/llm/type_synthesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py index d6631c89..5c706b84 100644 --- a/effectful/handlers/llm/type_synthesis.py +++ b/effectful/handlers/llm/type_synthesis.py @@ -148,7 +148,7 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t tree = ast.parse(module_code) for node in ast.walk(tree): if isinstance(node, ast.ClassDef) and node.name == type_name: - synthesized_type.__firstlineno__ = node.lineno # type: ignore[attr-defined] + synthesized_type.__firstlineno__ = node.lineno break except SyntaxError: pass From af01df4b87c2dbefb2ea5478bca97332a02b2cc3 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 11:07:29 -0500 Subject: [PATCH 53/65] Lint --- effectful/handlers/llm/type_synthesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py index 5c706b84..566b4d5d 100644 --- a/effectful/handlers/llm/type_synthesis.py +++ b/effectful/handlers/llm/type_synthesis.py @@ -107,7 +107,7 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t # Create a real module and put it to sys.modules mod = types.ModuleType(module_name) mod.__file__ = filename - sys.modules[module_name] = mod # type: ignore[attr-defined] + sys.modules[module_name] = mod # globals = module.__dict__ + context g = mod.__dict__ From ba3c1c8c8d136abe8dffb5c61234a659c507b9c9 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 11:10:24 -0500 Subject: [PATCH 54/65] Lint --- effectful/handlers/llm/type_synthesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py index 566b4d5d..3512c9e3 100644 --- a/effectful/handlers/llm/type_synthesis.py +++ b/effectful/handlers/llm/type_synthesis.py @@ -148,7 +148,7 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t tree = ast.parse(module_code) for node in ast.walk(tree): if isinstance(node, ast.ClassDef) and node.name == type_name: - synthesized_type.__firstlineno__ = node.lineno + synthesized_type.__firstlineno__ = node.lineno # type: ignore[attr-defined] break except SyntaxError: pass From 00dc079b2e010b04db17dd118450426cf15b455d Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 12:37:53 -0500 Subject: [PATCH 55/65] More 3.13+ compatibility --- effectful/handlers/llm/type_synthesis.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py index 3512c9e3..04457ea3 100644 --- a/effectful/handlers/llm/type_synthesis.py +++ b/effectful/handlers/llm/type_synthesis.py @@ -142,16 +142,19 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t module_code, ) - # Find the line number of the class definition for Python 3.13+ compatibility - # (inspect.getsource looks for __firstlineno__ on classes) - try: - tree = ast.parse(module_code) - for node in ast.walk(tree): - if isinstance(node, ast.ClassDef) and node.name == type_name: - synthesized_type.__firstlineno__ = node.lineno # type: ignore[attr-defined] - break - except SyntaxError: - pass + # NOTE: Set __firstlineno__ for Python 3.13+ compatibility + # Python 3.13's exec() should set this automatically, but we set it manually as a fallback + if not hasattr(synthesized_type, "__firstlineno__"): + firstlineno = 1 + try: + tree = ast.parse(module_code) + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef) and node.name == type_name: + firstlineno = node.lineno + break + except SyntaxError: + pass + synthesized_type.__firstlineno__ = firstlineno # type: ignore[attr-defined] # Attach source code directly for convenience synthesized_type.__source__ = module_code # type: ignore[attr-defined] From f7263a0143cc1590fcebba4b10aceaf5e76aabc9 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 12:56:10 -0500 Subject: [PATCH 56/65] Minor --- effectful/handlers/llm/type_synthesis.py | 32 +++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py index 04457ea3..8d1be267 100644 --- a/effectful/handlers/llm/type_synthesis.py +++ b/effectful/handlers/llm/type_synthesis.py @@ -118,7 +118,23 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t g.setdefault("__package__", module_name.rpartition(".")[0]) try: - code_obj = compile(module_code, filename, "exec") + # NOTE: Parse and inject __firstlineno__ into class bodies for Python 3.13+ compatibility + # inspect.getsource() looks for __firstlineno__ in vars(cls), which requires it to be in the class's __dict__. + # We inject it via AST before execution. + tree = ast.parse(module_code) + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + # Create: __firstlineno__ = + assign = ast.Assign( + targets=[ast.Name(id="__firstlineno__", ctx=ast.Store())], + value=ast.Constant(value=node.lineno), + lineno=node.lineno, + col_offset=0, + ) + ast.fix_missing_locations(assign) + node.body.insert(0, assign) + ast.fix_missing_locations(tree) + code_obj = compile(tree, filename, "exec") exec(code_obj, g, g) except SyntaxError as exc: raise SynthesisError( @@ -142,20 +158,6 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t module_code, ) - # NOTE: Set __firstlineno__ for Python 3.13+ compatibility - # Python 3.13's exec() should set this automatically, but we set it manually as a fallback - if not hasattr(synthesized_type, "__firstlineno__"): - firstlineno = 1 - try: - tree = ast.parse(module_code) - for node in ast.walk(tree): - if isinstance(node, ast.ClassDef) and node.name == type_name: - firstlineno = node.lineno - break - except SyntaxError: - pass - synthesized_type.__firstlineno__ = firstlineno # type: ignore[attr-defined] - # Attach source code directly for convenience synthesized_type.__source__ = module_code # type: ignore[attr-defined] synthesized_type.__synthesized__ = vl # type: ignore[attr-defined] From eb8a2a31a964d68a799702e6ca6edf2e453fcb48 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Wed, 17 Dec 2025 13:44:06 -0500 Subject: [PATCH 57/65] More compatibility fixing --- effectful/handlers/llm/type_synthesis.py | 32 +++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/effectful/handlers/llm/type_synthesis.py b/effectful/handlers/llm/type_synthesis.py index 8d1be267..bcc71456 100644 --- a/effectful/handlers/llm/type_synthesis.py +++ b/effectful/handlers/llm/type_synthesis.py @@ -3,6 +3,7 @@ import ast import collections import collections.abc +import ctypes import dataclasses import inspect import linecache @@ -11,6 +12,17 @@ import types import typing + +class _PyMappingProxyObject(ctypes.Structure): + """Internal ctypes structure to access the underlying dict of a mappingproxy.""" + + _fields_ = [ + ("ob_refcnt", ctypes.c_ssize_t), + ("ob_type", ctypes.py_object), + ("mapping", ctypes.py_object), + ] + + import pydantic from litellm import OpenAIMessageContentListBlock from pydantic import Field @@ -158,10 +170,28 @@ def decode(cls, vl: SynthesizedType, context: LexicalContext | None = None) -> t module_code, ) - # Attach source code directly for convenience + # Attach source code and module name synthesized_type.__source__ = module_code # type: ignore[attr-defined] synthesized_type.__synthesized__ = vl # type: ignore[attr-defined] synthesized_type.__module__ = module_name + + # NOTE: Set __firstlineno__ AFTER __module__ assignment! + # In Python 3.13, setting __module__ clears __firstlineno__ from vars(). + # We use ctypes to directly inject it into __dict__ for inspect.getsource(). + if "__firstlineno__" not in vars(synthesized_type): + firstlineno = next( + ( + n.lineno + for n in ast.walk(ast.parse(module_code)) + if isinstance(n, ast.ClassDef) and n.name == type_name + ), + 1, + ) + inner_dict = _PyMappingProxyObject.from_address( + id(vars(synthesized_type)) + ).mapping + inner_dict["__firstlineno__"] = firstlineno + return synthesized_type @classmethod From 50ce47c0d201dad10aa275a083bc47933a4f4ecc Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 22 Dec 2025 15:12:07 -0500 Subject: [PATCH 58/65] EncodableSynthesizedFunction --- effectful/handlers/llm/encoding.py | 25 +++-- effectful/handlers/llm/providers.py | 2 +- effectful/handlers/llm/synthesis.py | 154 +++++++++++++++++++++------- 3 files changed, 134 insertions(+), 47 deletions(-) diff --git a/effectful/handlers/llm/encoding.py b/effectful/handlers/llm/encoding.py index fe8b7e21..abcf45b5 100644 --- a/effectful/handlers/llm/encoding.py +++ b/effectful/handlers/llm/encoding.py @@ -37,7 +37,13 @@ def encode(cls, vl: T) -> U: @classmethod @abstractmethod - def decode(cls, vl: U) -> T: + def decode(cls, vl: U, template: typing.Any = None) -> T: + """Decode an encoded value back to the original type. + + Args: + vl: The encoded value + template: Optional Template providing context (e.g., lexical scope) + """ pass @classmethod @@ -67,7 +73,7 @@ def encode(cls, vl: T) -> T: return vl @classmethod - def decode(cls, vl: T) -> T: + def decode(cls, vl: T, template: typing.Any = None) -> T: return vl return typing.cast(Encodable[T], BaseEncodable()) @@ -81,7 +87,7 @@ class EncodablePydanticBaseModel(EncodableAs[T, T]): t: type[T] = ty @classmethod - def decode(cls, vl: T) -> T: + def decode(cls, vl: T, template: typing.Any = None) -> T: return vl @classmethod @@ -107,7 +113,9 @@ def encode(cls, image: Image.Image) -> ChatCompletionImageUrlObject: } @classmethod - def decode(cls, image: ChatCompletionImageUrlObject) -> Image.Image: + def decode( + cls, image: ChatCompletionImageUrlObject, template: typing.Any = None + ) -> Image.Image: image_url = image["url"] if not image_url.startswith("data:image/"): raise RuntimeError( @@ -156,13 +164,14 @@ def encode(cls, t: T) -> typing.Any: return tuple([enc.encode(elem) for enc, elem in zip(element_encoders, t)]) @classmethod - def decode(cls, t: typing.Any) -> T: + def decode(cls, t: typing.Any, template: typing.Any = None) -> T: if len(t) != len(element_encoders): raise ValueError( f"tuple length {len(t)} does not match expected length {len(element_encoders)}" ) decoded_elements: list[typing.Any] = [ - enc.decode(elem) for enc, elem in zip(element_encoders, t) + enc.decode(elem, template=template) + for enc, elem in zip(element_encoders, t) ] return typing.cast(T, tuple(decoded_elements)) @@ -217,9 +226,9 @@ def encode(cls, t: T) -> typing.Any: return [element_encoder.encode(elem) for elem in t] @classmethod - def decode(cls, t: typing.Any) -> T: + def decode(cls, t: typing.Any, template: typing.Any = None) -> T: decoded_elements: list[typing.Any] = [ - element_encoder.decode(elem) for elem in t + element_encoder.decode(elem, template=template) for elem in t ] return typing.cast(T, decoded_elements) diff --git a/effectful/handlers/llm/providers.py b/effectful/handlers/llm/providers.py index aa091d67..1a6537f6 100644 --- a/effectful/handlers/llm/providers.py +++ b/effectful/handlers/llm/providers.py @@ -437,7 +437,7 @@ def decode_response[**P, T](template: Callable[P, T], response: ModelResponse) - assert isinstance(result, Result) value = result.value # type: ignore - return encodable_ty.decode(value) # type: ignore + return encodable_ty.decode(value, template=template) # type: ignore @defop diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index b9308776..97eb6a1a 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,12 +1,17 @@ -import ast import collections.abc import dataclasses +import inspect import linecache -import re import textwrap import typing +from collections.abc import Callable -from effectful.handlers.llm import Template +import pydantic +from pydantic import Field + +from effectful.handlers.llm import LexicalContext, Template +from effectful.handlers.llm.encoding import EncodableAs, type_to_encodable_type +from effectful.handlers.llm.providers import OpenAIMessageContentListBlock from effectful.ops.semantics import fwd from effectful.ops.syntax import ObjectInterpretation, implements @@ -19,47 +24,124 @@ def __init__(self, message, code=None): self.code = code -class ProgramSynthesis(ObjectInterpretation): - """Provides a `template` handler to instruct the LLM to generate code of the - right form and with the right type. +class SynthesizedFunction(pydantic.BaseModel): + """Structured output for function synthesis. + Pydantic model representing synthesized code with function name and module code. """ - def _parse_and_eval[T](self, t: type[T], content: str) -> T: - pattern = r"(.*?)" - code_content = re.search(pattern, content, re.DOTALL) - if code_content is None: - raise SynthesisError(" tags not found", content) - code = code_content.group(1) + function_name: str = Field( + ..., + description="The name of the main function that satisfies the specification", + ) + module_code: str = Field( + ..., + description="Complete Python module code (no imports needed)", + ) + + +@type_to_encodable_type.register(collections.abc.Callable) +class EncodableSynthesizedFunction( + EncodableAs[Callable, SynthesizedFunction], +): + """Encodes Callable to SynthesizedFunction and vice versa.""" + + t = SynthesizedFunction + + @classmethod + def encode( + cls, vl: Callable, context: LexicalContext | None = None + ) -> SynthesizedFunction: + """Encode a Callable to a SynthesizedFunction. + + Extracts the function name and source code. + """ + func_name = vl.__name__ + try: + source = inspect.getsource(vl) + except (OSError, TypeError): + # If we can't get source, create a minimal representation + try: + sig = inspect.signature(vl) + source = f"def {func_name}{sig}:\n pass # Source unavailable" + except (ValueError, TypeError): + source = f"def {func_name}(...):\n pass # Source unavailable" + + return SynthesizedFunction( + function_name=func_name, module_code=textwrap.dedent(source).strip() + ) + + # Counter for unique filenames + _decode_counter: typing.ClassVar[int] = 0 + + @classmethod + def decode(cls, vl: SynthesizedFunction, template: typing.Any = None) -> Callable: + """Decode a SynthesizedFunction to a Callable. + + Executes the module code and returns the named function. + The module code becomes the function's lexical context, + optionally augmented with the template's context. + """ + # Extract lexical context from template if provided + context: LexicalContext | None = None + if template is not None and hasattr(template, "__context__"): + ctx = template.__context__ + context = ctx if isinstance(ctx, LexicalContext) else LexicalContext(ctx) + func_name = vl.function_name + module_code = textwrap.dedent(vl.module_code).strip() + + cls._decode_counter += 1 + filename = f"" + lines = module_code.splitlines(keepends=True) + # Ensure last line has newline for linecache + if lines and not lines[-1].endswith("\n"): + lines[-1] += "\n" + linecache.cache[filename] = ( + len(module_code), + None, + lines, + filename, + ) + + # Start with provided context or empty dict + # Include collections module for type hints in synthesized code + exec_globals: dict[str, typing.Any] = {"collections": collections} + if context: + exec_globals.update(context) try: - module_ast = ast.parse(code) + code_obj = compile(module_code, filename, "exec") + exec(code_obj, exec_globals) except SyntaxError as exc: - raise SynthesisError("failed to parse", content) from exc + raise SynthesisError( + f"Syntax error in generated code: {exc}", module_code + ) from exc + except Exception as exc: + raise SynthesisError(f"Evaluation failed: {exc!r}", module_code) from exc - if not isinstance(module_ast, ast.Module): - raise SynthesisError("not a module", content) + if func_name not in exec_globals: + raise SynthesisError( + f"Function '{func_name}' not found after execution. " + f"Available names: {[k for k in exec_globals.keys() if not k.startswith('_')]}", + module_code, + ) - last_decl = module_ast.body[-1] - if not isinstance(last_decl, ast.FunctionDef): - raise SynthesisError("last definition not a function", content) + func = exec_globals[func_name] + # Also attach source code directly for convenience + func.__source__ = module_code + func.__synthesized__ = vl + return func - source_code = textwrap.dedent(code) - lines = code.splitlines(keepends=True) - filename = f"" + @classmethod + def serialize(cls, vl: SynthesizedFunction) -> list[OpenAIMessageContentListBlock]: + return [{"type": "text", "text": vl.model_dump_json()}] - # register into linecache - linecache.cache[filename] = (len(source_code), None, lines, filename) - # TODO: support injecting lexical context for synthesized code - gs: dict = {} - try: - code_obj = compile(source_code, filename, "exec") - exec(code_obj, gs) - except Exception as exc: - raise SynthesisError("evaluation failed", content) from exc +class ProgramSynthesis(ObjectInterpretation): + """Provides a `template` handler to instruct the LLM to generate code of the + right form and with the right type. - return gs[last_decl.name] + """ @implements(Template.__call__) def _call(self, template, *args, **kwargs) -> None: @@ -85,16 +167,12 @@ def _call(self, template, *args, **kwargs) -> None: """).strip() - response = fwd( + return fwd( dataclasses.replace( template, __prompt_template__=prompt_ext, - __signature__=template.__signature__.replace(return_annotation=str), + __signature__=template.__signature__, ), *args, **kwargs, ) - - functional = self._parse_and_eval(ret_type, response) - - return functional From b0f28e7a4f60a6fa6b8e40572647bc8ffce203a2 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Mon, 22 Dec 2025 22:17:38 -0500 Subject: [PATCH 59/65] Passing test --- effectful/handlers/llm/encoding.py | 22 +++++-------- effectful/handlers/llm/providers.py | 9 +++-- effectful/handlers/llm/synthesis.py | 37 ++++++++++++++++----- tests/test_handlers_llm.py | 51 +++++++++++++++++++++++------ tests/test_handlers_llm_provider.py | 2 +- 5 files changed, 84 insertions(+), 37 deletions(-) diff --git a/effectful/handlers/llm/encoding.py b/effectful/handlers/llm/encoding.py index abcf45b5..2f82a97b 100644 --- a/effectful/handlers/llm/encoding.py +++ b/effectful/handlers/llm/encoding.py @@ -37,13 +37,8 @@ def encode(cls, vl: T) -> U: @classmethod @abstractmethod - def decode(cls, vl: U, template: typing.Any = None) -> T: - """Decode an encoded value back to the original type. - - Args: - vl: The encoded value - template: Optional Template providing context (e.g., lexical scope) - """ + def decode(cls, vl: U) -> T: + """Decode an encoded value back to the original type.""" pass @classmethod @@ -73,7 +68,7 @@ def encode(cls, vl: T) -> T: return vl @classmethod - def decode(cls, vl: T, template: typing.Any = None) -> T: + def decode(cls, vl: T) -> T: return vl return typing.cast(Encodable[T], BaseEncodable()) @@ -87,7 +82,7 @@ class EncodablePydanticBaseModel(EncodableAs[T, T]): t: type[T] = ty @classmethod - def decode(cls, vl: T, template: typing.Any = None) -> T: + def decode(cls, vl: T) -> T: return vl @classmethod @@ -164,14 +159,13 @@ def encode(cls, t: T) -> typing.Any: return tuple([enc.encode(elem) for enc, elem in zip(element_encoders, t)]) @classmethod - def decode(cls, t: typing.Any, template: typing.Any = None) -> T: + def decode(cls, t: typing.Any) -> T: if len(t) != len(element_encoders): raise ValueError( f"tuple length {len(t)} does not match expected length {len(element_encoders)}" ) decoded_elements: list[typing.Any] = [ - enc.decode(elem, template=template) - for enc, elem in zip(element_encoders, t) + enc.decode(elem) for enc, elem in zip(element_encoders, t) ] return typing.cast(T, tuple(decoded_elements)) @@ -226,9 +220,9 @@ def encode(cls, t: T) -> typing.Any: return [element_encoder.encode(elem) for elem in t] @classmethod - def decode(cls, t: typing.Any, template: typing.Any = None) -> T: + def decode(cls, t: typing.Any) -> T: decoded_elements: list[typing.Any] = [ - element_encoder.decode(elem, template=template) for elem in t + element_encoder.decode(elem) for elem in t ] return typing.cast(T, decoded_elements) diff --git a/effectful/handlers/llm/providers.py b/effectful/handlers/llm/providers.py index 1a6537f6..949d5399 100644 --- a/effectful/handlers/llm/providers.py +++ b/effectful/handlers/llm/providers.py @@ -414,9 +414,12 @@ def compute_response(template: Template, model_input: list[Any]) -> ModelRespons ) +@defop def decode_response[**P, T](template: Callable[P, T], response: ModelResponse) -> T: - """Decode an LLM response into an instance of the template return type. This - operation should raise if the output cannot be decoded. + """Decode an LLM response into an instance of the template return type. + + This is an operation that can be handled to customize decoding behavior. + The default implementation uses the encoder's decode method. """ assert isinstance(template, Template) choice: Choices = typing.cast(Choices, response.choices[0]) @@ -437,7 +440,7 @@ def decode_response[**P, T](template: Callable[P, T], response: ModelResponse) - assert isinstance(result, Result) value = result.value # type: ignore - return encodable_ty.decode(value, template=template) # type: ignore + return encodable_ty.decode(value) # type: ignore @defop diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 97eb6a1a..3467c5ef 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,3 +1,4 @@ +import collections import collections.abc import dataclasses import inspect @@ -7,11 +8,15 @@ from collections.abc import Callable import pydantic +from litellm.types.utils import ModelResponse from pydantic import Field from effectful.handlers.llm import LexicalContext, Template from effectful.handlers.llm.encoding import EncodableAs, type_to_encodable_type -from effectful.handlers.llm.providers import OpenAIMessageContentListBlock +from effectful.handlers.llm.providers import ( + OpenAIMessageContentListBlock, + decode_response, +) from effectful.ops.semantics import fwd from effectful.ops.syntax import ObjectInterpretation, implements @@ -75,18 +80,13 @@ def encode( _decode_counter: typing.ClassVar[int] = 0 @classmethod - def decode(cls, vl: SynthesizedFunction, template: typing.Any = None) -> Callable: + def decode(cls, vl: SynthesizedFunction) -> Callable: """Decode a SynthesizedFunction to a Callable. Executes the module code and returns the named function. - The module code becomes the function's lexical context, - optionally augmented with the template's context. + Uses _decode_context attribute on vl if present (set by ProgramSynthesis). """ - # Extract lexical context from template if provided - context: LexicalContext | None = None - if template is not None and hasattr(template, "__context__"): - ctx = template.__context__ - context = ctx if isinstance(ctx, LexicalContext) else LexicalContext(ctx) + context: LexicalContext | None = getattr(vl, "_decode_context", None) func_name = vl.function_name module_code = textwrap.dedent(vl.module_code).strip() @@ -176,3 +176,22 @@ def _call(self, template, *args, **kwargs) -> None: *args, **kwargs, ) + + @implements(decode_response) + def _decode_response(self, template: Template, response: ModelResponse) -> Callable: + """Decode a synthesized function response with lexical context.""" + ret_type = template.__signature__.return_annotation + origin = typing.get_origin(ret_type) + ret_type_origin = ret_type if origin is None else origin + + # Only handle Callable return types + if ret_type_origin is not collections.abc.Callable: + return fwd() + + # Parse JSON and attach context to the value for decode() to use + choice = response.choices[0] + result_str = choice.message.content or "" + Result = pydantic.create_model("Result", value=(SynthesizedFunction, ...)) + synth = Result.model_validate_json(result_str).value + object.__setattr__(synth, "_decode_context", template.__context__) + return EncodableSynthesizedFunction.decode(synth) diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index 9e0e510f..0bb1d87b 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -1,9 +1,10 @@ from collections.abc import Callable import pytest +from litellm.types.utils import Choices, Message, ModelResponse from effectful.handlers.llm import Template -from effectful.handlers.llm.providers import RetryLLMHandler +from effectful.handlers.llm.providers import RetryLLMHandler, decode_response from effectful.handlers.llm.synthesis import ProgramSynthesis from effectful.ops.semantics import NotHandled, handler from effectful.ops.syntax import ObjectInterpretation, implements @@ -39,13 +40,17 @@ def _call[**P]( class SingleResponseLLMProvider[T](ObjectInterpretation): - """Simplified mock provider that returns a single response for any prompt.""" + """Simplified mock provider that returns a single response for any prompt. + + Simulates LiteLLMProvider behavior by creating a ModelResponse and calling + decode_response, allowing handlers like ProgramSynthesis to intercept. + """ def __init__(self, response: T): - """Initialize with a single response string. + """Initialize with a single response. Args: - response: The response to return for any template call + response: The response value (will be converted to string for Message) """ self.response = response @@ -53,7 +58,30 @@ def __init__(self, response: T): def _call[**P]( self, template: Template[P, T], *args: P.args, **kwargs: P.kwargs ) -> T: - return self.response + # Convert response to string for Message.content (like real LLM) + # For structured output, wrap in {"value": ...} JSON format + if isinstance(self.response, str): + content = self.response + else: + import json + + content = json.dumps({"value": self.response}) + + # Create a mock ModelResponse like LiteLLMProvider would + mock_response = ModelResponse( + id="mock", + choices=[ + Choices( + finish_reason="stop", + index=0, + message=Message(content=content, role="assistant"), + ) + ], + created=0, + model="mock", + ) + # Call decode_response to let handlers intercept + return decode_response(template, mock_response) # Test templates from the notebook examples @@ -120,11 +148,14 @@ def test_primes_decode_int(): def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" - mock_code = """ -def count_occurrences(s): - return s.count('a') -""" - mock_provider = SingleResponseLLMProvider(mock_code) + # Use JSON format matching SynthesizedFunction schema (constrained decoding) + mock_response = """{ + "value": { + "function_name": "count_occurrences", + "module_code": "def count_occurrences(s):\\n return s.count('a')" + } + }""" + mock_provider = SingleResponseLLMProvider(mock_response) with handler(mock_provider), handler(ProgramSynthesis()): count_a = count_char("a") diff --git a/tests/test_handlers_llm_provider.py b/tests/test_handlers_llm_provider.py index ac51bebd..83684a0a 100644 --- a/tests/test_handlers_llm_provider.py +++ b/tests/test_handlers_llm_provider.py @@ -281,7 +281,7 @@ def smiley_face() -> Image.Image: @Template.define def categorise_image(image: Image.Image) -> str: - """Return a description of the following image. Do not use any tools. + """Return a description of the following image. You MUST NOT use any tools. {image}""" raise NotHandled From 7abea68af16d2a2c8e43a5040f08668e79a1b251 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 23 Dec 2025 09:47:37 -0500 Subject: [PATCH 60/65] Linting --- effectful/handlers/llm/synthesis.py | 6 ++-- tests/test_handlers_llm.py | 48 ++++++++++------------------- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 3467c5ef..7a48c2bc 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -189,9 +189,9 @@ def _decode_response(self, template: Template, response: ModelResponse) -> Calla return fwd() # Parse JSON and attach context to the value for decode() to use - choice = response.choices[0] - result_str = choice.message.content or "" + choice = typing.cast(typing.Any, response.choices[0]) + result_str: str = choice.message.content or "" Result = pydantic.create_model("Result", value=(SynthesizedFunction, ...)) - synth = Result.model_validate_json(result_str).value + synth: SynthesizedFunction = Result.model_validate_json(result_str).value # type: ignore[attr-defined] object.__setattr__(synth, "_decode_context", template.__context__) return EncodableSynthesizedFunction.decode(synth) diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index 0bb1d87b..073610b2 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -1,3 +1,4 @@ +import json from collections.abc import Callable import pytest @@ -5,7 +6,7 @@ from effectful.handlers.llm import Template from effectful.handlers.llm.providers import RetryLLMHandler, decode_response -from effectful.handlers.llm.synthesis import ProgramSynthesis +from effectful.handlers.llm.synthesis import ProgramSynthesis, SynthesizedFunction from effectful.ops.semantics import NotHandled, handler from effectful.ops.syntax import ObjectInterpretation, implements @@ -39,48 +40,33 @@ def _call[**P]( return response -class SingleResponseLLMProvider[T](ObjectInterpretation): - """Simplified mock provider that returns a single response for any prompt. +class SingleResponseLLMProvider(ObjectInterpretation): + """Simplified mock provider that returns a single string response. Simulates LiteLLMProvider behavior by creating a ModelResponse and calling decode_response, allowing handlers like ProgramSynthesis to intercept. """ - def __init__(self, response: T): - """Initialize with a single response. - - Args: - response: The response value (will be converted to string for Message) - """ + def __init__(self, response: str): + """Initialize with a response string (as LLM would return).""" self.response = response @implements(Template.__call__) - def _call[**P]( + def _call[**P, T]( self, template: Template[P, T], *args: P.args, **kwargs: P.kwargs ) -> T: - # Convert response to string for Message.content (like real LLM) - # For structured output, wrap in {"value": ...} JSON format - if isinstance(self.response, str): - content = self.response - else: - import json - - content = json.dumps({"value": self.response}) - - # Create a mock ModelResponse like LiteLLMProvider would mock_response = ModelResponse( id="mock", choices=[ Choices( finish_reason="stop", index=0, - message=Message(content=content, role="assistant"), + message=Message(content=self.response, role="assistant"), ) ], created=0, model="mock", ) - # Call decode_response to let handlers intercept return decode_response(template, mock_response) @@ -138,7 +124,7 @@ def test_limerick(): def test_primes_decode_int(): """Test the primes template correctly decodes integer response.""" - mock_provider = SingleResponseLLMProvider(61) + mock_provider = SingleResponseLLMProvider('{"value": 61}') with handler(mock_provider): result = primes(6) @@ -148,14 +134,14 @@ def test_primes_decode_int(): def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" - # Use JSON format matching SynthesizedFunction schema (constrained decoding) - mock_response = """{ - "value": { - "function_name": "count_occurrences", - "module_code": "def count_occurrences(s):\\n return s.count('a')" - } - }""" - mock_provider = SingleResponseLLMProvider(mock_response) + # JSON format as LLM would return with constrained decoding + mock_response = SynthesizedFunction( + function_name="count_occurrences", + module_code="def count_occurrences(s):\n return s.count('a')", + ) + mock_provider = SingleResponseLLMProvider( + json.dumps({"value": mock_response.model_dump()}) + ) with handler(mock_provider), handler(ProgramSynthesis()): count_a = count_char("a") From 19fbfa4eb2d8faff2ea20b1a8d75554c1799ca53 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 23 Dec 2025 09:51:20 -0500 Subject: [PATCH 61/65] Trim decode changes --- effectful/handlers/llm/encoding.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/effectful/handlers/llm/encoding.py b/effectful/handlers/llm/encoding.py index 2f82a97b..fe8b7e21 100644 --- a/effectful/handlers/llm/encoding.py +++ b/effectful/handlers/llm/encoding.py @@ -38,7 +38,6 @@ def encode(cls, vl: T) -> U: @classmethod @abstractmethod def decode(cls, vl: U) -> T: - """Decode an encoded value back to the original type.""" pass @classmethod @@ -108,9 +107,7 @@ def encode(cls, image: Image.Image) -> ChatCompletionImageUrlObject: } @classmethod - def decode( - cls, image: ChatCompletionImageUrlObject, template: typing.Any = None - ) -> Image.Image: + def decode(cls, image: ChatCompletionImageUrlObject) -> Image.Image: image_url = image["url"] if not image_url.startswith("data:image/"): raise RuntimeError( From ada85125736ee5bfb9da76d33cfb0b89d96253f8 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 23 Dec 2025 09:53:30 -0500 Subject: [PATCH 62/65] Linting tests --- tests/test_handlers_llm.py | 43 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index 073610b2..81b58ab6 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -6,7 +6,7 @@ from effectful.handlers.llm import Template from effectful.handlers.llm.providers import RetryLLMHandler, decode_response -from effectful.handlers.llm.synthesis import ProgramSynthesis, SynthesizedFunction +from effectful.handlers.llm.synthesis import ProgramSynthesis from effectful.ops.semantics import NotHandled, handler from effectful.ops.syntax import ObjectInterpretation, implements @@ -40,28 +40,39 @@ def _call[**P]( return response -class SingleResponseLLMProvider(ObjectInterpretation): - """Simplified mock provider that returns a single string response. +class SingleResponseLLMProvider[T](ObjectInterpretation): + """Simplified mock provider that returns a single response. Simulates LiteLLMProvider behavior by creating a ModelResponse and calling - decode_response, allowing handlers like ProgramSynthesis to intercept. + decode_response. Automatically wraps response in {"value": ...} for non-string types. """ - def __init__(self, response: str): - """Initialize with a response string (as LLM would return).""" + def __init__(self, response: T): + """Initialize with a response value.""" self.response = response @implements(Template.__call__) - def _call[**P, T]( - self, template: Template[P, T], *args: P.args, **kwargs: P.kwargs - ) -> T: + def _call[**P, R]( + self, template: Template[P, R], *args: P.args, **kwargs: P.kwargs + ) -> R: + from effectful.handlers.llm.encoding import type_to_encodable_type + + ret_type = template.__signature__.return_annotation + encodable_ty = type_to_encodable_type(ret_type) + + # For str types, use raw value; otherwise wrap in {"value": ...} + if encodable_ty.t == str: + content = str(self.response) + else: + content = json.dumps({"value": self.response}) + mock_response = ModelResponse( id="mock", choices=[ Choices( finish_reason="stop", index=0, - message=Message(content=self.response, role="assistant"), + message=Message(content=content, role="assistant"), ) ], created=0, @@ -124,7 +135,7 @@ def test_limerick(): def test_primes_decode_int(): """Test the primes template correctly decodes integer response.""" - mock_provider = SingleResponseLLMProvider('{"value": 61}') + mock_provider = SingleResponseLLMProvider(61) with handler(mock_provider): result = primes(6) @@ -134,13 +145,11 @@ def test_primes_decode_int(): def test_count_char_with_program_synthesis(): """Test the count_char template with program synthesis.""" - # JSON format as LLM would return with constrained decoding - mock_response = SynthesizedFunction( - function_name="count_occurrences", - module_code="def count_occurrences(s):\n return s.count('a')", - ) mock_provider = SingleResponseLLMProvider( - json.dumps({"value": mock_response.model_dump()}) + { + "function_name": "count_occurrences", + "module_code": "def count_occurrences(s):\n return s.count('a')", + } ) with handler(mock_provider), handler(ProgramSynthesis()): From e6e21bb883e8bb4d6cd2fe09f76dc1e8fdc888f2 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Tue, 23 Dec 2025 11:01:16 -0500 Subject: [PATCH 63/65] Minor --- effectful/handlers/llm/synthesis.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 7a48c2bc..292458a5 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -105,9 +105,8 @@ def decode(cls, vl: SynthesizedFunction) -> Callable: # Start with provided context or empty dict # Include collections module for type hints in synthesized code - exec_globals: dict[str, typing.Any] = {"collections": collections} - if context: - exec_globals.update(context) + exec_globals: dict[str, typing.Any] = {} + exec_globals.update(context) try: code_obj = compile(module_code, filename, "exec") From 74589b4ca19e397838f356a3bd931ff1285ef552 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Sun, 4 Jan 2026 13:41:50 -0500 Subject: [PATCH 64/65] Minor fix and merge --- effectful/handlers/llm/synthesis.py | 17 +++++++---------- tests/test_handlers_llm.py | 4 +--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 292458a5..1c1e7713 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -1,17 +1,18 @@ import collections import collections.abc -import dataclasses import inspect import linecache import textwrap import typing +from collections import ChainMap from collections.abc import Callable +from typing import Any import pydantic from litellm.types.utils import ModelResponse from pydantic import Field -from effectful.handlers.llm import LexicalContext, Template +from effectful.handlers.llm import Template from effectful.handlers.llm.encoding import EncodableAs, type_to_encodable_type from effectful.handlers.llm.providers import ( OpenAIMessageContentListBlock, @@ -55,7 +56,7 @@ class EncodableSynthesizedFunction( @classmethod def encode( - cls, vl: Callable, context: LexicalContext | None = None + cls, vl: Callable, context: ChainMap[str, Any] | None = None ) -> SynthesizedFunction: """Encode a Callable to a SynthesizedFunction. @@ -86,7 +87,7 @@ def decode(cls, vl: SynthesizedFunction) -> Callable: Executes the module code and returns the named function. Uses _decode_context attribute on vl if present (set by ProgramSynthesis). """ - context: LexicalContext | None = getattr(vl, "_decode_context", None) + context: ChainMap[str, Any] | None = getattr(vl, "_decode_context", None) func_name = vl.function_name module_code = textwrap.dedent(vl.module_code).strip() @@ -142,7 +143,7 @@ class ProgramSynthesis(ObjectInterpretation): """ - @implements(Template.__call__) + @implements(Template.__apply__) def _call(self, template, *args, **kwargs) -> None: ret_type = template.__signature__.return_annotation origin = typing.get_origin(ret_type) @@ -167,11 +168,7 @@ def _call(self, template, *args, **kwargs) -> None: """).strip() return fwd( - dataclasses.replace( - template, - __prompt_template__=prompt_ext, - __signature__=template.__signature__, - ), + template.replace(prompt_template=prompt_ext), *args, **kwargs, ) diff --git a/tests/test_handlers_llm.py b/tests/test_handlers_llm.py index aa1f74ec..ebaaeda9 100644 --- a/tests/test_handlers_llm.py +++ b/tests/test_handlers_llm.py @@ -1,12 +1,10 @@ -import json from collections.abc import Callable from typing import Annotated import pytest -from litellm.types.utils import Choices, Message, ModelResponse from effectful.handlers.llm import Template -from effectful.handlers.llm.providers import RetryLLMHandler, decode_response +from effectful.handlers.llm.providers import RetryLLMHandler from effectful.handlers.llm.synthesis import ProgramSynthesis from effectful.handlers.llm.template import IsRecursive from effectful.ops.semantics import NotHandled, handler From af65d9dbd13194276c61245bd4fc821c218b3685 Mon Sep 17 00:00:00 2001 From: datvo06 Date: Sun, 4 Jan 2026 14:06:17 -0500 Subject: [PATCH 65/65] Minor --- effectful/handlers/llm/encoding.py | 7 ------- effectful/handlers/llm/synthesis.py | 1 - 2 files changed, 8 deletions(-) diff --git a/effectful/handlers/llm/encoding.py b/effectful/handlers/llm/encoding.py index 1a319289..241634c3 100644 --- a/effectful/handlers/llm/encoding.py +++ b/effectful/handlers/llm/encoding.py @@ -74,12 +74,6 @@ def decode(cls, vl: T) -> T: return typing.cast(Encodable[T], BaseEncodable()) -<<<<<<< HEAD -# NOTE: Register str explicitly to avoid WeakKeyDictionary cache issues with singledispatch -@type_to_encodable_type.register(str) -def _type_encodable_type_str[T](ty: type[T]) -> Encodable[T]: - return _type_encodable_type_base(ty) -======= @type_to_encodable_type.register(Term) def _type_encodable_type_term[T: Term](ty: type[T]) -> Encodable[T]: raise TypeError("Terms cannot be encoded or decoded in general.") @@ -88,7 +82,6 @@ def _type_encodable_type_term[T: Term](ty: type[T]) -> Encodable[T]: @type_to_encodable_type.register(Operation) def _type_encodable_type_operation[T: Operation](ty: type[T]) -> Encodable[T]: raise TypeError("Operations cannot be encoded or decoded in general.") ->>>>>>> 74589b4ca19e397838f356a3bd931ff1285ef552 @type_to_encodable_type.register(pydantic.BaseModel) diff --git a/effectful/handlers/llm/synthesis.py b/effectful/handlers/llm/synthesis.py index 1bea2031..ce93043a 100644 --- a/effectful/handlers/llm/synthesis.py +++ b/effectful/handlers/llm/synthesis.py @@ -3,7 +3,6 @@ import inspect import linecache import textwrap -import types import typing from collections import ChainMap from collections.abc import Callable