Skip to content

Commit 7a24087

Browse files
dicksontsairushilpatel0
authored andcommitted
Working examples
Signed-off-by: Rushil Patel <[email protected]>
1 parent 5f90eb8 commit 7a24087

File tree

4 files changed

+485
-195
lines changed

4 files changed

+485
-195
lines changed

examples/streaming_mode.py

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Comprehensive examples of using ClaudeSDKClient for streaming mode.
4+
5+
This file demonstrates various patterns for building applications with
6+
the ClaudeSDKClient streaming interface.
7+
"""
8+
9+
import asyncio
10+
import contextlib
11+
12+
from claude_code_sdk import (
13+
AssistantMessage,
14+
ClaudeCodeOptions,
15+
ClaudeSDKClient,
16+
ResultMessage,
17+
TextBlock,
18+
)
19+
20+
21+
async def example_basic_streaming():
22+
"""Basic streaming with context manager."""
23+
print("=== Basic Streaming Example ===")
24+
25+
async with ClaudeSDKClient() as client:
26+
# Send a message
27+
await client.send_message("What is 2+2?")
28+
29+
# Receive complete response using the helper method
30+
messages, result = await client.receive_response()
31+
32+
# Extract text from assistant's response
33+
for msg in messages:
34+
if isinstance(msg, AssistantMessage):
35+
for block in msg.content:
36+
if isinstance(block, TextBlock):
37+
print(f"Claude: {block.text}")
38+
39+
# Print cost if available
40+
if result and result.total_cost_usd:
41+
print(f"Cost: ${result.total_cost_usd:.4f}")
42+
43+
print("Session ended\n")
44+
45+
46+
async def example_multi_turn_conversation():
47+
"""Multi-turn conversation using receive_response helper."""
48+
print("=== Multi-Turn Conversation Example ===")
49+
50+
async with ClaudeSDKClient() as client:
51+
# First turn
52+
print("User: What's the capital of France?")
53+
await client.send_message("What's the capital of France?")
54+
55+
messages, _ = await client.receive_response()
56+
57+
# Extract and print response
58+
for msg in messages:
59+
if isinstance(msg, AssistantMessage):
60+
for block in msg.content:
61+
if isinstance(block, TextBlock):
62+
print(f"Claude: {block.text}")
63+
64+
# Second turn - follow-up
65+
print("\nUser: What's the population of that city?")
66+
await client.send_message("What's the population of that city?")
67+
68+
messages, _ = await client.receive_response()
69+
70+
for msg in messages:
71+
if isinstance(msg, AssistantMessage):
72+
for block in msg.content:
73+
if isinstance(block, TextBlock):
74+
print(f"Claude: {block.text}")
75+
76+
for msg in messages:
77+
if isinstance(msg, AssistantMessage):
78+
for block in msg.content:
79+
if isinstance(block, TextBlock):
80+
print(f"Claude: {block.text}")
81+
82+
print("\nConversation ended\n")
83+
84+
85+
async def example_concurrent_responses():
86+
"""Handle responses while sending new messages."""
87+
print("=== Concurrent Send/Receive Example ===")
88+
89+
async with ClaudeSDKClient() as client:
90+
# Background task to continuously receive messages
91+
async def receive_messages():
92+
async for message in client.receive_messages():
93+
if isinstance(message, AssistantMessage):
94+
for block in message.content:
95+
if isinstance(block, TextBlock):
96+
print(f"Claude: {block.text}")
97+
98+
# Start receiving in background
99+
receive_task = asyncio.create_task(receive_messages())
100+
101+
# Send multiple messages with delays
102+
questions = [
103+
"What is 2 + 2?",
104+
"What is the square root of 144?",
105+
"What is 15% of 80?",
106+
]
107+
108+
for question in questions:
109+
print(f"\nUser: {question}")
110+
await client.send_message(question)
111+
await asyncio.sleep(3) # Wait between messages
112+
113+
# Give time for final responses
114+
await asyncio.sleep(2)
115+
116+
# Clean up
117+
receive_task.cancel()
118+
with contextlib.suppress(asyncio.CancelledError):
119+
await receive_task
120+
121+
print("\nSession ended\n")
122+
123+
124+
async def example_with_interrupt():
125+
"""Demonstrate interrupt capability."""
126+
print("=== Interrupt Example ===")
127+
print("IMPORTANT: Interrupts require active message consumption.")
128+
129+
async with ClaudeSDKClient() as client:
130+
# Start a long-running task
131+
print("\nUser: Count from 1 to 100 slowly")
132+
await client.send_message(
133+
"Count from 1 to 100 slowly, with a brief pause between each number"
134+
)
135+
136+
# Create a background task to consume messages
137+
messages_received = []
138+
interrupt_sent = False
139+
140+
async def consume_messages():
141+
"""Consume messages in the background to enable interrupt processing."""
142+
async for message in client.receive_messages():
143+
messages_received.append(message)
144+
if isinstance(message, AssistantMessage):
145+
for block in message.content:
146+
if isinstance(block, TextBlock):
147+
# Print first few numbers
148+
print(f"Claude: {block.text[:50]}...")
149+
150+
# Stop when we get a result after interrupt
151+
if isinstance(message, ResultMessage) and interrupt_sent:
152+
break
153+
154+
# Start consuming messages in the background
155+
consume_task = asyncio.create_task(consume_messages())
156+
157+
# Wait 2 seconds then send interrupt
158+
await asyncio.sleep(2)
159+
print("\n[After 2 seconds, sending interrupt...]")
160+
interrupt_sent = True
161+
await client.interrupt()
162+
163+
# Wait for the consume task to finish processing the interrupt
164+
await consume_task
165+
166+
# Send new instruction after interrupt
167+
print("\nUser: Never mind, just tell me a quick joke")
168+
await client.send_message("Never mind, just tell me a quick joke")
169+
170+
# Get the joke
171+
messages, result = await client.receive_response()
172+
173+
for msg in messages:
174+
if isinstance(msg, AssistantMessage):
175+
for block in msg.content:
176+
if isinstance(block, TextBlock):
177+
print(f"Claude: {block.text}")
178+
179+
print("\nSession ended\n")
180+
181+
182+
async def example_manual_message_handling():
183+
"""Manually handle message stream for custom logic."""
184+
print("=== Manual Message Handling Example ===")
185+
186+
async with ClaudeSDKClient() as client:
187+
await client.send_message(
188+
"List 5 programming languages and their main use cases"
189+
)
190+
191+
# Manually process messages with custom logic
192+
languages_found = []
193+
194+
async for message in client.receive_messages():
195+
if isinstance(message, AssistantMessage):
196+
for block in message.content:
197+
if isinstance(block, TextBlock):
198+
text = block.text
199+
# Custom logic: extract language names
200+
for lang in [
201+
"Python",
202+
"JavaScript",
203+
"Java",
204+
"C++",
205+
"Go",
206+
"Rust",
207+
"Ruby",
208+
]:
209+
if lang in text and lang not in languages_found:
210+
languages_found.append(lang)
211+
print(f"Found language: {lang}")
212+
213+
elif isinstance(message, ResultMessage):
214+
print(f"\nTotal languages mentioned: {len(languages_found)}")
215+
break
216+
217+
print("\nSession ended\n")
218+
219+
220+
async def example_with_options():
221+
"""Use ClaudeCodeOptions to configure the client."""
222+
print("=== Custom Options Example ===")
223+
224+
# Configure options
225+
options = ClaudeCodeOptions(
226+
allowed_tools=["Read", "Write"], # Allow file operations
227+
max_thinking_tokens=10000,
228+
system_prompt="You are a helpful coding assistant.",
229+
)
230+
231+
async with ClaudeSDKClient(options=options) as client:
232+
await client.send_message(
233+
"Create a simple hello.txt file with a greeting message"
234+
)
235+
236+
messages, result = await client.receive_response()
237+
238+
tool_uses = []
239+
for msg in messages:
240+
if isinstance(msg, AssistantMessage):
241+
for block in msg.content:
242+
if isinstance(block, TextBlock):
243+
print(f"Claude: {block.text}")
244+
elif hasattr(block, "name"): # ToolUseBlock
245+
tool_uses.append(getattr(block, "name", ""))
246+
247+
if tool_uses:
248+
print(f"\nTools used: {', '.join(tool_uses)}")
249+
250+
print("\nSession ended\n")
251+
252+
253+
async def example_error_handling():
254+
"""Demonstrate proper error handling."""
255+
print("=== Error Handling Example ===")
256+
257+
client = ClaudeSDKClient()
258+
259+
try:
260+
# Connect with custom stream
261+
async def message_stream():
262+
yield {
263+
"type": "user",
264+
"message": {"role": "user", "content": "Hello"},
265+
"parent_tool_use_id": None,
266+
"session_id": "error-demo",
267+
}
268+
269+
await client.connect(message_stream())
270+
271+
# Create a background task to consume messages (required for interrupt to work)
272+
consume_task = None
273+
274+
async def consume_messages():
275+
"""Background message consumer."""
276+
async for msg in client.receive_messages():
277+
if isinstance(msg, AssistantMessage):
278+
print("Received response from Claude")
279+
280+
# Receive messages with timeout
281+
try:
282+
# Start consuming messages in background
283+
consume_task = asyncio.create_task(consume_messages())
284+
285+
# Wait for response with timeout
286+
await asyncio.wait_for(consume_task, timeout=30.0)
287+
288+
except asyncio.TimeoutError:
289+
print("Response timeout - sending interrupt")
290+
# Note: interrupt requires active message consumption
291+
# Since we're already consuming in the background task, interrupt will work
292+
await client.interrupt()
293+
294+
# Cancel the consume task
295+
if consume_task:
296+
consume_task.cancel()
297+
with contextlib.suppress(asyncio.CancelledError):
298+
await consume_task
299+
300+
except Exception as e:
301+
print(f"Error: {e}")
302+
303+
finally:
304+
# Always disconnect
305+
await client.disconnect()
306+
307+
print("\nSession ended\n")
308+
309+
310+
async def main():
311+
"""Run all examples."""
312+
examples = [
313+
example_basic_streaming,
314+
example_multi_turn_conversation,
315+
example_concurrent_responses,
316+
example_with_interrupt,
317+
example_manual_message_handling,
318+
example_with_options,
319+
example_error_handling,
320+
]
321+
322+
for example in examples:
323+
await example()
324+
print("-" * 50 + "\n")
325+
326+
327+
if __name__ == "__main__":
328+
asyncio.run(main())

0 commit comments

Comments
 (0)