-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/weather tool #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
52b6ba5
feat: implement WeatherTool for fetching weather information by location
saqlain2204 43fb890
feat: add WeatherTool and example agent for weather information retri…
saqlain2204 6359cb7
feat: add types-requests to dev dependencies for type checking support
saqlain2204 8cd80ef
feat: add requests to dependencies and types-requests to optional dev…
saqlain2204 d622249
refactor: replace requests with httpx in WeatherTool, add URL encodin…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import os | ||
| from rich import print | ||
| from rich.panel import Panel | ||
| from dotenv import load_dotenv | ||
|
|
||
| from vantage import run_yaml_agent, WeatherTool, save_trace_png | ||
|
|
||
|
|
||
| def main() -> None: | ||
| load_dotenv() | ||
| base_dir = os.path.dirname(os.path.abspath(__file__)) | ||
| cfg_path = os.path.join(base_dir, "weather_agent.yaml") | ||
|
|
||
| prompt = "What is the weather like in New York?" | ||
| resp = run_yaml_agent(cfg_path, "weather_agent", prompt, tools=[WeatherTool()]) | ||
| print(Panel(resp.content, title="[bold][green]Final Response[/green][/bold]", border_style="green")) | ||
|
|
||
| # Save a visual trace of the execution | ||
| trace_path = os.path.join(base_dir, "trace.png") | ||
| save_trace_png(resp.trace, trace_path) | ||
| print(f"\n[bold][green]Visual trace saved to {trace_path}[/green][/bold]") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
|
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| agents: | ||
| weather_agent: | ||
| description: An agent that provides weather information using the `weather_tool` tool. | ||
| model: groq/llama-3.3-70b-versatile | ||
| tools: | ||
| - weather_tool | ||
| system_prompt: | | ||
| You are a helpful weather assistant. Use the `weather_tool` tool to answer weather-related questions accurately. | ||
| response_schema: | ||
| weather_summary: string | ||
| temperature_celsius: number | ||
| location: string | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| from .calculator import Calculator | ||
| from .weather_tool import WeatherTool | ||
|
|
||
| __all__ = ["Calculator"] | ||
| __all__ = ["Calculator", "WeatherTool"] | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from typing import Any, Dict | ||
| from urllib.parse import quote | ||
|
|
||
| import httpx | ||
|
|
||
| from ..core.bases import ToolBase | ||
|
|
||
| _DEFAULT_TIMEOUT = 10.0 | ||
| _RESPONSE_SNIPPET_LENGTH = 200 | ||
|
|
||
|
|
||
| class WeatherTool(ToolBase): | ||
| def __init__(self, timeout: float = _DEFAULT_TIMEOUT) -> None: | ||
| if timeout <= 0: | ||
| raise ValueError("timeout must be positive") | ||
| self._timeout = timeout | ||
|
|
||
| @property | ||
| def name(self) -> str: | ||
| return "weather_tool" | ||
|
|
||
| @property | ||
| def description(self) -> str: | ||
| return "Fetch weather information for a given location." | ||
|
|
||
| def input_schema(self) -> Dict[str, Any]: | ||
| return { | ||
| "type": "object", | ||
| "properties": { | ||
| "location": { | ||
| "type": "string", | ||
| "description": "Location for which to fetch weather information, e.g. 'New York, NY'", | ||
| }, | ||
| }, | ||
| "required": ["location"], | ||
| "additionalProperties": False, | ||
| } | ||
|
|
||
| def execute(self, **kwargs: Any) -> str: | ||
| location = str(kwargs.get("location", "")) | ||
| if not location.strip(): | ||
| raise ValueError("location is required") | ||
| encoded_location = quote(location) | ||
| url = f"https://wttr.in/{encoded_location}" | ||
| try: | ||
| response = httpx.get(url, params={"format": "3"}, timeout=self._timeout) | ||
| response.raise_for_status() | ||
| except httpx.HTTPStatusError as exc: | ||
| body = exc.response.text | ||
| snippet = body[:_RESPONSE_SNIPPET_LENGTH] + ("..." if len(body) > _RESPONSE_SNIPPET_LENGTH else "") | ||
| message = f"Failed to fetch weather data for {location} (status {exc.response.status_code})." | ||
| if snippet: | ||
| message += f" Response snippet: {snippet}" | ||
| raise RuntimeError(message) from exc | ||
| except httpx.RequestError as exc: | ||
| raise RuntimeError(f"Failed to fetch weather data for {location}: {exc}") from exc | ||
| return response.text | ||
|
|
||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| """Tests for WeatherTool.""" | ||
| from __future__ import annotations | ||
|
|
||
| import httpx | ||
| import pytest | ||
| import respx | ||
|
|
||
| from vantage.tools.weather_tool import WeatherTool | ||
|
|
||
|
|
||
| @respx.mock | ||
| def test_execute_returns_weather_text() -> None: | ||
| tool = WeatherTool() | ||
| respx.get("https://wttr.in/London").mock( | ||
| return_value=httpx.Response(200, text="London: ⛅ +15°C") | ||
| ) | ||
| result = tool.execute(location="London") | ||
| assert result == "London: ⛅ +15°C" | ||
|
|
||
|
|
||
| @respx.mock | ||
| def test_execute_url_encodes_location() -> None: | ||
| tool = WeatherTool() | ||
| respx.get("https://wttr.in/New%20York").mock( | ||
| return_value=httpx.Response(200, text="New York: ☀ +22°C") | ||
| ) | ||
| result = tool.execute(location="New York") | ||
| assert "New York" in result | ||
|
|
||
|
|
||
| def test_execute_raises_on_empty_location() -> None: | ||
| tool = WeatherTool() | ||
| with pytest.raises(ValueError, match="location is required"): | ||
| tool.execute(location="") | ||
|
|
||
|
|
||
| def test_execute_raises_on_blank_location() -> None: | ||
| tool = WeatherTool() | ||
| with pytest.raises(ValueError, match="location is required"): | ||
| tool.execute(location=" ") | ||
|
|
||
|
|
||
| @respx.mock | ||
| def test_execute_raises_runtime_error_on_http_error() -> None: | ||
| tool = WeatherTool() | ||
| respx.get("https://wttr.in/BadLocation").mock( | ||
| return_value=httpx.Response(404, text="Not Found") | ||
| ) | ||
| with pytest.raises(RuntimeError, match="status 404"): | ||
| tool.execute(location="BadLocation") | ||
|
|
||
|
|
||
| @respx.mock | ||
| def test_execute_raises_runtime_error_on_network_error() -> None: | ||
| tool = WeatherTool() | ||
| respx.get("https://wttr.in/London").mock(side_effect=httpx.ConnectError("DNS failure")) | ||
| with pytest.raises(RuntimeError, match="Failed to fetch weather data for London"): | ||
| tool.execute(location="London") | ||
|
|
||
|
|
||
| def test_tool_name() -> None: | ||
| assert WeatherTool().name == "weather_tool" | ||
|
|
||
|
|
||
| def test_tool_description() -> None: | ||
| assert "weather" in WeatherTool().description.lower() | ||
|
|
||
|
|
||
| def test_input_schema_requires_location() -> None: | ||
| schema = WeatherTool().input_schema() | ||
| assert "location" in schema["required"] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.