|
611 | 611 | "metadata": {}, |
612 | 612 | "outputs": [], |
613 | 613 | "source": [ |
614 | | - "import json\n", |
615 | | - "from typing import Any, Dict, Optional\n", |
616 | | - "\n", |
617 | | - "import requests\n", |
| 614 | + "from typing import Optional\n", |
618 | 615 | "from typing_extensions import Annotated\n", |
619 | 616 | "\n", |
620 | | - "\n", |
621 | | - "async def robust_web_search(query: Annotated[str, \"Search query\"]) -> str:\n", |
622 | | - " \"\"\"A robust web search tool that handles various error conditions.\"\"\"\n", |
623 | | - "\n", |
| 617 | + "async def weather_forecast(city: Annotated[str, \"City name\"]) -> str:\n", |
| 618 | + " \"\"\"Get weather forecast for a city with error handling.\"\"\"\n", |
| 619 | + " \n", |
624 | 620 | " # Input validation\n", |
625 | | - " if not query or not query.strip():\n", |
626 | | - " raise ValueError(\"Search query cannot be empty\")\n", |
627 | | - "\n", |
628 | | - " if len(query) > 500:\n", |
629 | | - " raise ValueError(\"Search query is too long (max 500 characters)\")\n", |
630 | | - "\n", |
631 | | - " try:\n", |
632 | | - " # Simulate API call (replace with actual search API)\n", |
633 | | - " # This is where you would make the actual HTTP request\n", |
634 | | - "\n", |
635 | | - " # Simulate different types of failures\n", |
636 | | - " if \"timeout\" in query.lower():\n", |
637 | | - " raise requests.exceptions.Timeout(\"Search request timed out\")\n", |
638 | | - "\n", |
639 | | - " if \"unauthorized\" in query.lower():\n", |
640 | | - " raise requests.exceptions.HTTPError(\"401 Unauthorized: Invalid API key\")\n", |
641 | | - "\n", |
642 | | - " if \"rate_limit\" in query.lower():\n", |
643 | | - " raise requests.exceptions.HTTPError(\"429 Too Many Requests: Rate limit exceeded\")\n", |
644 | | - "\n", |
645 | | - " # Simulate successful response\n", |
646 | | - " return f\"Search results for '{query}': Found relevant information about the topic.\"\n", |
647 | | - "\n", |
648 | | - " except requests.exceptions.Timeout as e:\n", |
649 | | - " # Handle timeout specifically\n", |
650 | | - " raise TimeoutError(f\"Search request for '{query}' timed out. Please try again.\") from e\n", |
651 | | - "\n", |
652 | | - " except requests.exceptions.HTTPError as e:\n", |
653 | | - " # Handle HTTP errors with specific messages\n", |
654 | | - " if \"401\" in str(e):\n", |
655 | | - " raise PermissionError(\"Search API authentication failed. Please check your API key.\") from e\n", |
656 | | - " elif \"429\" in str(e):\n", |
657 | | - " raise RuntimeError(\"Search API rate limit exceeded. Please wait before trying again.\") from e\n", |
658 | | - " else:\n", |
659 | | - " raise ConnectionError(f\"Search API error: {e}\") from e\n", |
660 | | - "\n", |
661 | | - " except requests.exceptions.RequestException as e:\n", |
662 | | - " # Handle other network-related errors\n", |
663 | | - " raise ConnectionError(f\"Network error during search: {e}\") from e\n", |
664 | | - "\n", |
665 | | - " except Exception as e:\n", |
666 | | - " # Handle any other unexpected errors\n", |
667 | | - " raise RuntimeError(f\"Unexpected error during search: {e}\") from e\n", |
668 | | - "\n", |
669 | | - "\n", |
670 | | - "async def file_processor(file_path: Annotated[str, \"Path to file to process\"]) -> str:\n", |
671 | | - " \"\"\"A file processing tool with comprehensive error handling.\"\"\"\n", |
672 | | - "\n", |
| 621 | + " if not city or not city.strip():\n", |
| 622 | + " raise ValueError(\"City name cannot be empty\")\n", |
| 623 | + " \n", |
673 | 624 | " try:\n", |
674 | | - " # Simulate file operations\n", |
675 | | - " if not file_path:\n", |
676 | | - " raise ValueError(\"File path cannot be empty\")\n", |
677 | | - "\n", |
678 | | - " if file_path == \"nonexistent.txt\":\n", |
679 | | - " raise FileNotFoundError(f\"File not found: {file_path}\")\n", |
680 | | - "\n", |
681 | | - " if file_path == \"permission_denied.txt\":\n", |
682 | | - " raise PermissionError(f\"Permission denied accessing file: {file_path}\")\n", |
683 | | - "\n", |
684 | | - " if file_path == \"corrupted.txt\":\n", |
685 | | - " raise ValueError(f\"File appears to be corrupted: {file_path}\")\n", |
686 | | - "\n", |
687 | | - " # Simulate successful processing\n", |
688 | | - " return f\"Successfully processed file: {file_path}\"\n", |
689 | | - "\n", |
690 | | - " except FileNotFoundError as e:\n", |
691 | | - " # Re-raise with more context\n", |
692 | | - " raise FileNotFoundError(f\"The file '{file_path}' does not exist. Please check the path and try again.\") from e\n", |
693 | | - "\n", |
694 | | - " except PermissionError as e:\n", |
695 | | - " # Re-raise with helpful message\n", |
696 | | - " raise PermissionError(f\"Access denied to file '{file_path}'. Please check file permissions.\") from e\n", |
697 | | - "\n", |
| 625 | + " # Simulate API behavior for demonstration\n", |
| 626 | + " if city.lower() == \"error\":\n", |
| 627 | + " raise ConnectionError(\"Failed to connect to weather service\")\n", |
| 628 | + " \n", |
| 629 | + " if city.lower() == \"unknown\":\n", |
| 630 | + " raise ValueError(f\"City not found: {city}\")\n", |
| 631 | + " \n", |
| 632 | + " # Successful case\n", |
| 633 | + " return f\"The weather in {city} is sunny with a high of 75°F\"\n", |
| 634 | + " \n", |
| 635 | + " except ConnectionError as e:\n", |
| 636 | + " # Network-related errors\n", |
| 637 | + " raise ConnectionError(f\"Network error: {e}. Please try again later.\")\n", |
| 638 | + " \n", |
698 | 639 | " except ValueError as e:\n", |
699 | | - " # Handle validation errors\n", |
700 | | - " raise ValueError(f\"Invalid file or content: {e}\") from e\n", |
701 | | - "\n", |
| 640 | + " # Data-related errors\n", |
| 641 | + " raise ValueError(f\"Invalid city: {e}\")\n", |
| 642 | + " \n", |
702 | 643 | " except Exception as e:\n", |
703 | | - " # Catch-all for unexpected errors\n", |
704 | | - " raise RuntimeError(f\"Unexpected error processing file '{file_path}': {e}\") from e\n", |
705 | | - "\n", |
706 | | - "\n", |
707 | | - "# Create tools\n", |
708 | | - "search_tool = FunctionTool(robust_web_search, description=\"Search the web for information\")\n", |
709 | | - "file_tool = FunctionTool(file_processor, description=\"Process files\")\n", |
710 | | - "\n", |
711 | | - "# Test different error scenarios\n", |
712 | | - "test_cases = [\n", |
713 | | - " (search_tool, {\"query\": \"python programming\"}), # Success\n", |
714 | | - " (search_tool, {\"query\": \"\"}), # Empty query error\n", |
715 | | - " (search_tool, {\"query\": \"timeout example\"}), # Timeout error\n", |
716 | | - " (file_tool, {\"file_path\": \"document.txt\"}), # Success\n", |
717 | | - " (file_tool, {\"file_path\": \"nonexistent.txt\"}), # File not found\n", |
718 | | - " (file_tool, {\"file_path\": \"permission_denied.txt\"}), # Permission error\n", |
719 | | - "]\n", |
| 644 | + " # Unexpected errors\n", |
| 645 | + " raise RuntimeError(f\"Unexpected error getting weather: {e}\")\n", |
720 | 646 | "\n", |
721 | 647 | "\n", |
722 | | - "async def test_error_handling() -> None:\n", |
723 | | - " \"\"\"Test various error scenarios.\"\"\"\n", |
724 | | - " cancellation_token = CancellationToken()\n", |
| 648 | + "# Create the tool\n", |
| 649 | + "weather_tool = FunctionTool(weather_forecast, description=\"Get weather forecast for a city\")\n", |
725 | 650 | "\n", |
726 | | - " for tool, args in test_cases:\n", |
| 651 | + "# Test different scenarios\n", |
| 652 | + "async def test_weather_tool() -> None:\n", |
| 653 | + " cancellation_token = CancellationToken()\n", |
| 654 | + " \n", |
| 655 | + " for city in [\"New York\", \"\", \"error\", \"unknown\"]:\n", |
727 | 656 | " try:\n", |
728 | | - " result = await tool.run_json(args, cancellation_token)\n", |
729 | | - " print(f\"✅ {tool.name}: {tool.return_value_as_string(result)}\")\n", |
| 657 | + " result = await weather_tool.run_json({\"city\": city}, cancellation_token)\n", |
| 658 | + " print(f\"✅ Weather for {city}: {weather_tool.return_value_as_string(result)}\")\n", |
730 | 659 | " except Exception as e:\n", |
731 | | - " print(f\"❌ {tool.name}: {type(e).__name__}: {e}\")\n", |
732 | | - "\n", |
| 660 | + " print(f\"❌ Error for {city}: {type(e).__name__}: {e}\")\n", |
733 | 661 | "\n", |
734 | 662 | "# Run the tests\n", |
735 | | - "await test_error_handling()" |
| 663 | + "await test_weather_tool()" |
736 | 664 | ] |
737 | 665 | }, |
738 | 666 | { |
|
0 commit comments