From 5df1440b5527728cf87e29ecf7f67c52e585b651 Mon Sep 17 00:00:00 2001 From: Erik Gaasedelen Date: Wed, 1 Oct 2025 20:14:24 -0700 Subject: [PATCH 1/3] support multiple tool calls in output --- cachy.jsonl | 45 ++++ lisette/_modidx.py | 3 + lisette/core.py | 43 ++- nbs/00_core.ipynb | 636 ++++++++++++++++++++++++--------------------- 4 files changed, 420 insertions(+), 307 deletions(-) diff --git a/cachy.jsonl b/cachy.jsonl index 7142464..9aac587 100644 --- a/cachy.jsonl +++ b/cachy.jsonl @@ -177,3 +177,48 @@ {"key": "a8b2d330", "response": "{\"id\":\"msg_01HSzeFmteF2FVLvWfZiQexf\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe calculation is complete! Using the async_add tool, I calculated that 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":568,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":30,\"service_tier\":\"standard\"}}"} {"key": "84d754f2", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01R3fz72tx2mQQcQfci52oXX\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":568,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nThe\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" calculation is complete! Using the async_add tool, I calculated that\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 5 + 7 = 12.\"}}\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":568,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":30} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} {"key": "35907e5f", "response": "{\"id\":\"msg_014cZ2xbyZHzoKMgaw8uRgJG\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"Hello! How can I help you today?\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":10,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":12,\"service_tier\":\"standard\"}}"} +{"key": "3a198dd2", "response": "{\"id\":\"msg_01Y8sRmyEFrP97An9GmZtTiY\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"This image shows an adorable **Cavalier King Charles Spaniel puppy**! The puppy has the breed's characteristic features:\\n\\n- **Coloring**: Brown (chestnut) and white markings\\n- **Expression**: Sweet, gentle face with big dark eyes\\n- **Setting**: Lying on grass next to purple flowers (possibly asters or similar blooms)\\n\\nThe puppy appears very young and has that irresistibly cute, innocent look that Cavalier King Charles Spaniels are known for. The photo seems to be a professional or well-composed shot with nice lighting and a natural outdoor setting.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":104,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":137,\"service_tier\":\"standard\"}}"} +{"key": "672b9d6a", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01Nz26fmn3agr7ps1EQT9Axh\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":9,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Hey\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"! How's it going? \ud83d\ude0a \"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"What can I help you with today?\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":9,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":22} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"} +{"key": "cdda3d99", "response": "{\"id\":\"msg_01LEgvcucDAZ4KYh2cbVdZ9b\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"I'll help you calculate both of those additions using the available tools.\\n\\nI need to perform two separate addition operations:\\n1. 5478954793 + 547982745\\n2. 5479749754 + 9875438979\\n\\nLet me calculate both of these for you now:\"},{\"type\":\"tool_use\",\"id\":\"toolu_01HxZy34zhwb93vsvZjJKgDB\",\"name\":\"simple_add\",\"input\":{\"a\":5478954793,\"b\":547982745}},{\"type\":\"tool_use\",\"id\":\"toolu_01J6dBewkpVWd4yBLwZwKjQG\",\"name\":\"simple_add\",\"input\":{\"a\":5479749754,\"b\":9875438979}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":658,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":199,\"service_tier\":\"standard\"}}"} +{"key": "af38cdd3", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01XJ5NyTk4dLpuxuivDAAg3h\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":658,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":6,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"I'll help you calculate both\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" of those additions using the available tool. Let me perform\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" both calculations for you.\\n\\nFor the first calculation\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\": 5478954793 + \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"547982745\\nFor the second calculation: 5479749\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"754 + 9875438979\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nSince these two calculations are independent of each other, I'll\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" perform them both at once.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01S1CtV8qksJZQ8J7WJTb7TR\",\"name\":\"simple_add\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\\\":\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\" 5478954\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"793\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"b\\\": 5\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"479827\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"45}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":2,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01L557qmRKJqeGp6EZUYAUYN\",\"name\":\"simple_add\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\\\": 547974\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"9754\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"b\\\": 98754\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"38979}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":2 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":658,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":211} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "87cf5d2e", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01LKNZw1g7tTuCkeFdDHRGkL\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":66,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"thinking\",\"thinking\":\"\",\"signature\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"This\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" is a straightforward calculus problem asking\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" for the derivative of a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" polynomial function.\\n\\nThe\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" function is: f\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"(x) = x\u00b3 +\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" 2x\u00b2 - 5x\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" + 1\\n\\nUsing\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" the power rule for\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" derivatives, which\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" states that d/dx(\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"x^n) = nx\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"^(n-1):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"\\n\\n- The derivative of x\u00b3\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" is 3x\u00b2\\n- The derivative of 2x\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"\u00b2 is 2(2x) = 4x\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"\\n- The derivative of -5x is -5\\n- The derivative of \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"1 (a constant) is 0\\n\\nTherefore\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\", f'(x) = 3x\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"\u00b2 + 4x - 5\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"signature_delta\",\"signature\":\"ErQECkYICBgCKkCP6BI68Kn+RnH4X4J8bRAVgHt0gkAqpnq6n4dpoT53f0C7gy6LDi+nTLfP5KTh/PGXfAr4EpZf6RGGD+KGKwgyEgy8FugZgVaXgQ29Y4QaDKTVhNjjjmc0pZoJICIw2EQixoKER132T0NkQTaBez6C9XL/v7KMZ4WJ8GmxHkOWJKlEYmVi8KMxaLakjq+QKpsD6fypkM9IQkbt9OSI0CPXFvJ0fGHT4/f65u98KmKxS4oqheKsn5wACiEu16Pq0O0tLeAI7WZaqGJOLA+cl1nV8nH+BgnvNXOTTahcpVkifFU8tAqbGN5FoxF6CNjkkk5MyD9bydlC/mceMhSLuoyjyldkT2KfCuTQAoFNR3vC4OssnEU0i8pVu750IYMKywJYyOlH4nEuw+g60bshO6dNcSs9lSlTpdn0pjS8fDbBmy3w6nNquvgime1C1RSVqr/pgM63X4op7LG2xMl6W2gl2qDblrx7nWdHrJS9bHYXoz9jYdYpuTRcSjyi6PIEWdJI+wttvE4mrYssIY059Z3ybR688fWYZuhLpDbexeaL3Eby2/NjiTHHFkTSuVG0OY06TVzNJUVhcpYDiiyofIrlKNOJLkGsTjtpjRu2dIP+9PKePkt7PekfklXyn7Hikt26S/rChumLLDCSmGqxY9Jqu1NpzxkUN5Wkq+D8fJBKzpbyTzB04tSVhcXVgdMgg63CJqukQc8yEYoaS3D04jT0X2q93dLQxidswh2KGAE=\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"# Finding the Derivative\\n\\nFor the function:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" **f(x) = x\u00b3\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" + 2x\u00b2 - 5x + 1**\\n\\nUsing\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" the power rule:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" d/dx(x\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"\u207f) = nx\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"\u207f\u207b\u00b9\\n\\nTaking\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" the derivative of each term:\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n- d/dx(x\u00b3) =\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" 3x\u00b2\\n- d/\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"dx(2x\u00b2) = \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"4x\\n- d/dx(-5\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"x) = -5\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n- d/dx(1) =\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" 0\\n\\n## Answer\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\":\\n**\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"f'(x) = 3\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"x\u00b2 + 4x - \"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"5**\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":66,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":303} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "5a403b7c", "response": "{\"id\":\"msg_011f7YbNeX8t4jv4pTswTTuc\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"server_tool_use\",\"id\":\"srvtoolu_01TMKBt7uWpGR729JWyJYCjx\",\"name\":\"web_search\",\"input\":{\"query\":\"otters\"}},{\"type\":\"web_search_tool_result\",\"tool_use_id\":\"srvtoolu_01TMKBt7uWpGR729JWyJYCjx\",\"content\":[{\"type\":\"web_search_result\",\"title\":\"Otter - Wikipedia\",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"encrypted_content\":\"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\",\"page_age\":\"1 week ago\"},{\"type\":\"web_search_result\",\"title\":\"North American river otter | Smithsonian's National Zoo and Conservation Biology Institute\",\"url\":\"https://nationalzoo.si.edu/animals/north-american-river-otter\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"Sea otter | Animals\",\"url\":\"https://www.montereybayaquarium.org/animals/animals-a-to-z/sea-otter\",\"encrypted_content\":\"EqIBCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDOyTtcnJG8+ih/XzixoMTqlbGZzguz9Z4oYfIjBMSBtxr8yyc9ntzf7fMssx8Lsb8PZcQ+fgI9yuMi6E76++eyriu/ViTta7FUuWTH0qJiuoNpjW1fyVtNZZrlWzkGRe8/OPoDaw7R5/OFlKLDoXYaGkRrWRGAM=\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"Wolf-Sized Otters Roamed Earth 6 Million Years Ago, Representing The Largest Known Carnivores In Shuitangba | IFLScience\",\"url\":\"https://www.iflscience.com/6-million-years-ago-these-wolf-sized-otters-were-the-largest-carnivores-in-their-ecosystem-80892\",\"encrypted_content\":\"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\",\"page_age\":\"1 week ago\"},{\"type\":\"web_search_result\",\"title\":\"River Otter - NYSDEC\",\"url\":\"https://dec.ny.gov/nature/animals-fish-plants/river-otter\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior\",\"url\":\"https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River otter | Washington Department of Fish & Wildlife\",\"url\":\"https://wdfw.wa.gov/species-habitats/species/lontra-canadensis\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River otter | Minnesota DNR\",\"url\":\"https://www.dnr.state.mn.us/mammals/riverotter.html\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River Otter - Yellowstone National Park (U.S. National Park Service)\",\"url\":\"https://www.nps.gov/yell/learn/nature/river-otter.htm\",\"encrypted_content\":\"EsQBCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDDgif1E5M6+L6t2+7BoMnclC1jaNojCTHZWlIjBHMf9JzyIO3JQwvAuxYjvgXv5wm0YyJkUD/qNlEuqmD5AX4j2a1v+1S+4d45UCIl8qSAfPUs5U1W1j8fk9uRMCEw/Ovt1e1UBbrHASVW3wNlKjYJkNblxicgZxPqePEr7mWuZauIqrUO156PWQjSjIQ949JYCFqLtOtBgD\",\"page_age\":\"April 18, 2025\"},{\"type\":\"web_search_result\",\"title\":\"Northern Sea Otter Species Profile, Alaska Department of Fish and Game\",\"url\":\"https://www.adfg.alaska.gov/index.cfm?adfg=seaotter.main\",\"encrypted_content\":\"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\",\"page_age\":null}]},{\"type\":\"text\",\"text\":\"\\n\\nOtters are \"},{\"citations\":[{\"type\":\"web_search_result_location\",\"cited_text\":\"Otters are carnivorous mammals in the subfamily Lutrinae. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDKGMLMJy9CqzKtqdQRoM5/Uk0Pc3XZrdaRWYIjAwyslNziDWYmnilFTXYQp+BsWujoJcgBtIgDb15NwP7Qe/B0rqVLUfbM11E2BFHjcqE1WSTLBALPdyFeYGRJmeEu9AxgwYBA==\"}],\"type\":\"text\",\"text\":\"carnivorous mammals in the subfamily Lutrinae\"},{\"type\":\"text\",\"text\":\", with \"},{\"citations\":[{\"type\":\"web_search_result_location\",\"cited_text\":\"The 14 extant otter species are all semiaquatic, both freshwater and marine. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDEg/Mobamj/CerQpcBoMeqkIOZnKRhaX7F2kIjDuxRt8DyxFOII2yU9L9tP/8wse6CIgkqVQwvzCAip0F23wIkI1QwywtFqdJ66WnlAqE/rII3xfJh/9Nazd+WkhMo3XIMIYBA==\"}],\"type\":\"text\",\"text\":\"14 extant species that are all semiaquatic\"},{\"type\":\"text\",\"text\":\". They're \"},{\"citations\":[{\"type\":\"web_search_result_location\",\"cited_text\":\"Lutrinae is a branch of the Mustelidae family, which includes weasels, badgers, mink, and wolverines, among other animals. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDNRfQMNOgljVmw4l4hoMqmvBKLLr1mmy/IP2IjD3hOViStK7aWuTLoYWV+K2Vr4CCRoXZQkMEzle5TQXi4/vf7I6gRWKSj3LLvDKzOIqE8ftcEeRSg4QJTaTLRHaccZR5+oYBA==\"}],\"type\":\"text\",\"text\":\"part of the Mustelidae family, which includes weasels, badgers, and wolverines\"},{\"type\":\"text\",\"text\":\".\\n\\n\"},{\"citations\":[{\"type\":\"web_search_result_location\",\"cited_text\":\"Otters are distinguished by their long, slim bodies, powerful webbed feet for swimming, and their dense fur, which keeps them warm and buoyant in wate...\",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDA9Bgtdidy549AQQIRoMdc0dmfpMVCoIdUTMIjBwY821+v+hfwg827S6KTcQylV9Tl2E+4Ke31xdATg1iwLbY31bxkt2sc36dQza8CIqE7EUazrJn503m3qo8d2PwoH14aAYBA==\"}],\"type\":\"text\",\"text\":\"They have long, slim bodies, powerful webbed feet for swimming, and dense fur\"},{\"type\":\"text\",\"text\":\". \"},{\"citations\":[{\"type\":\"web_search_result_location\",\"cited_text\":\"They can live up to 16 years, with their diet mainly consisting of fish and sometimes frogs, birds, or shellfish, depending on the species. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDKYFIaH73RV4IlT6oBoMKECCh3NZtd0Qds/qIjC/HcyZZ3nKPc5SA2HcoaAn+HqAggNdb47jqa/sPwYptWyI2azcsj9vSyT/1c6F/NgqE8xKI3ql+fIKOq2w5sScSPNyJY4YBA==\"}],\"type\":\"text\",\"text\":\"Their diet mainly consists of fish and sometimes frogs, birds, or shellfish\"},{\"type\":\"text\",\"text\":\", and \"},{\"citations\":[{\"type\":\"web_search_result_location\",\"cited_text\":\"They can live up to 16 years, with their diet mainly consisting of fish and sometimes frogs, birds, or shellfish, depending on the species. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDOMxooUJYtULMbzhGxoMQvWzf6CSbwfdkqchIjAoIim7c9ntctcA9X5qVNCjbLuQrUpxyXJS+d4rtOn1R/nkUidr4YmpiMXH+l5BHyMqE2YMyut+mWRskuLrG6LebHQ3+pkYBA==\"}],\"type\":\"text\",\"text\":\"they can live up to 16 years\"},{\"type\":\"text\",\"text\":\".\\n\\nOtters are \"},{\"citations\":[{\"type\":\"web_search_result_location\",\"cited_text\":\"They are playful animals, engaging in activities like sliding into water on natural slides and playing with stones. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDOIt+djfl+NZCO/K5hoMngF3VZrVt3u5HD+9IjAvVkkxx0v/3qbUaI9FA91qPMZCImPLincQ0ASlZEi1vEvI8zVD3SomGFNUYTFmjBEqE6fVCLVIUKptnF/N9SS4RmnIGeEYBA==\"}],\"type\":\"text\",\"text\":\"playful animals, engaging in activities like sliding into water on natural slides and playing with stones\"},{\"type\":\"text\",\"text\":\".\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":14882,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":297,\"service_tier\":\"standard\",\"server_tool_use\":{\"web_search_requests\":1,\"web_fetch_requests\":0}}}"} +{"key": "c2aaf6a8", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01ENhjAg5kDZnHz6Sjtw7p6i\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":2224,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"server_tool_use\",\"id\":\"srvtoolu_01Q3PRZ645thKhGsaaGk9oJo\",\"name\":\"web_search\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"q\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"uery\\\": \\\"otte\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"rs\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"web_search_tool_result\",\"tool_use_id\":\"srvtoolu_01Q3PRZ645thKhGsaaGk9oJo\",\"content\":[{\"type\":\"web_search_result\",\"title\":\"Otter - Wikipedia\",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"encrypted_content\":\"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\",\"page_age\":\"1 week ago\"},{\"type\":\"web_search_result\",\"title\":\"North American river otter | Smithsonian's National Zoo and Conservation Biology Institute\",\"url\":\"https://nationalzoo.si.edu/animals/north-american-river-otter\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"Sea otter | Animals\",\"url\":\"https://www.montereybayaquarium.org/animals/animals-a-to-z/sea-otter\",\"encrypted_content\":\"EqIBCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDDDhHWKE/uxVzH/neBoM4bSFsRuL8UO6jkkgIjB6EcJZdcS37hDOGz/FyJPyPyGqJur88sz20ufWPPGSDdOlWxMErTH8eFZeqIeAveUqJknmIQXLJKXmy+KdWIX6iZvbB6exHH+KrA8ml2JUTYyDIWvkrj7VGAM=\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"Wolf-Sized Otters Roamed Earth 6 Million Years Ago, Representing The Largest Known Carnivores In Shuitangba | IFLScience\",\"url\":\"https://www.iflscience.com/6-million-years-ago-these-wolf-sized-otters-were-the-largest-carnivores-in-their-ecosystem-80892\",\"encrypted_content\":\"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\",\"page_age\":\"1 week ago\"},{\"type\":\"web_search_result\",\"title\":\"River Otter - NYSDEC\",\"url\":\"https://dec.ny.gov/nature/animals-fish-plants/river-otter\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior\",\"url\":\"https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River otter | Washington Department of Fish & Wildlife\",\"url\":\"https://wdfw.wa.gov/species-habitats/species/lontra-canadensis\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River otter | Minnesota DNR\",\"url\":\"https://www.dnr.state.mn.us/mammals/riverotter.html\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River Otter - Yellowstone National Park (U.S. National Park Service)\",\"url\":\"https://www.nps.gov/yell/learn/nature/river-otter.htm\",\"encrypted_content\":\"EsQBCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDNu+sN11rVMHty66wRoMdCuG2fgkaS4seL1OIjA9zuYN+CMcqY72Jtd/5nAl6Jca+DpWpKjdW1r4LIH5tGDejcAZj/MWnjVXywyszd4qSKugrd7bCcKtnucYl3YkIfdufvASKGeOBnfEYwRxLyUrxDrwXNyZWHNdoUkexCKrpcnZd8fEBhF7yzO+hDJK08kkfGdspCYp7xgD\",\"page_age\":\"April 18, 2025\"},{\"type\":\"web_search_result\",\"title\":\"Northern Sea Otter Species Profile, Alaska Department of Fish and Game\",\"url\":\"https://www.adfg.alaska.gov/index.cfm?adfg=seaotter.main\",\"encrypted_content\":\"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\",\"page_age\":null}]} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":2,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":2 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":3,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"Otters are carnivorous mammals in the subfamily Lutrinae. The 14 extant otter species are all semiaquatic, both freshwater and marine. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"EpEBCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDDqyZAqfLhMPvmK8eRoMVXnBsd6S3Jt/0/ptIjCfB/StHrscFtHXcA1Tvzzt6Q8n6G2Lsdw5uLxuEuioyiywyt/nmrzIVul0ablBvO4qFaV9jGtj3IgoTeWjkI531RCmEK7NeBgE\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"text_delta\",\"text\":\"Otters are carnivorous mammals in the subfamily Lutrinae,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"text_delta\",\"text\":\" with 14 species that are all\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"text_delta\",\"text\":\" semiaquatic an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"text_delta\",\"text\":\"d live in both freshwater and marine environments.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":3 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":4,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":4,\"delta\":{\"type\":\"text_delta\",\"text\":\" \"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":4 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":5,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":5,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"Lutrinae is a branch of the Mustelidae family, which includes weasels, badgers, mink, and wolverines, among other animals. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDI5i8+i+51pxUNA+ahoMi73wByRTUw3z3FB+IjCI7hgw7NrsgQ+0USjGAr0H2LX+/Qs5Lxj9DyKFXwSwufv7iygGe06eU28fv/TP7xIqE3bKAMquTMWJR2jWTD9w0/I0EgAYBA==\"}}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":5,\"delta\":{\"type\":\"text_delta\",\"text\":\"They belong to the Mustelidae family, which includes weasels, badgers, and wolverines.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":5 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":6,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":6,\"delta\":{\"type\":\"text_delta\",\"text\":\" \\n\\n\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":6 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":7,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"Otters are distinguished by their long, slim bodies, powerful webbed feet for swimming, and their dense fur, which keeps them warm and buoyant in wate...\",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDKTpprv0GqWj77QUBBoMZLclmLahDlpZaCz8IjA+F05rwkrC4OP0WEYNGsbnIQ3F8HsXycPF6kFT9fhh67BmhWGBa01Cfss9EFm4HXAqE6jgyAoHXi/8a/G+tCYGqVt59bYYBA==\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"text_delta\",\"text\":\"Otters have long, slim bodies, powerful webbed feet for swimming, and dense\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"text_delta\",\"text\":\" fur that keeps them warm in water\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"text_delta\",\"text\":\".\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":7 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":8,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":8,\"delta\":{\"type\":\"text_delta\",\"text\":\" \"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":8 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":9,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":9,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"They can live up to 16 years, with their diet mainly consisting of fish and sometimes frogs, birds, or shellfish, depending on the species. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDH/1dYHMpPTL12LIJRoMz0Z25fchwlhgyXweIjAfOfd01hVOoebRkmUpZ/VAC+SJVRTyDMxUpGPg5zVn5x5CWYvBX2TPTprU4inHPVcqE+kN2+xfSmJAYa0YA2fLQXiO2VwYBA==\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":9,\"delta\":{\"type\":\"text_delta\",\"text\":\"They mainly eat fish, but may also consume frogs, birds, or shell\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":9,\"delta\":{\"type\":\"text_delta\",\"text\":\"fish depending on the species, and can live up to 16\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":9,\"delta\":{\"type\":\"text_delta\",\"text\":\" years.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":9 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":10,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":10,\"delta\":{\"type\":\"text_delta\",\"text\":\" \"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":10 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":11,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":11,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"They are playful animals, engaging in activities like sliding into water on natural slides and playing with stones. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDL0dJLL0+nklUsBtoBoMj9tI8LRH4jRls6iGIjCu8mni3+6pAfaIKSZ6M2CYeZKvmvGg2Cfdlru0MghAsq53zpgJog33ituyezRjrp8qE6z30RZba+re9vQ8rpPEXNwAg+QYBA==\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":11,\"delta\":{\"type\":\"text_delta\",\"text\":\"They are playful animals, known for sliding into water and playing with stones.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":11 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":14882,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":279,\"server_tool_use\":{\"web_search_requests\":1,\"web_fetch_requests\":0}} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "3baf32e2", "response": "{\"id\":\"msg_01Hqra1ndgS7zfMuix9foXZk\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"Hey Rens! Nice to meet you. How can I help you today?\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":14,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":20,\"service_tier\":\"standard\"}}"} +{"key": "f8fea31a", "response": "{\"id\":\"msg_01NFLfU24raDUM8u3eNwTbc1\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"Your name is Rens! You told me that when you introduced yourself at the start of our conversation.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":42,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":25,\"service_tier\":\"standard\"}}"} +{"key": "35e08ecb", "response": "{\"id\":\"msg_01T1SVk2XFjAJ9xRXWPZg3z8\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"Your name is Rens! You've asked me a couple times now - just checking if I'm paying attention? \ud83d\ude0a\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":76,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":30,\"service_tier\":\"standard\"}}"} +{"key": "e1935bc8", "response": "{\"id\":\"msg_01VBcS4xRZkw2JBbfRoWf39b\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01DJCz33tiH8FfcV1Wb1kHWM\",\"name\":\"simple_add\",\"input\":{\"a\":5,\"b\":7}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":616,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":70,\"service_tier\":\"standard\"}}"} +{"key": "4650b755", "response": "{\"id\":\"msg_01NJCKoknVhFihbNTLV2v8g3\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":738,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} +{"key": "31a041a7", "response": "{\"id\":\"msg_01Kcua6fTiDJyWhQjdXX39yH\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} +{"key": "eeeb628c", "response": "{\"id\":\"msg_018jr48P74cvR2fUdrAu4pby\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "d3e76949", "response": "{\"id\":\"msg_01Joh8tgDZgc8ugEPy3yykHu\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe answer is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":810,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "1bcf7f63", "response": "{\"id\":\"msg_01EQnE7RnMFbdrJeiDvwfNNu\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"This image shows an adorable Cavalier King Charles Spaniel puppy! The puppy has the classic brown (chestnut) and white coloring, with beautiful long, fluffy ears. It's lying on grass next to some purple flowers (they look like asters or similar flowers), and appears to be looking directly at the camera with those sweet, expressive eyes that the breed is known for. It's a really cute photo!\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":168,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":95,\"service_tier\":\"standard\"}}"} +{"key": "13d73459", "response": "{\"id\":\"msg_01QyhJ5cU9MhEAsMZfmYxw2A\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\" N S\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":275,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":5,\"service_tier\":\"standard\"}}"} +{"key": "394b7c8f", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01L4LrXQfuzQBzW4mGmYB5bQ\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":11,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"1, 2, 3, 4, 5\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":11,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":17} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "6f7cf945", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_011f45YczPiy9vSLSt65dtjd\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":44,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":8,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", 8, 9, \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"10\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":44,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":12} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"} +{"key": "b9e74921", "response": "{\"id\":\"msg_01StnokiN658gjfcmQVqp3js\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"I'll use the `simple_add` tool to calculate 5 + 3.\"},{\"type\":\"tool_use\",\"id\":\"toolu_013VsjzSGg7bHcouUftPeekd\",\"name\":\"simple_add\",\"input\":{\"a\":5,\"b\":3}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":621,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":90,\"service_tier\":\"standard\"}}"} +{"key": "4de21548", "response": "{\"id\":\"msg_01Cshp77XNoB1BLq52Arj6XE\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\n**Result: 5 + 3 = 8**\\n\\n**Explanation:**\\nThe `simple_add` function takes two parameters:\\n- `a` (first operand): I provided 5\\n- `b` (second operand): I provided 3\\n\\nThe function performed the addition operation and returned 8, which is the correct sum of 5 and 3.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":762,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":88,\"service_tier\":\"standard\"}}"} +{"key": "8acf0a9d", "response": "{\"id\":\"msg_017hERV1Cjm4x2Ecjk2ukvKW\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"The user is asking me to add 5 and 3 together. I have a function called `simple_add` that can do this. Let me use it with a=5 and b=3.\",\"signature\":\"Eq8CCkYICBgCKkBItYppVARNjd/zJSrIuPgYG2iMuuMh0gxsQR/MUYrcXhMGRw/y26TJBKiFOBgJg9Olwrcq8DnIAvaV2d21aNHsEgxhmlCGs8Vvt+lfA38aDOVmx7i/a+rLUoeH8SIwY2xRFwyG5bksEb6/WSSnL3CtkX1fxl9tOxaPWYVYb6OpyGL5b4hl3EqKZmA212huKpYBDc5BNQvHT1k+P7taINEaIEOrenmQpwzt2djwWOVkC421jLKFWN9aT3rNMGjnANzjLQ+HXMKMSJefnP28xVcy3MkaJS5h87M3pRoL5JzR5LRYqWYbKTmuYRl8nVOM9/HEMVR4ikgSVb/ziC5c5zoxq/CW3EMHLveWdViV5eDXz3BeHSrsDZdOFJYgzDScI6DxL4f3QA9rGAE=\"},{\"type\":\"tool_use\",\"id\":\"toolu_019CGrvryYqUkZa2E3GAiMZq\",\"name\":\"simple_add\",\"input\":{\"a\":5,\"b\":3}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":638,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":124,\"service_tier\":\"standard\"}}"} +{"key": "70ac02ac", "response": "{\"id\":\"msg_01KP4qe5YeUUkNFGssPH1qLA\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\n5 + 3 = **8**\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":813,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":14,\"service_tier\":\"standard\"}}"} +{"key": "755b9abc", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_018r9jDp9KxnvB3tD4ZoPm3c\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":2224,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"server_tool_use\",\"id\":\"srvtoolu_01NJnM2L3uYMo8s8fbWUzrC2\",\"name\":\"web_search\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"query\\\": \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"otters\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"web_search_tool_result\",\"tool_use_id\":\"srvtoolu_01NJnM2L3uYMo8s8fbWUzrC2\",\"content\":[{\"type\":\"web_search_result\",\"title\":\"Otter - Wikipedia\",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"encrypted_content\":\"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\",\"page_age\":\"1 week ago\"},{\"type\":\"web_search_result\",\"title\":\"North American river otter | Smithsonian's National Zoo and Conservation Biology Institute\",\"url\":\"https://nationalzoo.si.edu/animals/north-american-river-otter\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"Sea otter | Animals\",\"url\":\"https://www.montereybayaquarium.org/animals/animals-a-to-z/sea-otter\",\"encrypted_content\":\"EqIBCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDMnfJBOItp6I7LxN+xoMCmfdd1iPQCtFe4ENIjAchCQJ5wH6hwlieZQWh+bUSOR7wOqtV6aVDFugnTCS14Cd9zXOBvMLsTs16qNf1y4qJkXWg/nH32xnBz1NskyOaJL4RVFnH0p9Rn1phyUk4A/thb/5sMz7GAM=\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"Wolf-Sized Otters Roamed Earth 6 Million Years Ago, Representing The Largest Known Carnivores In Shuitangba | IFLScience\",\"url\":\"https://www.iflscience.com/6-million-years-ago-these-wolf-sized-otters-were-the-largest-carnivores-in-their-ecosystem-80892\",\"encrypted_content\":\"EpccCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDFjcz5XJf0QOcjvB+BoMqW/+Y/IU2ZKZ4ef+IjBsQFXsaMoiz1ICyluG9brwvmEfdH/n//kxHDNZbXjfdG1watvlH0Z6Ow7QJtVrwE8qmhuaoiBsv1/j6TOxCV6e8OKVIVHO4zB4mUj36RcDWKOaKAhmVIOtMIn6fa5AHkdT1OFaANEOC5h2+RzcWflOBJC03Crnd/Qc/+FY+FbBPZudPy6jDjKLgiS0GorC/2RhirRwmWn9e0STdCCJCjxN54inN/OduknqpMejCgIY/TgV5RsKCX+ujDVq273ocj7GFLhBnrR6IHmtLaZqDYJ7CxomQtJHYqPZtH8kDMuve0FAMtmtbZgYfn13BBcyFJnCoPMLfyvCEWhyvG0ri56CZenFAIGMXFdcxell0UzSWSV3bBo8w3Lte1uklgqTuHF047o1svqKiIryjumwqTTPT+e7mHghvUnv74fkxPunjwt6l+eqO/j3xH0FjqVEoBZVGvrLZoQgXAtlzKaxWdw9jwh50v2B2weX3BAIiSAPq6sam2Nj6g2QDpx/1dlwO0sT2YXVHWep7qEWo3evZZGkgAkG8b/YPDtw+kmCyUu9o3e7p9vuBmBKLtxRi0BKCn6p9LpV7bY6rW54YMSKX/TpQ0B03PZILpi4KbXxyNESoVaf7sivuOPZe2wFjbFHlPelOKGGp+CXgZNWq7/vrWOLN3KzERMUvS+TYbWLpynia2YzvlU0ywKtff1lhcppGDP+N01HRRvLQTcmJ+6WJ3pFJLHdpG8GBm0pOYptIgi3bWifxqvGuVDa7BAVQxQErtcEyXhRulPvs+/kkVeGp/Tjl1c6mMBirGD3sHWgAZCwQlOMMrORpu8lw1FG1NM6rrI4o5Q/TKO0aT6NYMSikl35bqS/sMn2JNF8w+i2JLNR+eWB2bwaLSLuoq0nIXyaf6t++xG2L09+TcZ0uCiJnQg2s7ZQMp+nxnyU98DioUudYCSOcXN7dnmmTXmMZA9aW1Yf35LCoqG7gu9J3+kEwC9y6fslRDZu0REgN8f3juExS2mIyTj4Ka2jAs0NzYoWnUIbS7PjXPw4zp3s52D2EDzL6S1Y3XlNdaFs2aVC/PKOJcZCtUPlIr3RYGNX7ePDjwlS9VE8fvWoB89MaBsmxScKNd47mQj4PQrettxQzwdVPhlk5wdK9y5usSLJDIaxW2bBqOPZstL57pwOXWCerw+KSmeyAyt1z9U6hNcZIDSLzvqWZQKYRg44Xq77J/tJDVAP/hYNUWVB+s5WeIneVioLnxqJrikTGaZbuG+K1LVYiLs4BGsH1Xh62brA4Yyc1GOXfHAto69LKL9Etxe9z5UlJxzP2dxPXFIL6SUlbzIBD70J0tUp11eOJS+FEXRn/y5j/OY/hLyxfY2PQwpvGxKOjRUUnh2WdE4S+/yBPWiLbHWheFA86tFa2Oc0cvrjquFib8Dh8vSUNtlD8/HzHS/gKZpz7VCiIsOu4+i1lI2wFB5JNj3fyB0qY0Q5F+ykG+jiRs4ybH1qWVCdCjJI2WRuPsblTWE8wUYojU3Ajyd0AX3wFESvw2sLgygTCNYgUyeQCM9Njj0aC82k4yEKxqrOddCrt0H3ew2FuIBnvAxsjSD7iR3i4KftQzyRKrY6pGRzTbVMBWDNzt7lKQD7uQru+4SxDePgoq6nXuoQ56X2JekZQ6V/rmH154ojGL4nsAhlGbNrzusya8S/orSVLKAmkFI/9FAnQI4qs+XNAPEXP8NSA95Itac6/eLyUzKpWrJgURZ8gKdMemrQqtTYE/r8+nAYy7t8WGGY54VNY6yXN3Xo+tt3oEkRXBRGNBqFa0h9VjGFdSEWE+1JhI/KkVvt7DSm6gn6jqP5wNy3PmWk3UMF9/n5NYbO2CBzlPjCOw7Lewk3RjzFuVMbaS1aPuqT7GMeGqCI0iRFbx4F4/MEGsqqPR9Bl1Gh/S7Oup8wOm/WQFxWgFDH9FKfis+IV2Ml+SMZoGo3eddXYF2zXxY49wnLrCn//OoYww3p5WysAmr5rbwfRIyjD44lLty3tiq7qeWW7Ro0B3eg7p+f1tkv8ECD3G0qQQlLq1mL9udYlD9TSwl5r7KTY130Q0YJmd7BEDdczEWetDt+NpVIk8sa9qTlaVtH6pijSR2yWchfmTsivPQVbIPNldbKnPMCq3V0ur8wiUeQ41z6Kk+7RcFAL69/9QWFKabqCXu938OxJCSdsfdEFtYTsyq/HtB4fXQk96ppaZx0ZasrE5BIO/p5kYG6yFu53OAKPHibR/VqQme05xrQNquSkBZtXztOEIg+r/KvMXX+nTLvMwwQbo6gbxNOBwTKMol5wBd7RLt+slh1+ptkBMRI4fMRMWgOFPC6YjIJHrJfav2yOcUbQs2y2boAgtZs+b+qHcTKk93OkYaZqRlkmMGRzNsFEeOIH6TrIsZ7KcDX6jRxrsRYZRtZRrXU9yfQfpmVxglAXJVPdeq4HDQptRK7qGbtwkWfvIYTJCp1mER6AEIzv68iQZrvp8DSXHrCrmHEWBahLqfwXlAWQHLCeBvyEaXTJjba5UlpxuuzuQrURJ665fFVa5i2RZJ9h0wTqXNcdvJ4J8DysqYNcaxc3mRjfr8Rs51JQ/LZ2WTF+SoLCWVaioHhr+vjT81tBY/sKD8E7TsouxqFcSSuiHzQzyTE+7Mm4u99ZjLskQm8GIt2t1Ni+xuTzTtJnUwZug4dR6CWGmgiNKnlobg/EWw4yuKpYAqaSMqPdzhbCKmOhKctZXsUHKewKX1ukByq9yGAfVRipa7wD77Z3+wNZQoUgXaZA9qtpqDM+KmrcDZIgKTfqLMyUKXLHPY6P1CO0Qq73G+gIL4zVT5kRogHIWUIxAFcq11beWLt593Q66aC1qAXJbgZ6OU0PhDFZ/FSay7MMDaKM266TYPRrvJ5+P3XzdvdZy1HpmKqa8FzmCKoQKjjGj/+rfCFGOaGHt3Rtz8qjFskNPzSl5eLNGeJ9VZ6fGh3ro6u1+UkNyKOuitf7/Ovv6R2GXAEU3YJXbksfr8D7wGoITPGFT21OIJmN00RFMN9K9m10K4529RzIPR+LVXhZCkkee79Hb8t2ErUDIt1533OOCdPSaxJ5fuDxN6TjaOuM7u/ZQ128Zdol6z8I/or2KRFbCJSIYRzZU+SbJ+GmrWAnmJCZYGlurRL0HPPDQO8+sxwzenwYujkHVoYH1QG/tgVfefkEH2IWfs5qBPTiCkDZUUGiyPXNMirzWxzdgUz91qI0ngIWPOb05CE4pSCcRo3RzmNw4Q6fsSrogK8h0czho0GG4MfmdETLZ8thC2mvkLE266MS4VMqPnOQsp9EIkCrsRdObXphncu0i5OSSVLfbf1ktv0oWZGEZTsIEWg/bTZghD/FLKHsQSFpEB23cvKJkHVuZU0an85hrJjF+UmeedtZf21lVlcp6iFw+tVWxzRtHG2oEuFz7doDIc4zU6cZYpFFR7MoVhT058Zmc2pODa1XdniK40DT+cBE3sOEG31UPcxz/l3gHptiIwpfmXn87x2ywkr9tIBl8wwP3uqRajJDSlnUecD0Id8ryVqmTXQ52kjLMBSnnGq5XjQa/drd7swKY8+UVsdo1KUiNG0T2hVRf0l7VgCvEpNtS9h1vFES05/S26Z/W0N2J5t4SqKG9h1DJAGevRqaj4cvCNt+O0+OKLqO7eu9uly3seF4A6vlClTiwEnrj2wizRPE26ldTQMn4QkpHNMnYcXO+++keL92BZLVsROU0Wp7A5P9ZYmWp3m7g3D/RYFaiC2/skqw0SIpIuvm2jGBYsuotuY88hN8PRdOadwu0eziqD1VexbSPVe8od7KVJK9Z27bseXamGjy5Lw1/9vyjOFzZOiUnxWhpb4+SKhX4nLy4LrksCupy+aFAgQa4iCAxoxomaySs/XlEsXQEFk2xb31o0V0F9snsP71cfQnpus4f412ssl8F9MNeqFcD1igkSyA0ipKYNEAa/4XdPhdAmtFuER+l/FbF7W0QYhgli+lQXEnU6IyjsilvWZXLY1FBaYkS1yfX/DGnlaznjbhMxeh42aRyGYltRgCqSWY28Hk631gFlWYo2HFhvQQgy8TJzfzHAEIMFr/dKQNkRjE56NJ2aC+3FVN+CPxDncuZLnXmhazOY62K74NAwAaKyGdXYUw1VHJIWBAZl9rXE60+uLFzqPOGDKVcvL1UFfwmEtve87ATkHETtQ+uXPO21cJxlxNd3pvRRCukTiLPpt1IMuiIU04hxMDi+irQAiUtYDo7NW+Esfl7lbceloqM9F53RoWcUzZ+1pJmlFK5HD982XRzzkMejtFnUXGfii8VhCVF4LyoA6ykPksJ2XpDj1NVL0SxRhcKbAc9ucwcByEfMRCSah73N0t+PQKSs4ZG3FIutIWm59hUgQCguyHIIYOvJgk766uayUiOGfY7gbuumB7N2DQ5NkNFUQD/y8qudn+bmP0djEnM7+Lqf27QegUwLK+RiXLllScpoaaHkjn+ObjumujV9wE8FjQyRW6E5iMGnL+EWXft6e1U4wwolHugUXsq+jcYJUh4R0Ypm3SCF7Z6BtQ9yC7XTyvoe2hfev4IhhLb6w2Zj1AgDNJM3BpPmdSIT56oNEqlh2jGnyiFTK8vJ+OXhnWCOnEgS2W2nt+Sz9xxJEUqmfPlhONyc/JtcUePDU29xn+n+YPaB2JzrmWw7bNpjvQm4DBhzP44MEPUoIJEoF4Fh50WBucu2SJom/5EQFSUVJehgD\",\"page_age\":\"1 week ago\"},{\"type\":\"web_search_result\",\"title\":\"River Otter - NYSDEC\",\"url\":\"https://dec.ny.gov/nature/animals-fish-plants/river-otter\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior\",\"url\":\"https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River otter | Washington Department of Fish & Wildlife\",\"url\":\"https://wdfw.wa.gov/species-habitats/species/lontra-canadensis\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River otter | Minnesota DNR\",\"url\":\"https://www.dnr.state.mn.us/mammals/riverotter.html\",\"encrypted_content\":\"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\",\"page_age\":null},{\"type\":\"web_search_result\",\"title\":\"River Otter - Yellowstone National Park (U.S. National Park Service)\",\"url\":\"https://www.nps.gov/yell/learn/nature/river-otter.htm\",\"encrypted_content\":\"EsQBCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDHHTJ++tN+m3IMheMhoMPsmUciTPp/dBnmepIjAip3WHSOAUYzq1zTksrLu/DUg6GBcq2vypf53na8T0egbdmKp5+YKlAILkKMqzuYYqSLXpsnwOSH+pAbhuHBp7Znldehza8IDTEvd0rKjMQlkkY9Lm9QuM2Is75RGWHjsS0pZwNp4NucqwtQWeOONWK4k7HFvbD/glHhgD\",\"page_age\":\"April 18, 2025\"},{\"type\":\"web_search_result\",\"title\":\"Northern Sea Otter Species Profile, Alaska Department of Fish and Game\",\"url\":\"https://www.adfg.alaska.gov/index.cfm?adfg=seaotter.main\",\"encrypted_content\":\"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\",\"page_age\":null}]} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":2,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":2}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":3,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"Otters are carnivorous mammals in the subfamily Lutrinae. The 14 extant otter species are all semiaquatic, both freshwater and marine. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"EpEBCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDPa1hiGXzVl1cOu9tBoM2SSi2MSsK4h318amIjCDafhsIhAQUG00zrRDvu1JVobwxy3xZGcZ8zXls4T6HJrt8mlcgw9xzunn1xPEKLIqFc8capP8K3UmNSXFNgTgzbzrzY8mWxgE\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"text_delta\",\"text\":\"Otters are carnivorous mammals that belong to the subfamily Lutrinae, with 14\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"text_delta\",\"text\":\" species that are all semiaquatic,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":3,\"delta\":{\"type\":\"text_delta\",\"text\":\" living in both freshwater and marine environments.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":3 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":4,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":4,\"delta\":{\"type\":\"text_delta\",\"text\":\" \"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":4 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":5,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":5,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"Lutrinae is a branch of the Mustelidae family, which includes weasels, badgers, mink, and wolverines, among other animals. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDAAzNZTfFnGImDIYpxoM+7rQO4ci0FmJCHMtIjCsDAB1rLWkR49lN4dAYGQs0L8FpncjXZ+Cj7nmPcKSBqMh3o58tzA6kStmsIujnfsqE9FULLMTZgH0EYX3yukOliTh2boYBA==\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":5,\"delta\":{\"type\":\"text_delta\",\"text\":\"They're part of the Mustelidae family, which includes w\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":5,\"delta\":{\"type\":\"text_delta\",\"text\":\"easels, badgers, and wolver\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":5,\"delta\":{\"type\":\"text_delta\",\"text\":\"ines.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":5 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":6,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":6,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":6 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":7,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"Otters are distinguished by their long, slim bodies, powerful webbed feet for swimming, and their dense fur, which keeps them warm and buoyant in wate...\",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDN1QImXybbBgulwbpBoMmEqv8qULm9fVKe4FIjBsAVus8CZ0akRZ/O8TU0M53C+lek4/bmayCz01hcRzXAG1rejxtgTocDq5UVyz+0UqE+Y00Teyv0rafMD63J1VnY3PywUYBA==\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"text_delta\",\"text\":\"Otters have long, slim bodies, powerful\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"text_delta\",\"text\":\" webbed feet for\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"text_delta\",\"text\":\" swimming, and dense fur\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"text_delta\",\"text\":\" that keeps them warm an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":7,\"delta\":{\"type\":\"text_delta\",\"text\":\"d buoyant in water.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":7 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":8,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":8,\"delta\":{\"type\":\"text_delta\",\"text\":\" \"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":8 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":9,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":9,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"They can live up to 16 years, with their diet mainly consisting of fish and sometimes frogs, birds, or shellfish, depending on the species. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDAS9k3v+Z53eT217nRoMIV2RrHYcX5KY5U9eIjA0+yy2r2OadiFgmva5KV9AV+kRzJ2ge2Fi4KiKACd6wFe+CVjYR/63DwE127RIEHAqE8fef1ps9NGFFdOeD8cM5TYBHyQYBA==\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":9,\"delta\":{\"type\":\"text_delta\",\"text\":\"They primarily eat fish but may also consume frogs, birds, or\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":9,\"delta\":{\"type\":\"text_delta\",\"text\":\" shellfish, and can live up to 16\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":9,\"delta\":{\"type\":\"text_delta\",\"text\":\" years.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":9 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":10,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":10,\"delta\":{\"type\":\"text_delta\",\"text\":\" \"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":10 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":11,\"content_block\":{\"citations\":[],\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":11,\"delta\":{\"type\":\"citations_delta\",\"citation\":{\"type\":\"web_search_result_location\",\"cited_text\":\"They are playful animals, engaging in activities like sliding into water on natural slides and playing with stones. \",\"url\":\"https://en.wikipedia.org/wiki/Otter\",\"title\":\"Otter - Wikipedia\",\"encrypted_index\":\"Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDLao4hhJwnaZQjOHeRoMN75LAFfo0qAn5QAdIjD7AYISjB5EbjuLskVA1mXBaMtP+oMH0U4IAe7IVZ6wS4LsBnBgPDNUc2X6OC/mrHkqEw2w2E8+99aF7vJtGt5YssbQyQwYBA==\"}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":11,\"delta\":{\"type\":\"text_delta\",\"text\":\"They're known for being playful animals, engaging in activities like sliding into water and playing with stones.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":11 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":14882,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":285,\"server_tool_use\":{\"web_search_requests\":1,\"web_fetch_requests\":0}} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "4a947368", "response": "{\"id\":\"msg_01WbB8EZJHTVA4i47wnDX3LE\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"I'll solve this step by step using the addition function.\\n\\n**Step 1:** First, let me calculate 5 + 3\"},{\"type\":\"tool_use\",\"id\":\"toolu_01K82p3nUAFfrsfM43QWYkSH\",\"name\":\"simple_add\",\"input\":{\"a\":5,\"b\":3}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":617,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":100,\"service_tier\":\"standard\"}}"} +{"key": "feb84417", "response": "{\"id\":\"msg_01BkVPWbysLmdJwjNztsjdkR\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\n**Step 2:** Now I'll add 7 to that result (8 + 7)\"},{\"type\":\"tool_use\",\"id\":\"toolu_012DMaKSvM5ssehCU1iqUszX\",\"name\":\"simple_add\",\"input\":{\"a\":8,\"b\":7}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":728,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":94,\"service_tier\":\"standard\"}}"} +{"key": "65a03f48", "response": "{\"id\":\"msg_01QPfGC2H3egJ8W5BvWCqD6B\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\n**Step 3:** Finally, I'll add 11 to that result (15 + 11)\"},{\"type\":\"tool_use\",\"id\":\"toolu_016SoPc5qmrreVLMReT73od6\",\"name\":\"simple_add\",\"input\":{\"a\":15,\"b\":11}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":833,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":95,\"service_tier\":\"standard\"}}"} +{"key": "1e14670f", "response": "{\"id\":\"msg_01UUd9nkfZGvbH1K6ByzyGAy\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\n**Answer:** ((5 + 3) + 7) + 11 = **26**\\n\\nHere's the breakdown:\\n- 5 + 3 = 8\\n- 8 + 7 = 15\\n- 15 + 11 = 26\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":939,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":68,\"service_tier\":\"standard\"}}"} +{"key": "35909f1f", "response": "{\"id\":\"msg_0118Z5nYcanqzLCkvbBmwQCP\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"I'll calculate this step by step, following the order of operations.\\n\\n**Step 1:** Calculate the expressions in parentheses first\\n- First parenthesis: 10 + 5\\n- Second parenthesis: 2 + 1\"},{\"type\":\"tool_use\",\"id\":\"toolu_01QGnA5EkWKTX8QTiVf5NtJ2\",\"name\":\"simple_add\",\"input\":{\"a\":10,\"b\":5}},{\"type\":\"tool_use\",\"id\":\"toolu_01KxdPWNQK1BN7p4EfQTDtj1\",\"name\":\"simple_add\",\"input\":{\"a\":2,\"b\":1}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":792,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":178,\"service_tier\":\"standard\"}}"} +{"key": "6e0b0b23", "response": "{\"id\":\"msg_01MWXwbHBytWCJgLZCyDhDtq\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\n**Step 2:** Multiply the result from the first parenthesis by 3\\n- 15 * 3\"},{\"type\":\"tool_use\",\"id\":\"toolu_01ELPz78sh1V1iBfGusJ6og7\",\"name\":\"multiply\",\"input\":{\"a\":15,\"b\":3}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":1033,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":96,\"service_tier\":\"standard\"}}"} +{"key": "ec00a2da", "response": "{\"id\":\"msg_01QvrK19THXncFuYAMVGspQS\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\n**Step 3:** Divide the result by 3\\n- 45 / 3\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":1154,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":30,\"service_tier\":\"standard\"}}"} +{"key": "95f77a54", "response": "{\"id\":\"msg_011QUvyPDk4mZKhZvPWQAe7e\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"I'll help you calculate this step by step using the tools!\\n\\nFirst, let me calculate 1+2:\"},{\"type\":\"tool_use\",\"id\":\"toolu_01J4JCXCvvpan8Mo2TZdrDrk\",\"name\":\"simple_add\",\"input\":{\"a\":1,\"b\":2}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":629,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":95,\"service_tier\":\"standard\"}}"} +{"key": "0aeaf94b", "response": "{\"id\":\"msg_01PSA5gmVxFHQeZHXyBbZ9KG\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nLet me continue with the next calculations:\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":775,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} +{"key": "9b4fe00c", "response": "{\"id\":\"msg_014FUyBPF1qreYCrSo9ic9hr\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"2+2 = 4\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":14,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":11,\"service_tier\":\"standard\"}}"} +{"key": "d974dd62", "response": "{\"id\":\"msg_018rakSVpRd4k817peCAMgp8\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"I'll use the async_add function to calculate 5 + 7 for you.\"},{\"type\":\"tool_use\",\"id\":\"toolu_01G5KQPS37jjw2qsK4JJnY7g\",\"name\":\"async_add\",\"input\":{\"a\":5,\"b\":7}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":607,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":91,\"service_tier\":\"standard\"}}"} +{"key": "12234d66", "response": "{\"id\":\"msg_01LhrJ8Uw3UF8yKTsSo486i8\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. The calculation shows that 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":749,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\"}}"} +{"key": "2e219967", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_019EAQeB6GdhPciwrQ56wkH2\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":607,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":3,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"I'll use\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" the async_add function to calculate \"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"5 + 7 for you.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01Bg8m1Vw2GTQroayJb9xHaE\",\"name\":\"async_add\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\": 5\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"b\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\": 7}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":607,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":91} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "edcc265f", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_016qSzkpbXJP3qZk3g2Nbcxh\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":749,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nThe\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" result is **12**. The calculation\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" shows that 5 + 7 = 12.\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":749,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":26} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "62125862", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01XpzHYJRGfXsSEz7w3RAK3Y\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":56,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"thinking\",\"thinking\":\"\",\"signature\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"For\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" sorting 1000 random integers, I\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" should consider:\\n\\n1. **Quick\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"sort** - O(n log n) average case,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" very efficient in practice, good cache\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" performance\\n2. **Mer\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"gesort** - O(n log n) guarantee\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"d, but needs extra space\\n3. **He\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"apsort** - O(n log\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" n) guaranteed, in-place\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"\\n4. **Built-in sort functions** - Usually\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" highly optimized (Timsort in\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" Python, Introsort in C++)\\n\\nFor\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" 1000 random integers specifically:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"\\n- The dataset is small-\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"to-medium sized\\n- Built-in sorting\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" functions are almost always the best choice -\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" they're highly optimized\\n- If\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" implementing from scratch,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" Quicksort is typically fastest\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" for random data\\n- For\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" random data, Quick\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"sort's worst case is unlikely\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"\\n\\nThe most practical\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" answer: **Use your\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" language's built-in sort** (\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"e.g., `sort()` in Python, `st\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"d::sort()` in C++,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" `Arrays.sort()` in Java).\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"\\n\\nIf implementing from scratch:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" **Quicksort** is usually most\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" efficient for random data of this size.\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"signature_delta\",\"signature\":\"ErAICkYICBgCKkAxZzsHzHlcyrB1Ujj1dPExdVUzzhra4+x7bT2tLlOYdymQZgwfnKggpc0ItFtNIFiDoSrHRajU+j+jZWyPH2VjEgwWewmst9o/FuCdHJAaDHvSQdll8AWWxgbDAyIw77NVaFrnnY0pKoNjVph6CUA6IcN2kQNvDyHJEaBk8dalEV2SpOpuhpxKefHat676KpcHpoV7vajZCzEyjS9h2dqZxL/T+Wi2myg5wyXXEjUsKybKCy+MPiZDICTccVz5fNKR5dDHkRuwPUKFlmvlEmGmkawQmj9EfiMfHnIgJ7xTbhkm6xkqTs3CilDc+qNI8zyx4F/Zvp1ZKSTmOlK3Ys3CBqneTgupL+BeSn5iPvLDFJUnGZuFqjxhNzuu+lXypA5nv4EIillqRlj9FhiQeXqsCeEos/mvamnafWmTVd+huf5fASfOufNnpQzQfQmae89p8e9LlP+O9Os2T+EIDCDS9vGMChODokkEt0j8qmjpovzn0nfi5Um48d+2WRD7xSY150Fp11Ga5tlhSdjqKXHAECkYjFSbiG0Wrsi2W+NPbjuIXyGnwFbSCrRAyGF/yrbD1ABP8RfJJOqxw7iyYgBQMnT7WNa5LuqTJ7WKj1swwRdZmRyDw23eAkFw76cay6tJvRiuJMr9pFtyeQkIS/LPxsaWoYyhsw6Fz3pDjqE4Lron4NXON35safVWE3wwgg4FbayHLZRSOq3GQVTIj0dWqU1lchztSDHLUe9OEMc94S1gknKlvyvQN3Jx65wBTLolrhg+ric6BLHqLrt9zfVWyPa2FlWmkM+ZTzFvkzbttlWOzKMsaPkaoUCEUHEsXU2GAczg7WgtUTRvZjUmwHSisNs3/p1d+rx5cHmZo4zdiG5Y1S7DfGAfs01l9bxDRyGJ6G/cHUrEZZIHjuCytFI8XlKqbCSe+IRCe4c8Qlh7eAV5tlBwoKpr9Q5tJKra40qw91EUZ9fgdxFTQCU8t5CjwZ7pZyihaI/pn/T1BOgc665rr/ikutEIc0EvwMU0YZriCF62fiidozS2/kbqG+A8jo2Jf05KnAdJ78G/A1kRd1r8cOi1zpNcHWg4+MJJdRkZJcOcFSJuWZ65S6pf1pi+hoMJCT3dQSTkAu7+JkMzYDHKh2jIWkI+9hYhYB/teUy6HYwyV6jBBSEQDjLlCxtAJCOiqNzuhc8pntc186KwJoCyxQMRXcuuZ4OC7Hmy3c4IVj1zEKobJ1vqhCoRjrymDIS1ONZV4yptCEa8qz2UNVYPipojje4iQPdPSPMWyzJyykOKgGSgK1nRBbFQrek6kghyQrNo59ZZ7jtXOXqEI7826jktGK6vb0M1nnr5P7DaOMfwkCkJOVf8YpH338jelYIwT10RekmNboq3b983mTHY3NpLWYO3+tBiu1a4sRn5FMKeA677hhgB\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"**Use your language's built-in sort function** (e.g., Python's `sorte\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"d()` or `.sort()`, C\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"++'s `std::sort()`,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" Java's `Arrays.sort()`).\\n\\nThese\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" are highly optimized and typically use hybrid algorithms\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" like:\\n- **Ti\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"msort** (Python) - combines\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" merge sort and insertion sort\\n- **Intros\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"ort** (C++) - starts with quick\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"sort, switches to heapsort if\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" needed\\n\\n**If implementing from scratch**: **Quicksort** is usually fastest\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" for random data of this size, with\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\" O(n log n) average case an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"d good cache performance.\\n\\nFor just 1000 elements\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\", the practical performance difference between goo\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"d O(n log n) algorithms is minimal\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"\u2014built-in functions win due to low\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"-level optimizations.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":56,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":444} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "a1904546", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_018aJXP8sDnN4wK3jVBFifDY\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":794,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":8,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"I'll calculate ((10 + \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"5) * 3) / (2 + 1) step by\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" step.\\n\\n**Step 1:** Calculate the\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" first parentheses (10 + 5)\\n**\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Step 2:** Multiply that result by 3\\n**Step \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"3:** Calculate the denominator (2 + 1)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n**Step 4:** Divide\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" the result from step 2 by the result from step\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 3\\n\\nLet me perform these calculations:\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01AKfRh9iDgX7ZNtjcX8FWgm\",\"name\":\"simple_add\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\\\": 10\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"b\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\": 5}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":2,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_017xC4A71CAwU2e27gjU9C7X\",\"name\":\"simple_add\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\": 2\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"b\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\": 1}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":2 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":794,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":228} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "ad552961", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01XgSG7RBE53DdQwTAoRK93Y\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":1085,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01MYhQybzNJ8FGXfZrC7dd4v\",\"name\":\"multiply\",\"input\":{}}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\\\": 15\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"b\\\": \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"3}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":1085,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":69}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "c1090493", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01CKqEsMZtfsa7WmibnMcudy\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":1179,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":9,\"service_tier\":\"standard\"}} }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":1179,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":9} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"} +{"key": "3aa12e4d", "response": "{\"id\":\"msg_016GgcVWcQ6YtXR3uv9FgeRx\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} +{"key": "f9a8ba88", "response": "{\"id\":\"msg_01BYyMXiWiRrsJgvnaJFbY95\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "97e73e3c", "response": "{\"id\":\"msg_01RHj2UcxLRrwHZVPiwjEHhH\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe answer is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":810,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} diff --git a/lisette/_modidx.py b/lisette/_modidx.py index e8eba63..7245f84 100644 --- a/lisette/_modidx.py +++ b/lisette/_modidx.py @@ -17,6 +17,9 @@ 'lisette.core._add_cache_control': ('core.html#_add_cache_control', 'lisette/core.py'), 'lisette.core._alite_call_func': ('core.html#_alite_call_func', 'lisette/core.py'), 'lisette.core._clean_str': ('core.html#_clean_str', 'lisette/core.py'), + 'lisette.core._fmt_thinking': ('core.html#_fmt_thinking', 'lisette/core.py'), + 'lisette.core._fmt_tool_call': ('core.html#_fmt_tool_call', 'lisette/core.py'), + 'lisette.core._fmt_tool_result': ('core.html#_fmt_tool_result', 'lisette/core.py'), 'lisette.core._has_search': ('core.html#_has_search', 'lisette/core.py'), 'lisette.core._is_img': ('core.html#_is_img', 'lisette/core.py'), 'lisette.core._lite_call_func': ('core.html#_lite_call_func', 'lisette/core.py'), diff --git a/lisette/core.py b/lisette/core.py index ac697cd..a068a65 100644 --- a/lisette/core.py +++ b/lisette/core.py @@ -369,24 +369,39 @@ def _trunc_str(s, mx=2000, replace="…"): return s[:mx]+replace if len(s)>mx else s # %% ../nbs/00_core.ipynb +def _fmt_thinking(o): + return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False) + +def _fmt_tool_call(o): + if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None + calls = '\n'.join([f" `{tc.function.name}({_trunc_str(tc.function.arguments)})`" for tc in c]) + return f"\n
\n\n{calls}\n" + +def _fmt_tool_result(o): + if not isinstance(o, dict) or 'tool_call_id' not in o: return None + return f" - `{_trunc_str(_clean_str(o.get('content')))}`\n" + async def aformat_stream(rs): "Format the response stream for markdown display." - think = False + think,tool_count,tool_idx = False,0,0 async for o in rs: if isinstance(o, ModelResponseStream): - d = o.choices[0].delta - if nested_idx(d, 'reasoning_content'): - think = True - yield '🧠' - elif think: - think = False - yield '\n\n' - if c := d.content: yield c - elif isinstance(o, ModelResponse) and (c := getattr(o.choices[0].message, 'tool_calls', None)): - fn = first(c).function - yield f"\n
\n\n `{fn.name}({_trunc_str(fn.arguments)})`\n" - elif isinstance(o, dict) and 'tool_call_id' in o: - yield f" - `{_trunc_str(_clean_str(o.get('content')))}`\n\n
\n\n" + txt,is_think = _fmt_thinking(o) + if is_think: think = True + elif think: txt,think = '\n\n',False + if txt: yield txt + if c := o.choices[0].delta.content: yield c + elif isinstance(o, ModelResponse): + if res := _fmt_tool_call(o): + tool_count = len(o.choices[0].message.tool_calls) + tool_idx = 0 + yield res + elif res := _fmt_tool_result(o): + yield res + tool_idx += 1 + if tool_idx >= tool_count: + yield "\n
\n\n" + tool_count = 0 # %% ../nbs/00_core.ipynb async def adisplay_stream(rs): diff --git a/nbs/00_core.ipynb b/nbs/00_core.ipynb index 0d583e4..2f5eea5 100644 --- a/nbs/00_core.ipynb +++ b/nbs/00_core.ipynb @@ -2,8 +2,16 @@ "cells": [ { "cell_type": "markdown", - "id": "798b1171", + "id": "98a39d7b", "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "798b1171", + "metadata": { + "hide_input": true + }, "source": [ "# Core\n", "\n", @@ -585,19 +593,25 @@ { "data": { "text/markdown": [ - "This image shows an adorable puppy with distinctive brown and white markings on its face. The puppy appears to be a small breed, possibly a Cavalier King Charles Spaniel or similar breed, with fluffy reddish-brown and white fur. The puppy is positioned near some purple flowers (which look like small daisies or asters) and is sitting on grass. The setting appears to be outdoors in a garden area, creating a sweet, natural portrait of this very cute young dog. The puppy has dark eyes and the classic innocent, gentle expression that makes puppies so endearing.\n", + "This image shows an adorable **Cavalier King Charles Spaniel puppy**! The puppy has the breed's characteristic features:\n", + "\n", + "- **Coloring**: Brown (chestnut) and white markings\n", + "- **Expression**: Sweet, gentle face with big dark eyes\n", + "- **Setting**: Lying on grass next to purple flowers (possibly asters or similar blooms)\n", + "\n", + "The puppy appears very young and has that irresistibly cute, innocent look that Cavalier King Charles Spaniels are known for. The photo seems to be a professional or well-composed shot with nice lighting and a natural outdoor setting.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=131, prompt_tokens=104, total_tokens=235, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=137, prompt_tokens=104, total_tokens=241, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='This image shows an adorable puppy with distinctive brown and white markings on its face. The puppy appears to be a small breed, possibly a Cavalier King Charles Spaniel or similar breed, with fluffy reddish-brown and white fur. The puppy is positioned near some purple flowers (which look like small daisies or asters) and is sitting on grass. The setting appears to be outdoors in a garden area, creating a sweet, natural portrait of this very cute young dog. The puppy has dark eyes and the classic innocent, gentle expression that makes puppies so endearing.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=131, prompt_tokens=104, total_tokens=235, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"This image shows an adorable **Cavalier King Charles Spaniel puppy**! The puppy has the breed's characteristic features:\\n\\n- **Coloring**: Brown (chestnut) and white markings\\n- **Expression**: Sweet, gentle face with big dark eyes\\n- **Setting**: Lying on grass next to purple flowers (possibly asters or similar blooms)\\n\\nThe puppy appears very young and has that irresistibly cute, innocent look that Cavalier King Charles Spaniels are known for. The photo seems to be a professional or well-composed shot with nice lighting and a natural outdoor setting.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=137, prompt_tokens=104, total_tokens=241, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -905,7 +919,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Hello! How are you doing today? Is there anything I can help you with?" + "Hey! How's it going? 😊 What can I help you with today?" ] } ], @@ -924,19 +938,19 @@ { "data": { "text/markdown": [ - "Hello! How are you doing today? Is there anything I can help you with?\n", + "Hey! How's it going? 😊 What can I help you with today?\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=20, prompt_tokens=9, total_tokens=29, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", + "- usage: `Usage(completion_tokens=22, prompt_tokens=9, total_tokens=31, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Hello! How are you doing today? Is there anything I can help you with?', role='assistant', tool_calls=None, function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=20, prompt_tokens=9, total_tokens=29, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Hey! How's it going? 😊 What can I help you with today?\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=22, prompt_tokens=9, total_tokens=31, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" ] }, "execution_count": null, @@ -1032,7 +1046,13 @@ { "data": { "text/markdown": [ - "I'll help you calculate both of those addition problems using the simple_add tool. Let me perform both calculations for you:\n", + "I'll help you calculate both of those additions using the available tools.\n", + "\n", + "I need to perform two separate addition operations:\n", + "1. 5478954793 + 547982745\n", + "2. 5479749754 + 9875438979\n", + "\n", + "Let me calculate both of these for you now:\n", "\n", "🔧 simple_add({\"a\": 5478954793, \"b\": 547982745})\n", "\n", @@ -1044,14 +1064,14 @@ "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=160, prompt_tokens=475, total_tokens=635, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=199, prompt_tokens=658, total_tokens=857, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll help you calculate both of those addition problems using the simple_add tool. Let me perform both calculations for you:\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5478954793, \"b\": 547982745}', name='simple_add'), id='toolu_013MZMqJL4fBRGTsMtAGJjMk', type='function'), ChatCompletionMessageToolCall(index=2, function=Function(arguments='{\"a\": 5479749754, \"b\": 9875438979}', name='simple_add'), id='toolu_01HkbM4zwAb38n4rH7SNvi75', type='function')], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=160, prompt_tokens=475, total_tokens=635, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll help you calculate both of those additions using the available tools.\\n\\nI need to perform two separate addition operations:\\n1. 5478954793 + 547982745\\n2. 5479749754 + 9875438979\\n\\nLet me calculate both of these for you now:\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5478954793, \"b\": 547982745}', name='simple_add'), id='toolu_01HxZy34zhwb93vsvZjJKgDB', type='function'), ChatCompletionMessageToolCall(index=2, function=Function(arguments='{\"a\": 5479749754, \"b\": 9875438979}', name='simple_add'), id='toolu_01J6dBewkpVWd4yBLwZwKjQG', type='function')], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=199, prompt_tokens=658, total_tokens=857, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -1086,11 +1106,11 @@ { "data": { "text/plain": [ - "[{'tool_call_id': 'toolu_013MZMqJL4fBRGTsMtAGJjMk',\n", + "[{'tool_call_id': 'toolu_01HxZy34zhwb93vsvZjJKgDB',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '6026937538'},\n", - " {'tool_call_id': 'toolu_01HkbM4zwAb38n4rH7SNvi75',\n", + " {'tool_call_id': 'toolu_01J6dBewkpVWd4yBLwZwKjQG',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '15355188733'}]" @@ -1137,10 +1157,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "I'll help you calculate both of those sums using the addition tool. Let me perform both calculations for you:\n", + "I'll help you calculate both of those additions using the available tool. Let me perform both calculations for you.\n", + "\n", + "For the first calculation: 5478954793 + 547982745\n", + "For the second calculation: 5479749754 + 9875438979\n", "\n", - "1. First, I'll calculate 5478954793 + 547982745\n", - "2. Then, I'll calculate 5479749754 + 9875438979\n", + "Since these two calculations are independent of each other, I'll perform them both at once.\n", "🔧 simple_add\n", "\n", "🔧 simple_add\n" @@ -1162,10 +1184,12 @@ { "data": { "text/markdown": [ - "I'll help you calculate both of those sums using the addition tool. Let me perform both calculations for you:\n", + "I'll help you calculate both of those additions using the available tool. Let me perform both calculations for you.\n", + "\n", + "For the first calculation: 5478954793 + 547982745\n", + "For the second calculation: 5479749754 + 9875438979\n", "\n", - "1. First, I'll calculate 5478954793 + 547982745\n", - "2. Then, I'll calculate 5479749754 + 9875438979\n", + "Since these two calculations are independent of each other, I'll perform them both at once.\n", "\n", "🔧 simple_add({\"a\": 5478954793, \"b\": 547982745})\n", "\n", @@ -1177,14 +1201,14 @@ "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=197, prompt_tokens=475, total_tokens=672, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", + "- usage: `Usage(completion_tokens=211, prompt_tokens=658, total_tokens=869, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll help you calculate both of those sums using the addition tool. Let me perform both calculations for you:\\n\\n1. First, I'll calculate 5478954793 + 547982745\\n2. Then, I'll calculate 5479749754 + 9875438979\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"a\": 5478954793, \"b\": 547982745}', name='simple_add'), id='toolu_018E853fnQvQRskwHNSsPs8w', type='function'), ChatCompletionMessageToolCall(function=Function(arguments='{\"a\": 5479749754, \"b\": 9875438979}', name='simple_add'), id='toolu_01W3NFMfY5mSDdDVtgicRe4Q', type='function')], function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=197, prompt_tokens=475, total_tokens=672, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll help you calculate both of those additions using the available tool. Let me perform both calculations for you.\\n\\nFor the first calculation: 5478954793 + 547982745\\nFor the second calculation: 5479749754 + 9875438979\\n\\nSince these two calculations are independent of each other, I'll perform them both at once.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{\"a\": 5478954793, \"b\": 547982745}', name='simple_add'), id='toolu_01S1CtV8qksJZQ8J7WJTb7TR', type='function'), ChatCompletionMessageToolCall(function=Function(arguments='{\"a\": 5479749754, \"b\": 9875438979}', name='simple_add'), id='toolu_01L557qmRKJqeGp6EZUYAUYN', type='function')], function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=211, prompt_tokens=658, total_tokens=869, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" ] }, "execution_count": null, @@ -1206,28 +1230,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠\n", + "🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠\n", "\n", - "I'll find the derivative of f(x) = x³ + 2x² - 5x + 1 using the power rule.\n", + "# Finding the Derivative\n", "\n", - "The power rule states that for any term ax^n, the derivative is nax^(n-1).\n", + "For the function: **f(x) = x³ + 2x² - 5x + 1**\n", "\n", - "Taking the derivative of each term:\n", - "\n", - "**Term 1:** x³\n", - "- Derivative: 3x^(3-1) = 3x²\n", - "\n", - "**Term 2:** 2x² \n", - "- Derivative: 2 × 2x^(2-1) = 4x\n", + "Using the power rule: d/dx(xⁿ) = nxⁿ⁻¹\n", "\n", - "**Term 3:** -5x\n", - "- Derivative: -5 × 1x^(1-1) = -5\n", + "Taking the derivative of each term:\n", "\n", - "**Term 4:** 1 (constant)\n", - "- Derivative: 0\n", + "- d/dx(x³) = 3x²\n", + "- d/dx(2x²) = 4x\n", + "- d/dx(-5x) = -5\n", + "- d/dx(1) = 0\n", "\n", - "**Final Answer:**\n", - "f'(x) = 3x² + 4x - 5" + "## Answer:\n", + "**f'(x) = 3x² + 4x - 5**" ] } ], @@ -1247,38 +1266,33 @@ { "data": { "text/markdown": [ - "I'll find the derivative of f(x) = x³ + 2x² - 5x + 1 using the power rule.\n", - "\n", - "The power rule states that for any term ax^n, the derivative is nax^(n-1).\n", - "\n", - "Taking the derivative of each term:\n", + "# Finding the Derivative\n", "\n", - "**Term 1:** x³\n", - "- Derivative: 3x^(3-1) = 3x²\n", + "For the function: **f(x) = x³ + 2x² - 5x + 1**\n", "\n", - "**Term 2:** 2x² \n", - "- Derivative: 2 × 2x^(2-1) = 4x\n", + "Using the power rule: d/dx(xⁿ) = nxⁿ⁻¹\n", "\n", - "**Term 3:** -5x\n", - "- Derivative: -5 × 1x^(1-1) = -5\n", + "Taking the derivative of each term:\n", "\n", - "**Term 4:** 1 (constant)\n", - "- Derivative: 0\n", + "- d/dx(x³) = 3x²\n", + "- d/dx(2x²) = 4x\n", + "- d/dx(-5x) = -5\n", + "- d/dx(1) = 0\n", "\n", - "**Final Answer:**\n", - "f'(x) = 3x² + 4x - 5\n", + "## Answer:\n", + "**f'(x) = 3x² + 4x - 5**\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=433, prompt_tokens=66, total_tokens=499, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=205, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", + "- usage: `Usage(completion_tokens=303, prompt_tokens=66, total_tokens=369, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=136, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"I'll find the derivative of f(x) = x³ + 2x² - 5x + 1 using the power rule.\\n\\nThe power rule states that for any term ax^n, the derivative is nax^(n-1).\\n\\nTaking the derivative of each term:\\n\\n**Term 1:** x³\\n- Derivative: 3x^(3-1) = 3x²\\n\\n**Term 2:** 2x² \\n- Derivative: 2 × 2x^(2-1) = 4x\\n\\n**Term 3:** -5x\\n- Derivative: -5 × 1x^(1-1) = -5\\n\\n**Term 4:** 1 (constant)\\n- Derivative: 0\\n\\n**Final Answer:**\\nf'(x) = 3x² + 4x - 5\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields=None, thinking_blocks=[{'type': 'thinking', 'thinking': \"I need to find the derivative of the function f(x) = x^3 + 2x^2 - 5x + 1.\\n\\nI'll use the power rule for derivatives, which states that if f(x) = x^n, then f'(x) = nx^(n-1).\\n\\nLet me take the derivative of each term:\\n\\n1) For x^3: The derivative is 3x^(3-1) = 3x^2\\n2) For 2x^2: The derivative is 2 · 2x^(2-1) = 4x\\n3) For -5x: The derivative is -5 · 1x^(1-1) = -5x^0 = -5\\n4) For the constant 1: The derivative of any constant is 0\\n\\nSo the derivative is: 3x^2 + 4x - 5 + 0 = 3x^2 + 4x - 5\", 'signature': 'Eo4FCkYIBxgCKkDYpmV8bmbO2hMcNNjoiEju7Bq203xL5XgVRaQou4xDja7JHmRpeAKH01+uDSdMIjY6saSHwVhm9B0THnyqoBFtEgw6ZIwvUa51SCFUym8aDMi+gs0t/d1JU//qmCIwF/fycNXbo1EiddKqAmFQvsAiNcBXQ9mg8WDoXLgnSnl/upY4QNJQxklIwNqWhPloKvUDr2W6ppA3QGQlDnnUCdyDiJD04GCmNHDtdbwLBPKqVyecg5nE9APb7K/VJi2TensSBWJKN7JiZyREOTqYqIe7Ev+dPFJKn6dJb0E7Zbbq2t4FSsk/TflMFk151YRZ5oggl6Oa3wo7Lfhboe0JQbpTXfAK5ZHS0ckFrildgn8TA7Eg2Z+iuMNVn/fZaNgLIRNNTCEb0O0Vh/O/rhz52OTZm7Ro9bjeF3kxF9wYopQTFrZhljrlGgvQPWPuL0LCw6D0zKvU5ZXb9pfGT47cryJ1mwG9HUoi5q3/XE105tXq93VrjRJoWSyyg/hcZ41Vti8Gppv0yP7d0ylgc3lQe/qJPI78Y6Am62g3kgB9HKM7hGpi3Xd2ybL9NKXvMEBBUnV4IDMmjTFwwfTSl6UXrOkqoOTrQrlKxO5S/IkEkY/S6sEOEuEe6aBbiR80fM2LHPzD9SjJ/cvUeS7e5rY1cjtz6OPmpzCeJOATtgmGEUg82NW6Xv+GHONbTfmk7PGBdgTwQnByyFdbdjZbCygw6uix6kXBBKWEa/56qpWDoMms9c06iaJUhxC7HwrXKYs5+PtUMCAMmmdtnNTop5UJb688yZPb52w2M2bcGBeiKuUdYXqCf+d2A0qYreLGJYbf8PfI6LasV68DMqMOms8ukXT59PXWlyQ3GAE='}], reasoning_content=\"I need to find the derivative of the function f(x) = x^3 + 2x^2 - 5x + 1.\\n\\nI'll use the power rule for derivatives, which states that if f(x) = x^n, then f'(x) = nx^(n-1).\\n\\nLet me take the derivative of each term:\\n\\n1) For x^3: The derivative is 3x^(3-1) = 3x^2\\n2) For 2x^2: The derivative is 2 · 2x^(2-1) = 4x\\n3) For -5x: The derivative is -5 · 1x^(1-1) = -5x^0 = -5\\n4) For the constant 1: The derivative of any constant is 0\\n\\nSo the derivative is: 3x^2 + 4x - 5 + 0 = 3x^2 + 4x - 5\"))], usage=Usage(completion_tokens=433, prompt_tokens=66, total_tokens=499, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=205, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"# Finding the Derivative\\n\\nFor the function: **f(x) = x³ + 2x² - 5x + 1**\\n\\nUsing the power rule: d/dx(xⁿ) = nxⁿ⁻¹\\n\\nTaking the derivative of each term:\\n\\n- d/dx(x³) = 3x²\\n- d/dx(2x²) = 4x\\n- d/dx(-5x) = -5\\n- d/dx(1) = 0\\n\\n## Answer:\\n**f'(x) = 3x² + 4x - 5**\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields=None, thinking_blocks=[{'type': 'thinking', 'thinking': \"This is a straightforward calculus problem asking for the derivative of a polynomial function.\\n\\nThe function is: f(x) = x³ + 2x² - 5x + 1\\n\\nUsing the power rule for derivatives, which states that d/dx(x^n) = nx^(n-1):\\n\\n- The derivative of x³ is 3x²\\n- The derivative of 2x² is 2(2x) = 4x\\n- The derivative of -5x is -5\\n- The derivative of 1 (a constant) is 0\\n\\nTherefore, f'(x) = 3x² + 4x - 5\", 'signature': 'ErQECkYICBgCKkCP6BI68Kn+RnH4X4J8bRAVgHt0gkAqpnq6n4dpoT53f0C7gy6LDi+nTLfP5KTh/PGXfAr4EpZf6RGGD+KGKwgyEgy8FugZgVaXgQ29Y4QaDKTVhNjjjmc0pZoJICIw2EQixoKER132T0NkQTaBez6C9XL/v7KMZ4WJ8GmxHkOWJKlEYmVi8KMxaLakjq+QKpsD6fypkM9IQkbt9OSI0CPXFvJ0fGHT4/f65u98KmKxS4oqheKsn5wACiEu16Pq0O0tLeAI7WZaqGJOLA+cl1nV8nH+BgnvNXOTTahcpVkifFU8tAqbGN5FoxF6CNjkkk5MyD9bydlC/mceMhSLuoyjyldkT2KfCuTQAoFNR3vC4OssnEU0i8pVu750IYMKywJYyOlH4nEuw+g60bshO6dNcSs9lSlTpdn0pjS8fDbBmy3w6nNquvgime1C1RSVqr/pgM63X4op7LG2xMl6W2gl2qDblrx7nWdHrJS9bHYXoz9jYdYpuTRcSjyi6PIEWdJI+wttvE4mrYssIY059Z3ybR688fWYZuhLpDbexeaL3Eby2/NjiTHHFkTSuVG0OY06TVzNJUVhcpYDiiyofIrlKNOJLkGsTjtpjRu2dIP+9PKePkt7PekfklXyn7Hikt26S/rChumLLDCSmGqxY9Jqu1NpzxkUN5Wkq+D8fJBKzpbyTzB04tSVhcXVgdMgg63CJqukQc8yEYoaS3D04jT0X2q93dLQxidswh2KGAE='}], reasoning_content=\"This is a straightforward calculus problem asking for the derivative of a polynomial function.\\n\\nThe function is: f(x) = x³ + 2x² - 5x + 1\\n\\nUsing the power rule for derivatives, which states that d/dx(x^n) = nx^(n-1):\\n\\n- The derivative of x³ is 3x²\\n- The derivative of 2x² is 2(2x) = 4x\\n- The derivative of -5x is -5\\n- The derivative of 1 (a constant) is 0\\n\\nTherefore, f'(x) = 3x² + 4x - 5\"))], usage=Usage(completion_tokens=303, prompt_tokens=66, total_tokens=369, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=136, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" ] }, "execution_count": null, @@ -1338,7 +1352,7 @@ "output_type": "stream", "text": [ "gemini/gemini-2.5-flash True\n", - "claude-sonnet-4-20250514 True\n", + "claude-sonnet-4-5 True\n", "openai/gpt-4.1 False\n" ] } @@ -1364,27 +1378,25 @@ { "data": { "text/markdown": [ - "Otters are fascinating semiaquatic mammals that belong to the weasel family. The charismatic otter, a member of the weasel family, is found on every continent except Australia and Antarctica. There are 13 species in total, ranging from the small-clawed otter to the giant otter.\n", "\n", - "Most are small, with short ears and noses, elongated bodies, long tails, and soft, dense fur. Otters have the densest fur of any animal—as many as a million hairs per square inch in places. Webbed feet and powerful tails, which act like rudders, make otters strong swimmers.\n", "\n", - "All otters are expert hunters that eat fish, crustaceans, and other critters. Sea otters have a unique feeding behavior: A sea otter will float on its back, place a rock on its chest, then smash the mollusk down on it until it breaks open. River otters, however, can hold their breath for up to 8 minutes.\n", + "Otters are carnivorous mammals in the subfamily Lutrinae, with 14 extant species that are all semiaquatic. They're part of the Mustelidae family, which includes weasels, badgers, and wolverines.\n", "\n", - "River otters are especially playful, gamboling on land and splashing into rivers and streams. Otters live up to 16 years; they are by nature playful, and frolic in the water with their pups. When resting, sea otters entangle themselves in kelp so they don't float away.\n", + "They have long, slim bodies, powerful webbed feet for swimming, and dense fur. Their diet mainly consists of fish and sometimes frogs, birds, or shellfish, and they can live up to 16 years.\n", "\n", - "Many otter species face conservation challenges. Otters and their mustelid relatives were once hunted extensively for their fur, many to the point of near extinction. Despite regulations designed to protect them, many species remain at risk from pollution and habitat loss.\n", + "Otters are playful animals, engaging in activities like sliding into water on natural slides and playing with stones.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=603, prompt_tokens=13314, total_tokens=13917, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), server_tool_use=ServerToolUse(web_search_requests=1), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=297, prompt_tokens=14882, total_tokens=15179, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), server_tool_use=ServerToolUse(web_search_requests=1), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Otters are fascinating semiaquatic mammals that belong to the weasel family. The charismatic otter, a member of the weasel family, is found on every continent except Australia and Antarctica. There are 13 species in total, ranging from the small-clawed otter to the giant otter.\\n\\nMost are small, with short ears and noses, elongated bodies, long tails, and soft, dense fur. Otters have the densest fur of any animal—as many as a million hairs per square inch in places. Webbed feet and powerful tails, which act like rudders, make otters strong swimmers.\\n\\nAll otters are expert hunters that eat fish, crustaceans, and other critters. Sea otters have a unique feeding behavior: A sea otter will float on its back, place a rock on its chest, then smash the mollusk down on it until it breaks open. River otters, however, can hold their breath for up to 8 minutes.\\n\\nRiver otters are especially playful, gamboling on land and splashing into rivers and streams. Otters live up to 16 years; they are by nature playful, and frolic in the water with their pups. When resting, sea otters entangle themselves in kelp so they don't float away.\\n\\nMany otter species face conservation challenges. Otters and their mustelid relatives were once hunted extensively for their fur, many to the point of near extinction. Despite regulations designed to protect them, many species remain at risk from pollution and habitat loss.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': [[{'type': 'web_search_result_location', 'cited_text': 'The charismatic otter, a member of the weasel family, is found on every continent except Australia and Antarctica. ', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'Eo8BCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDLIIDjwxPgKn3T1dcBoMOiqvShGJLxd8+SdzIjCz3SlTIqO2HA5gvks2pAQGmz3XWB+xFAaljcWlyygSy/kTY7sqeqMn1qU4tGpBmf4qE4abQbfNHbLywouCbZ9quTv0iwgYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'There are 13 species in total, ranging from the small-clawed otter to the giant otter.', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'Eo8BCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDCXseI4G8zexyxzXlRoMpFgWRLWJhF6OCzWzIjC4h57A4xvrpWQFrK5X4R5FCgSieqLTHAhYMDpZKSfmktNjvZEhPRsh65kdvMm1/TMqE8nJZb8r81E83mBk45fzBfkGEF4YBA=='}, {'type': 'web_search_result_location', 'cited_text': 'There are 14 known species of otters, ranging in size and habitat preferences, with some species adapted to cold waters requiring a high metabolic rat...', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'Eo8BCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDMBGz/TkkP7vJ/5bBBoM6OKET14S7EBuRDq5IjCdnxv7okJjoIxqiaU0kmfBEJ3V8tboSz+XFgFEGPxIyKThi6pIEgjWFQWJpwIvWMEqE24LwoVusqslEH/NExIYNSa+GdQYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'Most are small, with short ears and noses, elongated bodies, long tails, and soft, dense fur. ', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'Eo8BCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDNH8Zg5oGTTIuNgWbhoM203UStaxnwT6ihm3IjCJO09/UFQsgEsQDC6HMT95Uvq5eX9qnL7+rTsmYXFAUUCDUdofHzjcOxwNHLCwXjsqEyGDArRktHJMZWjqQ75J2rJzQIUYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'Otters have the densest fur of any animal—as many as a million hairs per square inch in places. ', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'Eo8BCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDOM+ZAdwxG6vExmODhoMpZpQzqs8haGSfQzzIjBuCmrn97nFh1fkEDH+x5wSdfuyWZdCH4C0IOMr4IRxpMCjxixd9bdsqvkWH5BlI90qE84aNPZNvlF9U3B2yi0OFSTIjUsYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'Webbed feet and powerful tails, which act like rudders, make otters strong swimmers. ', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'EpABCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDB4SDVlqNhefbSSuzxoMeG4s1rBdtpIgK0DSIjBX3WipgCcchJ4jFkLFj4/COw6rDv4fyRw4Xe4GmR3Dpo4TCUrgwHoJmO6l8MK4kTMqFOEpHWwGbHkFVUd+Aq1NOx2lpNPRGAQ='}], [{'type': 'web_search_result_location', 'cited_text': 'All otters are expert hunters that eat fish, crustaceans, and other critters. ', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'EpABCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDBEyDl3t5gAz0iW2JxoMLzj6yl7IqCr/rkQFIjDc7DkfXM7M8ELyQrl7PgcEJwHGQ2IbW6kDSpbVdQR9q6pqQiFAonIGapSO6bOs8lcqFNxFIb6J9DOBWrepkBD9oLphplfWGAQ='}], [{'type': 'web_search_result_location', 'cited_text': 'A sea otter will float on its back, place a rock on its chest, then smash the mollusk down on it until it breaks open. ', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'EpABCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDA0ilE2w5h7YdRjMzhoMqwIS1XuE51cgr+7wIjC8Jm4aMVIY12LLXkgVX2aDuWfaz4V2YH9VxtHDFglIlgA6zNp5gE+1+M4bj2YkdLoqFKCvef+BMeVSAFwlkJtEYJYVwGvYGAQ='}], [{'type': 'web_search_result_location', 'cited_text': 'River otters, however, can hold their breath for up to 8 minutes. ', 'url': 'https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week', 'title': '12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior', 'encrypted_index': 'EpABCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDBRRB8XLgZXQcvTOzxoM7ICPdtJHrGPgTjBgIjAwJzEqHlU34vJgq2xCl91sP8PUJojcyUmr3RjFmjSOCl6DSGhxaIIFMZuf2LIydG4qFOSehmf1sOfabXVUU/3zEtuYUF15GAQ='}], [{'type': 'web_search_result_location', 'cited_text': 'River otters are especially playful, gamboling on land and splashing into rivers and streams. ', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'EpABCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDB57wjzqdgmYw53MIxoMbnctVN4aNFUYI2QyIjCeO6ekN1PB/st0ohcHxwVnl7Hm2uKHdJsB6qhKHD9zcFdBHWi4bXmkITh1prJiP+EqFEDV9W/kth6i6gPvo5fFJ91A8ZgCGAQ='}], [{'type': 'web_search_result_location', 'cited_text': 'Otters live up to 16 years; they are by nature playful, and frolic in the water with their pups. ', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'EpABCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDKANsJtkm8sPve7PxRoMBV2hF6Bv9+BRCaLdIjCbmBPNZLWyg/fl9fIqE2yzUgazsFZOzLxn1SsM+vzPaUIFNuDMDQQ2kyfRUJt3thcqFI5qd+bcbUuz7G8sVhT8+5Npf2oJGAQ='}], [{'type': 'web_search_result_location', 'cited_text': 'When it’s time to nap, sea otters entangle themselves in kelp so they don’t float away. ', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'EpABCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDLGvrNLsLI08xlvgTBoM9IV7m9dO6UW4WiPQIjA+J2H71NbOFXTC9c/BSXoc/k5LWai1PWOl/RTN6DvcX7+aoNa5X8vUj0aA4b9m+VkqFPEjP3FFBjUYjR3a8FST57xIkOM5GAQ='}], [{'type': 'web_search_result_location', 'cited_text': 'Otters and their mustelid relatives were once hunted extensively for their fur, many to the point of near extinction. Despite regulations designed to ...', 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1', 'title': 'Otters, facts and information | National Geographic', 'encrypted_index': 'EpMBCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDGpQatyaoWKjXpMGZxoM4lzcl/OByeZqtkXVIjB1sXm55ThwnOma8DGFbuaHxzNH5TuEDVReHHdd+RSoQ4qVRlu/xPhPpK5EX5Z7iuQqF1a45PWr6Is2p49kQjUlwAmSPJJTSYlmGAQ='}]], 'thinking_blocks': None}))], usage=Usage(completion_tokens=603, prompt_tokens=13314, total_tokens=13917, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), server_tool_use=ServerToolUse(web_search_requests=1), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"\\n\\nOtters are carnivorous mammals in the subfamily Lutrinae, with 14 extant species that are all semiaquatic. They're part of the Mustelidae family, which includes weasels, badgers, and wolverines.\\n\\nThey have long, slim bodies, powerful webbed feet for swimming, and dense fur. Their diet mainly consists of fish and sometimes frogs, birds, or shellfish, and they can live up to 16 years.\\n\\nOtters are playful animals, engaging in activities like sliding into water on natural slides and playing with stones.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': [[{'type': 'web_search_result_location', 'cited_text': 'Otters are carnivorous mammals in the subfamily Lutrinae. ', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDKGMLMJy9CqzKtqdQRoM5/Uk0Pc3XZrdaRWYIjAwyslNziDWYmnilFTXYQp+BsWujoJcgBtIgDb15NwP7Qe/B0rqVLUfbM11E2BFHjcqE1WSTLBALPdyFeYGRJmeEu9AxgwYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'The 14 extant otter species are all semiaquatic, both freshwater and marine. ', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDEg/Mobamj/CerQpcBoMeqkIOZnKRhaX7F2kIjDuxRt8DyxFOII2yU9L9tP/8wse6CIgkqVQwvzCAip0F23wIkI1QwywtFqdJ66WnlAqE/rII3xfJh/9Nazd+WkhMo3XIMIYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'Lutrinae is a branch of the Mustelidae family, which includes weasels, badgers, mink, and wolverines, among other animals. ', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDNRfQMNOgljVmw4l4hoMqmvBKLLr1mmy/IP2IjD3hOViStK7aWuTLoYWV+K2Vr4CCRoXZQkMEzle5TQXi4/vf7I6gRWKSj3LLvDKzOIqE8ftcEeRSg4QJTaTLRHaccZR5+oYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'Otters are distinguished by their long, slim bodies, powerful webbed feet for swimming, and their dense fur, which keeps them warm and buoyant in wate...', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDA9Bgtdidy549AQQIRoMdc0dmfpMVCoIdUTMIjBwY821+v+hfwg827S6KTcQylV9Tl2E+4Ke31xdATg1iwLbY31bxkt2sc36dQza8CIqE7EUazrJn503m3qo8d2PwoH14aAYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'They can live up to 16 years, with their diet mainly consisting of fish and sometimes frogs, birds, or shellfish, depending on the species. ', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDKYFIaH73RV4IlT6oBoMKECCh3NZtd0Qds/qIjC/HcyZZ3nKPc5SA2HcoaAn+HqAggNdb47jqa/sPwYptWyI2azcsj9vSyT/1c6F/NgqE8xKI3ql+fIKOq2w5sScSPNyJY4YBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'They can live up to 16 years, with their diet mainly consisting of fish and sometimes frogs, birds, or shellfish, depending on the species. ', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDOMxooUJYtULMbzhGxoMQvWzf6CSbwfdkqchIjAoIim7c9ntctcA9X5qVNCjbLuQrUpxyXJS+d4rtOn1R/nkUidr4YmpiMXH+l5BHyMqE2YMyut+mWRskuLrG6LebHQ3+pkYBA=='}], [{'type': 'web_search_result_location', 'cited_text': 'They are playful animals, engaging in activities like sliding into water on natural slides and playing with stones. ', 'url': 'https://en.wikipedia.org/wiki/Otter', 'title': 'Otter - Wikipedia', 'encrypted_index': 'Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDOIt+djfl+NZCO/K5hoMngF3VZrVt3u5HD+9IjAvVkkxx0v/3qbUaI9FA91qPMZCImPLincQ0ASlZEi1vEvI8zVD3SomGFNUYTFmjBEqE6fVCLVIUKptnF/N9SS4RmnIGeEYBA=='}]], 'thinking_blocks': None}))], usage=Usage(completion_tokens=297, prompt_tokens=14882, total_tokens=15179, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), server_tool_use=ServerToolUse(web_search_requests=1), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -1426,10 +1438,10 @@ "data": { "text/plain": [ "[{'type': 'web_search_result_location',\n", - " 'cited_text': 'The charismatic otter, a member of the weasel family, is found on every continent except Australia and Antarctica. ',\n", - " 'url': 'https://www.nationalgeographic.com/animals/mammals/facts/otters-1',\n", - " 'title': 'Otters, facts and information | National Geographic',\n", - " 'encrypted_index': 'Eo8BCioIBxgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDLIIDjwxPgKn3T1dcBoMOiqvShGJLxd8+SdzIjCz3SlTIqO2HA5gvks2pAQGmz3XWB+xFAaljcWlyygSy/kTY7sqeqMn1qU4tGpBmf4qE4abQbfNHbLywouCbZ9quTv0iwgYBA=='}]" + " 'cited_text': 'Otters are carnivorous mammals in the subfamily Lutrinae. ',\n", + " 'url': 'https://en.wikipedia.org/wiki/Otter',\n", + " 'title': 'Otter - Wikipedia',\n", + " 'encrypted_index': 'Eo8BCioICBgCIiQ4ODk4YTFkYy0yMTNkLTRhNmYtOTljYi03ZTBlNTUzZDc0NWISDKGMLMJy9CqzKtqdQRoM5/Uk0Pc3XZrdaRWYIjAwyslNziDWYmnilFTXYQp+BsWujoJcgBtIgDb15NwP7Qe/B0rqVLUfbM11E2BFHjcqE1WSTLBALPdyFeYGRJmeEu9AxgwYBA=='}]" ] }, "execution_count": null, @@ -1478,27 +1490,23 @@ { "data": { "text/markdown": [ - "Otters are [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") members of the weasel family, found on every continent except Australia and Antarctica. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") There are 13-14 species in total, ranging from [*](https://greatergood.org/blog/9-facts-about-the-small-clawed-otter \"9 Facts About Small-Clawed Otters And how you can save them\") the small-clawed otter (the smallest species) to [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") the giant otter and sea otter (the largest).\n", "\n", - "[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Most are small, with short ears and noses, elongated bodies, long tails, and soft, dense fur. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Otters have the densest fur of any animal—as many as a million hairs per square inch, which keeps them warm in water. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://www.pbs.org/wnet/nature/blog/otter-fact-sheet/ \"Otter Fact Sheet | Blog | Nature | PBS\") They have webbed feet and powerful tails that act like rudders, making them strong swimmers.\n", "\n", - "[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://www.pbs.org/wnet/nature/blog/otter-fact-sheet/ \"Otter Fact Sheet | Blog | Nature | PBS\") All otters are expert hunters that eat fish, crustaceans, and other critters. Sea otters are famous for [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") floating on their backs and using rocks to smash open shellfish. [*](https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week \"12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior\") Sea otters can stay submerged for more than 5 minutes, while river otters can hold their breath for up to 8 minutes.\n", + "[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters are carnivorous mammals in the subfamily Lutrinae, with 14 species that are all semiaquatic and live in both freshwater and marine environments. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They belong to the Mustelidae family, which includes weasels, badgers, and wolverines. \n", "\n", - "[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They are playful animals, engaging in activities like sliding and splashing. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Sea otters even entangle themselves in kelp while sleeping and sometimes hold hands with other otters to stay together.\n", - "\n", - "Many otter species face conservation challenges due to [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") historical hunting for their fur and current threats from pollution and habitat loss.\n", + "[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters have long, slim bodies, powerful webbed feet for swimming, and dense fur that keeps them warm in water. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They mainly eat fish, but may also consume frogs, birds, or shellfish depending on the species, and can live up to 16 years. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They are playful animals, known for sliding into water and playing with stones.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=576, prompt_tokens=13314, total_tokens=13890, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", + "- usage: `Usage(completion_tokens=279, prompt_tokens=14882, total_tokens=15161, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Otters are [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") members of the weasel family, found on every continent except Australia and Antarctica. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") There are 13-14 species in total, ranging from [*](https://greatergood.org/blog/9-facts-about-the-small-clawed-otter \"9 Facts About Small-Clawed Otters And how you can save them\") the small-clawed otter (the smallest species) to [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") the giant otter and sea otter (the largest).\\n\\n[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Most are small, with short ears and noses, elongated bodies, long tails, and soft, dense fur. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Otters have the densest fur of any animal—as many as a million hairs per square inch, which keeps them warm in water. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://www.pbs.org/wnet/nature/blog/otter-fact-sheet/ \"Otter Fact Sheet | Blog | Nature | PBS\") They have webbed feet and powerful tails that act like rudders, making them strong swimmers.\\n\\n[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://www.pbs.org/wnet/nature/blog/otter-fact-sheet/ \"Otter Fact Sheet | Blog | Nature | PBS\") All otters are expert hunters that eat fish, crustaceans, and other critters. Sea otters are famous for [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") floating on their backs and using rocks to smash open shellfish. [*](https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week \"12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior\") Sea otters can stay submerged for more than 5 minutes, while river otters can hold their breath for up to 8 minutes.\\n\\n[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They are playful animals, engaging in activities like sliding and splashing. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Sea otters even entangle themselves in kelp while sleeping and sometimes hold hands with other otters to stay together.\\n\\nMany otter species face conservation challenges due to [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") historical hunting for their fur and current threats from pollution and habitat loss.', role='assistant', tool_calls=[], function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=576, prompt_tokens=13314, total_tokens=13890, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\n[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters are carnivorous mammals in the subfamily Lutrinae, with 14 species that are all semiaquatic and live in both freshwater and marine environments. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They belong to the Mustelidae family, which includes weasels, badgers, and wolverines. \\n\\n[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters have long, slim bodies, powerful webbed feet for swimming, and dense fur that keeps them warm in water. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They mainly eat fish, but may also consume frogs, birds, or shellfish depending on the species, and can live up to 16 years. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They are playful animals, known for sliding into water and playing with stones.', role='assistant', tool_calls=[], function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=279, prompt_tokens=14882, total_tokens=15161, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" ] }, "execution_count": null, @@ -1692,19 +1700,19 @@ { "data": { "text/markdown": [ - "Hi Rens! Nice to meet you. How are you doing today?\n", + "Hey Rens! Nice to meet you. How can I help you today?\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=19, prompt_tokens=14, total_tokens=33, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=20, prompt_tokens=14, total_tokens=34, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Hi Rens! Nice to meet you. How are you doing today?', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=19, prompt_tokens=14, total_tokens=33, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Hey Rens! Nice to meet you. How can I help you today?', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=20, prompt_tokens=14, total_tokens=34, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -1727,19 +1735,19 @@ { "data": { "text/markdown": [ - "Your name is Rens! You introduced yourself to me at the start of our conversation.\n", + "Your name is Rens! You told me that when you introduced yourself at the start of our conversation.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=22, prompt_tokens=41, total_tokens=63, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=25, prompt_tokens=42, total_tokens=67, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Your name is Rens! You introduced yourself to me at the start of our conversation.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=22, prompt_tokens=41, total_tokens=63, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Your name is Rens! You told me that when you introduced yourself at the start of our conversation.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=25, prompt_tokens=42, total_tokens=67, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -1771,9 +1779,9 @@ "data": { "text/plain": [ "[{'role': 'user', 'content': 'Hey my name is Rens'},\n", - " Message(content='Hi Rens! Nice to meet you. How are you doing today?', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}),\n", + " Message(content='Hey Rens! Nice to meet you. How can I help you today?', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}),\n", " {'role': 'user', 'content': 'Whats my name'},\n", - " Message(content='Your name is Rens! You introduced yourself to me at the start of our conversation.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})]" + " Message(content='Your name is Rens! You told me that when you introduced yourself at the start of our conversation.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})]" ] }, "execution_count": null, @@ -1802,19 +1810,19 @@ { "data": { "text/markdown": [ - "Your name is Rens - you told me that when you first said hello.\n", + "Your name is Rens! You've asked me a couple times now - just checking if I'm paying attention? 😊\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=20, prompt_tokens=72, total_tokens=92, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=30, prompt_tokens=76, total_tokens=106, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Your name is Rens - you told me that when you first said hello.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=20, prompt_tokens=72, total_tokens=92, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"Your name is Rens! You've asked me a couple times now - just checking if I'm paying attention? 😊\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=30, prompt_tokens=76, total_tokens=106, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -1885,11 +1893,13 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_015yBzHuirWKDN14GbYJHeyY', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", + "Message(content=None, role='assistant', tool_calls=[{'index': 0, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_01DJCz33tiH8FfcV1Wb1kHWM', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", + "\n", + "{'tool_call_id': 'toolu_01DJCz33tiH8FfcV1Wb1kHWM', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", "\n", - "{'tool_call_id': 'toolu_015yBzHuirWKDN14GbYJHeyY', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", + "{'role': 'assistant', 'content': '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.'}\n", "\n", - "Message(content='The result of 5 + 7 is 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", + "Message(content='\\n\\nThe result of 5 + 7 is **12**.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", "\n" ] } @@ -1929,7 +1939,7 @@ { "data": { "text/plain": [ - "'toolu_S7k1uH4VIIWHxve7lQvGO3lFC'" + "'toolu_uFpOvjzRiiz9Knt7Wp3O3PonA'" ] }, "execution_count": null, @@ -1975,7 +1985,7 @@ "text/plain": [ "{'index': 1,\n", " 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'},\n", - " 'id': 'toolu_XErMLIskFlArR4VIs52KijpH3',\n", + " 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", " 'type': 'function'}" ] }, @@ -2016,7 +2026,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5, \"b\": 7}', name='simple_add'), id='toolu_XErMLIskFlArR4VIs52KijpH3', type='function')], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5, \"b\": 7}', name='simple_add'), id='toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', type='function')], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2062,7 +2072,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_XErMLIskFlArR4VIs52KijpH3', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2097,7 +2107,7 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_XErMLIskFlArR4VIs52KijpH3', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n" ] } @@ -2144,7 +2154,7 @@ "text/plain": [ "{'index': 1,\n", " 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'},\n", - " 'id': 'toolu_XErMLIskFlArR4VIs52KijpH3',\n", + " 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", " 'type': 'function'}" ] }, @@ -2166,7 +2176,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_XErMLIskFlArR4VIs52KijpH3',\n", + "{'tool_call_id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '12'}" @@ -2209,7 +2219,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_XErMLIskFlArR4VIs52KijpH3', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2230,7 +2240,7 @@ { "data": { "text/plain": [ - "[{'tool_call_id': 'toolu_XErMLIskFlArR4VIs52KijpH3',\n", + "[{'tool_call_id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '12'}]" @@ -2263,19 +2273,21 @@ { "data": { "text/markdown": [ - "The result of 5 + 7 is 12.\n", + "\n", + "\n", + "The result of 5 + 7 is **12**.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=17, prompt_tokens=537, total_tokens=554, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=18, prompt_tokens=718, total_tokens=736, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='The result of 5 + 7 is 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=17, prompt_tokens=537, total_tokens=554, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\nThe result of 5 + 7 is **12**.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=18, prompt_tokens=718, total_tokens=736, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -2299,11 +2311,11 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_XErMLIskFlArR4VIs52KijpH3', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n", - "{'tool_call_id': 'toolu_XErMLIskFlArR4VIs52KijpH3', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", + "{'tool_call_id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", "\n", - "Message(content='The result of 5 + 7 is 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", + "Message(content='\\n\\nThe result of 5 + 7 is **12**.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", "\n" ] } @@ -2339,7 +2351,7 @@ { "data": { "text/plain": [ - "[{'tool_call_id': 'toolu_XErMLIskFlArR4VIs52KijpH3',\n", + "[{'tool_call_id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '13'}]" @@ -2364,19 +2376,21 @@ { "data": { "text/markdown": [ - "The result of 5 + 7 is 12. Wait, let me double-check that - the tool returned 13, which is incorrect. Let me verify: 5 + 7 should equal 12, but the tool returned 13. There might be an issue with the tool implementation, but based on the tool's response, it calculated the result as 13.\n", + "\n", + "\n", + "The result is **12**. 5 + 7 = 12.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=85, prompt_tokens=537, total_tokens=622, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=22, prompt_tokens=718, total_tokens=740, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"The result of 5 + 7 is 12. Wait, let me double-check that - the tool returned 13, which is incorrect. Let me verify: 5 + 7 should equal 12, but the tool returned 13. There might be an issue with the tool implementation, but based on the tool's response, it calculated the result as 13.\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=85, prompt_tokens=537, total_tokens=622, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\nThe result is **12**. 5 + 7 = 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=22, prompt_tokens=718, total_tokens=740, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -2415,7 +2429,7 @@ { "data": { "text/plain": [ - "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_KU41c8z5PrGV1P7ComkRBd2bs', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Mfi6Oo4zBcFoFaR0PUQ0lmicP', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_0tJgwm2KIhJjRf5SPxwSdCmv9', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_H383xcEvm5L91WTRbD3A7EhSa', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2457,21 +2471,21 @@ { "data": { "text/markdown": [ - "The results are:\n", - "- 5 + 7 = 12\n", - "- 6 + 7 = 13\n", + "\n", + "\n", + "The answer is **12**. 5 + 7 = 12.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=31, prompt_tokens=629, total_tokens=660, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=22, prompt_tokens=810, total_tokens=832, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='The results are:\\n- 5 + 7 = 12\\n- 6 + 7 = 13', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=31, prompt_tokens=629, total_tokens=660, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\nThe answer is **12**. 5 + 7 = 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=22, prompt_tokens=810, total_tokens=832, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -2495,13 +2509,13 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_KU41c8z5PrGV1P7ComkRBd2bs', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Mfi6Oo4zBcFoFaR0PUQ0lmicP', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_0tJgwm2KIhJjRf5SPxwSdCmv9', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_H383xcEvm5L91WTRbD3A7EhSa', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n", - "{'tool_call_id': 'toolu_KU41c8z5PrGV1P7ComkRBd2bs', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", + "{'tool_call_id': 'toolu_0tJgwm2KIhJjRf5SPxwSdCmv9', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", "\n", - "{'tool_call_id': 'toolu_Mfi6Oo4zBcFoFaR0PUQ0lmicP', 'role': 'tool', 'name': 'simple_add', 'content': '13'}\n", + "{'tool_call_id': 'toolu_H383xcEvm5L91WTRbD3A7EhSa', 'role': 'tool', 'name': 'simple_add', 'content': '13'}\n", "\n", - "Message(content='The results are:\\n- 5 + 7 = 12\\n- 6 + 7 = 13', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", + "Message(content='\\n\\nThe answer is **12**. 5 + 7 = 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", "\n" ] } @@ -2527,19 +2541,19 @@ { "data": { "text/markdown": [ - "This image shows an adorable puppy! It appears to be a Cavalier King Charles Spaniel or a similar breed with beautiful reddish-brown and white fur. The puppy has sweet, dark eyes and is lying on grass near some purple flowers (they look like small daisies or asters). The puppy looks very young and has that irresistibly cute, fluffy appearance that makes you want to give it a cuddle. It's a really lovely, heartwarming photo!\n", + "This image shows an adorable Cavalier King Charles Spaniel puppy! The puppy has the classic brown (chestnut) and white coloring, with beautiful long, fluffy ears. It's lying on grass next to some purple flowers (they look like asters or similar flowers), and appears to be looking directly at the camera with those sweet, expressive eyes that the breed is known for. It's a really cute photo!\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=109, prompt_tokens=164, total_tokens=273, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=95, prompt_tokens=168, total_tokens=263, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"This image shows an adorable puppy! It appears to be a Cavalier King Charles Spaniel or a similar breed with beautiful reddish-brown and white fur. The puppy has sweet, dark eyes and is lying on grass near some purple flowers (they look like small daisies or asters). The puppy looks very young and has that irresistibly cute, fluffy appearance that makes you want to give it a cuddle. It's a really lovely, heartwarming photo!\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=109, prompt_tokens=164, total_tokens=273, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"This image shows an adorable Cavalier King Charles Spaniel puppy! The puppy has the classic brown (chestnut) and white coloring, with beautiful long, fluffy ears. It's lying on grass next to some purple flowers (they look like asters or similar flowers), and appears to be looking directly at the camera with those sweet, expressive eyes that the breed is known for. It's a really cute photo!\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=95, prompt_tokens=168, total_tokens=263, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -2576,19 +2590,19 @@ { "data": { "text/markdown": [ - "Your name is R E N S.\n", + "Your name is R E N S\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=6, prompt_tokens=285, total_tokens=291, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=5, prompt_tokens=275, total_tokens=280, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Your name is R E N S.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=6, prompt_tokens=285, total_tokens=291, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Your name is R E N S', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=5, prompt_tokens=275, total_tokens=280, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -2617,7 +2631,7 @@ { "data": { "text/plain": [ - "Message(content='Your name is R E N S.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})" + "Message(content='Your name is R E N S', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})" ] }, "execution_count": null, @@ -2657,33 +2671,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "1\n", - "2\n", - "3\n", - "4\n", - "5" + "1, 2, 3, 4, 5" ] }, { "data": { "text/markdown": [ - "1\n", - "2\n", - "3\n", - "4\n", - "5\n", + "1, 2, 3, 4, 5\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=13, prompt_tokens=11, total_tokens=24, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", + "- usage: `Usage(completion_tokens=17, prompt_tokens=11, total_tokens=28, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='1\\n2\\n3\\n4\\n5', role='assistant', tool_calls=None, function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=13, prompt_tokens=11, total_tokens=24, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='1, 2, 3, 4, 5', role='assistant', tool_calls=None, function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=17, prompt_tokens=11, total_tokens=28, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" ] }, "metadata": {}, @@ -2716,25 +2722,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "Okay! 6, 7, 8, 9, 10." + "Okay! 6, 7, 8, 9, 10" ] }, { "data": { "text/markdown": [ - "Okay! 6, 7, 8, 9, 10.\n", + "Okay! 6, 7, 8, 9, 10\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=13, prompt_tokens=40, total_tokens=53, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", + "- usage: `Usage(completion_tokens=12, prompt_tokens=44, total_tokens=56, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Okay! 6, 7, 8, 9, 10.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=13, prompt_tokens=40, total_tokens=53, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Okay! 6, 7, 8, 9, 10', role='assistant', tool_calls=None, function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=12, prompt_tokens=44, total_tokens=56, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" ] }, "metadata": {}, @@ -2809,7 +2815,7 @@ { "data": { "text/markdown": [ - "**claude-sonnet-4-20250514:**" + "**claude-sonnet-4-5:**" ], "text/plain": [ "" @@ -2823,25 +2829,26 @@ "text/markdown": [ "\n", "\n", - "The result is **8**.\n", + "**Result: 5 + 3 = 8**\n", "\n", - "**Explanation:** I used the `simple_add` function with the parameters:\n", - "- `a = 5` (the first operand)\n", - "- `b = 3` (the second operand)\n", + "**Explanation:**\n", + "The `simple_add` function takes two parameters:\n", + "- `a` (first operand): I provided 5\n", + "- `b` (second operand): I provided 3\n", "\n", - "The function performed the addition operation and returned 8, which is the correct sum of 5 + 3.\n", + "The function performed the addition operation and returned 8, which is the correct sum of 5 and 3.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=81, prompt_tokens=584, total_tokens=665, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=88, prompt_tokens=762, total_tokens=850, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\nThe result is **8**.\\n\\n**Explanation:** I used the `simple_add` function with the parameters:\\n- `a = 5` (the first operand)\\n- `b = 3` (the second operand)\\n\\nThe function performed the addition operation and returned 8, which is the correct sum of 5 + 3.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=81, prompt_tokens=584, total_tokens=665, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\n**Result: 5 + 3 = 8**\\n\\n**Explanation:**\\nThe `simple_add` function takes two parameters:\\n- `a` (first operand): I provided 5\\n- `b` (second operand): I provided 3\\n\\nThe function performed the addition operation and returned 8, which is the correct sum of 5 and 3.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=88, prompt_tokens=762, total_tokens=850, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -2916,14 +2923,14 @@ "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=165, prompt_tokens=455, total_tokens=620, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=81, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=124, prompt_tokens=638, total_tokens=762, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=41, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 3}', 'name': 'simple_add'}, 'id': 'toolu_01QBLkGLtd85Yj8eEiFVBu4v', 'type': 'function'}], function_call=None, reasoning_content='The user is asking me to add 5 + 3. I have access to a simple_add function that can add two numbers together. Let me check the parameters:\\n- a: first operand (required) - this would be 5\\n- b: second operand (default 0, optional) - this would be 3\\n\\nI have both values needed, so I can call the function.', thinking_blocks=[{'type': 'thinking', 'thinking': 'The user is asking me to add 5 + 3. I have access to a simple_add function that can add two numbers together. Let me check the parameters:\\n- a: first operand (required) - this would be 5\\n- b: second operand (default 0, optional) - this would be 3\\n\\nI have both values needed, so I can call the function.', 'signature': 'EtcDCkYIBxgCKkBsQpKkRzBiOdz/WsOaqhfLGR5Ek0wqybtz7oGGXAeA3v3R5xaFWOGSV2FqryJ8GFGwXVwi72lf159aKHKEeFegEgznHzdwjcISQRbx+AgaDAHewzC+gZsxWSyOviIwZ5a/EK+wqk6VMo2ul4EIY62/bK54isjBzFYYyN5oAuVYdkyClXYN713Z5Blp+jKfKr4CiNIHa9Hwy/7UK6L8S4rY/YF7rJvu2kZlz8wF5W8Vvl8VNHQHgeNygMgoXAamixxGPIgB98kR5cmY6IcVym0MMmQA4r4RxcAf8NjRdyksYB3Y4mszWr6Edtnr8v4BPjr3fQtxgOQVjiEXsMKKmNVJBGikVksm3r7x/1EDJ+rs+jI6WHCUWoNOEmu5XbrAqHDf06SmkrhF9NKvVC8rqQWi9hB5LEPR8oWsgpxHivdlbVmJqT7WV+V5779TmESIvBGl4aor1SSWM0muYh3pDY9n1wSY4DQvVQPM3bavZZJo9rvH39EA093Q6zSZhBFDII+QRN7O77rjSf9XVumOF09xtsHye8HKhjsYJfvPAOV2+NEthD3jYZI+rrBEHqkyLgnNaLlExxByTNxMytXlTIc42cB1FSDOJoXzd62pPdHVGAE='}], provider_specific_fields={'citations': None, 'thinking_blocks': [{'type': 'thinking', 'thinking': 'The user is asking me to add 5 + 3. I have access to a simple_add function that can add two numbers together. Let me check the parameters:\\n- a: first operand (required) - this would be 5\\n- b: second operand (default 0, optional) - this would be 3\\n\\nI have both values needed, so I can call the function.', 'signature': 'EtcDCkYIBxgCKkBsQpKkRzBiOdz/WsOaqhfLGR5Ek0wqybtz7oGGXAeA3v3R5xaFWOGSV2FqryJ8GFGwXVwi72lf159aKHKEeFegEgznHzdwjcISQRbx+AgaDAHewzC+gZsxWSyOviIwZ5a/EK+wqk6VMo2ul4EIY62/bK54isjBzFYYyN5oAuVYdkyClXYN713Z5Blp+jKfKr4CiNIHa9Hwy/7UK6L8S4rY/YF7rJvu2kZlz8wF5W8Vvl8VNHQHgeNygMgoXAamixxGPIgB98kR5cmY6IcVym0MMmQA4r4RxcAf8NjRdyksYB3Y4mszWr6Edtnr8v4BPjr3fQtxgOQVjiEXsMKKmNVJBGikVksm3r7x/1EDJ+rs+jI6WHCUWoNOEmu5XbrAqHDf06SmkrhF9NKvVC8rqQWi9hB5LEPR8oWsgpxHivdlbVmJqT7WV+V5779TmESIvBGl4aor1SSWM0muYh3pDY9n1wSY4DQvVQPM3bavZZJo9rvH39EA093Q6zSZhBFDII+QRN7O77rjSf9XVumOF09xtsHye8HKhjsYJfvPAOV2+NEthD3jYZI+rrBEHqkyLgnNaLlExxByTNxMytXlTIc42cB1FSDOJoXzd62pPdHVGAE='}]}))], usage=Usage(completion_tokens=165, prompt_tokens=455, total_tokens=620, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=81, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=None, role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 3}', 'name': 'simple_add'}, 'id': 'toolu_019CGrvryYqUkZa2E3GAiMZq', 'type': 'function'}], function_call=None, reasoning_content='The user is asking me to add 5 and 3 together. I have a function called `simple_add` that can do this. Let me use it with a=5 and b=3.', thinking_blocks=[{'type': 'thinking', 'thinking': 'The user is asking me to add 5 and 3 together. I have a function called `simple_add` that can do this. Let me use it with a=5 and b=3.', 'signature': 'Eq8CCkYICBgCKkBItYppVARNjd/zJSrIuPgYG2iMuuMh0gxsQR/MUYrcXhMGRw/y26TJBKiFOBgJg9Olwrcq8DnIAvaV2d21aNHsEgxhmlCGs8Vvt+lfA38aDOVmx7i/a+rLUoeH8SIwY2xRFwyG5bksEb6/WSSnL3CtkX1fxl9tOxaPWYVYb6OpyGL5b4hl3EqKZmA212huKpYBDc5BNQvHT1k+P7taINEaIEOrenmQpwzt2djwWOVkC421jLKFWN9aT3rNMGjnANzjLQ+HXMKMSJefnP28xVcy3MkaJS5h87M3pRoL5JzR5LRYqWYbKTmuYRl8nVOM9/HEMVR4ikgSVb/ziC5c5zoxq/CW3EMHLveWdViV5eDXz3BeHSrsDZdOFJYgzDScI6DxL4f3QA9rGAE='}], provider_specific_fields={'citations': None, 'thinking_blocks': [{'type': 'thinking', 'thinking': 'The user is asking me to add 5 and 3 together. I have a function called `simple_add` that can do this. Let me use it with a=5 and b=3.', 'signature': 'Eq8CCkYICBgCKkBItYppVARNjd/zJSrIuPgYG2iMuuMh0gxsQR/MUYrcXhMGRw/y26TJBKiFOBgJg9Olwrcq8DnIAvaV2d21aNHsEgxhmlCGs8Vvt+lfA38aDOVmx7i/a+rLUoeH8SIwY2xRFwyG5bksEb6/WSSnL3CtkX1fxl9tOxaPWYVYb6OpyGL5b4hl3EqKZmA212huKpYBDc5BNQvHT1k+P7taINEaIEOrenmQpwzt2djwWOVkC421jLKFWN9aT3rNMGjnANzjLQ+HXMKMSJefnP28xVcy3MkaJS5h87M3pRoL5JzR5LRYqWYbKTmuYRl8nVOM9/HEMVR4ikgSVb/ziC5c5zoxq/CW3EMHLveWdViV5eDXz3BeHSrsDZdOFJYgzDScI6DxL4f3QA9rGAE='}]}))], usage=Usage(completion_tokens=124, prompt_tokens=638, total_tokens=762, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=41, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -2932,7 +2939,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_01QBLkGLtd85Yj8eEiFVBu4v',\n", + "{'tool_call_id': 'toolu_019CGrvryYqUkZa2E3GAiMZq',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '8'}" @@ -2946,19 +2953,19 @@ "text/markdown": [ "\n", "\n", - "5 + 3 = 8\n", + "5 + 3 = **8**\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=13, prompt_tokens=673, total_tokens=686, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=14, prompt_tokens=813, total_tokens=827, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\n5 + 3 = 8', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=13, prompt_tokens=673, total_tokens=686, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\n5 + 3 = **8**', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=14, prompt_tokens=813, total_tokens=827, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -2989,37 +2996,33 @@ "name": "stdout", "output_type": "stream", "text": [ - "Otters are charismatic members of the weasel family, found on every continent except Australia and Antarctica. There are 13-14 species in total, ranging from the Asian small-clawed otter (smallest) to the giant otter and sea otter (largest).\n", "\n", - "These semi-aquatic mammals have elongated bodies, long tails, and soft, dense fur. Otters have the densest fur of any animal—as many as a million hairs per square inch. They're equipped with powerful webbed feet for swimming and seal-like abilities for holding breath underwater, with river otters able to hold their breath for up to 8 minutes.\n", "\n", - "All otters are expert hunters that eat fish, crustaceans, and other critters. Sea otters have an ingenious method to open shellfish by floating on their backs, placing a rock on their chest, then smashing mollusks against it. River otters are especially playful, gamboling on land and splashing into rivers.\n", + "Otters are carnivorous mammals that belong to the subfamily Lutrinae, with 14 species that are all semiaquatic, living in both freshwater and marine environments. They're part of the Mustelidae family, which includes weasels, badgers, and wolverines.\n", "\n", - "Baby otters stay with their mothers until they're up to a year old, and otters can live up to 16 years. Unfortunately, many species remain at risk from pollution and habitat loss after being extensively hunted for their fur." + "Otters have long, slim bodies, powerful webbed feet for swimming, and dense fur that keeps them warm and buoyant in water. They primarily eat fish but may also consume frogs, birds, or shellfish, and can live up to 16 years. They're known for being playful animals, engaging in activities like sliding into water and playing with stones." ] }, { "data": { "text/markdown": [ - "Otters are [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") charismatic members of the weasel family, found on every continent except Australia and Antarctica. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") There are 13-14 species in total, ranging from [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") the Asian small-clawed otter (smallest) to the giant otter and sea otter (largest).\n", "\n", - "These semi-aquatic mammals have [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") elongated bodies, long tails, and soft, dense fur. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Otters have the densest fur of any animal—as many as a million hairs per square inch. They're [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") equipped with powerful webbed feet for swimming and seal-like abilities for holding breath underwater, with [*](https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week \"12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior\") [*](https://www.pbs.org/wnet/nature/blog/otter-fact-sheet/ \"Otter Fact Sheet | Blog | Nature | PBS\") river otters able to hold their breath for up to 8 minutes.\n", "\n", - "[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") All otters are expert hunters that eat fish, crustaceans, and other critters. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Sea otters have an ingenious method to open shellfish by floating on their backs, placing a rock on their chest, then smashing mollusks against it. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") River otters are especially playful, gamboling on land and splashing into rivers.\n", + "[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters are carnivorous mammals that belong to the subfamily Lutrinae, with 14 species that are all semiaquatic, living in both freshwater and marine environments. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They're part of the Mustelidae family, which includes weasels, badgers, and wolverines.\n", "\n", - "[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Baby otters stay with their mothers until they're up to a year old, and [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") otters can live up to 16 years. Unfortunately, [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") many species remain at risk from pollution and habitat loss after being extensively hunted for their fur.\n", + "[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters have long, slim bodies, powerful webbed feet for swimming, and dense fur that keeps them warm and buoyant in water. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They primarily eat fish but may also consume frogs, birds, or shellfish, and can live up to 16 years. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They're known for being playful animals, engaging in activities like sliding into water and playing with stones.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=551, prompt_tokens=13314, total_tokens=13865, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", + "- usage: `Usage(completion_tokens=285, prompt_tokens=14882, total_tokens=15167, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='Otters are [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") charismatic members of the weasel family, found on every continent except Australia and Antarctica. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") There are 13-14 species in total, ranging from [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") the Asian small-clawed otter (smallest) to the giant otter and sea otter (largest).\\n\\nThese semi-aquatic mammals have [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") elongated bodies, long tails, and soft, dense fur. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Otters have the densest fur of any animal—as many as a million hairs per square inch. They\\'re [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") equipped with powerful webbed feet for swimming and seal-like abilities for holding breath underwater, with [*](https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week \"12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior\") [*](https://www.pbs.org/wnet/nature/blog/otter-fact-sheet/ \"Otter Fact Sheet | Blog | Nature | PBS\") river otters able to hold their breath for up to 8 minutes.\\n\\n[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") All otters are expert hunters that eat fish, crustaceans, and other critters. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Sea otters have an ingenious method to open shellfish by floating on their backs, placing a rock on their chest, then smashing mollusks against it. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") River otters are especially playful, gamboling on land and splashing into rivers.\\n\\n[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Baby otters stay with their mothers until they\\'re up to a year old, and [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") otters can live up to 16 years. Unfortunately, [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") many species remain at risk from pollution and habitat loss after being extensively hunted for their fur.', role='assistant', tool_calls=[], function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=551, prompt_tokens=13314, total_tokens=13865, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\n[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters are carnivorous mammals that belong to the subfamily Lutrinae, with 14 species that are all semiaquatic, living in both freshwater and marine environments. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They\\'re part of the Mustelidae family, which includes weasels, badgers, and wolverines.\\n\\n[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters have long, slim bodies, powerful webbed feet for swimming, and dense fur that keeps them warm and buoyant in water. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They primarily eat fish but may also consume frogs, birds, or shellfish, and can live up to 16 years. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They\\'re known for being playful animals, engaging in activities like sliding into water and playing with stones.', role='assistant', tool_calls=[], function_call=None, provider_specific_fields=None))], usage=Usage(completion_tokens=285, prompt_tokens=14882, total_tokens=15167, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=None), prompt_tokens_details=None))" ] }, "metadata": {}, @@ -3061,7 +3064,7 @@ "text/markdown": [ "I'll solve this step by step using the addition function.\n", "\n", - "First, let me calculate 5 + 3:\n", + "**Step 1:** First, let me calculate 5 + 3\n", "\n", "🔧 simple_add({\"a\": 5, \"b\": 3})\n", "\n", @@ -3069,14 +3072,14 @@ "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=96, prompt_tokens=434, total_tokens=530, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=100, prompt_tokens=617, total_tokens=717, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll solve this step by step using the addition function.\\n\\nFirst, let me calculate 5 + 3:\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 3}', 'name': 'simple_add'}, 'id': 'toolu_01PbJNpks9G6o9UYjRs5nd4b', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=96, prompt_tokens=434, total_tokens=530, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll solve this step by step using the addition function.\\n\\n**Step 1:** First, let me calculate 5 + 3\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 3}', 'name': 'simple_add'}, 'id': 'toolu_01K82p3nUAFfrsfM43QWYkSH', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=100, prompt_tokens=617, total_tokens=717, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3085,7 +3088,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_01PbJNpks9G6o9UYjRs5nd4b',\n", + "{'tool_call_id': 'toolu_01K82p3nUAFfrsfM43QWYkSH',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '8'}" @@ -3097,7 +3100,9 @@ { "data": { "text/markdown": [ - "Now I'll add 7 to that result (8 + 7):\n", + "\n", + "\n", + "**Step 2:** Now I'll add 7 to that result (8 + 7)\n", "\n", "🔧 simple_add({\"a\": 8, \"b\": 7})\n", "\n", @@ -3105,14 +3110,14 @@ "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=88, prompt_tokens=543, total_tokens=631, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=94, prompt_tokens=728, total_tokens=822, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"Now I'll add 7 to that result (8 + 7):\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 8, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_01PAuvDwaBNgM2JbEwJwxov5', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=88, prompt_tokens=543, total_tokens=631, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"\\n\\n**Step 2:** Now I'll add 7 to that result (8 + 7)\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 8, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_012DMaKSvM5ssehCU1iqUszX', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=94, prompt_tokens=728, total_tokens=822, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3121,7 +3126,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_01PAuvDwaBNgM2JbEwJwxov5',\n", + "{'tool_call_id': 'toolu_012DMaKSvM5ssehCU1iqUszX',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '15'}" @@ -3133,7 +3138,9 @@ { "data": { "text/markdown": [ - "Finally, I'll add 11 to that result (15 + 11):\n", + "\n", + "\n", + "**Step 3:** Finally, I'll add 11 to that result (15 + 11)\n", "\n", "🔧 simple_add({\"a\": 15, \"b\": 11})\n", "\n", @@ -3141,14 +3148,14 @@ "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=89, prompt_tokens=644, total_tokens=733, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=95, prompt_tokens=833, total_tokens=928, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"Finally, I'll add 11 to that result (15 + 11):\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 15, \"b\": 11}', 'name': 'simple_add'}, 'id': 'toolu_01KeELPbBSpCePrk4c8EzbyB', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=89, prompt_tokens=644, total_tokens=733, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"\\n\\n**Step 3:** Finally, I'll add 11 to that result (15 + 11)\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 15, \"b\": 11}', 'name': 'simple_add'}, 'id': 'toolu_016SoPc5qmrreVLMReT73od6', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=95, prompt_tokens=833, total_tokens=928, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3157,7 +3164,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_01KeELPbBSpCePrk4c8EzbyB',\n", + "{'tool_call_id': 'toolu_016SoPc5qmrreVLMReT73od6',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '26'}" @@ -3169,24 +3176,26 @@ { "data": { "text/markdown": [ - "So working step by step:\n", + "\n", + "\n", + "**Answer:** ((5 + 3) + 7) + 11 = **26**\n", + "\n", + "Here's the breakdown:\n", "- 5 + 3 = 8\n", - "- 8 + 7 = 15 \n", + "- 8 + 7 = 15\n", "- 15 + 11 = 26\n", "\n", - "Therefore, ((5 + 3) + 7) + 11 = **26**\n", - "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=68, prompt_tokens=746, total_tokens=814, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=68, prompt_tokens=939, total_tokens=1007, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='So working step by step:\\n- 5 + 3 = 8\\n- 8 + 7 = 15 \\n- 15 + 11 = 26\\n\\nTherefore, ((5 + 3) + 7) + 11 = **26**', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=68, prompt_tokens=746, total_tokens=814, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"\\n\\n**Answer:** ((5 + 3) + 7) + 11 = **26**\\n\\nHere's the breakdown:\\n- 5 + 3 = 8\\n- 8 + 7 = 15\\n- 15 + 11 = 26\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=68, prompt_tokens=939, total_tokens=1007, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3360,24 +3369,30 @@ { "data": { "text/markdown": [ - "I'll calculate this step by step using the available functions, following the order of operations (parentheses first, then multiplication/division from left to right).\n", + "I'll calculate this step by step, following the order of operations.\n", "\n", - "Step 1: Calculate (10 + 5)\n", + "**Step 1:** Calculate the expressions in parentheses first\n", + "- First parenthesis: 10 + 5\n", + "- Second parenthesis: 2 + 1\n", "\n", "🔧 simple_add({\"a\": 10, \"b\": 5})\n", "\n", "\n", + "\n", + "🔧 simple_add({\"a\": 2, \"b\": 1})\n", + "\n", + "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=116, prompt_tokens=609, total_tokens=725, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=178, prompt_tokens=792, total_tokens=970, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll calculate this step by step using the available functions, following the order of operations (parentheses first, then multiplication/division from left to right).\\n\\nStep 1: Calculate (10 + 5)\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 10, \"b\": 5}', 'name': 'simple_add'}, 'id': 'toolu_013mAGGxvDDy3ftqSS3u2qPh', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=116, prompt_tokens=609, total_tokens=725, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll calculate this step by step, following the order of operations.\\n\\n**Step 1:** Calculate the expressions in parentheses first\\n- First parenthesis: 10 + 5\\n- Second parenthesis: 2 + 1\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 10, \"b\": 5}', 'name': 'simple_add'}, 'id': 'toolu_01QGnA5EkWKTX8QTiVf5NtJ2', 'type': 'function'}, {'index': 2, 'function': {'arguments': '{\"a\": 2, \"b\": 1}', 'name': 'simple_add'}, 'id': 'toolu_01KxdPWNQK1BN7p4EfQTDtj1', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=178, prompt_tokens=792, total_tokens=970, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3386,7 +3401,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_013mAGGxvDDy3ftqSS3u2qPh',\n", + "{'tool_call_id': 'toolu_01QGnA5EkWKTX8QTiVf5NtJ2',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '15'}" @@ -3395,25 +3410,40 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/plain": [ + "{'tool_call_id': 'toolu_01KxdPWNQK1BN7p4EfQTDtj1',\n", + " 'role': 'tool',\n", + " 'name': 'simple_add',\n", + " 'content': '3'}" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/markdown": [ - "Step 2: Calculate (2 + 1)\n", "\n", - "🔧 simple_add({\"a\": 2, \"b\": 1})\n", + "\n", + "**Step 2:** Multiply the result from the first parenthesis by 3\n", + "- 15 * 3\n", + "\n", + "🔧 multiply({\"a\": 15, \"b\": 3})\n", "\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=83, prompt_tokens=738, total_tokens=821, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=96, prompt_tokens=1033, total_tokens=1129, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content='Step 2: Calculate (2 + 1)', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 2, \"b\": 1}', 'name': 'simple_add'}, 'id': 'toolu_019KZ7aFBcAoHieWEFX6hYwA', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=83, prompt_tokens=738, total_tokens=821, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content='\\n\\n**Step 2:** Multiply the result from the first parenthesis by 3\\n- 15 * 3', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 15, \"b\": 3}', 'name': 'multiply'}, 'id': 'toolu_01ELPz78sh1V1iBfGusJ6og7', 'type': 'function'}], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=96, prompt_tokens=1033, total_tokens=1129, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3422,10 +3452,10 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_019KZ7aFBcAoHieWEFX6hYwA',\n", + "{'tool_call_id': 'toolu_01ELPz78sh1V1iBfGusJ6og7',\n", " 'role': 'tool',\n", - " 'name': 'simple_add',\n", - " 'content': '3'}" + " 'name': 'multiply',\n", + " 'content': '45'}" ] }, "metadata": {}, @@ -3436,19 +3466,20 @@ "text/markdown": [ "\n", "\n", - "Step 3: Multiply 15 * 3\n", + "**Step 3:** Divide the result by 3\n", + "- 45 / 3\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=22, prompt_tokens=848, total_tokens=870, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=30, prompt_tokens=1154, total_tokens=1184, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\nStep 3: Multiply 15 * 3', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=22, prompt_tokens=848, total_tokens=870, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\n**Step 3:** Divide the result by 3\\n- 45 / 3', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=30, prompt_tokens=1154, total_tokens=1184, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3508,25 +3539,19 @@ "text/markdown": [ "\n", "\n", - "I was only able to complete the first calculation: 1 + 2 = 3.\n", - "\n", - "To complete your request, I would need to make two additional tool calls:\n", - "1. Add 2 to the result (3 + 2 = 5)\n", - "2. Add 3 to that result (5 + 3 = 8)\n", - "\n", - "The final answer should be 8, but I wasn't able to use the tools for all the steps. Would you like me to continue with the remaining calculations if you provide more tool uses?\n", + "Let me continue with the next calculations:\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=123, prompt_tokens=584, total_tokens=707, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=18, prompt_tokens=775, total_tokens=793, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"\\n\\nI was only able to complete the first calculation: 1 + 2 = 3.\\n\\nTo complete your request, I would need to make two additional tool calls:\\n1. Add 2 to the result (3 + 2 = 5)\\n2. Add 3 to that result (5 + 3 = 8)\\n\\nThe final answer should be 8, but I wasn't able to use the tools for all the steps. Would you like me to continue with the remaining calculations if you provide more tool uses?\", role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=123, prompt_tokens=584, total_tokens=707, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\nLet me continue with the next calculations:', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=18, prompt_tokens=775, total_tokens=793, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -3684,19 +3709,19 @@ { "data": { "text/markdown": [ - "2 + 2 = 4\n", + "2+2 = 4\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=13, prompt_tokens=14, total_tokens=27, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=11, prompt_tokens=14, total_tokens=25, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='2 + 2 = 4', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=13, prompt_tokens=14, total_tokens=27, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='2+2 = 4', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=11, prompt_tokens=14, total_tokens=25, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "execution_count": null, @@ -3739,7 +3764,7 @@ { "data": { "text/markdown": [ - "I'll use the async_add tool to calculate 5 + 7 for you.\n", + "I'll use the async_add function to calculate 5 + 7 for you.\n", "\n", "🔧 async_add({\"a\": 5, \"b\": 7})\n", "\n", @@ -3747,14 +3772,14 @@ "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `tool_calls`\n", - "- usage: `Usage(completion_tokens=91, prompt_tokens=424, total_tokens=515, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=91, prompt_tokens=607, total_tokens=698, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll use the async_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5, \"b\": 7}', name='async_add'), id='toolu_01X1GDcGawNLq77xytxEGDBw', type='function')], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=91, prompt_tokens=424, total_tokens=515, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='tool_calls', index=0, message=Message(content=\"I'll use the async_add function to calculate 5 + 7 for you.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5, \"b\": 7}', name='async_add'), id='toolu_01G5KQPS37jjw2qsK4JJnY7g', type='function')], function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=91, prompt_tokens=607, total_tokens=698, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3763,7 +3788,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_01X1GDcGawNLq77xytxEGDBw',\n", + "{'tool_call_id': 'toolu_01G5KQPS37jjw2qsK4JJnY7g',\n", " 'role': 'tool',\n", " 'name': 'async_add',\n", " 'content': '12'}" @@ -3777,19 +3802,19 @@ "text/markdown": [ "\n", "\n", - "The calculation is complete! Using the async_add tool, I calculated that 5 + 7 = 12.\n", + "The result is **12**. The calculation shows that 5 + 7 = 12.\n", "\n", "
\n", "\n", "- id: `chatcmpl-xxx`\n", - "- model: `claude-sonnet-4-20250514`\n", + "- model: `claude-sonnet-4-5-20250929`\n", "- finish_reason: `stop`\n", - "- usage: `Usage(completion_tokens=30, prompt_tokens=568, total_tokens=598, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", + "- usage: `Usage(completion_tokens=26, prompt_tokens=749, total_tokens=775, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`\n", "\n", "
" ], "text/plain": [ - "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-20250514', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\nThe calculation is complete! Using the async_add tool, I calculated that 5 + 7 = 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=30, prompt_tokens=568, total_tokens=598, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" + "ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='\\n\\nThe result is **12**. The calculation shows that 5 + 7 = 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=26, prompt_tokens=749, total_tokens=775, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0))" ] }, "metadata": {}, @@ -3812,7 +3837,7 @@ "data": { "text/plain": [ "[{'role': 'user', 'content': 'What is 2+2?'},\n", - " Message(content='2 + 2 = 4', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})]" + " Message(content='2+2 = 4', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})]" ] }, "execution_count": null, @@ -3850,12 +3875,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "I'll use the async_add tool to calculate 5 + 7 for you.\n", + "I'll use the async_add function to calculate 5 + 7 for you.\n", "🔧 async_add\n", - "{'tool_call_id': 'toolu_01NTrwnCZYd6K7ZeNQtUyx9k', 'role': 'tool', 'name': 'async_add', 'content': '12'}\n", + "{'tool_call_id': 'toolu_01Bg8m1Vw2GTQroayJb9xHaE', 'role': 'tool', 'name': 'async_add', 'content': '12'}\n", "\n", "\n", - "The calculation is complete! Using the async_add tool, I calculated that 5 + 7 = 12." + "The result is **12**. The calculation shows that 5 + 7 = 12." ] } ], @@ -3910,24 +3935,39 @@ "outputs": [], "source": [ "#| export\n", + "def _fmt_thinking(o):\n", + " return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False)\n", + "\n", + "def _fmt_tool_call(o):\n", + " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", + " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", + " return f\"\\n
\\n\\n{calls}\\n\"\n", + "\n", + "def _fmt_tool_result(o):\n", + " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", + " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", + "\n", "async def aformat_stream(rs):\n", " \"Format the response stream for markdown display.\"\n", - " think = False\n", + " think,tool_count,tool_idx = False,0,0\n", " async for o in rs:\n", " if isinstance(o, ModelResponseStream):\n", - " d = o.choices[0].delta\n", - " if nested_idx(d, 'reasoning_content'): \n", - " think = True\n", - " yield '🧠'\n", - " elif think:\n", - " think = False\n", - " yield '\\n\\n'\n", - " if c := d.content: yield c\n", - " elif isinstance(o, ModelResponse) and (c := getattr(o.choices[0].message, 'tool_calls', None)):\n", - " fn = first(c).function\n", - " yield f\"\\n
\\n\\n `{fn.name}({_trunc_str(fn.arguments)})`\\n\"\n", - " elif isinstance(o, dict) and 'tool_call_id' in o: \n", - " yield f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\\n
\\n\\n\"" + " txt,is_think = _fmt_thinking(o)\n", + " if is_think: think = True\n", + " elif think: txt,think = '\\n\\n',False\n", + " if txt: yield txt\n", + " if c := o.choices[0].delta.content: yield c\n", + " elif isinstance(o, ModelResponse):\n", + " if res := _fmt_tool_call(o):\n", + " tool_count = len(o.choices[0].message.tool_calls)\n", + " tool_idx = 0\n", + " yield res\n", + " elif res := _fmt_tool_result(o):\n", + " yield res\n", + " tool_idx += 1\n", + " if tool_idx >= tool_count: \n", + " yield \"\\n
\\n\\n\"\n", + " tool_count = 0" ] }, { @@ -3979,7 +4019,7 @@ { "data": { "text/markdown": [ - "I'll use the async_add tool to calculate 5 + 7 for you.\n", + "I'll use the async_add function to calculate 5 + 7 for you.\n", "
\n", "\n", " `async_add({\"a\": 5, \"b\": 7})`\n", @@ -3989,7 +4029,7 @@ "\n", "\n", "\n", - "The calculation is complete! Using the async_add tool, I calculated that 5 + 7 = 12." + "The result is **12**. The calculation shows that 5 + 7 = 12." ], "text/plain": [ "" @@ -4022,17 +4062,17 @@ { "data": { "text/markdown": [ - "🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠\n", + "🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠\n", "\n", - "For 1000 random integers, the **most efficient approach is to use your programming language's built-in sort function** (like `sort()` in Python, `Arrays.sort()` in Java, or `std::sort()` in C++).\n", + "**Use your language's built-in sort function** (e.g., Python's `sorted()` or `.sort()`, C++'s `std::sort()`, Java's `Arrays.sort()`).\n", "\n", - "These implementations typically use highly optimized hybrid algorithms like:\n", - "- **Introsort** (introspective sort) - starts with quicksort, switches to heapsort for worst-case scenarios\n", - "- **Timsort** (Python) - optimized for real-world data patterns\n", + "These are highly optimized and typically use hybrid algorithms like:\n", + "- **Timsort** (Python) - combines merge sort and insertion sort\n", + "- **Introsort** (C++) - starts with quicksort, switches to heapsort if needed\n", "\n", - "If implementing from scratch, **quicksort** would be most efficient for random data at this size, with O(n log n) average performance.\n", + "**If implementing from scratch**: **Quicksort** is usually fastest for random data of this size, with O(n log n) average case and good cache performance.\n", "\n", - "For integers specifically, **radix sort** could also be very fast (O(kn) where k is the number of digits), but built-in functions are still usually the best choice due to their optimization and testing." + "For just 1000 elements, the practical performance difference between good O(n log n) algorithms is minimal—built-in functions win due to low-level optimizations." ], "text/plain": [ "" @@ -4065,28 +4105,31 @@ { "data": { "text/markdown": [ - "I'll calculate ((10 + 5) * 3) / (2 + 1) step by step using the available functions.\n", + "I'll calculate ((10 + 5) * 3) / (2 + 1) step by step.\n", + "\n", + "**Step 1:** Calculate the first parentheses (10 + 5)\n", + "**Step 2:** Multiply that result by 3\n", + "**Step 3:** Calculate the denominator (2 + 1)\n", + "**Step 4:** Divide the result from step 2 by the result from step 3\n", "\n", - "**Step 1: Calculate (10 + 5)**\n", + "Let me perform these calculations:\n", "
\n", "\n", " `simple_add({\"a\": 10, \"b\": 5})`\n", + " `simple_add({\"a\": 2, \"b\": 1})`\n", " - `15`\n", + " - `3`\n", "\n", "
\n", "\n", - "**Step 2: Calculate (2 + 1)**\n", + "\n", "
\n", "\n", - " `simple_add({\"a\": 2, \"b\": 1})`\n", - " - `3`\n", + " `multiply({\"a\": 15, \"b\": 3})`\n", + " - `45`\n", "\n", "
\n", - "\n", - "\n", - "\n", - "**Step 3: Multiply the result from Step 1 by 3**\n", - "Now I'll multiply 15 * 3:" + "\n" ], "text/plain": [ "" @@ -4114,7 +4157,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll calculate ((10 + 5) * 3) / (2 + 1) step by step using the available functions.\\n\\n**Step 1: Calculate (10 + 5)**\", role='assistant', tool_calls=[{'function': {'arguments': '{\"a\": 10, \"b\": 5}', 'name': 'simple_add'}, 'id': 'toolu_017SE1tgPMBDtS5xLLMGLg9b', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll calculate ((10 + 5) * 3) / (2 + 1) step by step.\\n\\n**Step 1:** Calculate the first parentheses (10 + 5)\\n**Step 2:** Multiply that result by 3\\n**Step 3:** Calculate the denominator (2 + 1)\\n**Step 4:** Divide the result from step 2 by the result from step 3\\n\\nLet me perform these calculations:\", role='assistant', tool_calls=[{'function': {'arguments': '{\"a\": 10, \"b\": 5}', 'name': 'simple_add'}, 'id': 'toolu_01AKfRh9iDgX7ZNtjcX8FWgm', 'type': 'function'}, {'function': {'arguments': '{\"a\": 2, \"b\": 1}', 'name': 'simple_add'}, 'id': 'toolu_017xC4A71CAwU2e27gjU9C7X', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -4135,7 +4178,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_017SE1tgPMBDtS5xLLMGLg9b',\n", + "{'tool_call_id': 'toolu_01AKfRh9iDgX7ZNtjcX8FWgm',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '15'}" @@ -4159,7 +4202,10 @@ { "data": { "text/plain": [ - "Message(content='**Step 2: Calculate (2 + 1)**', role='assistant', tool_calls=[{'function': {'arguments': '{\"a\": 2, \"b\": 1}', 'name': 'simple_add'}, 'id': 'toolu_013N4boUAcDB1X2h9aNNivug', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "{'tool_call_id': 'toolu_017xC4A71CAwU2e27gjU9C7X',\n", + " 'role': 'tool',\n", + " 'name': 'simple_add',\n", + " 'content': '3'}" ] }, "execution_count": null, @@ -4188,13 +4234,11 @@ { "data": { "text/markdown": [ - "Otters are [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") charismatic members of the weasel family, found on every continent except Australia and Antarctica. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") There are 13-14 species in total, ranging from [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") the Asian small-clawed otter (smallest) to the giant otter and sea otter (largest).\n", "\n", - "These semi-aquatic mammals have [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") elongated bodies, long tails, and soft, dense fur. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Otters have the densest fur of any animal—as many as a million hairs per square inch. They're [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") equipped with powerful webbed feet for swimming and seal-like abilities for holding breath underwater, with [*](https://www.doi.gov/blog/12-facts-about-otters-sea-otter-awareness-week \"12 Facts About Otters for Sea Otter Awareness Week | U.S. Department of the Interior\") [*](https://www.pbs.org/wnet/nature/blog/otter-fact-sheet/ \"Otter Fact Sheet | Blog | Nature | PBS\") river otters able to hold their breath for up to 8 minutes.\n", "\n", - "[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") All otters are expert hunters that eat fish, crustaceans, and other critters. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Sea otters have an ingenious method to open shellfish by floating on their backs, placing a rock on their chest, then smashing mollusks against it. [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") River otters are especially playful, gamboling on land and splashing into rivers.\n", + "[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters are carnivorous mammals that belong to the subfamily Lutrinae, with 14 species that are all semiaquatic, living in both freshwater and marine environments. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They're part of the Mustelidae family, which includes weasels, badgers, and wolverines.\n", "\n", - "[*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") Baby otters stay with their mothers until they're up to a year old, and [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") otters can live up to 16 years. Unfortunately, [*](https://www.nationalgeographic.com/animals/mammals/facts/otters-1 \"Otters, facts and information | National Geographic\") many species remain at risk from pollution and habitat loss after being extensively hunted for their fur." + "[*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") Otters have long, slim bodies, powerful webbed feet for swimming, and dense fur that keeps them warm and buoyant in water. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They primarily eat fish but may also consume frogs, birds, or shellfish, and can live up to 16 years. [*](https://en.wikipedia.org/wiki/Otter \"Otter - Wikipedia\") They're known for being playful animals, engaging in activities like sliding into water and playing with stones." ], "text/plain": [ "" @@ -4230,7 +4274,13 @@ ] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "python3", + "language": "python", + "name": "python3" + } + }, "nbformat": 4, "nbformat_minor": 5 } From d019231289b02c0b15c6f68803baff2d8eeaa680 Mon Sep 17 00:00:00 2001 From: Erik Gaasedelen Date: Thu, 2 Oct 2025 12:09:41 -0700 Subject: [PATCH 2/3] refactor --- cachy.jsonl | 19 ++ lisette/_modidx.py | 4 + lisette/core.py | 43 ++- nbs/00_core.ipynb | 689 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 724 insertions(+), 31 deletions(-) diff --git a/cachy.jsonl b/cachy.jsonl index 9aac587..a04ad77 100644 --- a/cachy.jsonl +++ b/cachy.jsonl @@ -222,3 +222,22 @@ {"key": "3aa12e4d", "response": "{\"id\":\"msg_016GgcVWcQ6YtXR3uv9FgeRx\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} {"key": "f9a8ba88", "response": "{\"id\":\"msg_01BYyMXiWiRrsJgvnaJFbY95\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} {"key": "97e73e3c", "response": "{\"id\":\"msg_01RHj2UcxLRrwHZVPiwjEHhH\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe answer is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":810,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "06bc8ac9", "response": "{\"id\":\"msg_012GrTEKKoXescRHgyKE8E9A\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} +{"key": "00988a9d", "response": "{\"id\":\"msg_01XSWgwpB1Nk8mCabsjmUhtD\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "4ac64a79", "response": "{\"id\":\"msg_01WVzdMJuY6M2GwWy3WWwTEw\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe answer is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":810,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "8f71f4b7", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01WbVkin5YGjDLfLyNcKP8jR\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":28394,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":28394,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Looking\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" at the code\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", I can see that the `a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"format_stream` function manages\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" opening and closing `\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"
` tags for tool usage\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\". The issue you fixe\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d was ensuring that when there are multiple tool calls, the details\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" tags are properly balanced.\\n\\nHere\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'s a test to verify the correct\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" number of open and closed detail tags:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n```python\\n#| export\\nasync def count\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_detail_tags(rs):\\n \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Count open and closed detail tags in a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" response stream\\\"\\n open\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = 0\\n close\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count = 0\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n async for chunk\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" in aformat_stream(rs):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n open_count += chunk.count(\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"
\\\")\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n close_count += chunk.\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"count(\\\"
\\\")\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n return open_count, close\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count\\n\\n# Test function\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nasync def test_detail\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_tags_balanced():\\n \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Test that detail tags are properly balanced for\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" multiple tool calls\\\"\\n chat = AsyncChat(model, tools=[\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"simple_add, multiply])\\n \\n # Test with multiple\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" tool calls that should generate\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" one details block\\n res = await chat(\\\"Calculate\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" (5 + 3) an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d (7 * 2)\\\", stream\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"=True, return\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_all=True)\\n \"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n open\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count, close_count = await\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" count_detail_tags(res)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n \\n #\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Should have equal\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" number of open and close\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" tags\\n assert open_count ==\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" close_count, f\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Unbalanced tags\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\": {open_count} open,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" {close_count} close\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n \\n # For multiple tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" calls in one response, shoul\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d have at least one details block\\n assert open\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count >=\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 1, f\\\"Expecte\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d at least 1 details block, got {open_count}\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n \\n print(f\\\"\u2713\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Detail tags balanced: {open_count} open, {\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"close_count} close\\\")\\n\\n#\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Run the test\\nawait test_detail_tags_balanced()\\n```\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nThis test:\\n1. Counts\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" the number of `\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"
`\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" opening tags\\n2. Counts the number\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" of `
` closing tags\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" \\n3. Ass\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"erts they are equal (\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"balanced)\\n4. Ensures\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" we have at least one details block when\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" tool calls are made\\n\\nThe test\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" specifically targets\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" the scenario you\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" fixed where multiple tool calls nee\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d to be properly\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" wrapped in balance\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d detail tags.\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":28394,\"cache_read_input_tokens\":0,\"output_tokens\":532} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "e23ad61a", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01PoTMbEFsrxjvrdbgPb1sES\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":700,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":4,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"I'll calculate both\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" of those for you.\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01FzFnp3EWSrTNRDMcjWdxKe\",\"name\":\"simple_add\",\"input\":{}} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\\\": 5\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"b\\\": 3}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":2,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_018Zv3fvoXPwmBar78Ghs4DA\",\"name\":\"multiply\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\": 7\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"b\\\": 2}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":2 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":700,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":131} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "10489a59", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01Pak6tvdC93evLiaAbAJbhx\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":932,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nThe\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" results are:\\n- (5 + 3) = **\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"8**\\n- (7 * 2) = **14**\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":932,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":34} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "ff42f8f2", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01UsEWcq9hHjHgwDmXg85kV5\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":29884,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":29884,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":3,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"You're right\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"! The generator gets consumed when we iterate\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" through it. Here's a quick solution\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" using `itertools.tee` to copy the generator so\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" we can use it for both counting and displaying:\\n\\n```python\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nfrom itertools import tee\\n\\n# In the\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" multi tool calling example, right before displaying\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\":\\nchat = AsyncChat(model, tools=[\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"simple_add, multiply, divide])\\nres = await chat(\\\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Calculate ((10 + 5) * \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"3) / (2 + \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"1) Explain step by step.\\\",\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" \\n max_steps=3, stream=True,\\n final\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_prompt=\\\"Please wrap-up for now and summarize how far we got\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".\\\")\\n\\n# Copy the generator for testing an\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d display\\nres_for_test, res_for_display = tee\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(res, 2)\\n\\n# Quick\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" test for balanced detail tags\\nasync def quick\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_detail_test(stream):\\n open\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count = close_count = 0\\n async\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" for chunk in aformat_stream(stream):\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"open_count += chunk.count(\\\"
\\\")\\n close_count +=\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" chunk.count(\\\"
\\\")\\n return open_count, close\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count\\n\\nopen_count, close_count = await\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" quick_detail_test(res_for_test)\\nassert open_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"count == close_count, f\\\"Detail\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" tags unbalanced: {open_count} \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\u2260 {close_count}\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nprint(f\\\"\u2713 Detail tags\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" balanced: {open_count} open\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", {close_count} close\\\")\\n\\n# Now\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" display the stream\\nawait adisplay_stream(res_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"for_display)\\n```\\n\\nThis way you\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" get a quick inline test right before the display without having to restruct\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ure the example!\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":29884,\"cache_read_input_tokens\":0,\"output_tokens\":392} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"} +{"key": "5684c272", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01AajkQ6jYjC5ZfN5WnNSRGZ\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":28563,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":28563,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Yes\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", you can fix this! The issue is that `tee()\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"` doesn't work with async generators. You\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" need to use async-compatible approaches. Here are a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" few solutions:\\n\\n**Option 1:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Collect results first, then process twice**\\n```python\\nchat\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = AsyncChat(model, tools=[simple_add, multiply, divide])\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nres = await chat(\\\"Calculate ((10 + 5) * \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"3) / (2 + 1) Explain step by step.\\\",\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" \\n max_steps=3, stream=\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"True,\\n final_prompt=\\\"Please wrap-up for now and summar\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ize how far we got.\\\")\\n\\n#\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Collect all results first\\nresults = []\\nasync for item\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" in res:\\n results.append(item)\\n\\n# Now\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" you can process the collected results multiple times\\nasync\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" def async_iter_from_list(items):\\n for item in items:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n yield item\\n\\n# Test with the collecte\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d results\\nres_for_test = async_iter_from_list(results)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nopen_count, close_count = await\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" quick_detail_test(res_for_test)\\nassert\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" open_count == close_count,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" f\\\"Detail tags unbalanced:\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" {open_count} \u2260 {close_count}\\\"\\nprint\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(f\\\"\u2713 Detail tags balanced: {open_count} open\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", {close_count} close\\\")\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n# Display with the collecte\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d results\\nres_for_display = async_iter\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_from_list(results)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nawait adisplay_stream(res\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_for_display)\\n```\\n\\n**Option 2: Just\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" skip the test and display directly**\\n```python\\nchat = Async\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Chat(model, tools=[simple_add, multiply, divide])\\nres\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = await chat(\\\"Calculate ((10 + 5) * 3)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" / (2 + 1) Explain step by step.\\\", \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n max_steps=3, stream=True,\\n final_prompt\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"=\\\"Please wrap-up for now and summarize how far we got.\\\")\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n# Skip the test and just display\\nawait\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" adisplay_stream(res)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n```\\n\\n**Option 3:\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Create an async tee function**\\n```python\\nasync\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" def async_tee(agen\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", n=2):\\n \\\"\\\"\\\"Split an async generator into n independent\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" async generators\\\"\\\"\\\"\\n items = []\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n async def collector():\\n async for item in \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"agen:\\n items.append(item)\\n \\n await\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" collector()\\n \\n async def replay\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"():\\n for item in items:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n yield item\\n \\n return tuple\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(replay() for _ in range(n))\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n# Then use it:\\nchat = AsyncChat(model, tools=[\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"simple_add, multiply, divide])\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nres = await chat(\\\"Calculate ((\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"10 + 5) * \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"3) / (2 + 1) Explain step by step.\\\",\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" \\n max_steps=3\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", stream=True,\\n final_prompt=\\\"Please wrap-up for\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" now and summarize how far we got\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".\\\")\\n\\nres_for_test, res_for_display = await\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" async_tee(res, 2)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nopen_count, close_count = await\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" quick_detail_test(res_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"for_test)\\nassert open_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"count == close_count, f\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Detail tags unbalanced: {open_count} \u2260 {\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"close_count}\\\"\\nprint(f\\\"\u2713 Detail tags balanced:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" {open_count} open, {close_count} close\\\")\\n\\nawait\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" adisplay_stream(res_for_display)\\n```\\n\\nI\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'d recommend **Option 1** as it\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'s the most straightforward and readable approach.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":28563,\"cache_read_input_tokens\":0,\"output_tokens\":812} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "a5c22cf5", "response": "{\"id\":\"msg_012tcT8SGbsoZWijnv3m2GDs\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} +{"key": "577e4e1c", "response": "{\"id\":\"msg_01HCH5Ej1dsbPQmDgeDUMFEC\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "028732dd", "response": "{\"id\":\"msg_017aeExRrgsj9PFa1R3paXwG\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe answer is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":810,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "c2623a91", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01TopyVd4yp6DydvWinDdGhm\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":27374,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":27374,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Looking\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" at your `aformat_stream` function, I think\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" you're absolutely right! The current implementation is\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" quite complex with multiple state variables and conditional\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" logic all mixed together. Breaking\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" it into 3 separate iterators would make it\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" much cleaner and easier to understand.\\n\\nHere's how\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" I'd approach it:\\n\\n```python\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nasync def _\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"thinking_stream(rs):\\n \\\"Extract\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" thinking indicators from stream\\\"\\n think =\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" False\\n async for o in rs:\\n txt\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", is_think = _fmt_thinking\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(o)\\n if is_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"think: \\n think = True\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"yield txt\\n elif think: \"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n yield '\\\\n\\\\n'\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n think = False\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nasync def _content\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_stream(rs):\\n \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Extract content from stream\\\"\\n async\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" for o in rs:\\n if\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" c := nested_idx(o, \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'choices', 0, '\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"delta', 'content'\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"): \\n yield c\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nasync def _tool_stream(rs\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"):\\n \\\"Extract tool calls an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d results from stream\\\"\\n tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count, tool_idx = 0, \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"0\\n async for o in rs\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\":\\n # Tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" call start\\n if res := _fmt_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tool_call(o):\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tool_count = len(o.choices[0].message.tool_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"calls)\\n tool_idx =\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 0\\n yield res\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n # Tool results\\n if res := _fmt_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tool_result(o):\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"yield res\\n tool_idx += 1\\n if tool_idx\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" >= tool_count: \\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"yield \\\"\\\\n
\\\\n\\\\n\\\"\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tool_count = 0\\n\\nasync def aformat_stream(rs):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n \\\"Format the response stream for\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" markdown display by combining\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" all stream types\\\"\\n #\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" You'\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d need to implement some\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" way to merge these async\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" iterators\\n # This\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" is the tr\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"icky part - how to inter\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"leave them properly\\n pass\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n```\\n\\nThe challenge\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" is how to properly interleave these\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 3 async\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" iterators while\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" maintaining the correct order\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\". You'd need something\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" like `asyncio.merge\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"` or a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" custom solution that\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" processes the original\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" stream once\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" and routes outputs\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" to the appropriate formatter\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".\\n\\nWhat\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" approach were you thinking for\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" combining the 3 iter\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ators?\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":27374,\"cache_read_input_tokens\":0,\"output_tokens\":503} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"} +{"key": "8e4aa6ee", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01E4JgcS4phcWcSrPkbdhNzC\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":28461,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":28461,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"I\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" think the cleanest approach would be to process\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" the original stream once and have each\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" formatter function yield strings that we concatenate. Here\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'s what I'd suggest:\\n\\n```python\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nasync def _fmt_thinking_str\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(o):\\n \\\"Return\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" thinking string or empty string\\\"\\n txt,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" is_think = _fmt_thinking(o)\\n if is_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"think: return txt\\n # Handle transition\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" from thinking to regular content\\n if has\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"attr(_fmt_thinking_str, '_was\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_thinking') and _fmt_thinking_str._\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"was_thinking:\\n _fmt_thinking_str._was_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"thinking = False\\n return '\\\\n\\\\n'\\n _\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"fmt_thinking_str._was_thinking = is\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_think\\n return ''\\n\\nasync def _fmt_content\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_str(o):\\n \\\"Return content string or empty string\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n if c := nested_idx(o, \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'choices', 0, 'delta', 'content'): \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n return c\\n return ''\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nasync def _fmt_tool_str(o):\\n \\\"Return\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" tool string or empty string\\\"\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"# Tool call start\\n if res := _fmt\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_tool_call(o):\\n _fmt_tool_str._tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_count = len(o.choices[0].message.tool_calls\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\")\\n _fmt_tool_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"str._tool_idx = 0\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n return res\\n \\n # Tool results\\n if res\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" := _fmt_tool_result(o):\\n _\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"fmt_tool_str._tool_idx =\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" getattr(_fmt_tool_str,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" '_tool_idx', 0) + 1\\n result\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = res\\n if _fmt_tool_str._tool_idx >=\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" getattr(_fmt_tool_str, '_tool_count', 0):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n result += \\\"\\\\n
\\\\n\\\\n\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n _fmt_tool_str._tool_count\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = 0\\n return result\\n \\n return ''\\n\\nasync def\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" aformat_stream(rs):\\n \\\"Format the response stream for markdown\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" display.\\\"\\n async for o in rs:\\n #\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Combine outputs from all formatters\\n parts\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = [\\n await _fmt_thinking_str(o\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"),\\n await _fmt_content\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_str(o), \\n await _fmt_tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_str(o)\\n ]\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n combined = ''.join(parts)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n if combined: # Only yield if there's actual content\\n yiel\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d combined\\n```\\n\\nThis approach:\\n1.\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Processes the original stream once\\n2. Each\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" formatter returns a string (or empty string)\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n3. We concatenate and yield the\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" combined result\\n4. Uses function attributes to maintain state (\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"a bit hacky but simple)\\n\\nThe main advantage\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" is that we maintain the correct order since\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" we're processing each item in the original stream sequ\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"entially. What do you think of this approach?\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":28461,\"cache_read_input_tokens\":0,\"output_tokens\":640} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "757d6f90", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01TZYBzw3C9HXDMpbJ4o2KL2\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":27535,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":27535,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"You\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'re absolutely right! The current approach with function-level state tracking is fragile and hard to understand. Here\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" are several ways to make this much cleaner:\\n\\n## Option\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 1: Simple State Class\\n\\n```python\\nclass Stream\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Formatter:\\n def __init__(self):\\n self.was\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_thinking = False\\n self.tool_count = 0\\n \"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"self.tool_idx = 0\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n def format_thinking(self, o):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n txt = '\ud83e\udde0' if nested_idx(o, 'choices\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"', 0, 'delta', 'reasoning_content') else ''\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n if txt:\\n self.was_thinking =\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" True\\n return txt\\n elif self.was_thinking:\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"self.was_thinking = False\\n return '\\\\n\\\\n'\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n return ''\\n \\n def format_content\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(self, o):\\n return neste\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d_idx(o, 'choices\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"', 0, 'delta', 'content')\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" or ''\\n \\n def format_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tool(self, o):\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"if tc := _fmt_tool_call(o):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n self.tool_count =\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" len(o.choices[0].message.tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_calls)\\n self.tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_idx = 0\\n return\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" tc\\n \\n if tr\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" := _fmt_tool_result(\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"o):\\n self.tool_idx +=\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 1\\n result = tr\\n if self.tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_idx >= self.tool_count\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\":\\n result += \\\"\\\\n\\\\n\\\\n\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n self.tool_count = 0\\n return result\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n return ''\\n \\n def format(\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"self, o):\\n return ''.join([\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n self.format_thinking(o\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"),\\n self.format_content\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(o), \\n self.format_tool\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(o)\\n ])\\n\\nasync\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" def aformat_stream(rs):\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"formatter = StreamFormatter()\\n async for\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" o in rs: \\n yield formatter.format(o\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\")\\n```\\n\\n## Option 2: Even Simpler -\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Collect Then Format\\n\\n```python\\nasync def a\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"format_stream(rs):\\n \\\"\\\"\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Collect all chunks first, then format them properly\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\\"\\\"\\\"\\n chunks = []\\n async for chunk in rs:\\n \"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"chunks.append(chunk)\\n \\n result\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = []\\n in_thinking = False\\n \\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"for i, chunk in enumerate(chunks):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n # Handle thinking\\n if nested_idx(chunk, 'choices\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"', 0, 'delta', 'reasoning_content'):\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"if not in_thinking:\\n result.append('\ud83e\udde0')\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n in_thinking = True\\n elif in_thinking:\\n result.\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"append('\\\\n\\\\n')\\n in_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"thinking = False\\n \\n #\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Handle content\\n if content := nested_idx(chunk, \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'choices', 0, 'delta', 'content'):\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"result.append(content)\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n # Handle tools (simpler since\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" we have full context)\\n if tc\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" := _fmt_tool_call(chunk):\\n result.append(\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tc)\\n elif tr := _fmt_tool_result\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(chunk):\\n result.append(tr)\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"# Check if this is the last tool result\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" in sequence\\n if i == len(chunks\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\")-1 or not _fmt_tool_result\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(chunks[i+1]):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n result.append(\\\"\\\\n\\\\n\\\\n\\\")\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n \\n yield ''.join(result)\\n```\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n## Option 3: Generator with Explicit\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" State\\n\\n```python\\nasync def aformat_stream(rs):\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n async def _format_generator():\\n was\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_thinking = False\\n tool_results = []\\n \\n async\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" for chunk in rs:\\n # Thinking\\n if nested_idx(chunk\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", 'choices', 0, 'delta', 'reasoning_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"content'):\\n if not was_thinking:\\n yiel\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d '\ud83e\udde0'\\n was_thinking = True\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"elif was_thinking:\\n yield '\\\\n\\\\n'\\n was\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_thinking = False\\n \\n # Content\\n if content := neste\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d_idx(chunk, 'choices', 0, 'delta',\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 'content'):\\n yield content\\n \\n # Tools -\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" collect results, emit when complete\\n if tc := _fmt\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"_tool_call(chunk):\\n yield tc\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tool_results = [] # Reset for new tool sequence\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n elif tr := _fmt_tool_result(chunk):\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tool_results.append(tr)\\n yiel\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d tr\\n elif tool_results: # En\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d of tool sequence\\n yield \\\"\\\\n\\\\n\\\\n\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n tool_results = []\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n \\n async for formatted in _format_generator():\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"yield formatted\\n```\\n\\n## Recommendation\\n\\nI'd go\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" with **Option 1** (State Class) because\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" it:\\n- Makes state explicit and contained\\n- Is\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" easy to test each formatting method independently \\n- Keeps the async\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" generator simple\\n- Is extens\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ible for future formatting needs\\n\\nThe key insight is that streaming\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" formatters naturally need state, so we shoul\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d make that state explicit rather than hiding it in function\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" attributes. What do you think?\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":27535,\"cache_read_input_tokens\":0,\"output_tokens\":1175} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"} +{"key": "bb07cf5b", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01AptfcftZu84LpVufobtp8i\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":789,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_017PenLZXRVF7PecWZARZQBy\",\"name\":\"simple_add\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\\\": 10\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"b\\\": 5}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01SUwaZxGKbhur6E5T1ubivq\",\"name\":\"multiply\",\"input\":{}} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"a\\\": 2\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"b\\\": 1}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":789,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":121} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "4abe9d2a", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01Un7MDtc4o8HuxMRh4Z5NnD\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":972,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":3,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nThe results\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" are:\\n- (10 + 5) = **\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"15**\\n- (2 *\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 1) = **2**\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":972,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":34} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "2a6c206a", "response": "{\"id\":\"msg_019iek4r1kkRAeCNEkdQqgME\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} +{"key": "2d77e722", "response": "{\"id\":\"msg_016pFvqKD8d1ZYRoVfZqxQYD\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "07f00e35", "response": "{\"id\":\"msg_019fwXMNf9gq9ziJ23f9iPZY\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe answer is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":810,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} diff --git a/lisette/_modidx.py b/lisette/_modidx.py index 7245f84..c7dbc95 100644 --- a/lisette/_modidx.py +++ b/lisette/_modidx.py @@ -17,11 +17,14 @@ 'lisette.core._add_cache_control': ('core.html#_add_cache_control', 'lisette/core.py'), 'lisette.core._alite_call_func': ('core.html#_alite_call_func', 'lisette/core.py'), 'lisette.core._clean_str': ('core.html#_clean_str', 'lisette/core.py'), + 'lisette.core._content': ('core.html#_content', 'lisette/core.py'), 'lisette.core._fmt_thinking': ('core.html#_fmt_thinking', 'lisette/core.py'), + 'lisette.core._fmt_tool': ('core.html#_fmt_tool', 'lisette/core.py'), 'lisette.core._fmt_tool_call': ('core.html#_fmt_tool_call', 'lisette/core.py'), 'lisette.core._fmt_tool_result': ('core.html#_fmt_tool_result', 'lisette/core.py'), 'lisette.core._has_search': ('core.html#_has_search', 'lisette/core.py'), 'lisette.core._is_img': ('core.html#_is_img', 'lisette/core.py'), + 'lisette.core._is_thinking': ('core.html#_is_thinking', 'lisette/core.py'), 'lisette.core._lite_call_func': ('core.html#_lite_call_func', 'lisette/core.py'), 'lisette.core._mk_content': ('core.html#_mk_content', 'lisette/core.py'), 'lisette.core._mk_img': ('core.html#_mk_img', 'lisette/core.py'), @@ -38,6 +41,7 @@ 'lisette/core.py'), 'lisette.core.mk_msg': ('core.html#mk_msg', 'lisette/core.py'), 'lisette.core.mk_msgs': ('core.html#mk_msgs', 'lisette/core.py'), + 'lisette.core.mk_stream_formatter': ('core.html#mk_stream_formatter', 'lisette/core.py'), 'lisette.core.mk_tc': ('core.html#mk_tc', 'lisette/core.py'), 'lisette.core.mk_tc_req': ('core.html#mk_tc_req', 'lisette/core.py'), 'lisette.core.mk_tc_result': ('core.html#mk_tc_result', 'lisette/core.py'), diff --git a/lisette/core.py b/lisette/core.py index a068a65..be69ae4 100644 --- a/lisette/core.py +++ b/lisette/core.py @@ -5,7 +5,7 @@ # %% auto 0 __all__ = ['sonn45', 'effort', 'patch_litellm', 'mk_msg', 'mk_msgs', 'stream_with_complete', 'lite_mk_func', 'cite_footnote', 'cite_footnotes', 'Chat', 'random_tool_id', 'mk_tc', 'mk_tc_req', 'mk_tc_result', 'mk_tc_results', - 'astream_with_complete', 'AsyncChat', 'aformat_stream', 'adisplay_stream'] + 'astream_with_complete', 'AsyncChat', 'aformat_stream', 'mk_stream_formatter', 'adisplay_stream'] # %% ../nbs/00_core.ipynb import asyncio, base64, json, litellm, mimetypes, random, string @@ -403,10 +403,49 @@ async def aformat_stream(rs): yield "\n\n\n" tool_count = 0 +# %% ../nbs/00_core.ipynb +def _fmt_thinking(o): + return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False) + +def _fmt_tool_call(o): + if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None + calls = '\n'.join([f" `{tc.function.name}({_trunc_str(tc.function.arguments)})`" for tc in c]) + return f"\n
\n\n{calls}\n" + +def _fmt_tool_result(o): + if not isinstance(o, dict) or 'tool_call_id' not in o: return None + return f" - `{_trunc_str(_clean_str(o.get('content')))}`\n" + +def _is_thinking(o): return nested_idx(o, 'choices', 0, 'delta', 'reasoning_content') + +def _content(o): return nested_idx(o, 'choices', 0, 'delta', 'content') + +def _fmt_tool(o): + if isinstance(o, ModelResponse): return _fmt_tool_call(o) + return _fmt_tool_result(o) + +def mk_stream_formatter(): + "Create a stream formatter function with closure state" + th,n,i = False,0,0 + + def fmt(o): + nonlocal th,n,i + if isinstance(o, ModelResponseStream): + if _is_thinking(o): th=True; return '🧠' + if th: th=False; return '\n\n' + return _content(o) or '' + if (res := _fmt_tool(o)): + if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0 + else: i+=1; res += "\n
\n\n" if i>=n else "" + return res + return '' + return fmt + # %% ../nbs/00_core.ipynb async def adisplay_stream(rs): "Use IPython.display to markdown display the response stream." md = '' async for o in aformat_stream(rs): - md+=o + md+=o if o else '' display(Markdown(md),clear=True) + return md diff --git a/nbs/00_core.ipynb b/nbs/00_core.ipynb index 2f5eea5..0a74eb4 100644 --- a/nbs/00_core.ipynb +++ b/nbs/00_core.ipynb @@ -1939,7 +1939,7 @@ { "data": { "text/plain": [ - "'toolu_uFpOvjzRiiz9Knt7Wp3O3PonA'" + "'toolu_HAWGYipg63FDcvtOCdKXLo90D'" ] }, "execution_count": null, @@ -1985,7 +1985,7 @@ "text/plain": [ "{'index': 1,\n", " 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'},\n", - " 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", + " 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", " 'type': 'function'}" ] }, @@ -2026,7 +2026,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5, \"b\": 7}', name='simple_add'), id='toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', type='function')], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5, \"b\": 7}', name='simple_add'), id='toolu_PeSBGlKmjj7YNLdu7d20tdsGz', type='function')], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2072,7 +2072,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2107,7 +2107,7 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n" ] } @@ -2154,7 +2154,7 @@ "text/plain": [ "{'index': 1,\n", " 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'},\n", - " 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", + " 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", " 'type': 'function'}" ] }, @@ -2176,7 +2176,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", + "{'tool_call_id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '12'}" @@ -2219,7 +2219,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2240,7 +2240,7 @@ { "data": { "text/plain": [ - "[{'tool_call_id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", + "[{'tool_call_id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '12'}]" @@ -2311,9 +2311,9 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n", - "{'tool_call_id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", + "{'tool_call_id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", "\n", "Message(content='\\n\\nThe result of 5 + 7 is **12**.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", "\n" @@ -2351,7 +2351,7 @@ { "data": { "text/plain": [ - "[{'tool_call_id': 'toolu_Md3PKUgcxIq4sOsgDHyb3pVEM',\n", + "[{'tool_call_id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '13'}]" @@ -2429,7 +2429,7 @@ { "data": { "text/plain": [ - "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_0tJgwm2KIhJjRf5SPxwSdCmv9', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_H383xcEvm5L91WTRbD3A7EhSa', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_ZvJjHG6icmlhYQHVboNambds5', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_TpcilPbegJbUaYvdod2k8i8hl', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2509,11 +2509,11 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_0tJgwm2KIhJjRf5SPxwSdCmv9', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_H383xcEvm5L91WTRbD3A7EhSa', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_ZvJjHG6icmlhYQHVboNambds5', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_TpcilPbegJbUaYvdod2k8i8hl', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n", - "{'tool_call_id': 'toolu_0tJgwm2KIhJjRf5SPxwSdCmv9', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", + "{'tool_call_id': 'toolu_ZvJjHG6icmlhYQHVboNambds5', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", "\n", - "{'tool_call_id': 'toolu_H383xcEvm5L91WTRbD3A7EhSa', 'role': 'tool', 'name': 'simple_add', 'content': '13'}\n", + "{'tool_call_id': 'toolu_TpcilPbegJbUaYvdod2k8i8hl', 'role': 'tool', 'name': 'simple_add', 'content': '13'}\n", "\n", "Message(content='\\n\\nThe answer is **12**. 5 + 7 = 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", "\n" @@ -3970,6 +3970,632 @@ " tool_count = 0" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e8cd17c", + "metadata": {}, + "outputs": [], + "source": [ + "class StreamFormatter:\n", + " def __init__(self):\n", + " self.was_thinking = False\n", + " self.tool_count = 0\n", + " self.tool_idx = 0\n", + " \n", + " def format_thinking(self, o):\n", + " txt = '🧠' if nested_idx(o, 'choices', 0, 'delta', 'reasoning_content') else ''\n", + " if txt:\n", + " self.was_thinking = True\n", + " return txt\n", + " elif self.was_thinking:\n", + " self.was_thinking = False\n", + " return '\\n\\n'\n", + " return ''\n", + " \n", + " def format_content(self, o): return nested_idx(o, 'choices', 0, 'delta', 'content') or ''\n", + " \n", + " def format_tool(self, o):\n", + " if tc := _fmt_tool_call(o):\n", + " self.tool_count = len(o.choices[0].message.tool_calls)\n", + " self.tool_idx = 0\n", + " return tc\n", + " \n", + " if tr := _fmt_tool_result(o):\n", + " self.tool_idx += 1\n", + " result = tr\n", + " if self.tool_idx >= self.tool_count:\n", + " result += \"\\n\\n\\n\"\n", + " self.tool_count = 0\n", + " return result\n", + " return ''\n", + " \n", + " def format(self, o): return ''.join([self.format_thinking(o), self.format_content(o), self.format_tool(o)])\n", + "\n", + "async def aformat_stream(rs):\n", + " formatter = StreamFormatter()\n", + " async for o in rs: yield formatter.format(o)" + ] + }, + { + "cell_type": "markdown", + "id": "a4e6a5ec", + "metadata": {}, + "source": [ + "I'm starting to like the streamformatter pattern better. Is there a more \"fast.ai\" way of doing this?\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "Yes! The fast.ai approach would be much more functional and concise. Instead of a class, you could use a closure to maintain state:\n", + "\n", + "```python\n", + "def mk_stream_formatter():\n", + " \"Create a stream formatter function with closure state\"\n", + " thinking,tc,ti = False,0,0\n", + " \n", + " def fmt(o):\n", + " nonlocal thinking,tc,ti\n", + " if isinstance(o, ModelResponseStream):\n", + " if nested_idx(o, 'choices', 0, 'delta', 'reasoning_content'): thinking = True; return '🧠'\n", + " elif thinking: thinking = False; return '\\n\\n'\n", + " return nested_idx(o, 'choices', 0, 'delta', 'content') or ''\n", + " elif isinstance(o, ModelResponse):\n", + " if res := _fmt_tool_call(o): tc,ti = len(o.choices[0].message.tool_calls),0; return res\n", + " elif res := _fmt_tool_result(o):\n", + " ti += 1\n", + " return res + (\"\\n\\n\\n\" if ti >= tc else \"\")\n", + " return ''\n", + " return fmt\n", + "```\n", + "\n", + "Then use it like:\n", + "\n", + "```python\n", + "async def aformat_stream(rs):\n", + " fmt = mk_stream_formatter()\n", + " async for o in rs: yield fmt(o)\n", + "```\n", + "\n", + "This is much more in the fast.ai style - functional, concise, uses short variable names (`tc` for tool_count, `ti` for tool_index), combines logic on single lines with `;`, and avoids unnecessary OOP structure." + ] + }, + { + "cell_type": "markdown", + "id": "c816c0a5", + "metadata": {}, + "source": [ + "I love it. Do you think there are any other improvements we could make in the fastai style to make this code more understandable?\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "Yes! A few more fast.ai style improvements:\n", + "\n", + "```python\n", + "def mk_stream_formatter():\n", + " \"Create a stream formatter function with closure state\"\n", + " th,n,i = False,0,0\n", + " \n", + " def fmt(o):\n", + " nonlocal th,n,i\n", + " if isinstance(o, ModelResponseStream):\n", + " if nested_idx(o, 'choices', 0, 'delta', 'reasoning_content'): th=True; return '🧠'\n", + " if th: th=False; return '\\n\\n'\n", + " return nested_idx(o, 'choices', 0, 'delta', 'content') or ''\n", + " if isinstance(o, ModelResponse) and (res := _fmt_tool_call(o)): n,i = len(o.choices[0].message.tool_calls),0; return res\n", + " if (res := _fmt_tool_result(o)): i+=1; return res + (\"\\n\\n\\n\" if i>=n else \"\")\n", + " return ''\n", + " return fmt\n", + "```\n", + "\n", + "Key improvements:\n", + "- Even shorter var names (`th` for thinking, `n` for count, `i` for index)\n", + "- Flattened `elif` to separate `if` statements for cleaner flow\n", + "- Used walrus operator more consistently\n", + "- Combined assignment and return logic on single lines\n", + "- Removed unnecessary `elif` chains\n", + "\n", + "This makes it more readable while staying true to the functional, concise fast.ai approach." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5544fb64", + "metadata": {}, + "outputs": [], + "source": [ + "def _fmt_thinking(o):\n", + " return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False)\n", + "\n", + "def _fmt_tool_call(o):\n", + " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", + " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", + " return f\"\\n
\\n\\n{calls}\\n\"\n", + "\n", + "def _fmt_tool_result(o):\n", + " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", + " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", + "\n", + "def _is_thinking(o): return nested_idx(o, 'choices', 0, 'delta', 'reasoning_content')\n", + "\n", + "def _content(o): return nested_idx(o, 'choices', 0, 'delta', 'content')\n", + "\n", + "def mk_stream_formatter():\n", + " \"Create a stream formatter function with closure state\"\n", + " th,n,i = False,0,0\n", + " \n", + " def fmt(o):\n", + " nonlocal th,n,i\n", + " if isinstance(o, ModelResponseStream):\n", + " if _is_thinking(o): th=True; return '🧠'\n", + " if th: th=False; return '\\n\\n'\n", + " return _content(o) or ''\n", + " if isinstance(o, ModelResponse) and (res := _fmt_tool_call(o)): n,i = len(o.choices[0].message.tool_calls),0; return res\n", + " if (res := _fmt_tool_result(o)): i+=1; return res + (\"\\n
\\n\\n\" if i>=n else \"\")\n", + " return ''\n", + " return fmt\n", + "\n", + "async def aformat_stream(rs):\n", + " fmt = mk_stream_formatter()\n", + " async for o in rs: yield fmt(o)" + ] + }, + { + "cell_type": "markdown", + "id": "026c2f63", + "metadata": {}, + "source": [ + "I started to piece out a few helper functions to make mK_stream_formatter even more understandable. Do you think this is a good way of going about it?\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "Absolutely! This is perfect fast.ai style - breaking complex logic into small, focused functions with clear names. The main formatter flow is now much cleaner and easier to follow.\n", + "\n", + "You could make it even more concise by extracting the tool logic too:\n", + "\n", + "```python\n", + "def _fmt_tool(o):\n", + " if isinstance(o, ModelResponse): return _fmt_tool_call(o)\n", + " return _fmt_tool_result(o)\n", + "\n", + "def mk_stream_formatter():\n", + " \"Create a stream formatter function with closure state\"\n", + " th,n,i = False,0,0\n", + " \n", + " def fmt(o):\n", + " nonlocal th,n,i\n", + " if isinstance(o, ModelResponseStream):\n", + " if _is_thinking(o): th=True; return '🧠'\n", + " if th: th=False; return '\\n\\n'\n", + " return _content(o) or ''\n", + " if (res := _fmt_tool(o)): \n", + " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0\n", + " else: i+=1; res += \"\\n\\n\\n\" if i>=n else \"\"\n", + " return res\n", + " return ''\n", + " return fmt\n", + "```\n", + "\n", + "This approach of composing small, single-purpose functions is exactly what makes code readable and maintainable in the fast.ai style." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "def84abe", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def _fmt_thinking(o):\n", + " return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False)\n", + "\n", + "def _fmt_tool_call(o):\n", + " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", + " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", + " return f\"\\n
\\n\\n{calls}\\n\"\n", + "\n", + "def _fmt_tool_result(o):\n", + " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", + " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", + "\n", + "def _is_thinking(o): return nested_idx(o, 'choices', 0, 'delta', 'reasoning_content')\n", + "\n", + "def _content(o): return nested_idx(o, 'choices', 0, 'delta', 'content')\n", + "\n", + "def _fmt_tool(o):\n", + " if isinstance(o, ModelResponse): return _fmt_tool_call(o)\n", + " return _fmt_tool_result(o)\n", + "\n", + "def mk_stream_formatter():\n", + " \"Create a stream formatter function with closure state\"\n", + " th,n,i = False,0,0\n", + " \n", + " def fmt(o):\n", + " nonlocal th,n,i\n", + " if isinstance(o, ModelResponseStream):\n", + " if _is_thinking(o): th=True; return '🧠'\n", + " if th: th=False; return '\\n\\n'\n", + " return _content(o) or ''\n", + " if (res := _fmt_tool(o)): \n", + " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0\n", + " else: i+=1; res += \"\\n
\\n\\n\" if i>=n else \"\"\n", + " return res\n", + " return ''\n", + " return fmt" + ] + }, + { + "cell_type": "markdown", + "id": "cd9b33ab", + "metadata": {}, + "source": [ + "Nice! This is just getting better and better. I'm wondering if we can leverage any other exported methods in this dialog to reduce some of the helpers. Or I dont know what would you recommend next?\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "Looking at your existing code, you already have `delta_text` that handles thinking and content extraction! You could simplify by using it:\n", + "\n", + "```python\n", + "def mk_stream_formatter():\n", + " \"Create a stream formatter function with closure state\"\n", + " n,i = 0,0\n", + " \n", + " def fmt(o):\n", + " nonlocal n,i\n", + " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " if (res := _fmt_tool(o)): \n", + " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0\n", + " else: i+=1; res += \"\\n\\n\\n\" if i>=n else \"\"\n", + " return res\n", + " return ''\n", + " return fmt\n", + "```\n", + "\n", + "This eliminates `_is_thinking`, `_content`, and the thinking state variable since `delta_text` already handles the thinking logic with the `🧠` and `\\n\\n` transitions.\n", + "\n", + "You could also consider making the tool counting more functional by extracting it into a helper, but the current approach is quite clean already." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4c5f358", + "metadata": {}, + "outputs": [], + "source": [ + "def _fmt_tool_call(o):\n", + " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", + " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", + " return f\"\\n
\\n\\n{calls}\\n\"\n", + "\n", + "def _fmt_tool_result(o):\n", + " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", + " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", + "\n", + "def _fmt_tool(o):\n", + " if isinstance(o, ModelResponse): return _fmt_tool_call(o)\n", + " return _fmt_tool_result(o)\n", + "\n", + "def mk_stream_formatter():\n", + " \"Create a stream formatter function with closure state\"\n", + " n,i = 0,0\n", + " \n", + " def fmt(o):\n", + " nonlocal n,i\n", + " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " if (res := _fmt_tool(o)): \n", + " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0\n", + " else: i+=1; res += \"\\n
\\n\\n\" if i>=n else \"\"\n", + " return res\n", + " return ''\n", + " return fmt" + ] + }, + { + "cell_type": "markdown", + "id": "d93149e5", + "metadata": {}, + "source": [ + "Wow this is looking really great, what were you recommending about the tool counting logic?\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "You could extract the tool counting logic into a helper to make the main flow cleaner:\n", + "\n", + "```python\n", + "def mk_tool_counter():\n", + " n,i = 0,0\n", + " def count(o, res):\n", + " nonlocal n,i\n", + " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0; return res\n", + " i+=1; return res + (\"\\n\\n\\n\" if i>=n else \"\")\n", + " return count\n", + "\n", + "def mk_stream_formatter():\n", + " \"Create a stream formatter function with closure state\"\n", + " count_tools = mk_tool_counter()\n", + " \n", + " def fmt(o):\n", + " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " if (res := _fmt_tool(o)): return count_tools(o, res)\n", + " return ''\n", + " return fmt\n", + "```\n", + "\n", + "This separates concerns - the formatter handles stream routing while the counter manages tool state. The main logic becomes much cleaner and easier to follow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0195d1b4", + "metadata": {}, + "outputs": [], + "source": [ + "def _fmt_tool_call(o):\n", + " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", + " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", + " return f\"\\n
\\n\\n{calls}\\n\"\n", + "\n", + "def _fmt_tool_result(o):\n", + " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", + " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", + "\n", + "def _fmt_tool(o): return _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o)\n", + "\n", + "def mk_tool_counter():\n", + " n,i = 0,0\n", + " def count(o, res):\n", + " nonlocal n,i\n", + " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0; return res\n", + " i+=1; return res + (\"\\n
\\n\\n\" if i>=n else \"\")\n", + " return count\n", + "\n", + "def mk_stream_formatter():\n", + " count_tools = mk_tool_counter()\n", + " \n", + " def fmt(o):\n", + " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " if (res := _fmt_tool(o)): return count_tools(o, res)\n", + " return ''\n", + " return fmt\n", + "\n", + "async def aformat_stream(rs):\n", + " fmt = mk_stream_formatter()\n", + " async for o in rs: yield fmt(o)" + ] + }, + { + "cell_type": "markdown", + "id": "23b6c9aa", + "metadata": {}, + "source": [ + "I'm still loving this. Any other logic you can tell can be removed by leveraging other parts of the dialog? Or other general improvements?\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "Looking at your existing patterns, you could inline `_fmt_tool` since it's just a simple dispatch:\n", + "\n", + "```python\n", + "def mk_stream_formatter():\n", + " count_tools = mk_tool_counter()\n", + " \n", + " def fmt(o):\n", + " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " if isinstance(o, ModelResponse): res = _fmt_tool_call(o)\n", + " else: res = _fmt_tool_result(o)\n", + " if res: return count_tools(o, res)\n", + " return ''\n", + " return fmt\n", + "```\n", + "\n", + "Or even more concise:\n", + "\n", + "```python\n", + "def mk_stream_formatter():\n", + " count_tools = mk_tool_counter()\n", + " \n", + " def fmt(o):\n", + " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " res = _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o)\n", + " return count_tools(o, res) if res else ''\n", + " return fmt\n", + "```\n", + "\n", + "This removes the extra function layer while keeping the logic clear. The code is already quite lean and follows good functional patterns!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd63e67", + "metadata": {}, + "outputs": [], + "source": [ + "def _fmt_tool_call(o):\n", + " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", + " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", + " return f\"\\n
\\n\\n{calls}\\n\"\n", + "\n", + "def _fmt_tool_result(o):\n", + " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", + " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", + "\n", + "def mk_tool_counter():\n", + " n,i = 0,0\n", + " def count(o, res):\n", + " nonlocal n,i\n", + " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0; return res\n", + " i+=1; return res + (\"\\n
\\n\\n\" if i>=n else \"\")\n", + " return count\n", + "\n", + "def mk_stream_formatter():\n", + " count_tools = mk_tool_counter()\n", + " \n", + " def fmt(o):\n", + " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " res = _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o)\n", + " return count_tools(o, res) if res else ''\n", + " return fmt\n", + "\n", + "async def aformat_stream(rs):\n", + " fmt = mk_stream_formatter()\n", + " async for o in rs: yield fmt(o)" + ] + }, + { + "cell_type": "markdown", + "id": "ffc73dd4", + "metadata": {}, + "source": [ + "Awesome. Any other recommendations?\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "The code looks excellent! One small optimization - you could ensure the formatting functions always return strings to eliminate the `if res` check:\n", + "\n", + "```python\n", + "def _fmt_tool_call(o):\n", + " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return ''\n", + " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", + " return f\"\\n
\\n\\n{calls}\\n\"\n", + "\n", + "def _fmt_tool_result(o):\n", + " if not isinstance(o, dict) or 'tool_call_id' not in o: return ''\n", + " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", + "\n", + "def mk_stream_formatter():\n", + " count_tools = mk_tool_counter()\n", + " \n", + " def fmt(o):\n", + " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " res = _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o)\n", + " return count_tools(o, res) if res else res\n", + " return fmt\n", + "```\n", + "\n", + "This makes the control flow even cleaner by guaranteeing string returns everywhere." + ] + }, + { + "cell_type": "markdown", + "id": "3aeb1a46", + "metadata": {}, + "source": [ + "One more thing, Is there anything we should be careful of to improve in delta_text since we are now exporting it and using it.\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "Looking at `delta_text`, a few potential improvements:\n", + "\n", + "```python\n", + "def delta_text(msg):\n", + " \"Extract printable content from streaming delta, return empty string if nothing to print\"\n", + " if not (c := nested_idx(msg, 'choices', 0)): return ''\n", + " if not hasattr(c,'delta'): return ''\n", + " delta = c.delta\n", + " if delta.content: return delta.content\n", + " if delta.tool_calls:\n", + " res = ''.join(f\"🔧 {tc.function.name}\" for tc in delta.tool_calls if tc.id and tc.function.name)\n", + " return f'\\n{res}\\n' if res else ''\n", + " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", + " return ''\n", + "```\n", + "\n", + "Key improvements:\n", + "- Use `nested_idx` for safer access (consistent with your other code)\n", + "- Always return strings instead of `None` for cleaner usage\n", + "- Remove the commented debug code\n", + "- Ensure empty string return for tool calls that don't match criteria\n", + "\n", + "This makes it more robust and consistent with the rest of your codebase." + ] + }, + { + "cell_type": "markdown", + "id": "135a39a0", + "metadata": {}, + "source": [ + "If multiple tools are run at once I dont love how they are printed on separate lines\n", + "\n", + "'\\n🔧 simple_add\\n\\n🔧 multiply\\n\\n
\\n\\n `simple_add({\"a\": 10, \"b\": 5})`\\n `multiply({\"a\": 2, \"b\": 1})`\\n - `15`\\n - `2`\\n\\n
\\n\\n\\n\\nThe results are:\\n- (10 + 5) = **15**\\n- (2 * 1) = **2**'\n", + "\n", + "can we fix that?\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "The issue is in `delta_text` - when multiple tool calls come in, they're being formatted with too much spacing. Let's fix it:\n", + "\n", + "```python\n", + "def delta_text(msg):\n", + " \"Extract printable content from streaming delta, return empty string if nothing to print\"\n", + " if not (c := nested_idx(msg, 'choices', 0)): return ''\n", + " if not hasattr(c,'delta'): return ''\n", + " delta = c.delta\n", + " if delta.content: return delta.content\n", + " if delta.tool_calls:\n", + " res = ' '.join(f\"🔧 {tc.function.name}\" for tc in delta.tool_calls if tc.id and tc.function.name)\n", + " return f'\\n{res}\\n' if res else ''\n", + " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", + " return ''\n", + "```\n", + "\n", + "This changes the `''.join()` to `' '.join()` so multiple tools appear as `🔧 simple_add 🔧 multiply` on one line instead of separate lines." + ] + }, + { + "cell_type": "markdown", + "id": "be3a3e72", + "metadata": {}, + "source": [ + "No you see there is a `\\n` in between the tool calls still. not sure why\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "Ah, I see the issue! Each tool call comes as a separate delta, so each one gets wrapped with `\\n`. Let's remove those:\n", + "\n", + "```python\n", + "def delta_text(msg):\n", + " \"Extract printable content from streaming delta, return empty string if nothing to print\"\n", + " if not (c := nested_idx(msg, 'choices', 0)): return ''\n", + " if not hasattr(c,'delta'): return ''\n", + " delta = c.delta\n", + " if delta.content: return delta.content\n", + " if delta.tool_calls:\n", + " res = ''.join(f\"🔧 {tc.function.name}\" for tc in delta.tool_calls if tc.id and tc.function.name)\n", + " return f'\\n{res}' if res else ''\n", + " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", + " return ''\n", + "```\n", + "\n", + "This removes the trailing `\\n` from tool calls so they appear as `🔧 simple_add🔧 multiply` on one line." + ] + }, + { + "cell_type": "markdown", + "id": "eda91fdf", + "metadata": {}, + "source": [ + "Could we instead have it be 🔧 [simple_add, multiply]\n", + "\n", + "##### 🤖Reply🤖\n", + "\n", + "```python\n", + "def delta_text(msg):\n", + " \"Extract printable content from streaming delta, return empty string if nothing to print\"\n", + " if not (c := nested_idx(msg, 'choices', 0)): return ''\n", + " if not hasattr(c,'delta'): return ''\n", + " delta = c.delta\n", + " if delta.content: return delta.content\n", + " if delta.tool_calls:\n", + " names = [tc.function.name for tc in delta.tool_calls if tc.id and tc.function.name]\n", + " return f'\\n🔧 [{\", \".join(names)}]\\n' if names else ''\n", + " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", + " return ''\n", + "```" + ] + }, { "cell_type": "code", "execution_count": null, @@ -3982,8 +4608,9 @@ " \"Use IPython.display to markdown display the response stream.\"\n", " md = ''\n", " async for o in aformat_stream(rs): \n", - " md+=o\n", - " display(Markdown(md),clear=True)" + " md+=o if o else ''\n", + " display(Markdown(md),clear=True)\n", + " return md" ] }, { @@ -4042,7 +4669,7 @@ "source": [ "chat = AsyncChat(model, tools=[async_add])\n", "res = await chat(\"What is 5 + 7? Use the tool to calculate it.\", stream=True)\n", - "await adisplay_stream(res)" + "await adisplay_stream(res);" ] }, { @@ -4085,7 +4712,7 @@ "source": [ "chat = AsyncChat(model)\n", "res = await chat(\"Briefly, what's the most efficient way to sort a list of 1000 random integers?\", think='l',stream=True)\n", - "await adisplay_stream(res)" + "await adisplay_stream(res);" ] }, { @@ -4145,7 +4772,17 @@ "res = await chat(\"Calculate ((10 + 5) * 3) / (2 + 1) Explain step by step.\", \n", " max_steps=3, stream=True,\n", " final_prompt=\"Please wrap-up for now and summarize how far we got.\")\n", - "await adisplay_stream(res)" + "md = await adisplay_stream(res);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2acea12", + "metadata": {}, + "outputs": [], + "source": [ + "assert md.count(' Date: Thu, 2 Oct 2025 12:14:47 -0700 Subject: [PATCH 3/3] clean --- cachy.jsonl | 5 + lisette/_modidx.py | 6 +- lisette/core.py | 97 +++---- nbs/00_core.ipynb | 691 +++------------------------------------------ 4 files changed, 80 insertions(+), 719 deletions(-) diff --git a/cachy.jsonl b/cachy.jsonl index a04ad77..96f9157 100644 --- a/cachy.jsonl +++ b/cachy.jsonl @@ -241,3 +241,8 @@ {"key": "2a6c206a", "response": "{\"id\":\"msg_019iek4r1kkRAeCNEkdQqgME\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} {"key": "2d77e722", "response": "{\"id\":\"msg_016pFvqKD8d1ZYRoVfZqxQYD\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} {"key": "07f00e35", "response": "{\"id\":\"msg_019fwXMNf9gq9ziJ23f9iPZY\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe answer is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":810,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "736cff74", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01VCC1dVQJnMC95B5MJGDsg2\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":30505,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":30505,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Looking\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" at the error, there's a JSON parsing issue in\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" your notebook file. The error indicates \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Extra data: line 4307 \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"column 1 (char 201491)\\\" which means\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" there's invalid JSON structure around that location\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".\\n\\nHere's how\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" to find and fix it:\\n\\n```\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"python\\n# Read the notebook file to\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" examine the problematic area\\nwith open('\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"00_core.ipynb', 'r') as\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" f:\\n content\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = f.read()\\n\\n# Check\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" around the\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" error position\\nerror_pos\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" = 201491\\nstart =\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" max(0, error_pos -\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 100)\\nend = min(len(\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"content), error_pos + 100\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\")\\nprint(\\\"Content around error position:\\\")\\nprint(repr\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"(content[start:end]))\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n# Also check line\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 4307\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nlines = content.split('\\\\n')\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\nif len(lines) > 4307\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\":\\n print(f\\\"\\\\nLine 4307:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" {repr(lines[4306\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"])}\\\") # 0-indexed\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n print(f\\\"Line 4308\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\": {repr(lines[4307])}\\\")\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n print(f\\\"Line 4309: {repr(lines[4308\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"])}\\\")\\n```\\n\\nCommon\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" causes of this error in Jupyter notebooks:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n1. **Trailing commas** in\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" JSON arrays or objects\\n2. **Un\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"escaped quotes** in cell content (\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"especially in code cells with strings)\\n3. **Missing\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" commas** between JSON elements\\n4. **Mal\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"formed cell structure**\\n\\nTo fix it\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" quickly, you can also try:\\n\\n```python\\nimport\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" json\\n\\n# Try to load an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d identify the exact issue\\ntry:\\n with open\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"('00_core.ipynb', 'r') as f:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n json.load(f)\\nexcept json.\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"JSONDecodeError as e:\\n print(f\\\"Error\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" at line {e.lineno},\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" column {e.colno}\\\")\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n print(f\\\"Error message\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\": {e.msg}\\\")\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n # Read the file an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d show the problematic line\\n with open('00_core\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".ipynb', 'r')\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" as f:\\n lines = f.readlines()\\n \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"if e.lineno <=\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" len(lines):\\n print(\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"f\\\"Problematic line: {repr(lines\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"[e.lineno-1])\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"}\\\")\\n```\\n\\nThe most\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" likely culprit is unescape\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d content in one of your code cells,\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" possibly related to the image data or JSON\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" strings in your tool calling examples.\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":30505,\"cache_read_input_tokens\":0,\"output_tokens\":551} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "143ac91c", "response": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_01KUCoQv7WDnB1S7sgcNtTa7\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":32015,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":32015,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Looking\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" at the error, this is from **Cell In\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"[126], line 2** which contains:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n```python\\n#| hide\\nimport nb\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"dev; nbdev.nbdev_\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"export()\\n```\\n\\nThe issue\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" is that there are **ANSI escape sequences** (\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"color codes) in the notebook's JSON that contain\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" invalid control characters. These escape sequences like `\\\\u001b[36m\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"` (cyan color), `\\\\u001b[39m\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"` (reset), etc. are being stored in the notebook's error\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" output or traceback.\\n\\nThis is the **last cell** in your\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" notebook - the export cell that's trying to run `nbdev_export()` but\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" failing because of the JSON corruption.\\n\\nThe problem likely occurre\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d when the previous error traceback (the one we\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" saw earlier with the JSONDecodeError) got save\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d into the notebook with these ANSI color codes,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" which are now causing the JSON to be invalid.\\n\\nTo fix this:\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n1. **Clear all outputs** in your\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" notebook:\\n - In Jupyter: Cell \u2192 All\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Output \u2192 Clear\\n - Or manually edit\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" the JSON to remove the problematic output\\n\\n2. **Or\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"** find and remove the specific problematic output around line 4286 in the\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" notebook file\\n\\n3. **Then** save an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d try the export again\\n\\nThe root cause is that\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" Jupyter stored a colorized error traceback in the notebook's\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" JSON, but those ANSI escape sequences contain control characters that break\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" JSON parsing.\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":32015,\"cache_read_input_tokens\":0,\"output_tokens\":317} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n"} +{"key": "d13b6faf", "response": "{\"id\":\"msg_01Cn9tbVyAwHgXUSEUEHq1w1\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result of 5 + 7 is **12**.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":18,\"service_tier\":\"standard\"}}"} +{"key": "0347ef47", "response": "{\"id\":\"msg_015ruqSMCGUcmJHBFdr1qVYd\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe result is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":718,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} +{"key": "f941f659", "response": "{\"id\":\"msg_01LSqufT7fFummr4QYLVWRC7\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-5-20250929\",\"content\":[{\"type\":\"text\",\"text\":\"\\n\\nThe answer is **12**. 5 + 7 = 12.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":810,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":22,\"service_tier\":\"standard\"}}"} diff --git a/lisette/_modidx.py b/lisette/_modidx.py index c7dbc95..eb4fe2b 100644 --- a/lisette/_modidx.py +++ b/lisette/_modidx.py @@ -17,14 +17,10 @@ 'lisette.core._add_cache_control': ('core.html#_add_cache_control', 'lisette/core.py'), 'lisette.core._alite_call_func': ('core.html#_alite_call_func', 'lisette/core.py'), 'lisette.core._clean_str': ('core.html#_clean_str', 'lisette/core.py'), - 'lisette.core._content': ('core.html#_content', 'lisette/core.py'), - 'lisette.core._fmt_thinking': ('core.html#_fmt_thinking', 'lisette/core.py'), - 'lisette.core._fmt_tool': ('core.html#_fmt_tool', 'lisette/core.py'), 'lisette.core._fmt_tool_call': ('core.html#_fmt_tool_call', 'lisette/core.py'), 'lisette.core._fmt_tool_result': ('core.html#_fmt_tool_result', 'lisette/core.py'), 'lisette.core._has_search': ('core.html#_has_search', 'lisette/core.py'), 'lisette.core._is_img': ('core.html#_is_img', 'lisette/core.py'), - 'lisette.core._is_thinking': ('core.html#_is_thinking', 'lisette/core.py'), 'lisette.core._lite_call_func': ('core.html#_lite_call_func', 'lisette/core.py'), 'lisette.core._mk_content': ('core.html#_mk_content', 'lisette/core.py'), 'lisette.core._mk_img': ('core.html#_mk_img', 'lisette/core.py'), @@ -36,6 +32,7 @@ 'lisette.core.astream_with_complete': ('core.html#astream_with_complete', 'lisette/core.py'), 'lisette.core.cite_footnote': ('core.html#cite_footnote', 'lisette/core.py'), 'lisette.core.cite_footnotes': ('core.html#cite_footnotes', 'lisette/core.py'), + 'lisette.core.delta_text': ('core.html#delta_text', 'lisette/core.py'), 'lisette.core.lite_mk_func': ('core.html#lite_mk_func', 'lisette/core.py'), 'lisette.core.litellm.ModelResponse._repr_markdown_': ( 'core.html#litellm.modelresponse._repr_markdown_', 'lisette/core.py'), @@ -46,6 +43,7 @@ 'lisette.core.mk_tc_req': ('core.html#mk_tc_req', 'lisette/core.py'), 'lisette.core.mk_tc_result': ('core.html#mk_tc_result', 'lisette/core.py'), 'lisette.core.mk_tc_results': ('core.html#mk_tc_results', 'lisette/core.py'), + 'lisette.core.mk_tool_counter': ('core.html#mk_tool_counter', 'lisette/core.py'), 'lisette.core.patch_litellm': ('core.html#patch_litellm', 'lisette/core.py'), 'lisette.core.random_tool_id': ('core.html#random_tool_id', 'lisette/core.py'), 'lisette.core.stream_with_complete': ('core.html#stream_with_complete', 'lisette/core.py')}}} diff --git a/lisette/core.py b/lisette/core.py index be69ae4..319456e 100644 --- a/lisette/core.py +++ b/lisette/core.py @@ -3,9 +3,10 @@ # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb. # %% auto 0 -__all__ = ['sonn45', 'effort', 'patch_litellm', 'mk_msg', 'mk_msgs', 'stream_with_complete', 'lite_mk_func', 'cite_footnote', - 'cite_footnotes', 'Chat', 'random_tool_id', 'mk_tc', 'mk_tc_req', 'mk_tc_result', 'mk_tc_results', - 'astream_with_complete', 'AsyncChat', 'aformat_stream', 'mk_stream_formatter', 'adisplay_stream'] +__all__ = ['sonn45', 'effort', 'patch_litellm', 'mk_msg', 'mk_msgs', 'stream_with_complete', 'lite_mk_func', 'delta_text', + 'cite_footnote', 'cite_footnotes', 'Chat', 'random_tool_id', 'mk_tc', 'mk_tc_req', 'mk_tc_result', + 'mk_tc_results', 'astream_with_complete', 'AsyncChat', 'mk_tool_counter', 'mk_stream_formatter', + 'aformat_stream', 'adisplay_stream'] # %% ../nbs/00_core.ipynb import asyncio, base64, json, litellm, mimetypes, random, string @@ -158,6 +159,20 @@ def _lite_call_func(tc,ns,raise_on_err=True): res = call_func(tc.function.name, fargs,ns=ns) return {"tool_call_id": tc.id, "role": "tool", "name": tc.function.name, "content": str(res)} +# %% ../nbs/00_core.ipynb +def delta_text(msg, show_tools=True): + "Extract printable content from streaming delta, return None if nothing to print" + c = msg.choices[0] + if not c: return c + if not hasattr(c,'delta'): return None #f'{c}' + delta = c.delta + if delta.content: return delta.content + if delta.tool_calls and show_tools: + res = ''.join(f"🔧 {tc.function.name}" for tc in delta.tool_calls if tc.id and tc.function.name) + if res: return f'\n{res}\n' + if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\n\n' + return None + # %% ../nbs/00_core.ipynb def _has_search(m): i = get_model_info(m) @@ -369,78 +384,36 @@ def _trunc_str(s, mx=2000, replace="…"): return s[:mx]+replace if len(s)>mx else s # %% ../nbs/00_core.ipynb -def _fmt_thinking(o): - return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False) - -def _fmt_tool_call(o): - if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None - calls = '\n'.join([f" `{tc.function.name}({_trunc_str(tc.function.arguments)})`" for tc in c]) - return f"\n
\n\n{calls}\n" - -def _fmt_tool_result(o): - if not isinstance(o, dict) or 'tool_call_id' not in o: return None - return f" - `{_trunc_str(_clean_str(o.get('content')))}`\n" - -async def aformat_stream(rs): - "Format the response stream for markdown display." - think,tool_count,tool_idx = False,0,0 - async for o in rs: - if isinstance(o, ModelResponseStream): - txt,is_think = _fmt_thinking(o) - if is_think: think = True - elif think: txt,think = '\n\n',False - if txt: yield txt - if c := o.choices[0].delta.content: yield c - elif isinstance(o, ModelResponse): - if res := _fmt_tool_call(o): - tool_count = len(o.choices[0].message.tool_calls) - tool_idx = 0 - yield res - elif res := _fmt_tool_result(o): - yield res - tool_idx += 1 - if tool_idx >= tool_count: - yield "\n
\n\n" - tool_count = 0 - -# %% ../nbs/00_core.ipynb -def _fmt_thinking(o): - return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False) - def _fmt_tool_call(o): - if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None + if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return '' calls = '\n'.join([f" `{tc.function.name}({_trunc_str(tc.function.arguments)})`" for tc in c]) return f"\n
\n\n{calls}\n" def _fmt_tool_result(o): - if not isinstance(o, dict) or 'tool_call_id' not in o: return None + if not isinstance(o, dict) or 'tool_call_id' not in o: return '' return f" - `{_trunc_str(_clean_str(o.get('content')))}`\n" -def _is_thinking(o): return nested_idx(o, 'choices', 0, 'delta', 'reasoning_content') - -def _content(o): return nested_idx(o, 'choices', 0, 'delta', 'content') - -def _fmt_tool(o): - if isinstance(o, ModelResponse): return _fmt_tool_call(o) - return _fmt_tool_result(o) +def mk_tool_counter(): + n,i = 0,0 + def count(o, res): + nonlocal n,i + if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0; return res + i+=1; return res + ("\n
\n\n" if i>=n else "") # Only add once we have enough tcs + return count def mk_stream_formatter(): - "Create a stream formatter function with closure state" - th,n,i = False,0,0 + count_tools = mk_tool_counter() def fmt(o): - nonlocal th,n,i - if isinstance(o, ModelResponseStream): - if _is_thinking(o): th=True; return '🧠' - if th: th=False; return '\n\n' - return _content(o) or '' - if (res := _fmt_tool(o)): - if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0 - else: i+=1; res += "\n
\n\n" if i>=n else "" - return res - return '' + if isinstance(o, ModelResponseStream): return delta_text(o, show_tools=False) + res = _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o) + return count_tools(o, res) if res else '' return fmt +async def aformat_stream(rs): + fmt = mk_stream_formatter() + async for o in rs: yield fmt(o) + # %% ../nbs/00_core.ipynb async def adisplay_stream(rs): "Use IPython.display to markdown display the response stream." diff --git a/nbs/00_core.ipynb b/nbs/00_core.ipynb index 0a74eb4..0a0ae5a 100644 --- a/nbs/00_core.ipynb +++ b/nbs/00_core.ipynb @@ -1133,14 +1133,15 @@ "metadata": {}, "outputs": [], "source": [ - "def delta_text(msg):\n", + "#| export\n", + "def delta_text(msg, show_tools=True):\n", " \"Extract printable content from streaming delta, return None if nothing to print\"\n", " c = msg.choices[0]\n", " if not c: return c\n", " if not hasattr(c,'delta'): return None #f'{c}'\n", " delta = c.delta\n", " if delta.content: return delta.content\n", - " if delta.tool_calls:\n", + " if delta.tool_calls and show_tools:\n", " res = ''.join(f\"🔧 {tc.function.name}\" for tc in delta.tool_calls if tc.id and tc.function.name)\n", " if res: return f'\\n{res}\\n'\n", " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", @@ -1939,7 +1940,7 @@ { "data": { "text/plain": [ - "'toolu_HAWGYipg63FDcvtOCdKXLo90D'" + "'toolu_3eXcaC9L1Mv7YgZBbqroioCTA'" ] }, "execution_count": null, @@ -1985,7 +1986,7 @@ "text/plain": [ "{'index': 1,\n", " 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'},\n", - " 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", + " 'id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B',\n", " 'type': 'function'}" ] }, @@ -2026,7 +2027,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5, \"b\": 7}', name='simple_add'), id='toolu_PeSBGlKmjj7YNLdu7d20tdsGz', type='function')], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[ChatCompletionMessageToolCall(index=1, function=Function(arguments='{\"a\": 5, \"b\": 7}', name='simple_add'), id='toolu_ZWeL0ejWdUWkat1kV2wS6sE1B', type='function')], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2072,7 +2073,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2107,7 +2108,7 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n" ] } @@ -2154,7 +2155,7 @@ "text/plain": [ "{'index': 1,\n", " 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'},\n", - " 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", + " 'id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B',\n", " 'type': 'function'}" ] }, @@ -2176,7 +2177,7 @@ { "data": { "text/plain": [ - "{'tool_call_id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", + "{'tool_call_id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '12'}" @@ -2219,7 +2220,7 @@ { "data": { "text/plain": [ - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2240,7 +2241,7 @@ { "data": { "text/plain": [ - "[{'tool_call_id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", + "[{'tool_call_id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '12'}]" @@ -2311,9 +2312,9 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content=\"I'll use the simple_add tool to calculate 5 + 7 for you.\", role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n", - "{'tool_call_id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", + "{'tool_call_id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", "\n", "Message(content='\\n\\nThe result of 5 + 7 is **12**.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", "\n" @@ -2351,7 +2352,7 @@ { "data": { "text/plain": [ - "[{'tool_call_id': 'toolu_PeSBGlKmjj7YNLdu7d20tdsGz',\n", + "[{'tool_call_id': 'toolu_ZWeL0ejWdUWkat1kV2wS6sE1B',\n", " 'role': 'tool',\n", " 'name': 'simple_add',\n", " 'content': '13'}]" @@ -2429,7 +2430,7 @@ { "data": { "text/plain": [ - "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_ZvJjHG6icmlhYQHVboNambds5', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_TpcilPbegJbUaYvdod2k8i8hl', 'type': 'function'}], function_call=None, provider_specific_fields=None)" + "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_9r8EEH59O1GxH8WN94gzO3imu', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_iS9iRmZw9oRfEkcXY7vARnI6Y', 'type': 'function'}], function_call=None, provider_specific_fields=None)" ] }, "execution_count": null, @@ -2509,11 +2510,11 @@ "text": [ "{'role': 'user', 'content': 'What is 5 + 7? Use the tool to calculate it.'}\n", "\n", - "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_ZvJjHG6icmlhYQHVboNambds5', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_TpcilPbegJbUaYvdod2k8i8hl', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", + "Message(content='I will calculate these for you!', role='assistant', tool_calls=[{'index': 1, 'function': {'arguments': '{\"a\": 5, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_9r8EEH59O1GxH8WN94gzO3imu', 'type': 'function'}, {'index': 1, 'function': {'arguments': '{\"a\": 6, \"b\": 7}', 'name': 'simple_add'}, 'id': 'toolu_iS9iRmZw9oRfEkcXY7vARnI6Y', 'type': 'function'}], function_call=None, provider_specific_fields=None)\n", "\n", - "{'tool_call_id': 'toolu_ZvJjHG6icmlhYQHVboNambds5', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", + "{'tool_call_id': 'toolu_9r8EEH59O1GxH8WN94gzO3imu', 'role': 'tool', 'name': 'simple_add', 'content': '12'}\n", "\n", - "{'tool_call_id': 'toolu_TpcilPbegJbUaYvdod2k8i8hl', 'role': 'tool', 'name': 'simple_add', 'content': '13'}\n", + "{'tool_call_id': 'toolu_iS9iRmZw9oRfEkcXY7vARnI6Y', 'role': 'tool', 'name': 'simple_add', 'content': '13'}\n", "\n", "Message(content='\\n\\nThe answer is **12**. 5 + 7 = 12.', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None})\n", "\n" @@ -3935,491 +3936,13 @@ "outputs": [], "source": [ "#| export\n", - "def _fmt_thinking(o):\n", - " return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False)\n", - "\n", "def _fmt_tool_call(o):\n", - " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", - " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", - " return f\"\\n
\\n\\n{calls}\\n\"\n", - "\n", - "def _fmt_tool_result(o):\n", - " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", - " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", - "\n", - "async def aformat_stream(rs):\n", - " \"Format the response stream for markdown display.\"\n", - " think,tool_count,tool_idx = False,0,0\n", - " async for o in rs:\n", - " if isinstance(o, ModelResponseStream):\n", - " txt,is_think = _fmt_thinking(o)\n", - " if is_think: think = True\n", - " elif think: txt,think = '\\n\\n',False\n", - " if txt: yield txt\n", - " if c := o.choices[0].delta.content: yield c\n", - " elif isinstance(o, ModelResponse):\n", - " if res := _fmt_tool_call(o):\n", - " tool_count = len(o.choices[0].message.tool_calls)\n", - " tool_idx = 0\n", - " yield res\n", - " elif res := _fmt_tool_result(o):\n", - " yield res\n", - " tool_idx += 1\n", - " if tool_idx >= tool_count: \n", - " yield \"\\n
\\n\\n\"\n", - " tool_count = 0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1e8cd17c", - "metadata": {}, - "outputs": [], - "source": [ - "class StreamFormatter:\n", - " def __init__(self):\n", - " self.was_thinking = False\n", - " self.tool_count = 0\n", - " self.tool_idx = 0\n", - " \n", - " def format_thinking(self, o):\n", - " txt = '🧠' if nested_idx(o, 'choices', 0, 'delta', 'reasoning_content') else ''\n", - " if txt:\n", - " self.was_thinking = True\n", - " return txt\n", - " elif self.was_thinking:\n", - " self.was_thinking = False\n", - " return '\\n\\n'\n", - " return ''\n", - " \n", - " def format_content(self, o): return nested_idx(o, 'choices', 0, 'delta', 'content') or ''\n", - " \n", - " def format_tool(self, o):\n", - " if tc := _fmt_tool_call(o):\n", - " self.tool_count = len(o.choices[0].message.tool_calls)\n", - " self.tool_idx = 0\n", - " return tc\n", - " \n", - " if tr := _fmt_tool_result(o):\n", - " self.tool_idx += 1\n", - " result = tr\n", - " if self.tool_idx >= self.tool_count:\n", - " result += \"\\n\\n\\n\"\n", - " self.tool_count = 0\n", - " return result\n", - " return ''\n", - " \n", - " def format(self, o): return ''.join([self.format_thinking(o), self.format_content(o), self.format_tool(o)])\n", - "\n", - "async def aformat_stream(rs):\n", - " formatter = StreamFormatter()\n", - " async for o in rs: yield formatter.format(o)" - ] - }, - { - "cell_type": "markdown", - "id": "a4e6a5ec", - "metadata": {}, - "source": [ - "I'm starting to like the streamformatter pattern better. Is there a more \"fast.ai\" way of doing this?\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "Yes! The fast.ai approach would be much more functional and concise. Instead of a class, you could use a closure to maintain state:\n", - "\n", - "```python\n", - "def mk_stream_formatter():\n", - " \"Create a stream formatter function with closure state\"\n", - " thinking,tc,ti = False,0,0\n", - " \n", - " def fmt(o):\n", - " nonlocal thinking,tc,ti\n", - " if isinstance(o, ModelResponseStream):\n", - " if nested_idx(o, 'choices', 0, 'delta', 'reasoning_content'): thinking = True; return '🧠'\n", - " elif thinking: thinking = False; return '\\n\\n'\n", - " return nested_idx(o, 'choices', 0, 'delta', 'content') or ''\n", - " elif isinstance(o, ModelResponse):\n", - " if res := _fmt_tool_call(o): tc,ti = len(o.choices[0].message.tool_calls),0; return res\n", - " elif res := _fmt_tool_result(o):\n", - " ti += 1\n", - " return res + (\"\\n\\n\\n\" if ti >= tc else \"\")\n", - " return ''\n", - " return fmt\n", - "```\n", - "\n", - "Then use it like:\n", - "\n", - "```python\n", - "async def aformat_stream(rs):\n", - " fmt = mk_stream_formatter()\n", - " async for o in rs: yield fmt(o)\n", - "```\n", - "\n", - "This is much more in the fast.ai style - functional, concise, uses short variable names (`tc` for tool_count, `ti` for tool_index), combines logic on single lines with `;`, and avoids unnecessary OOP structure." - ] - }, - { - "cell_type": "markdown", - "id": "c816c0a5", - "metadata": {}, - "source": [ - "I love it. Do you think there are any other improvements we could make in the fastai style to make this code more understandable?\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "Yes! A few more fast.ai style improvements:\n", - "\n", - "```python\n", - "def mk_stream_formatter():\n", - " \"Create a stream formatter function with closure state\"\n", - " th,n,i = False,0,0\n", - " \n", - " def fmt(o):\n", - " nonlocal th,n,i\n", - " if isinstance(o, ModelResponseStream):\n", - " if nested_idx(o, 'choices', 0, 'delta', 'reasoning_content'): th=True; return '🧠'\n", - " if th: th=False; return '\\n\\n'\n", - " return nested_idx(o, 'choices', 0, 'delta', 'content') or ''\n", - " if isinstance(o, ModelResponse) and (res := _fmt_tool_call(o)): n,i = len(o.choices[0].message.tool_calls),0; return res\n", - " if (res := _fmt_tool_result(o)): i+=1; return res + (\"\\n\\n\\n\" if i>=n else \"\")\n", - " return ''\n", - " return fmt\n", - "```\n", - "\n", - "Key improvements:\n", - "- Even shorter var names (`th` for thinking, `n` for count, `i` for index)\n", - "- Flattened `elif` to separate `if` statements for cleaner flow\n", - "- Used walrus operator more consistently\n", - "- Combined assignment and return logic on single lines\n", - "- Removed unnecessary `elif` chains\n", - "\n", - "This makes it more readable while staying true to the functional, concise fast.ai approach." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5544fb64", - "metadata": {}, - "outputs": [], - "source": [ - "def _fmt_thinking(o):\n", - " return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False)\n", - "\n", - "def _fmt_tool_call(o):\n", - " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", - " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", - " return f\"\\n
\\n\\n{calls}\\n\"\n", - "\n", - "def _fmt_tool_result(o):\n", - " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", - " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", - "\n", - "def _is_thinking(o): return nested_idx(o, 'choices', 0, 'delta', 'reasoning_content')\n", - "\n", - "def _content(o): return nested_idx(o, 'choices', 0, 'delta', 'content')\n", - "\n", - "def mk_stream_formatter():\n", - " \"Create a stream formatter function with closure state\"\n", - " th,n,i = False,0,0\n", - " \n", - " def fmt(o):\n", - " nonlocal th,n,i\n", - " if isinstance(o, ModelResponseStream):\n", - " if _is_thinking(o): th=True; return '🧠'\n", - " if th: th=False; return '\\n\\n'\n", - " return _content(o) or ''\n", - " if isinstance(o, ModelResponse) and (res := _fmt_tool_call(o)): n,i = len(o.choices[0].message.tool_calls),0; return res\n", - " if (res := _fmt_tool_result(o)): i+=1; return res + (\"\\n
\\n\\n\" if i>=n else \"\")\n", - " return ''\n", - " return fmt\n", - "\n", - "async def aformat_stream(rs):\n", - " fmt = mk_stream_formatter()\n", - " async for o in rs: yield fmt(o)" - ] - }, - { - "cell_type": "markdown", - "id": "026c2f63", - "metadata": {}, - "source": [ - "I started to piece out a few helper functions to make mK_stream_formatter even more understandable. Do you think this is a good way of going about it?\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "Absolutely! This is perfect fast.ai style - breaking complex logic into small, focused functions with clear names. The main formatter flow is now much cleaner and easier to follow.\n", - "\n", - "You could make it even more concise by extracting the tool logic too:\n", - "\n", - "```python\n", - "def _fmt_tool(o):\n", - " if isinstance(o, ModelResponse): return _fmt_tool_call(o)\n", - " return _fmt_tool_result(o)\n", - "\n", - "def mk_stream_formatter():\n", - " \"Create a stream formatter function with closure state\"\n", - " th,n,i = False,0,0\n", - " \n", - " def fmt(o):\n", - " nonlocal th,n,i\n", - " if isinstance(o, ModelResponseStream):\n", - " if _is_thinking(o): th=True; return '🧠'\n", - " if th: th=False; return '\\n\\n'\n", - " return _content(o) or ''\n", - " if (res := _fmt_tool(o)): \n", - " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0\n", - " else: i+=1; res += \"\\n\\n\\n\" if i>=n else \"\"\n", - " return res\n", - " return ''\n", - " return fmt\n", - "```\n", - "\n", - "This approach of composing small, single-purpose functions is exactly what makes code readable and maintainable in the fast.ai style." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "def84abe", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "def _fmt_thinking(o):\n", - " return ('🧠',True) if nested_idx(o.choices[0].delta, 'reasoning_content') else ('', False)\n", - "\n", - "def _fmt_tool_call(o):\n", - " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", - " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", - " return f\"\\n
\\n\\n{calls}\\n\"\n", - "\n", - "def _fmt_tool_result(o):\n", - " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", - " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", - "\n", - "def _is_thinking(o): return nested_idx(o, 'choices', 0, 'delta', 'reasoning_content')\n", - "\n", - "def _content(o): return nested_idx(o, 'choices', 0, 'delta', 'content')\n", - "\n", - "def _fmt_tool(o):\n", - " if isinstance(o, ModelResponse): return _fmt_tool_call(o)\n", - " return _fmt_tool_result(o)\n", - "\n", - "def mk_stream_formatter():\n", - " \"Create a stream formatter function with closure state\"\n", - " th,n,i = False,0,0\n", - " \n", - " def fmt(o):\n", - " nonlocal th,n,i\n", - " if isinstance(o, ModelResponseStream):\n", - " if _is_thinking(o): th=True; return '🧠'\n", - " if th: th=False; return '\\n\\n'\n", - " return _content(o) or ''\n", - " if (res := _fmt_tool(o)): \n", - " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0\n", - " else: i+=1; res += \"\\n
\\n\\n\" if i>=n else \"\"\n", - " return res\n", - " return ''\n", - " return fmt" - ] - }, - { - "cell_type": "markdown", - "id": "cd9b33ab", - "metadata": {}, - "source": [ - "Nice! This is just getting better and better. I'm wondering if we can leverage any other exported methods in this dialog to reduce some of the helpers. Or I dont know what would you recommend next?\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "Looking at your existing code, you already have `delta_text` that handles thinking and content extraction! You could simplify by using it:\n", - "\n", - "```python\n", - "def mk_stream_formatter():\n", - " \"Create a stream formatter function with closure state\"\n", - " n,i = 0,0\n", - " \n", - " def fmt(o):\n", - " nonlocal n,i\n", - " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", - " if (res := _fmt_tool(o)): \n", - " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0\n", - " else: i+=1; res += \"\\n\\n\\n\" if i>=n else \"\"\n", - " return res\n", - " return ''\n", - " return fmt\n", - "```\n", - "\n", - "This eliminates `_is_thinking`, `_content`, and the thinking state variable since `delta_text` already handles the thinking logic with the `🧠` and `\\n\\n` transitions.\n", - "\n", - "You could also consider making the tool counting more functional by extracting it into a helper, but the current approach is quite clean already." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d4c5f358", - "metadata": {}, - "outputs": [], - "source": [ - "def _fmt_tool_call(o):\n", - " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", - " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", - " return f\"\\n
\\n\\n{calls}\\n\"\n", - "\n", - "def _fmt_tool_result(o):\n", - " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", - " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", - "\n", - "def _fmt_tool(o):\n", - " if isinstance(o, ModelResponse): return _fmt_tool_call(o)\n", - " return _fmt_tool_result(o)\n", - "\n", - "def mk_stream_formatter():\n", - " \"Create a stream formatter function with closure state\"\n", - " n,i = 0,0\n", - " \n", - " def fmt(o):\n", - " nonlocal n,i\n", - " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", - " if (res := _fmt_tool(o)): \n", - " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0\n", - " else: i+=1; res += \"\\n
\\n\\n\" if i>=n else \"\"\n", - " return res\n", - " return ''\n", - " return fmt" - ] - }, - { - "cell_type": "markdown", - "id": "d93149e5", - "metadata": {}, - "source": [ - "Wow this is looking really great, what were you recommending about the tool counting logic?\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "You could extract the tool counting logic into a helper to make the main flow cleaner:\n", - "\n", - "```python\n", - "def mk_tool_counter():\n", - " n,i = 0,0\n", - " def count(o, res):\n", - " nonlocal n,i\n", - " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0; return res\n", - " i+=1; return res + (\"\\n\\n\\n\" if i>=n else \"\")\n", - " return count\n", - "\n", - "def mk_stream_formatter():\n", - " \"Create a stream formatter function with closure state\"\n", - " count_tools = mk_tool_counter()\n", - " \n", - " def fmt(o):\n", - " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", - " if (res := _fmt_tool(o)): return count_tools(o, res)\n", - " return ''\n", - " return fmt\n", - "```\n", - "\n", - "This separates concerns - the formatter handles stream routing while the counter manages tool state. The main logic becomes much cleaner and easier to follow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0195d1b4", - "metadata": {}, - "outputs": [], - "source": [ - "def _fmt_tool_call(o):\n", - " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", - " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", - " return f\"\\n
\\n\\n{calls}\\n\"\n", - "\n", - "def _fmt_tool_result(o):\n", - " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", - " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", - "\n", - "def _fmt_tool(o): return _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o)\n", - "\n", - "def mk_tool_counter():\n", - " n,i = 0,0\n", - " def count(o, res):\n", - " nonlocal n,i\n", - " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0; return res\n", - " i+=1; return res + (\"\\n
\\n\\n\" if i>=n else \"\")\n", - " return count\n", - "\n", - "def mk_stream_formatter():\n", - " count_tools = mk_tool_counter()\n", - " \n", - " def fmt(o):\n", - " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", - " if (res := _fmt_tool(o)): return count_tools(o, res)\n", - " return ''\n", - " return fmt\n", - "\n", - "async def aformat_stream(rs):\n", - " fmt = mk_stream_formatter()\n", - " async for o in rs: yield fmt(o)" - ] - }, - { - "cell_type": "markdown", - "id": "23b6c9aa", - "metadata": {}, - "source": [ - "I'm still loving this. Any other logic you can tell can be removed by leveraging other parts of the dialog? Or other general improvements?\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "Looking at your existing patterns, you could inline `_fmt_tool` since it's just a simple dispatch:\n", - "\n", - "```python\n", - "def mk_stream_formatter():\n", - " count_tools = mk_tool_counter()\n", - " \n", - " def fmt(o):\n", - " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", - " if isinstance(o, ModelResponse): res = _fmt_tool_call(o)\n", - " else: res = _fmt_tool_result(o)\n", - " if res: return count_tools(o, res)\n", - " return ''\n", - " return fmt\n", - "```\n", - "\n", - "Or even more concise:\n", - "\n", - "```python\n", - "def mk_stream_formatter():\n", - " count_tools = mk_tool_counter()\n", - " \n", - " def fmt(o):\n", - " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", - " res = _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o)\n", - " return count_tools(o, res) if res else ''\n", - " return fmt\n", - "```\n", - "\n", - "This removes the extra function layer while keeping the logic clear. The code is already quite lean and follows good functional patterns!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ecd63e67", - "metadata": {}, - "outputs": [], - "source": [ - "def _fmt_tool_call(o):\n", - " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return None\n", + " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return ''\n", " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", " return f\"\\n
\\n\\n{calls}\\n\"\n", "\n", "def _fmt_tool_result(o):\n", - " if not isinstance(o, dict) or 'tool_call_id' not in o: return None\n", + " if not isinstance(o, dict) or 'tool_call_id' not in o: return ''\n", " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", "\n", "def mk_tool_counter():\n", @@ -4427,14 +3950,14 @@ " def count(o, res):\n", " nonlocal n,i\n", " if isinstance(o, ModelResponse): n,i = len(o.choices[0].message.tool_calls),0; return res\n", - " i+=1; return res + (\"\\n
\\n\\n\" if i>=n else \"\")\n", + " i+=1; return res + (\"\\n\\n\\n\" if i>=n else \"\") # Only add once we have enough tcs\n", " return count\n", "\n", "def mk_stream_formatter():\n", " count_tools = mk_tool_counter()\n", " \n", " def fmt(o):\n", - " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", + " if isinstance(o, ModelResponseStream): return delta_text(o, show_tools=False)\n", " res = _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o)\n", " return count_tools(o, res) if res else ''\n", " return fmt\n", @@ -4444,158 +3967,6 @@ " async for o in rs: yield fmt(o)" ] }, - { - "cell_type": "markdown", - "id": "ffc73dd4", - "metadata": {}, - "source": [ - "Awesome. Any other recommendations?\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "The code looks excellent! One small optimization - you could ensure the formatting functions always return strings to eliminate the `if res` check:\n", - "\n", - "```python\n", - "def _fmt_tool_call(o):\n", - " if not (c := getattr(o.choices[0].message, 'tool_calls', None)): return ''\n", - " calls = '\\n'.join([f\" `{tc.function.name}({_trunc_str(tc.function.arguments)})`\" for tc in c])\n", - " return f\"\\n
\\n\\n{calls}\\n\"\n", - "\n", - "def _fmt_tool_result(o):\n", - " if not isinstance(o, dict) or 'tool_call_id' not in o: return ''\n", - " return f\" - `{_trunc_str(_clean_str(o.get('content')))}`\\n\"\n", - "\n", - "def mk_stream_formatter():\n", - " count_tools = mk_tool_counter()\n", - " \n", - " def fmt(o):\n", - " if isinstance(o, ModelResponseStream): return delta_text(o) or ''\n", - " res = _fmt_tool_call(o) if isinstance(o, ModelResponse) else _fmt_tool_result(o)\n", - " return count_tools(o, res) if res else res\n", - " return fmt\n", - "```\n", - "\n", - "This makes the control flow even cleaner by guaranteeing string returns everywhere." - ] - }, - { - "cell_type": "markdown", - "id": "3aeb1a46", - "metadata": {}, - "source": [ - "One more thing, Is there anything we should be careful of to improve in delta_text since we are now exporting it and using it.\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "Looking at `delta_text`, a few potential improvements:\n", - "\n", - "```python\n", - "def delta_text(msg):\n", - " \"Extract printable content from streaming delta, return empty string if nothing to print\"\n", - " if not (c := nested_idx(msg, 'choices', 0)): return ''\n", - " if not hasattr(c,'delta'): return ''\n", - " delta = c.delta\n", - " if delta.content: return delta.content\n", - " if delta.tool_calls:\n", - " res = ''.join(f\"🔧 {tc.function.name}\" for tc in delta.tool_calls if tc.id and tc.function.name)\n", - " return f'\\n{res}\\n' if res else ''\n", - " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", - " return ''\n", - "```\n", - "\n", - "Key improvements:\n", - "- Use `nested_idx` for safer access (consistent with your other code)\n", - "- Always return strings instead of `None` for cleaner usage\n", - "- Remove the commented debug code\n", - "- Ensure empty string return for tool calls that don't match criteria\n", - "\n", - "This makes it more robust and consistent with the rest of your codebase." - ] - }, - { - "cell_type": "markdown", - "id": "135a39a0", - "metadata": {}, - "source": [ - "If multiple tools are run at once I dont love how they are printed on separate lines\n", - "\n", - "'\\n🔧 simple_add\\n\\n🔧 multiply\\n\\n
\\n\\n `simple_add({\"a\": 10, \"b\": 5})`\\n `multiply({\"a\": 2, \"b\": 1})`\\n - `15`\\n - `2`\\n\\n
\\n\\n\\n\\nThe results are:\\n- (10 + 5) = **15**\\n- (2 * 1) = **2**'\n", - "\n", - "can we fix that?\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "The issue is in `delta_text` - when multiple tool calls come in, they're being formatted with too much spacing. Let's fix it:\n", - "\n", - "```python\n", - "def delta_text(msg):\n", - " \"Extract printable content from streaming delta, return empty string if nothing to print\"\n", - " if not (c := nested_idx(msg, 'choices', 0)): return ''\n", - " if not hasattr(c,'delta'): return ''\n", - " delta = c.delta\n", - " if delta.content: return delta.content\n", - " if delta.tool_calls:\n", - " res = ' '.join(f\"🔧 {tc.function.name}\" for tc in delta.tool_calls if tc.id and tc.function.name)\n", - " return f'\\n{res}\\n' if res else ''\n", - " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", - " return ''\n", - "```\n", - "\n", - "This changes the `''.join()` to `' '.join()` so multiple tools appear as `🔧 simple_add 🔧 multiply` on one line instead of separate lines." - ] - }, - { - "cell_type": "markdown", - "id": "be3a3e72", - "metadata": {}, - "source": [ - "No you see there is a `\\n` in between the tool calls still. not sure why\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "Ah, I see the issue! Each tool call comes as a separate delta, so each one gets wrapped with `\\n`. Let's remove those:\n", - "\n", - "```python\n", - "def delta_text(msg):\n", - " \"Extract printable content from streaming delta, return empty string if nothing to print\"\n", - " if not (c := nested_idx(msg, 'choices', 0)): return ''\n", - " if not hasattr(c,'delta'): return ''\n", - " delta = c.delta\n", - " if delta.content: return delta.content\n", - " if delta.tool_calls:\n", - " res = ''.join(f\"🔧 {tc.function.name}\" for tc in delta.tool_calls if tc.id and tc.function.name)\n", - " return f'\\n{res}' if res else ''\n", - " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", - " return ''\n", - "```\n", - "\n", - "This removes the trailing `\\n` from tool calls so they appear as `🔧 simple_add🔧 multiply` on one line." - ] - }, - { - "cell_type": "markdown", - "id": "eda91fdf", - "metadata": {}, - "source": [ - "Could we instead have it be 🔧 [simple_add, multiply]\n", - "\n", - "##### 🤖Reply🤖\n", - "\n", - "```python\n", - "def delta_text(msg):\n", - " \"Extract printable content from streaming delta, return empty string if nothing to print\"\n", - " if not (c := nested_idx(msg, 'choices', 0)): return ''\n", - " if not hasattr(c,'delta'): return ''\n", - " delta = c.delta\n", - " if delta.content: return delta.content\n", - " if delta.tool_calls:\n", - " names = [tc.function.name for tc in delta.tool_calls if tc.id and tc.function.name]\n", - " return f'\\n🔧 [{\", \".join(names)}]\\n' if names else ''\n", - " if hasattr(delta,'reasoning_content'): return '🧠' if delta.reasoning_content else '\\n\\n'\n", - " return ''\n", - "```" - ] - }, { "cell_type": "code", "execution_count": null, @@ -4909,9 +4280,23 @@ "#| hide\n", "import nbdev; nbdev.nbdev_export()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca7108fd", + "metadata": {}, + "outputs": [], + "source": [] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "python3", + "language": "python", + "name": "python3" + } + }, "nbformat": 4, "nbformat_minor": 5 }