mirror of
https://github.com/temporal-community/temporal-ai-agent.git
synced 2026-03-15 14:08:08 +01:00
mock football data if no key (#37)
This commit is contained in:
committed by
GitHub
parent
7bb6688797
commit
5a3bfbd848
@@ -1,7 +1,8 @@
|
|||||||
RAPIDAPI_KEY=9df2cb5...
|
RAPIDAPI_KEY=9df2cb5...
|
||||||
RAPIDAPI_HOST_FLIGHTS=sky-scrapper.p.rapidapi.com #For travel flight information tool
|
RAPIDAPI_HOST_FLIGHTS=sky-scrapper.p.rapidapi.com #For travel flight information tool
|
||||||
RAPIDAPI_HOST_PACKAGE=trackingpackage.p.rapidapi.com #For eCommerce order status package tracking tool
|
RAPIDAPI_HOST_PACKAGE=trackingpackage.p.rapidapi.com #For eCommerce order status package tracking tool
|
||||||
FOOTBALL_DATA_API_KEY=....
|
FOOTBALL_DATA_API_KEY=
|
||||||
|
# Leave blank to use the built-in mock fixtures generator
|
||||||
|
|
||||||
STRIPE_API_KEY=sk_test_51J...
|
STRIPE_API_KEY=sk_test_51J...
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import NavBar from "../components/NavBar";
|
|||||||
import ChatWindow from "../components/ChatWindow";
|
import ChatWindow from "../components/ChatWindow";
|
||||||
import { apiService } from "../services/api";
|
import { apiService } from "../services/api";
|
||||||
|
|
||||||
const POLL_INTERVAL = 500; // 0.5 seconds
|
const POLL_INTERVAL = 600; // 0.6 seconds
|
||||||
const INITIAL_ERROR_STATE = { visible: false, message: '' };
|
const INITIAL_ERROR_STATE = { visible: false, message: '' };
|
||||||
const DEBOUNCE_DELAY = 300; // 300ms debounce for user input
|
const DEBOUNCE_DELAY = 300; // 300ms debounce for user input
|
||||||
|
|
||||||
|
|||||||
3
setup.md
3
setup.md
@@ -192,8 +192,7 @@ Here is configuration guidance for specific goals. Travel and financial goals ha
|
|||||||
- Note, there is failure built in to this demo (the train booking step) to show how the agent can handle failures and retry. See Tool Configuration below for details.
|
- Note, there is failure built in to this demo (the train booking step) to show how the agent can handle failures and retry. See Tool Configuration below for details.
|
||||||
#### Configuring Agent Goal: goal_match_train_invoice
|
#### Configuring Agent Goal: goal_match_train_invoice
|
||||||
NOTE: This goal was developed for an on-stage demo and has failure (and its resolution) built in to show how the agent can handle failures and retry.
|
NOTE: This goal was developed for an on-stage demo and has failure (and its resolution) built in to show how the agent can handle failures and retry.
|
||||||
* Finding a match requires a key from [Football Data](https://www.football-data.org). Sign up for a free account, then see the 'My Account' page to get your API token. Set `FOOTBALL_DATA_API_KEY` to this value.
|
* Finding a match requires a key from [Football Data](https://www.football-data.org). Sign up for a free account, then see the 'My Account' page to get your API token. Set `FOOTBALL_DATA_API_KEY` to this value. If the key is omitted, the `SearchFixtures` tool automatically returns mock Premier League fixtures (3 months into the future only).
|
||||||
* If you're lazy go to `tools/search_fixtures.py` and replace the `search_fixtures` function with the mock `search_fixtures_example` that exists in the same file.
|
|
||||||
* We use a mock function to search for trains. Start the train API server to use the real API: `python thirdparty/train_api.py`
|
* We use a mock function to search for trains. Start the train API server to use the real API: `python thirdparty/train_api.py`
|
||||||
* * The train activity is 'enterprise' so it's written in C# and requires a .NET runtime. See the [.NET backend](#net-(enterprise)-backend) section for details on running it.
|
* * The train activity is 'enterprise' so it's written in C# and requires a .NET runtime. See the [.NET backend](#net-(enterprise)-backend) section for details on running it.
|
||||||
* Requires a Stripe key for the `create_invoice` tool. Set this in the `STRIPE_API_KEY` environment variable in .env
|
* Requires a Stripe key for the `create_invoice` tool. Set this in the `STRIPE_API_KEY` environment variable in .env
|
||||||
|
|||||||
@@ -114,10 +114,10 @@ goal_match_train_invoice = AgentGoal(
|
|||||||
],
|
],
|
||||||
description="The user wants to book a trip to a city in the UK around the dates of a premier league match. "
|
description="The user wants to book a trip to a city in the UK around the dates of a premier league match. "
|
||||||
"Help the user find a premier league match to attend, search and book trains for that match and offers to invoice them for the cost of train tickets. "
|
"Help the user find a premier league match to attend, search and book trains for that match and offers to invoice them for the cost of train tickets. "
|
||||||
"The user lives in London. "
|
"The user lives in London. Premier league fixtures may be mocked data, so don't worry about valid season dates and teams. "
|
||||||
"Gather args for these tools in order, ensuring you move the user from one tool to the next: "
|
"Gather args for these tools in order, ensuring you move the user from one tool to the next: "
|
||||||
"1. SearchFixtures: Search for fixtures for a team within a specified date range. The user might ask questions about the matches dates and locations to decide on where to go. "
|
"1. SearchFixtures: Search for fixtures for a team within a specified date range. The user might ask questions about the matches dates and locations to decide on where to go. "
|
||||||
"2. SearchTrains: Search for trains to the city of the match and list them for the customer to choose from "
|
"2. SearchTrains: Search for trains to the city of the match. Ensure you list them for the customer to choose from "
|
||||||
"3. BookTrains: Book the train tickets, used to invoice the user for the cost of the train tickets "
|
"3. BookTrains: Book the train tickets, used to invoice the user for the cost of the train tickets "
|
||||||
"4. CreateInvoice: Invoices the user for the cost of train tickets, with total and details inferred from the conversation history ",
|
"4. CreateInvoice: Invoices the user for the cost of train tickets, with total and details inferred from the conversation history ",
|
||||||
starter_prompt=starter_prompt_generic,
|
starter_prompt=starter_prompt_generic,
|
||||||
@@ -489,6 +489,6 @@ if multi_goal_mode:
|
|||||||
if tool.name == "ListAgents":
|
if tool.name == "ListAgents":
|
||||||
list_agents_found = True
|
list_agents_found = True
|
||||||
continue
|
continue
|
||||||
if list_agents_found == False:
|
if list_agents_found is False:
|
||||||
goal.tools.append(tool_registry.list_agents_tool)
|
goal.tools.append(tool_registry.list_agents_tool)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -1,64 +1,263 @@
|
|||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
from datetime import datetime, timedelta
|
import random
|
||||||
|
from datetime import datetime, timedelta, date
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
PREMIER_LEAGUE_CLUBS_DATA = [
|
||||||
|
{"name": "Arsenal FC", "stadium": "Emirates Stadium"},
|
||||||
|
{"name": "Aston Villa FC", "stadium": "Villa Park"},
|
||||||
|
{"name": "AFC Bournemouth", "stadium": "Vitality Stadium"},
|
||||||
|
{"name": "Brentford FC", "stadium": "Gtech Community Stadium"},
|
||||||
|
{"name": "Brighton & Hove Albion FC", "stadium": "American Express Stadium"},
|
||||||
|
{"name": "Chelsea FC", "stadium": "Stamford Bridge"},
|
||||||
|
{"name": "Crystal Palace FC", "stadium": "Selhurst Park"},
|
||||||
|
{"name": "Everton FC", "stadium": "Goodison Park"},
|
||||||
|
{"name": "Fulham FC", "stadium": "Craven Cottage"},
|
||||||
|
{"name": "Ipswich Town FC", "stadium": "Portman Road"},
|
||||||
|
{"name": "Leicester City FC", "stadium": "King Power Stadium"},
|
||||||
|
{"name": "Liverpool FC", "stadium": "Anfield"},
|
||||||
|
{"name": "Manchester City FC", "stadium": "Etihad Stadium"},
|
||||||
|
{"name": "Manchester United FC", "stadium": "Old Trafford"},
|
||||||
|
{"name": "Newcastle United FC", "stadium": "St James' Park"},
|
||||||
|
{"name": "Nottingham Forest FC", "stadium": "City Ground"},
|
||||||
|
{"name": "Southampton FC", "stadium": "St Mary's Stadium"},
|
||||||
|
{"name": "Tottenham Hotspur FC", "stadium": "Tottenham Hotspur Stadium"},
|
||||||
|
{"name": "West Ham United FC", "stadium": "London Stadium"},
|
||||||
|
{"name": "Wolverhampton Wanderers FC", "stadium": "Molineux Stadium"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_future_matches(
|
||||||
|
team_name: str,
|
||||||
|
all_clubs_data: list,
|
||||||
|
num_matches: int = 12,
|
||||||
|
date_from: date = None,
|
||||||
|
date_to: date = None,
|
||||||
|
) -> list:
|
||||||
|
"""Generate a set of future Premier League matches for ``team_name``.
|
||||||
|
|
||||||
|
This is a purely mocked schedule. It returns up to ``num_matches``
|
||||||
|
fixtures, respecting the ``date_from`` and ``date_to`` constraints.
|
||||||
|
Matches are typically on Saturdays or Sundays.
|
||||||
|
"""
|
||||||
|
matches = []
|
||||||
|
|
||||||
|
team_details = next((c for c in all_clubs_data if c["name"] == team_name), None)
|
||||||
|
if not team_details:
|
||||||
|
return []
|
||||||
|
|
||||||
|
opponents_pool = [c for c in all_clubs_data if c["name"] != team_name]
|
||||||
|
if not opponents_pool:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Determine the maximum number of matches we can generate based on opponents
|
||||||
|
# and the requested num_matches
|
||||||
|
num_actual_matches_to_generate = min(num_matches, len(opponents_pool))
|
||||||
|
if num_actual_matches_to_generate == 0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Shuffle opponents once and pick them sequentially
|
||||||
|
random.shuffle(opponents_pool) # Shuffle in place
|
||||||
|
|
||||||
|
# Determine the initial Saturday for match week consideration
|
||||||
|
today_date = date.today()
|
||||||
|
# Default to next Saturday
|
||||||
|
current_match_week_saturday = today_date + timedelta(
|
||||||
|
days=(5 - today_date.weekday() + 7) % 7
|
||||||
|
)
|
||||||
|
|
||||||
|
# If today is Saturday and it's late evening, or if today is Sunday,
|
||||||
|
# advance to the following Saturday.
|
||||||
|
now_time = datetime.now().time()
|
||||||
|
if (
|
||||||
|
today_date.weekday() == 5
|
||||||
|
and now_time > datetime.strptime("20:00", "%H:%M").time()
|
||||||
|
) or (today_date.weekday() == 6):
|
||||||
|
current_match_week_saturday += timedelta(days=7)
|
||||||
|
|
||||||
|
# If date_from is specified, ensure our starting Saturday is not before it.
|
||||||
|
if date_from:
|
||||||
|
if current_match_week_saturday < date_from:
|
||||||
|
current_match_week_saturday = date_from
|
||||||
|
# Align current_match_week_saturday to be a Saturday on or after the potentially adjusted date
|
||||||
|
current_match_week_saturday += timedelta(
|
||||||
|
days=(5 - current_match_week_saturday.weekday() + 7) % 7
|
||||||
|
)
|
||||||
|
|
||||||
|
opponent_idx = 0
|
||||||
|
while len(matches) < num_actual_matches_to_generate and opponent_idx < len(
|
||||||
|
opponents_pool
|
||||||
|
):
|
||||||
|
# If the current week's Saturday is already past date_to, stop.
|
||||||
|
if date_to and current_match_week_saturday > date_to:
|
||||||
|
break
|
||||||
|
|
||||||
|
opponent_details = opponents_pool[opponent_idx]
|
||||||
|
is_saturday_game = random.choice([True, True, False])
|
||||||
|
actual_match_date = None
|
||||||
|
kick_off_time = ""
|
||||||
|
|
||||||
|
if is_saturday_game:
|
||||||
|
actual_match_date = current_match_week_saturday
|
||||||
|
kick_off_time = random.choice(["12:30", "15:00", "17:30"])
|
||||||
|
else: # Sunday game
|
||||||
|
actual_match_date = current_match_week_saturday + timedelta(days=1)
|
||||||
|
kick_off_time = random.choice(["14:00", "16:30"])
|
||||||
|
|
||||||
|
# Check if this specific match date is within the date_to constraint
|
||||||
|
if date_to and actual_match_date > date_to:
|
||||||
|
# If this game is too late, try the next week if possible.
|
||||||
|
# (This mainly affects Sunday games if Saturday was the last valid day)
|
||||||
|
current_match_week_saturday += timedelta(days=7)
|
||||||
|
continue # Skip adding this match, try next week.
|
||||||
|
|
||||||
|
match_datetime_gmt = (
|
||||||
|
f"{actual_match_date.strftime('%Y-%m-%d')} {kick_off_time} GMT"
|
||||||
|
)
|
||||||
|
is_home_match = random.choice([True, False])
|
||||||
|
|
||||||
|
if is_home_match:
|
||||||
|
team1_name = team_details["name"]
|
||||||
|
team2_name = opponent_details["name"]
|
||||||
|
stadium_name = team_details["stadium"]
|
||||||
|
else:
|
||||||
|
team1_name = opponent_details["name"]
|
||||||
|
team2_name = team_details["name"]
|
||||||
|
stadium_name = opponent_details["stadium"]
|
||||||
|
|
||||||
|
matches.append(
|
||||||
|
{
|
||||||
|
"team1": team1_name,
|
||||||
|
"team2": team2_name,
|
||||||
|
"stadium": stadium_name,
|
||||||
|
"datetime_gmt": match_datetime_gmt,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
opponent_idx += 1
|
||||||
|
current_match_week_saturday += timedelta(
|
||||||
|
days=7
|
||||||
|
) # Advance to next week's Saturday
|
||||||
|
|
||||||
|
return matches
|
||||||
|
|
||||||
|
|
||||||
BASE_URL = "https://api.football-data.org/v4"
|
BASE_URL = "https://api.football-data.org/v4"
|
||||||
|
|
||||||
|
|
||||||
def search_fixtures(args: dict) -> dict:
|
def search_fixtures(args: dict) -> dict:
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
api_key = os.getenv("FOOTBALL_DATA_API_KEY", "YOUR_DEFAULT_KEY")
|
api_key = os.getenv("FOOTBALL_DATA_API_KEY")
|
||||||
|
|
||||||
team_name = args.get("team")
|
team_name = args.get("team")
|
||||||
date_from_str = args.get("date_from")
|
date_from_str = args.get("date_from")
|
||||||
date_to_str = args.get("date_to")
|
date_to_str = args.get("date_to")
|
||||||
headers = {"X-Auth-Token": api_key}
|
|
||||||
team_name = team_name.lower()
|
|
||||||
|
|
||||||
try:
|
if not team_name:
|
||||||
date_from = datetime.strptime(date_from_str, "%Y-%m-%d")
|
return {"error": "Team name is required."}
|
||||||
date_to = datetime.strptime(date_to_str, "%Y-%m-%d")
|
|
||||||
except ValueError:
|
parsed_date_from = None
|
||||||
|
if date_from_str:
|
||||||
|
try:
|
||||||
|
parsed_date_from = datetime.strptime(date_from_str, "%Y-%m-%d").date()
|
||||||
|
except ValueError:
|
||||||
|
return {
|
||||||
|
"error": f"Invalid date_from: '{date_from_str}'. Expected format YYYY-MM-DD."
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed_date_to = None
|
||||||
|
if date_to_str:
|
||||||
|
try:
|
||||||
|
parsed_date_to = datetime.strptime(date_to_str, "%Y-%m-%d").date()
|
||||||
|
except ValueError:
|
||||||
|
return {
|
||||||
|
"error": f"Invalid date_to: '{date_to_str}'. Expected format YYYY-MM-DD."
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed_date_from and parsed_date_to and parsed_date_from > parsed_date_to:
|
||||||
|
return {"error": "date_from cannot be after date_to."}
|
||||||
|
|
||||||
|
# If no API key, fall back to mocked data
|
||||||
|
if not api_key:
|
||||||
|
# Use the parsed date objects (which can be None)
|
||||||
|
fixtures = get_future_matches(
|
||||||
|
team_name,
|
||||||
|
PREMIER_LEAGUE_CLUBS_DATA,
|
||||||
|
date_from=parsed_date_from,
|
||||||
|
date_to=parsed_date_to,
|
||||||
|
# num_matches can be passed explicitly if needed, otherwise defaults to 12
|
||||||
|
)
|
||||||
|
if not fixtures:
|
||||||
|
# Check if the team name itself was invalid, as get_future_matches returns [] for that too
|
||||||
|
team_details_check = next(
|
||||||
|
(c for c in PREMIER_LEAGUE_CLUBS_DATA if c["name"] == team_name), None
|
||||||
|
)
|
||||||
|
if not team_details_check:
|
||||||
|
return {"error": f"Team '{team_name}' not found in mocked data."}
|
||||||
|
# If team is valid, an empty fixtures list means no matches fit the criteria (e.g., date range)
|
||||||
|
return {"fixtures": fixtures}
|
||||||
|
|
||||||
|
# API Key is present, proceed with API logic
|
||||||
|
# The API requires both date_from and date_to
|
||||||
|
if not parsed_date_from or not parsed_date_to:
|
||||||
return {
|
return {
|
||||||
"error": "Invalid date provided. Expected format YYYY-MM-DD for both date_from and date_to."
|
"error": "Both date_from and date_to (YYYY-MM-DD) are required for API search."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
headers = {"X-Auth-Token": api_key}
|
||||||
|
# For API calls, team name matching might be case-insensitive or require specific handling
|
||||||
|
# The existing logic uses team_name.lower() for the API search path later.
|
||||||
|
|
||||||
# Fetch team ID
|
# Fetch team ID
|
||||||
teams_response = requests.get(f"{BASE_URL}/competitions/PL/teams", headers=headers)
|
teams_response = requests.get(f"{BASE_URL}/competitions/PL/teams", headers=headers)
|
||||||
if teams_response.status_code != 200:
|
if teams_response.status_code != 200:
|
||||||
return {"error": "Failed to fetch teams data."}
|
return {
|
||||||
|
"error": f"Failed to fetch teams data from API (status {teams_response.status_code})."
|
||||||
|
}
|
||||||
|
|
||||||
teams_data = teams_response.json()
|
teams_data = teams_response.json()
|
||||||
team_id = None
|
team_id = None
|
||||||
for team in teams_data["teams"]:
|
# Using lower() for comparison, assuming API team names might have varied casing
|
||||||
if team_name in team["name"].lower():
|
# or the input team_name might not be exact.
|
||||||
team_id = team["id"]
|
# The `ToolDefinition` lists exact names, so direct match might also be an option.
|
||||||
|
for team_api_data in teams_data.get("teams", []):
|
||||||
|
if team_name.lower() in team_api_data.get("name", "").lower():
|
||||||
|
team_id = team_api_data["id"]
|
||||||
break
|
break
|
||||||
|
|
||||||
if not team_id:
|
if not team_id:
|
||||||
return {"error": "Team not found."}
|
return {"error": f"Team '{team_name}' not found via API."}
|
||||||
|
|
||||||
date_from_formatted = date_from.strftime("%Y-%m-%d")
|
date_from_formatted = parsed_date_from.strftime("%Y-%m-%d")
|
||||||
date_to_formatted = date_to.strftime("%Y-%m-%d")
|
date_to_formatted = parsed_date_to.strftime("%Y-%m-%d")
|
||||||
fixtures_url = f"{BASE_URL}/teams/{team_id}/matches?dateFrom={date_from_formatted}&dateTo={date_to_formatted}"
|
fixtures_url = f"{BASE_URL}/teams/{team_id}/matches?dateFrom={date_from_formatted}&dateTo={date_to_formatted}"
|
||||||
print(fixtures_url)
|
# print(fixtures_url) # Keep for debugging if necessary
|
||||||
|
|
||||||
fixtures_response = requests.get(fixtures_url, headers=headers)
|
fixtures_response = requests.get(fixtures_url, headers=headers)
|
||||||
if fixtures_response.status_code != 200:
|
if fixtures_response.status_code != 200:
|
||||||
return {"error": "Failed to fetch fixtures data."}
|
return {
|
||||||
|
"error": f"Failed to fetch fixtures data from API (status {fixtures_response.status_code})."
|
||||||
|
}
|
||||||
|
|
||||||
fixtures_data = fixtures_response.json()
|
fixtures_data = fixtures_response.json()
|
||||||
matching_fixtures = []
|
matching_fixtures = []
|
||||||
|
|
||||||
for match in fixtures_data.get("matches", []):
|
for match in fixtures_data.get("matches", []):
|
||||||
match_datetime = datetime.strptime(match["utcDate"], "%Y-%m-%dT%H:%M:%SZ")
|
# Ensure match datetime parsing is robust
|
||||||
if match["competition"]["code"] == "PL":
|
try:
|
||||||
|
match_datetime_utc = datetime.strptime(
|
||||||
|
match["utcDate"], "%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
# Skip malformed match entries or log an error
|
||||||
|
continue
|
||||||
|
|
||||||
|
if match.get("competition", {}).get("code") == "PL":
|
||||||
matching_fixtures.append(
|
matching_fixtures.append(
|
||||||
{
|
{
|
||||||
"date": match_datetime.strftime("%Y-%m-%d"),
|
"date": match_datetime_utc.strftime("%Y-%m-%d"),
|
||||||
"homeTeam": match["homeTeam"]["name"],
|
"homeTeam": match.get("homeTeam", {}).get("name", "N/A"),
|
||||||
"awayTeam": match["awayTeam"]["name"],
|
"awayTeam": match.get("awayTeam", {}).get("name", "N/A"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -82,34 +281,69 @@ def search_fixtures_example(args: dict) -> dict:
|
|||||||
|
|
||||||
# Validate dates
|
# Validate dates
|
||||||
try:
|
try:
|
||||||
date_from = datetime.strptime(date_from_str, "%Y-%m-%d")
|
# Ensure date strings are not None before parsing
|
||||||
date_to = datetime.strptime(date_to_str, "%Y-%m-%d")
|
if date_from_str is None or date_to_str is None:
|
||||||
|
raise ValueError("Date strings cannot be None")
|
||||||
|
date_from_obj = datetime.strptime(date_from_str, "%Y-%m-%d")
|
||||||
|
date_to_obj = datetime.strptime(date_to_str, "%Y-%m-%d")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return {
|
return {
|
||||||
"error": "Invalid date provided. Expected format YYYY-MM-DD for both date_from and date_to."
|
"error": "Invalid date provided. Expected format YYYY-MM-DD for both date_from and date_to."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Calculate 3 reasonable fixture dates within the given range
|
# Calculate 3 reasonable fixture dates within the given range
|
||||||
date_range = (date_to - date_from).days
|
date_range = (date_to_obj - date_from_obj).days
|
||||||
|
if date_range < 0: # date_from is after date_to
|
||||||
|
return {"fixtures": []} # No fixtures possible
|
||||||
|
|
||||||
|
fixture_dates_timestamps = []
|
||||||
if date_range < 21:
|
if date_range < 21:
|
||||||
# If range is less than 3 weeks, use evenly spaced fixtures
|
# If range is less than 3 weeks, use evenly spaced fixtures if possible
|
||||||
fixture_dates = [
|
if date_range >= 2: # Need at least some gap for 3 fixtures
|
||||||
date_from + timedelta(days=max(1, date_range // 3)),
|
fixture_dates_timestamps = [
|
||||||
date_from + timedelta(days=max(2, date_range * 2 // 3)),
|
date_from_obj
|
||||||
date_to - timedelta(days=min(2, date_range // 4)),
|
+ timedelta(days=max(0, date_range // 4)), # Closer to start
|
||||||
]
|
date_from_obj + timedelta(days=max(1, date_range // 2)), # Middle
|
||||||
|
date_to_obj - timedelta(days=max(0, date_range // 4)), # Closer to end
|
||||||
|
]
|
||||||
|
elif date_range == 1: # Only two days
|
||||||
|
fixture_dates_timestamps = [date_from_obj, date_to_obj]
|
||||||
|
elif date_range == 0: # Only one day
|
||||||
|
fixture_dates_timestamps = [date_from_obj]
|
||||||
|
else: # date_range is negative, handled above, or 0 (single day)
|
||||||
|
fixture_dates_timestamps = [date_from_obj] if date_range == 0 else []
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Otherwise space them out by weeks
|
# Otherwise space them out by weeks, ensuring they are within the bounds
|
||||||
fixture_dates = [
|
d1 = date_from_obj + timedelta(days=7)
|
||||||
date_from + timedelta(days=7),
|
d2 = date_from_obj + timedelta(days=14)
|
||||||
date_from + timedelta(days=14),
|
d3 = date_to_obj - timedelta(days=7) # Potential third game from the end
|
||||||
date_to - timedelta(days=7),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Ensure we only have 3 dates
|
fixture_dates_timestamps.append(d1)
|
||||||
fixture_dates = fixture_dates[:3]
|
if d2 <= date_to_obj and d2 > d1: # ensure d2 is valid and distinct
|
||||||
|
fixture_dates_timestamps.append(d2)
|
||||||
|
if (
|
||||||
|
d3 >= date_from_obj and d3 > d2 and d3 <= date_to_obj
|
||||||
|
): # ensure d3 is valid and distinct
|
||||||
|
fixture_dates_timestamps.append(d3)
|
||||||
|
elif (
|
||||||
|
d3 < date_from_obj and len(fixture_dates_timestamps) < 3
|
||||||
|
): # if d3 is too early, try using date_to_obj itself if distinct
|
||||||
|
if date_to_obj not in fixture_dates_timestamps:
|
||||||
|
fixture_dates_timestamps.append(date_to_obj)
|
||||||
|
|
||||||
|
# Ensure unique dates and sort, then take up to 3.
|
||||||
|
fixture_dates_timestamps = sorted(
|
||||||
|
list(
|
||||||
|
set(
|
||||||
|
f_date
|
||||||
|
for f_date in fixture_dates_timestamps
|
||||||
|
if date_from_obj <= f_date <= date_to_obj
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fixture_dates_final = fixture_dates_timestamps[:3]
|
||||||
|
|
||||||
# Expanded pool of opponent teams to avoid team playing against itself
|
|
||||||
all_opponents = [
|
all_opponents = [
|
||||||
"Manchester United FC",
|
"Manchester United FC",
|
||||||
"Leicester City FC",
|
"Leicester City FC",
|
||||||
@@ -120,35 +354,35 @@ def search_fixtures_example(args: dict) -> dict:
|
|||||||
"Tottenham Hotspur FC",
|
"Tottenham Hotspur FC",
|
||||||
"West Ham United FC",
|
"West Ham United FC",
|
||||||
"Everton FC",
|
"Everton FC",
|
||||||
|
"Generic Opponent A",
|
||||||
|
"Generic Opponent B",
|
||||||
|
"Generic Opponent C", # Fallbacks
|
||||||
]
|
]
|
||||||
|
|
||||||
# Select opponents that aren't the same as the requested team
|
|
||||||
available_opponents = [
|
available_opponents = [
|
||||||
team for team in all_opponents if team.lower() != team_name.lower()
|
team for team in all_opponents if team.lower() != team_name.lower()
|
||||||
]
|
]
|
||||||
|
|
||||||
# Ensure we have at least 3 opponents
|
# Ensure we have enough opponents for the number of fixtures we'll generate
|
||||||
if len(available_opponents) < 3:
|
if len(available_opponents) < len(fixture_dates_final):
|
||||||
# Add generic opponents if needed
|
needed = len(fixture_dates_final) - len(available_opponents)
|
||||||
additional_teams = [f"Opponent {i} FC" for i in range(1, 4)]
|
for i in range(needed):
|
||||||
available_opponents.extend(additional_teams)
|
available_opponents.append(f"Placeholder Opponent {i+1}")
|
||||||
|
|
||||||
# Take only the first 3 opponents
|
opponents = available_opponents[: len(fixture_dates_final)]
|
||||||
opponents = available_opponents[:3]
|
|
||||||
|
|
||||||
# Generate fixtures - always exactly 3
|
|
||||||
fixtures = []
|
fixtures = []
|
||||||
for i, fixture_date in enumerate(fixture_dates):
|
for i, fixture_date_obj in enumerate(fixture_dates_final):
|
||||||
date_str = fixture_date.strftime("%Y-%m-%d")
|
if i >= len(opponents): # Should not happen with the logic above
|
||||||
|
break
|
||||||
# Alternate between home and away games
|
date_str = fixture_date_obj.strftime("%Y-%m-%d")
|
||||||
if i % 2 == 0:
|
if i % 2 == 0: # Home game
|
||||||
fixtures.append(
|
|
||||||
{"date": date_str, "homeTeam": opponents[i], "awayTeam": team_name}
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
fixtures.append(
|
fixtures.append(
|
||||||
{"date": date_str, "homeTeam": team_name, "awayTeam": opponents[i]}
|
{"date": date_str, "homeTeam": team_name, "awayTeam": opponents[i]}
|
||||||
)
|
)
|
||||||
|
else: # Away game
|
||||||
|
fixtures.append(
|
||||||
|
{"date": date_str, "homeTeam": opponents[i], "awayTeam": team_name}
|
||||||
|
)
|
||||||
|
|
||||||
return {"fixtures": fixtures}
|
return {"fixtures": fixtures}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ search_flights_tool = ToolDefinition(
|
|||||||
|
|
||||||
search_trains_tool = ToolDefinition(
|
search_trains_tool = ToolDefinition(
|
||||||
name="SearchTrains",
|
name="SearchTrains",
|
||||||
description="Search for trains between two English cities. Returns a list of train information for the user to choose from.",
|
description="Search for trains between two English cities. Returns a list of train information for the user to choose from. Present the list to the user.",
|
||||||
arguments=[
|
arguments=[
|
||||||
ToolArgument(
|
ToolArgument(
|
||||||
name="origin",
|
name="origin",
|
||||||
@@ -156,7 +156,7 @@ create_invoice_tool = ToolDefinition(
|
|||||||
|
|
||||||
search_fixtures_tool = ToolDefinition(
|
search_fixtures_tool = ToolDefinition(
|
||||||
name="SearchFixtures",
|
name="SearchFixtures",
|
||||||
description="Search for upcoming fixtures for a given team within a date range inferred from the user's description. Valid teams this 24/25 season are Arsenal FC, Aston Villa FC, AFC Bournemouth, Brentford FC, Brighton & Hove Albion FC, Chelsea FC, Crystal Palace FC, Everton FC, Fulham FC, Ipswich Town FC, Leicester City FC, Liverpool FC, Manchester City FC, Manchester United FC, Newcastle United FC, Nottingham Forest FC, Southampton FC, Tottenham Hotspur FC, West Ham United FC, Wolverhampton Wanderers FC",
|
description="Search for upcoming fixtures for a given team within a date range inferred from the user's description. Ignore valid premier league dates. Valid teams this season are Arsenal FC, Aston Villa FC, AFC Bournemouth, Brentford FC, Brighton & Hove Albion FC, Chelsea FC, Crystal Palace FC, Everton FC, Fulham FC, Ipswich Town FC, Leicester City FC, Liverpool FC, Manchester City FC, Manchester United FC, Newcastle United FC, Nottingham Forest FC, Southampton FC, Tottenham Hotspur FC, West Ham United FC, Wolverhampton Wanderers FC",
|
||||||
arguments=[
|
arguments=[
|
||||||
ToolArgument(
|
ToolArgument(
|
||||||
name="team",
|
name="team",
|
||||||
|
|||||||
Reference in New Issue
Block a user