#!/usr/bin/env python3 """ Debug version of streaming chat that shows API errors in detail. """ import asyncio import sys import os import json from datetime import datetime from typing import Dict, Any sys.path.insert(0, 'src') from claude import ClaudeAgentClient, AgentOptions, ToolResult from claude.streaming import StreamParser import httpx # ANSI color codes class Colors: USER = '\033[1;36m' # Cyan ASSISTANT = '\033[1;32m' # Green TOOL = '\033[1;33m' # Yellow ERROR = '\033[1;31m' # Red STAT = '\033[0;90m' # Gray RESET = '\033[0m' async def test_simple_message(): """Test a simple message first.""" print("=" * 70) print(f"{Colors.ASSISTANT}Testing Simple Message (No Tools){Colors.RESET}") print("=" * 70) options = AgentOptions( model="claude-sonnet-4-5-20250929", stream=True, allowed_tools=[] # No tools for this test ) client = ClaudeAgentClient(options=options) try: print(f"\n{Colors.USER}You:{Colors.RESET} Hello Claude") print(f"\n{Colors.ASSISTANT}Assistant:{Colors.RESET} ", end='', flush=True) async for chunk in client.send_message_stream("Hello Claude, please respond with a short greeting."): if chunk.text_delta: print(chunk.text_delta, end='', flush=True) print() print(f"\n{Colors.ASSISTANT}✅ Simple message works!{Colors.RESET}\n") except httpx.HTTPStatusError as e: print(f"\n\n{Colors.ERROR}❌ HTTP Error: {e.response.status_code}{Colors.RESET}") print(f"{Colors.ERROR}Response: {e.response.text}{Colors.RESET}\n") return False except Exception as e: print(f"\n\n{Colors.ERROR}❌ Error: {e}{Colors.RESET}\n") import traceback traceback.print_exc() return False finally: await client.close() return True async def test_with_tools(): """Test with tools enabled.""" print("=" * 70) print(f"{Colors.ASSISTANT}Testing With Tools Enabled{Colors.RESET}") print("=" * 70) options = AgentOptions( model="claude-sonnet-4-5-20250929", stream=True, allowed_tools=["Bash"] # Only Bash for this test ) client = ClaudeAgentClient(options=options) # Show what we're sending print(f"\n{Colors.STAT}Request configuration:{Colors.RESET}") print(f" Model: {options.model}") print(f" Stream: {options.stream}") print(f" Tools: {options.allowed_tools}") filtered_tools = client._filter_tools() print(f" Filtered tools: {[t['name'] for t in filtered_tools]}") system_prompt = client._build_system_prompt() print(f" System prompt blocks: {len(system_prompt)}") if not system_prompt: print(f" {Colors.ERROR}⚠️ WARNING: System prompt is empty!{Colors.RESET}") try: print(f"\n{Colors.USER}You:{Colors.RESET} Check my Python version") print(f"\n{Colors.ASSISTANT}Assistant:{Colors.RESET} ", end='', flush=True) parser = StreamParser() async for chunk in client.send_message_stream("Check my Python version"): parser.add_chunk(chunk) if chunk.text_delta: print(chunk.text_delta, end='', flush=True) print() # Check for tool uses complete_message = parser.to_dict() content_blocks = complete_message.get("content", []) tool_uses = [block for block in content_blocks if block.get("type") == "tool_use"] if tool_uses: print(f"\n{Colors.TOOL}Claude wants to use {len(tool_uses)} tool(s){Colors.RESET}") for tool_use in tool_uses: print(f"{Colors.TOOL}[Tool: {tool_use.get('name')}]{Colors.RESET}") print(f"{Colors.STAT} Input: {json.dumps(tool_use.get('input', {}), indent=2)}{Colors.RESET}") else: print(f"\n{Colors.STAT}No tools used in response{Colors.RESET}") print(f"\n{Colors.ASSISTANT}✅ Request with tools succeeded!{Colors.RESET}\n") except httpx.HTTPStatusError as e: print(f"\n\n{Colors.ERROR}❌ HTTP Error: {e.response.status_code}{Colors.RESET}") print(f"{Colors.ERROR}Response body:{Colors.RESET}") try: error_json = e.response.json() print(json.dumps(error_json, indent=2)) except: print(e.response.text) # Show what we tried to send print(f"\n{Colors.ERROR}Request that failed:{Colors.RESET}") print(f" Messages: {len(client.messages)}") print(f" System: {len(system_prompt)} blocks") print(f" Tools: {len(filtered_tools)}") return False except Exception as e: print(f"\n\n{Colors.ERROR}❌ Error: {e}{Colors.RESET}\n") import traceback traceback.print_exc() return False finally: await client.close() return True async def main(): """Run debug tests.""" # Check for API key if not os.getenv('ANTHROPIC_API_KEY'): print(f"{Colors.ERROR}❌ Error: ANTHROPIC_API_KEY environment variable not set{Colors.RESET}") print("\nSet your API key:") print(" export ANTHROPIC_API_KEY='sk-ant-...'") return 1 print(f"\n{Colors.ASSISTANT}Claude API Debug Tool{Colors.RESET}\n") # Test 1: Simple message without tools success1 = await test_simple_message() if not success1: print(f"{Colors.ERROR}Simple message failed - stopping here{Colors.RESET}") return 1 # Test 2: With tools enabled success2 = await test_with_tools() if success2: print(f"\n{Colors.ASSISTANT}✅ All tests passed!{Colors.RESET}\n") return 0 else: print(f"\n{Colors.ERROR}❌ Tool test failed{Colors.RESET}\n") return 1 if __name__ == "__main__": try: exit_code = asyncio.run(main()) sys.exit(exit_code) except KeyboardInterrupt: print(f"\n\n{Colors.ASSISTANT}⚠️ Interrupted by user{Colors.RESET}") sys.exit(130) except Exception as e: print(f"\n{Colors.ERROR}❌ Fatal error: {e}{Colors.RESET}") import traceback traceback.print_exc() sys.exit(1)