from IPython.display import display, Markdown, clear_output
from pprint import pprint
' '.join(models)
'gpt-5 gpt-5-mini gpt-5-nano o1-preview o1-mini gpt-4o gpt-4o-mini gpt-4-turbo gpt-4 gpt-4-32k gpt-3.5-turbo gpt-3.5-turbo-instruct o1 o3-mini chatgpt-4o-latest o1-pro o3 o4-mini gpt-4.1 gpt-4.1-mini gpt-4.1-nano'
model = models[1]
model
'gpt-5-mini'

Sample Data

def _get_orders_customers():
    orders = {
        "O1": dict(id="O1", product="Widget A", quantity=2, price=19.99, status="Shipped"),
        "O2": dict(id="O2", product="Gadget B", quantity=1, price=49.99, status="Processing"),
        "O3": dict(id="O3", product="Gadget B", quantity=2, price=49.99, status="Shipped")}

    customers = {
        "C1": dict(name="John Doe", email="john@example.com", phone="123-456-7890",
                   orders=[orders['O1'], orders['O2']]),
        "C2": dict(name="Jane Smith", email="jane@example.com", phone="987-654-3210",
                   orders=[orders['O3']])
    }
    return orders, customers
orders, customers = _get_orders_customers()
def get_customer_info(
    customer_id:str # ID of the customer
): # Customer's name, email, phone number, and list of orders
    "Retrieves a customer's information and their orders based on the customer ID"
    print(f'- Retrieving customer {customer_id}')
    return customers.get(customer_id, "Customer not found")

def get_order_details(
    order_id:str # ID of the order
): # Order's ID, product name, quantity, price, and order status
    "Retrieves the details of a specific order based on the order ID"
    print(f'- Retrieving order {order_id}')
    return orders.get(order_id, "Order not found")

def cancel_order(
    order_id:str # ID of the order to cancel
)->bool: # True if the cancellation is successful
    "Cancels an order based on the provided order ID"
    print(f'- Cancelling order {order_id}')
    if order_id not in orders: return False
    orders[order_id]['status'] = 'Cancelled'
    return True
chatkw = dict(
    text={ "verbosity": "low" },
    reasoning={ "effort": "minimal" }
)
tools = [get_customer_info, get_order_details, cancel_order]
chat = Chat(model, tools=tools, **chatkw)
r = chat('Hi.')
r

Hello! How can I help you today?

  • id: resp_6897e0de4c348190bf1946e354518b8b0c0dd261ca702a77
  • created_at: 1754783966.0
  • error: None
  • incomplete_details: None
  • instructions: None
  • metadata: {}
  • model: gpt-5-mini-2025-08-07
  • object: response
  • output: [ResponseReasoningItem(id=‘rs_6897e0def4188190ac0593dbdac886be0c0dd261ca702a77’, summary=[], type=‘reasoning’, content=None, encrypted_content=None, status=None), ResponseOutputMessage(id=‘msg_6897e0df0d088190954040dcc2c7f18c0c0dd261ca702a77’, content=[ResponseOutputText(annotations=[], text=‘Hello! How can I help you today?’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
  • parallel_tool_calls: True
  • temperature: 1.0
  • tool_choice: auto
  • tools: [FunctionTool(name=‘get_customer_info’, parameters={‘type’: ‘object’, ‘properties’: {‘customer_id’: {‘type’: ‘string’, ‘description’: ‘ID of the customer’}}, ‘required’: [‘customer_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=“Retrieves a customer’s information and their orders based on the customer ID”), FunctionTool(name=‘get_order_details’, parameters={‘type’: ‘object’, ‘properties’: {‘order_id’: {‘type’: ‘string’, ‘description’: ‘ID of the order’}}, ‘required’: [‘order_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Retrieves the details of a specific order based on the order ID’), FunctionTool(name=‘cancel_order’, parameters={‘type’: ‘object’, ‘properties’: {‘order_id’: {‘type’: ‘string’, ‘description’: ‘ID of the order to cancel’}}, ‘required’: [‘order_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Cancels an order based on the provided order ID:- type: boolean’)]
  • top_p: 1.0
  • background: False
  • max_output_tokens: 4096
  • max_tool_calls: None
  • previous_response_id: None
  • prompt: None
  • prompt_cache_key: None
  • reasoning: Reasoning(effort=‘minimal’, generate_summary=None, summary=None)
  • safety_identifier: None
  • service_tier: default
  • status: completed
  • text: ResponseTextConfig(format=ResponseFormatText(type=‘text’), verbosity=‘low’)
  • top_logprobs: 0
  • truncation: disabled
  • usage: ResponseUsage(input_tokens=136, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=15, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=151)
  • user: None
  • store: True
r = chat('Can you tell me the email address for customer C2?')
r.output
- Retrieving customer C2
[ResponseReasoningItem(id='rs_6897e0f0949c8190af24da7373614a300c0dd261ca702a77', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"customer_id":"C2"}', call_id='call_7qPUHLapCRWKcPqdoFkRYChc', name='get_customer_info', type='function_call', id='fc_6897e0f0c5248190b17036339c4be62a0c0dd261ca702a77', status='completed')]
r = chat()
r.output
[ResponseOutputMessage(id='msg_6897e0f5a8b48190b648cb12f43898240c0dd261ca702a77', content=[ResponseOutputText(annotations=[], text='The email for customer C2 is jane@example.com.', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')]
chat = Chat(model, tools=tools)
r = chat('Please cancel all orders for customer C1 for me.')
r.output
- Retrieving customer C1
[ResponseReasoningItem(id='rs_6897e0f99ddc8191b8f2fafa74dc06d40913d9b020597909', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"customer_id":"C1"}', call_id='call_LwrqtbAyvJshnZo2a6MIG8GK', name='get_customer_info', type='function_call', id='fc_6897e0fb1f648191b15ad2d73156e6260913d9b020597909', status='completed')]
r = chat()
r.output
- Cancelling order O1
- Cancelling order O2
[ResponseReasoningItem(id='rs_6897e0fd41e0819189fc599d98d476280913d9b020597909', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"order_id":"O1"}', call_id='call_q8Xfu230VPo7EtebReDFkXQW', name='cancel_order', type='function_call', id='fc_6897e0fec5fc8191989ee1c6f5456bf90913d9b020597909', status='completed'),
 ResponseFunctionToolCall(arguments='{"order_id":"O2"}', call_id='call_eyVUvcqHfbLPdRluohRibIC1', name='cancel_order', type='function_call', id='fc_6897e0fee2c48191be1350e29034816a0913d9b020597909', status='completed')]

toolloop implementation


source

Chat.toolloop

 Chat.toolloop (pr, max_steps=10, cont_func:<built-
                infunctioncallable>=<function noop>, final_prompt='You
                have no more tool uses. Please summarize your findings. If
                you did not complete your goal please tell the user what
                further work needs to be done so they can choose how best
                to proceed.', stream:bool=False, tools=None,
                tool_choice=None,
                background:Optional[bool]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>, conversation:Optional[response_create_par
                ams.Conversation]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>, include:Optional[List[ResponseIncludable]
                ]|Omit=<openai.Omit object at 0x7f5b558ffdc0>,
                input:Union[str,ResponseInputParam]|Omit=<openai.Omit
                object at 0x7f5b558ffdc0>,
                instructions:Optional[str]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>,
                max_output_tokens:Optional[int]|Omit=<openai.Omit object
                at 0x7f5b558ffdc0>,
                max_tool_calls:Optional[int]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>,
                metadata:Optional[Metadata]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>, model:ResponsesModel|Omit=<openai.Omit
                object at 0x7f5b558ffdc0>,
                parallel_tool_calls:Optional[bool]|Omit=<openai.Omit
                object at 0x7f5b558ffdc0>,
                previous_response_id:Optional[str]|Omit=<openai.Omit
                object at 0x7f5b558ffdc0>,
                prompt:Optional[ResponsePromptParam]|Omit=<openai.Omit
                object at 0x7f5b558ffdc0>,
                prompt_cache_key:str|Omit=<openai.Omit object at
                0x7f5b558ffdc0>,
                prompt_cache_retention:"Optional[Literal['in-
                memory','24h']]|Omit"=<openai.Omit object at
                0x7f5b558ffdc0>,
                reasoning:Optional[Reasoning]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>, safety_identifier:str|Omit=<openai.Omit
                object at 0x7f5b558ffdc0>, service_tier:"Optional[Literal[
                'auto','default','flex','scale','priority']]|Omit"=<openai
                .Omit object at 0x7f5b558ffdc0>,
                store:Optional[bool]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>, stream_options:Optional[response_create_p
                arams.StreamOptions]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>,
                temperature:Optional[float]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>,
                text:ResponseTextConfigParam|Omit=<openai.Omit object at
                0x7f5b558ffdc0>,
                top_logprobs:Optional[int]|Omit=<openai.Omit object at
                0x7f5b558ffdc0>, top_p:Optional[float]|Omit=<openai.Omit
                object at 0x7f5b558ffdc0>, truncation:"Optional[Literal['a
                uto','disabled']]|Omit"=<openai.Omit object at
                0x7f5b558ffdc0>, user:str|Omit=<openai.Omit object at
                0x7f5b558ffdc0>, extra_headers:Headers|None=None,
                extra_query:Query|None=None, extra_body:Body|None=None,
                timeout:float|httpx.Timeout|None|NotGiven=NOT_GIVEN)

Add prompt pr to dialog and get a response from Claude, automatically following up with tool_use messages

Type Default Details
pr Prompt to pass to Claude
max_steps int 10 Maximum number of tool requests to loop through
cont_func callable noop Function that stops loop if returns False
final_prompt str You have no more tool uses. Please summarize your findings. If you did not complete your goal please tell the user what further work needs to be done so they can choose how best to proceed. Prompt to add if last message is a tool call
stream bool False Stream response?
tools NoneType None Tools to use
tool_choice NoneType None Required tools to use
background Optional[bool] | Omit <openai.Omit object at 0x7f5b558ffdc0>
conversation Optional[response_create_params.Conversation] | Omit <openai.Omit object at 0x7f5b558ffdc0>
include Optional[List[ResponseIncludable]] | Omit <openai.Omit object at 0x7f5b558ffdc0>
input Union[str, ResponseInputParam] | Omit <openai.Omit object at 0x7f5b558ffdc0>
instructions Optional[str] | Omit <openai.Omit object at 0x7f5b558ffdc0>
max_output_tokens Optional[int] | Omit <openai.Omit object at 0x7f5b558ffdc0>
max_tool_calls Optional[int] | Omit <openai.Omit object at 0x7f5b558ffdc0>
metadata Optional[Metadata] | Omit <openai.Omit object at 0x7f5b558ffdc0>
model ResponsesModel | Omit <openai.Omit object at 0x7f5b558ffdc0>
parallel_tool_calls Optional[bool] | Omit <openai.Omit object at 0x7f5b558ffdc0>
previous_response_id Optional[str] | Omit <openai.Omit object at 0x7f5b558ffdc0>
prompt Optional[ResponsePromptParam] | Omit <openai.Omit object at 0x7f5b558ffdc0>
prompt_cache_key str | Omit <openai.Omit object at 0x7f5b558ffdc0>
prompt_cache_retention Optional[Literal[‘in-memory’, ‘24h’]] | Omit <openai.Omit object at 0x7f5b558ffdc0>
reasoning Optional[Reasoning] | Omit <openai.Omit object at 0x7f5b558ffdc0>
safety_identifier str | Omit <openai.Omit object at 0x7f5b558ffdc0>
service_tier Optional[Literal[‘auto’, ‘default’, ‘flex’, ‘scale’, ‘priority’]] | Omit <openai.Omit object at 0x7f5b558ffdc0>
store Optional[bool] | Omit <openai.Omit object at 0x7f5b558ffdc0>
stream_options Optional[response_create_params.StreamOptions] | Omit <openai.Omit object at 0x7f5b558ffdc0>
temperature Optional[float] | Omit <openai.Omit object at 0x7f5b558ffdc0>
text ResponseTextConfigParam | Omit <openai.Omit object at 0x7f5b558ffdc0>
top_logprobs Optional[int] | Omit <openai.Omit object at 0x7f5b558ffdc0>
top_p Optional[float] | Omit <openai.Omit object at 0x7f5b558ffdc0>
truncation Optional[Literal[‘auto’, ‘disabled’]] | Omit <openai.Omit object at 0x7f5b558ffdc0>
user str | Omit <openai.Omit object at 0x7f5b558ffdc0>
extra_headers Optional None Use the following arguments if you need to pass additional parameters to the API that aren’t available via kwargs.
The extra values given here take precedence over values defined on the client or passed to this method.
extra_query Query | None None
extra_body Body | None None
timeout float | httpx.Timeout | None | NotGiven NOT_GIVEN
Exported source
_final_prompt = "You have no more tool uses. Please summarize your findings. If you did not complete your goal please tell the user what further work needs to be done so they can choose how best to proceed."
Exported source
@patch
@delegates(Chat.__call__)
def toolloop(self:Chat,
             pr, # Prompt to pass to Claude
             max_steps=10, # Maximum number of tool requests to loop through
             cont_func:callable=noop, # Function that stops loop if returns False
             final_prompt=_final_prompt, # Prompt to add if last message is a tool call
             **kwargs):
    "Add prompt `pr` to dialog and get a response from Claude, automatically following up with `tool_use` messages"
    @save_iter
    def _f(o):
        init_n = len(self.h)
        r = self(pr, **kwargs)
        yield r
        if len(self.last)>1: yield from self.last[1:]
        for i in range(max_steps-1):
            x = self.h[-1]
            if not (isinstance(x, dict) and x['type']=='function_call_output'): break
            r = self(final_prompt if i==max_steps-2 else None, **kwargs)
            yield r
            if len(self.last)>1: yield from self.last[1:]
            if not cont_func(*self.h[-3:]): break
        o.value = self.h[init_n+1:]
    return _f()

Test Customer Dataset

def show(x):
    if getattr(x, 'output_text', None): r = x
    else: r = getattr(x,'output',x)
    display(r)
chat = Chat(model, tools=tools)
pr = 'Can you tell me the email address for customer C1?'
r = chat.toolloop(pr)
res = list(r)
for o in r: show(o)
- Retrieving customer C1

The email for customer C1 is john@example.com. Need anything else about this customer?

  • id: resp_6897e128b4448193bf249ab92f1de8780e7bd7a6ca08f04c
  • created_at: 1754784041.0
  • error: None
  • incomplete_details: None
  • instructions: None
  • metadata: {}
  • model: gpt-5-mini-2025-08-07
  • object: response
  • output: [ResponseReasoningItem(id=‘rs_6897e12998dc8193b861f1ead58b702e0e7bd7a6ca08f04c’, summary=[], type=‘reasoning’, content=None, encrypted_content=None, status=None), ResponseOutputMessage(id=‘msg_6897e12a73f4819387e6321b9d3acbd90e7bd7a6ca08f04c’, content=[ResponseOutputText(annotations=[], text=‘The email for customer C1 is john@example.com. Need anything else about this customer?’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
  • parallel_tool_calls: True
  • temperature: 1.0
  • tool_choice: auto
  • tools: [FunctionTool(name=‘get_customer_info’, parameters={‘type’: ‘object’, ‘properties’: {‘customer_id’: {‘type’: ‘string’, ‘description’: ‘ID of the customer’}}, ‘required’: [‘customer_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=“Retrieves a customer’s information and their orders based on the customer ID”), FunctionTool(name=‘get_order_details’, parameters={‘type’: ‘object’, ‘properties’: {‘order_id’: {‘type’: ‘string’, ‘description’: ‘ID of the order’}}, ‘required’: [‘order_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Retrieves the details of a specific order based on the order ID’), FunctionTool(name=‘cancel_order’, parameters={‘type’: ‘object’, ‘properties’: {‘order_id’: {‘type’: ‘string’, ‘description’: ‘ID of the order to cancel’}}, ‘required’: [‘order_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Cancels an order based on the provided order ID:- type: boolean’)]
  • top_p: 1.0
  • background: False
  • max_output_tokens: 4096
  • max_tool_calls: None
  • previous_response_id: None
  • prompt: None
  • prompt_cache_key: None
  • reasoning: Reasoning(effort=‘medium’, 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=327, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=24, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=351)
  • user: None
  • store: True
ResponseOutputMessage(id='msg_6897e12a73f4819387e6321b9d3acbd90e7bd7a6ca08f04c', content=[ResponseOutputText(annotations=[], text='The email for customer C1 is john@example.com. Need anything else about this customer?', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')

source

loop_outputs

 loop_outputs (res)
Exported source
def loop_outputs(res):
    return [dict(p) for o in res for p in ([o] if isinstance(o,dict) else getattr(o,'output',[]))]
cl = loop_outputs(res)
cl
[{'id': 'rs_6897e1248f908193a6cf91c7b9d24f180e7bd7a6ca08f04c',
  'summary': [],
  'type': 'reasoning',
  'content': None,
  'encrypted_content': None,
  'status': None},
 {'arguments': '{"customer_id":"C1"}',
  'call_id': 'call_iSsBtRfj1j7BrbBfilN81sWt',
  'name': 'get_customer_info',
  'type': 'function_call',
  'id': 'fc_6897e12505408193898876b3915318c40e7bd7a6ca08f04c',
  'status': 'completed'},
 {'type': 'function_call_output',
  'call_id': 'call_iSsBtRfj1j7BrbBfilN81sWt',
  'output': "{'name': 'John Doe', 'email': 'john@example.com', 'phone': '123-456-7890', 'orders': [{'id': 'O1', 'product': 'Widget A', 'quantity': 2, 'price': 19.99, 'status': 'Cancelled'}, {'id': 'O2', 'product': 'Gadget B', 'quantity': 1, 'price': 49.99, 'status': 'Cancelled'}]}"},
 {'id': 'rs_6897e1268cb48193aeec6139bf6891d20e7bd7a6ca08f04c',
  'summary': [],
  'type': 'reasoning',
  'content': None,
  'encrypted_content': None,
  'status': None},
 {'id': 'msg_6897e1275b4c8193b2b2e8505b757b470e7bd7a6ca08f04c',
  'content': [ResponseOutputText(annotations=[], text='The email address for customer C1 is john@example.com. Would you like any other details (phone number, orders, etc.)?', type='output_text', logprobs=[])],
  'role': 'assistant',
  'status': 'completed',
  'type': 'message'}]
def disp_tc(x):
    if x['type']=='function_call': return f"- `{x['name']}({x['arguments']})`\n"
    elif x['type']=='function_call_output': return f"  - `{x['output']}`\n\n"
    else: return ''.join(o.text for o in x['content'])
# Markdown(''.join(map(disp_tc, cl)))
pprint(r.value)
[ResponseReasoningItem(id='rs_6897e12998dc8193b861f1ead58b702e0e7bd7a6ca08f04c', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseOutputMessage(id='msg_6897e12a73f4819387e6321b9d3acbd90e7bd7a6ca08f04c', content=[ResponseOutputText(annotations=[], text='The email for customer C1 is john@example.com. Need anything else about this customer?', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')]
orders, customers = _get_orders_customers()
chat = Chat(model, tools=tools)
r = chat.toolloop('What is the status of order O2?')
for o in r: display(getattr(o,'output',o))
- Retrieving order O2
[ResponseReasoningItem(id='rs_6897e152296c81938b18183a7b3a3f2b070371bcef68a1b8', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"order_id":"O2"}', call_id='call_nO3ZOyxkiOtKQTY6UMwDsp9d', name='get_order_details', type='function_call', id='fc_6897e152a24881938ecb7209c4408013070371bcef68a1b8', status='completed')]
ResponseFunctionToolCall(arguments='{"order_id":"O2"}', call_id='call_nO3ZOyxkiOtKQTY6UMwDsp9d', name='get_order_details', type='function_call', id='fc_6897e152a24881938ecb7209c4408013070371bcef68a1b8', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_nO3ZOyxkiOtKQTY6UMwDsp9d',
 'output': "{'id': 'O2', 'product': 'Gadget B', 'quantity': 1, 'price': 49.99, 'status': 'Processing'}"}
[ResponseOutputMessage(id='msg_6897e155231c8193946aeae2f3c7bf85070371bcef68a1b8', content=[ResponseOutputText(annotations=[], text='Order O2 (Gadget B, qty 1) is currently: Processing.', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')]
r = chat.toolloop('Please cancel all orders for customer C1 for me.')
res = list(r)
for o in res: display(getattr(o,'output',o))
- Retrieving customer C1
- Cancelling order O1
- Cancelling order O2
[ResponseReasoningItem(id='rs_6897e15728f4819396207358d0b5ff31070371bcef68a1b8', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"customer_id":"C1"}', call_id='call_pfwlNfsFTJxcwhO5MiDnKFCc', name='get_customer_info', type='function_call', id='fc_6897e15a33c481938b60a80c6c9c6e75070371bcef68a1b8', status='completed')]
ResponseFunctionToolCall(arguments='{"customer_id":"C1"}', call_id='call_pfwlNfsFTJxcwhO5MiDnKFCc', name='get_customer_info', type='function_call', id='fc_6897e15a33c481938b60a80c6c9c6e75070371bcef68a1b8', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_pfwlNfsFTJxcwhO5MiDnKFCc',
 'output': "{'name': 'John Doe', 'email': 'john@example.com', 'phone': '123-456-7890', 'orders': [{'id': 'O1', 'product': 'Widget A', 'quantity': 2, 'price': 19.99, 'status': 'Shipped'}, {'id': 'O2', 'product': 'Gadget B', 'quantity': 1, 'price': 49.99, 'status': 'Processing'}]}"}
[ResponseReasoningItem(id='rs_6897e15c06048193a117eba237950a60070371bcef68a1b8', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"order_id":"O1"}', call_id='call_pGZ7LfguiTY7pTpE8IPPZDW3', name='cancel_order', type='function_call', id='fc_6897e15e276c8193a8bb5668615cb96d070371bcef68a1b8', status='completed'),
 ResponseFunctionToolCall(arguments='{"order_id":"O2"}', call_id='call_TqGkkG2TRTXlXvoiBTgwJlZx', name='cancel_order', type='function_call', id='fc_6897e15e5ef88193b8678bd20f6d0104070371bcef68a1b8', status='completed')]
ResponseFunctionToolCall(arguments='{"order_id":"O1"}', call_id='call_pGZ7LfguiTY7pTpE8IPPZDW3', name='cancel_order', type='function_call', id='fc_6897e15e276c8193a8bb5668615cb96d070371bcef68a1b8', status='completed')
ResponseFunctionToolCall(arguments='{"order_id":"O2"}', call_id='call_TqGkkG2TRTXlXvoiBTgwJlZx', name='cancel_order', type='function_call', id='fc_6897e15e5ef88193b8678bd20f6d0104070371bcef68a1b8', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_pGZ7LfguiTY7pTpE8IPPZDW3',
 'output': 'True'}
{'type': 'function_call_output',
 'call_id': 'call_TqGkkG2TRTXlXvoiBTgwJlZx',
 'output': 'True'}
[ResponseReasoningItem(id='rs_6897e15fd9388193acca7aca2463bb9d070371bcef68a1b8', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseOutputMessage(id='msg_6897e16302e48193ad31367b6139e859070371bcef68a1b8', content=[ResponseOutputText(annotations=[], text='I cancelled all orders for customer C1 (John Doe, john@example.com).\n\nResults:\n- Order O1 (Widget A, qty 2): Cancelled successfully.\n- Order O2 (Gadget B, qty 1): Cancelled successfully.\n\nWould you like me to start refunds, send a cancellation confirmation email, or do anything else for John Doe?', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')]
ResponseOutputMessage(id='msg_6897e16302e48193ad31367b6139e859070371bcef68a1b8', content=[ResponseOutputText(annotations=[], text='I cancelled all orders for customer C1 (John Doe, john@example.com).\n\nResults:\n- Order O1 (Widget A, qty 2): Cancelled successfully.\n- Order O2 (Gadget B, qty 1): Cancelled successfully.\n\nWould you like me to start refunds, send a cancellation confirmation email, or do anything else for John Doe?', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')
# cl = loop_outputs(res)
# Markdown('\n'.join(map(disp_tc, cl)))
for o in chat.toolloop('What is the status of order O2?'): display(o)
- Retrieving order O2
  • id: resp_6897e16824308193863428bf4240a87c070371bcef68a1b8
  • created_at: 1754784104.0
  • error: None
  • incomplete_details: None
  • instructions: None
  • metadata: {}
  • model: gpt-5-mini-2025-08-07
  • object: response
  • output: [ResponseReasoningItem(id=‘rs_6897e168e56c8193a92dbe9d9ac28d8c070371bcef68a1b8’, summary=[], type=‘reasoning’, content=None, encrypted_content=None, status=None), ResponseFunctionToolCall(arguments=‘{“order_id”:“O2”}’, call_id=‘call_s79z5383Uxatc6sNpm33VEl1’, name=‘get_order_details’, type=‘function_call’, id=‘fc_6897e16a30708193a4f46fa44c3bb203070371bcef68a1b8’, status=‘completed’)]
  • parallel_tool_calls: True
  • temperature: 1.0
  • tool_choice: auto
  • tools: [FunctionTool(name=‘get_customer_info’, parameters={‘type’: ‘object’, ‘properties’: {‘customer_id’: {‘type’: ‘string’, ‘description’: ‘ID of the customer’}}, ‘required’: [‘customer_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=“Retrieves a customer’s information and their orders based on the customer ID”), FunctionTool(name=‘get_order_details’, parameters={‘type’: ‘object’, ‘properties’: {‘order_id’: {‘type’: ‘string’, ‘description’: ‘ID of the order’}}, ‘required’: [‘order_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Retrieves the details of a specific order based on the order ID’), FunctionTool(name=‘cancel_order’, parameters={‘type’: ‘object’, ‘properties’: {‘order_id’: {‘type’: ‘string’, ‘description’: ‘ID of the order to cancel’}}, ‘required’: [‘order_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Cancels an order based on the provided order ID:- type: boolean’)]
  • top_p: 1.0
  • background: False
  • max_output_tokens: 4096
  • max_tool_calls: None
  • previous_response_id: None
  • prompt: None
  • prompt_cache_key: None
  • reasoning: Reasoning(effort=‘medium’, 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=532, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=87, output_tokens_details=OutputTokensDetails(reasoning_tokens=64), total_tokens=619)
  • user: None
  • store: True
ResponseFunctionToolCall(arguments='{"order_id":"O2"}', call_id='call_s79z5383Uxatc6sNpm33VEl1', name='get_order_details', type='function_call', id='fc_6897e16a30708193a4f46fa44c3bb203070371bcef68a1b8', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_s79z5383Uxatc6sNpm33VEl1',
 'output': "{'id': 'O2', 'product': 'Gadget B', 'quantity': 1, 'price': 49.99, 'status': 'Cancelled'}"}

Order O2 (Gadget B, qty 1) is currently: Cancelled.

  • id: resp_6897e16b5fa88193b00c69f98e9dd053070371bcef68a1b8
  • created_at: 1754784107.0
  • error: None
  • incomplete_details: None
  • instructions: None
  • metadata: {}
  • model: gpt-5-mini-2025-08-07
  • object: response
  • output: [ResponseOutputMessage(id=‘msg_6897e16be6248193aa6c8c7968793ee4070371bcef68a1b8’, content=[ResponseOutputText(annotations=[], text=‘Order O2 (Gadget B, qty 1) is currently: Cancelled.’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
  • parallel_tool_calls: True
  • temperature: 1.0
  • tool_choice: auto
  • tools: [FunctionTool(name=‘get_customer_info’, parameters={‘type’: ‘object’, ‘properties’: {‘customer_id’: {‘type’: ‘string’, ‘description’: ‘ID of the customer’}}, ‘required’: [‘customer_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=“Retrieves a customer’s information and their orders based on the customer ID”), FunctionTool(name=‘get_order_details’, parameters={‘type’: ‘object’, ‘properties’: {‘order_id’: {‘type’: ‘string’, ‘description’: ‘ID of the order’}}, ‘required’: [‘order_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Retrieves the details of a specific order based on the order ID’), FunctionTool(name=‘cancel_order’, parameters={‘type’: ‘object’, ‘properties’: {‘order_id’: {‘type’: ‘string’, ‘description’: ‘ID of the order to cancel’}}, ‘required’: [‘order_id’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Cancels an order based on the provided order ID:- type: boolean’)]
  • top_p: 1.0
  • background: False
  • max_output_tokens: 4096
  • max_tool_calls: None
  • previous_response_id: None
  • prompt: None
  • prompt_cache_key: None
  • reasoning: Reasoning(effort=‘medium’, 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=685, input_tokens_details=InputTokensDetails(cached_tokens=535), output_tokens=22, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=707)
  • user: None
  • store: True

Test Math Example

def add(x: int, y: int) -> int:
    "adds x and y."
    return x + y

def mul(x: int, y: int) -> int:
    "multiplies x and y."
    return x * y
chat = Chat(model, tools=[add, mul], **chatkw)
pr = 'Can you add 1258585825128 to 34959234595, multiply by 93, and then add (-12439149)?'
r = chat.toolloop(pr)
for o in r: show(o)
[ResponseReasoningItem(id='rs_6897e18c5388819190a54e0ce1441bdb015feb53bd0dd55d', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"x":1258585825128,"y":34959234595}', call_id='call_ueQGOLD99pG6r2sGdgsVKlw6', name='add', type='function_call', id='fc_6897e18e8f88819191645a755cda603e015feb53bd0dd55d', status='completed')]
ResponseFunctionToolCall(arguments='{"x":1258585825128,"y":34959234595}', call_id='call_ueQGOLD99pG6r2sGdgsVKlw6', name='add', type='function_call', id='fc_6897e18e8f88819191645a755cda603e015feb53bd0dd55d', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_ueQGOLD99pG6r2sGdgsVKlw6',
 'output': '1293545059723'}
[ResponseReasoningItem(id='rs_6897e19014fc81919b186f766cb9b0b7015feb53bd0dd55d', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"x":1293545059723,"y":93}', call_id='call_3bMfuqmgxtFi73cSXwFpypFP', name='mul', type='function_call', id='fc_6897e190e7b88191a01ac2531eb232a4015feb53bd0dd55d', status='completed')]
ResponseFunctionToolCall(arguments='{"x":1293545059723,"y":93}', call_id='call_3bMfuqmgxtFi73cSXwFpypFP', name='mul', type='function_call', id='fc_6897e190e7b88191a01ac2531eb232a4015feb53bd0dd55d', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_3bMfuqmgxtFi73cSXwFpypFP',
 'output': '120299690554239'}
[ResponseReasoningItem(id='rs_6897e192bbbc8191bf48ca20d003dfc1015feb53bd0dd55d', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"x":120299690554239,"y":-12439149}', call_id='call_ildkWrO6EHnC0jtBFXAq93P5', name='add', type='function_call', id='fc_6897e1935f7481918b1d79751e99aede015feb53bd0dd55d', status='completed')]
ResponseFunctionToolCall(arguments='{"x":120299690554239,"y":-12439149}', call_id='call_ildkWrO6EHnC0jtBFXAq93P5', name='add', type='function_call', id='fc_6897e1935f7481918b1d79751e99aede015feb53bd0dd55d', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_ildkWrO6EHnC0jtBFXAq93P5',
 'output': '120299678115090'}

120,299,678,115,090

  • id: resp_6897e19438d081919b9c36459cc8cd50015feb53bd0dd55d
  • created_at: 1754784148.0
  • error: None
  • incomplete_details: None
  • instructions: None
  • metadata: {}
  • model: gpt-5-mini-2025-08-07
  • object: response
  • output: [ResponseReasoningItem(id=‘rs_6897e194c62481918b6ac433e9648d01015feb53bd0dd55d’, summary=[], type=‘reasoning’, content=None, encrypted_content=None, status=None), ResponseOutputMessage(id=‘msg_6897e195ccbc8191806147fc7db8fe8e015feb53bd0dd55d’, content=[ResponseOutputText(annotations=[], text=‘120,299,678,115,090’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
  • parallel_tool_calls: True
  • temperature: 1.0
  • tool_choice: auto
  • tools: [FunctionTool(name=‘add’, parameters={‘type’: ‘object’, ‘properties’: {‘x’: {‘type’: ‘integer’, ‘description’: ’‘}, ’y’: {‘type’: ‘integer’, ‘description’: ’‘}}, ’required’: [‘x’, ‘y’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘adds x and y.:- type: integer’), FunctionTool(name=‘mul’, parameters={‘type’: ‘object’, ‘properties’: {‘x’: {‘type’: ‘integer’, ‘description’: ’‘}, ’y’: {‘type’: ‘integer’, ‘description’: ’‘}}, ’required’: [‘x’, ‘y’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘multiplies x and y.:- type: integer’)]
  • top_p: 1.0
  • background: False
  • max_output_tokens: 4096
  • max_tool_calls: None
  • previous_response_id: None
  • prompt: None
  • prompt_cache_key: None
  • reasoning: Reasoning(effort=‘medium’, 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=496, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=79, output_tokens_details=OutputTokensDetails(reasoning_tokens=64), total_tokens=575)
  • user: None
  • store: True
ResponseOutputMessage(id='msg_6897e195ccbc8191806147fc7db8fe8e015feb53bd0dd55d', content=[ResponseOutputText(annotations=[], text='120,299,678,115,090', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')
(1258585825128 + 34959234595) * 93 - 12439149
120299678115090
chat = Chat(model, tools=[add, mul], **chatkw)
r = chat.toolloop(pr, stream=True)
for o in r:
    if isinstance(o, dict): print('- ', o)
    else:
        for p in o: print(p, end='')
        if hasattr(o, 'value'): show(o.value)
[ResponseReasoningItem(id='rs_6897e1c5d79081a3ae88e03578f661a90c8460ccb833b112', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"x":1258585825128,"y":34959234595}', call_id='call_SdIaAHdmOEYB4tAU7E6vh3p4', name='add', type='function_call', id='fc_6897e1c6170081a3b97d0966c86325730c8460ccb833b112', status='completed')]
('arguments', '{"x":1258585825128,"y":34959234595}')('call_id', 'call_SdIaAHdmOEYB4tAU7E6vh3p4')('name', 'add')('type', 'function_call')('id', 'fc_6897e1c6170081a3b97d0966c86325730c8460ccb833b112')('status', 'completed')-  {'type': 'function_call_output', 'call_id': 'call_SdIaAHdmOEYB4tAU7E6vh3p4', 'output': '1293545059723'}
[ResponseReasoningItem(id='rs_6897e1c72d0481a38ffd9daf156d758e0c8460ccb833b112', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"x":1293545059723,"y":93}', call_id='call_kjtm1Q8cccNUleOw1msfEgmK', name='mul', type='function_call', id='fc_6897e1c77bac81a3baf3e35799923fc80c8460ccb833b112', status='completed')]
('arguments', '{"x":1293545059723,"y":93}')('call_id', 'call_kjtm1Q8cccNUleOw1msfEgmK')('name', 'mul')('type', 'function_call')('id', 'fc_6897e1c77bac81a3baf3e35799923fc80c8460ccb833b112')('status', 'completed')-  {'type': 'function_call_output', 'call_id': 'call_kjtm1Q8cccNUleOw1msfEgmK', 'output': '120299690554239'}
[ResponseReasoningItem(id='rs_6897e1c87c7081a38c9faf505ed5e29a0c8460ccb833b112', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"x":120299690554239,"y":-12439149}', call_id='call_cuKZQ6Oa3Lc2P75mDEx0jugE', name='add', type='function_call', id='fc_6897e1c8c5e481a3a9bb8a58bda392740c8460ccb833b112', status='completed')]
('arguments', '{"x":120299690554239,"y":-12439149}')('call_id', 'call_cuKZQ6Oa3Lc2P75mDEx0jugE')('name', 'add')('type', 'function_call')('id', 'fc_6897e1c8c5e481a3a9bb8a58bda392740c8460ccb833b112')('status', 'completed')-  {'type': 'function_call_output', 'call_id': 'call_cuKZQ6Oa3Lc2P75mDEx0jugE', 'output': '120299678115090'}
120,299,678,115,090

120,299,678,115,090

  • id: resp_6897e1c9821081a3a1f3648c7b2bf4320c8460ccb833b112
  • created_at: 1754784201.0
  • error: None
  • incomplete_details: None
  • instructions: None
  • metadata: {}
  • model: gpt-5-mini-2025-08-07
  • object: response
  • output: [ResponseOutputMessage(id=‘msg_6897e1ca77d081a3b2636d387eea6f370c8460ccb833b112’, content=[ResponseOutputText(annotations=[], text=‘120,299,678,115,090’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
  • parallel_tool_calls: True
  • temperature: 1.0
  • tool_choice: auto
  • tools: [FunctionTool(name=‘add’, parameters={‘type’: ‘object’, ‘properties’: {‘x’: {‘type’: ‘integer’, ‘description’: ’‘}, ’y’: {‘type’: ‘integer’, ‘description’: ’‘}}, ’required’: [‘x’, ‘y’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘adds x and y.:- type: integer’), FunctionTool(name=‘mul’, parameters={‘type’: ‘object’, ‘properties’: {‘x’: {‘type’: ‘integer’, ‘description’: ’‘}, ’y’: {‘type’: ‘integer’, ‘description’: ’‘}}, ’required’: [‘x’, ‘y’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘multiplies x and y.:- type: integer’)]
  • top_p: 1.0
  • background: False
  • max_output_tokens: 4096
  • max_tool_calls: None
  • previous_response_id: None
  • prompt: None
  • prompt_cache_key: None
  • reasoning: Reasoning(effort=‘minimal’, generate_summary=None, summary=None)
  • safety_identifier: None
  • service_tier: default
  • status: completed
  • text: ResponseTextConfig(format=ResponseFormatText(type=‘text’), verbosity=‘low’)
  • top_logprobs: 0
  • truncation: disabled
  • usage: ResponseUsage(input_tokens=278, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=13, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=291)
  • user: None
  • store: True

Error Conditions: Out of Iterations, Exception During Tool Invocation

def mydiv(a:float, b:float):
    "Divide two numbers"
    return a / b
chat = Chat(model, tools=[mydiv], **chatkw)
r = chat.toolloop('Please calculate this sequence using your tools: 43/23454; 652/previous result; 6843/previous result; 321/previous result', max_steps=2)
for o in r: show(o)
[ResponseReasoningItem(id='rs_6897e1e3f7d08190819e22b16bd62a6b047a40cd4df49f91', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"a":43,"b":23454}', call_id='call_1EchGcZOxUCwzWFmPG6ZbagQ', name='mydiv', type='function_call', id='fc_6897e1e44ac08190819052abd2ee034a047a40cd4df49f91', status='completed'),
 ResponseFunctionToolCall(arguments='{"a":652,"b":0}', call_id='call_VzY6kLA8EYBQWLQyM1P7Uu39', name='mydiv', type='function_call', id='fc_6897e1e4736c819093be6536512c13f8047a40cd4df49f91', status='completed'),
 ResponseFunctionToolCall(arguments='{"a":6843,"b":0}', call_id='call_Ifuh3smqGl5gBCHuqPOyAwWC', name='mydiv', type='function_call', id='fc_6897e1e499788190adcc563eb10050d6047a40cd4df49f91', status='completed'),
 ResponseFunctionToolCall(arguments='{"a":321,"b":0}', call_id='call_Ib36ay9PqhAms1U200xMh3AB', name='mydiv', type='function_call', id='fc_6897e1e4c6f48190b4715a9fcfc33d4e047a40cd4df49f91', status='completed')]
ResponseFunctionToolCall(arguments='{"a":43,"b":23454}', call_id='call_1EchGcZOxUCwzWFmPG6ZbagQ', name='mydiv', type='function_call', id='fc_6897e1e44ac08190819052abd2ee034a047a40cd4df49f91', status='completed')
ResponseFunctionToolCall(arguments='{"a":652,"b":0}', call_id='call_VzY6kLA8EYBQWLQyM1P7Uu39', name='mydiv', type='function_call', id='fc_6897e1e4736c819093be6536512c13f8047a40cd4df49f91', status='completed')
ResponseFunctionToolCall(arguments='{"a":6843,"b":0}', call_id='call_Ifuh3smqGl5gBCHuqPOyAwWC', name='mydiv', type='function_call', id='fc_6897e1e499788190adcc563eb10050d6047a40cd4df49f91', status='completed')
ResponseFunctionToolCall(arguments='{"a":321,"b":0}', call_id='call_Ib36ay9PqhAms1U200xMh3AB', name='mydiv', type='function_call', id='fc_6897e1e4c6f48190b4715a9fcfc33d4e047a40cd4df49f91', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_1EchGcZOxUCwzWFmPG6ZbagQ',
 'output': '0.001833375969983798'}
{'type': 'function_call_output',
 'call_id': 'call_VzY6kLA8EYBQWLQyM1P7Uu39',
 'output': 'Traceback (most recent call last):\n  File "/Users/jhoward/aai-ws/toolslm/toolslm/funccall.py", line 203, in call_func\n    try: return func(**fc_inputs)\n                ^^^^^^^^^^^^^^^^^\n  File "/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/ipykernel_23490/246724137.py", line 3, in mydiv\n    return a / b\n           ~~^~~\nZeroDivisionError: division by zero\n'}
{'type': 'function_call_output',
 'call_id': 'call_Ifuh3smqGl5gBCHuqPOyAwWC',
 'output': 'Traceback (most recent call last):\n  File "/Users/jhoward/aai-ws/toolslm/toolslm/funccall.py", line 203, in call_func\n    try: return func(**fc_inputs)\n                ^^^^^^^^^^^^^^^^^\n  File "/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/ipykernel_23490/246724137.py", line 3, in mydiv\n    return a / b\n           ~~^~~\nZeroDivisionError: division by zero\n'}
{'type': 'function_call_output',
 'call_id': 'call_Ib36ay9PqhAms1U200xMh3AB',
 'output': 'Traceback (most recent call last):\n  File "/Users/jhoward/aai-ws/toolslm/toolslm/funccall.py", line 203, in call_func\n    try: return func(**fc_inputs)\n                ^^^^^^^^^^^^^^^^^\n  File "/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/ipykernel_23490/246724137.py", line 3, in mydiv\n    return a / b\n           ~~^~~\nZeroDivisionError: division by zero\n'}

I successfully computed the first division: - 43 / 23454 = 0.001833375969983798

I was then instructed to divide 652 by the “previous result” and continue chaining divisions, but my subsequent tool calls failed because I attempted to divide by zero (the tool calls were given invalid b=0), so I could not complete the remaining steps.

To finish the sequence you want, the next steps are: 1) Compute 652 / 0.001833375969983798 = 355,554.879… (approx) 2) Compute 6843 / (result of step 1) = 0.019247… (approx) 3) Compute 321 / (result of step 2) = 16,683.6… (approx)

If you want, I can now compute those three remaining divisions directly (no tools required) and give exact or rounded results. Which would you prefer?

  • id: resp_6897e1e562908190a3ecc40a07d57645047a40cd4df49f91
  • created_at: 1754784229.0
  • error: None
  • incomplete_details: None
  • instructions: None
  • metadata: {}
  • model: gpt-5-mini-2025-08-07
  • object: response
  • output: [ResponseReasoningItem(id=‘rs_6897e1e5fafc81909e1cbc28898ba791047a40cd4df49f91’, summary=[], type=‘reasoning’, content=None, encrypted_content=None, status=None), ResponseOutputMessage(id=‘msg_6897e1e61cdc81909f47e73a5337ebec047a40cd4df49f91’, content=[ResponseOutputText(annotations=[], text=‘I successfully computed the first division:- 43 / 23454 = 0.001833375969983798was then instructed to divide 652 by the “previous result” and continue chaining divisions, but my subsequent tool calls failed because I attempted to divide by zero (the tool calls were given invalid b=0), so I could not complete the remaining steps.finish the sequence you want, the next steps are:) Compute 652 / 0.001833375969983798 = 355,554.879… (approx)) Compute 6843 / (result of step 1) = 0.019247… (approx)) Compute 321 / (result of step 2) = 16,683.6… (approx)you want, I can now compute those three remaining divisions directly (no tools required) and give exact or rounded results. Which would you prefer?’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
  • parallel_tool_calls: True
  • temperature: 1.0
  • tool_choice: auto
  • tools: [FunctionTool(name=‘mydiv’, parameters={‘type’: ‘object’, ‘properties’: {‘a’: {‘type’: ‘number’, ‘description’: ’‘}, ’b’: {‘type’: ‘number’, ‘description’: ’‘}}, ’required’: [‘a’, ‘b’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Divide two numbers’)]
  • top_p: 1.0
  • background: False
  • max_output_tokens: 4096
  • max_tool_calls: None
  • previous_response_id: None
  • prompt: None
  • prompt_cache_key: None
  • reasoning: Reasoning(effort=‘minimal’, generate_summary=None, summary=None)
  • safety_identifier: None
  • service_tier: default
  • status: completed
  • text: ResponseTextConfig(format=ResponseFormatText(type=‘text’), verbosity=‘low’)
  • top_logprobs: 0
  • truncation: disabled
  • usage: ResponseUsage(input_tokens=603, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=199, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=802)
  • user: None
  • store: True
ResponseOutputMessage(id='msg_6897e1e61cdc81909f47e73a5337ebec047a40cd4df49f91', content=[ResponseOutputText(annotations=[], text='I successfully computed the first division:\n- 43 / 23454 = 0.001833375969983798\n\nI was then instructed to divide 652 by the "previous result" and continue chaining divisions, but my subsequent tool calls failed because I attempted to divide by zero (the tool calls were given invalid b=0), so I could not complete the remaining steps.\n\nTo finish the sequence you want, the next steps are:\n1) Compute 652 / 0.001833375969983798 = 355,554.879... (approx)\n2) Compute 6843 / (result of step 1) = 0.019247... (approx)\n3) Compute 321 / (result of step 2) = 16,683.6... (approx)\n\nIf you want, I can now compute those three remaining divisions directly (no tools required) and give exact or rounded results. Which would you prefer?', type='output_text', logprobs=[])], role='assistant', status='completed', type='message')

This tests raise_on_err=False change to toolslm.call_func invocation. We should see this return an error as a string instead of crash:

chat = Chat(model, tools=[mydiv], **chatkw)
r = chat.toolloop('Try dividing 1 by 0 and see what the error result is')
for o in r: show(o)
[ResponseReasoningItem(id='rs_6897e1e939d48193a63805a335b1a33304d3e564186ebe0a', summary=[], type='reasoning', content=None, encrypted_content=None, status=None),
 ResponseFunctionToolCall(arguments='{"a":1,"b":0}', call_id='call_dxZkdTpNuZH6tWjycwgHZeo9', name='mydiv', type='function_call', id='fc_6897e1e999a08193b7f7af1bc2c213ec04d3e564186ebe0a', status='completed')]
ResponseFunctionToolCall(arguments='{"a":1,"b":0}', call_id='call_dxZkdTpNuZH6tWjycwgHZeo9', name='mydiv', type='function_call', id='fc_6897e1e999a08193b7f7af1bc2c213ec04d3e564186ebe0a', status='completed')
{'type': 'function_call_output',
 'call_id': 'call_dxZkdTpNuZH6tWjycwgHZeo9',
 'output': 'Traceback (most recent call last):\n  File "/Users/jhoward/aai-ws/toolslm/toolslm/funccall.py", line 203, in call_func\n    try: return func(**fc_inputs)\n                ^^^^^^^^^^^^^^^^^\n  File "/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/ipykernel_23490/246724137.py", line 3, in mydiv\n    return a / b\n           ~~^~~\nZeroDivisionError: division by zero\n'}

The function raised a ZeroDivisionError with message: “division by zero”.

  • id: resp_6897e1eac59c81938b9c646c2a8c34ef04d3e564186ebe0a
  • created_at: 1754784234.0
  • error: None
  • incomplete_details: None
  • instructions: None
  • metadata: {}
  • model: gpt-5-mini-2025-08-07
  • object: response
  • output: [ResponseOutputMessage(id=‘msg_6897e1eb389081939fdcfd37dbf877e404d3e564186ebe0a’, content=[ResponseOutputText(annotations=[], text=‘The function raised a ZeroDivisionError with message: “division by zero”.’, type=‘output_text’, logprobs=[])], role=‘assistant’, status=‘completed’, type=‘message’)]
  • parallel_tool_calls: True
  • temperature: 1.0
  • tool_choice: auto
  • tools: [FunctionTool(name=‘mydiv’, parameters={‘type’: ‘object’, ‘properties’: {‘a’: {‘type’: ‘number’, ‘description’: ’‘}, ’b’: {‘type’: ‘number’, ‘description’: ’‘}}, ’required’: [‘a’, ‘b’], ‘additionalProperties’: False}, strict=True, type=‘function’, description=‘Divide two numbers’)]
  • top_p: 1.0
  • background: False
  • max_output_tokens: 4096
  • max_tool_calls: None
  • previous_response_id: None
  • prompt: None
  • prompt_cache_key: None
  • reasoning: Reasoning(effort=‘minimal’, generate_summary=None, summary=None)
  • safety_identifier: None
  • service_tier: default
  • status: completed
  • text: ResponseTextConfig(format=ResponseFormatText(type=‘text’), verbosity=‘low’)
  • top_logprobs: 0
  • truncation: disabled
  • usage: ResponseUsage(input_tokens=220, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=19, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=239)
  • user: None
  • store: True