44
55This file demonstrates various patterns for building applications with
66the ClaudeSDKClient streaming interface.
7+
8+ The queries are intentionally simplistic. In reality, a query can be a more
9+ complex task that Claude SDK uses its agentic capabilities and tools (e.g. run
10+ bash commands, edit files, search the web, fetch web content) to accomplish.
11+
12+ Usage:
13+ ./examples/streaming_mode.py - List the examples
14+ ./examples/streaming_mode.py all - Run all examples
15+ ./examples/streaming_mode.py basic_streaming - Run a specific example
716"""
817
918import asyncio
1019import contextlib
20+ import sys
1121
1222from claude_code_sdk import (
1323 AssistantMessage ,
1424 ClaudeCodeOptions ,
1525 ClaudeSDKClient ,
1626 CLIConnectionError ,
1727 ResultMessage ,
28+ SystemMessage ,
1829 TextBlock ,
30+ UserMessage ,
1931)
2032
2133
34+ def display_message (msg ):
35+ """Standardized message display function.
36+
37+ - UserMessage: "User: <content>"
38+ - AssistantMessage: "Claude: <content>"
39+ - SystemMessage: ignored
40+ - ResultMessage: "Result ended" + cost if available
41+ """
42+ if isinstance (msg , UserMessage ):
43+ for block in msg .content :
44+ if isinstance (block , TextBlock ):
45+ print (f"User: { block .text } " )
46+ elif isinstance (msg , AssistantMessage ):
47+ for block in msg .content :
48+ if isinstance (block , TextBlock ):
49+ print (f"Claude: { block .text } " )
50+ elif isinstance (msg , SystemMessage ):
51+ # Ignore system messages
52+ pass
53+ elif isinstance (msg , ResultMessage ):
54+ print ("Result ended" )
55+
56+
2257async def example_basic_streaming ():
2358 """Basic streaming with context manager."""
2459 print ("=== Basic Streaming Example ===" )
2560
2661 async with ClaudeSDKClient () as client :
27- # Send a message
28- await client .send_message ("What is 2+2?" )
62+ print ( "User: What is 2+2?" )
63+ await client .query ("What is 2+2?" )
2964
3065 # Receive complete response using the helper method
3166 async for msg in client .receive_response ():
32- if isinstance (msg , AssistantMessage ):
33- for block in msg .content :
34- if isinstance (block , TextBlock ):
35- print (f"Claude: { block .text } " )
36- elif isinstance (msg , ResultMessage ) and msg .total_cost_usd :
37- print (f"Cost: ${ msg .total_cost_usd :.4f} " )
67+ display_message (msg )
3868
39- print ("Session ended \n " )
69+ print ("\n " )
4070
4171
4272async def example_multi_turn_conversation ():
@@ -46,26 +76,20 @@ async def example_multi_turn_conversation():
4676 async with ClaudeSDKClient () as client :
4777 # First turn
4878 print ("User: What's the capital of France?" )
49- await client .send_message ("What's the capital of France?" )
79+ await client .query ("What's the capital of France?" )
5080
5181 # Extract and print response
5282 async for msg in client .receive_response ():
53- content_blocks = getattr (msg , 'content' , [])
54- for block in content_blocks :
55- if isinstance (block , TextBlock ):
56- print (f"{ block .text } " )
83+ display_message (msg )
5784
5885 # Second turn - follow-up
5986 print ("\n User: What's the population of that city?" )
60- await client .send_message ("What's the population of that city?" )
87+ await client .query ("What's the population of that city?" )
6188
6289 async for msg in client .receive_response ():
63- content_blocks = getattr (msg , 'content' , [])
64- for block in content_blocks :
65- if isinstance (block , TextBlock ):
66- print (f"{ block .text } " )
90+ display_message (msg )
6791
68- print ("\n Conversation ended \ n " )
92+ print ("\n " )
6993
7094
7195async def example_concurrent_responses ():
@@ -76,10 +100,7 @@ async def example_concurrent_responses():
76100 # Background task to continuously receive messages
77101 async def receive_messages ():
78102 async for message in client .receive_messages ():
79- if isinstance (message , AssistantMessage ):
80- for block in message .content :
81- if isinstance (block , TextBlock ):
82- print (f"Claude: { block .text } " )
103+ display_message (message )
83104
84105 # Start receiving in background
85106 receive_task = asyncio .create_task (receive_messages ())
@@ -93,7 +114,7 @@ async def receive_messages():
93114
94115 for question in questions :
95116 print (f"\n User: { question } " )
96- await client .send_message (question )
117+ await client .query (question )
97118 await asyncio .sleep (3 ) # Wait between messages
98119
99120 # Give time for final responses
@@ -104,7 +125,7 @@ async def receive_messages():
104125 with contextlib .suppress (asyncio .CancelledError ):
105126 await receive_task
106127
107- print ("\n Session ended \ n " )
128+ print ("\n " )
108129
109130
110131async def example_with_interrupt ():
@@ -115,7 +136,7 @@ async def example_with_interrupt():
115136 async with ClaudeSDKClient () as client :
116137 # Start a long-running task
117138 print ("\n User: Count from 1 to 100 slowly" )
118- await client .send_message (
139+ await client .query (
119140 "Count from 1 to 100 slowly, with a brief pause between each number"
120141 )
121142
@@ -132,10 +153,10 @@ async def consume_messages():
132153 if isinstance (block , TextBlock ):
133154 # Print first few numbers
134155 print (f"Claude: { block .text [:50 ]} ..." )
135-
136- # Stop when we get a result after interrupt
137- if isinstance ( message , ResultMessage ) and interrupt_sent :
138- break
156+ elif isinstance ( message , ResultMessage ):
157+ display_message ( message )
158+ if interrupt_sent :
159+ break
139160
140161 # Start consuming messages in the background
141162 consume_task = asyncio .create_task (consume_messages ())
@@ -151,24 +172,21 @@ async def consume_messages():
151172
152173 # Send new instruction after interrupt
153174 print ("\n User: Never mind, just tell me a quick joke" )
154- await client .send_message ("Never mind, just tell me a quick joke" )
175+ await client .query ("Never mind, just tell me a quick joke" )
155176
156177 # Get the joke
157178 async for msg in client .receive_response ():
158- if isinstance (msg , AssistantMessage ):
159- for block in msg .content :
160- if isinstance (block , TextBlock ):
161- print (f"Claude: { block .text } " )
179+ display_message (msg )
162180
163- print ("\n Session ended \ n " )
181+ print ("\n " )
164182
165183
166184async def example_manual_message_handling ():
167185 """Manually handle message stream for custom logic."""
168186 print ("=== Manual Message Handling Example ===" )
169187
170188 async with ClaudeSDKClient () as client :
171- await client .send_message (
189+ await client .query (
172190 "List 5 programming languages and their main use cases"
173191 )
174192
@@ -180,6 +198,7 @@ async def example_manual_message_handling():
180198 for block in message .content :
181199 if isinstance (block , TextBlock ):
182200 text = block .text
201+ print (f"Claude: { text } " )
183202 # Custom logic: extract language names
184203 for lang in [
185204 "Python" ,
@@ -193,12 +212,12 @@ async def example_manual_message_handling():
193212 if lang in text and lang not in languages_found :
194213 languages_found .append (lang )
195214 print (f"Found language: { lang } " )
196-
197215 elif isinstance (message , ResultMessage ):
198- print (f"\n Total languages mentioned: { len (languages_found )} " )
216+ display_message (message )
217+ print (f"Total languages mentioned: { len (languages_found )} " )
199218 break
200219
201- print ("\n Session ended \ n " )
220+ print ("\n " )
202221
203222
204223async def example_with_options ():
@@ -213,23 +232,75 @@ async def example_with_options():
213232 )
214233
215234 async with ClaudeSDKClient (options = options ) as client :
216- await client .send_message (
235+ print ("User: Create a simple hello.txt file with a greeting message" )
236+ await client .query (
217237 "Create a simple hello.txt file with a greeting message"
218238 )
219239
220240 tool_uses = []
221241 async for msg in client .receive_response ():
222242 if isinstance (msg , AssistantMessage ):
243+ display_message (msg )
223244 for block in msg .content :
224- if isinstance (block , TextBlock ):
225- print ( f"Claude: { block . text } " )
226- elif hasattr ( block , "name" ): # ToolUseBlock
245+ if hasattr (block , "name" ) and not isinstance (
246+ block , TextBlock
247+ ): # ToolUseBlock
227248 tool_uses .append (getattr (block , "name" , "" ))
249+ else :
250+ display_message (msg )
228251
229252 if tool_uses :
230- print (f"\n Tools used: { ', ' .join (tool_uses )} " )
253+ print (f"Tools used: { ', ' .join (tool_uses )} " )
254+
255+ print ("\n " )
256+
257+
258+ async def example_async_iterable_prompt ():
259+ """Demonstrate send_message with async iterable."""
260+ print ("=== Async Iterable Prompt Example ===" )
261+
262+ async def create_message_stream ():
263+ """Generate a stream of messages."""
264+ print ("User: Hello! I have multiple questions." )
265+ yield {
266+ "type" : "user" ,
267+ "message" : {"role" : "user" , "content" : "Hello! I have multiple questions." },
268+ "parent_tool_use_id" : None ,
269+ "session_id" : "qa-session" ,
270+ }
271+
272+ print ("User: First, what's the capital of Japan?" )
273+ yield {
274+ "type" : "user" ,
275+ "message" : {
276+ "role" : "user" ,
277+ "content" : "First, what's the capital of Japan?" ,
278+ },
279+ "parent_tool_use_id" : None ,
280+ "session_id" : "qa-session" ,
281+ }
282+
283+ print ("User: Second, what's 15% of 200?" )
284+ yield {
285+ "type" : "user" ,
286+ "message" : {"role" : "user" , "content" : "Second, what's 15% of 200?" },
287+ "parent_tool_use_id" : None ,
288+ "session_id" : "qa-session" ,
289+ }
290+
291+ async with ClaudeSDKClient () as client :
292+ # Send async iterable of messages
293+ await client .query (create_message_stream ())
294+
295+ # Receive the three responses
296+ async for msg in client .receive_response ():
297+ display_message (msg )
298+ async for msg in client .receive_response ():
299+ display_message (msg )
300+ async for msg in client .receive_response ():
301+ display_message (msg )
231302
232- print ("\n Session ended \ n " )
303+ print ("\n " )
233304
234305
235306async def example_error_handling ():
@@ -242,7 +313,8 @@ async def example_error_handling():
242313 await client .connect ()
243314
244315 # Send a message that will take time to process
245- await client .send_message ("Run a bash sleep command for 60 seconds" )
316+ print ("User: Run a bash sleep command for 60 seconds" )
317+ await client .query ("Run a bash sleep command for 60 seconds" )
246318
247319 # Try to receive response with a short timeout
248320 try :
@@ -255,11 +327,13 @@ async def example_error_handling():
255327 if isinstance (block , TextBlock ):
256328 print (f"Claude: { block .text [:50 ]} ..." )
257329 elif isinstance (msg , ResultMessage ):
258- print ( "Received complete response" )
330+ display_message ( msg )
259331 break
260332
261333 except asyncio .TimeoutError :
262- print ("\n Response timeout after 10 seconds - demonstrating graceful handling" )
334+ print (
335+ "\n Response timeout after 10 seconds - demonstrating graceful handling"
336+ )
263337 print (f"Received { len (messages )} messages before timeout" )
264338
265339 except CLIConnectionError as e :
@@ -272,24 +346,48 @@ async def example_error_handling():
272346 # Always disconnect
273347 await client .disconnect ()
274348
275- print ("\n Session ended \ n " )
349+ print ("\n " )
276350
277351
278352async def main ():
279- """Run all examples."""
280- examples = [
281- example_basic_streaming ,
282- example_multi_turn_conversation ,
283- example_concurrent_responses ,
284- example_with_interrupt ,
285- example_manual_message_handling ,
286- example_with_options ,
287- example_error_handling ,
288- ]
289-
290- for example in examples :
291- await example ()
292- print ("-" * 50 + "\n " )
353+ """Run all examples or a specific example based on command line argument."""
354+ examples = {
355+ "basic_streaming" : example_basic_streaming ,
356+ "multi_turn_conversation" : example_multi_turn_conversation ,
357+ "concurrent_responses" : example_concurrent_responses ,
358+ "with_interrupt" : example_with_interrupt ,
359+ "manual_message_handling" : example_manual_message_handling ,
360+ "with_options" : example_with_options ,
361+ "async_iterable_prompt" : example_async_iterable_prompt ,
362+ "error_handling" : example_error_handling ,
363+ }
364+
365+ if len (sys .argv ) < 2 :
366+ # List available examples
367+ print ("Usage: python streaming_mode.py <example_name>" )
368+ print ("\n Available examples:" )
369+ print (" all - Run all examples" )
370+ for name in examples :
371+ print (f" { name } " )
372+ sys .exit (0 )
373+
374+ example_name = sys .argv [1 ]
375+
376+ if example_name == "all" :
377+ # Run all examples
378+ for example in examples .values ():
379+ await example ()
380+ print ("-" * 50 + "\n " )
381+ elif example_name in examples :
382+ # Run specific example
383+ await examples [example_name ]()
384+ else :
385+ print (f"Error: Unknown example '{ example_name } '" )
386+ print ("\n Available examples:" )
387+ print (" all - Run all examples" )
388+ for name in examples :
389+ print (f" { name } " )
390+ sys .exit (1 )
293391
294392
295393if __name__ == "__main__" :
0 commit comments