mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
WIP: Starting work on running games via websocket
First crack, mostly Claude, on running each agent as an individual connection to the server via WebSocket.
This commit is contained in:
parent
78d9ed6818
commit
aa0e5852e5
10 changed files with 2064 additions and 855 deletions
131
.vscode/launch.json
vendored
131
.vscode/launch.json
vendored
|
|
@ -14,6 +14,137 @@
|
|||
"--remote-debugging-port=9223"
|
||||
],
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Multi Bot Launcher",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bot_client/multi_bot_launcher.py",
|
||||
"args": [
|
||||
"--hostname",
|
||||
"localhost",
|
||||
"--port",
|
||||
"8432",
|
||||
"--username-base",
|
||||
"bot",
|
||||
"--password",
|
||||
"password",
|
||||
"--log-level",
|
||||
"INFO"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Multi Bot Launcher (Existing Game)",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bot_client/multi_bot_launcher.py",
|
||||
"args": [
|
||||
"--hostname",
|
||||
"localhost",
|
||||
"--port",
|
||||
"8432",
|
||||
"--username-base",
|
||||
"bot",
|
||||
"--password",
|
||||
"password",
|
||||
"--game-id",
|
||||
"${input:gameId}",
|
||||
"--log-level",
|
||||
"INFO"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Single Bot Player",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bot_client/single_bot_player.py",
|
||||
"args": [
|
||||
"--hostname",
|
||||
"localhost",
|
||||
"--port",
|
||||
"8432",
|
||||
"--username",
|
||||
"bot_${input:powerName}",
|
||||
"--password",
|
||||
"password",
|
||||
"--power",
|
||||
"${input:powerName}",
|
||||
"--model",
|
||||
"${input:modelName}",
|
||||
"--log-level",
|
||||
"INFO"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Single Bot Player (Existing Game)",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bot_client/single_bot_player.py",
|
||||
"args": [
|
||||
"--hostname",
|
||||
"localhost",
|
||||
"--port",
|
||||
"8432",
|
||||
"--username",
|
||||
"bot_${input:powerName}",
|
||||
"--password",
|
||||
"password",
|
||||
"--power",
|
||||
"${input:powerName}",
|
||||
"--model",
|
||||
"${input:modelName}",
|
||||
"--game-id",
|
||||
"${input:gameId}",
|
||||
"--log-level",
|
||||
"INFO"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "gameId",
|
||||
"type": "promptString",
|
||||
"description": "Enter the game ID to join"
|
||||
},
|
||||
{
|
||||
"id": "powerName",
|
||||
"type": "pickString",
|
||||
"description": "Select the power to control",
|
||||
"options": [
|
||||
"AUSTRIA",
|
||||
"ENGLAND",
|
||||
"FRANCE",
|
||||
"GERMANY",
|
||||
"ITALY",
|
||||
"RUSSIA",
|
||||
"TURKEY"
|
||||
],
|
||||
"default": "FRANCE"
|
||||
},
|
||||
{
|
||||
"id": "modelName",
|
||||
"type": "pickString",
|
||||
"description": "Select the AI model to use",
|
||||
"options": [
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4",
|
||||
"claude-3-haiku",
|
||||
"claude-3-sonnet",
|
||||
"gemini-pro"
|
||||
],
|
||||
"default": "gpt-3.5-turbo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
5
bot_client/config.py
Normal file
5
bot_client/config.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
class Configuration(BaseSettings):
|
||||
DEBUG: bool = False
|
||||
|
|
@ -14,7 +14,6 @@ import os
|
|||
import json
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
import concurrent.futures
|
||||
|
||||
# Suppress Gemini/PaLM gRPC warnings
|
||||
os.environ["GRPC_PYTHON_LOG_LEVEL"] = "40"
|
||||
|
|
@ -23,9 +22,15 @@ os.environ["ABSL_MIN_LOG_LEVEL"] = "2"
|
|||
os.environ["GRPC_POLL_STRATEGY"] = "poll"
|
||||
|
||||
# Import our WebSocket client instead of direct Game import
|
||||
from websocket_diplomacy_client import WebSocketDiplomacyClient, connect_to_diplomacy_server
|
||||
from diplomacy.engine.message import GLOBAL, Message
|
||||
from diplomacy.utils.export import to_saved_game_format
|
||||
from websocket_diplomacy_client import (
|
||||
WebSocketDiplomacyClient,
|
||||
connect_to_diplomacy_server,
|
||||
)
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
from ai_diplomacy.clients import load_model_client
|
||||
from ai_diplomacy.utils import (
|
||||
|
|
@ -37,17 +42,11 @@ from ai_diplomacy.negotiations import conduct_negotiations
|
|||
from ai_diplomacy.planning import planning_phase
|
||||
from ai_diplomacy.game_history import GameHistory
|
||||
from ai_diplomacy.agent import DiplomacyAgent
|
||||
import ai_diplomacy.narrative
|
||||
from ai_diplomacy.initialization import initialize_agent_state_ext
|
||||
from loguru import logger
|
||||
|
||||
dotenv.load_dotenv()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
|
||||
datefmt="%H:%M:%S",
|
||||
)
|
||||
# Silence noisy dependencies
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
logging.getLogger("root").setLevel(logging.WARNING)
|
||||
|
|
@ -127,7 +126,9 @@ def parse_arguments():
|
|||
return parser.parse_args()
|
||||
|
||||
|
||||
async def join_powers_for_testing(client: WebSocketDiplomacyClient, power_model_map: dict):
|
||||
async def join_powers_for_testing(
|
||||
client: WebSocketDiplomacyClient, power_model_map: dict
|
||||
):
|
||||
"""
|
||||
Join multiple powers in the same game for testing purposes.
|
||||
This simulates having multiple AI players in one game.
|
||||
|
|
@ -139,15 +140,16 @@ async def join_powers_for_testing(client: WebSocketDiplomacyClient, power_model_
|
|||
try:
|
||||
logger.info(f"Attempting to join power {power_name}")
|
||||
await client.channel.join_game(
|
||||
game_id=client.game_id,
|
||||
power_name=power_name
|
||||
game_id=client.game_id, power_name=power_name
|
||||
)
|
||||
logger.info(f"Successfully joined {power_name}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not join {power_name}: {e}")
|
||||
|
||||
|
||||
async def create_or_join_game(client: WebSocketDiplomacyClient, args, power_model_map: dict):
|
||||
async def create_or_join_game(
|
||||
client: WebSocketDiplomacyClient, args, power_model_map: dict
|
||||
):
|
||||
"""
|
||||
Create a new game or join an existing one based on arguments.
|
||||
"""
|
||||
|
|
@ -158,16 +160,15 @@ async def create_or_join_game(client: WebSocketDiplomacyClient, args, power_mode
|
|||
# List available games first to see what's available
|
||||
try:
|
||||
games = await client.list_games()
|
||||
logger.info(f"Available games: {[g.get('game_id', 'unknown') for g in games]}")
|
||||
logger.info(
|
||||
f"Available games: {[g.get('game_id', 'unknown') for g in games]}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not list games: {e}")
|
||||
|
||||
# For testing, we'll join as the first power in our model map
|
||||
first_power = list(power_model_map.keys())[0]
|
||||
game = await client.join_game(
|
||||
game_id=args.game_id,
|
||||
power_name=first_power
|
||||
)
|
||||
game = await client.join_game(game_id=args.game_id, power_name=first_power)
|
||||
|
||||
if args.create_multi_power_game:
|
||||
await join_powers_for_testing(client, power_model_map)
|
||||
|
|
@ -177,14 +178,20 @@ async def create_or_join_game(client: WebSocketDiplomacyClient, args, power_mode
|
|||
logger.info("Creating new game")
|
||||
|
||||
# Get the first power to control
|
||||
first_power = list(power_model_map.keys())[0] if not args.create_multi_power_game else None
|
||||
first_power = (
|
||||
list(power_model_map.keys())[0]
|
||||
if not args.create_multi_power_game
|
||||
else None
|
||||
)
|
||||
|
||||
game = await client.create_game(
|
||||
map_name="standard",
|
||||
rules=["NO_PRESS", "IGNORE_ERRORS", "POWER_CHOICE"],
|
||||
power_name=first_power,
|
||||
n_controls=7 if not args.create_multi_power_game else 1, # Lower requirement for testing
|
||||
deadline=None # No time pressure for AI testing
|
||||
n_controls=7
|
||||
if not args.create_multi_power_game
|
||||
else 1, # Lower requirement for testing
|
||||
deadline=None, # No time pressure for AI testing
|
||||
)
|
||||
|
||||
if args.create_multi_power_game:
|
||||
|
|
@ -223,10 +230,10 @@ async def main():
|
|||
|
||||
# Setup general file logging
|
||||
general_log_file_path = os.path.join(result_folder, "general_game.log")
|
||||
file_handler = logging.FileHandler(general_log_file_path, mode='a')
|
||||
file_handler = logging.FileHandler(general_log_file_path, mode="a")
|
||||
file_formatter = logging.Formatter(
|
||||
"%(asctime)s - %(levelname)s - %(name)s - [%(funcName)s:%(lineno)d] - %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S"
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
file_handler.setLevel(logging.INFO)
|
||||
|
|
@ -236,13 +243,23 @@ async def main():
|
|||
|
||||
# File paths
|
||||
manifesto_path = f"{result_folder}/game_manifesto.txt"
|
||||
game_file_path = args.output if args.output else f"{result_folder}/lmvsgame_websocket.json"
|
||||
game_file_path = (
|
||||
args.output if args.output else f"{result_folder}/lmvsgame_websocket.json"
|
||||
)
|
||||
overview_file_path = f"{result_folder}/overview.jsonl"
|
||||
llm_log_file_path = f"{result_folder}/llm_responses.csv"
|
||||
|
||||
# Handle power model mapping
|
||||
if args.models:
|
||||
powers_order = ["AUSTRIA", "ENGLAND", "FRANCE", "GERMANY", "ITALY", "RUSSIA", "TURKEY"]
|
||||
powers_order = [
|
||||
"AUSTRIA",
|
||||
"ENGLAND",
|
||||
"FRANCE",
|
||||
"GERMANY",
|
||||
"ITALY",
|
||||
"RUSSIA",
|
||||
"TURKEY",
|
||||
]
|
||||
provided_models = [name.strip() for name in args.models.split(",")]
|
||||
if len(provided_models) != len(powers_order):
|
||||
logger.error(
|
||||
|
|
@ -260,7 +277,7 @@ async def main():
|
|||
hostname=args.hostname,
|
||||
port=args.port,
|
||||
username=args.username,
|
||||
password=args.password
|
||||
password=args.password,
|
||||
)
|
||||
|
||||
# Create or join game
|
||||
|
|
@ -294,28 +311,46 @@ async def main():
|
|||
client_obj = load_model_client(model_id)
|
||||
agent = DiplomacyAgent(power_name=power_name, client=client_obj)
|
||||
agents[power_name] = agent
|
||||
logger.info(f"Preparing initialization task for {power_name} with model {model_id}")
|
||||
logger.info(
|
||||
f"Preparing initialization task for {power_name} with model {model_id}"
|
||||
)
|
||||
initialization_tasks.append(
|
||||
initialize_agent_state_ext(agent, client.game, game_history, llm_log_file_path)
|
||||
initialize_agent_state_ext(
|
||||
agent, client.game, game_history, llm_log_file_path
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create agent for {power_name} with model {model_id}: {e}", exc_info=True)
|
||||
logger.error(
|
||||
f"Failed to create agent for {power_name} with model {model_id}: {e}",
|
||||
exc_info=True,
|
||||
)
|
||||
else:
|
||||
logger.info(f"Skipping agent initialization for {power_name} (no model or eliminated)")
|
||||
logger.info(
|
||||
f"Skipping agent initialization for {power_name} (no model or eliminated)"
|
||||
)
|
||||
|
||||
# Run initializations concurrently
|
||||
if initialization_tasks:
|
||||
logger.info(f"Running {len(initialization_tasks)} agent initializations concurrently...")
|
||||
initialization_results = await asyncio.gather(*initialization_tasks, return_exceptions=True)
|
||||
logger.info(
|
||||
f"Running {len(initialization_tasks)} agent initializations concurrently..."
|
||||
)
|
||||
initialization_results = await asyncio.gather(
|
||||
*initialization_tasks, return_exceptions=True
|
||||
)
|
||||
|
||||
initialized_powers = list(agents.keys())
|
||||
for i, result in enumerate(initialization_results):
|
||||
if i < len(initialized_powers):
|
||||
power_name = initialized_powers[i]
|
||||
if isinstance(result, Exception):
|
||||
logger.error(f"Failed to initialize agent state for {power_name}: {result}", exc_info=result)
|
||||
logger.error(
|
||||
f"Failed to initialize agent state for {power_name}: {result}",
|
||||
exc_info=result,
|
||||
)
|
||||
else:
|
||||
logger.info(f"Successfully initialized agent state for {power_name}.")
|
||||
logger.info(
|
||||
f"Successfully initialized agent state for {power_name}."
|
||||
)
|
||||
|
||||
# Main game loop
|
||||
all_phase_relationships = {}
|
||||
|
|
@ -332,7 +367,9 @@ async def main():
|
|||
game_history.add_phase(current_phase)
|
||||
current_short_phase = client.get_current_short_phase()
|
||||
|
||||
logger.info(f"PHASE: {current_phase} (time so far: {phase_start - start_whole:.2f}s)")
|
||||
logger.info(
|
||||
f"PHASE: {current_phase} (time so far: {phase_start - start_whole:.2f}s)"
|
||||
)
|
||||
|
||||
# Prevent unbounded simulation based on year
|
||||
year_str = current_phase[1:5]
|
||||
|
|
@ -344,7 +381,9 @@ async def main():
|
|||
# Negotiations for movement phases
|
||||
if client.get_current_short_phase().endswith("M"):
|
||||
if args.num_negotiation_rounds > 0:
|
||||
logger.info(f"Running {args.num_negotiation_rounds} rounds of negotiations...")
|
||||
logger.info(
|
||||
f"Running {args.num_negotiation_rounds} rounds of negotiations..."
|
||||
)
|
||||
game_history = await conduct_negotiations(
|
||||
client.game, # Pass the NetworkGame object
|
||||
agents,
|
||||
|
|
@ -354,7 +393,9 @@ async def main():
|
|||
log_file_path=llm_log_file_path,
|
||||
)
|
||||
else:
|
||||
logger.info("Skipping negotiation phase as num_negotiation_rounds=0")
|
||||
logger.info(
|
||||
"Skipping negotiation phase as num_negotiation_rounds=0"
|
||||
)
|
||||
|
||||
# Planning phase (if enabled)
|
||||
if args.planning_phase:
|
||||
|
|
@ -368,17 +409,19 @@ async def main():
|
|||
)
|
||||
|
||||
# Generate negotiation diary entries
|
||||
logger.info(f"Generating negotiation diary entries for phase {current_short_phase}...")
|
||||
active_powers_for_neg_diary = [p for p in agents.keys() if not client.get_power(p).is_eliminated()]
|
||||
logger.info(
|
||||
f"Generating negotiation diary entries for phase {current_short_phase}..."
|
||||
)
|
||||
active_powers_for_neg_diary = [
|
||||
p for p in agents.keys() if not client.get_power(p).is_eliminated()
|
||||
]
|
||||
|
||||
neg_diary_tasks = []
|
||||
for power_name, agent in agents.items():
|
||||
if not client.get_power(power_name).is_eliminated():
|
||||
neg_diary_tasks.append(
|
||||
agent.generate_negotiation_diary_entry(
|
||||
client.game,
|
||||
game_history,
|
||||
llm_log_file_path
|
||||
client.game, game_history, llm_log_file_path
|
||||
)
|
||||
)
|
||||
if neg_diary_tasks:
|
||||
|
|
@ -386,7 +429,9 @@ async def main():
|
|||
|
||||
# AI Decision Making: Get orders for each controlled power
|
||||
logger.info("Getting orders from agents...")
|
||||
active_powers_for_orders = [p for p in agents.keys() if not client.get_power(p).is_eliminated()]
|
||||
active_powers_for_orders = [
|
||||
p for p in agents.keys() if not client.get_power(p).is_eliminated()
|
||||
]
|
||||
|
||||
order_tasks = []
|
||||
order_power_names = []
|
||||
|
|
@ -394,23 +439,35 @@ async def main():
|
|||
|
||||
for power_name, agent in agents.items():
|
||||
if client.get_power(power_name).is_eliminated():
|
||||
logger.debug(f"Skipping order generation for eliminated power {power_name}.")
|
||||
logger.debug(
|
||||
f"Skipping order generation for eliminated power {power_name}."
|
||||
)
|
||||
continue
|
||||
|
||||
# Diagnostic logging
|
||||
logger.info(f"--- Diagnostic Log for {power_name} in phase {current_phase} ---")
|
||||
logger.info(
|
||||
f"--- Diagnostic Log for {power_name} in phase {current_phase} ---"
|
||||
)
|
||||
try:
|
||||
orderable_locs = client.get_orderable_locations(power_name)
|
||||
logger.info(f"[{power_name}][{current_phase}] Orderable locations: {orderable_locs}")
|
||||
logger.info(
|
||||
f"[{power_name}][{current_phase}] Orderable locations: {orderable_locs}"
|
||||
)
|
||||
actual_units = client.get_units(power_name)
|
||||
logger.info(f"[{power_name}][{current_phase}] Actual units: {actual_units}")
|
||||
logger.info(
|
||||
f"[{power_name}][{current_phase}] Actual units: {actual_units}"
|
||||
)
|
||||
except Exception as e_diag:
|
||||
logger.error(f"[{power_name}][{current_phase}] Error during diagnostic logging: {e_diag}")
|
||||
logger.error(
|
||||
f"[{power_name}][{current_phase}] Error during diagnostic logging: {e_diag}"
|
||||
)
|
||||
|
||||
# Calculate possible orders
|
||||
possible_orders = gather_possible_orders(client.game, power_name)
|
||||
if not possible_orders:
|
||||
logger.debug(f"No orderable locations for {power_name}; submitting empty orders.")
|
||||
logger.debug(
|
||||
f"No orderable locations for {power_name}; submitting empty orders."
|
||||
)
|
||||
await client.set_orders(power_name, [])
|
||||
continue
|
||||
|
||||
|
|
@ -436,8 +493,12 @@ async def main():
|
|||
|
||||
# Run order generation concurrently
|
||||
if order_tasks:
|
||||
logger.debug(f"Running {len(order_tasks)} order generation tasks concurrently...")
|
||||
order_results = await asyncio.gather(*order_tasks, return_exceptions=True)
|
||||
logger.debug(
|
||||
f"Running {len(order_tasks)} order generation tasks concurrently..."
|
||||
)
|
||||
order_results = await asyncio.gather(
|
||||
*order_tasks, return_exceptions=True
|
||||
)
|
||||
else:
|
||||
order_results = []
|
||||
|
||||
|
|
@ -447,27 +508,35 @@ async def main():
|
|||
agent = agents[p_name]
|
||||
|
||||
if isinstance(result, Exception):
|
||||
logger.error(f"Error during get_valid_orders for {p_name}: {result}", exc_info=result)
|
||||
logger.error(
|
||||
f"Error during get_valid_orders for {p_name}: {result}",
|
||||
exc_info=result,
|
||||
)
|
||||
await client.set_orders(p_name, [])
|
||||
elif result is None:
|
||||
logger.warning(f"get_valid_orders returned None for {p_name}. Setting empty orders.")
|
||||
logger.warning(
|
||||
f"get_valid_orders returned None for {p_name}. Setting empty orders."
|
||||
)
|
||||
await client.set_orders(p_name, [])
|
||||
else:
|
||||
orders = result
|
||||
logger.debug(f"Validated orders for {p_name}: {orders}")
|
||||
if orders:
|
||||
await client.set_orders(p_name, orders)
|
||||
logger.debug(f"Set orders for {p_name} in {current_short_phase}: {orders}")
|
||||
logger.debug(
|
||||
f"Set orders for {p_name} in {current_short_phase}: {orders}"
|
||||
)
|
||||
|
||||
# Generate order diary entry
|
||||
try:
|
||||
await agent.generate_order_diary_entry(
|
||||
client.game,
|
||||
orders,
|
||||
llm_log_file_path
|
||||
client.game, orders, llm_log_file_path
|
||||
)
|
||||
except Exception as e_diary:
|
||||
logger.error(f"Error generating order diary for {p_name}: {e_diary}", exc_info=True)
|
||||
logger.error(
|
||||
f"Error generating order diary for {p_name}: {e_diary}",
|
||||
exc_info=True,
|
||||
)
|
||||
else:
|
||||
await client.set_orders(p_name, [])
|
||||
|
||||
|
|
@ -494,13 +563,22 @@ async def main():
|
|||
# Collect relationships for this phase
|
||||
current_relationships_for_phase = {}
|
||||
for power_name, agent in agents.items():
|
||||
if power_name in client.powers and not client.get_power(power_name).is_eliminated():
|
||||
if (
|
||||
power_name in client.powers
|
||||
and not client.get_power(power_name).is_eliminated()
|
||||
):
|
||||
current_relationships_for_phase[power_name] = agent.relationships
|
||||
all_phase_relationships[current_short_phase] = current_relationships_for_phase
|
||||
all_phase_relationships[current_short_phase] = (
|
||||
current_relationships_for_phase
|
||||
)
|
||||
|
||||
# Generate phase result diary entries
|
||||
logger.info(f"Generating phase result diary entries for completed phase {current_phase}...")
|
||||
phase_summary = getattr(client.game, 'phase_summaries', {}).get(current_phase, "(Summary not generated)")
|
||||
logger.info(
|
||||
f"Generating phase result diary entries for completed phase {current_phase}..."
|
||||
)
|
||||
phase_summary = getattr(client.game, "phase_summaries", {}).get(
|
||||
current_phase, "(Summary not generated)"
|
||||
)
|
||||
all_orders_this_phase = current_order_history
|
||||
|
||||
phase_result_diary_tasks = []
|
||||
|
|
@ -512,7 +590,7 @@ async def main():
|
|||
game_history,
|
||||
phase_summary,
|
||||
all_orders_this_phase,
|
||||
llm_log_file_path
|
||||
llm_log_file_path,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -520,11 +598,16 @@ async def main():
|
|||
await asyncio.gather(*phase_result_diary_tasks, return_exceptions=True)
|
||||
|
||||
# State update analysis
|
||||
logger.info(f"Starting state update analysis for completed phase {current_phase}...")
|
||||
logger.info(
|
||||
f"Starting state update analysis for completed phase {current_phase}..."
|
||||
)
|
||||
current_board_state = client.get_state()
|
||||
|
||||
active_agent_powers = [(p, power) for p, power in client.powers.items()
|
||||
if p in agents and not power.is_eliminated()]
|
||||
active_agent_powers = [
|
||||
(p, power)
|
||||
for p, power in client.powers.items()
|
||||
if p in agents and not power.is_eliminated()
|
||||
]
|
||||
|
||||
if active_agent_powers:
|
||||
state_update_tasks = []
|
||||
|
|
@ -563,29 +646,29 @@ async def main():
|
|||
# Create a simplified saved game format
|
||||
# Note: The NetworkGame may not have all the same export capabilities as a local Game
|
||||
saved_game = {
|
||||
'game_id': client.game_id,
|
||||
'map_name': 'standard',
|
||||
'rules': ['NO_PRESS', 'IGNORE_ERRORS', 'POWER_CHOICE'],
|
||||
'phases': [],
|
||||
'powers': {},
|
||||
'messages': {},
|
||||
'phase_summaries': getattr(client.game, 'phase_summaries', {}),
|
||||
'final_agent_states': {}
|
||||
"game_id": client.game_id,
|
||||
"map_name": "standard",
|
||||
"rules": ["NO_PRESS", "IGNORE_ERRORS", "POWER_CHOICE"],
|
||||
"phases": [],
|
||||
"powers": {},
|
||||
"messages": {},
|
||||
"phase_summaries": getattr(client.game, "phase_summaries", {}),
|
||||
"final_agent_states": {},
|
||||
}
|
||||
|
||||
# Add final agent states
|
||||
for power_name, agent in agents.items():
|
||||
saved_game['final_agent_states'][power_name] = {
|
||||
saved_game["final_agent_states"][power_name] = {
|
||||
"relationships": agent.relationships,
|
||||
"goals": agent.goals,
|
||||
}
|
||||
|
||||
# Add power information
|
||||
for power_name, power in client.powers.items():
|
||||
saved_game['powers'][power_name] = {
|
||||
'centers': list(power.centers),
|
||||
'units': list(power.units),
|
||||
'is_eliminated': power.is_eliminated()
|
||||
saved_game["powers"][power_name] = {
|
||||
"centers": list(power.centers),
|
||||
"units": list(power.units),
|
||||
"is_eliminated": power.is_eliminated(),
|
||||
}
|
||||
|
||||
logger.info(f"Saving game to {output_path}...")
|
||||
|
|
@ -604,10 +687,11 @@ async def main():
|
|||
logger.error(f"Error during game execution: {e}", exc_info=True)
|
||||
finally:
|
||||
# Clean up connection
|
||||
if 'client' in locals():
|
||||
if "client" in locals():
|
||||
await client.close()
|
||||
logger.info("Done.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
|
|
|
|||
|
|
@ -8,17 +8,20 @@ to an existing game.
|
|||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
from loguru import logger
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
from websocket_diplomacy_client import connect_to_diplomacy_server
|
||||
# Add parent directory to path for ai_diplomacy imports (runtime only)
|
||||
import sys
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
from websocket_diplomacy_client import connect_to_diplomacy_server
|
||||
from diplomacy.engine.game import Game
|
||||
|
||||
|
||||
class MultiBotLauncher:
|
||||
|
|
@ -30,12 +33,14 @@ class MultiBotLauncher:
|
|||
2. Launch bots to join an existing game
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
def __init__(
|
||||
self,
|
||||
hostname: str = "localhost",
|
||||
port: int = 8432,
|
||||
base_username: str = "bot",
|
||||
password: str = "password"):
|
||||
|
||||
password: str = "password",
|
||||
):
|
||||
self.game: Game
|
||||
self.hostname = hostname
|
||||
self.port = port
|
||||
self.base_username = base_username
|
||||
|
|
@ -51,7 +56,7 @@ class MultiBotLauncher:
|
|||
"GERMANY": "gpt-3.5-turbo",
|
||||
"ITALY": "gemini-pro",
|
||||
"RUSSIA": "gpt-4",
|
||||
"TURKEY": "claude-3-sonnet"
|
||||
"TURKEY": "claude-3-sonnet",
|
||||
}
|
||||
|
||||
async def create_game(self, creator_power: str = "FRANCE") -> str:
|
||||
|
|
@ -72,16 +77,16 @@ class MultiBotLauncher:
|
|||
hostname=self.hostname,
|
||||
port=self.port,
|
||||
username=creator_username,
|
||||
password=self.password
|
||||
password=self.password,
|
||||
)
|
||||
|
||||
# Create the game
|
||||
game = await client.create_game(
|
||||
self.game = await client.create_game(
|
||||
map_name="standard",
|
||||
rules=["IGNORE_ERRORS", "POWER_CHOICE"], # Allow messages and power choice
|
||||
power_name=creator_power,
|
||||
n_controls=7, # Full 7-player game
|
||||
deadline=None # No time pressure
|
||||
deadline=None, # No time pressure
|
||||
)
|
||||
|
||||
game_id = client.game_id
|
||||
|
|
@ -90,14 +95,12 @@ class MultiBotLauncher:
|
|||
# Leave the game so the bot can join properly
|
||||
await client.game.leave()
|
||||
await client.close()
|
||||
|
||||
assert game_id is not None, "game_id cannot be None, failed to create new game."
|
||||
return game_id
|
||||
|
||||
def launch_bot(self,
|
||||
power: str,
|
||||
model: str,
|
||||
game_id: str,
|
||||
log_level: str = "INFO") -> subprocess.Popen:
|
||||
def launch_bot(
|
||||
self, power: str, model: str, game_id: str, log_level: str = "INFO"
|
||||
) -> subprocess.Popen:
|
||||
"""
|
||||
Launch a single bot process.
|
||||
|
||||
|
|
@ -113,15 +116,24 @@ class MultiBotLauncher:
|
|||
username = f"{self.base_username}_{power.lower()}"
|
||||
|
||||
cmd = [
|
||||
sys.executable, "single_bot_player.py",
|
||||
"--hostname", self.hostname,
|
||||
"--port", str(self.port),
|
||||
"--username", username,
|
||||
"--password", self.password,
|
||||
"--power", power,
|
||||
"--model", model,
|
||||
"--game-id", game_id,
|
||||
"--log-level", log_level
|
||||
sys.executable,
|
||||
"single_bot_player.py",
|
||||
"--hostname",
|
||||
self.hostname,
|
||||
"--port",
|
||||
str(self.port),
|
||||
"--username",
|
||||
username,
|
||||
"--password",
|
||||
self.password,
|
||||
"--power",
|
||||
power,
|
||||
"--model",
|
||||
model,
|
||||
"--game-id",
|
||||
game_id,
|
||||
"--log-level",
|
||||
log_level,
|
||||
]
|
||||
|
||||
logger.info(f"Launching bot for {power} with model {model}")
|
||||
|
|
@ -133,17 +145,19 @@ class MultiBotLauncher:
|
|||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True,
|
||||
bufsize=1 # Line buffered
|
||||
bufsize=1, # Line buffered
|
||||
)
|
||||
|
||||
return process
|
||||
|
||||
async def launch_all_bots(self,
|
||||
async def launch_all_bots(
|
||||
self,
|
||||
game_id: str,
|
||||
models: Optional[Dict[str, str]] = None,
|
||||
powers: Optional[List[str]] = None,
|
||||
log_level: str = "INFO",
|
||||
stagger_delay: float = 2.0):
|
||||
stagger_delay: float = 2.0,
|
||||
):
|
||||
"""
|
||||
Launch bots for all specified powers.
|
||||
|
||||
|
|
@ -169,7 +183,9 @@ class MultiBotLauncher:
|
|||
process = self.launch_bot(power, model, game_id, log_level)
|
||||
self.bot_processes.append(process)
|
||||
|
||||
logger.info(f"Launched bot {i+1}/{len(powers)}: {power} (PID: {process.pid})")
|
||||
logger.info(
|
||||
f"Launched bot {i + 1}/{len(powers)}: {power} (PID: {process.pid})"
|
||||
)
|
||||
|
||||
# Stagger the launches to avoid overwhelming the server
|
||||
if i < len(powers) - 1: # Don't delay after the last bot
|
||||
|
|
@ -193,7 +209,7 @@ class MultiBotLauncher:
|
|||
while self.bot_processes:
|
||||
active_processes = []
|
||||
|
||||
for i, process in enumerate(self.bot_processes):
|
||||
for _, process in enumerate(self.bot_processes):
|
||||
if process.poll() is None: # Still running
|
||||
active_processes.append(process)
|
||||
|
||||
|
|
@ -209,13 +225,17 @@ class MultiBotLauncher:
|
|||
else:
|
||||
# Process has ended
|
||||
return_code = process.returncode
|
||||
logger.info(f"Bot process {process.pid} ended with code {return_code}")
|
||||
logger.info(
|
||||
f"Bot process {process.pid} ended with code {return_code}"
|
||||
)
|
||||
|
||||
# Read any remaining output
|
||||
try:
|
||||
remaining_output = process.stdout.read()
|
||||
if remaining_output:
|
||||
print(f"Bot-{process.pid} final output: {remaining_output}")
|
||||
print(
|
||||
f"Bot-{process.pid} final output: {remaining_output}"
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
|
@ -251,10 +271,12 @@ class MultiBotLauncher:
|
|||
self.bot_processes.clear()
|
||||
logger.info("All bots stopped")
|
||||
|
||||
async def run_full_game(self,
|
||||
async def run_full_game(
|
||||
self,
|
||||
models: Optional[Dict[str, str]] = None,
|
||||
log_level: str = "INFO",
|
||||
creator_power: str = "FRANCE"):
|
||||
creator_power: str = "FRANCE",
|
||||
):
|
||||
"""
|
||||
Create a game and launch all bots for a complete game.
|
||||
|
||||
|
|
@ -282,11 +304,13 @@ class MultiBotLauncher:
|
|||
finally:
|
||||
self.stop_all_bots()
|
||||
|
||||
async def join_existing_game(self,
|
||||
async def join_existing_game(
|
||||
self,
|
||||
game_id: str,
|
||||
powers: List[str],
|
||||
models: Optional[Dict[str, str]] = None,
|
||||
log_level: str = "INFO"):
|
||||
log_level: str = "INFO",
|
||||
):
|
||||
"""
|
||||
Launch bots to join an existing game.
|
||||
|
||||
|
|
@ -319,11 +343,19 @@ def parse_arguments():
|
|||
parser.add_argument("--port", type=int, default=8432, help="Server port")
|
||||
parser.add_argument("--username-base", default="bot", help="Base username for bots")
|
||||
parser.add_argument("--password", default="password", help="Password for all bots")
|
||||
parser.add_argument("--game-id", help="Game ID to join (creates new if not specified)")
|
||||
parser.add_argument("--powers", nargs="+", help="Powers to launch bots for (default: all)")
|
||||
parser.add_argument("--models", help="Comma-separated list of models in power order")
|
||||
parser.add_argument(
|
||||
"--game-id", help="Game ID to join (creates new if not specified)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--powers", nargs="+", help="Powers to launch bots for (default: all)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--models", help="Comma-separated list of models in power order"
|
||||
)
|
||||
parser.add_argument("--log-level", default="INFO", help="Logging level")
|
||||
parser.add_argument("--creator-power", default="FRANCE", help="Power that creates the game")
|
||||
parser.add_argument(
|
||||
"--creator-power", default="FRANCE", help="Power that creates the game"
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
|
@ -336,7 +368,7 @@ async def main():
|
|||
hostname=args.hostname,
|
||||
port=args.port,
|
||||
base_username=args.username_base,
|
||||
password=args.password
|
||||
password=args.password,
|
||||
)
|
||||
|
||||
# Parse models if provided
|
||||
|
|
@ -345,7 +377,9 @@ async def main():
|
|||
model_list = [m.strip() for m in args.models.split(",")]
|
||||
powers = args.powers or list(launcher.default_models.keys())
|
||||
if len(model_list) != len(powers):
|
||||
logger.error(f"Number of models ({len(model_list)}) must match number of powers ({len(powers)})")
|
||||
logger.error(
|
||||
f"Number of models ({len(model_list)}) must match number of powers ({len(powers)})"
|
||||
)
|
||||
return
|
||||
models = dict(zip(powers, model_list))
|
||||
|
||||
|
|
@ -357,14 +391,14 @@ async def main():
|
|||
game_id=args.game_id,
|
||||
powers=powers,
|
||||
models=models,
|
||||
log_level=args.log_level
|
||||
log_level=args.log_level,
|
||||
)
|
||||
else:
|
||||
# Create new game and launch all bots
|
||||
await launcher.run_full_game(
|
||||
models=models,
|
||||
log_level=args.log_level,
|
||||
creator_power=args.creator_power
|
||||
creator_power=args.creator_power,
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
|
|
|
|||
|
|
@ -5,14 +5,26 @@ description = "Add your description here"
|
|||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"aiohttp>=3.12.13",
|
||||
"anthropic>=0.54.0",
|
||||
"diplomacy",
|
||||
"dotenv>=0.9.9",
|
||||
"google-generativeai>=0.8.5",
|
||||
"json-repair>=0.46.2",
|
||||
"json5>=0.12.0",
|
||||
"loguru>=0.7.3",
|
||||
"openai>=1.87.0",
|
||||
"pydantic>=2.11.7",
|
||||
"pydantic-settings>=2.9.1",
|
||||
"tornado>=6.5.1",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
diplomacy = { path = "../diplomacy", editable = true }
|
||||
|
||||
[tool.pyright]
|
||||
extraPaths = [".."]
|
||||
include = [".", ".."]
|
||||
|
||||
[tool.ruff]
|
||||
src = ["..", "."]
|
||||
|
|
|
|||
|
|
@ -8,12 +8,11 @@ as a separate process for each bot in a multi-player game.
|
|||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import signal
|
||||
from typing import Optional, Dict, Any, List
|
||||
from typing import Optional
|
||||
import dotenv
|
||||
from loguru import logger
|
||||
|
||||
# Suppress warnings
|
||||
os.environ["GRPC_PYTHON_LOG_LEVEL"] = "40"
|
||||
|
|
@ -21,19 +20,32 @@ os.environ["GRPC_VERBOSITY"] = "ERROR"
|
|||
os.environ["ABSL_MIN_LOG_LEVEL"] = "2"
|
||||
os.environ["GRPC_POLL_STRATEGY"] = "poll"
|
||||
|
||||
from websocket_diplomacy_client import connect_to_diplomacy_server
|
||||
from diplomacy.communication import notifications
|
||||
from diplomacy.engine.message import Message, GLOBAL
|
||||
# Add parent directory to path for ai_diplomacy imports (runtime only)
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
from websocket_diplomacy_client import (
|
||||
connect_to_diplomacy_server,
|
||||
WebSocketDiplomacyClient,
|
||||
)
|
||||
from diplomacy.engine.message import Message
|
||||
|
||||
from ai_diplomacy.clients import load_model_client
|
||||
from ai_diplomacy.utils import get_valid_orders, gather_possible_orders
|
||||
from ai_diplomacy.game_history import GameHistory
|
||||
from ai_diplomacy.agent import DiplomacyAgent
|
||||
from ai_diplomacy.initialization import initialize_agent_state_ext
|
||||
from config import Configuration
|
||||
|
||||
dotenv.load_dotenv()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
config = Configuration()
|
||||
|
||||
if config.DEBUG:
|
||||
import tracemalloc
|
||||
|
||||
tracemalloc.start()
|
||||
|
||||
|
||||
class SingleBotPlayer:
|
||||
|
|
@ -46,7 +58,8 @@ class SingleBotPlayer:
|
|||
- When the game phase updates, analyzes the new situation
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
def __init__(
|
||||
self,
|
||||
hostname: str = "localhost",
|
||||
port: int = 8432,
|
||||
username: str = "bot_player",
|
||||
|
|
@ -54,8 +67,7 @@ class SingleBotPlayer:
|
|||
power_name: str = "FRANCE",
|
||||
model_name: str = "gpt-3.5-turbo",
|
||||
game_id: Optional[str] = None,
|
||||
log_level: str = "INFO"):
|
||||
|
||||
):
|
||||
self.hostname = hostname
|
||||
self.port = port
|
||||
self.username = username
|
||||
|
|
@ -64,13 +76,9 @@ class SingleBotPlayer:
|
|||
self.model_name = model_name
|
||||
self.game_id = game_id
|
||||
|
||||
# Setup logging
|
||||
log_format = f"[{self.username}:{self.power_name}] %(asctime)s - %(levelname)s - %(message)s"
|
||||
logging.basicConfig(level=getattr(logging, log_level.upper()), format=log_format)
|
||||
|
||||
# Bot state
|
||||
self.client = None
|
||||
self.agent = None
|
||||
self.client: WebSocketDiplomacyClient
|
||||
self.agent: DiplomacyAgent
|
||||
self.game_history = GameHistory()
|
||||
self.running = True
|
||||
self.current_phase = None
|
||||
|
|
@ -98,15 +106,14 @@ class SingleBotPlayer:
|
|||
hostname=self.hostname,
|
||||
port=self.port,
|
||||
username=self.username,
|
||||
password=self.password
|
||||
password=self.password,
|
||||
)
|
||||
|
||||
# Join or create game
|
||||
if self.game_id:
|
||||
logger.info(f"Joining existing game {self.game_id} as {self.power_name}")
|
||||
game = await self.client.join_game(
|
||||
game_id=self.game_id,
|
||||
power_name=self.power_name
|
||||
game_id=self.game_id, power_name=self.power_name
|
||||
)
|
||||
else:
|
||||
logger.info(f"Creating new game as {self.power_name}")
|
||||
|
|
@ -115,7 +122,7 @@ class SingleBotPlayer:
|
|||
rules=["IGNORE_ERRORS", "POWER_CHOICE"], # Allow messages
|
||||
power_name=self.power_name,
|
||||
n_controls=7, # Full game
|
||||
deadline=None
|
||||
deadline=None,
|
||||
)
|
||||
logger.info(f"Created game {self.client.game_id}")
|
||||
|
||||
|
|
@ -125,7 +132,9 @@ class SingleBotPlayer:
|
|||
self.agent = DiplomacyAgent(power_name=self.power_name, client=model_client)
|
||||
|
||||
# Initialize agent state
|
||||
await initialize_agent_state_ext(self.agent, self.client.game, self.game_history, None)
|
||||
await initialize_agent_state_ext(
|
||||
self.agent, self.client.game, self.game_history, None
|
||||
)
|
||||
|
||||
# Setup game event callbacks
|
||||
self._setup_event_callbacks()
|
||||
|
|
@ -141,7 +150,7 @@ class SingleBotPlayer:
|
|||
# Check if we need to submit orders immediately
|
||||
await self._check_if_orders_needed()
|
||||
|
||||
def _setup_event_callbacks(self):
|
||||
async def _setup_event_callbacks(self):
|
||||
"""Setup callbacks for game events from the server."""
|
||||
|
||||
# Game phase updates (new turn)
|
||||
|
|
@ -154,7 +163,7 @@ class SingleBotPlayer:
|
|||
self.client.game.add_on_game_message_received(self._on_message_received)
|
||||
|
||||
# Game status changes
|
||||
self.client.game.add_on_game_status_update(self._on_status_update)
|
||||
self.client.game.add_on_game_status_update(await self._on_status_update)
|
||||
|
||||
# Power updates (other players joining/leaving)
|
||||
self.client.game.add_on_powers_controllers(self._on_powers_update)
|
||||
|
|
@ -194,14 +203,16 @@ class SingleBotPlayer:
|
|||
async def _on_message_received(self, game, notification):
|
||||
"""Handle incoming diplomatic messages."""
|
||||
message = notification.message
|
||||
logger.info(f"Message received from {message.sender} to {message.recipient}: {message.message}")
|
||||
logger.info(
|
||||
f"Message received from {message.sender} to {message.recipient}: {message.message}"
|
||||
)
|
||||
|
||||
# Add message to game history
|
||||
self.game_history.add_message(
|
||||
phase=message.phase,
|
||||
sender=message.sender,
|
||||
recipient=message.recipient,
|
||||
content=message.message
|
||||
content=message.message,
|
||||
)
|
||||
|
||||
# If it's a private message to us, consider responding
|
||||
|
|
@ -230,16 +241,20 @@ class SingleBotPlayer:
|
|||
current_short_phase = self.client.get_current_short_phase()
|
||||
|
||||
# We submit orders in Movement and Retreat phases
|
||||
if current_short_phase.endswith('M') or current_short_phase.endswith('R'):
|
||||
if current_short_phase.endswith("M") or current_short_phase.endswith("R"):
|
||||
# Check if we have units that can receive orders
|
||||
try:
|
||||
orderable_locations = self.client.get_orderable_locations(self.power_name)
|
||||
orderable_locations = self.client.get_orderable_locations(
|
||||
self.power_name
|
||||
)
|
||||
if orderable_locations:
|
||||
logger.info(f"Orders needed for phase {current_short_phase}")
|
||||
self.waiting_for_orders = True
|
||||
await self._submit_orders()
|
||||
else:
|
||||
logger.info(f"No orderable locations for {self.power_name} in {current_short_phase}")
|
||||
logger.info(
|
||||
f"No orderable locations for {self.power_name} in {current_short_phase}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking orderable locations: {e}")
|
||||
|
||||
|
|
@ -272,11 +287,11 @@ class SingleBotPlayer:
|
|||
power_name=self.power_name,
|
||||
possible_orders=possible_orders,
|
||||
game_history=self.game_history,
|
||||
error_stats=self.error_stats,
|
||||
model_error_stats=self.error_stats,
|
||||
agent_goals=self.agent.goals,
|
||||
agent_relationships=self.agent.relationships,
|
||||
agent_private_diary_str=self.agent.format_private_diary_for_prompt(),
|
||||
phase=self.current_phase
|
||||
phase=self.current_phase,
|
||||
)
|
||||
|
||||
# Submit orders
|
||||
|
|
@ -288,7 +303,7 @@ class SingleBotPlayer:
|
|||
await self.agent.generate_order_diary_entry(
|
||||
self.client.game,
|
||||
orders,
|
||||
None # No log file path
|
||||
None, # No log file path
|
||||
)
|
||||
else:
|
||||
logger.info("No valid orders generated, submitting empty order set")
|
||||
|
|
@ -324,7 +339,7 @@ class SingleBotPlayer:
|
|||
board_state=board_state,
|
||||
phase_summary=phase_summary,
|
||||
game_history=self.game_history,
|
||||
log_file_path=None
|
||||
log_file_path=None,
|
||||
)
|
||||
|
||||
logger.info("Phase analysis complete")
|
||||
|
|
@ -336,12 +351,12 @@ class SingleBotPlayer:
|
|||
"""Consider whether to respond to a diplomatic message."""
|
||||
try:
|
||||
# Simple logic: if someone greets us, greet back
|
||||
if any(word in message.message.lower() for word in ['hello', 'hi', 'greetings']):
|
||||
if any(
|
||||
word in message.message.lower() for word in ["hello", "hi", "greetings"]
|
||||
):
|
||||
response = f"Hello {message.sender}! Good to hear from you."
|
||||
await self.client.send_message(
|
||||
sender=self.power_name,
|
||||
recipient=message.sender,
|
||||
message=response
|
||||
sender=self.power_name, recipient=message.sender, message=response
|
||||
)
|
||||
logger.info(f"Sent response to {message.sender}: {response}")
|
||||
|
||||
|
|
@ -403,7 +418,9 @@ def parse_arguments():
|
|||
parser.add_argument("--password", default="password", help="Bot password")
|
||||
parser.add_argument("--power", default="FRANCE", help="Power to control")
|
||||
parser.add_argument("--model", default="gpt-3.5-turbo", help="AI model to use")
|
||||
parser.add_argument("--game-id", help="Game ID to join (creates new if not specified)")
|
||||
parser.add_argument(
|
||||
"--game-id", help="Game ID to join (creates new if not specified)"
|
||||
)
|
||||
parser.add_argument("--log-level", default="INFO", help="Logging level")
|
||||
|
||||
return parser.parse_args()
|
||||
|
|
@ -421,7 +438,6 @@ async def main():
|
|||
power_name=args.power,
|
||||
model_name=args.model,
|
||||
game_id=args.game_id,
|
||||
log_level=args.log_level
|
||||
)
|
||||
|
||||
await bot.run()
|
||||
|
|
|
|||
571
bot_client/uv.lock
generated
571
bot_client/uv.lock
generated
|
|
@ -2,6 +2,61 @@ version = 1
|
|||
revision = 2
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[[package]]
|
||||
name = "aiohappyeyeballs"
|
||||
version = "2.6.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.12.13"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohappyeyeballs" },
|
||||
{ name = "aiosignal" },
|
||||
{ name = "attrs" },
|
||||
{ name = "frozenlist" },
|
||||
{ name = "multidict" },
|
||||
{ name = "propcache" },
|
||||
{ name = "yarl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/42/6e/ab88e7cb2a4058bed2f7870276454f85a7c56cd6da79349eb314fc7bbcaa/aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce", size = 7819160, upload-time = "2025-06-14T15:15:41.354Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/11/0f/db19abdf2d86aa1deec3c1e0e5ea46a587b97c07a16516b6438428b3a3f8/aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938", size = 694910, upload-time = "2025-06-14T15:14:30.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/81/0ab551e1b5d7f1339e2d6eb482456ccbe9025605b28eed2b1c0203aaaade/aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace", size = 472566, upload-time = "2025-06-14T15:14:32.275Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/3f/6b7d336663337672d29b1f82d1f252ec1a040fe2d548f709d3f90fa2218a/aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb", size = 464856, upload-time = "2025-06-14T15:14:34.132Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/7f/32ca0f170496aa2ab9b812630fac0c2372c531b797e1deb3deb4cea904bd/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7", size = 1703683, upload-time = "2025-06-14T15:14:36.034Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/53/d5513624b33a811c0abea8461e30a732294112318276ce3dbf047dbd9d8b/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b", size = 1684946, upload-time = "2025-06-14T15:14:38Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/72/4c237dd127827b0247dc138d3ebd49c2ded6114c6991bbe969058575f25f/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177", size = 1737017, upload-time = "2025-06-14T15:14:39.951Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/67/8a7eb3afa01e9d0acc26e1ef847c1a9111f8b42b82955fcd9faeb84edeb4/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef", size = 1786390, upload-time = "2025-06-14T15:14:42.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/19/0377df97dd0176ad23cd8cad4fd4232cfeadcec6c1b7f036315305c98e3f/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103", size = 1708719, upload-time = "2025-06-14T15:14:44.039Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/97/ade1982a5c642b45f3622255173e40c3eed289c169f89d00eeac29a89906/aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da", size = 1622424, upload-time = "2025-06-14T15:14:45.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/ab/00ad3eea004e1d07ccc406e44cfe2b8da5acb72f8c66aeeb11a096798868/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d", size = 1675447, upload-time = "2025-06-14T15:14:47.911Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/fe/74e5ce8b2ccaba445fe0087abc201bfd7259431d92ae608f684fcac5d143/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041", size = 1707110, upload-time = "2025-06-14T15:14:50.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/c4/39af17807f694f7a267bd8ab1fbacf16ad66740862192a6c8abac2bff813/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1", size = 1649706, upload-time = "2025-06-14T15:14:52.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/e8/f5a0a5f44f19f171d8477059aa5f28a158d7d57fe1a46c553e231f698435/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1", size = 1725839, upload-time = "2025-06-14T15:14:54.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/ac/81acc594c7f529ef4419d3866913f628cd4fa9cab17f7bf410a5c3c04c53/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911", size = 1759311, upload-time = "2025-06-14T15:14:56.597Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/0d/aabe636bd25c6ab7b18825e5a97d40024da75152bec39aa6ac8b7a677630/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3", size = 1708202, upload-time = "2025-06-14T15:14:58.598Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/ab/561ef2d8a223261683fb95a6283ad0d36cb66c87503f3a7dde7afe208bb2/aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd", size = 420794, upload-time = "2025-06-14T15:15:00.939Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/47/b11d0089875a23bff0abd3edb5516bcd454db3fefab8604f5e4b07bd6210/aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706", size = 446735, upload-time = "2025-06-14T15:15:02.858Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiosignal"
|
||||
version = "1.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "frozenlist" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424, upload-time = "2024-12-13T17:10:40.86Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597, upload-time = "2024-12-13T17:10:38.469Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
|
|
@ -42,6 +97,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "25.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "4.3.0"
|
||||
|
|
@ -97,26 +161,45 @@ name = "bot-client"
|
|||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
{ name = "anthropic" },
|
||||
{ name = "diplomacy" },
|
||||
{ name = "dotenv" },
|
||||
{ name = "google-generativeai" },
|
||||
{ name = "json-repair" },
|
||||
{ name = "json5" },
|
||||
{ name = "loguru" },
|
||||
{ name = "openai" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pydantic-settings" },
|
||||
{ name = "tornado" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "aiohttp", specifier = ">=3.12.13" },
|
||||
{ name = "anthropic", specifier = ">=0.54.0" },
|
||||
{ name = "diplomacy", editable = "../diplomacy" },
|
||||
{ name = "dotenv", specifier = ">=0.9.9" },
|
||||
{ name = "google-generativeai", specifier = ">=0.8.5" },
|
||||
{ name = "json-repair", specifier = ">=0.46.2" },
|
||||
{ name = "json5", specifier = ">=0.12.0" },
|
||||
{ name = "loguru", specifier = ">=0.7.3" },
|
||||
{ name = "openai", specifier = ">=1.87.0" },
|
||||
{ name = "pydantic", specifier = ">=2.11.7" },
|
||||
{ name = "pydantic-settings", specifier = ">=2.9.1" },
|
||||
{ name = "tornado", specifier = ">=6.5.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cachetools"
|
||||
version = "5.5.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.6.15"
|
||||
|
|
@ -126,6 +209,28 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
|
|
@ -192,6 +297,191 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frozenlist"
|
||||
version = "1.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791, upload-time = "2025-06-09T23:01:09.368Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165, upload-time = "2025-06-09T23:01:10.653Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881, upload-time = "2025-06-09T23:01:12.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409, upload-time = "2025-06-09T23:01:13.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132, upload-time = "2025-06-09T23:01:15.264Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638, upload-time = "2025-06-09T23:01:16.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539, upload-time = "2025-06-09T23:01:18.202Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646, upload-time = "2025-06-09T23:01:19.649Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233, upload-time = "2025-06-09T23:01:21.175Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996, upload-time = "2025-06-09T23:01:23.098Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280, upload-time = "2025-06-09T23:01:24.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717, upload-time = "2025-06-09T23:01:26.28Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644, upload-time = "2025-06-09T23:01:27.887Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879, upload-time = "2025-06-09T23:01:29.524Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502, upload-time = "2025-06-09T23:01:31.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169, upload-time = "2025-06-09T23:01:35.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219, upload-time = "2025-06-09T23:01:36.784Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345, upload-time = "2025-06-09T23:01:38.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880, upload-time = "2025-06-09T23:01:39.887Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498, upload-time = "2025-06-09T23:01:41.318Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296, upload-time = "2025-06-09T23:01:42.685Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103, upload-time = "2025-06-09T23:01:44.166Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869, upload-time = "2025-06-09T23:01:45.681Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467, upload-time = "2025-06-09T23:01:47.234Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028, upload-time = "2025-06-09T23:01:48.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294, upload-time = "2025-06-09T23:01:50.394Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898, upload-time = "2025-06-09T23:01:52.234Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465, upload-time = "2025-06-09T23:01:53.788Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385, upload-time = "2025-06-09T23:01:55.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771, upload-time = "2025-06-09T23:01:57.4Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206, upload-time = "2025-06-09T23:01:58.936Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-ai-generativelanguage"
|
||||
version = "0.6.15"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core", extra = ["grpc"] },
|
||||
{ name = "google-auth" },
|
||||
{ name = "proto-plus" },
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/11/d1/48fe5d7a43d278e9f6b5ada810b0a3530bbeac7ed7fcbcd366f932f05316/google_ai_generativelanguage-0.6.15.tar.gz", hash = "sha256:8f6d9dc4c12b065fe2d0289026171acea5183ebf2d0b11cefe12f3821e159ec3", size = 1375443, upload-time = "2025-01-13T21:50:47.459Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/a3/67b8a6ff5001a1d8864922f2d6488dc2a14367ceb651bc3f09a947f2f306/google_ai_generativelanguage-0.6.15-py3-none-any.whl", hash = "sha256:5a03ef86377aa184ffef3662ca28f19eeee158733e45d7947982eb953c6ebb6c", size = 1327356, upload-time = "2025-01-13T21:50:44.174Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-api-core"
|
||||
version = "2.25.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-auth" },
|
||||
{ name = "googleapis-common-protos" },
|
||||
{ name = "proto-plus" },
|
||||
{ name = "protobuf" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/dc/21/e9d043e88222317afdbdb567165fdbc3b0aad90064c7e0c9eb0ad9955ad8/google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8", size = 165443, upload-time = "2025-06-12T20:52:20.439Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/14/4b/ead00905132820b623732b175d66354e9d3e69fcf2a5dcdab780664e7896/google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7", size = 160807, upload-time = "2025-06-12T20:52:19.334Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
grpc = [
|
||||
{ name = "grpcio" },
|
||||
{ name = "grpcio-status" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.172.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core" },
|
||||
{ name = "google-auth" },
|
||||
{ name = "google-auth-httplib2" },
|
||||
{ name = "httplib2" },
|
||||
{ name = "uritemplate" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/02/69/c0cec6be5878d4de161f64096edb3d4a2d1a838f036b8425ea8358d0dfb3/google_api_python_client-2.172.0.tar.gz", hash = "sha256:dcb3b7e067154b2aa41f1776cf86584a5739c0ac74e6ff46fc665790dca0e6a6", size = 13074841, upload-time = "2025-06-10T16:58:41.181Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/15/fc/8850ccf21c5df43faeaf8bba8c4149ee880b41b8dc7066e3259bcfd921ca/google_api_python_client-2.172.0-py3-none-any.whl", hash = "sha256:9f1b9a268d5dc1228207d246c673d3a09ee211b41a11521d38d9212aeaa43af7", size = 13595800, upload-time = "2025-06-10T16:58:38.143Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-auth"
|
||||
version = "2.40.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cachetools" },
|
||||
{ name = "pyasn1-modules" },
|
||||
{ name = "rsa" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-auth-httplib2"
|
||||
version = "0.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-auth" },
|
||||
{ name = "httplib2" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05", size = 10842, upload-time = "2023-12-12T17:40:30.722Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d", size = 9253, upload-time = "2023-12-12T17:40:13.055Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-generativeai"
|
||||
version = "0.8.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-ai-generativelanguage" },
|
||||
{ name = "google-api-core" },
|
||||
{ name = "google-api-python-client" },
|
||||
{ name = "google-auth" },
|
||||
{ name = "protobuf" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/40/c42ff9ded9f09ec9392879a8e6538a00b2dc185e834a3392917626255419/google_generativeai-0.8.5-py3-none-any.whl", hash = "sha256:22b420817fb263f8ed520b33285f45976d5b21e904da32b80d4fd20c055123a2", size = 155427, upload-time = "2025-04-17T00:40:00.67Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "googleapis-common-protos"
|
||||
version = "1.70.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio"
|
||||
version = "1.73.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8e/7b/ca3f561aeecf0c846d15e1b38921a60dffffd5d4113931198fbf455334ee/grpcio-1.73.0.tar.gz", hash = "sha256:3af4c30918a7f0d39de500d11255f8d9da4f30e94a2033e70fe2a720e184bd8e", size = 12786424, upload-time = "2025-06-09T10:08:23.365Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/60/da/6f3f7a78e5455c4cbe87c85063cc6da05d65d25264f9d4aed800ece46294/grpcio-1.73.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:da1d677018ef423202aca6d73a8d3b2cb245699eb7f50eb5f74cae15a8e1f724", size = 5335867, upload-time = "2025-06-09T10:04:03.153Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/14/7d1f2526b98b9658d7be0bb163fd78d681587de6709d8b0c74b4b481b013/grpcio-1.73.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:36bf93f6a657f37c131d9dd2c391b867abf1426a86727c3575393e9e11dadb0d", size = 10595587, upload-time = "2025-06-09T10:04:05.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/24/a293c398ae44e741da1ed4b29638edbb002258797b07a783f65506165b4c/grpcio-1.73.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:d84000367508ade791d90c2bafbd905574b5ced8056397027a77a215d601ba15", size = 5765793, upload-time = "2025-06-09T10:04:09.235Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/24/d84dbd0b5bf36fb44922798d525a85cefa2ffee7b7110e61406e9750ed15/grpcio-1.73.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c98ba1d928a178ce33f3425ff823318040a2b7ef875d30a0073565e5ceb058d9", size = 6415494, upload-time = "2025-06-09T10:04:12.377Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/85/c80dc65aed8e9dce3d54688864bac45331d9c7600985541f18bd5cb301d4/grpcio-1.73.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a73c72922dfd30b396a5f25bb3a4590195ee45ecde7ee068acb0892d2900cf07", size = 6007279, upload-time = "2025-06-09T10:04:14.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/fc/207c00a4c6fa303d26e2cbd62fbdb0582facdfd08f55500fd83bf6b0f8db/grpcio-1.73.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:10e8edc035724aba0346a432060fd192b42bd03675d083c01553cab071a28da5", size = 6105505, upload-time = "2025-06-09T10:04:17.39Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/35/8fe69af820667b87ebfcb24214e42a1d53da53cb39edd6b4f84f6b36da86/grpcio-1.73.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f5cdc332b503c33b1643b12ea933582c7b081957c8bc2ea4cc4bc58054a09288", size = 6753792, upload-time = "2025-06-09T10:04:19.989Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/d8/738c77c1e821e350da4a048849f695ff88a02b291f8c69db23908867aea6/grpcio-1.73.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:07ad7c57233c2109e4ac999cb9c2710c3b8e3f491a73b058b0ce431f31ed8145", size = 6287593, upload-time = "2025-06-09T10:04:22.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/ec/8498eabc018fa39ae8efe5e47e3f4c1bc9ed6281056713871895dc998807/grpcio-1.73.0-cp313-cp313-win32.whl", hash = "sha256:0eb5df4f41ea10bda99a802b2a292d85be28958ede2a50f2beb8c7fc9a738419", size = 3668637, upload-time = "2025-06-09T10:04:25.787Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/35/347db7d2e7674b621afd21b12022e7f48c7b0861b5577134b4e939536141/grpcio-1.73.0-cp313-cp313-win_amd64.whl", hash = "sha256:38cf518cc54cd0c47c9539cefa8888549fcc067db0b0c66a46535ca8032020c4", size = 4335872, upload-time = "2025-06-09T10:04:29.032Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio-status"
|
||||
version = "1.71.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "googleapis-common-protos" },
|
||||
{ name = "grpcio" },
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/53/a911467bece076020456401f55a27415d2d70d3bc2c37af06b44ea41fc5c/grpcio_status-1.71.0.tar.gz", hash = "sha256:11405fed67b68f406b3f3c7c5ae5104a79d2d309666d10d61b152e91d28fb968", size = 13669, upload-time = "2025-03-10T19:29:00.901Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/d6/31fbc43ff097d8c4c9fc3df741431b8018f67bf8dfbe6553a555f6e5f675/grpcio_status-1.71.0-py3-none-any.whl", hash = "sha256:843934ef8c09e3e858952887467f8256aac3910c55f077a359a65b2b3cde3e68", size = 14424, upload-time = "2025-03-10T19:27:04.967Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.16.0"
|
||||
|
|
@ -214,6 +504,18 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httplib2"
|
||||
version = "0.22.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyparsing" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3d/ad/2371116b22d616c194aa25ec410c9c6c37f23599dcd590502b74db197584/httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81", size = 351116, upload-time = "2023-03-21T22:29:37.214Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/6c/d2fbdaaa5959339d53ba38e94c123e4e84b8fbc4b84beb0e70d7c1608486/httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", size = 96854, upload-time = "2023-03-21T22:29:35.683Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.28.1"
|
||||
|
|
@ -304,6 +606,62 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/41/9f/3500910d5a98549e3098807493851eeef2b89cdd3032227558a104dfe926/json5-0.12.0-py3-none-any.whl", hash = "sha256:6d37aa6c08b0609f16e1ec5ff94697e2cbbfbad5ac112afa05794da9ab7810db", size = 36079, upload-time = "2025-04-03T16:33:11.927Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loguru"
|
||||
version = "0.7.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "win32-setctime", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.4.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/91/2f/a3470242707058fe856fe59241eee5635d79087100b7042a867368863a27/multidict-6.4.4.tar.gz", hash = "sha256:69ee9e6ba214b5245031b76233dd95408a0fd57fdb019ddcc1ead4790932a8e8", size = 90183, upload-time = "2025-05-19T14:16:37.381Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/2a/e166d2ffbf4b10131b2d5b0e458f7cee7d986661caceae0de8753042d4b2/multidict-6.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:82ffabefc8d84c2742ad19c37f02cde5ec2a1ee172d19944d380f920a340e4b9", size = 64123, upload-time = "2025-05-19T14:15:11.044Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/96/e200e379ae5b6f95cbae472e0199ea98913f03d8c9a709f42612a432932c/multidict-6.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6a2f58a66fe2c22615ad26156354005391e26a2f3721c3621504cd87c1ea87bf", size = 38049, upload-time = "2025-05-19T14:15:12.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/fb/47afd17b83f6a8c7fa863c6d23ac5ba6a0e6145ed8a6bcc8da20b2b2c1d2/multidict-6.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5883d6ee0fd9d8a48e9174df47540b7545909841ac82354c7ae4cbe9952603bd", size = 37078, upload-time = "2025-05-19T14:15:14.282Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/70/1af3143000eddfb19fd5ca5e78393985ed988ac493bb859800fe0914041f/multidict-6.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9abcf56a9511653fa1d052bfc55fbe53dbee8f34e68bd6a5a038731b0ca42d15", size = 224097, upload-time = "2025-05-19T14:15:15.566Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/39/d570c62b53d4fba844e0378ffbcd02ac25ca423d3235047013ba2f6f60f8/multidict-6.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6ed5ae5605d4ad5a049fad2a28bb7193400700ce2f4ae484ab702d1e3749c3f9", size = 230768, upload-time = "2025-05-19T14:15:17.308Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/f8/ed88f2c4d06f752b015933055eb291d9bc184936903752c66f68fb3c95a7/multidict-6.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbfcb60396f9bcfa63e017a180c3105b8c123a63e9d1428a36544e7d37ca9e20", size = 231331, upload-time = "2025-05-19T14:15:18.73Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/6f/8e07cffa32f483ab887b0d56bbd8747ac2c1acd00dc0af6fcf265f4a121e/multidict-6.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0f1987787f5f1e2076b59692352ab29a955b09ccc433c1f6b8e8e18666f608b", size = 230169, upload-time = "2025-05-19T14:15:20.179Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/2b/5dcf173be15e42f330110875a2668ddfc208afc4229097312212dc9c1236/multidict-6.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0121ccce8c812047d8d43d691a1ad7641f72c4f730474878a5aeae1b8ead8c", size = 222947, upload-time = "2025-05-19T14:15:21.714Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/75/4ddcbcebe5ebcd6faa770b629260d15840a5fc07ce8ad295a32e14993726/multidict-6.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83ec4967114295b8afd120a8eec579920c882831a3e4c3331d591a8e5bfbbc0f", size = 215761, upload-time = "2025-05-19T14:15:23.242Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/c9/55e998ae45ff15c5608e384206aa71a11e1b7f48b64d166db400b14a3433/multidict-6.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:995f985e2e268deaf17867801b859a282e0448633f1310e3704b30616d269d69", size = 227605, upload-time = "2025-05-19T14:15:24.763Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/49/c2404eac74497503c77071bd2e6f88c7e94092b8a07601536b8dbe99be50/multidict-6.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d832c608f94b9f92a0ec8b7e949be7792a642b6e535fcf32f3e28fab69eeb046", size = 226144, upload-time = "2025-05-19T14:15:26.249Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/c5/0cd0c3c6f18864c40846aa2252cd69d308699cb163e1c0d989ca301684da/multidict-6.4.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d21c1212171cf7da703c5b0b7a0e85be23b720818aef502ad187d627316d5645", size = 221100, upload-time = "2025-05-19T14:15:28.303Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/7b/f2f3887bea71739a046d601ef10e689528d4f911d84da873b6be9194ffea/multidict-6.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cbebaa076aaecad3d4bb4c008ecc73b09274c952cf6a1b78ccfd689e51f5a5b0", size = 232731, upload-time = "2025-05-19T14:15:30.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/b3/d9de808349df97fa75ec1372758701b5800ebad3c46ae377ad63058fbcc6/multidict-6.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c93a6fb06cc8e5d3628b2b5fda215a5db01e8f08fc15fadd65662d9b857acbe4", size = 229637, upload-time = "2025-05-19T14:15:33.337Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/57/13207c16b615eb4f1745b44806a96026ef8e1b694008a58226c2d8f5f0a5/multidict-6.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8cd8f81f1310182362fb0c7898145ea9c9b08a71081c5963b40ee3e3cac589b1", size = 225594, upload-time = "2025-05-19T14:15:34.832Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/e4/d23bec2f70221604f5565000632c305fc8f25ba953e8ce2d8a18842b9841/multidict-6.4.4-cp313-cp313-win32.whl", hash = "sha256:3e9f1cd61a0ab857154205fb0b1f3d3ace88d27ebd1409ab7af5096e409614cd", size = 35359, upload-time = "2025-05-19T14:15:36.246Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/7a/cfe1a47632be861b627f46f642c1d031704cc1c0f5c0efbde2ad44aa34bd/multidict-6.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:8ffb40b74400e4455785c2fa37eba434269149ec525fc8329858c862e4b35373", size = 38903, upload-time = "2025-05-19T14:15:37.507Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/7b/15c259b0ab49938a0a1c8f3188572802704a779ddb294edc1b2a72252e7c/multidict-6.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6a602151dbf177be2450ef38966f4be3467d41a86c6a845070d12e17c858a156", size = 68895, upload-time = "2025-05-19T14:15:38.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/7d/168b5b822bccd88142e0a3ce985858fea612404edd228698f5af691020c9/multidict-6.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d2b9712211b860d123815a80b859075d86a4d54787e247d7fbee9db6832cf1c", size = 40183, upload-time = "2025-05-19T14:15:40.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/b7/d4b8d98eb850ef28a4922ba508c31d90715fd9b9da3801a30cea2967130b/multidict-6.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d2fa86af59f8fc1972e121ade052145f6da22758f6996a197d69bb52f8204e7e", size = 39592, upload-time = "2025-05-19T14:15:41.508Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/28/a554678898a19583548e742080cf55d169733baf57efc48c2f0273a08583/multidict-6.4.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50855d03e9e4d66eab6947ba688ffb714616f985838077bc4b490e769e48da51", size = 226071, upload-time = "2025-05-19T14:15:42.877Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/dc/7ba6c789d05c310e294f85329efac1bf5b450338d2542498db1491a264df/multidict-6.4.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5bce06b83be23225be1905dcdb6b789064fae92499fbc458f59a8c0e68718601", size = 222597, upload-time = "2025-05-19T14:15:44.412Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/4f/34eadbbf401b03768dba439be0fb94b0d187facae9142821a3d5599ccb3b/multidict-6.4.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66ed0731f8e5dfd8369a883b6e564aca085fb9289aacabd9decd70568b9a30de", size = 228253, upload-time = "2025-05-19T14:15:46.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/e6/493225a3cdb0d8d80d43a94503fc313536a07dae54a3f030d279e629a2bc/multidict-6.4.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:329ae97fc2f56f44d91bc47fe0972b1f52d21c4b7a2ac97040da02577e2daca2", size = 226146, upload-time = "2025-05-19T14:15:48.003Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/70/e411a7254dc3bff6f7e6e004303b1b0591358e9f0b7c08639941e0de8bd6/multidict-6.4.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c27e5dcf520923d6474d98b96749e6805f7677e93aaaf62656005b8643f907ab", size = 220585, upload-time = "2025-05-19T14:15:49.546Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/8f/beb3ae7406a619100d2b1fb0022c3bb55a8225ab53c5663648ba50dfcd56/multidict-6.4.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:058cc59b9e9b143cc56715e59e22941a5d868c322242278d28123a5d09cdf6b0", size = 212080, upload-time = "2025-05-19T14:15:51.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/ec/355124e9d3d01cf8edb072fd14947220f357e1c5bc79c88dff89297e9342/multidict-6.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:69133376bc9a03f8c47343d33f91f74a99c339e8b58cea90433d8e24bb298031", size = 226558, upload-time = "2025-05-19T14:15:52.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/22/d2b95cbebbc2ada3be3812ea9287dcc9712d7f1a012fad041770afddb2ad/multidict-6.4.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d6b15c55721b1b115c5ba178c77104123745b1417527ad9641a4c5e2047450f0", size = 212168, upload-time = "2025-05-19T14:15:55.279Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/c5/62bfc0b2f9ce88326dbe7179f9824a939c6c7775b23b95de777267b9725c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a887b77f51d3d41e6e1a63cf3bc7ddf24de5939d9ff69441387dfefa58ac2e26", size = 217970, upload-time = "2025-05-19T14:15:56.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/74/977cea1aadc43ff1c75d23bd5bc4768a8fac98c14e5878d6ee8d6bab743c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:632a3bf8f1787f7ef7d3c2f68a7bde5be2f702906f8b5842ad6da9d974d0aab3", size = 226980, upload-time = "2025-05-19T14:15:58.313Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/fc/cc4a1a2049df2eb84006607dc428ff237af38e0fcecfdb8a29ca47b1566c/multidict-6.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a145c550900deb7540973c5cdb183b0d24bed6b80bf7bddf33ed8f569082535e", size = 220641, upload-time = "2025-05-19T14:15:59.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/6a/a7444d113ab918701988d4abdde373dbdfd2def7bd647207e2bf645c7eac/multidict-6.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc5d83c6619ca5c9672cb78b39ed8542f1975a803dee2cda114ff73cbb076edd", size = 221728, upload-time = "2025-05-19T14:16:01.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/b0/fdf4c73ad1c55e0f4dbbf2aa59dd37037334091f9a4961646d2b7ac91a86/multidict-6.4.4-cp313-cp313t-win32.whl", hash = "sha256:3312f63261b9df49be9d57aaa6abf53a6ad96d93b24f9cc16cf979956355ce6e", size = 41913, upload-time = "2025-05-19T14:16:03.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/92/27989ecca97e542c0d01d05a98a5ae12198a243a9ee12563a0313291511f/multidict-6.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:ba852168d814b2c73333073e1c7116d9395bea69575a01b0b3c89d2d5a87c8fb", size = 46112, upload-time = "2025-05-19T14:16:04.909Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/5d/e17845bb0fa76334477d5de38654d27946d5b5d3695443987a094a71b440/multidict-6.4.4-py3-none-any.whl", hash = "sha256:bd4557071b561a8b3b6075c3ce93cf9bfb6182cb241805c3d66ced3b75eff4ac", size = 10481, upload-time = "2025-05-19T14:16:36.024Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.87.0"
|
||||
|
|
@ -323,6 +681,94 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/36/ac/313ded47ce1d5bc2ec02ed5dd5506bf5718678a4655ac20f337231d9aae3/openai-1.87.0-py3-none-any.whl", hash = "sha256:f9bcae02ac4fff6522276eee85d33047335cfb692b863bd8261353ce4ada5692", size = 734368, upload-time = "2025-06-16T19:04:23.181Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "propcache"
|
||||
version = "0.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proto-plus"
|
||||
version = "1.26.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "5.29.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/43/29/d09e70352e4e88c9c7a198d5645d7277811448d76c23b00345670f7c8a38/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84", size = 425226, upload-time = "2025-05-28T23:51:59.82Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/11/6e40e9fc5bba02988a214c07cf324595789ca7820160bfd1f8be96e48539/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079", size = 422963, upload-time = "2025-05-28T23:51:41.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/7f/73cefb093e1a2a7c3ffd839e6f9fcafb7a427d300c7f8aef9c64405d8ac6/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc", size = 434818, upload-time = "2025-05-28T23:51:44.297Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671", size = 418091, upload-time = "2025-05-28T23:51:45.907Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/04/98f6f8cf5b07ab1294c13f34b4e69b3722bb609c5b701d6c169828f9f8aa/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015", size = 319824, upload-time = "2025-05-28T23:51:47.545Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61", size = 319942, upload-time = "2025-05-28T23:51:49.11Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823, upload-time = "2025-05-28T23:51:58.157Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1"
|
||||
version = "0.6.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1-modules"
|
||||
version = "0.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyasn1" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.7"
|
||||
|
|
@ -366,6 +812,29 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-settings"
|
||||
version = "2.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234, upload-time = "2025-04-18T16:44:48.265Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload-time = "2025-04-18T16:44:46.617Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyreadline3"
|
||||
version = "3.5.4"
|
||||
|
|
@ -405,6 +874,33 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "4.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyasn1" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
|
|
@ -492,3 +988,78 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/d7/0c/9837fece153051e19c7bade9f88f9b409e026b9525927824cdf16293b43b/ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165", size = 38766, upload-time = "2024-05-14T02:01:32.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/72/6cb6728e2738c05bbe9bd522d6fc79f86b9a28402f38663e85a28fddd4a0/ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539", size = 42212, upload-time = "2024-05-14T02:01:33.97Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uritemplate"
|
||||
version = "4.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "win32-setctime"
|
||||
version = "1.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.20.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "idna" },
|
||||
{ name = "multidict" },
|
||||
{ name = "propcache" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811, upload-time = "2025-06-10T00:44:18.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078, upload-time = "2025-06-10T00:44:20.635Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748, upload-time = "2025-06-10T00:44:22.34Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595, upload-time = "2025-06-10T00:44:24.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616, upload-time = "2025-06-10T00:44:26.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324, upload-time = "2025-06-10T00:44:27.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676, upload-time = "2025-06-10T00:44:30.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614, upload-time = "2025-06-10T00:44:32.171Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766, upload-time = "2025-06-10T00:44:34.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615, upload-time = "2025-06-10T00:44:36.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982, upload-time = "2025-06-10T00:44:39.141Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792, upload-time = "2025-06-10T00:44:40.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049, upload-time = "2025-06-10T00:44:42.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774, upload-time = "2025-06-10T00:44:45.275Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252, upload-time = "2025-06-10T00:44:47.31Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198, upload-time = "2025-06-10T00:44:49.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346, upload-time = "2025-06-10T00:44:51.182Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826, upload-time = "2025-06-10T00:44:52.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217, upload-time = "2025-06-10T00:44:54.658Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700, upload-time = "2025-06-10T00:44:56.784Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644, upload-time = "2025-06-10T00:44:59.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452, upload-time = "2025-06-10T00:45:01.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378, upload-time = "2025-06-10T00:45:03.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261, upload-time = "2025-06-10T00:45:05.992Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987, upload-time = "2025-06-10T00:45:08.227Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361, upload-time = "2025-06-10T00:45:10.11Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460, upload-time = "2025-06-10T00:45:12.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486, upload-time = "2025-06-10T00:45:13.995Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219, upload-time = "2025-06-10T00:45:16.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693, upload-time = "2025-06-10T00:45:18.399Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803, upload-time = "2025-06-10T00:45:20.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,13 +6,9 @@ and interacting with a Diplomacy server via WebSocket.
|
|||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from loguru import logger
|
||||
from websocket_diplomacy_client import connect_to_diplomacy_server
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def basic_client_example():
|
||||
"""
|
||||
|
|
@ -25,7 +21,7 @@ async def basic_client_example():
|
|||
hostname="localhost",
|
||||
port=8432,
|
||||
username="test_player",
|
||||
password="test_password"
|
||||
password="test_password",
|
||||
)
|
||||
logger.info("Connected successfully!")
|
||||
|
||||
|
|
@ -34,8 +30,10 @@ async def basic_client_example():
|
|||
games = await client.list_games()
|
||||
logger.info(f"Found {len(games)} games:")
|
||||
for game in games:
|
||||
logger.info(f" Game {game.get('game_id', 'unknown')}: {game.get('status', 'unknown')} "
|
||||
f"({game.get('n_players', 0)}/{game.get('n_controls', 0)} players)")
|
||||
logger.info(
|
||||
f" Game {game.get('game_id', 'unknown')}: {game.get('status', 'unknown')} "
|
||||
f"({game.get('n_players', 0)}/{game.get('n_controls', 0)} players)"
|
||||
)
|
||||
|
||||
# Get available maps
|
||||
logger.info("Getting available maps...")
|
||||
|
|
@ -49,7 +47,7 @@ async def basic_client_example():
|
|||
rules=["NO_PRESS", "IGNORE_ERRORS", "POWER_CHOICE"],
|
||||
power_name="FRANCE", # Control France
|
||||
n_controls=1, # Only need 1 player to start (for testing)
|
||||
deadline=None # No time pressure
|
||||
deadline=None, # No time pressure
|
||||
)
|
||||
logger.info(f"Created game {client.game_id} as {client.power_name}")
|
||||
|
||||
|
|
@ -62,21 +60,21 @@ async def basic_client_example():
|
|||
# Get possible orders
|
||||
logger.info("Getting possible orders for France...")
|
||||
possible_orders = client.get_all_possible_orders()
|
||||
france_orders = possible_orders.get('FRANCE', [])
|
||||
france_orders = possible_orders.get("FRANCE", [])
|
||||
logger.info(f"France can make {len(france_orders)} possible orders")
|
||||
if france_orders:
|
||||
logger.info(f"First few orders: {france_orders[:5]}")
|
||||
|
||||
# Submit some orders (example: hold all units)
|
||||
logger.info("Submitting hold orders for all French units...")
|
||||
units = client.get_units('FRANCE')
|
||||
units = client.get_units("FRANCE")
|
||||
hold_orders = []
|
||||
for unit in units:
|
||||
# Format: "A PAR H" means Army in Paris holds
|
||||
hold_orders.append(f"{unit} H")
|
||||
|
||||
if hold_orders:
|
||||
await client.set_orders('FRANCE', hold_orders)
|
||||
await client.set_orders("FRANCE", hold_orders)
|
||||
logger.info(f"Submitted orders: {hold_orders}")
|
||||
|
||||
# Try to process the game (might fail if we don't have admin rights)
|
||||
|
|
@ -87,7 +85,9 @@ async def basic_client_example():
|
|||
|
||||
# Synchronize to get updated state
|
||||
await client.synchronize()
|
||||
logger.info(f"After processing - Current phase: {client.get_current_phase()}")
|
||||
logger.info(
|
||||
f"After processing - Current phase: {client.get_current_phase()}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not process game (normal if not admin): {e}")
|
||||
|
|
@ -101,7 +101,7 @@ async def basic_client_example():
|
|||
logger.error(f"Error in example: {e}", exc_info=True)
|
||||
finally:
|
||||
# Clean up
|
||||
if 'client' in locals():
|
||||
if "client" in locals():
|
||||
await client.close()
|
||||
logger.info("Example completed")
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ async def join_existing_game_example(game_id: str):
|
|||
hostname="localhost",
|
||||
port=8432,
|
||||
username="test_player_2",
|
||||
password="test_password"
|
||||
password="test_password",
|
||||
)
|
||||
|
||||
# Join as an observer first
|
||||
|
|
@ -133,13 +133,15 @@ async def join_existing_game_example(game_id: str):
|
|||
|
||||
# List powers and their status
|
||||
for power_name, power in client.powers.items():
|
||||
logger.info(f"{power_name}: {len(power.centers)} centers, "
|
||||
f"{len(power.units)} units, eliminated: {power.is_eliminated()}")
|
||||
logger.info(
|
||||
f"{power_name}: {len(power.centers)} centers, "
|
||||
f"{len(power.units)} units, eliminated: {power.is_eliminated()}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error joining game: {e}", exc_info=True)
|
||||
finally:
|
||||
if 'client' in locals():
|
||||
if "client" in locals():
|
||||
await client.close()
|
||||
|
||||
|
||||
|
|
@ -152,16 +154,19 @@ async def message_sending_example():
|
|||
|
||||
# Create a game with PRESS allowed
|
||||
game = await client.create_game(
|
||||
rules=["IGNORE_ERRORS", "POWER_CHOICE"], # Remove NO_PRESS to allow messages
|
||||
rules=[
|
||||
"IGNORE_ERRORS",
|
||||
"POWER_CHOICE",
|
||||
], # Remove NO_PRESS to allow messages
|
||||
power_name="FRANCE",
|
||||
n_controls=1
|
||||
n_controls=1,
|
||||
)
|
||||
|
||||
# Send a public message
|
||||
await client.send_message(
|
||||
sender="FRANCE",
|
||||
recipient="GLOBAL",
|
||||
message="Greetings from France! Let's have a fair game."
|
||||
message="Greetings from France! Let's have a fair game.",
|
||||
)
|
||||
logger.info("Sent public message")
|
||||
|
||||
|
|
@ -170,7 +175,7 @@ async def message_sending_example():
|
|||
await client.send_message(
|
||||
sender="FRANCE",
|
||||
recipient="ENGLAND",
|
||||
message="Hello England, shall we discuss an alliance?"
|
||||
message="Hello England, shall we discuss an alliance?",
|
||||
)
|
||||
logger.info("Sent private message to England")
|
||||
except Exception as e:
|
||||
|
|
@ -179,7 +184,7 @@ async def message_sending_example():
|
|||
except Exception as e:
|
||||
logger.error(f"Error in messaging example: {e}", exc_info=True)
|
||||
finally:
|
||||
if 'client' in locals():
|
||||
if "client" in locals():
|
||||
await client.close()
|
||||
|
||||
|
||||
|
|
@ -193,3 +198,4 @@ if __name__ == "__main__":
|
|||
else:
|
||||
# Run basic example
|
||||
asyncio.run(basic_client_example())
|
||||
|
||||
|
|
|
|||
|
|
@ -5,21 +5,15 @@ A simplified client wrapper for connecting to a Diplomacy server via WebSocket
|
|||
and playing games remotely, designed as a drop-in replacement for direct Game() usage.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import time
|
||||
from typing import Dict, List, Optional, Any
|
||||
from tornado import gen
|
||||
from diplomacy.engine.game import Game
|
||||
from loguru import logger
|
||||
|
||||
from diplomacy.client.connection import connect
|
||||
from diplomacy.client.channel import Channel
|
||||
from diplomacy.client.network_game import NetworkGame
|
||||
from diplomacy.communication import requests
|
||||
from diplomacy.engine.message import Message
|
||||
from diplomacy.utils.exceptions import DiplomacyException
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebSocketDiplomacyClient:
|
||||
"""
|
||||
|
|
@ -29,7 +23,9 @@ class WebSocketDiplomacyClient:
|
|||
but communicates with a remote server via WebSocket connections.
|
||||
"""
|
||||
|
||||
def __init__(self, hostname: str = "localhost", port: int = 8432, use_ssl: bool = False):
|
||||
def __init__(
|
||||
self, hostname: str = "localhost", port: int = 8432, use_ssl: bool = False
|
||||
):
|
||||
"""
|
||||
Initialize the WebSocket client.
|
||||
|
||||
|
|
@ -42,9 +38,9 @@ class WebSocketDiplomacyClient:
|
|||
self.port = port
|
||||
self.use_ssl = use_ssl
|
||||
|
||||
self.game: NetworkGame
|
||||
self.connection = None
|
||||
self.channel = None
|
||||
self.game = None
|
||||
self.username = None
|
||||
self.token = None
|
||||
|
||||
|
|
@ -71,14 +67,16 @@ class WebSocketDiplomacyClient:
|
|||
|
||||
logger.info("Successfully connected and authenticated")
|
||||
|
||||
async def create_game(self,
|
||||
async def create_game(
|
||||
self,
|
||||
map_name: str = "standard",
|
||||
rules: Optional[List[str]] = None,
|
||||
game_id: Optional[str] = None,
|
||||
power_name: Optional[str] = None,
|
||||
n_controls: int = 7,
|
||||
deadline: Optional[int] = None,
|
||||
registration_password: Optional[str] = None) -> NetworkGame:
|
||||
registration_password: Optional[str] = None,
|
||||
) -> NetworkGame:
|
||||
"""
|
||||
Create a new game on the server.
|
||||
|
||||
|
|
@ -109,7 +107,7 @@ class WebSocketDiplomacyClient:
|
|||
power_name=power_name,
|
||||
n_controls=n_controls,
|
||||
deadline=deadline,
|
||||
registration_password=registration_password
|
||||
registration_password=registration_password,
|
||||
)
|
||||
|
||||
self._game_id = self.game.game_id
|
||||
|
|
@ -119,10 +117,12 @@ class WebSocketDiplomacyClient:
|
|||
logger.info(f"Created game {self._game_id} as {self._game_role}")
|
||||
return self.game
|
||||
|
||||
async def join_game(self,
|
||||
async def join_game(
|
||||
self,
|
||||
game_id: str,
|
||||
power_name: Optional[str] = None,
|
||||
registration_password: Optional[str] = None) -> NetworkGame:
|
||||
registration_password: Optional[str] = None,
|
||||
) -> NetworkGame:
|
||||
"""
|
||||
Join an existing game.
|
||||
|
||||
|
|
@ -142,7 +142,7 @@ class WebSocketDiplomacyClient:
|
|||
self.game = await self.channel.join_game(
|
||||
game_id=game_id,
|
||||
power_name=power_name,
|
||||
registration_password=registration_password
|
||||
registration_password=registration_password,
|
||||
)
|
||||
|
||||
self._game_id = game_id
|
||||
|
|
@ -152,11 +152,13 @@ class WebSocketDiplomacyClient:
|
|||
logger.info(f"Joined game {game_id} as {self._game_role}")
|
||||
return self.game
|
||||
|
||||
async def list_games(self,
|
||||
async def list_games(
|
||||
self,
|
||||
game_id_filter: Optional[str] = None,
|
||||
map_name: Optional[str] = None,
|
||||
status: Optional[str] = None,
|
||||
include_protected: bool = False) -> List[Dict[str, Any]]:
|
||||
include_protected: bool = False,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
List available games on the server.
|
||||
|
||||
|
|
@ -176,7 +178,7 @@ class WebSocketDiplomacyClient:
|
|||
game_id=game_id_filter,
|
||||
map_name=map_name,
|
||||
status=status,
|
||||
include_protected=include_protected
|
||||
include_protected=include_protected,
|
||||
)
|
||||
|
||||
return games
|
||||
|
|
@ -193,7 +195,9 @@ class WebSocketDiplomacyClient:
|
|||
|
||||
return await self.channel.get_available_maps()
|
||||
|
||||
async def set_orders(self, power_name: str, orders: List[str], wait: Optional[bool] = None) -> None:
|
||||
async def set_orders(
|
||||
self, power_name: str, orders: List[str], wait: Optional[bool] = None
|
||||
) -> None:
|
||||
"""
|
||||
Set orders for a power.
|
||||
|
||||
|
|
@ -238,7 +242,9 @@ class WebSocketDiplomacyClient:
|
|||
else:
|
||||
await self.game.no_wait()
|
||||
|
||||
async def send_message(self, sender: str, recipient: str, message: str, phase: Optional[str] = None) -> None:
|
||||
async def send_message(
|
||||
self, sender: str, recipient: str, message: str, phase: Optional[str] = None
|
||||
) -> None:
|
||||
"""
|
||||
Send a diplomatic message.
|
||||
|
||||
|
|
@ -254,12 +260,7 @@ class WebSocketDiplomacyClient:
|
|||
if phase is None:
|
||||
phase = self.game.current_short_phase
|
||||
|
||||
msg = Message(
|
||||
sender=sender,
|
||||
recipient=recipient,
|
||||
message=message,
|
||||
phase=phase
|
||||
)
|
||||
msg = Message(sender=sender, recipient=recipient, message=message, phase=phase)
|
||||
|
||||
logger.debug(f"Sending message from {sender} to {recipient}: {message}")
|
||||
await self.game.send_game_message(message=msg)
|
||||
|
|
@ -284,9 +285,9 @@ class WebSocketDiplomacyClient:
|
|||
logger.debug("Synchronizing game state")
|
||||
await self.game.synchronize()
|
||||
|
||||
async def get_phase_history(self,
|
||||
from_phase: Optional[str] = None,
|
||||
to_phase: Optional[str] = None) -> List[Dict[str, Any]]:
|
||||
async def get_phase_history(
|
||||
self, from_phase: Optional[str] = None, to_phase: Optional[str] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get phase history for the game.
|
||||
|
||||
|
|
@ -301,8 +302,7 @@ class WebSocketDiplomacyClient:
|
|||
raise DiplomacyException("Must join a game first")
|
||||
|
||||
return await self.game.get_phase_history(
|
||||
from_phase=from_phase,
|
||||
to_phase=to_phase
|
||||
from_phase=from_phase, to_phase=to_phase
|
||||
)
|
||||
|
||||
async def vote(self, power_name: str, vote: str) -> None:
|
||||
|
|
@ -431,11 +431,13 @@ class WebSocketDiplomacyClient:
|
|||
|
||||
|
||||
# Convenience function for quick setup
|
||||
async def connect_to_diplomacy_server(hostname: str = "localhost",
|
||||
async def connect_to_diplomacy_server(
|
||||
hostname: str = "localhost",
|
||||
port: int = 8432,
|
||||
username: str = "player",
|
||||
password: str = "password",
|
||||
use_ssl: bool = False) -> WebSocketDiplomacyClient:
|
||||
use_ssl: bool = False,
|
||||
) -> WebSocketDiplomacyClient:
|
||||
"""
|
||||
Convenience function to quickly connect to a Diplomacy server.
|
||||
|
||||
|
|
@ -452,3 +454,4 @@ async def connect_to_diplomacy_server(hostname: str = "localhost",
|
|||
client = WebSocketDiplomacyClient(hostname, port, use_ssl)
|
||||
await client.connect_and_authenticate(username, password)
|
||||
return client
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue