-
Notifications
You must be signed in to change notification settings - Fork 8.2k
feat(crewai-tools): add Live Tennis API tool #7000
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
Open
bensynapse
wants to merge
4
commits into
crewAIInc:main
Choose a base branch
from
bensynapse:add-live-tennis-tool
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
a9bc259
feat(crewai-tools): add Live Tennis API tool
bensynapse 90c8c6e
fix(crewai-tools): address review — drop utr listing, numeric player_…
bensynapse 37a4493
docs(crewai-tools): parse the player id from the search response in e…
bensynapse b3678a1
docs(crewai-tools): guard the search-to-profile example against error…
bensynapse 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
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 |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| --- | ||
| title: Live Tennis Tool | ||
| description: Live tennis scores, fixtures, player profiles and rankings for CrewAI agents via the Live Tennis API. | ||
| icon: trophy | ||
| mode: "wide" | ||
| --- | ||
|
|
||
| # `LiveTennisTool` | ||
|
|
||
| ## Description | ||
|
|
||
| Query professional tennis data from the [Live Tennis API](https://livetennisapi.com): matches currently in play with live scores, upcoming matches, scheduled fixtures, player search and profiles (including current ranking), published ranking tables, and your API quota usage. | ||
|
|
||
| The tool is scoped to REST endpoints, most of which are available on the API's free tier (keyed, 30 requests/minute, 100 requests/day), so it can be tried without payment. Completed-match history is a paid feature of the API and is not part of this tool. | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| ```bash | ||
| LIVETENNIS_API_KEY=your_api_key # Free key at livetennisapi.com | ||
| ``` | ||
|
|
||
| ## Actions | ||
|
|
||
| | `action` | What it returns | Plan | | ||
| |---|---|---| | ||
| | `live_matches` | Matches in play with current scores (optional `tour` filter) | Free | | ||
| | `upcoming_matches` | Matches starting soon | Free | | ||
| | `fixtures` | Scheduled matches | Free | | ||
| | `search_players` | Player search by name (requires `search`) | Free | | ||
| | `player_profile` | Single player detail, including current ranking (requires `player_id`) | Free | | ||
| | `rankings` | Published ranking table (requires `system`: `atp`, `wta`, `itf_jt`, `itf_mt`, `itf_wt`, `utr`) | PRO | | ||
| | `usage` | Your API quota vs. consumption | Free | | ||
|
|
||
| ## Basic Usage | ||
|
|
||
| ```python | ||
| from crewai import Agent | ||
| from crewai_tools import LiveTennisTool | ||
|
|
||
| tool = LiveTennisTool() | ||
|
|
||
| reporter = Agent( | ||
| role="Tennis Reporter", | ||
| goal="Summarise what is happening on tour right now", | ||
| backstory="You follow professional tennis and report live developments.", | ||
| tools=[tool], | ||
| ) | ||
| ``` | ||
|
|
||
| ## Direct Invocation | ||
|
|
||
| ```python | ||
| from crewai_tools import LiveTennisTool | ||
|
|
||
| tool = LiveTennisTool() | ||
|
|
||
| # Matches in play right now | ||
| print(tool.run(action="live_matches", tour="atp")) | ||
|
|
||
| # Find a player, then load their profile | ||
| players = tool.run(action="search_players", search="alcaraz") | ||
| profile = tool.run(action="player_profile", player_id="<id from search>") | ||
| ``` | ||
|
|
||
| ## Error Handling | ||
|
|
||
| The tool returns readable messages (rather than raising) when the API key is missing, invalid (401), the endpoint needs a higher plan (403), or the rate limit is hit (429), so an agent can recover or report the problem. | ||
|
|
||
| Full API documentation: [docs.livetennisapi.com](https://docs.livetennisapi.com). |
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
60 changes: 60 additions & 0 deletions
60
lib/crewai-tools/src/crewai_tools/tools/live_tennis_tool/README.md
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,60 @@ | ||
| # Live Tennis Tool | ||
|
|
||
| Query professional tennis data from the [Live Tennis API](https://livetennisapi.com) — live match scores, upcoming matches, scheduled fixtures, player search and profiles, ranking tables, and API usage. | ||
|
|
||
| The tool is scoped to REST endpoints, most of which are available on the free tier, so it can be tried without payment. | ||
|
|
||
| ## Actions | ||
|
|
||
| | `action` | Endpoint | Plan | Notes | | ||
| |---|---|---|---| | ||
| | `live_matches` | `GET /matches?status=live` | Free | Matches in play with current scores | | ||
| | `upcoming_matches` | `GET /matches?status=upcoming` | Free | Matches starting soon | | ||
| | `fixtures` | `GET /fixtures` | Free | Scheduled matches | | ||
| | `search_players` | `GET /players?search=` | Free | Requires `search` | | ||
| | `player_profile` | `GET /players/{id}` | Free | Requires `player_id`; includes current ranking | | ||
| | `rankings` | `GET /rankings?system=` | PRO | Requires `system` (`atp`, `wta`, `itf_jt`, `itf_mt`, `itf_wt`, `utr`) | | ||
| | `usage` | `GET /usage` | Free | Your quota vs. consumption; exempt from quota | | ||
|
|
||
| The free tier is keyed and rate-limited to 30 requests/minute and 100 requests/day. Completed-match history is a paid feature and is not part of this tool. The API also offers a WebSocket push feed and model win probability on its top tier; this tool intentionally sticks to the polling REST surface. | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| ```env | ||
| LIVETENNIS_API_KEY=your_api_key | ||
| ``` | ||
|
|
||
| Get a free key at [livetennisapi.com](https://livetennisapi.com). Full API documentation: [docs.livetennisapi.com](https://docs.livetennisapi.com). | ||
|
|
||
| ## Example Usage | ||
|
|
||
| ```python | ||
| from crewai_tools import LiveTennisTool | ||
|
|
||
| tool = LiveTennisTool() | ||
|
|
||
| # Matches in play right now | ||
| print(tool.run(action="live_matches", tour="atp")) | ||
|
|
||
| # Find a player, then load their profile | ||
| players = tool.run(action="search_players", search="alcaraz") | ||
| profile = tool.run(action="player_profile", player_id="<id from search>") | ||
| ``` | ||
|
|
||
| ## With an Agent | ||
|
|
||
| ```python | ||
| from crewai import Agent | ||
| from crewai_tools import LiveTennisTool | ||
|
|
||
| reporter = Agent( | ||
| role="Tennis Reporter", | ||
| goal="Summarise what is happening on tour right now", | ||
| backstory="You follow professional tennis and report live developments.", | ||
| tools=[LiveTennisTool()], | ||
| ) | ||
| ``` | ||
|
|
||
| ## Error Handling | ||
|
|
||
| The tool returns readable messages (rather than raising) when the API key is missing, invalid (401), the endpoint needs a higher plan (403), or the rate limit is hit (429), so an agent can recover or report the problem. | ||
8 changes: 8 additions & 0 deletions
8
lib/crewai-tools/src/crewai_tools/tools/live_tennis_tool/__init__.py
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,8 @@ | ||
| from crewai_tools.tools.live_tennis_tool.live_tennis_tool import LiveTennisTool | ||
| from crewai_tools.tools.live_tennis_tool.schemas import LiveTennisToolSchema | ||
|
|
||
|
|
||
| __all__ = [ | ||
| "LiveTennisTool", | ||
| "LiveTennisToolSchema", | ||
| ] |
123 changes: 123 additions & 0 deletions
123
lib/crewai-tools/src/crewai_tools/tools/live_tennis_tool/live_tennis_tool.py
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,123 @@ | ||
| """Tool for querying professional tennis data from the Live Tennis API.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| import os | ||
| from typing import Any | ||
| from urllib.parse import quote | ||
|
|
||
| from crewai.tools import BaseTool, EnvVar | ||
| from pydantic import BaseModel, Field, ValidationError | ||
| import requests | ||
|
|
||
| from crewai_tools.tools.live_tennis_tool.schemas import LiveTennisToolSchema | ||
|
|
||
|
|
||
| API_KEY_ENV_VAR = "LIVETENNIS_API_KEY" | ||
|
|
||
|
|
||
| class LiveTennisTool(BaseTool): | ||
| """Query the Live Tennis API (https://livetennisapi.com). | ||
|
|
||
| Covers the endpoints available on the free tier: live match scores, | ||
| upcoming matches, scheduled fixtures, player search and profiles | ||
| (including current ranking), and API usage. The 'rankings' action | ||
| (full published ranking tables) requires a paid plan. | ||
| """ | ||
|
|
||
| name: str = "Live Tennis API" | ||
| description: str = ( | ||
| "Fetches professional tennis data from the Live Tennis API: matches " | ||
| "currently in play with live scores, upcoming matches, scheduled " | ||
| "fixtures, player search and profiles (including current ranking and " | ||
| "ranking points), published ranking tables, and your API quota usage. " | ||
| "Results are returned as JSON." | ||
| ) | ||
| args_schema: type[BaseModel] = LiveTennisToolSchema | ||
| base_url: str = "https://api.livetennisapi.com/api/public/v1" | ||
| request_timeout: float = 30.0 | ||
| env_vars: list[EnvVar] = Field( | ||
| default_factory=lambda: [ | ||
| EnvVar( | ||
| name=API_KEY_ENV_VAR, | ||
| description="API key for the Live Tennis API (free tier available)", | ||
| required=True, | ||
| ), | ||
| ] | ||
| ) | ||
|
|
||
| def _run(self, **kwargs: Any) -> str: | ||
| api_key = os.environ.get(API_KEY_ENV_VAR, "").strip() | ||
| if not api_key: | ||
| return ( | ||
| f"The {API_KEY_ENV_VAR} environment variable is not set. " | ||
| "Get a free API key at https://livetennisapi.com and set " | ||
| f"{API_KEY_ENV_VAR} to use this tool." | ||
| ) | ||
|
|
||
| try: | ||
| params = LiveTennisToolSchema(**kwargs) | ||
| except ValidationError as e: | ||
| return f"Invalid arguments for the Live Tennis API tool: {e}" | ||
|
|
||
| path, query = self._build_request(params) | ||
| try: | ||
| response = requests.get( | ||
| f"{self.base_url}{path}", | ||
| headers={"Authorization": f"Bearer {api_key}"}, | ||
| params=query, | ||
| timeout=self.request_timeout, | ||
| ) | ||
| except requests.RequestException as e: | ||
| return f"Live Tennis API request failed: {e}" | ||
|
|
||
| if response.status_code == 401: | ||
| return ( | ||
| "Live Tennis API rejected the request (401): the " | ||
| f"{API_KEY_ENV_VAR} key is missing, unknown, or disabled." | ||
| ) | ||
| if response.status_code == 403: | ||
| return ( | ||
| "Live Tennis API returned 403: this endpoint requires a higher " | ||
| f"subscription tier than the current key provides. {response.text}" | ||
| ) | ||
| if response.status_code == 429: | ||
| return f"Live Tennis API rate limit exceeded (429): {response.text}" | ||
| if not response.ok: | ||
| return ( | ||
| f"Live Tennis API returned HTTP {response.status_code}: {response.text}" | ||
| ) | ||
|
|
||
| try: | ||
| return json.dumps(response.json()) | ||
| except ValueError: | ||
| return response.text | ||
|
|
||
| @staticmethod | ||
| def _build_request(params: LiveTennisToolSchema) -> tuple[str, dict[str, Any]]: | ||
| """Map a validated action to its endpoint path and query parameters.""" | ||
| query: dict[str, Any] = {} | ||
| if params.limit is not None: | ||
| query["limit"] = params.limit | ||
| if params.offset is not None: | ||
| query["offset"] = params.offset | ||
|
|
||
| if params.action in ("live_matches", "upcoming_matches"): | ||
| query["status"] = "live" if params.action == "live_matches" else "upcoming" | ||
| if params.tour: | ||
| query["tour"] = params.tour | ||
| return "/matches", query | ||
| if params.action == "fixtures": | ||
| if params.tour: | ||
| query["tour"] = params.tour | ||
| return "/fixtures", query | ||
| if params.action == "search_players": | ||
| query["search"] = params.search | ||
| return "/players", query | ||
| if params.action == "player_profile": | ||
| return f"/players/{quote(str(params.player_id), safe='')}", {} | ||
| if params.action == "rankings": | ||
| query["system"] = params.system | ||
| return "/rankings", query | ||
| return "/usage", {} |
88 changes: 88 additions & 0 deletions
88
lib/crewai-tools/src/crewai_tools/tools/live_tennis_tool/schemas.py
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,88 @@ | ||
| """Pydantic input schemas for the Live Tennis API tool.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from typing import Literal | ||
|
|
||
| from pydantic import BaseModel, Field, model_validator | ||
|
|
||
|
|
||
| LiveTennisAction = Literal[ | ||
| "live_matches", | ||
| "upcoming_matches", | ||
| "fixtures", | ||
| "search_players", | ||
| "player_profile", | ||
| "rankings", | ||
| "usage", | ||
| ] | ||
|
|
||
| RankingSystem = Literal["atp", "wta", "itf_jt", "itf_mt", "itf_wt", "utr"] | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| class LiveTennisToolSchema(BaseModel): | ||
| """Input schema for LiveTennisTool.""" | ||
|
|
||
| action: LiveTennisAction = Field( | ||
| ..., | ||
| description=( | ||
| "Operation to perform: 'live_matches' (matches in play right now, " | ||
| "with current scores), 'upcoming_matches' (matches starting soon), " | ||
| "'fixtures' (scheduled matches), 'search_players' (find players by " | ||
| "name, requires 'search'), 'player_profile' (single player detail " | ||
| "including current ranking, requires 'player_id'), 'rankings' " | ||
| "(published ranking table, requires 'system'), 'usage' (your API " | ||
| "quota and consumption)." | ||
| ), | ||
| ) | ||
| tour: str | None = Field( | ||
| default=None, | ||
| description=( | ||
| "Optional tour filter for match and fixture actions, e.g. 'atp' or 'wta'." | ||
| ), | ||
| ) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| search: str | None = Field( | ||
| default=None, | ||
| description=( | ||
| "Player name (or name fragment) to look up. Required for the " | ||
| "'search_players' action." | ||
| ), | ||
| ) | ||
| player_id: str | None = Field( | ||
| default=None, | ||
| description=( | ||
| "Player id as returned by 'search_players'. Required for the " | ||
| "'player_profile' action." | ||
| ), | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| ) | ||
| system: RankingSystem | None = Field( | ||
| default=None, | ||
| description=( | ||
| "Ranking system for the 'rankings' action: 'atp', 'wta', 'itf_jt', " | ||
| "'itf_mt', 'itf_wt' or 'utr'." | ||
| ), | ||
| ) | ||
| limit: int | None = Field( | ||
| default=None, | ||
| ge=1, | ||
| description="Maximum number of results to return (API default: 50).", | ||
| ) | ||
| offset: int | None = Field( | ||
| default=None, | ||
| ge=0, | ||
| description="Pagination offset into the result list.", | ||
| ) | ||
|
|
||
| @model_validator(mode="after") | ||
| def _validate_action_arguments(self) -> LiveTennisToolSchema: | ||
| if self.action == "search_players" and not ( | ||
| self.search and self.search.strip() | ||
| ): | ||
| raise ValueError("'search' is required for the 'search_players' action.") | ||
| if self.action == "player_profile" and not ( | ||
| self.player_id and self.player_id.strip() | ||
| ): | ||
| raise ValueError("'player_id' is required for the 'player_profile' action.") | ||
| if self.action == "rankings" and self.system is None: | ||
| raise ValueError("'system' is required for the 'rankings' action.") | ||
| return self | ||
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.