Try an interactive version of this dialog: Sign up at solve.it.com, click Upload, and pass this URL.

Code: 16 ()

from lisette import *
from fastcore.utils import *

Note: 4

Test Tools

Code: 99 ()

def add(a:int, b:int) -> int:
    "Add two numbers"
    return a + b

def multiply(x:int, y:int) -> int:
    "Multiply two numbers"
    return x * y

def get_secret() -> str:
    "Get a secret value"
    return "secret123"

Note: 24

1. tc_refs=False (default) - no tool_call_id shown

Code: 54 ()

chat1 = Chat('claude-sonnet-4-5', tools=[add])
chat1("Call add(5,3) and tell me the tool_call_id")

Output: 502

The add function was successfully called with parameters 5 and 3, and it returned the result: 8.

However, I don't have access to the tool_call_id from my perspective. The tool_call_id is typically an internal identifier used by the system to track function calls, but it's not exposed in the function results I receive. If you need the tool_call_id for debugging or logging purposes, you may need to check the system logs or API response on your end where this metadata would be available.

  • id: chatcmpl-c8100e77-525a-4ea7-99eb-d0161ee3d4c7
  • model: claude-sonnet-4-5-20250929
  • finish_reason: stop
  • usage: Usage(completion_tokens=116, prompt_tokens=751, total_tokens=867, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, cache_creation_tokens=0, cache_creation_token_details=CacheCreationTokenDetails(ephemeral_5m_input_tokens=0, ephemeral_1h_input_tokens=0)), cache_creation_input_tokens=0, cache_read_input_tokens=0)

Code: 13 ()

# Should be None
chat1.tc_res

Code: 21 ()

list(L(chat1.hist).attrgot('tool_calls').filter())

Output: 214

[[{'index': 1,
   'function': {'arguments': '{"a": 5, "b": 3}', 'name': 'add'},
   'id': 'toolu_01RrwkJ7ZszFMigNUD38NEVm',
   'type': 'function'}]]

Note: 21

2. tc_refs=True - tool_call_id shown and stored

Code: 60 ()

chat2 = Chat('claude-sonnet-4-5', tools=[add], tc_refs=True)
chat2("Call add(5,3) and tell me the tool_call_id")

Output: 445

The add function was successfully called with parameters 5 and 3, which returned the result of 8.

The tool_call_id is: toolu_012WuF6G8q9WaWgJNER2sT2i

The task has been completed successfully.

  • id: chatcmpl-ee2e761d-e2e5-4b33-88e0-4f73a7b301b0
  • model: claude-sonnet-4-5-20250929
  • finish_reason: stop
  • usage: Usage(completion_tokens=71, prompt_tokens=867, total_tokens=938, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, cache_creation_tokens=0, cache_creation_token_details=CacheCreationTokenDetails(ephemeral_5m_input_tokens=0, ephemeral_1h_input_tokens=0)), cache_creation_input_tokens=0, cache_read_input_tokens=0)

Code: 16 ()

# Should have the result stored
chat2.tc_res

Output: 51

{'toolu_012WuF6G8q9WaWgJNER2sT2i': 8}

Code: 21 ()

list(L(chat2.hist).attrgot('tool_calls').filter())

Output: 219

[[{'index': 1,
   'function': {'arguments': '{"a": 5, "b": 3}', 'name': 'add'},
   'id': 'toolu_012WuF6G8q9WaWgJNER2sT2i',
   'type': 'function'}]]

Note: 27

3. Chained tool calls - AI uses $tool_call_id syntax

Code: 85 ()

chat3 = Chat('claude-sonnet-4-5', tools=[add, multiply], tc_refs=True)
chat3("First call add(2,3), then multiply that result by 4 using the $`tool_call_id` syntax", max_steps=10)

Output: 420

Perfect! The result is:

  • First, 2 + 3 = 5
  • Then, 5 × 4 = 20

The final answer is 20.

  • id: chatcmpl-c0fd8112-6035-4c21-b291-58bc3e0b6ab4
  • model: claude-sonnet-4-5-20250929
  • finish_reason: stop
  • usage: Usage(completion_tokens=45, prompt_tokens=1066, total_tokens=1111, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, cache_creation_tokens=0, cache_creation_token_details=CacheCreationTokenDetails(ephemeral_5m_input_tokens=0, ephemeral_1h_input_tokens=0)), cache_creation_input_tokens=0, cache_read_input_tokens=0)

Code: 15 ()

# Check both results stored
chat3.tc_res

Output: 91

{'toolu_01DuDodRZzrgFYKjGuPZbyqm': 5, 'toolu_01XJTtHS8JMYLkPw2NguGPUy': 20}

Code: 21 ()

list(L(chat3.hist).attrgot('tool_calls').filter())

Output: 465

[[{'index': 1,
   'function': {'arguments': '{"a": 2, "b": 3}', 'name': 'add'},
   'id': 'toolu_01DuDodRZzrgFYKjGuPZbyqm',
   'type': 'function'}],
 [{'index': 1,
   'function': {'arguments': '{"x": "$`toolu_01DuDodRZzrgFYKjGuPZbyqm`", "y": 4}',
    'name': 'multiply'},
   'id': 'toolu_01XJTtHS8JMYLkPw2NguGPUy',
   'type': 'function'}]]

Note: 15

4. ToolResponse with tc_refs=True

Code: 244 ()

import base64
img_data = base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==')
durl = f"data:image/png;base64,{base64.b64encode(img_data).decode()}"

def view_img() -> ToolResponse:
    "View an image"
    return ToolResponse([{'type': 'image_url', 'image_url': {'url': durl}}])

chat4 = Chat('claude-sonnet-4-5', tools=[view_img], tc_refs=True)
chat4("Use view_img and tell me the tool_call_id")

Output: 460

The tool_call_id from the view_img function call is: toolu_01KjWvAFenKroAHYN5hNtQfs

The function successfully executed and returned an image (appears to be a small red square or icon). The tool_call_id is the unique identifier assigned to this specific function invocation, which can be used to reference the results of this call in subsequent operations.

  • id: chatcmpl-397c50ca-f7a9-4fa2-a972-bcce19268009
  • model: claude-sonnet-4-5-20250929
  • finish_reason: stop
  • usage: Usage(completion_tokens=96, prompt_tokens=824, total_tokens=920, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, cache_creation_tokens=0, cache_creation_token_details=CacheCreationTokenDetails(ephemeral_5m_input_tokens=0, ephemeral_1h_input_tokens=0)), cache_creation_input_tokens=0, cache_read_input_tokens=0)

Code: 21 ()

list(L(chat4.hist).attrgot('tool_calls').filter())

Output: 190

[[{'index': 1,
   'function': {'arguments': '{}', 'name': 'view_img'},
   'id': 'toolu_01KjWvAFenKroAHYN5hNtQfs',
   'type': 'function'}]]

Code: 21 ()

# Should store the ToolResponse content (list)
chat4.tc_res

Output: 213

{'toolu_01KjWvAFenKroAHYN5hNtQfs': [{'type': 'image_url',
   'image_url': {'url': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=='}}]}

Note: 15

5. AsyncChat with tc_refs=True

Code: 82 ()

achat = AsyncChat('claude-sonnet-4-5', tools=[add, multiply], tc_refs=True)
await achat("First call add(10,20), then multiply the result by 2 using $`tool_call_id`", max_steps=10)

Output: 375

Perfect! The result of adding 10 and 20 is 30, and multiplying that by 2 gives us 60.

  • id: chatcmpl-575e6be8-09ef-4437-bc66-592943024d97
  • model: claude-sonnet-4-5-20250929
  • finish_reason: stop
  • usage: Usage(completion_tokens=33, prompt_tokens=1064, total_tokens=1097, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, cache_creation_tokens=0, cache_creation_token_details=CacheCreationTokenDetails(ephemeral_5m_input_tokens=0, ephemeral_1h_input_tokens=0)), cache_creation_input_tokens=0, cache_read_input_tokens=0)

Code: 4 ()

achat.tc_res

Output: 90

{'toolu_01BRAkfZCRVgKhEgEnT4HD9x': 30, 'toolu_01YKckNBtvo979GAyPkHZi3H': 60}

Code: 21 ()

list(L(achat.hist).attrgot('tool_calls').filter())

Output: 463

[[{'index': 1,
   'function': {'arguments': '{"a": 10, "b": 20}', 'name': 'add'},
   'id': 'toolu_01BRAkfZCRVgKhEgEnT4HD9x',
   'type': 'function'}],
 [{'index': 1,
   'function': {'arguments': '{"x": "$`toolu_01BRAkfZCRVgKhEgEnT4HD9x`", "y": 2}',
    'name': 'multiply'},
   'id': 'toolu_01YKckNBtvo979GAyPkHZi3H',
   'type': 'function'}]]

Note: 15

6. Missing tool reference - error message

Code: 72 ()

# Unit test for missing reference
from lisette.core import _resolve_tool_refs
tc_res = {'toolu_abc': 'hello'}
_resolve_tool_refs('{"x": "$`toolu_missing`"}', tc_res)

Output: 48

{'x': "Tool result 'toolu_missing' not found!"}

Note: 15

7. Chaining with class type argument

Code: 234 ()

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int

def create_person(name: str, age: int) -> str:
    "Create a person and return their info"
    return f"{name},{age}"

def greet_person(person_info: str) -> str:
    "Greet a person given their info as 'name,age'"
    name, age = person_info.split(',')
    return f"Hello {name}, you are {age} years old!"

chat5 = Chat('claude-sonnet-4-5', tools=[create_person, greet_person], tc_refs=True)
chat5("Create a person named Alice aged 30, then greet them using the result", max_steps=10)

Output: 379

Perfect! I've created a person named Alice aged 30, and then greeted them. The greeting says: "Hello Alice, you are 30 years old!"

  • id: chatcmpl-6abf6acc-268c-47b9-b855-ec01017bd390
  • model: claude-sonnet-4-5-20250929
  • finish_reason: stop
  • usage: Usage(completion_tokens=38, prompt_tokens=1061, total_tokens=1099, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, cache_creation_tokens=0, cache_creation_token_details=CacheCreationTokenDetails(ephemeral_5m_input_tokens=0, ephemeral_1h_input_tokens=0)), cache_creation_input_tokens=0, cache_read_input_tokens=0)

Code: 6 ()

chat5.tc_res

Output: 129

{'toolu_01XBuV1yPGtspwaXP7LsgqCB': 'Alice,30',
 'toolu_01Nx76ibKWpe6nSCFGRjq6A7': 'Hello Alice, you are 30 years old!'}

Code: 21 ()

list(L(chat5.hist).attrgot('tool_calls').filter())

Output: 472

[[{'index': 1,
   'function': {'arguments': '{"name": "Alice", "age": 30}',
    'name': 'create_person'},
   'id': 'toolu_01XBuV1yPGtspwaXP7LsgqCB',
   'type': 'function'}],
 [{'index': 1,
   'function': {'arguments': '{"person_info": "$`toolu_01XBuV1yPGtspwaXP7LsgqCB`"}',
    'name': 'greet_person'},
   'id': 'toolu_01Nx76ibKWpe6nSCFGRjq6A7',
   'type': 'function'}]]

Note: 16

8. Chaining ToolResponse into another tool

Code: 178 ()

def fetch_data() -> ToolResponse:
    "Fetch some structured data"
    return ToolResponse([{'type': 'text', 'text': 'important_data_123'}])

def process_fetched(data: list) -> str:
    "Process the fetched data"
    return f"Processed: {data[0]['text'].upper()}"

chat6 = Chat('claude-sonnet-4-5', tools=[fetch_data, process_fetched], tc_refs=True)
chat6("First fetch_data, then process it using the tool_call_id reference", max_steps=10)

Output: 445

Perfect! I've successfully:

  1. Fetched the data, which returned: important_data_123
  2. Processed the fetched data by referencing the first tool call's ID, which resulted in: Processed: IMPORTANT_DATA_123

The data was transformed to uppercase during processing.

  • id: chatcmpl-55e34c36-2352-47af-b75b-e547a4b55283
  • model: claude-sonnet-4-5-20250929
  • finish_reason: stop
  • usage: Usage(completion_tokens=72, prompt_tokens=971, total_tokens=1043, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, cache_creation_tokens=0, cache_creation_token_details=CacheCreationTokenDetails(ephemeral_5m_input_tokens=0, ephemeral_1h_input_tokens=0)), cache_creation_input_tokens=0, cache_read_input_tokens=0)

Code: 6 ()

chat6.tc_res

Output: 172

{'toolu_012vE7BDk7pcUgu8VB1YVVqr': [{'type': 'text',
   'text': 'important_data_123'}],
 'toolu_015XbXHNm1oW435pbVt5aFV3': 'Processed: IMPORTANT_DATA_123'}

Code: 21 ()

list(L(chat6.hist).attrgot('tool_calls').filter())

Output: 436

[[{'index': 1,
   'function': {'arguments': '{}', 'name': 'fetch_data'},
   'id': 'toolu_012vE7BDk7pcUgu8VB1YVVqr',
   'type': 'function'}],
 [{'index': 1,
   'function': {'arguments': '{"data": "$`toolu_012vE7BDk7pcUgu8VB1YVVqr`"}',
    'name': 'process_fetched'},
   'id': 'toolu_015XbXHNm1oW435pbVt5aFV3',
   'type': 'function'}]]