From 693f792248815cccfbdc2541278e284a652d4e53 Mon Sep 17 00:00:00 2001 From: Xuhui Zhou Date: Wed, 4 Dec 2024 23:58:52 -0500 Subject: [PATCH 01/26] feat: FastAPI Implementation of Sotopia Part Two (w websocket) (#252) * api doc * add PUT * add an temp example for websocket * websocket * update readme * Update README.md * update websocket live simulation api doc * [autofix.ci] apply automated fixes * update websocket doc * add api server with websocket as well as a client * fix mypy errors * support stopping the chat * add 404 to the status code * fix mypy issue * update the returned message types * redesign websocket api * update websocket, fix mypy error * add example of using websocket * clean code & change to existing functions for simulation * fix typing mismatch * update doc & mypy type fix * add type check for run_async_server * move example --------- Co-authored-by: Hao Zhu Co-authored-by: Zhe Su <360307598@qq.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../websocket/websocket_test_client.py | 97 +++++++ sotopia/server.py | 209 ++++++++------ sotopia/ui/README.md | 85 ++++-- sotopia/ui/fastapi_server.py | 254 +++++++++++++++++- sotopia/ui/websocket_utils.py | 186 +++++++++++++ uv.lock | 46 ++++ 6 files changed, 754 insertions(+), 123 deletions(-) create mode 100644 examples/experimental/websocket/websocket_test_client.py create mode 100644 sotopia/ui/websocket_utils.py diff --git a/examples/experimental/websocket/websocket_test_client.py b/examples/experimental/websocket/websocket_test_client.py new file mode 100644 index 00000000..c1bd74b6 --- /dev/null +++ b/examples/experimental/websocket/websocket_test_client.py @@ -0,0 +1,97 @@ +""" +A test client for the WebSocket server +""" + +import json +from sotopia.database import EnvironmentProfile, AgentProfile + +import asyncio +import websockets +import sys +from pathlib import Path + + +class WebSocketClient: + def __init__(self, uri: str, token: str, client_id: int): + self.uri = uri + self.token = token + self.client_id = client_id + self.message_file = Path(f"message_{client_id}.txt") + + async def save_message(self, message: str) -> None: + """Save received message to a file""" + with open(self.message_file, "a", encoding="utf-8") as f: + f.write(f"{message}\n") + + async def connect(self) -> None: + """Establish and maintain websocket connection""" + uri_with_token = f"{self.uri}?token=test_token_{self.client_id}" + + try: + async with websockets.connect(uri_with_token) as websocket: + print(f"Client {self.client_id}: Connected to {self.uri}") + + # Send initial message + # Note: You'll need to implement the logic to get agent_ids and env_id + # This is just an example structure + agent_ids = [agent.pk for agent in AgentProfile.find().all()[:2]] + env_id = EnvironmentProfile.find().all()[0].pk + start_message = { + "type": "START_SIM", + "data": { + "env_id": env_id, # Replace with actual env_id + "agent_ids": agent_ids, # Replace with actual agent_ids + }, + } + await websocket.send(json.dumps(start_message)) + print(f"Client {self.client_id}: Sent START_SIM message") + + # Receive and process messages + while True: + try: + message = await websocket.recv() + print( + f"\nClient {self.client_id} received message:", + json.dumps(json.loads(message), indent=2), + ) + assert isinstance(message, str) + await self.save_message(message) + except websockets.ConnectionClosed: + print(f"Client {self.client_id}: Connection closed") + break + except Exception as e: + print(f"Client {self.client_id} error:", str(e)) + break + + except Exception as e: + print(f"Client {self.client_id} connection error:", str(e)) + + +async def main() -> None: + # Create multiple WebSocket clients + num_clients = 0 + uri = "ws://localhost:8800/ws/simulation" + + # Create and store client instances + clients = [ + WebSocketClient(uri=uri, token=f"test_token_{i}", client_id=i) + for i in range(num_clients) + ] + clients.append(WebSocketClient(uri=uri, token="test_token_10", client_id=10)) + clients.append( + WebSocketClient(uri=uri, token="test_token_10", client_id=10) + ) # test duplicate token + + # Create tasks for each client + tasks = [asyncio.create_task(client.connect()) for client in clients] + + # Wait for all tasks to complete + await asyncio.gather(*tasks) + + +if __name__ == "__main__": + try: + asyncio.run(main()) + except KeyboardInterrupt: + print("\nShutting down clients...") + sys.exit(0) diff --git a/sotopia/server.py b/sotopia/server.py index d285558a..aec81a0f 100644 --- a/sotopia/server.py +++ b/sotopia/server.py @@ -1,7 +1,7 @@ import asyncio import itertools import logging -from typing import Literal, Sequence, Type +from typing import Literal, Sequence, Type, AsyncGenerator, Union import gin import rich @@ -25,7 +25,7 @@ unweighted_aggregate_evaluate, ) from sotopia.generation_utils.generate import LLM_Name, agenerate_script -from sotopia.messages import AgentAction, Message, Observation +from sotopia.messages import AgentAction, Message, Observation, SimpleMessage from sotopia.messages.message_classes import ( ScriptBackground, ScriptEnvironmentResponse, @@ -104,6 +104,12 @@ def run_sync_server( return messages +def flatten_listed_messages( + messages: list[list[tuple[str, str, Message]]], +) -> list[tuple[str, str, Message]]: + return list(itertools.chain.from_iterable(messages)) + + @gin.configurable async def arun_one_episode( env: ParallelSotopiaEnv, @@ -113,102 +119,125 @@ async def arun_one_episode( json_in_script: bool = False, tag: str | None = None, push_to_db: bool = False, -) -> list[tuple[str, str, Message]]: + streaming: bool = False, +) -> Union[ + list[tuple[str, str, Message]], + AsyncGenerator[list[list[tuple[str, str, Message]]], None], +]: agents = Agents({agent.agent_name: agent for agent in agent_list}) - environment_messages = env.reset(agents=agents, omniscient=omniscient) - agents.reset() - - messages: list[list[tuple[str, str, Message]]] = [] - # Main Event Loop - done = False - messages.append( - [ - ("Environment", agent_name, environment_messages[agent_name]) - for agent_name in env.agents - ] - ) - # set goal for agents - for index, agent_name in enumerate(env.agents): - agents[agent_name].goal = env.profile.agent_goals[index] - rewards: list[list[float]] = [] - reasons: list[str] = [] - while not done: - # gather agent messages - agent_messages: dict[str, AgentAction] = dict() - actions = await asyncio.gather( - *[ - agents[agent_name].aact(environment_messages[agent_name]) - for agent_name in env.agents - ] - ) - if script_like: - # manually mask one message - agent_mask = env.action_mask - for idx in range(len(agent_mask)): - print("Current mask: ", agent_mask) - if agent_mask[idx] == 0: - print("Action not taken: ", actions[idx]) - actions[idx] = AgentAction(action_type="none", argument="") - else: - print("Current action taken: ", actions[idx]) - - # actions = cast(list[AgentAction], actions) - for idx, agent_name in enumerate(env.agents): - agent_messages[agent_name] = actions[idx] - - messages[-1].append((agent_name, "Environment", agent_messages[agent_name])) + async def generate_messages() -> ( + AsyncGenerator[list[list[tuple[str, str, Message]]], None] + ): + environment_messages = env.reset(agents=agents, omniscient=omniscient) + agents.reset() + messages: list[list[tuple[str, str, Message]]] = [] - # send agent messages to environment - ( - environment_messages, - rewards_in_turn, - terminated, - ___, - info, - ) = await env.astep(agent_messages) + # Main Event Loop + done = False messages.append( [ ("Environment", agent_name, environment_messages[agent_name]) for agent_name in env.agents ] ) - # print("Environment message: ", environment_messages) - # exit(0) - rewards.append([rewards_in_turn[agent_name] for agent_name in env.agents]) - reasons.append( - " ".join(info[agent_name]["comments"] for agent_name in env.agents) + yield messages + + # set goal for agents + for index, agent_name in enumerate(env.agents): + agents[agent_name].goal = env.profile.agent_goals[index] + rewards: list[list[float]] = [] + reasons: list[str] = [] + while not done: + # gather agent messages + agent_messages: dict[str, AgentAction] = dict() + actions = await asyncio.gather( + *[ + agents[agent_name].aact(environment_messages[agent_name]) + for agent_name in env.agents + ] + ) + if script_like: + # manually mask one message + agent_mask = env.action_mask + for idx in range(len(agent_mask)): + if agent_mask[idx] == 0: + actions[idx] = AgentAction(action_type="none", argument="") + else: + pass + + # actions = cast(list[AgentAction], actions) + for idx, agent_name in enumerate(env.agents): + agent_messages[agent_name] = actions[idx] + + messages[-1].append( + (agent_name, "Environment", agent_messages[agent_name]) + ) + + # send agent messages to environment + ( + environment_messages, + rewards_in_turn, + terminated, + ___, + info, + ) = await env.astep(agent_messages) + messages.append( + [ + ("Environment", agent_name, environment_messages[agent_name]) + for agent_name in env.agents + ] + ) + + yield messages + rewards.append([rewards_in_turn[agent_name] for agent_name in env.agents]) + reasons.append( + " ".join(info[agent_name]["comments"] for agent_name in env.agents) + ) + done = all(terminated.values()) + + epilog = EpisodeLog( + environment=env.profile.pk, + agents=[agent.profile.pk for agent in agent_list], + tag=tag, + models=[env.model_name, agent_list[0].model_name, agent_list[1].model_name], + messages=[ + [(m[0], m[1], m[2].to_natural_language()) for m in messages_in_turn] + for messages_in_turn in messages + ], + reasoning=info[env.agents[0]]["comments"], + rewards=[info[agent_name]["complete_rating"] for agent_name in env.agents], + rewards_prompt=info["rewards_prompt"]["overall_prompt"], ) - done = all(terminated.values()) + rich.print(epilog.rewards_prompt) + agent_profiles, conversation = epilog.render_for_humans() + for agent_profile in agent_profiles: + rich.print(agent_profile) + for message in conversation: + rich.print(message) + + if streaming: + # yield the rewards and reasonings + messages.append( + [("Evaluation", "Rewards", SimpleMessage(message=str(epilog.rewards)))] + ) + messages.append( + [("Evaluation", "Reasoning", SimpleMessage(message=epilog.reasoning))] + ) + yield messages - # TODO: clean up this part - epilog = EpisodeLog( - environment=env.profile.pk, - agents=[agent.profile.pk for agent in agent_list], - tag=tag, - models=[env.model_name, agent_list[0].model_name, agent_list[1].model_name], - messages=[ - [(m[0], m[1], m[2].to_natural_language()) for m in messages_in_turn] - for messages_in_turn in messages - ], - reasoning=info[env.agents[0]]["comments"], - rewards=[info[agent_name]["complete_rating"] for agent_name in env.agents], - rewards_prompt=info["rewards_prompt"]["overall_prompt"], - ) - rich.print(epilog.rewards_prompt) - agent_profiles, conversation = epilog.render_for_humans() - for agent_profile in agent_profiles: - rich.print(agent_profile) - for message in conversation: - rich.print(message) + if push_to_db: + try: + epilog.save() + except Exception as e: + logging.error(f"Failed to save episode log: {e}") - if push_to_db: - try: - epilog.save() - except Exception as e: - logging.error(f"Failed to save episode log: {e}") - # flatten nested list messages - return list(itertools.chain(*messages)) + if streaming: + return generate_messages() + else: + async for last_messages in generate_messages(): + pass + return flatten_listed_messages(last_messages) @gin.configurable @@ -310,7 +339,13 @@ def get_agent_class( else [await i for i in episode_futures] ) - return batch_results + if len(batch_results) > 0: + first_result = batch_results[0] + assert isinstance( + first_result, list + ), f"Unexpected result type: {type(first_result)}" + + return batch_results # type: ignore async def arun_one_script( diff --git a/sotopia/ui/README.md b/sotopia/ui/README.md index ca8b679d..156050a4 100644 --- a/sotopia/ui/README.md +++ b/sotopia/ui/README.md @@ -78,33 +78,31 @@ EnvironmentProfile returns: - scenario_id: str -#### DELETE /agents/{agent_id} +### Updating Data in the API Server -Delete agent profile from the API server. +#### PUT /agents/{agent_id} + +Update agent profile in the API server. +Request Body: +AgentProfile returns: - agent_id: str -#### DELETE /scenarios/{scenario_id} -Delete scenario profile from the API server. +#### PUT /scenarios/{scenario_id} + +Update scenario profile in the API server. +Request Body: +EnvironmentProfile returns: - scenario_id: str - -### Error Code -For RESTful APIs above we have the following error codes: -| **Error Code** | **Description** | -|-----------------|--------------------------------------| -| **404** | A resource is not found | -| **403** | The query is not authorized | -| **500** | Internal running error | - ### Initiating a new non-streaming simulation episode #### POST /episodes/ -[!] Currently not planning to implement + ```python class SimulationEpisodeInitiation(BaseModel): scenario_id: str @@ -147,14 +145,14 @@ returns: | Type | Direction | Description | |-----------|--------|-------------| | SERVER_MSG | Server → Client | Standard message from server (payload: `messageForRendering` [here](https://github.com/sotopia-lab/sotopia-demo/blob/main/socialstream/rendering_utils.py) ) | -| CLIENT_MSG | Client → Server | Standard message from client (payload: Currently not needed) | -| ERROR | Server → Client | Error notification (payload: `{"type": ERROR_TYPE, "description": DESC}`) | +| CLIENT_MSG | Client → Server | Standard message from client (payload: TBD) | +| ERROR | Server → Client | Error notification (payload: TBD) | | START_SIM | Client → Server | Initialize simulation (payload: `SimulationEpisodeInitialization`) | | END_SIM | Client → Server | End simulation (payload: not needed) | | FINISH_SIM | Server → Client | Terminate simulation (payload: not needed) | -**ERROR_TYPE** +**Error Type** | Error Code | Description | |------------|-------------| @@ -167,14 +165,53 @@ returns: | OTHER | Other unspecified errors | -**Conversation Message From the Server** -The server returns messages encapsulated in a structured format which is defined as follows: +**Implementation plan**: Currently only support LLM-LLM simulation based on [this function](https://github.com/sotopia-lab/sotopia/blob/19d39e068c3bca9246fc366e5759414f62284f93/sotopia/server.py#L108). + + +## An example to run simulation with the API + +**Get all scenarios**: +```bash +curl -X GET "http://localhost:8000/scenarios" +``` + +This gonna give you all the scenarios, and you can randomly pick one + + +**Get all agents**: +```bash +curl -X GET "http://localhost:8000/agents" +``` + +This gonna give you all the agents, and you can randomly pick one + +**Connecting to the websocket server**: +We recommend using Python. Here is the simplist way to start a simulation and receive the results in real time: ```python -class MessageForRendering(TypedDict): - role: str # Specifies the origin of the message. Common values include "Background Info", "Environment", "{Agent Names} - type: str # Categorizes the nature of the message. Common types include: "comment", "said", "action" - content: str +import aiohttp +import asyncio +import json + +async def main(): + async with aiohttp.ClientSession() as session: + async with session.ws_connect(f'ws://{API_BASE}/ws/simulation?token={YOUR_TOKEN}') as ws: + start_message = { + "type": "START_SIM", + "data": { + "env_id": "{ENV_ID}", + "agent_ids": ["{AGENT1_PK}", "{AGENT2_PK}"], + }, + } + await ws.send_json(start_message) + + async for msg in ws: + if msg.type == aiohttp.WSMsgType.TEXT: + print(f"Received: {msg.data}") + elif msg.type == aiohttp.WSMsgType.CLOSED: + break + elif msg.type == aiohttp.WSMsgType.ERROR: + break ``` -**Implementation plan**: Currently only support LLM-LLM simulation based on [this function](https://github.com/sotopia-lab/sotopia/blob/19d39e068c3bca9246fc366e5759414f62284f93/sotopia/server.py#L108). +Please check out an detailed example in `examples/experimental/websocket/websocket_test_client.py` diff --git a/sotopia/ui/fastapi_server.py b/sotopia/ui/fastapi_server.py index ea53f4e5..543dafd2 100644 --- a/sotopia/ui/fastapi_server.py +++ b/sotopia/ui/fastapi_server.py @@ -1,11 +1,33 @@ -from fastapi import FastAPI -from typing import Literal, cast, Dict -from sotopia.database import EnvironmentProfile, AgentProfile, EpisodeLog +from fastapi import FastAPI, WebSocket, HTTPException, WebSocketDisconnect +from typing import Literal, cast, Optional, Any +from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel + +from sotopia.database import EnvironmentProfile, AgentProfile, EpisodeLog +from sotopia.ui.websocket_utils import ( + WebSocketSotopiaSimulator, + WSMessageType, + ErrorType, +) import uvicorn +import asyncio + +from contextlib import asynccontextmanager +from typing import AsyncIterator +import logging + +logger = logging.getLogger(__name__) app = FastAPI() +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) # TODO: Whether allowing CORS for all origins + class AgentProfileWrapper(BaseModel): """ @@ -64,6 +86,12 @@ async def get_scenarios( EnvironmentProfile.codename == value ).all() scenarios.extend(cast(list[EnvironmentProfile], json_models)) + + if not scenarios: + raise HTTPException( + status_code=404, detail=f"No scenarios found with {get_by}={value}" + ) + return scenarios @@ -85,9 +113,20 @@ async def get_agents( elif get_by == "occupation": json_models = AgentProfile.find(AgentProfile.occupation == value).all() agents_profiles.extend(cast(list[AgentProfile], json_models)) + + if not agents_profiles: + raise HTTPException( + status_code=404, detail=f"No agents found with {get_by}={value}" + ) + return agents_profiles +@app.get("/episodes", response_model=list[EpisodeLog]) +async def get_episodes_all() -> list[EpisodeLog]: + return EpisodeLog.all() + + @app.get("/episodes/{get_by}/{value}", response_model=list[EpisodeLog]) async def get_episodes(get_by: Literal["id", "tag"], value: str) -> list[EpisodeLog]: episodes: list[EpisodeLog] = [] @@ -96,10 +135,15 @@ async def get_episodes(get_by: Literal["id", "tag"], value: str) -> list[Episode elif get_by == "tag": json_models = EpisodeLog.find(EpisodeLog.tag == value).all() episodes.extend(cast(list[EpisodeLog], json_models)) + + if not episodes: + raise HTTPException( + status_code=404, detail=f"No episodes found with {get_by}={value}" + ) return episodes -@app.post("/agents/") +@app.post("/agents/", response_model=str) async def create_agent(agent: AgentProfileWrapper) -> str: agent_profile = AgentProfile(**agent.model_dump()) agent_profile.save() @@ -110,7 +154,6 @@ async def create_agent(agent: AgentProfileWrapper) -> str: @app.post("/scenarios/", response_model=str) async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: - print(scenario) scenario_profile = EnvironmentProfile(**scenario.model_dump()) scenario_profile.save() pk = scenario_profile.pk @@ -118,21 +161,208 @@ async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: return pk +@app.put("/agents/{agent_id}", response_model=str) +async def update_agent(agent_id: str, agent: AgentProfileWrapper) -> str: + try: + old_agent = AgentProfile.get(pk=agent_id) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, detail=f"Agent with id={agent_id} not found" + ) + old_agent.update(**agent.model_dump()) # type: ignore + assert old_agent.pk is not None + return old_agent.pk + + +@app.put("/scenarios/{scenario_id}", response_model=str) +async def update_scenario(scenario_id: str, scenario: EnvironmentProfileWrapper) -> str: + try: + old_scenario = EnvironmentProfile.get(pk=scenario_id) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, detail=f"Scenario with id={scenario_id} not found" + ) + old_scenario.update(**scenario.model_dump()) # type: ignore + assert old_scenario.pk is not None + return old_scenario.pk + + @app.delete("/agents/{agent_id}", response_model=str) async def delete_agent(agent_id: str) -> str: - AgentProfile.delete(agent_id) - return agent_id + try: + agent = AgentProfile.get(pk=agent_id) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, detail=f"Agent with id={agent_id} not found" + ) + AgentProfile.delete(agent.pk) + assert agent.pk is not None + return agent.pk @app.delete("/scenarios/{scenario_id}", response_model=str) async def delete_scenario(scenario_id: str) -> str: - EnvironmentProfile.delete(scenario_id) - return scenario_id + try: + scenario = EnvironmentProfile.get(pk=scenario_id) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, detail=f"Scenario with id={scenario_id} not found" + ) + EnvironmentProfile.delete(scenario.pk) + assert scenario.pk is not None + return scenario.pk + + +@app.get("/models", response_model=list[str]) +async def get_models() -> list[str]: + # TODO figure out how to get the available models + return ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo"] + + +class SimulationState: + _instance: Optional["SimulationState"] = None + _lock = asyncio.Lock() + _active_simulations: dict[str, bool] = {} + + def __new__(cls) -> "SimulationState": + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._instance._active_simulations = {} + return cls._instance + + async def try_acquire_token(self, token: str) -> tuple[bool, str]: + async with self._lock: + if not token: + return False, "Invalid token" + + if self._active_simulations.get(token): + return False, "Token is active already" + + self._active_simulations[token] = True + return True, "Token is valid" + + async def release_token(self, token: str) -> None: + async with self._lock: + self._active_simulations.pop(token, None) + + @asynccontextmanager + async def start_simulation(self, token: str) -> AsyncIterator[bool]: + try: + yield True + finally: + await self.release_token(token) + + +class SimulationManager: + def __init__(self) -> None: + self.state = SimulationState() + + async def verify_token(self, token: str) -> dict[str, Any]: + is_valid, msg = await self.state.try_acquire_token(token) + return {"is_valid": is_valid, "msg": msg} + + async def create_simulator( + self, env_id: str, agent_ids: list[str] + ) -> WebSocketSotopiaSimulator: + try: + return WebSocketSotopiaSimulator(env_id=env_id, agent_ids=agent_ids) + except Exception as e: + error_msg = f"Failed to create simulator: {e}" + logger.error(error_msg) + raise Exception(error_msg) + + async def handle_client_message( + self, + websocket: WebSocket, + simulator: WebSocketSotopiaSimulator, + message: dict[str, Any], + timeout: float = 0.1, + ) -> bool: + try: + msg_type = message.get("type") + if msg_type == WSMessageType.FINISH_SIM.value: + return True + # TODO handle other message types + return False + except Exception as e: + msg = f"Error handling client message: {e}" + logger.error(msg) + await self.send_error(websocket, ErrorType.INVALID_MESSAGE, msg) + return False + + async def run_simulation( + self, websocket: WebSocket, simulator: WebSocketSotopiaSimulator + ) -> None: + try: + async for message in simulator.arun(): + await self.send_message(websocket, WSMessageType.SERVER_MSG, message) + + try: + data = await asyncio.wait_for(websocket.receive_json(), timeout=0.1) + if await self.handle_client_message(websocket, simulator, data): + break + except asyncio.TimeoutError: + continue + + except Exception as e: + msg = f"Error running simulation: {e}" + logger.error(msg) + await self.send_error(websocket, ErrorType.SIMULATION_ISSUE, msg) + finally: + await self.send_message(websocket, WSMessageType.END_SIM, {}) + + @staticmethod + async def send_message( + websocket: WebSocket, msg_type: WSMessageType, data: dict[str, Any] + ) -> None: + await websocket.send_json({"type": msg_type.value, "data": data}) + + @staticmethod + async def send_error( + websocket: WebSocket, error_type: ErrorType, details: str = "" + ) -> None: + await websocket.send_json( + { + "type": WSMessageType.ERROR.value, + "data": {"type": error_type.value, "details": details}, + } + ) + + +@app.websocket("/ws/simulation") +async def websocket_endpoint(websocket: WebSocket, token: str) -> None: + manager = SimulationManager() + + token_status = await manager.verify_token(token) + if not token_status["is_valid"]: + await websocket.close(code=1008, reason=token_status["msg"]) + return + + try: + await websocket.accept() + + while True: + start_msg = await websocket.receive_json() + if start_msg.get("type") != WSMessageType.START_SIM.value: + continue + async with manager.state.start_simulation(token): + simulator = await manager.create_simulator( + env_id=start_msg["data"]["env_id"], + agent_ids=start_msg["data"]["agent_ids"], + ) + await manager.run_simulation(websocket, simulator) -active_simulations: Dict[ - str, bool -] = {} # TODO check whether this is the correct way to store the active simulations + except WebSocketDisconnect: + logger.info(f"Client disconnected: {token}") + except Exception as e: + logger.error(f"Unexpected error: {e}") + await manager.send_error(websocket, ErrorType.SIMULATION_ISSUE, str(e)) + finally: + try: + await websocket.close() + except Exception as e: + logger.error(f"Error closing websocket: {e}") if __name__ == "__main__": diff --git a/sotopia/ui/websocket_utils.py b/sotopia/ui/websocket_utils.py new file mode 100644 index 00000000..5b29da73 --- /dev/null +++ b/sotopia/ui/websocket_utils.py @@ -0,0 +1,186 @@ +from sotopia.envs.evaluators import ( + EvaluationForTwoAgents, + ReachGoalLLMEvaluator, + RuleBasedTerminatedEvaluator, + SotopiaDimensions, +) +from sotopia.agents import Agents, LLMAgent +from sotopia.messages import Observation +from sotopia.envs import ParallelSotopiaEnv +from sotopia.database import EnvironmentProfile, AgentProfile, EpisodeLog +from sotopia.server import arun_one_episode + +from enum import Enum +from typing import TypedDict, Any, AsyncGenerator +from pydantic import BaseModel + + +class WSMessageType(str, Enum): + SERVER_MSG = "SERVER_MSG" + CLIENT_MSG = "CLIENT_MSG" + ERROR = "ERROR" + START_SIM = "START_SIM" + END_SIM = "END_SIM" + FINISH_SIM = "FINISH_SIM" + + +class ErrorType(str, Enum): + NOT_AUTHORIZED = "NOT_AUTHORIZED" + SIMULATION_ALREADY_STARTED = "SIMULATION_ALREADY_STARTED" + SIMULATION_NOT_STARTED = "SIMULATION_NOT_STARTED" + SIMULATION_ISSUE = "SIMULATION_ISSUE" + INVALID_MESSAGE = "INVALID_MESSAGE" + OTHER = "OTHER" + + +class MessageForRendering(TypedDict): + role: str + type: str + content: str + + +class WSMessage(BaseModel): + type: WSMessageType + data: dict[str, Any] + + model_config = {"arbitrary_types_allowed": True, "protected_namespaces": ()} + + def to_json(self) -> dict[str, Any]: + return { + "type": self.type.value, # TODO check whether we want to use the enum value or the enum itself + "data": self.data, + } + + +def get_env_agents( + env_id: str, + agent_ids: list[str], + agent_models: list[str], + evaluator_model: str, +) -> tuple[ParallelSotopiaEnv, Agents, dict[str, Observation]]: + # environment_profile = EnvironmentProfile.find().all()[0] + # agent_profiles = AgentProfile.find().all()[:2] + assert len(agent_ids) == len( + agent_models + ), f"Provided {len(agent_ids)} agent_ids but {len(agent_models)} agent_models" + + environment_profile: EnvironmentProfile = EnvironmentProfile.get(env_id) + agent_profiles: list[AgentProfile] = [ + AgentProfile.get(agent_id) for agent_id in agent_ids + ] + + agent_list = [ + LLMAgent( + agent_profile=agent_profile, + model_name=agent_models[idx], + ) + for idx, agent_profile in enumerate(agent_profiles) + ] + for idx, goal in enumerate(environment_profile.agent_goals): + agent_list[idx].goal = goal + + agents = Agents({agent.agent_name: agent for agent in agent_list}) + env = ParallelSotopiaEnv( + action_order="round-robin", + model_name="gpt-4o-mini", + evaluators=[ + RuleBasedTerminatedEvaluator(max_turn_number=20, max_stale_turn=2), + ], + terminal_evaluators=[ + ReachGoalLLMEvaluator( + evaluator_model, + EvaluationForTwoAgents[SotopiaDimensions], + ), + ], + env_profile=environment_profile, + ) + + environment_messages = env.reset(agents=agents, omniscient=False) + agents.reset() + + return env, agents, environment_messages + + +def parse_reasoning(reasoning: str, num_agents: int) -> tuple[list[str], str]: + """Parse the reasoning string into a dictionary.""" + sep_token = "SEPSEP" + for i in range(1, num_agents + 1): + reasoning = ( + reasoning.replace(f"Agent {i} comments:\n", sep_token) + .strip(" ") + .strip("\n") + ) + all_chunks = reasoning.split(sep_token) + general_comment = all_chunks[0].strip(" ").strip("\n") + comment_chunks = all_chunks[-num_agents:] + + return comment_chunks, general_comment + + +class WebSocketSotopiaSimulator: + def __init__( + self, + env_id: str, + agent_ids: list[str], + agent_models: list[str] = ["gpt-4o-mini", "gpt-4o-mini"], + evaluator_model: str = "gpt-4o", + ) -> None: + self.env, self.agents, self.environment_messages = get_env_agents( + env_id, agent_ids, agent_models, evaluator_model + ) + self.messages: list[list[tuple[str, str, str]]] = [] + self.messages.append( + [ + ( + "Environment", + agent_name, + self.environment_messages[agent_name].to_natural_language(), + ) + for agent_name in self.env.agents + ] + ) + for index, agent_name in enumerate(self.env.agents): + self.agents[agent_name].goal = self.env.profile.agent_goals[index] + + async def arun(self) -> AsyncGenerator[dict[str, Any], None]: + # Use sotopia to run the simulation + generator = arun_one_episode( + env=self.env, + agent_list=list(self.agents.values()), + push_to_db=False, + streaming=True, + ) + + assert isinstance( + generator, AsyncGenerator + ), "generator should be async generator" + + async for messages in await generator: # type: ignore + reasoning, rewards = "", [0.0, 0.0] + eval_available = False + if messages[-1][0][0] == "Evaluation": + reasoning = messages[-1][0][2].to_natural_language() + rewards = eval(messages[-2][0][2].to_natural_language()) + eval_available = True + + epilog = EpisodeLog( + environment=self.env.profile.pk, + agents=[agent.profile.pk for agent in self.agents.values()], + tag="test", + models=["gpt-4o", "gpt-4o", "gpt-4o-mini"], + messages=[ + [(m[0], m[1], m[2].to_natural_language()) for m in messages_in_turn] + for messages_in_turn in messages + ], + reasoning=reasoning, + rewards=rewards, + rewards_prompt="", + ) + agent_profiles, parsed_messages = epilog.render_for_humans() + if not eval_available: + parsed_messages = parsed_messages[:-2] + + yield { + "type": "messages", + "messages": parsed_messages, + } diff --git a/uv.lock b/uv.lock index 5017e0e0..71152b36 100644 --- a/uv.lock +++ b/uv.lock @@ -115,6 +115,51 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f5/76/a57ceff577ae26fe9a6f31ac799bc638ecf26e4acdf04295290b9929b349/aiohttp-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d18a8b44ec8502a7fde91446cd9c9b95ce7c49f1eacc1fb2358b8907d4369fd", size = 1690038 }, { url = "https://files.pythonhosted.org/packages/4b/81/b20e09003b6989a7f23a721692137a6143420a151063c750ab2a04878e3c/aiohttp-3.11.7-cp312-cp312-win32.whl", hash = "sha256:3d1c9c15d3999107cbb9b2d76ca6172e6710a12fda22434ee8bd3f432b7b17e8", size = 409887 }, { url = "https://files.pythonhosted.org/packages/b7/0b/607c98bff1d07bb21e0c39e7711108ef9ff4f2a361a3ec1ce8dce93623a5/aiohttp-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:018f1b04883a12e77e7fc161934c0f298865d3a484aea536a6a2ca8d909f0ba0", size = 436462 }, + { url = "https://files.pythonhosted.org/packages/3d/dd/3d40c0e67e79c5c42671e3e268742f1ff96c6573ca43823563d01abd9475/aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f", size = 586969 }, + { url = "https://files.pythonhosted.org/packages/75/64/8de41b5555e5b43ef6d4ed1261891d33fe45ecc6cb62875bfafb90b9ab93/aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9", size = 399367 }, + { url = "https://files.pythonhosted.org/packages/96/36/27bd62ea7ce43906d1443a73691823fc82ffb8fa03276b0e2f7e1037c286/aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8", size = 390720 }, + { url = "https://files.pythonhosted.org/packages/e8/4d/d516b050d811ce0dd26325c383013c104ffa8b58bd361b82e52833f68e78/aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1", size = 1228820 }, + { url = "https://files.pythonhosted.org/packages/53/94/964d9327a3e336d89aad52260836e4ec87fdfa1207176550fdf384eaffe7/aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a", size = 1264616 }, + { url = "https://files.pythonhosted.org/packages/0c/20/70ce17764b685ca8f5bf4d568881b4e1f1f4ea5e8170f512fdb1a33859d2/aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd", size = 1298402 }, + { url = "https://files.pythonhosted.org/packages/d1/d1/5248225ccc687f498d06c3bca5af2647a361c3687a85eb3aedcc247ee1aa/aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026", size = 1222205 }, + { url = "https://files.pythonhosted.org/packages/f2/a3/9296b27cc5d4feadf970a14d0694902a49a985f3fae71b8322a5f77b0baa/aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b", size = 1193804 }, + { url = "https://files.pythonhosted.org/packages/d9/07/f3760160feb12ac51a6168a6da251a4a8f2a70733d49e6ceb9b3e6ee2f03/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d", size = 1193544 }, + { url = "https://files.pythonhosted.org/packages/7e/4c/93a70f9a4ba1c30183a6dd68bfa79cddbf9a674f162f9c62e823a74a5515/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7", size = 1193047 }, + { url = "https://files.pythonhosted.org/packages/ff/a3/36a1e23ff00c7a0cd696c5a28db05db25dc42bfc78c508bd78623ff62a4a/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a", size = 1247201 }, + { url = "https://files.pythonhosted.org/packages/55/ae/95399848557b98bb2c402d640b2276ce3a542b94dba202de5a5a1fe29abe/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc", size = 1264102 }, + { url = "https://files.pythonhosted.org/packages/38/f5/02e5c72c1b60d7cceb30b982679a26167e84ac029fd35a93dd4da52c50a3/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68", size = 1215760 }, + { url = "https://files.pythonhosted.org/packages/30/17/1463840bad10d02d0439068f37ce5af0b383884b0d5838f46fb027e233bf/aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257", size = 362678 }, + { url = "https://files.pythonhosted.org/packages/dd/01/a0ef707d93e867a43abbffee3a2cdf30559910750b9176b891628c7ad074/aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6", size = 381097 }, + { url = "https://files.pythonhosted.org/packages/72/31/3c351d17596194e5a38ef169a4da76458952b2497b4b54645b9d483cbbb0/aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f", size = 586501 }, + { url = "https://files.pythonhosted.org/packages/a4/a8/a559d09eb08478cdead6b7ce05b0c4a133ba27fcdfa91e05d2e62867300d/aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb", size = 398993 }, + { url = "https://files.pythonhosted.org/packages/c5/47/7736d4174613feef61d25332c3bd1a4f8ff5591fbd7331988238a7299485/aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871", size = 390647 }, + { url = "https://files.pythonhosted.org/packages/27/21/e9ba192a04b7160f5a8952c98a1de7cf8072ad150fa3abd454ead1ab1d7f/aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c", size = 1306481 }, + { url = "https://files.pythonhosted.org/packages/cf/50/f364c01c8d0def1dc34747b2470969e216f5a37c7ece00fe558810f37013/aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38", size = 1344652 }, + { url = "https://files.pythonhosted.org/packages/1d/c2/74f608e984e9b585649e2e83883facad6fa3fc1d021de87b20cc67e8e5ae/aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb", size = 1378498 }, + { url = "https://files.pythonhosted.org/packages/9f/a7/05a48c7c0a7a80a5591b1203bf1b64ca2ed6a2050af918d09c05852dc42b/aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7", size = 1292718 }, + { url = "https://files.pythonhosted.org/packages/7d/78/a925655018747e9790350180330032e27d6e0d7ed30bde545fae42f8c49c/aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911", size = 1251776 }, + { url = "https://files.pythonhosted.org/packages/47/9d/85c6b69f702351d1236594745a4fdc042fc43f494c247a98dac17e004026/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092", size = 1271716 }, + { url = "https://files.pythonhosted.org/packages/7f/a7/55fc805ff9b14af818903882ece08e2235b12b73b867b521b92994c52b14/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142", size = 1266263 }, + { url = "https://files.pythonhosted.org/packages/1f/ec/d2be2ca7b063e4f91519d550dbc9c1cb43040174a322470deed90b3d3333/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9", size = 1321617 }, + { url = "https://files.pythonhosted.org/packages/c9/a3/b29f7920e1cd0a9a68a45dd3eb16140074d2efb1518d2e1f3e140357dc37/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1", size = 1339227 }, + { url = "https://files.pythonhosted.org/packages/8a/81/34b67235c47e232d807b4bbc42ba9b927c7ce9476872372fddcfd1e41b3d/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a", size = 1299068 }, + { url = "https://files.pythonhosted.org/packages/04/1f/26a7fe11b6ad3184f214733428353c89ae9fe3e4f605a657f5245c5e720c/aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94", size = 362223 }, + { url = "https://files.pythonhosted.org/packages/10/91/85dcd93f64011434359ce2666bece981f08d31bc49df33261e625b28595d/aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959", size = 381576 }, + { url = "https://files.pythonhosted.org/packages/ae/99/4c5aefe5ad06a1baf206aed6598c7cdcbc7c044c46801cd0d1ecb758cae3/aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c", size = 583536 }, + { url = "https://files.pythonhosted.org/packages/a9/36/8b3bc49b49cb6d2da40ee61ff15dbcc44fd345a3e6ab5bb20844df929821/aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28", size = 395693 }, + { url = "https://files.pythonhosted.org/packages/e1/77/0aa8660dcf11fa65d61712dbb458c4989de220a844bd69778dff25f2d50b/aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f", size = 390898 }, + { url = "https://files.pythonhosted.org/packages/38/d2/b833d95deb48c75db85bf6646de0a697e7fb5d87bd27cbade4f9746b48b1/aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138", size = 1312060 }, + { url = "https://files.pythonhosted.org/packages/aa/5f/29fd5113165a0893de8efedf9b4737e0ba92dfcd791415a528f947d10299/aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742", size = 1350553 }, + { url = "https://files.pythonhosted.org/packages/ad/cc/f835f74b7d344428469200105236d44606cfa448be1e7c95ca52880d9bac/aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7", size = 1392646 }, + { url = "https://files.pythonhosted.org/packages/bf/fe/1332409d845ca601893bbf2d76935e0b93d41686e5f333841c7d7a4a770d/aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16", size = 1306310 }, + { url = "https://files.pythonhosted.org/packages/e4/a1/25a7633a5a513278a9892e333501e2e69c83e50be4b57a62285fb7a008c3/aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8", size = 1260255 }, + { url = "https://files.pythonhosted.org/packages/f2/39/30eafe89e0e2a06c25e4762844c8214c0c0cd0fd9ffc3471694a7986f421/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6", size = 1271141 }, + { url = "https://files.pythonhosted.org/packages/5b/fc/33125df728b48391ef1fcb512dfb02072158cc10d041414fb79803463020/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a", size = 1280244 }, + { url = "https://files.pythonhosted.org/packages/3b/61/e42bf2c2934b5caa4e2ec0b5e5fd86989adb022b5ee60c2572a9d77cf6fe/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9", size = 1316805 }, + { url = "https://files.pythonhosted.org/packages/18/32/f52a5e2ae9ad3bba10e026a63a7a23abfa37c7d97aeeb9004eaa98df3ce3/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a", size = 1343930 }, + { url = "https://files.pythonhosted.org/packages/05/be/6a403b464dcab3631fe8e27b0f1d906d9e45c5e92aca97ee007e5a895560/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205", size = 1306186 }, + { url = "https://files.pythonhosted.org/packages/8e/fd/bb50fe781068a736a02bf5c7ad5f3ab53e39f1d1e63110da6d30f7605edc/aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628", size = 359289 }, + { url = "https://files.pythonhosted.org/packages/70/9e/5add7e240f77ef67c275c82cc1d08afbca57b77593118c1f6e920ae8ad3f/aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf", size = 379313 }, ] [[package]] @@ -3166,6 +3211,7 @@ requires-dist = [ { name = "pytest-asyncio", marker = "extra == 'test'" }, { name = "pytest-cov", marker = "extra == 'test'" }, { name = "redis-om", specifier = ">=0.3.0,<0.4.0" }, + { name = "rel", marker = "extra == 'chat'" }, { name = "rich", specifier = ">=13.6.0,<14.0.0" }, { name = "scipy", marker = "extra == 'examples'" }, { name = "together", specifier = ">=0.2.4,<1.4.0" }, From 5a9f4b75f428d7f045ef0baa009d56b96f0ed9be Mon Sep 17 00:00:00 2001 From: Zhe Su <360307598@qq.com> Date: Sun, 8 Dec 2024 15:50:08 -0500 Subject: [PATCH 02/26] Add customizable evaluation dimensions (#256) * add customizable evaluation dimensions * add docs * fix mypy error & refactor examples * add docs for evaluation dimensions * update docs and examples * add test cases and fix mypy issue * fix mypy issue * Fix test_create_custom_dimension to use CustomEvaluationDimension.get(pk) (#262) Co-authored-by: openhands * Fix/custom eval dimension test (#263) * Fix test_create_custom_dimension to use CustomEvaluationDimension.get(pk) * Update documentation for SotopiaDimension and EvaluationDimensionBuilder * [autofix.ci] apply automated fixes * Add API documentation for evaluation dimensions * Refine API documentation for evaluation_dimensions.py to match style * [autofix.ci] apply automated fixes --------- Co-authored-by: openhands Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * add doc --------- Co-authored-by: XuhuiZhou Co-authored-by: openhands Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- docs/pages/concepts/evaluation_dimension.md | 116 +++++++++ .../database/evaluation_dimensions.md | 54 ++++ examples/experiment_eval.py | 17 +- examples/use_custom_dimensions.py | 234 ++++++++++++++++++ sotopia/database/__init__.py | 8 + sotopia/database/evaluation_dimensions.py | 147 +++++++++++ tests/database/test_database.py | 20 ++ 7 files changed, 595 insertions(+), 1 deletion(-) create mode 100644 docs/pages/concepts/evaluation_dimension.md create mode 100644 docs/pages/python_API/database/evaluation_dimensions.md create mode 100644 examples/use_custom_dimensions.py create mode 100644 sotopia/database/evaluation_dimensions.py diff --git a/docs/pages/concepts/evaluation_dimension.md b/docs/pages/concepts/evaluation_dimension.md new file mode 100644 index 00000000..f86b7a89 --- /dev/null +++ b/docs/pages/concepts/evaluation_dimension.md @@ -0,0 +1,116 @@ +## Overview + +Evaluation dimensions are used to evaluate the quality of social interactions. +In original Sotopia paper, there are 7 dimensions to evaluate the quality of social interactions, where we named them as `sotopia` evaluation dimensions: +- believability +- relationship +- knowledge +- secret +- social rules +- financial and material benefits +- goal + +The `SotopiaDimensions` can be used directly without initializing the database. It provides a set of predefined evaluation dimensions that are ready to use for evaluating social interactions. For example, + +```python +from sotopia.envs.parallel import ParallelSotopiaEnv +from sotopia.envs.evaluators import EvaluationForTwoAgents, ReachGoalLLMEvaluator, RuleBasedTerminatedEvaluator, SotopiaDimensions + +env = ParallelSotopiaEnv( + env_profile=env_profile, + model_name=model_names["env"], + action_order="round-robin", + evaluators=[ + RuleBasedTerminatedEvaluator(max_turn_number=20, max_stale_turn=2), + ], + terminal_evaluators=[ + ReachGoalLLMEvaluator( + model_names["env"], + EvaluationForTwoAgents[SotopiaDimensions], # type: ignore + # TODO check how to do type annotation + ), + ], + ) +``` + + +However we observe under many use cases people may want to evaluate with customized evaluation metrics, so we provide a way to build custom evaluation dimensions. +For a quick reference, you can directly check out the `examples/use_custom_dimensions.py`. + +### CustomEvaluationDimension +The [`CustomEvaluationDimension`](/python_API/database/evaluation_dimensions) is a class that can be used to create a custom evaluation dimension. +There are four parameters: +- name: the name of the dimension +- description: the description of the dimension +- range_low: the minimum score of the dimension (should be an integer) +- range_high: the maximum score of the dimension (should be an integer) + +### CustomEvaluationDimensionList +The [`CustomEvaluationDimensionList`](/python_API/database/evaluation_dimensions) is a class that can be used to create a custom evaluation dimension list based on the existing dimensions. It helps one to group multiple dimensions together for a specific use case. +There are two parameters: +- name: the name of the dimension list +- dimension_pks: the primary keys of the dimensions in the dimension list + +### EvaluationDimensionBuilder +The [`EvaluationDimensionBuilder`](/python_API/database/evaluation_dimensions) is a class that can be used to generate a custom evaluation dimension model based on the existing dimensions. + + +## Usage +### Initialize the database +The default evaluation metric is still `SotopiaDimensions` in `sotopia.env.evaluators`.There is no `CustomEvaluationDimension` in the database by default. To initialize the database, please refer to `examples/use_custom_dimensions.py`. + + +### Use the custom evaluation dimensions +After you initialize your customized evaluation dimensions, you can choose to use any one of these methods provided below: + +#### Method 1: Choose dimensions by names +```python +evaluation_dimensions = ( + EvaluationDimensionBuilder.select_existing_dimension_model_by_name( + ["transactivity", "verbal_equity"] + ) +) +``` + +#### Method 2: Directly choose the grouped evaluation dimension list +```python +evaluation_dimensions = ( + EvaluationDimensionBuilder.select_existing_dimension_model_by_list_name( + "sotopia" + ) +) +``` + +#### Method 3: Build a custom evaluation dimension model temporarily +We provide multiple ways to build a custom evaluation dimension model with `EvaluationDimensionBuilder`, specifically: +- `generate_dimension_model`: build an evaluation dimension from existing dimension primary keys. +- `generate_dimension_model_from_dict`: build an evaluation dimension from a dictionary that specifies the parameters of the `CustomEvaluationDimension`. For example +```json +[ + { + "name": "believability", + "description": "The believability of the interaction", + "range_low": 0, + "range_high": 10 + }, + ... +] +``` +- `select_existing_dimension_model_by_name`: build an evaluation dimension from existing dimension names. For example `['believability', 'goal']` +- `select_existing_dimension_model_by_list_name`: build an evaluation dimension from existing `CustomEvaluationDimensionList` list names. For example, directly use `sotopia`. + + +After you get the evaluation dimension model, you can pass it as a parameter for the `Evaluator`, for example, +```python +evaluation_dimensions = ( + EvaluationDimensionBuilder.select_existing_dimension_model_by_list_name( + "sotopia" + ) +) +terminal_evaluators=[ + ReachGoalLLMEvaluator( + model_names["env"], + EvaluationForTwoAgents[evaluation_dimensions], # type: ignore + ), +], +``` diff --git a/docs/pages/python_API/database/evaluation_dimensions.md b/docs/pages/python_API/database/evaluation_dimensions.md new file mode 100644 index 00000000..4a826a55 --- /dev/null +++ b/docs/pages/python_API/database/evaluation_dimensions.md @@ -0,0 +1,54 @@ +# `evaluation_dimensions.py` + +This module provides classes and utilities for defining and managing custom evaluation dimensions within the Sotopia environment. It includes classes for individual dimensions, lists of dimensions, and a builder for creating dimension models. + +## Classes + +### `CustomEvaluationDimension` + +Represents a custom evaluation dimension with specific attributes such as name, description, and score range. + +#### Attributes +- `name`: `str`. The name of the dimension. +- `description`: `str`. A brief description of the dimension. +- `range_low`: `int`. The minimum score for the dimension. +- `range_high`: `int`. The maximum score for the dimension. + +### `CustomEvaluationDimensionList` + +Groups multiple custom evaluation dimensions together. + +#### Attributes +- `name`: `str`. The name of the dimension list. +- `dimension_pks`: `list[str]`. A list of primary keys for the dimensions included in the list. + +### `EvaluationDimensionBuilder` + +Provides utility methods to create and manage evaluation dimension models. + +#### Methods +- `create_range_validator(low: int, high: int)`: Creates a validator for score ranges. + + **Arguments:** + - `low`: `int`. The minimum score allowed. + - `high`: `int`. The maximum score allowed. + +- `build_dimension_model(dimension_ids: list[str])`: Builds a dimension model from primary keys. + + **Arguments:** + - `dimension_ids`: `list[str]`. A list of dimension primary keys. + +- `build_dimension_model_from_dict(dimensions: list[dict[str, Union[str, int]]])`: Builds a dimension model from a dictionary. + + **Arguments:** + - `dimensions`: `list[dict[str, Union[str, int]]]`. A list of dictionaries specifying dimension attributes. + +- `select_existing_dimension_model_by_name(dimension_names: list[str])`: Selects a dimension model by dimension names. + + **Arguments:** + - `dimension_names`: `list[str]`. A list of dimension names. + +- `select_existing_dimension_model_by_list_name(list_name: str)`: Selects a dimension model by list name. + + **Arguments:** + - `list_name`: `str`. The name of the dimension list. diff --git a/examples/experiment_eval.py b/examples/experiment_eval.py index ee0df3f1..82fe4bbd 100644 --- a/examples/experiment_eval.py +++ b/examples/experiment_eval.py @@ -17,6 +17,7 @@ EnvAgentComboStorage, EnvironmentProfile, EpisodeLog, + EvaluationDimensionBuilder, ) from sotopia.envs.evaluators import ( EvaluationForTwoAgents, @@ -34,6 +35,7 @@ ) from sotopia.server import run_async_server from sotopia_conf.gin_utils import parse_gin_flags, run +# from sotopia.database import EvaluationDimensionBuilder _DEFAULT_GIN_SEARCH_PATHS = [ os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -109,6 +111,18 @@ def _iterate_env_agent_combo_not_in_db( tag: str | None = None, ) -> Generator[EnvAgentCombo[Observation, AgentAction], None, None]: """We iterate over each environment and return the **first** env-agent combo that is not in the database.""" + # loading evaluation metric + try: + evaluation_dimensions = EvaluationDimensionBuilder.select_existing_dimension_model_by_list_name( + "sotopia" + ) # Initialize your customized dimension, please refer to `examples/use_custom_dimensions.py` + except Exception as e: + print( + "No customized evaluation dimensions found, using default SotopiaDimensions", + e, + ) + evaluation_dimensions = SotopiaDimensions + if not env_ids: env_ids = list(EnvironmentProfile.all_pks()) for env_id in env_ids: @@ -152,7 +166,8 @@ def _iterate_env_agent_combo_not_in_db( terminal_evaluators=[ ReachGoalLLMEvaluator( model_names["env"], - EvaluationForTwoAgents[SotopiaDimensions], + EvaluationForTwoAgents[evaluation_dimensions], # type: ignore + # TODO check how to do type annotation ), ], ) diff --git a/examples/use_custom_dimensions.py b/examples/use_custom_dimensions.py new file mode 100644 index 00000000..0bbdfda8 --- /dev/null +++ b/examples/use_custom_dimensions.py @@ -0,0 +1,234 @@ +from pydantic import BaseModel +from sotopia.database import ( + EvaluationDimensionBuilder, + CustomEvaluationDimensionList, + CustomEvaluationDimension, +) +from typing import Type, Union +from redis_om import Migrator +from sotopia.envs.evaluators import ( + ReachGoalLLMEvaluator, + EvaluationForTwoAgents, + RuleBasedTerminatedEvaluator, +) +from sotopia.server import arun_one_episode +from typing import Optional, cast +from sotopia.envs import ParallelSotopiaEnv +from sotopia.agents import LLMAgent +from sotopia.database import AgentProfile, EnvironmentProfile +import asyncio + + +def save_dimensions(dimensions: list[dict[str, Union[str, int]]]) -> None: + Migrator().run() + for dimension in dimensions: + if ( + len( + CustomEvaluationDimension.find( + CustomEvaluationDimension.name == dimension["name"] + ).all() + ) + == 0 + ): + print("No existing dimension found, creating a new one") + CustomEvaluationDimension(**dimension).save() + print("Saved {}".format(dimension["name"])) + else: + print( + CustomEvaluationDimension.find( + CustomEvaluationDimension.name == dimension["name"] + ).all()[0], + "already exists", + ) + + +def save_dimension_list( + dimensions: list[dict[str, Union[str, int]]], list_name: str +) -> None: + Migrator().run() + dimension_list = CustomEvaluationDimensionList.find( + CustomEvaluationDimensionList.name == list_name + ).all() + + if len(dimension_list) == 0: + all_dimensions_pks = [] + for dimension in dimensions: + find_dimension = CustomEvaluationDimension.find( + CustomEvaluationDimension.name == dimension["name"] + ).all() + assert ( + len(find_dimension) == 1 + ), f"Expected 1 dimension for {dimension['name']}, but found {len(find_dimension)}" + all_dimensions_pks.append(find_dimension[0].pk) + CustomEvaluationDimensionList( + name=list_name, dimension_pks=all_dimensions_pks + ).save() + print("Saved {}".format(list_name)) + else: + print(dimension_list[0], "already exists") + + +def build_sotopia_dimensions() -> Type[BaseModel]: + """ + Build the default Sotopia dimensions, the same as + `sotopia.env.evaluators.SotopiaDimensions` + """ + sotopia_dimensions: list[dict[str, Union[str, int]]] = [ + { + "name": "believability", + "description": "Reasoning requirement: 1. Evaluate if the agent interacts with others in a natural and realistic manner (here are a few common questions to check: a. whether the agent is confusing with its own identity? b. whether the agent repeats others' words/actions without any reason? c. whether the agent is being overly polite considering the context?). Start the analysis with tag 2. Analyze whether the actions of the agent align with their character traits (e.g., personality, values, and etc.). Start the analysis with tag . Output your reasoning process to the 'reasoning' field. Output an integer score ranging from 0 and 10 in the 'score' field. A higher score indicates that the agent is more believable.", + "range_high": 10, + "range_low": 0, + }, + { + "name": "relationship", + "description": "Please first analyze what relationship the participant has with the other agent(s) before the interaction. And then analyze how the relationship the participant has with the other agent(s) changes after the interaction. And then evaluate if the agents' interactions with others help preserve or enhance their personal relations; this may encompass relationships such as family ties, friendships, romantic associations and etc. Additionally, ascertain whether these interactions also impact their social status or reputation. In the 'reasoning' field, provide a comprehensive account of the logic or thought process that led you to your conclusion. Further, provide an integer score ranging from -5 to 5 in the 'score' field. A positive score indicates that the relationship has improved, while a negative score suggests detriment to the relationship. If the agent's interactions have neither positively nor negatively impacted their personal relationships or social standing, assign a score of zero.", + "range_high": 5, + "range_low": -5, + }, + { + "name": "knowledge", + "description": "Please first assess what information the agent has gained through the interaction. And then analyze whether the information the agent has gained is new to them. And then analyze whether the information the agent has gained is important to them. In the 'reasoning' field, provide a comprehensive account of the logic or thought process that led you to your conclusion. Further, provide an integer score ranging from 0 and 10 in the 'score' field. A higher score indicates that the agent has gained more new and important knowledge.", + "range_high": 10, + "range_low": 0, + }, + { + "name": "secret", + "description": "First please analyze what secret or secretive intention the participant wants to keep. And then analyze whether they keep it successfully. Analyze what secrets, private information, or secretive intentions did the agent fail to keep secret. Please output the reasoning in the reasoning field. Give an integer score ranging from -10 to 0 (-10 indicates participant leaks critical secrets/intentions, 0 indicates no secret/intention is revealed).", + "range_high": 0, + "range_low": -10, + }, + { + "name": "social_rules", + "description": "Please evaluate whether the agent violates any moral rules or laws in their interactions with others. In the 'reasoning' field, provide a comprehensive account of the logic or thought process that led you to your conclusion. Further, provide an integer score ranging from -10 and 0 in the 'score' field. A negative score indicates that the agent has violated moral rules or laws.", + "range_high": 0, + "range_low": -10, + }, + { + "name": "financial_and_material_benefits", + "description": "Please evaluate whether the agent's interactions with others contribute towards financial and material benefits. Analyze what the agent would gain/lose after the interactions. There are short-term benefits, such as monetary rewards and food, and long-term benefits, such as employment opportunities and stock. In the 'reasoning' field, provide a comprehensive account of the logic or thought process that led you to your conclusion. Further, provide an integer score ranging from -5 and 5 in the 'score' field. Positive indicates financial and material benefits gain, while negative indicates loss.", + "range_high": 5, + "range_low": -5, + }, + { + "name": "goal", + "description": "Please first reiterate agent's social goals. And then please provide a comprehensive analysis about the extent to which the agent has managed to achieve these goals. In the 'reasoning' field, provide a comprehensive account of the logic or thought process that led you to your conclusion. Further, provide an integer score ranging from 0 and 10 in the 'score' field. 0 represents minimal goals achievement, 10 represents complete goal achievement, and a higher score indicates that the agent is making progress towards their social goals.", + "range_high": 10, + "range_low": 0, + }, + ] + + dimensions = EvaluationDimensionBuilder.build_dimension_model_from_dict( + dimensions=sotopia_dimensions + ) + save_dimensions(sotopia_dimensions) + save_dimension_list(sotopia_dimensions, "sotopia") + + return dimensions + + +def build_custom_dimensions( + custom_dimensions: list[dict[str, Union[str, int]]], list_name: Optional[str] = None +) -> Type[BaseModel]: + """ + Build a custom evaluation dimension model, + : param custom_dimensions: a list of dictionaries that specify the parameters of the `CustomEvaluationDimension`. + : param list_name: the name of the list to save the custom dimensions to. If None, no list will be saved. + """ + dimensions = EvaluationDimensionBuilder.build_dimension_model_from_dict( + dimensions=custom_dimensions + ) + + save_dimensions(custom_dimensions) + if list_name is not None: + save_dimension_list(custom_dimensions, list_name=list_name) + + return dimensions + + +def run_simple_sample_with_custom_samples( + custom_dimensions: list[dict[str, Union[str, int]]], +) -> None: + custom_dimensions_type = build_custom_dimensions( + custom_dimensions, list_name="custom" + ) + evaluator = RuleBasedTerminatedEvaluator(max_turn_number=10, max_stale_turn=2) + terminal_evaluator = ReachGoalLLMEvaluator( + model_name="gpt-4o-mini", + response_format_class=EvaluationForTwoAgents[custom_dimensions_type], # type: ignore + ) + + all_agents: list[AgentProfile] = cast( + list[AgentProfile], + AgentProfile.find().page(0, 2), # type: ignore + ) + all_envs: list[EnvironmentProfile] = cast( + list[EnvironmentProfile], + EnvironmentProfile.find().page(0, 1), # type: ignore + ) + environment: ParallelSotopiaEnv = ParallelSotopiaEnv( + env_profile=all_envs[0], + model_name="gpt-4o-mini", + action_order="round-robin", + evaluators=[evaluator], + terminal_evaluators=[terminal_evaluator], + ) + agents: list[LLMAgent] = [ + LLMAgent(agent_profile=agent_profile, model_name="gpt-4o-mini") + for agent_profile in all_agents[:2] + ] + + res = asyncio.run( + arun_one_episode( + env=environment, + agent_list=agents, + omniscient=False, + script_like=False, + tag=None, + push_to_db=False, + ) + ) + + print(res) + + +if __name__ == "__main__": + """ + A sample dimension: + custom_dimensions: list[dict[str, Union[str, int]]] = [ + { + "name": "transactivity", + "description": "Analyze the provided social interaction episode between the given pair/team, focusing on identifying instances of transactive exchanges. Evaluate the level of transactivity by considering the following aspects: elaboration, building upon ideas, questioning, argumentation. Analyze whether these transactive patterns persist consistently across the entire interaction or if there are notable variations throughout the exchange. In the 'reasoning' field, provide a comprehensive account of the logic and thought process that led to your conclusion. Consider how the observed instances of transactivity contribute to or detract from the overall quality and depth of the interaction. In the 'score' field, provide an integer score ranging from 0 to 10, where a higher score indicates a higher level of transactivity.", + "range_high": 10, + "range_low": 0, + }, + { + "name": "verbal_equity", + "description": "Analyze the script and measure the level of verbal equity reflected in the interaction between the agents. And then analyze the extent to which the interaction shows a balanced distribution of speaking opportunities among team members. In the 'reasoning' field, provide a comprehensive account of the logic or thought process that led you to your conclusion. Further, provide an integer score ranging from 0 and 10 in the 'score' field. A higher score indicates a higher level of verbal equity.", + "range_high": 10, + "range_low": 0, + }, + ] + """ + + custom_dimensions: list[dict[str, Union[str, int]]] = [ + { + "name": "transactivity", + "description": "Analyze the provided social interaction episode between the given pair/team, focusing on identifying instances of transactive exchanges. Evaluate the level of transactivity by considering the following aspects: elaboration, building upon ideas, questioning, argumentation. Analyze whether these transactive patterns persist consistently across the entire interaction or if there are notable variations throughout the exchange. In the 'reasoning' field, provide a comprehensive account of the logic and thought process that led to your conclusion. Consider how the observed instances of transactivity contribute to or detract from the overall quality and depth of the interaction. In the 'score' field, provide an integer score ranging from 0 to 10, where a higher score indicates a higher level of transactivity.", + "range_high": 10, + "range_low": 0, + }, + { + "name": "verbal_equity", + "description": "Analyze the script and measure the level of verbal equity reflected in the interaction between the agents. And then analyze the extent to which the interaction shows a balanced distribution of speaking opportunities among team members. In the 'reasoning' field, provide a comprehensive account of the logic or thought process that led you to your conclusion. Further, provide an integer score ranging from 0 and 10 in the 'score' field. A higher score indicates a higher level of verbal equity.", + "range_high": 10, + "range_low": 0, + }, + ] + + # Only build evaluation dimensions + build_sotopia_dimensions() + build_custom_dimensions(custom_dimensions=custom_dimensions, list_name="custom") + + # Build and use evaluation dimensions + run_simple_sample_with_custom_samples(custom_dimensions=custom_dimensions) diff --git a/sotopia/database/__init__.py b/sotopia/database/__init__.py index bd737855..07f02c1d 100644 --- a/sotopia/database/__init__.py +++ b/sotopia/database/__init__.py @@ -30,6 +30,11 @@ from .session_transaction import MessageTransaction, SessionTransaction from .waiting_room import MatchingInWaitingRoom from .aggregate_annotations import map_human_annotations_to_episode_logs +from .evaluation_dimensions import ( + EvaluationDimensionBuilder, + CustomEvaluationDimension, + CustomEvaluationDimensionList, +) from logging import Logger @@ -65,6 +70,9 @@ "jsonl_to_relationshipprofiles", "jsonl_to_envagnetcombostorage", "get_rewards_from_episode", + "EvaluationDimensionBuilder", + "CustomEvaluationDimension", + "CustomEvaluationDimensionList", ] InheritedJsonModel = TypeVar("InheritedJsonModel", bound="JsonModel") diff --git a/sotopia/database/evaluation_dimensions.py b/sotopia/database/evaluation_dimensions.py new file mode 100644 index 00000000..4b2a2c2a --- /dev/null +++ b/sotopia/database/evaluation_dimensions.py @@ -0,0 +1,147 @@ +from redis_om import JsonModel +from redis_om.model.model import Field +from pydantic import BaseModel, create_model +from typing import Type, Callable, Tuple, Annotated, Union, cast, Any + + +class CustomEvaluationDimension(JsonModel): + name: str = Field(index=True) + description: str = Field(index=True) + range_high: int = Field(index=True) + range_low: int = Field(index=True) + + +class CustomEvaluationDimensionList(JsonModel): + name: str = Field(index=True) + dimension_pks: list[str] = Field(default_factory=lambda: [], index=True) + + +class EvaluationDimensionBuilder: + """ + EvaluationDimensionBuilder is a utility class for creating and managing evaluation dimensions. + It provides methods to build evaluation dimension models from various inputs such as primary keys, dictionaries, and names. + """ + + @staticmethod + def create_range_validator( + low: int, high: int + ) -> Callable[[Tuple[str, int]], Tuple[str, int]]: + def validator(x: Tuple[str, int]) -> Tuple[str, int]: + if not isinstance(x, tuple) or len(x) != 2: + raise ValueError("Must be a tuple of (str, int)") + if not isinstance(x[1], int) or not low <= x[1] <= high: + raise ValueError(f"Score must be between {low} and {high}") + return x + + return validator + + @staticmethod + def build_dimension_model(dimension_ids: list[str]) -> Type[BaseModel]: + """ + Build an evaluation dimension from existing dimension primary keys. + The returned model is a pydantic model that can be used to evaluate the conversation. + """ + fields: dict[str, Any] = {} + + for dimension_id in dimension_ids: + dimension = CustomEvaluationDimension.get(dimension_id) + range_validator = EvaluationDimensionBuilder.create_range_validator( + dimension.range_low, dimension.range_high + ) + field_type = Annotated[Tuple[str, int], range_validator] + + fields[dimension.name] = ( + field_type, + Field(..., description=dimension.description), + ) + + model: Type[BaseModel] = create_model( + "CustomEvaluationDimensionModel", + __base__=BaseModel, + **fields, + ) + return model + + @staticmethod + def build_dimension_model_from_dict( + dimensions: list[dict[str, Union[str, int]]], + ) -> Type[BaseModel]: + """ + Build an evaluation dimension from a dictionary that specifies the parameters of the `CustomEvaluationDimension`. + The returned model is a pydantic model that can be used to evaluate the conversation. + """ + fields: dict[str, Any] = {} + for dimension_dict in dimensions: + dimension = CustomEvaluationDimension(**dimension_dict) + range_validator = EvaluationDimensionBuilder.create_range_validator( + dimension.range_low, dimension.range_high + ) + field_type = Annotated[Tuple[str, int], range_validator] + + fields[dimension.name] = ( + field_type, + Field(..., description=dimension.description), + ) + + dimension_model = create_model( + "CustomEvaluationDimensionModel", + __base__=BaseModel, + **fields, + ) + return dimension_model + + @staticmethod + def select_existing_dimension_model_by_name( + dimension_names: list[str], + ) -> Type[BaseModel]: + """ + Build an evaluation dimension from existing dimension names. For example `['believability', 'goal']` + The returned model is a pydantic model that can be used to evaluate the conversation. + """ + fields: dict[str, Any] = {} + for dimension_name in dimension_names: + dimensions = CustomEvaluationDimension.find( + CustomEvaluationDimension.name == dimension_name + ).all() + assert ( + len(dimensions) == 1 + ), f"Expected 1 dimension for {dimension_name}, but found {len(dimensions)}" + dimension = cast(CustomEvaluationDimension, dimensions[0]) + range_validator = EvaluationDimensionBuilder.create_range_validator( + dimension.range_low, dimension.range_high + ) + field_type = Annotated[Tuple[str, int], range_validator] + + fields[dimension.name] = ( + field_type, + Field(..., description=dimension.description), + ) + + model: Type[BaseModel] = create_model( + "CustomEvaluationDimensionModel", + __base__=BaseModel, + **fields, + ) + return model + + @staticmethod + def select_existing_dimension_model_by_list_name( + list_name: str, + ) -> Type[BaseModel]: + """ + Build an evaluation dimension from existing `CustomEvaluationDimensionList` list names. For example, directly use `sotopia` + The returned model is a pydantic model that can be used to evaluate the conversation. + """ + # if list_name == "sotopia": + # return SotopiaDimensions # TODO see if we could make this work in `experiment_eval.py`. Right now there is a circular import + + dimensions = CustomEvaluationDimensionList.find( + CustomEvaluationDimensionList.name == list_name + ).all() + assert ( + len(dimensions) == 1 + ), f"Expected 1 dimension list for {list_name}, but found {len(dimensions)}" + dimension_list = cast(CustomEvaluationDimensionList, dimensions[0]) + dimension_ids = dimension_list.dimension_pks + model = EvaluationDimensionBuilder.build_dimension_model(dimension_ids) + return model diff --git a/tests/database/test_database.py b/tests/database/test_database.py index 5279ecc9..142e2bd1 100644 --- a/tests/database/test_database.py +++ b/tests/database/test_database.py @@ -8,6 +8,7 @@ AgentProfile, EnvironmentProfile, EpisodeLog, + CustomEvaluationDimension, ) from sotopia.envs.parallel import ParallelSotopiaEnv from sotopia.messages import SimpleMessage @@ -42,6 +43,25 @@ def test_create_agent_profile() -> None: AgentProfile.delete(pk) +def test_create_custom_dimension() -> None: + custom_dimension = CustomEvaluationDimension( + name="verbosity_custom", + description="The verbosity of the conversation", + range_low=0, + range_high=10, + ) + custom_dimension.save() + pk = custom_dimension.pk + dimension = CustomEvaluationDimension.get(pk) + assert ( + dimension.name == custom_dimension.name + and dimension.description == custom_dimension.description + and dimension.range_low == custom_dimension.range_low + and dimension.range_high == custom_dimension.range_high + ) + CustomEvaluationDimension.delete(pk) + + @pytest.fixture def _test_create_episode_log_setup_and_tear_down() -> Generator[None, None, None]: AgentProfile(first_name="John", last_name="Doe", pk="tmppk_agent1").save() From dea25d39d8127090dc4d31bf9c548256029edfdf Mon Sep 17 00:00:00 2001 From: Xuhui Zhou Date: Wed, 11 Dec 2024 14:19:20 -0500 Subject: [PATCH 03/26] Feat/addtional fast apis for non-streaming simulation and managing relationshio (#265) * temp run * add relationship api * fix mypy error * update relationship api * simulate episode non-streaming * modify sim episodes * add simulation status * task error * add background task * [autofix.ci] apply automated fixes * back to arun one episode * upload the code * use rq to execute background tasks * temp sol --------- Co-authored-by: Hao Zhu Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../websocket/websocket_test_client.py | 18 +- examples/fast_api_example.py | 122 +++++++++ sotopia/database/__init__.py | 4 +- sotopia/database/logs.py | 7 +- sotopia/database/persistent_profile.py | 5 + sotopia/database/serialization.py | 2 +- sotopia/envs/parallel.py | 2 +- sotopia/server.py | 16 +- sotopia/ui/README.md | 11 + sotopia/ui/fastapi_server.py | 258 ++++++++++++++++-- stubs/redis_om/__init__.pyi | 3 + tests/ui/test_fastapi.py | 135 ++++++++- uv.lock | 46 ---- 13 files changed, 536 insertions(+), 93 deletions(-) create mode 100644 examples/fast_api_example.py diff --git a/examples/experimental/websocket/websocket_test_client.py b/examples/experimental/websocket/websocket_test_client.py index c1bd74b6..ab9f3027 100644 --- a/examples/experimental/websocket/websocket_test_client.py +++ b/examples/experimental/websocket/websocket_test_client.py @@ -12,8 +12,8 @@ class WebSocketClient: - def __init__(self, uri: str, token: str, client_id: int): - self.uri = uri + def __init__(self, url: str, token: str, client_id: int): + self.url = url self.token = token self.client_id = client_id self.message_file = Path(f"message_{client_id}.txt") @@ -25,11 +25,11 @@ async def save_message(self, message: str) -> None: async def connect(self) -> None: """Establish and maintain websocket connection""" - uri_with_token = f"{self.uri}?token=test_token_{self.client_id}" + url_with_token = f"{self.url}?token=test_token_{self.client_id}" try: - async with websockets.connect(uri_with_token) as websocket: - print(f"Client {self.client_id}: Connected to {self.uri}") + async with websockets.connect(url_with_token) as websocket: + print(f"Client {self.client_id}: Connected to {self.url}") # Send initial message # Note: You'll need to implement the logic to get agent_ids and env_id @@ -70,16 +70,16 @@ async def connect(self) -> None: async def main() -> None: # Create multiple WebSocket clients num_clients = 0 - uri = "ws://localhost:8800/ws/simulation" + url = "ws://localhost:8800/ws/simulation" # Create and store client instances clients = [ - WebSocketClient(uri=uri, token=f"test_token_{i}", client_id=i) + WebSocketClient(url=url, token=f"test_token_{i}", client_id=i) for i in range(num_clients) ] - clients.append(WebSocketClient(uri=uri, token="test_token_10", client_id=10)) + clients.append(WebSocketClient(url=url, token="test_token_10", client_id=10)) clients.append( - WebSocketClient(uri=uri, token="test_token_10", client_id=10) + WebSocketClient(url=url, token="test_token_10", client_id=10) ) # test duplicate token # Create tasks for each client diff --git a/examples/fast_api_example.py b/examples/fast_api_example.py new file mode 100644 index 00000000..f10b850b --- /dev/null +++ b/examples/fast_api_example.py @@ -0,0 +1,122 @@ +# Example curl command to call the simulate endpoint: +import requests +import time + +BASE_URL = "http://localhost:8080" + + +def _create_mock_agent_profile() -> None: + agent1_data = { + "first_name": "John", + "last_name": "Doe", + "occupation": "test_occupation", + "gender": "test_gender", + "pk": "tmppk_agent1", + "tag": "test_tag", + } + response = requests.post( + f"{BASE_URL}/agents/", + headers={"Content-Type": "application/json"}, + json=agent1_data, + ) + assert response.status_code == 200 + + agent2_data = { + "first_name": "Jane", + "last_name": "Doe", + "occupation": "test_occupation", + "gender": "test_gender", + "pk": "tmppk_agent2", + "tag": "test_tag", + } + response = requests.post( + f"{BASE_URL}/agents/", + headers={"Content-Type": "application/json"}, + json=agent2_data, + ) + assert response.status_code == 200 + + +def _create_mock_env_profile() -> None: + env_data = { + "codename": "test_codename", + "scenario": "A", + "agent_goals": [ + "B", + "C", + ], + "pk": "tmppk_env_profile", + "tag": "test_tag", + } + response = requests.post( + f"{BASE_URL}/scenarios/", + headers={"Content-Type": "application/json"}, + json=env_data, + ) + assert response.status_code == 200 + + +_create_mock_agent_profile() +_create_mock_env_profile() + + +data = { + "env_id": "tmppk_env_profile", + "agent_ids": ["tmppk_agent1", "tmppk_agent2"], + "models": ["custom/structured-llama3.2:1b@http://localhost:8000/v1"] * 3, + "max_turns": 10, + "tag": "test_tag", +} +try: + response = requests.post( + f"{BASE_URL}/simulate/", headers={"Content-Type": "application/json"}, json=data + ) + print(response) + assert response.status_code == 202 + assert isinstance(response.content.decode(), str) + episode_pk = response.content.decode() + print(episode_pk) + max_retries = 200 + retry_count = 0 + while retry_count < max_retries: + try: + response = requests.get(f"{BASE_URL}/simulation_status/{episode_pk}") + assert response.status_code == 200 + status = response.content.decode() + print(status) + if status == "Error": + raise Exception("Error running simulation") + elif status == "Completed": + break + # Status is "Started", keep polling + time.sleep(1) + retry_count += 1 + except Exception as e: + print(f"Error checking simulation status: {e}") + time.sleep(1) + retry_count += 1 + else: + raise TimeoutError("Simulation timed out after 10 retries") + +finally: + try: + response = requests.delete(f"{BASE_URL}/agents/tmppk_agent1") + assert response.status_code == 200 + except Exception as e: + print(e) + try: + response = requests.delete(f"{BASE_URL}/agents/tmppk_agent2") + assert response.status_code == 200 + except Exception as e: + print(e) + try: + response = requests.delete(f"{BASE_URL}/scenarios/tmppk_env_profile") + assert response.status_code == 200 + except Exception as e: + print(e) + + try: + response = requests.delete(f"{BASE_URL}/episodes/{episode_pk}") + assert response.status_code == 200 + except Exception as e: + print(e) diff --git a/sotopia/database/__init__.py b/sotopia/database/__init__.py index 07f02c1d..d9156a98 100644 --- a/sotopia/database/__init__.py +++ b/sotopia/database/__init__.py @@ -2,7 +2,7 @@ from redis_om import JsonModel, Migrator from .annotators import Annotator from .env_agent_combo_storage import EnvAgentComboStorage -from .logs import AnnotationForEpisode, EpisodeLog +from .logs import AnnotationForEpisode, EpisodeLog, NonStreamingSimulationStatus from .persistent_profile import ( AgentProfile, EnvironmentProfile, @@ -44,6 +44,7 @@ "AgentProfile", "EnvironmentProfile", "EpisodeLog", + "NonStreamingSimulationStatus", "EnvAgentComboStorage", "AnnotationForEpisode", "Annotator", @@ -73,6 +74,7 @@ "EvaluationDimensionBuilder", "CustomEvaluationDimension", "CustomEvaluationDimensionList", + "NonStreamingSimulationStatus", ] InheritedJsonModel = TypeVar("InheritedJsonModel", bound="JsonModel") diff --git a/sotopia/database/logs.py b/sotopia/database/logs.py index b3c5ff41..4a2551ae 100644 --- a/sotopia/database/logs.py +++ b/sotopia/database/logs.py @@ -8,10 +8,15 @@ from pydantic import model_validator from redis_om import JsonModel from redis_om.model.model import Field - +from typing import Literal from sotopia.database.persistent_profile import AgentProfile +class NonStreamingSimulationStatus(JsonModel): + episode_pk: str = Field(index=True) + status: Literal["Started", "Error", "Completed"] + + class EpisodeLog(JsonModel): # Note that we did not validate the following constraints: # 1. The number of turns in messages and rewards should be the same or off by 1 diff --git a/sotopia/database/persistent_profile.py b/sotopia/database/persistent_profile.py index ee99f660..c2e0e8e8 100644 --- a/sotopia/database/persistent_profile.py +++ b/sotopia/database/persistent_profile.py @@ -94,6 +94,11 @@ class RelationshipProfile(JsonModel): description="0 means stranger, 1 means know_by_name, 2 means acquaintance, 3 means friend, 4 means romantic_relationship, 5 means family_member", ) # this could be improved by limiting str to a relationship Enum background_story: str | None = Field(default_factory=lambda: None) + tag: str = Field( + index=True, + default_factory=lambda: "", + description="The tag of the relationship, used for searching, could be convenient to document relationship profiles from different works and sources", + ) class EnvironmentList(JsonModel): diff --git a/sotopia/database/serialization.py b/sotopia/database/serialization.py index 1fcc8b69..c38e3c6c 100644 --- a/sotopia/database/serialization.py +++ b/sotopia/database/serialization.py @@ -84,7 +84,7 @@ def _map_gender_to_adj(gender: str) -> str: "Nonbinary": "nonbinary", } if gender: - return gender_to_adj[gender] + return gender_to_adj.get(gender, "") else: return "" diff --git a/sotopia/envs/parallel.py b/sotopia/envs/parallel.py index 5d27f687..e0a928d3 100644 --- a/sotopia/envs/parallel.py +++ b/sotopia/envs/parallel.py @@ -51,7 +51,7 @@ def _map_gender_to_adj(gender: str) -> str: "Nonbinary": "nonbinary", } if gender: - return gender_to_adj[gender] + return gender_to_adj.get(gender, "") else: return "" diff --git a/sotopia/server.py b/sotopia/server.py index aec81a0f..ba88e9a7 100644 --- a/sotopia/server.py +++ b/sotopia/server.py @@ -15,7 +15,7 @@ ScriptWritingAgent, ) from sotopia.agents.base_agent import BaseAgent -from sotopia.database import EpisodeLog +from sotopia.database import EpisodeLog, NonStreamingSimulationStatus from sotopia.envs import ParallelSotopiaEnv from sotopia.envs.evaluators import ( EvaluationForTwoAgents, @@ -119,12 +119,15 @@ async def arun_one_episode( json_in_script: bool = False, tag: str | None = None, push_to_db: bool = False, + episode_pk: str | None = None, streaming: bool = False, + simulation_status: NonStreamingSimulationStatus | None = None, ) -> Union[ list[tuple[str, str, Message]], AsyncGenerator[list[list[tuple[str, str, Message]]], None], ]: agents = Agents({agent.agent_name: agent for agent in agent_list}) + print(f"Running episode with tag: {tag}------------------") async def generate_messages() -> ( AsyncGenerator[list[list[tuple[str, str, Message]]], None] @@ -188,7 +191,7 @@ async def generate_messages() -> ( for agent_name in env.agents ] ) - + print(f"Messages: {messages}") yield messages rewards.append([rewards_in_turn[agent_name] for agent_name in env.agents]) reasons.append( @@ -228,7 +231,14 @@ async def generate_messages() -> ( if push_to_db: try: - epilog.save() + if episode_pk: + epilog.pk = episode_pk + epilog.save() + else: + epilog.save() + if simulation_status: + simulation_status.status = "Completed" + simulation_status.save() except Exception as e: logging.error(f"Failed to save episode log: {e}") diff --git a/sotopia/ui/README.md b/sotopia/ui/README.md index 156050a4..4d8bb773 100644 --- a/sotopia/ui/README.md +++ b/sotopia/ui/README.md @@ -4,6 +4,17 @@ ## FastAPI Server +To run the FastAPI server, you can use the following command: +```bash +uv run rq worker +uv run fastapi run sotopia/ui/fastapi_server.py --workers 4 --port 8080 +``` + +Here is also an example of using the FastAPI server: +```bash +uv run python examples/fast_api_example.py +``` + The API server is a FastAPI application that is used to connect the Sotopia UI to the Sotopia backend. This could also help with other projects that need to connect to the Sotopia backend through HTTP requests. diff --git a/sotopia/ui/fastapi_server.py b/sotopia/ui/fastapi_server.py index 543dafd2..1d3a9d0e 100644 --- a/sotopia/ui/fastapi_server.py +++ b/sotopia/ui/fastapi_server.py @@ -1,9 +1,34 @@ -from fastapi import FastAPI, WebSocket, HTTPException, WebSocketDisconnect -from typing import Literal, cast, Optional, Any +from typing import Literal, cast, Dict, Self + +from redis_om import get_redis_connection +import rq +from sotopia.database import ( + EnvironmentProfile, + AgentProfile, + EpisodeLog, + RelationshipProfile, + RelationshipType, + NonStreamingSimulationStatus, +) +from sotopia.envs.parallel import ParallelSotopiaEnv +from sotopia.envs.evaluators import ( + RuleBasedTerminatedEvaluator, + ReachGoalLLMEvaluator, + EvaluationForTwoAgents, + SotopiaDimensions, +) +from sotopia.server import arun_one_episode +from sotopia.agents import LLMAgent, Agents +from fastapi import ( + FastAPI, + WebSocket, + HTTPException, + WebSocketDisconnect, +) +from typing import Optional, Any from fastapi.middleware.cors import CORSMiddleware -from pydantic import BaseModel +from pydantic import BaseModel, model_validator, field_validator -from sotopia.database import EnvironmentProfile, AgentProfile, EpisodeLog from sotopia.ui.websocket_utils import ( WebSocketSotopiaSimulator, WSMessageType, @@ -15,9 +40,11 @@ from contextlib import asynccontextmanager from typing import AsyncIterator import logging +from fastapi.responses import Response logger = logging.getLogger(__name__) + app = FastAPI() app.add_middleware( @@ -29,11 +56,21 @@ ) # TODO: Whether allowing CORS for all origins +class RelationshipWrapper(BaseModel): + pk: str = "" + agent_1_id: str = "" + agent_2_id: str = "" + relationship: Literal[0, 1, 2, 3, 4, 5] = 0 + backstory: str = "" + tag: str = "" + + class AgentProfileWrapper(BaseModel): """ Wrapper for AgentProfile to avoid pydantic v2 issues """ + pk: str = "" first_name: str last_name: str age: int = 0 @@ -57,6 +94,7 @@ class EnvironmentProfileWrapper(BaseModel): Wrapper for EnvironmentProfile to avoid pydantic v2 issues """ + pk: str = "" codename: str source: str = "" scenario: str = "" @@ -68,6 +106,33 @@ class EnvironmentProfileWrapper(BaseModel): tag: str = "" +class SimulationRequest(BaseModel): + env_id: str + agent_ids: list[str] + models: list[str] + max_turns: int + tag: str + + @field_validator("agent_ids") + @classmethod + def validate_agent_ids(cls, v: list[str]) -> list[str]: + if len(v) != 2: + raise ValueError( + "Currently only 2 agents are supported, we are working on supporting more agents" + ) + return v + + @model_validator(mode="after") + def validate_models(self) -> Self: + models = self.models + agent_ids = self.agent_ids + if len(models) != len(agent_ids) + 1: + raise ValueError( + f"models must have exactly {len(agent_ids) + 1} elements, if there are {len(agent_ids)} agents, the first model is the evaluator model" + ) + return self + + @app.get("/scenarios", response_model=list[EnvironmentProfile]) async def get_scenarios_all() -> list[EnvironmentProfile]: return EnvironmentProfile.all() @@ -122,6 +187,18 @@ async def get_agents( return agents_profiles +@app.get("/relationship/{agent_1_id}/{agent_2_id}", response_model=str) +async def get_relationship(agent_1_id: str, agent_2_id: str) -> str: + relationship_profiles = RelationshipProfile.find( + (RelationshipProfile.agent_1_id == agent_1_id) + & (RelationshipProfile.agent_2_id == agent_2_id) + ).all() + assert len(relationship_profiles) == 1 + relationship_profile = relationship_profiles[0] + assert isinstance(relationship_profile, RelationshipProfile) + return f"{str(relationship_profile.relationship)}: {RelationshipType(relationship_profile.relationship).name}" + + @app.get("/episodes", response_model=list[EpisodeLog]) async def get_episodes_all() -> list[EpisodeLog]: return EpisodeLog.all() @@ -143,6 +220,15 @@ async def get_episodes(get_by: Literal["id", "tag"], value: str) -> list[Episode return episodes +@app.post("/scenarios/", response_model=str) +async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: + scenario_profile = EnvironmentProfile(**scenario.model_dump()) + scenario_profile.save() + pk = scenario_profile.pk + assert pk is not None + return pk + + @app.post("/agents/", response_model=str) async def create_agent(agent: AgentProfileWrapper) -> str: agent_profile = AgentProfile(**agent.model_dump()) @@ -152,39 +238,146 @@ async def create_agent(agent: AgentProfileWrapper) -> str: return pk -@app.post("/scenarios/", response_model=str) -async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: - scenario_profile = EnvironmentProfile(**scenario.model_dump()) - scenario_profile.save() - pk = scenario_profile.pk +@app.post("/relationship/", response_model=str) +async def create_relationship(relationship: RelationshipWrapper) -> str: + relationship_profile = RelationshipProfile(**relationship.model_dump()) + relationship_profile.save() + pk = relationship_profile.pk assert pk is not None return pk -@app.put("/agents/{agent_id}", response_model=str) -async def update_agent(agent_id: str, agent: AgentProfileWrapper) -> str: +async def run_simulation( + episode_pk: str, + simulation_request: SimulationRequest, + simulation_status: NonStreamingSimulationStatus, +) -> None: try: - old_agent = AgentProfile.get(pk=agent_id) + env_profile: EnvironmentProfile = EnvironmentProfile.get( + pk=simulation_request.env_id + ) except Exception: # TODO Check the exception type raise HTTPException( - status_code=404, detail=f"Agent with id={agent_id} not found" + status_code=404, + detail=f"Environment with id={simulation_request.env_id} not found", + ) + try: + agent_1_profile = AgentProfile.get(pk=simulation_request.agent_ids[0]) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, + detail=f"Agent with id={simulation_request.agent_ids[0]} not found", + ) + try: + agent_2_profile = AgentProfile.get(pk=simulation_request.agent_ids[1]) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, + detail=f"Agent with id={simulation_request.agent_ids[1]} not found", ) - old_agent.update(**agent.model_dump()) # type: ignore - assert old_agent.pk is not None - return old_agent.pk - -@app.put("/scenarios/{scenario_id}", response_model=str) -async def update_scenario(scenario_id: str, scenario: EnvironmentProfileWrapper) -> str: + env_params: dict[str, Any] = { + "model_name": simulation_request.models[0], + "action_order": "round-robin", + "evaluators": [ + RuleBasedTerminatedEvaluator( + max_turn_number=simulation_request.max_turns, max_stale_turn=2 + ), + ], + "terminal_evaluators": [ + ReachGoalLLMEvaluator( + simulation_request.models[0], + EvaluationForTwoAgents[SotopiaDimensions], + ), + ], + } + env = ParallelSotopiaEnv(env_profile=env_profile, **env_params) + agents = Agents( + { + "agent1": LLMAgent( + "agent1", + model_name=simulation_request.models[1], + agent_profile=agent_1_profile, + ), + "agent2": LLMAgent( + "agent2", + model_name=simulation_request.models[2], + agent_profile=agent_2_profile, + ), + } + ) + + await arun_one_episode( + env=env, + agent_list=list(agents.values()), + push_to_db=True, + tag=simulation_request.tag, + episode_pk=episode_pk, + simulation_status=simulation_status, + ) + + +@app.post("/simulate/", response_model=str) +def simulate(simulation_request: SimulationRequest) -> Response: try: - old_scenario = EnvironmentProfile.get(pk=scenario_id) + _: EnvironmentProfile = EnvironmentProfile.get(pk=simulation_request.env_id) except Exception: # TODO Check the exception type raise HTTPException( - status_code=404, detail=f"Scenario with id={scenario_id} not found" + status_code=404, + detail=f"Environment with id={simulation_request.env_id} not found", + ) + try: + __ = AgentProfile.get(pk=simulation_request.agent_ids[0]) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, + detail=f"Agent with id={simulation_request.agent_ids[0]} not found", + ) + try: + ___ = AgentProfile.get(pk=simulation_request.agent_ids[1]) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, + detail=f"Agent with id={simulation_request.agent_ids[1]} not found", + ) + + episode_pk = EpisodeLog( + environment="", + agents=[], + models=[], + messages=[], + reasoning="", + rewards=[], # Pseudorewards + rewards_prompt="", + ).pk + try: + simulation_status = NonStreamingSimulationStatus( + episode_pk=episode_pk, + status="Started", ) - old_scenario.update(**scenario.model_dump()) # type: ignore - assert old_scenario.pk is not None - return old_scenario.pk + simulation_status.save() + queue = rq.Queue("default", connection=get_redis_connection()) + queue.enqueue( + run_simulation, + episode_pk=episode_pk, + simulation_request=simulation_request, + simulation_status=simulation_status, + ) + + except Exception as e: + logger.error(f"Error starting simulation: {e}") + simulation_status.status = "Error" + simulation_status.save() + return Response(content=episode_pk, status_code=202) + + +@app.get("/simulation_status/{episode_pk}", response_model=str) +async def get_simulation_status(episode_pk: str) -> str: + status = NonStreamingSimulationStatus.find( + NonStreamingSimulationStatus.episode_pk == episode_pk + ).all()[0] + assert isinstance(status, NonStreamingSimulationStatus) + return status.status @app.delete("/agents/{agent_id}", response_model=str) @@ -213,6 +406,23 @@ async def delete_scenario(scenario_id: str) -> str: return scenario.pk +@app.delete("/relationship/{relationship_id}", response_model=str) +async def delete_relationship(relationship_id: str) -> str: + RelationshipProfile.delete(relationship_id) + return relationship_id + + +@app.delete("/episodes/{episode_id}", response_model=str) +async def delete_episode(episode_id: str) -> str: + EpisodeLog.delete(episode_id) + return episode_id + + +active_simulations: Dict[ + str, bool +] = {} # TODO check whether this is the correct way to store the active simulations + + @app.get("/models", response_model=list[str]) async def get_models() -> list[str]: # TODO figure out how to get the available models diff --git a/stubs/redis_om/__init__.pyi b/stubs/redis_om/__init__.pyi index abbae6f4..133b6caf 100644 --- a/stubs/redis_om/__init__.pyi +++ b/stubs/redis_om/__init__.pyi @@ -2,6 +2,7 @@ import abc from typing import Any, Generator, TypeVar from pydantic import BaseModel +import redis from redis_om.model.model import Field from pydantic._internal._model_construction import ModelMetaclass from redis_om.model.model import FindQuery @@ -37,3 +38,5 @@ class EmbeddedJsonModel(JsonModel): ... class Migrator: def run(self) -> None: ... + +def get_redis_connection() -> redis.Redis[bytes]: ... diff --git a/tests/ui/test_fastapi.py b/tests/ui/test_fastapi.py index 9395104f..b8c7bb7d 100644 --- a/tests/ui/test_fastapi.py +++ b/tests/ui/test_fastapi.py @@ -1,5 +1,10 @@ from fastapi.testclient import TestClient -from sotopia.database import EnvironmentProfile, AgentProfile, EpisodeLog +from sotopia.database import ( + EnvironmentProfile, + AgentProfile, + EpisodeLog, + RelationshipProfile, +) from sotopia.messages import SimpleMessage from sotopia.ui.fastapi_server import app import pytest @@ -63,7 +68,7 @@ def create_dummy_episode_log() -> None: @pytest.fixture -def create_mock_data() -> Generator[None, None, None]: +def create_mock_data(for_posting: bool = False) -> Generator[None, None, None]: def _create_mock_agent_profile() -> None: AgentProfile( first_name="John", @@ -71,6 +76,7 @@ def _create_mock_agent_profile() -> None: occupation="test_occupation", gender="test_gender", pk="tmppk_agent1", + tag="test_tag", ).save() AgentProfile( first_name="Jane", @@ -78,6 +84,7 @@ def _create_mock_agent_profile() -> None: occupation="test_occupation", gender="test_gender", pk="tmppk_agent2", + tag="test_tag", ).save() def _create_mock_env_profile() -> None: @@ -89,18 +96,55 @@ def _create_mock_env_profile() -> None: "C", ], pk="tmppk_env_profile", + tag="test_tag", ) env_profile.save() + def _create_mock_relationship() -> None: + RelationshipProfile( + pk="tmppk_relationship", + agent_1_id="tmppk_agent1", + agent_2_id="tmppk_agent2", + relationship=1.0, + ).save() + _create_mock_agent_profile() _create_mock_env_profile() - + _create_mock_relationship() yield - AgentProfile.delete("tmppk_agent1") - AgentProfile.delete("tmppk_agent2") - EnvironmentProfile.delete("tmppk_env_profile") - EpisodeLog.delete("tmppk_episode_log") + try: + AgentProfile.delete("tmppk_agent1") + except Exception as e: + print(e) + try: + AgentProfile.delete("tmppk_agent2") + except Exception as e: + print(e) + try: + EnvironmentProfile.delete("tmppk_env_profile") + except Exception as e: + print(e) + try: + RelationshipProfile.delete("tmppk_relationship") + except Exception as e: + print(e) + try: + EpisodeLog.delete("tmppk_episode_log") + except Exception as e: + print(e) + + try: + EpisodeLog.delete("tmppk_episode_log") + except Exception as e: + print(e) + + try: + episodes = EpisodeLog.find(EpisodeLog.tag == "test_tag").all() + for episode in episodes: + EpisodeLog.delete(episode.pk) + except Exception as e: + print(e) def test_get_scenarios_all(create_mock_data: Callable[[], None]) -> None: @@ -169,8 +213,17 @@ def test_get_episodes_by_tag(create_mock_data: Callable[[], None]) -> None: assert response.json()[0]["tag"] == tag +def test_get_relationship(create_mock_data: Callable[[], None]) -> None: + response = client.get("/relationship/tmppk_agent1/tmppk_agent2") + assert response.status_code == 200 + assert isinstance(response.json(), str) + assert response.json() == "1: know_by_name" + + +@pytest.mark.parametrize("create_mock_data", [True], indirect=True) def test_create_agent(create_mock_data: Callable[[], None]) -> None: agent_data = { + "pk": "tmppk_agent1", "first_name": "test_first_name", "last_name": "test_last_name", } @@ -179,13 +232,30 @@ def test_create_agent(create_mock_data: Callable[[], None]) -> None: assert isinstance(response.json(), str) +@pytest.mark.parametrize("create_mock_data", [True], indirect=True) def test_create_scenario(create_mock_data: Callable[[], None]) -> None: scenario_data = { + "pk": "tmppk_env_profile", "codename": "test_codename", "scenario": "test_scenario", "tag": "test", } response = client.post("/scenarios/", json=scenario_data) + EnvironmentProfile.delete("tmppk_env_profile") + assert response.status_code == 200 + assert isinstance(response.json(), str) + + +@pytest.mark.parametrize("create_mock_data", [True], indirect=True) +def test_create_relationship(create_mock_data: Callable[[], None]) -> None: + relationship_data = { + "pk": "tmppk_relationship", + "agent_1_id": "tmppk_agent1", + "agent_2_id": "tmppk_agent2", + "relationship": 1.0, + "tag": "test_tag", + } + response = client.post("/relationship", json=relationship_data) assert response.status_code == 200 assert isinstance(response.json(), str) @@ -200,3 +270,54 @@ def test_delete_scenario(create_mock_data: Callable[[], None]) -> None: response = client.delete("/scenarios/tmppk_env_profile") assert response.status_code == 200 assert isinstance(response.json(), str) + + +def test_delete_relationship(create_mock_data: Callable[[], None]) -> None: + response = client.delete("/relationship/tmppk_relationship") + assert response.status_code == 200 + assert isinstance(response.json(), str) + + +# def test_simulate(create_mock_data: Callable[[], None]) -> None: +# response = client.post( +# "/simulate", +# json={ +# "env_id": "tmppk_env_profile", +# "agent_ids": ["tmppk_agent1", "tmppk_agent2"], +# "models": [ +# # "custom/llama3.2:1b@http://localhost:8000/v1", +# # "custom/llama3.2:1b@http://localhost:8000/v1", +# # "custom/llama3.2:1b@http://localhost:8000/v1" +# "gpt-4o-mini", +# "gpt-4o-mini", +# "gpt-4o-mini", +# ], +# "max_turns": 2, +# "tag": "test_tag", +# }, +# ) +# assert response.status_code == 200 +# assert isinstance(response.json(), str) +# max_retries = 20 +# retry_count = 0 +# while retry_count < max_retries: +# try: +# status = NonStreamingSimulationStatus.find( +# NonStreamingSimulationStatus.episode_pk == response.json() +# ).all()[0] +# assert isinstance(status, NonStreamingSimulationStatus) +# print(status) +# if status.status == "Error": +# raise Exception("Error running simulation") +# elif status.status == "Completed": +# # EpisodeLog.get(response.json()) +# break +# # Status is "Started", keep polling +# time.sleep(1) +# retry_count += 1 +# except Exception as e: +# print(f"Error checking simulation status: {e}") +# time.sleep(1) +# retry_count += 1 +# else: +# raise TimeoutError("Simulation timed out after 10 retries") diff --git a/uv.lock b/uv.lock index 71152b36..5017e0e0 100644 --- a/uv.lock +++ b/uv.lock @@ -115,51 +115,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f5/76/a57ceff577ae26fe9a6f31ac799bc638ecf26e4acdf04295290b9929b349/aiohttp-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d18a8b44ec8502a7fde91446cd9c9b95ce7c49f1eacc1fb2358b8907d4369fd", size = 1690038 }, { url = "https://files.pythonhosted.org/packages/4b/81/b20e09003b6989a7f23a721692137a6143420a151063c750ab2a04878e3c/aiohttp-3.11.7-cp312-cp312-win32.whl", hash = "sha256:3d1c9c15d3999107cbb9b2d76ca6172e6710a12fda22434ee8bd3f432b7b17e8", size = 409887 }, { url = "https://files.pythonhosted.org/packages/b7/0b/607c98bff1d07bb21e0c39e7711108ef9ff4f2a361a3ec1ce8dce93623a5/aiohttp-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:018f1b04883a12e77e7fc161934c0f298865d3a484aea536a6a2ca8d909f0ba0", size = 436462 }, - { url = "https://files.pythonhosted.org/packages/3d/dd/3d40c0e67e79c5c42671e3e268742f1ff96c6573ca43823563d01abd9475/aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f", size = 586969 }, - { url = "https://files.pythonhosted.org/packages/75/64/8de41b5555e5b43ef6d4ed1261891d33fe45ecc6cb62875bfafb90b9ab93/aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9", size = 399367 }, - { url = "https://files.pythonhosted.org/packages/96/36/27bd62ea7ce43906d1443a73691823fc82ffb8fa03276b0e2f7e1037c286/aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8", size = 390720 }, - { url = "https://files.pythonhosted.org/packages/e8/4d/d516b050d811ce0dd26325c383013c104ffa8b58bd361b82e52833f68e78/aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1", size = 1228820 }, - { url = "https://files.pythonhosted.org/packages/53/94/964d9327a3e336d89aad52260836e4ec87fdfa1207176550fdf384eaffe7/aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a", size = 1264616 }, - { url = "https://files.pythonhosted.org/packages/0c/20/70ce17764b685ca8f5bf4d568881b4e1f1f4ea5e8170f512fdb1a33859d2/aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd", size = 1298402 }, - { url = "https://files.pythonhosted.org/packages/d1/d1/5248225ccc687f498d06c3bca5af2647a361c3687a85eb3aedcc247ee1aa/aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026", size = 1222205 }, - { url = "https://files.pythonhosted.org/packages/f2/a3/9296b27cc5d4feadf970a14d0694902a49a985f3fae71b8322a5f77b0baa/aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b", size = 1193804 }, - { url = "https://files.pythonhosted.org/packages/d9/07/f3760160feb12ac51a6168a6da251a4a8f2a70733d49e6ceb9b3e6ee2f03/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d", size = 1193544 }, - { url = "https://files.pythonhosted.org/packages/7e/4c/93a70f9a4ba1c30183a6dd68bfa79cddbf9a674f162f9c62e823a74a5515/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7", size = 1193047 }, - { url = "https://files.pythonhosted.org/packages/ff/a3/36a1e23ff00c7a0cd696c5a28db05db25dc42bfc78c508bd78623ff62a4a/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a", size = 1247201 }, - { url = "https://files.pythonhosted.org/packages/55/ae/95399848557b98bb2c402d640b2276ce3a542b94dba202de5a5a1fe29abe/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc", size = 1264102 }, - { url = "https://files.pythonhosted.org/packages/38/f5/02e5c72c1b60d7cceb30b982679a26167e84ac029fd35a93dd4da52c50a3/aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68", size = 1215760 }, - { url = "https://files.pythonhosted.org/packages/30/17/1463840bad10d02d0439068f37ce5af0b383884b0d5838f46fb027e233bf/aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257", size = 362678 }, - { url = "https://files.pythonhosted.org/packages/dd/01/a0ef707d93e867a43abbffee3a2cdf30559910750b9176b891628c7ad074/aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6", size = 381097 }, - { url = "https://files.pythonhosted.org/packages/72/31/3c351d17596194e5a38ef169a4da76458952b2497b4b54645b9d483cbbb0/aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f", size = 586501 }, - { url = "https://files.pythonhosted.org/packages/a4/a8/a559d09eb08478cdead6b7ce05b0c4a133ba27fcdfa91e05d2e62867300d/aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb", size = 398993 }, - { url = "https://files.pythonhosted.org/packages/c5/47/7736d4174613feef61d25332c3bd1a4f8ff5591fbd7331988238a7299485/aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871", size = 390647 }, - { url = "https://files.pythonhosted.org/packages/27/21/e9ba192a04b7160f5a8952c98a1de7cf8072ad150fa3abd454ead1ab1d7f/aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c", size = 1306481 }, - { url = "https://files.pythonhosted.org/packages/cf/50/f364c01c8d0def1dc34747b2470969e216f5a37c7ece00fe558810f37013/aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38", size = 1344652 }, - { url = "https://files.pythonhosted.org/packages/1d/c2/74f608e984e9b585649e2e83883facad6fa3fc1d021de87b20cc67e8e5ae/aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb", size = 1378498 }, - { url = "https://files.pythonhosted.org/packages/9f/a7/05a48c7c0a7a80a5591b1203bf1b64ca2ed6a2050af918d09c05852dc42b/aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7", size = 1292718 }, - { url = "https://files.pythonhosted.org/packages/7d/78/a925655018747e9790350180330032e27d6e0d7ed30bde545fae42f8c49c/aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911", size = 1251776 }, - { url = "https://files.pythonhosted.org/packages/47/9d/85c6b69f702351d1236594745a4fdc042fc43f494c247a98dac17e004026/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092", size = 1271716 }, - { url = "https://files.pythonhosted.org/packages/7f/a7/55fc805ff9b14af818903882ece08e2235b12b73b867b521b92994c52b14/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142", size = 1266263 }, - { url = "https://files.pythonhosted.org/packages/1f/ec/d2be2ca7b063e4f91519d550dbc9c1cb43040174a322470deed90b3d3333/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9", size = 1321617 }, - { url = "https://files.pythonhosted.org/packages/c9/a3/b29f7920e1cd0a9a68a45dd3eb16140074d2efb1518d2e1f3e140357dc37/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1", size = 1339227 }, - { url = "https://files.pythonhosted.org/packages/8a/81/34b67235c47e232d807b4bbc42ba9b927c7ce9476872372fddcfd1e41b3d/aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a", size = 1299068 }, - { url = "https://files.pythonhosted.org/packages/04/1f/26a7fe11b6ad3184f214733428353c89ae9fe3e4f605a657f5245c5e720c/aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94", size = 362223 }, - { url = "https://files.pythonhosted.org/packages/10/91/85dcd93f64011434359ce2666bece981f08d31bc49df33261e625b28595d/aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959", size = 381576 }, - { url = "https://files.pythonhosted.org/packages/ae/99/4c5aefe5ad06a1baf206aed6598c7cdcbc7c044c46801cd0d1ecb758cae3/aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c", size = 583536 }, - { url = "https://files.pythonhosted.org/packages/a9/36/8b3bc49b49cb6d2da40ee61ff15dbcc44fd345a3e6ab5bb20844df929821/aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28", size = 395693 }, - { url = "https://files.pythonhosted.org/packages/e1/77/0aa8660dcf11fa65d61712dbb458c4989de220a844bd69778dff25f2d50b/aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f", size = 390898 }, - { url = "https://files.pythonhosted.org/packages/38/d2/b833d95deb48c75db85bf6646de0a697e7fb5d87bd27cbade4f9746b48b1/aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138", size = 1312060 }, - { url = "https://files.pythonhosted.org/packages/aa/5f/29fd5113165a0893de8efedf9b4737e0ba92dfcd791415a528f947d10299/aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742", size = 1350553 }, - { url = "https://files.pythonhosted.org/packages/ad/cc/f835f74b7d344428469200105236d44606cfa448be1e7c95ca52880d9bac/aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7", size = 1392646 }, - { url = "https://files.pythonhosted.org/packages/bf/fe/1332409d845ca601893bbf2d76935e0b93d41686e5f333841c7d7a4a770d/aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16", size = 1306310 }, - { url = "https://files.pythonhosted.org/packages/e4/a1/25a7633a5a513278a9892e333501e2e69c83e50be4b57a62285fb7a008c3/aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8", size = 1260255 }, - { url = "https://files.pythonhosted.org/packages/f2/39/30eafe89e0e2a06c25e4762844c8214c0c0cd0fd9ffc3471694a7986f421/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6", size = 1271141 }, - { url = "https://files.pythonhosted.org/packages/5b/fc/33125df728b48391ef1fcb512dfb02072158cc10d041414fb79803463020/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a", size = 1280244 }, - { url = "https://files.pythonhosted.org/packages/3b/61/e42bf2c2934b5caa4e2ec0b5e5fd86989adb022b5ee60c2572a9d77cf6fe/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9", size = 1316805 }, - { url = "https://files.pythonhosted.org/packages/18/32/f52a5e2ae9ad3bba10e026a63a7a23abfa37c7d97aeeb9004eaa98df3ce3/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a", size = 1343930 }, - { url = "https://files.pythonhosted.org/packages/05/be/6a403b464dcab3631fe8e27b0f1d906d9e45c5e92aca97ee007e5a895560/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205", size = 1306186 }, - { url = "https://files.pythonhosted.org/packages/8e/fd/bb50fe781068a736a02bf5c7ad5f3ab53e39f1d1e63110da6d30f7605edc/aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628", size = 359289 }, - { url = "https://files.pythonhosted.org/packages/70/9e/5add7e240f77ef67c275c82cc1d08afbca57b77593118c1f6e920ae8ad3f/aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf", size = 379313 }, ] [[package]] @@ -3211,7 +3166,6 @@ requires-dist = [ { name = "pytest-asyncio", marker = "extra == 'test'" }, { name = "pytest-cov", marker = "extra == 'test'" }, { name = "redis-om", specifier = ">=0.3.0,<0.4.0" }, - { name = "rel", marker = "extra == 'chat'" }, { name = "rich", specifier = ">=13.6.0,<14.0.0" }, { name = "scipy", marker = "extra == 'examples'" }, { name = "together", specifier = ">=0.2.4,<1.4.0" }, From cadf06d4225d298967b3771eb2496b7e1cfe6222 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Thu, 12 Dec 2024 15:32:58 -0500 Subject: [PATCH 04/26] fix ci error --- sotopia/ui/fastapi_server.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sotopia/ui/fastapi_server.py b/sotopia/ui/fastapi_server.py index 1d3a9d0e..611878c4 100644 --- a/sotopia/ui/fastapi_server.py +++ b/sotopia/ui/fastapi_server.py @@ -1,4 +1,10 @@ -from typing import Literal, cast, Dict, Self +from typing import Literal, cast, Dict +import sys + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self from redis_om import get_redis_connection import rq @@ -193,7 +199,9 @@ async def get_relationship(agent_1_id: str, agent_2_id: str) -> str: (RelationshipProfile.agent_1_id == agent_1_id) & (RelationshipProfile.agent_2_id == agent_2_id) ).all() - assert len(relationship_profiles) == 1 + assert ( + len(relationship_profiles) == 1 + ), f"{len(relationship_profiles)} relationship profiles found for agents {agent_1_id} and {agent_2_id}, expected 1" relationship_profile = relationship_profiles[0] assert isinstance(relationship_profile, RelationshipProfile) return f"{str(relationship_profile.relationship)}: {RelationshipType(relationship_profile.relationship).name}" From 187a21b0ebe72fad9ab3a11589df88023a013f40 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Thu, 12 Dec 2024 20:56:36 +0000 Subject: [PATCH 05/26] solving pytests --- tests/ui/test_fastapi.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/ui/test_fastapi.py b/tests/ui/test_fastapi.py index b8c7bb7d..1aa4a5b0 100644 --- a/tests/ui/test_fastapi.py +++ b/tests/ui/test_fastapi.py @@ -108,9 +108,10 @@ def _create_mock_relationship() -> None: relationship=1.0, ).save() - _create_mock_agent_profile() - _create_mock_env_profile() - _create_mock_relationship() + if not for_posting: + _create_mock_agent_profile() + _create_mock_env_profile() + _create_mock_relationship() yield try: From ec5c394bcfc8f3fe462cf5f2df1e9238c95a9ccd Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Thu, 12 Dec 2024 21:33:56 +0000 Subject: [PATCH 06/26] improve the tests --- tests/sampler/test_sampler.py | 12 ++++++++++-- tests/ui/test_fastapi.py | 1 - 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/sampler/test_sampler.py b/tests/sampler/test_sampler.py index 3b039f17..53a40691 100644 --- a/tests/sampler/test_sampler.py +++ b/tests/sampler/test_sampler.py @@ -33,16 +33,24 @@ def _test_create_episode_log_setup_and_tear_down() -> Generator[None, None, None age_constraint="[(18, 70), (18, 70)]", ).save() RelationshipProfile( - agent_1_id="tmppk_agent1", agent_2_id="tmppk_agent2", relationship=2 + agent_1_id="tmppk_agent1", + agent_2_id="tmppk_agent2", + relationship=2, + pk="tmppk_relationship1", ).save() RelationshipProfile( - agent_1_id="tmppk_agent1", agent_2_id="tmppk_agent3", relationship=2 + agent_1_id="tmppk_agent1", + agent_2_id="tmppk_agent3", + relationship=2, + pk="tmppk_relationship2", ).save() yield AgentProfile.delete("tmppk_agent1") AgentProfile.delete("tmppk_agent2") AgentProfile.delete("tmppk_agent3") EnvironmentProfile.delete("tmppk_environment") + RelationshipProfile.delete("tmppk_relationship1") + RelationshipProfile.delete("tmppk_relationship2") def _generate_name() -> str: diff --git a/tests/ui/test_fastapi.py b/tests/ui/test_fastapi.py index 1aa4a5b0..ac00eacf 100644 --- a/tests/ui/test_fastapi.py +++ b/tests/ui/test_fastapi.py @@ -242,7 +242,6 @@ def test_create_scenario(create_mock_data: Callable[[], None]) -> None: "tag": "test", } response = client.post("/scenarios/", json=scenario_data) - EnvironmentProfile.delete("tmppk_env_profile") assert response.status_code == 200 assert isinstance(response.json(), str) From 1a1244ecc19d1e2523777378c58a4cf9f1cb23c1 Mon Sep 17 00:00:00 2001 From: Xuhui Zhou Date: Fri, 13 Dec 2024 15:45:04 -0500 Subject: [PATCH 07/26] add custom eval fast api (#268) --- examples/use_custom_dimensions.py | 1 - sotopia/ui/fastapi_server.py | 81 ++++++++++++++++++++++++++++++- tests/ui/test_fastapi.py | 65 ++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 3 deletions(-) diff --git a/examples/use_custom_dimensions.py b/examples/use_custom_dimensions.py index 0bbdfda8..ff712224 100644 --- a/examples/use_custom_dimensions.py +++ b/examples/use_custom_dimensions.py @@ -45,7 +45,6 @@ def save_dimensions(dimensions: list[dict[str, Union[str, int]]]) -> None: def save_dimension_list( dimensions: list[dict[str, Union[str, int]]], list_name: str ) -> None: - Migrator().run() dimension_list = CustomEvaluationDimensionList.find( CustomEvaluationDimensionList.name == list_name ).all() diff --git a/sotopia/ui/fastapi_server.py b/sotopia/ui/fastapi_server.py index 611878c4..9bd0d4f3 100644 --- a/sotopia/ui/fastapi_server.py +++ b/sotopia/ui/fastapi_server.py @@ -15,6 +15,8 @@ RelationshipProfile, RelationshipType, NonStreamingSimulationStatus, + CustomEvaluationDimensionList, + CustomEvaluationDimension, ) from sotopia.envs.parallel import ParallelSotopiaEnv from sotopia.envs.evaluators import ( @@ -33,7 +35,7 @@ ) from typing import Optional, Any from fastapi.middleware.cors import CORSMiddleware -from pydantic import BaseModel, model_validator, field_validator +from pydantic import BaseModel, model_validator, field_validator, Field from sotopia.ui.websocket_utils import ( WebSocketSotopiaSimulator, @@ -112,6 +114,16 @@ class EnvironmentProfileWrapper(BaseModel): tag: str = "" +class CustomEvaluationDimensionsWrapper(BaseModel): + pk: str = "" + name: str = Field( + default="", description="The name of the custom evaluation dimension list" + ) + dimensions: list[CustomEvaluationDimension] = Field( + default=[], description="The dimensions of the custom evaluation dimension list" + ) + + class SimulationRequest(BaseModel): env_id: str agent_ids: list[str] @@ -228,6 +240,20 @@ async def get_episodes(get_by: Literal["id", "tag"], value: str) -> list[Episode return episodes +@app.get( + "/evaluation_dimensions/", response_model=dict[str, list[CustomEvaluationDimension]] +) +async def get_evaluation_dimensions() -> dict[str, list[CustomEvaluationDimension]]: + custom_evaluation_dimensions: dict[str, list[CustomEvaluationDimension]] = {} + custom_evaluation_dimension_list = CustomEvaluationDimensionList.all() + for custom_evaluation_dimension_list in custom_evaluation_dimension_list: + custom_evaluation_dimensions[custom_evaluation_dimension_list.name] = [ + CustomEvaluationDimension.get(pk=pk) + for pk in custom_evaluation_dimension_list.dimension_pks + ] + return custom_evaluation_dimensions + + @app.post("/scenarios/", response_model=str) async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: scenario_profile = EnvironmentProfile(**scenario.model_dump()) @@ -255,6 +281,49 @@ async def create_relationship(relationship: RelationshipWrapper) -> str: return pk +@app.post("/evaluation_dimensions/", response_model=str) +async def create_evaluation_dimensions( + evaluation_dimensions: CustomEvaluationDimensionsWrapper, +) -> str: + dimension_list = CustomEvaluationDimensionList.find( + CustomEvaluationDimensionList.name == evaluation_dimensions.name + ).all() + + if len(dimension_list) == 0: + all_dimensions_pks = [] + for dimension in evaluation_dimensions.dimensions: + find_dimension = CustomEvaluationDimension.find( + CustomEvaluationDimension.name == dimension.name + ).all() + if len(find_dimension) == 0: + dimension.save() + all_dimensions_pks.append(dimension.pk) + elif len(find_dimension) == 1: + all_dimensions_pks.append(find_dimension[0].pk) + else: + raise HTTPException( + status_code=409, + detail=f"Evaluation dimension with name={dimension.name} already exists", + ) + + custom_evaluation_dimension_list = CustomEvaluationDimensionList( + pk=evaluation_dimensions.pk, + name=evaluation_dimensions.name, + dimension_pks=all_dimensions_pks, + ) + custom_evaluation_dimension_list.save() + logger.info(f"Created evaluation dimension list {evaluation_dimensions.name}") + else: + raise HTTPException( + status_code=409, + detail=f"Evaluation dimension list with name={evaluation_dimensions.name} already exists", + ) + + pk = custom_evaluation_dimension_list.pk + assert pk is not None + return pk + + async def run_simulation( episode_pk: str, simulation_request: SimulationRequest, @@ -426,6 +495,16 @@ async def delete_episode(episode_id: str) -> str: return episode_id +@app.delete("/evaluation_dimensions/{evaluation_dimension_list_pk}", response_model=str) +async def delete_evaluation_dimension_list(evaluation_dimension_list_pk: str) -> str: + for dimension_pk in CustomEvaluationDimensionList.get( + evaluation_dimension_list_pk + ).dimension_pks: + CustomEvaluationDimension.delete(dimension_pk) + CustomEvaluationDimensionList.delete(evaluation_dimension_list_pk) + return evaluation_dimension_list_pk + + active_simulations: Dict[ str, bool ] = {} # TODO check whether this is the correct way to store the active simulations diff --git a/tests/ui/test_fastapi.py b/tests/ui/test_fastapi.py index ac00eacf..0ce87278 100644 --- a/tests/ui/test_fastapi.py +++ b/tests/ui/test_fastapi.py @@ -4,6 +4,8 @@ AgentProfile, EpisodeLog, RelationshipProfile, + CustomEvaluationDimension, + CustomEvaluationDimensionList, ) from sotopia.messages import SimpleMessage from sotopia.ui.fastapi_server import app @@ -68,7 +70,9 @@ def create_dummy_episode_log() -> None: @pytest.fixture -def create_mock_data(for_posting: bool = False) -> Generator[None, None, None]: +def create_mock_data(request: pytest.FixtureRequest) -> Generator[None, None, None]: + for_posting = request.param if hasattr(request, "param") else False + def _create_mock_agent_profile() -> None: AgentProfile( first_name="John", @@ -108,10 +112,26 @@ def _create_mock_relationship() -> None: relationship=1.0, ).save() + def _create_mock_evaluation_dimension() -> None: + CustomEvaluationDimension( + pk="tmppk_evaluation_dimension", + name="test_dimension", + description="test_description", + range_high=10, + range_low=-10, + ).save() + CustomEvaluationDimensionList( + pk="tmppk_evaluation_dimension_list", + name="test_dimension_list", + dimension_pks=["tmppk_evaluation_dimension"], + ).save() + if not for_posting: _create_mock_agent_profile() _create_mock_env_profile() _create_mock_relationship() + _create_mock_evaluation_dimension() + print("created mock data") yield try: @@ -147,6 +167,15 @@ def _create_mock_relationship() -> None: except Exception as e: print(e) + try: + CustomEvaluationDimension.delete("tmppk_evaluation_dimension") + except Exception as e: + print(e) + try: + CustomEvaluationDimensionList.delete("tmppk_evaluation_dimension_list") + except Exception as e: + print(e) + def test_get_scenarios_all(create_mock_data: Callable[[], None]) -> None: response = client.get("/scenarios") @@ -221,6 +250,13 @@ def test_get_relationship(create_mock_data: Callable[[], None]) -> None: assert response.json() == "1: know_by_name" +def test_get_evaluation_dimensions(create_mock_data: Callable[[], None]) -> None: + response = client.get("/evaluation_dimensions/") + assert response.status_code == 200 + assert isinstance(response.json(), dict) + assert response.json()["test_dimension_list"][0]["name"] == "test_dimension" + + @pytest.mark.parametrize("create_mock_data", [True], indirect=True) def test_create_agent(create_mock_data: Callable[[], None]) -> None: agent_data = { @@ -260,6 +296,27 @@ def test_create_relationship(create_mock_data: Callable[[], None]) -> None: assert isinstance(response.json(), str) +@pytest.mark.parametrize("create_mock_data", [True], indirect=True) +def test_create_evaluation_dimensions(create_mock_data: Callable[[], None]) -> None: + evaluation_dimension_data = { + "pk": "tmppk_evaluation_dimension_list", + "name": "test_dimension_list", + "dimensions": [ + { + "pk": "tmppk_evaluation_dimension", + "name": "test_dimension", + "description": "test_description", + "range_high": 10, + "range_low": -10, + } + ], + } + response = client.post("/evaluation_dimensions", json=evaluation_dimension_data) + print(response.json()) + assert response.status_code == 200 + assert isinstance(response.json(), str) + + def test_delete_agent(create_mock_data: Callable[[], None]) -> None: response = client.delete("/agents/tmppk_agent1") assert response.status_code == 200 @@ -278,6 +335,12 @@ def test_delete_relationship(create_mock_data: Callable[[], None]) -> None: assert isinstance(response.json(), str) +def test_delete_evaluation_dimension(create_mock_data: Callable[[], None]) -> None: + response = client.delete("/evaluation_dimensions/tmppk_evaluation_dimension_list") + assert response.status_code == 200 + assert isinstance(response.json(), str) + + # def test_simulate(create_mock_data: Callable[[], None]) -> None: # response = client.post( # "/simulate", From ae4014ec1e925044885d5cbfbbe71f265b84c362 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Fri, 13 Dec 2024 15:59:34 -0500 Subject: [PATCH 08/26] fix mypy error --- sotopia/ui/fastapi_server.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sotopia/ui/fastapi_server.py b/sotopia/ui/fastapi_server.py index 9bd0d4f3..2eacef02 100644 --- a/sotopia/ui/fastapi_server.py +++ b/sotopia/ui/fastapi_server.py @@ -245,8 +245,11 @@ async def get_episodes(get_by: Literal["id", "tag"], value: str) -> list[Episode ) async def get_evaluation_dimensions() -> dict[str, list[CustomEvaluationDimension]]: custom_evaluation_dimensions: dict[str, list[CustomEvaluationDimension]] = {} - custom_evaluation_dimension_list = CustomEvaluationDimensionList.all() - for custom_evaluation_dimension_list in custom_evaluation_dimension_list: + all_custom_evaluation_dimension_list = CustomEvaluationDimensionList.all() + for custom_evaluation_dimension_list in all_custom_evaluation_dimension_list: + assert isinstance( + custom_evaluation_dimension_list, CustomEvaluationDimensionList + ) custom_evaluation_dimensions[custom_evaluation_dimension_list.name] = [ CustomEvaluationDimension.get(pk=pk) for pk in custom_evaluation_dimension_list.dimension_pks From ab6903a2a8e957e6ecdd30f48c9f5ac291968891 Mon Sep 17 00:00:00 2001 From: Xuhui Zhou Date: Sat, 14 Dec 2024 15:47:11 -0500 Subject: [PATCH 09/26] aact moderator (#257) * initial framework * initial conv * fix module error * feat: Add 3 new features to Moderator (#266) * feat:introduce booting procedure, saving, and ending chat to moderator * fix: moderator will now ignore none AgentAction, Observations now don't have to include all channels in the mapping * merge changes of example into the original one * fix: 1. save() method now accepts push_to_db config 2. booting()'s waiting time is changed to 0.1 sec * fix: rewrite booting() so that different agent will receive different background information * fix: moderator now inherits from Node directly, instead of from BaseAgent --------- Co-authored-by: JXZhou * add save condition for moderator * push to db false * to fully stop * stopping all agents * fix mypy * fix mypy error --------- Co-authored-by: JXZhou <156194797+JXZhou0224@users.noreply.github.com> --- .../nodes/initial_message_node.py | 2 + .../llm_agent_sotopia.py | 113 ++++++++ .../sotopia_original_replica/origin.svg | 1 + .../sotopia_original_replica/origin.toml | 52 ++++ .../sotopia_original_replica/readme.md | 13 + pyproject.toml | 3 + sotopia/experimental/agents/base_agent.py | 2 + sotopia/experimental/agents/datamodels.py | 42 +++ sotopia/experimental/agents/moderator.py | 270 ++++++++++++++++++ tests/experimental/test_agent.py | 2 + uv.lock | 9 +- 11 files changed, 503 insertions(+), 6 deletions(-) create mode 100644 examples/experimental/sotopia_original_replica/llm_agent_sotopia.py create mode 100644 examples/experimental/sotopia_original_replica/origin.svg create mode 100644 examples/experimental/sotopia_original_replica/origin.toml create mode 100644 examples/experimental/sotopia_original_replica/readme.md create mode 100644 sotopia/experimental/agents/datamodels.py create mode 100644 sotopia/experimental/agents/moderator.py diff --git a/examples/experimental/nodes/initial_message_node.py b/examples/experimental/nodes/initial_message_node.py index 9cb7f63c..9ff4c3bd 100644 --- a/examples/experimental/nodes/initial_message_node.py +++ b/examples/experimental/nodes/initial_message_node.py @@ -18,6 +18,7 @@ def __init__( input_tick_channel: str, output_channels: list[str], env_scenario: str, + node_name: str, redis_url: str = "redis://localhost:6379/0", ): super().__init__( @@ -26,6 +27,7 @@ def __init__( (output_channel, Text) for output_channel in output_channels ], redis_url=redis_url, + node_name=node_name, ) self.env_scenario = env_scenario self.output_channels = output_channels diff --git a/examples/experimental/sotopia_original_replica/llm_agent_sotopia.py b/examples/experimental/sotopia_original_replica/llm_agent_sotopia.py new file mode 100644 index 00000000..abe95929 --- /dev/null +++ b/examples/experimental/sotopia_original_replica/llm_agent_sotopia.py @@ -0,0 +1,113 @@ +import logging +import sys +from rich.logging import RichHandler + +from aact import NodeFactory + +from sotopia.experimental.agents.base_agent import BaseAgent +from sotopia.experimental.agents.datamodels import Observation, AgentAction + +from sotopia.generation_utils import agenerate +from sotopia.generation_utils.generate import StrOutputParser + +# Check Python version +if sys.version_info >= (3, 11): + pass +else: + pass + +# Configure logging +FORMAT = "%(asctime)s - %(levelname)s - %(name)s - %(message)s" +logging.basicConfig( + level=logging.WARNING, + format=FORMAT, + datefmt="[%X]", + handlers=[RichHandler()], +) + + +@NodeFactory.register("llm_agent") +class LLMAgent(BaseAgent[Observation, AgentAction]): + def __init__( + self, + input_channels: list[str], + output_channel: str, + query_interval: int, + agent_name: str, + node_name: str, + goal: str, + model_name: str, + redis_url: str, + ): + super().__init__( + [(input_channel, Observation) for input_channel in input_channels], + [(output_channel, AgentAction)], + redis_url, + node_name, + ) + self.output_channel = output_channel + self.query_interval = query_interval + self.count_ticks = 0 + self.message_history: list[Observation] = [] + self.name = agent_name + self.model_name = model_name + self.goal = goal + + def _format_message_history(self, message_history: list[Observation]) -> str: + ## TODO: akhatua Fix the mapping of action to be gramatically correct + return "\n".join(message.to_natural_language() for message in message_history) + + async def aact(self, obs: Observation) -> AgentAction: + if obs.turn_number == -1: + return AgentAction( + agent_name=self.name, + output_channel=self.output_channel, + action_type="none", + argument=self.model_name, + ) + + self.message_history.append(obs) + + if len(obs.available_actions) == 1 and "none" in obs.available_actions: + return AgentAction( + agent_name=self.name, + output_channel=self.output_channel, + action_type="none", + argument="", + ) + elif len(obs.available_actions) == 1 and "leave" in obs.available_actions: + self.shutdown_event.set() + return AgentAction( + agent_name=self.name, + output_channel=self.output_channel, + action_type="leave", + argument="", + ) + else: + history = self._format_message_history(self.message_history) + action: str = await agenerate( + model_name=self.model_name, + template="Imagine that you are a friend of the other persons. Here is the " + "conversation between you and them.\n" + "You are {agent_name} in the conversation.\n" + "{message_history}\n" + "and you plan to {goal}.\n" + "You can choose to interrupt the other person " + "by saying something or not to interrupt by outputting notiong. What would you say? " + "Please only output a sentence or not outputting anything." + "{format_instructions}", + input_values={ + "message_history": history, + "goal": self.goal, + "agent_name": self.name, + }, + temperature=0.7, + output_parser=StrOutputParser(), + ) + + return AgentAction( + agent_name=self.name, + output_channel=self.output_channel, + action_type="speak", + argument=action, + ) diff --git a/examples/experimental/sotopia_original_replica/origin.svg b/examples/experimental/sotopia_original_replica/origin.svg new file mode 100644 index 00000000..78717b14 --- /dev/null +++ b/examples/experimental/sotopia_original_replica/origin.svg @@ -0,0 +1 @@ +

examples/experimental/sotopia_original_replica/origin.toml

Jane:moderator

Jack:moderator

moderator:Jane

moderator:Jack

Jane:Jack

Jack:Jane

Agent:Runtime

'Jane'

'moderator'

'Jack'

'chat_print'

diff --git a/examples/experimental/sotopia_original_replica/origin.toml b/examples/experimental/sotopia_original_replica/origin.toml new file mode 100644 index 00000000..7bf22527 --- /dev/null +++ b/examples/experimental/sotopia_original_replica/origin.toml @@ -0,0 +1,52 @@ +redis_url = "redis://localhost:6379/0" +extra_modules = ["examples.experimental.sotopia_original_replica.llm_agent_sotopia", "examples.experimental.nodes.chat_print_node", "sotopia.experimental.agents.moderator"] + + +[[nodes]] +node_name = "moderator" +node_class = "moderator" + +[nodes.node_args] +output_channels = ["moderator:Jane", "moderator:Jack"] +input_channels = ["Jane:moderator", "Jack:moderator"] +agent_backgrounds = {"Jane" = "", "Jack" = ""} +agent_mapping = {"moderator:Jane" = "Jane", "moderator:Jack" = "Jack"} +scenario = "Two friends are sitting in a cafe and catching up with each other's lives." +max_turns = 2 +push_to_db = false + +[[nodes]] +node_name = "Jack" +node_class = "llm_agent" + +[nodes.node_args] +query_interval = 5 +input_channels = ["moderator:Jack"] +output_channel = "Jack:moderator" +goal = "Your goal is to borrow 5000 dollars from Jane." +model_name = "gpt-4o-mini" +agent_name = "Jack" + + +[[nodes]] +node_name = "Jane" +node_class = "llm_agent" + +[nodes.node_args] +query_interval = 7 +output_channel = "Jane:moderator" +input_channels = ["moderator:Jane"] +goal = "Your goal is to help Jack however, you are in a finicial crisis yourself and can only afford to give him 500 dollars." +model_name = "gpt-4o-mini" +agent_name = "Jane" + +[[nodes]] +node_name = "chat_print" +node_class = "chat_print" + +[nodes.node_args.print_channel_types] +"Jane:moderator" = "agent_action" +"Jack:moderator" = "agent_action" + +[nodes.node_args] +env_agents = ["Jack", "Jane"] diff --git a/examples/experimental/sotopia_original_replica/readme.md b/examples/experimental/sotopia_original_replica/readme.md new file mode 100644 index 00000000..cb3931dc --- /dev/null +++ b/examples/experimental/sotopia_original_replica/readme.md @@ -0,0 +1,13 @@ +To run this example, please use aact to launch. + +```bash +aact run-dataflow examples/experimental/sotopia_original_replica/origin.toml +``` + +To view the flow of the information, please run: + +```bash +aact draw-dataflow examples/experimental/sotopia_original_replica/origin.toml --svg-path examples/experimental/sotopia_original_replica/origin.svg +``` + +![Alt text](./origin.svg) diff --git a/pyproject.toml b/pyproject.toml index 57af6cc3..b9edcc94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,9 @@ plugins = [ module = "transformers.*" ignore_missing_imports = true +[tool.uv.sources] +aact = { git = "https://github.com/ProKil/aact" , branch = "feature/node-manager" } + [tool.pytest.ini_options] testpaths = ["tests"] python_files = "test_*.py" diff --git a/sotopia/experimental/agents/base_agent.py b/sotopia/experimental/agents/base_agent.py index a7bbafae..6d9466bb 100644 --- a/sotopia/experimental/agents/base_agent.py +++ b/sotopia/experimental/agents/base_agent.py @@ -22,11 +22,13 @@ def __init__( input_channel_types: list[tuple[str, type[T_agent_observation]]], output_channel_types: list[tuple[str, type[T_agent_action]]], redis_url: str = "redis://localhost:6379/0", + node_name: str = "base_agent", ): super().__init__( input_channel_types=input_channel_types, output_channel_types=output_channel_types, redis_url=redis_url, + node_name=node_name, ) self.observation_queue: asyncio.Queue[T_agent_observation] = asyncio.Queue() diff --git a/sotopia/experimental/agents/datamodels.py b/sotopia/experimental/agents/datamodels.py new file mode 100644 index 00000000..a243a52a --- /dev/null +++ b/sotopia/experimental/agents/datamodels.py @@ -0,0 +1,42 @@ +from sotopia.messages import ActionType +from aact.messages import DataModel, DataModelFactory +from pydantic import Field + + +@DataModelFactory.register("observation") +class Observation(DataModel): + agent_name: str = Field(description="the name of the agent") + last_turn: str = Field(description="the last turn of the conversation") + turn_number: int = Field(description="the turn number of the conversation") + available_actions: list[ActionType] = Field(description="the available actions") + + def to_natural_language(self) -> str: + if self.turn_number == 0: + return f"\n{self.last_turn}\nConversation Starts:\n" + else: + return f"Turn #{self.turn_number-1}: {self.last_turn}\n" + + +@DataModelFactory.register("agent_action") +class AgentAction(DataModel): + agent_name: str = Field(description="the name of the agent") + output_channel: str = Field(description="the name of the output channel") + action_type: ActionType = Field( + description="whether to speak at this turn or choose to not do anything" + ) + argument: str = Field( + description="the utterance if choose to speak, the expression or gesture if choose non-verbal communication, or the physical action if choose action" + ) + + def to_natural_language(self) -> str: + match self.action_type: + case "none": + return "did nothing" + case "speak": + return f'said: "{self.argument}"' + case "non-verbal communication": + return f"[{self.action_type}] {self.argument}" + case "action": + return f"[{self.action_type}] {self.argument}" + case "leave": + return "left the conversation" diff --git a/sotopia/experimental/agents/moderator.py b/sotopia/experimental/agents/moderator.py new file mode 100644 index 00000000..ce57fb38 --- /dev/null +++ b/sotopia/experimental/agents/moderator.py @@ -0,0 +1,270 @@ +import asyncio +import sys + + +if sys.version_info < (3, 11): + from typing_extensions import Self +else: + from typing import Self + + +from aact import Message, NodeFactory, Node +from aact.messages import DataModel, DataModelFactory + +from typing import Literal, Any, AsyncIterator +from pydantic import Field + +from sotopia.database import EpisodeLog +from .datamodels import AgentAction, Observation +from sotopia.messages import ActionType + + +@DataModelFactory.register("observations") +class Observations(DataModel): + observations_map: dict[str, Observation] = Field( + description="the observations of the agents" + ) + + +@NodeFactory.register("moderator") +class Moderator(Node[AgentAction, Observation]): + def __init__( + self, + input_channels: list[str], + output_channels: list[str], + scenario: str, + agent_mapping: dict[str, str], + node_name: str, + agent_backgrounds: dict[str, str], + redis_url: str = "redis://localhost:6379/0", + action_order: Literal["simultaneous", "round-robin", "random"] = "round-robin", + available_actions: list[ActionType] = [ + "none", + "speak", + "non-verbal communication", + "action", + "leave", + ], + max_turns: int = 20, + push_to_db: bool = False, + ): + super().__init__( + input_channel_types=[ + (input_channel, AgentAction) for input_channel in input_channels + ], + output_channel_types=[ + (output_channel, Observation) for output_channel in output_channels + ], + redis_url=redis_url, + node_name=node_name, + ) + self.observation_queue: asyncio.Queue[AgentAction] = asyncio.Queue() + self.task_scheduler: asyncio.Task[None] | None = None + self.shutdown_event: asyncio.Event = asyncio.Event() + self.agent_mapping: dict[str, str] = agent_mapping + self.action_order: Literal["simultaneous", "round-robin", "random"] = ( + action_order + ) + self.available_actions: list[ActionType] = available_actions + self.turn_number: int = 0 + self.max_turns: int = max_turns + self.current_agent_index: int = 0 + self.scenario: str = scenario + self.agents: list[str] = list(agent_mapping.values()) + self.agent_models: dict[str, str] = {} + self.agents_awake: dict[str, bool] = {name: False for name in self.agents} + self.all_agents_awake: asyncio.Event = asyncio.Event() + self.message_history: list[list[tuple[str, str, str]]] = [ + [("Environment", "Environment", self.scenario)] + ] + self.push_to_db = push_to_db + self.agent_backgrounds = agent_backgrounds + + if self.action_order == "round-robin": + pass + else: + raise NotImplementedError( + "the selected action order is currently not implemented" + ) + + async def __aenter__(self) -> Self: + print(self.scenario) + asyncio.create_task(self.booting()) + self.task_scheduler = asyncio.create_task(self._task_scheduler()) + return await super().__aenter__() + + async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: + self.shutdown_event.set() + if self.task_scheduler is not None: + self.task_scheduler.cancel() + return await super().__aexit__(exc_type, exc_value, traceback) + + async def send(self, observations: Observations) -> None: + for output_channel, output_channel_type in self.output_channel_types.items(): + if output_channel in observations.observations_map: + await self.r.publish( + output_channel, + Message[output_channel_type]( # type:ignore[valid-type] + data=observations.observations_map[output_channel] + ).model_dump_json(), + ) + + async def event_handler( + self, channel: str, message: Message[AgentAction] + ) -> AsyncIterator[tuple[str, Message[Observation]]]: + if channel in self.input_channel_types: + await self.observation_queue.put(message.data) + else: + raise ValueError(f"Invalid channel: {channel}") + yield "", self.output_type() + + async def _task_scheduler(self) -> None: + await self.all_agents_awake.wait() + while not self.shutdown_event.is_set(): + observation = await self.observation_queue.get() + action_or_none = await self.aact(observation) + if action_or_none is not None: + await self.send(action_or_none) + self.observation_queue.task_done() + + async def booting(self) -> None: + """ + 1. send checking message to agents for every 0.1 seconds, until all agents are awake + - this message has turn_number of -1 for identification, agents should not record this into actual message_history + - if the agent booted succesfully, he is expected to return its model name for record. + 2. (under round-robin action order)after all agents are awake, send agent[0] a message to allow the agent to start speaking + """ + while not self.all_agents_awake.is_set(): + await self.send( + Observations( + observations_map={ + output_channel: Observation( + agent_name="moderator", + last_turn=self.scenario, + turn_number=-1, + available_actions=["none"], + ) + for output_channel, agent_name in self.agent_mapping.items() + } + ) + ) + await asyncio.sleep(0.1) + while not self.observation_queue.empty(): + agent_action = await self.observation_queue.get() + self.agents_awake[agent_action.agent_name] = True + self.agent_models[agent_action.agent_name] = agent_action.argument + if False not in self.agents_awake.values(): + self.all_agents_awake.set() + + if self.action_order == "round-robin": + await self.send( + Observations( + observations_map={ + output_channel: Observation( + agent_name="moderator", + last_turn=self.agent_backgrounds[agent_name], + turn_number=0, + available_actions=self.available_actions + if agent_name == self.agents[0] + else ["none"], + ) + for output_channel, agent_name in self.agent_mapping.items() + } + ) + ) + self.current_agent_index += 1 + + async def wrap_up_and_stop(self) -> None: + if self.push_to_db: + await self.save() + await asyncio.sleep(0.5) + print("stopping all agents") + await self.r.publish( + f"shutdown:{self.node_name}", + "shutdown", + ) + + async def save(self) -> EpisodeLog: + """ + save the EpisodeLog to redis, without evaluating + TODO: specify what to be added inside tag + TODO: update the code so that EpisodeLog.render_for_humans() can work + -currently it cannot work because no AgentProfile has been uploaded to redis + -such a process should be done back in the agents' end + -also the current agentslist is consist of names, but not uuid's of agents + """ + epilog = EpisodeLog( + environment=self.scenario, + agents=self.agents, + tag=None, + models=list(self.agent_models.values()), + messages=self.message_history, + reasoning="", + rewards=[0] * len(self.agents), + rewards_prompt="", + ) + epilog.save() + # print(epilog.render_for_humans()) + return epilog + + async def aact(self, agent_action: AgentAction) -> Observations | None: + if agent_action.action_type == "leave": + self.agents_awake[agent_action.agent_name] = False + if True not in self.agents_awake.values(): + await self.wrap_up_and_stop() + return None + if agent_action.action_type == "none": + return None + + if len(self.message_history) == 1: + self.message_history[0].append( + ( + agent_action.agent_name, + "Environment", + agent_action.to_natural_language(), + ) + ) + else: + self.message_history.append( + [ + ( + agent_action.agent_name, + "Environment", + agent_action.to_natural_language(), + ) + ] + ) + + if self.turn_number < self.max_turns: + self.turn_number += 1 + else: + return Observations( + observations_map={ + output_channel: Observation( + agent_name="moderator", + last_turn=self.scenario, + turn_number=self.turn_number + 1, + available_actions=["leave"], + ) + for output_channel, agent_name in self.agent_mapping.items() + } + ) + + observations_map: dict[str, Observation] = {} + for output_channel, output_channel_type in self.output_channel_types.items(): + agent_name = self.agent_mapping[output_channel] + available_actions: list[ActionType] = ["none"] + if self.action_order == "round-robin": + if agent_name == self.agents[self.current_agent_index]: + available_actions = self.available_actions + + observation = Observation( + agent_name=agent_name, + last_turn=agent_action.to_natural_language(), + turn_number=self.turn_number, + available_actions=available_actions, + ) + observations_map[output_channel] = observation + self.current_agent_index = (self.current_agent_index + 1) % len(self.agents) + + return Observations(observations_map=observations_map) diff --git a/tests/experimental/test_agent.py b/tests/experimental/test_agent.py index 020c2131..834c4286 100644 --- a/tests/experimental/test_agent.py +++ b/tests/experimental/test_agent.py @@ -19,11 +19,13 @@ async def aact(self, observation: Tick) -> Tick: @pytest.mark.asyncio async def test_base_agent() -> None: async with ReturnPlusOneAgent( + node_name="test_base_agent", input_channel_types=[("input", Tick)], output_channel_types=[("output", Tick)], redis_url="redis://localhost:6379/0", ) as agent1: async with ReturnPlusOneAgent( + node_name="test_base_agent_2", input_channel_types=[("output", Tick)], output_channel_types=[("final", Tick)], redis_url="redis://localhost:6379/0", diff --git a/uv.lock b/uv.lock index 5017e0e0..217200dd 100644 --- a/uv.lock +++ b/uv.lock @@ -10,9 +10,10 @@ resolution-markers = [ [[package]] name = "aact" version = "0.0.10" -source = { registry = "https://pypi.org/simple" } +source = { git = "https://github.com/ProKil/aact?branch=feature%2Fnode-manager#56cd2a2aad8a0e806e4f3a170e848cb1e1ad0720" } dependencies = [ { name = "aiofiles" }, + { name = "aiohttp" }, { name = "aiostream" }, { name = "numpy" }, { name = "pydantic" }, @@ -22,10 +23,6 @@ dependencies = [ { name = "tomlkit", marker = "python_full_version < '3.11'" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/9f/2b32aca3e2fe614df4e04a074870b6b27ef037af62f639b0e4d0b33abb31/aact-0.0.10.tar.gz", hash = "sha256:0cde5360d27bab002a43e9895c4006bfa541f6c2db798412f4aad1fdb685632e", size = 113329 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/18/32beed32416f8c9618ed4fc42e33eef94d7c181caf59c6909b3841047006/aact-0.0.10-py3-none-any.whl", hash = "sha256:2c1959666270acc681aafc1452aa089cb26a24a0871b01faa7761fa300b2fc9a", size = 29102 }, -] [[package]] name = "absl-py" @@ -3144,7 +3141,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "aact" }, + { name = "aact", git = "https://github.com/ProKil/aact?branch=feature%2Fnode-manager" }, { name = "absl-py", specifier = ">=2.0.0,<3.0.0" }, { name = "anthropic", marker = "extra == 'anthropic'" }, { name = "beartype", specifier = ">=0.14.0,<0.20.0" }, From 1f4fb0ab38a472a3c09d752b3211be641117c608 Mon Sep 17 00:00:00 2001 From: Zhe Su <360307598@qq.com> Date: Sat, 28 Dec 2024 11:59:56 -0500 Subject: [PATCH 10/26] Deploy the api to modal (#267) * prototype for modal serving * add openai secret * fix type annotation * add doc * bug fix for simulation api * add customize model, evaluator model and evaluation dimensions * Implement modal API server with Redis integration and FastAPI setup - Added a new script for the modal API server that initializes a Redis instance. - Created a persistent volume for Redis data and included a function to download initial data if not present. - Configured a Docker image with necessary dependencies including Redis Stack and FastAPI. - Implemented a web API class that sets up and cleans up the Redis connection, ensuring readiness before serving requests. - Integrated the SotopiaFastAPI application within the modal framework. --------- Co-authored-by: XuhuiZhou --- docs/pages/examples/deployment.md | 6 + scripts/modal/modal_api_server.py | 102 ++++ sotopia/ui/README.md | 9 +- sotopia/ui/fastapi_server.py | 822 ++++++++++++++++-------------- sotopia/ui/websocket_utils.py | 44 +- 5 files changed, 572 insertions(+), 411 deletions(-) create mode 100644 docs/pages/examples/deployment.md create mode 100644 scripts/modal/modal_api_server.py diff --git a/docs/pages/examples/deployment.md b/docs/pages/examples/deployment.md new file mode 100644 index 00000000..6a818570 --- /dev/null +++ b/docs/pages/examples/deployment.md @@ -0,0 +1,6 @@ +# Deploy Sotopia Python API to Modal +We offer a script to deploy Sotopia Python API to [Modal](https://modal.com/). +To do so, simply go to the `sotopia/sotopia/ui` directory and run the following command: +```bash +modal deploy sotopia/ui/modal_api_server.py +``` diff --git a/scripts/modal/modal_api_server.py b/scripts/modal/modal_api_server.py new file mode 100644 index 00000000..30c8c8da --- /dev/null +++ b/scripts/modal/modal_api_server.py @@ -0,0 +1,102 @@ +import modal +import subprocess +import time +import os + +import redis +from sotopia.ui.fastapi_server import SotopiaFastAPI + +# Create persistent volume for Redis data +redis_volume = modal.Volume.from_name("sotopia-api", create_if_missing=True) + + +def initialize_redis_data() -> None: + """Download Redis data if it doesn't exist""" + if not os.path.exists("/vol/redis/dump.rdb"): + os.makedirs("/vol/redis", exist_ok=True) + print("Downloading initial Redis data...") + subprocess.run( + "curl -L https://cmu.box.com/shared/static/e3vd31r7916jb70j9cgtcq9etryrxml0.rdb --output /vol/redis/dump.rdb", + shell=True, + check=True, + ) + print("Redis data downloaded") + + +# Create image with all necessary dependencies +image = ( + modal.Image.debian_slim(python_version="3.11") + .apt_install( + "git", + "curl", + "gpg", + "lsb-release", + "wget", + "procps", # for ps command + "redis-tools", # for redis-cli + ) + .run_commands( + # Update and install basic dependencies + "apt-get update", + "apt-get install -y curl gpg lsb-release", + # Add Redis Stack repository + "curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg", + "chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg", + 'echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/redis.list', + "apt-get update", + "apt-get install -y redis-stack-server", + ) + .pip_install( + "sotopia>=0.1.2", + "fastapi>0.100", # TODO: remove this dependency after pypi release + "uvicorn", # TODO: remove this dependency after pypi release + ) +) +redis_volume = modal.Volume.from_name("sotopia-api", create_if_missing=True) + +# Create stub for the application +app = modal.App("sotopia-fastapi", image=image, volumes={"/vol/redis": redis_volume}) + + +@app.cls( + image=image, + concurrency_limit=1, + allow_concurrent_inputs=5, + secrets=[modal.Secret.from_name("openai-secret")], +) +class WebAPI: + def __init__(self) -> None: + self.web_app = SotopiaFastAPI() + + @modal.enter() + def setup(self) -> None: + # Start Redis server + subprocess.Popen( + ["redis-stack-server", "--dir", "/vol/redis", "--port", "6379"] + ) + + # Wait for Redis to be ready + max_retries = 30 + for _ in range(max_retries): + try: + initialize_redis_data() + # Attempt to create Redis client and ping the server + temp_client = redis.Redis(host="localhost", port=6379, db=0) + temp_client.ping() + self.redis_client = temp_client + print("Successfully connected to Redis") + return + except (redis.exceptions.ConnectionError, redis.exceptions.ResponseError): + print("Waiting for Redis to be ready...") + time.sleep(1) + + raise Exception("Could not connect to Redis after multiple attempts") + + @modal.exit() + def cleanup(self) -> None: + if hasattr(self, "redis_client"): + self.redis_client.close() + + @modal.asgi_app() + def serve(self) -> SotopiaFastAPI: + return self.web_app diff --git a/sotopia/ui/README.md b/sotopia/ui/README.md index 4d8bb773..f483f3ea 100644 --- a/sotopia/ui/README.md +++ b/sotopia/ui/README.md @@ -155,7 +155,7 @@ returns: **WSMessageType** | Type | Direction | Description | |-----------|--------|-------------| -| SERVER_MSG | Server → Client | Standard message from server (payload: `messageForRendering` [here](https://github.com/sotopia-lab/sotopia-demo/blob/main/socialstream/rendering_utils.py) ) | +| SERVER_MSG | Server → Client | Standard message from server (payload: `EpisodeLog`) | | CLIENT_MSG | Client → Server | Standard message from client (payload: TBD) | | ERROR | Server → Client | Error notification (payload: TBD) | | START_SIM | Client → Server | Initialize simulation (payload: `SimulationEpisodeInitialization`) | @@ -179,6 +179,13 @@ returns: **Implementation plan**: Currently only support LLM-LLM simulation based on [this function](https://github.com/sotopia-lab/sotopia/blob/19d39e068c3bca9246fc366e5759414f62284f93/sotopia/server.py#L108). +**SERVER_MSG payload** +The server message is a dictionary that has the following keys: +- type: str, indicates the type of the message, typically it is "messages" +- messages: Any. Typically this is the dictionary of the `EpisodeLog` for the current simulation state. (Which means the reward part could be empty) + + + ## An example to run simulation with the API diff --git a/sotopia/ui/fastapi_server.py b/sotopia/ui/fastapi_server.py index 2eacef02..ce5e1728 100644 --- a/sotopia/ui/fastapi_server.py +++ b/sotopia/ui/fastapi_server.py @@ -53,15 +53,19 @@ logger = logging.getLogger(__name__) -app = FastAPI() +# app = FastAPI() -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) # TODO: Whether allowing CORS for all origins +# app.add_middleware( +# CORSMiddleware, +# allow_origins=["*"], +# allow_credentials=True, +# allow_methods=["*"], +# allow_headers=["*"], +# ) # TODO: Whether allowing CORS for all origins + +active_simulations: Dict[ + str, bool +] = {} # TODO check whether this is the correct way to store the active simulations class RelationshipWrapper(BaseModel): @@ -151,181 +155,126 @@ def validate_models(self) -> Self: return self -@app.get("/scenarios", response_model=list[EnvironmentProfile]) -async def get_scenarios_all() -> list[EnvironmentProfile]: - return EnvironmentProfile.all() - - -@app.get("/scenarios/{get_by}/{value}", response_model=list[EnvironmentProfile]) -async def get_scenarios( - get_by: Literal["id", "codename"], value: str -) -> list[EnvironmentProfile]: - # Implement logic to fetch scenarios based on the parameters - scenarios: list[EnvironmentProfile] = [] # Replace with actual fetching logic - if get_by == "id": - scenarios.append(EnvironmentProfile.get(pk=value)) - elif get_by == "codename": - json_models = EnvironmentProfile.find( - EnvironmentProfile.codename == value - ).all() - scenarios.extend(cast(list[EnvironmentProfile], json_models)) - - if not scenarios: - raise HTTPException( - status_code=404, detail=f"No scenarios found with {get_by}={value}" - ) - - return scenarios - - -@app.get("/agents", response_model=list[AgentProfile]) -async def get_agents_all() -> list[AgentProfile]: - return AgentProfile.all() - - -@app.get("/agents/{get_by}/{value}", response_model=list[AgentProfile]) -async def get_agents( - get_by: Literal["id", "gender", "occupation"], value: str -) -> list[AgentProfile]: - agents_profiles: list[AgentProfile] = [] - if get_by == "id": - agents_profiles.append(AgentProfile.get(pk=value)) - elif get_by == "gender": - json_models = AgentProfile.find(AgentProfile.gender == value).all() - agents_profiles.extend(cast(list[AgentProfile], json_models)) - elif get_by == "occupation": - json_models = AgentProfile.find(AgentProfile.occupation == value).all() - agents_profiles.extend(cast(list[AgentProfile], json_models)) - - if not agents_profiles: - raise HTTPException( - status_code=404, detail=f"No agents found with {get_by}={value}" - ) - - return agents_profiles - - -@app.get("/relationship/{agent_1_id}/{agent_2_id}", response_model=str) -async def get_relationship(agent_1_id: str, agent_2_id: str) -> str: - relationship_profiles = RelationshipProfile.find( - (RelationshipProfile.agent_1_id == agent_1_id) - & (RelationshipProfile.agent_2_id == agent_2_id) - ).all() - assert ( - len(relationship_profiles) == 1 - ), f"{len(relationship_profiles)} relationship profiles found for agents {agent_1_id} and {agent_2_id}, expected 1" - relationship_profile = relationship_profiles[0] - assert isinstance(relationship_profile, RelationshipProfile) - return f"{str(relationship_profile.relationship)}: {RelationshipType(relationship_profile.relationship).name}" - +class SimulationState: + _instance: Optional["SimulationState"] = None + _lock = asyncio.Lock() + _active_simulations: dict[str, bool] = {} -@app.get("/episodes", response_model=list[EpisodeLog]) -async def get_episodes_all() -> list[EpisodeLog]: - return EpisodeLog.all() + def __new__(cls) -> "SimulationState": + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._instance._active_simulations = {} + return cls._instance + async def try_acquire_token(self, token: str) -> tuple[bool, str]: + async with self._lock: + if not token: + return False, "Invalid token" -@app.get("/episodes/{get_by}/{value}", response_model=list[EpisodeLog]) -async def get_episodes(get_by: Literal["id", "tag"], value: str) -> list[EpisodeLog]: - episodes: list[EpisodeLog] = [] - if get_by == "id": - episodes.append(EpisodeLog.get(pk=value)) - elif get_by == "tag": - json_models = EpisodeLog.find(EpisodeLog.tag == value).all() - episodes.extend(cast(list[EpisodeLog], json_models)) + if self._active_simulations.get(token): + return False, "Token is active already" - if not episodes: - raise HTTPException( - status_code=404, detail=f"No episodes found with {get_by}={value}" - ) - return episodes + self._active_simulations[token] = True + return True, "Token is valid" + async def release_token(self, token: str) -> None: + async with self._lock: + self._active_simulations.pop(token, None) -@app.get( - "/evaluation_dimensions/", response_model=dict[str, list[CustomEvaluationDimension]] -) -async def get_evaluation_dimensions() -> dict[str, list[CustomEvaluationDimension]]: - custom_evaluation_dimensions: dict[str, list[CustomEvaluationDimension]] = {} - all_custom_evaluation_dimension_list = CustomEvaluationDimensionList.all() - for custom_evaluation_dimension_list in all_custom_evaluation_dimension_list: - assert isinstance( - custom_evaluation_dimension_list, CustomEvaluationDimensionList - ) - custom_evaluation_dimensions[custom_evaluation_dimension_list.name] = [ - CustomEvaluationDimension.get(pk=pk) - for pk in custom_evaluation_dimension_list.dimension_pks - ] - return custom_evaluation_dimensions + @asynccontextmanager + async def start_simulation(self, token: str) -> AsyncIterator[bool]: + try: + yield True + finally: + await self.release_token(token) -@app.post("/scenarios/", response_model=str) -async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: - scenario_profile = EnvironmentProfile(**scenario.model_dump()) - scenario_profile.save() - pk = scenario_profile.pk - assert pk is not None - return pk +class SimulationManager: + def __init__(self) -> None: + self.state = SimulationState() + async def verify_token(self, token: str) -> dict[str, Any]: + is_valid, msg = await self.state.try_acquire_token(token) + return {"is_valid": is_valid, "msg": msg} -@app.post("/agents/", response_model=str) -async def create_agent(agent: AgentProfileWrapper) -> str: - agent_profile = AgentProfile(**agent.model_dump()) - agent_profile.save() - pk = agent_profile.pk - assert pk is not None - return pk + async def create_simulator( + self, + env_id: str, + agent_ids: list[str], + agent_models: list[str], + evaluator_model: str, + evaluation_dimension_list_name: str, + ) -> WebSocketSotopiaSimulator: + try: + return WebSocketSotopiaSimulator( + env_id=env_id, + agent_ids=agent_ids, + agent_models=agent_models, + evaluator_model=evaluator_model, + evaluation_dimension_list_name=evaluation_dimension_list_name, + ) + except Exception as e: + error_msg = f"Failed to create simulator: {e}" + logger.error(error_msg) + raise Exception(error_msg) + async def handle_client_message( + self, + websocket: WebSocket, + simulator: WebSocketSotopiaSimulator, + message: dict[str, Any], + timeout: float = 0.1, + ) -> bool: + try: + msg_type = message.get("type") + if msg_type == WSMessageType.FINISH_SIM.value: + return True + # TODO handle other message types + return False + except Exception as e: + msg = f"Error handling client message: {e}" + logger.error(msg) + await self.send_error(websocket, ErrorType.INVALID_MESSAGE, msg) + return False -@app.post("/relationship/", response_model=str) -async def create_relationship(relationship: RelationshipWrapper) -> str: - relationship_profile = RelationshipProfile(**relationship.model_dump()) - relationship_profile.save() - pk = relationship_profile.pk - assert pk is not None - return pk + async def run_simulation( + self, websocket: WebSocket, simulator: WebSocketSotopiaSimulator + ) -> None: + try: + async for message in simulator.arun(): + await self.send_message(websocket, WSMessageType.SERVER_MSG, message) + try: + data = await asyncio.wait_for(websocket.receive_json(), timeout=0.1) + if await self.handle_client_message(websocket, simulator, data): + break + except asyncio.TimeoutError: + continue -@app.post("/evaluation_dimensions/", response_model=str) -async def create_evaluation_dimensions( - evaluation_dimensions: CustomEvaluationDimensionsWrapper, -) -> str: - dimension_list = CustomEvaluationDimensionList.find( - CustomEvaluationDimensionList.name == evaluation_dimensions.name - ).all() + except Exception as e: + msg = f"Error running simulation: {e}" + logger.error(msg) + await self.send_error(websocket, ErrorType.SIMULATION_ISSUE, msg) + finally: + await self.send_message(websocket, WSMessageType.END_SIM, {}) - if len(dimension_list) == 0: - all_dimensions_pks = [] - for dimension in evaluation_dimensions.dimensions: - find_dimension = CustomEvaluationDimension.find( - CustomEvaluationDimension.name == dimension.name - ).all() - if len(find_dimension) == 0: - dimension.save() - all_dimensions_pks.append(dimension.pk) - elif len(find_dimension) == 1: - all_dimensions_pks.append(find_dimension[0].pk) - else: - raise HTTPException( - status_code=409, - detail=f"Evaluation dimension with name={dimension.name} already exists", - ) + @staticmethod + async def send_message( + websocket: WebSocket, msg_type: WSMessageType, data: dict[str, Any] + ) -> None: + await websocket.send_json({"type": msg_type.value, "data": data}) - custom_evaluation_dimension_list = CustomEvaluationDimensionList( - pk=evaluation_dimensions.pk, - name=evaluation_dimensions.name, - dimension_pks=all_dimensions_pks, - ) - custom_evaluation_dimension_list.save() - logger.info(f"Created evaluation dimension list {evaluation_dimensions.name}") - else: - raise HTTPException( - status_code=409, - detail=f"Evaluation dimension list with name={evaluation_dimensions.name} already exists", + @staticmethod + async def send_error( + websocket: WebSocket, error_type: ErrorType, details: str = "" + ) -> None: + await websocket.send_json( + { + "type": WSMessageType.ERROR.value, + "data": {"type": error_type.value, "details": details}, + } ) - pk = custom_evaluation_dimension_list.pk - assert pk is not None - return pk - async def run_simulation( episode_pk: str, @@ -397,273 +346,354 @@ async def run_simulation( ) -@app.post("/simulate/", response_model=str) -def simulate(simulation_request: SimulationRequest) -> Response: - try: - _: EnvironmentProfile = EnvironmentProfile.get(pk=simulation_request.env_id) - except Exception: # TODO Check the exception type - raise HTTPException( - status_code=404, - detail=f"Environment with id={simulation_request.env_id} not found", - ) - try: - __ = AgentProfile.get(pk=simulation_request.agent_ids[0]) - except Exception: # TODO Check the exception type - raise HTTPException( - status_code=404, - detail=f"Agent with id={simulation_request.agent_ids[0]} not found", - ) - try: - ___ = AgentProfile.get(pk=simulation_request.agent_ids[1]) - except Exception: # TODO Check the exception type +async def get_scenarios_all() -> list[EnvironmentProfile]: + return EnvironmentProfile.all() + + +async def get_scenarios( + get_by: Literal["id", "codename"], value: str +) -> list[EnvironmentProfile]: + # Implement logic to fetch scenarios based on the parameters + scenarios: list[EnvironmentProfile] = [] # Replace with actual fetching logic + if get_by == "id": + scenarios.append(EnvironmentProfile.get(pk=value)) + elif get_by == "codename": + json_models = EnvironmentProfile.find( + EnvironmentProfile.codename == value + ).all() + scenarios.extend(cast(list[EnvironmentProfile], json_models)) + + if not scenarios: raise HTTPException( - status_code=404, - detail=f"Agent with id={simulation_request.agent_ids[1]} not found", + status_code=404, detail=f"No scenarios found with {get_by}={value}" ) - episode_pk = EpisodeLog( - environment="", - agents=[], - models=[], - messages=[], - reasoning="", - rewards=[], # Pseudorewards - rewards_prompt="", - ).pk - try: - simulation_status = NonStreamingSimulationStatus( - episode_pk=episode_pk, - status="Started", - ) - simulation_status.save() - queue = rq.Queue("default", connection=get_redis_connection()) - queue.enqueue( - run_simulation, - episode_pk=episode_pk, - simulation_request=simulation_request, - simulation_status=simulation_status, - ) + return scenarios - except Exception as e: - logger.error(f"Error starting simulation: {e}") - simulation_status.status = "Error" - simulation_status.save() - return Response(content=episode_pk, status_code=202) +async def get_agents_all() -> list[AgentProfile]: + return AgentProfile.all() -@app.get("/simulation_status/{episode_pk}", response_model=str) -async def get_simulation_status(episode_pk: str) -> str: - status = NonStreamingSimulationStatus.find( - NonStreamingSimulationStatus.episode_pk == episode_pk - ).all()[0] - assert isinstance(status, NonStreamingSimulationStatus) - return status.status +async def get_agents( + get_by: Literal["id", "gender", "occupation"], value: str +) -> list[AgentProfile]: + agents_profiles: list[AgentProfile] = [] + if get_by == "id": + agents_profiles.append(AgentProfile.get(pk=value)) + elif get_by == "gender": + json_models = AgentProfile.find(AgentProfile.gender == value).all() + agents_profiles.extend(cast(list[AgentProfile], json_models)) + elif get_by == "occupation": + json_models = AgentProfile.find(AgentProfile.occupation == value).all() + agents_profiles.extend(cast(list[AgentProfile], json_models)) -@app.delete("/agents/{agent_id}", response_model=str) -async def delete_agent(agent_id: str) -> str: - try: - agent = AgentProfile.get(pk=agent_id) - except Exception: # TODO Check the exception type + if not agents_profiles: raise HTTPException( - status_code=404, detail=f"Agent with id={agent_id} not found" + status_code=404, detail=f"No agents found with {get_by}={value}" ) - AgentProfile.delete(agent.pk) - assert agent.pk is not None - return agent.pk + return agents_profiles -@app.delete("/scenarios/{scenario_id}", response_model=str) -async def delete_scenario(scenario_id: str) -> str: - try: - scenario = EnvironmentProfile.get(pk=scenario_id) - except Exception: # TODO Check the exception type - raise HTTPException( - status_code=404, detail=f"Scenario with id={scenario_id} not found" - ) - EnvironmentProfile.delete(scenario.pk) - assert scenario.pk is not None - return scenario.pk +async def get_relationship(agent_1_id: str, agent_2_id: str) -> str: + relationship_profiles = RelationshipProfile.find( + (RelationshipProfile.agent_1_id == agent_1_id) + & (RelationshipProfile.agent_2_id == agent_2_id) + ).all() + assert ( + len(relationship_profiles) == 1 + ), f"{len(relationship_profiles)} relationship profiles found for agents {agent_1_id} and {agent_2_id}, expected 1" + relationship_profile = relationship_profiles[0] + assert isinstance(relationship_profile, RelationshipProfile) + return f"{str(relationship_profile.relationship)}: {RelationshipType(relationship_profile.relationship).name}" -@app.delete("/relationship/{relationship_id}", response_model=str) -async def delete_relationship(relationship_id: str) -> str: - RelationshipProfile.delete(relationship_id) - return relationship_id +async def get_episodes_all() -> list[EpisodeLog]: + return EpisodeLog.all() -@app.delete("/episodes/{episode_id}", response_model=str) -async def delete_episode(episode_id: str) -> str: - EpisodeLog.delete(episode_id) - return episode_id +async def get_episodes(get_by: Literal["id", "tag"], value: str) -> list[EpisodeLog]: + episodes: list[EpisodeLog] = [] + if get_by == "id": + episodes.append(EpisodeLog.get(pk=value)) + elif get_by == "tag": + json_models = EpisodeLog.find(EpisodeLog.tag == value).all() + episodes.extend(cast(list[EpisodeLog], json_models)) -@app.delete("/evaluation_dimensions/{evaluation_dimension_list_pk}", response_model=str) -async def delete_evaluation_dimension_list(evaluation_dimension_list_pk: str) -> str: - for dimension_pk in CustomEvaluationDimensionList.get( - evaluation_dimension_list_pk - ).dimension_pks: - CustomEvaluationDimension.delete(dimension_pk) - CustomEvaluationDimensionList.delete(evaluation_dimension_list_pk) - return evaluation_dimension_list_pk + if not episodes: + raise HTTPException( + status_code=404, detail=f"No episodes found with {get_by}={value}" + ) + return episodes -active_simulations: Dict[ - str, bool -] = {} # TODO check whether this is the correct way to store the active simulations +async def get_evaluation_dimensions() -> dict[str, list[CustomEvaluationDimension]]: + custom_evaluation_dimensions: dict[str, list[CustomEvaluationDimension]] = {} + all_custom_evaluation_dimension_list = CustomEvaluationDimensionList.all() + for custom_evaluation_dimension_list in all_custom_evaluation_dimension_list: + assert isinstance( + custom_evaluation_dimension_list, CustomEvaluationDimensionList + ) + custom_evaluation_dimensions[custom_evaluation_dimension_list.name] = [ + CustomEvaluationDimension.get(pk=pk) + for pk in custom_evaluation_dimension_list.dimension_pks + ] + return custom_evaluation_dimensions -@app.get("/models", response_model=list[str]) async def get_models() -> list[str]: # TODO figure out how to get the available models return ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo"] -class SimulationState: - _instance: Optional["SimulationState"] = None - _lock = asyncio.Lock() - _active_simulations: dict[str, bool] = {} - - def __new__(cls) -> "SimulationState": - if cls._instance is None: - cls._instance = super().__new__(cls) - cls._instance._active_simulations = {} - return cls._instance - - async def try_acquire_token(self, token: str) -> tuple[bool, str]: - async with self._lock: - if not token: - return False, "Invalid token" - - if self._active_simulations.get(token): - return False, "Token is active already" - - self._active_simulations[token] = True - return True, "Token is valid" - - async def release_token(self, token: str) -> None: - async with self._lock: - self._active_simulations.pop(token, None) - - @asynccontextmanager - async def start_simulation(self, token: str) -> AsyncIterator[bool]: - try: - yield True - finally: - await self.release_token(token) - - -class SimulationManager: - def __init__(self) -> None: - self.state = SimulationState() - - async def verify_token(self, token: str) -> dict[str, Any]: - is_valid, msg = await self.state.try_acquire_token(token) - return {"is_valid": is_valid, "msg": msg} - - async def create_simulator( - self, env_id: str, agent_ids: list[str] - ) -> WebSocketSotopiaSimulator: - try: - return WebSocketSotopiaSimulator(env_id=env_id, agent_ids=agent_ids) - except Exception as e: - error_msg = f"Failed to create simulator: {e}" - logger.error(error_msg) - raise Exception(error_msg) - - async def handle_client_message( - self, - websocket: WebSocket, - simulator: WebSocketSotopiaSimulator, - message: dict[str, Any], - timeout: float = 0.1, - ) -> bool: - try: - msg_type = message.get("type") - if msg_type == WSMessageType.FINISH_SIM.value: - return True - # TODO handle other message types - return False - except Exception as e: - msg = f"Error handling client message: {e}" - logger.error(msg) - await self.send_error(websocket, ErrorType.INVALID_MESSAGE, msg) - return False - - async def run_simulation( - self, websocket: WebSocket, simulator: WebSocketSotopiaSimulator - ) -> None: - try: - async for message in simulator.arun(): - await self.send_message(websocket, WSMessageType.SERVER_MSG, message) - - try: - data = await asyncio.wait_for(websocket.receive_json(), timeout=0.1) - if await self.handle_client_message(websocket, simulator, data): - break - except asyncio.TimeoutError: - continue - - except Exception as e: - msg = f"Error running simulation: {e}" - logger.error(msg) - await self.send_error(websocket, ErrorType.SIMULATION_ISSUE, msg) - finally: - await self.send_message(websocket, WSMessageType.END_SIM, {}) - - @staticmethod - async def send_message( - websocket: WebSocket, msg_type: WSMessageType, data: dict[str, Any] - ) -> None: - await websocket.send_json({"type": msg_type.value, "data": data}) - - @staticmethod - async def send_error( - websocket: WebSocket, error_type: ErrorType, details: str = "" - ) -> None: - await websocket.send_json( - { - "type": WSMessageType.ERROR.value, - "data": {"type": error_type.value, "details": details}, - } +class SotopiaFastAPI(FastAPI): + def __init__(self, *args, **kwargs) -> None: # type: ignore + super().__init__(*args, **kwargs) + self.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], ) + self.setup_routes() + def setup_routes(self) -> None: + self.get("/scenarios", response_model=list[EnvironmentProfile])( + get_scenarios_all + ) + self.get( + "/scenarios/{get_by}/{value}", response_model=list[EnvironmentProfile] + )(get_scenarios) + self.get("/agents", response_model=list[AgentProfile])(get_agents_all) + self.get("/agents/{get_by}/{value}", response_model=list[AgentProfile])( + get_agents + ) + self.get("/relationship/{agent_1_id}/{agent_2_id}", response_model=str)( + get_relationship + ) + self.get("/episodes", response_model=list[EpisodeLog])(get_episodes_all) + self.get("/episodes/{get_by}/{value}", response_model=list[EpisodeLog])( + get_episodes + ) + self.get("/models", response_model=list[str])(get_models) + self.get( + "/evaluation_dimensions", + response_model=dict[str, list[CustomEvaluationDimension]], + )(get_evaluation_dimensions) + + @self.post("/scenarios/", response_model=str) + async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: + scenario_profile = EnvironmentProfile(**scenario.model_dump()) + scenario_profile.save() + pk = scenario_profile.pk + assert pk is not None + return pk + + @self.post("/agents/", response_model=str) + async def create_agent(agent: AgentProfileWrapper) -> str: + agent_profile = AgentProfile(**agent.model_dump()) + agent_profile.save() + pk = agent_profile.pk + assert pk is not None + return pk + + @self.post("/relationship/", response_model=str) + async def create_relationship(relationship: RelationshipWrapper) -> str: + relationship_profile = RelationshipProfile(**relationship.model_dump()) + relationship_profile.save() + pk = relationship_profile.pk + assert pk is not None + return pk + + @self.post("/evaluation_dimensions/", response_model=str) + async def create_evaluation_dimensions( + evaluation_dimensions: CustomEvaluationDimensionsWrapper, + ) -> str: + dimension_list = CustomEvaluationDimensionList.find( + CustomEvaluationDimensionList.name == evaluation_dimensions.name + ).all() -@app.websocket("/ws/simulation") -async def websocket_endpoint(websocket: WebSocket, token: str) -> None: - manager = SimulationManager() + if len(dimension_list) == 0: + all_dimensions_pks = [] + for dimension in evaluation_dimensions.dimensions: + find_dimension = CustomEvaluationDimension.find( + CustomEvaluationDimension.name == dimension.name + ).all() + if len(find_dimension) == 0: + dimension.save() + all_dimensions_pks.append(dimension.pk) + elif len(find_dimension) == 1: + all_dimensions_pks.append(find_dimension[0].pk) + else: + raise HTTPException( + status_code=409, + detail=f"Evaluation dimension with name={dimension.name} already exists", + ) + + custom_evaluation_dimension_list = CustomEvaluationDimensionList( + pk=evaluation_dimensions.pk, + name=evaluation_dimensions.name, + dimension_pks=all_dimensions_pks, + ) + custom_evaluation_dimension_list.save() + logger.info( + f"Created evaluation dimension list {evaluation_dimensions.name}" + ) + else: + raise HTTPException( + status_code=409, + detail=f"Evaluation dimension list with name={evaluation_dimensions.name} already exists", + ) - token_status = await manager.verify_token(token) - if not token_status["is_valid"]: - await websocket.close(code=1008, reason=token_status["msg"]) - return + pk = custom_evaluation_dimension_list.pk + assert pk is not None + return pk - try: - await websocket.accept() + @self.post("/simulate/", response_model=str) + def simulate(simulation_request: SimulationRequest) -> Response: + try: + _: EnvironmentProfile = EnvironmentProfile.get( + pk=simulation_request.env_id + ) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, + detail=f"Environment with id={simulation_request.env_id} not found", + ) + try: + __ = AgentProfile.get(pk=simulation_request.agent_ids[0]) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, + detail=f"Agent with id={simulation_request.agent_ids[0]} not found", + ) + try: + ___ = AgentProfile.get(pk=simulation_request.agent_ids[1]) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, + detail=f"Agent with id={simulation_request.agent_ids[1]} not found", + ) - while True: - start_msg = await websocket.receive_json() - if start_msg.get("type") != WSMessageType.START_SIM.value: - continue + episode_pk = EpisodeLog( + environment="", + agents=[], + models=[], + messages=[], + reasoning="", + rewards=[], # Pseudorewards + rewards_prompt="", + ).pk + try: + simulation_status = NonStreamingSimulationStatus( + episode_pk=episode_pk, + status="Started", + ) + simulation_status.save() + queue = rq.Queue("default", connection=get_redis_connection()) + queue.enqueue( + run_simulation, + episode_pk=episode_pk, + simulation_request=simulation_request, + simulation_status=simulation_status, + ) - async with manager.state.start_simulation(token): - simulator = await manager.create_simulator( - env_id=start_msg["data"]["env_id"], - agent_ids=start_msg["data"]["agent_ids"], + except Exception as e: + logger.error(f"Error starting simulation: {e}") + simulation_status.status = "Error" + simulation_status.save() + return Response(content=episode_pk, status_code=202) + + @self.get("/simulation_status/{episode_pk}", response_model=str) + async def get_simulation_status(episode_pk: str) -> str: + status = NonStreamingSimulationStatus.find( + NonStreamingSimulationStatus.episode_pk == episode_pk + ).all()[0] + assert isinstance(status, NonStreamingSimulationStatus) + return status.status + + @self.delete("/agents/{agent_id}", response_model=str) + async def delete_agent(agent_id: str) -> str: + try: + agent = AgentProfile.get(pk=agent_id) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, detail=f"Agent with id={agent_id} not found" ) - await manager.run_simulation(websocket, simulator) - - except WebSocketDisconnect: - logger.info(f"Client disconnected: {token}") - except Exception as e: - logger.error(f"Unexpected error: {e}") - await manager.send_error(websocket, ErrorType.SIMULATION_ISSUE, str(e)) - finally: - try: - await websocket.close() - except Exception as e: - logger.error(f"Error closing websocket: {e}") + AgentProfile.delete(agent.pk) + assert agent.pk is not None + return agent.pk + + @self.delete("/scenarios/{scenario_id}", response_model=str) + async def delete_scenario(scenario_id: str) -> str: + try: + scenario = EnvironmentProfile.get(pk=scenario_id) + except Exception: # TODO Check the exception type + raise HTTPException( + status_code=404, detail=f"Scenario with id={scenario_id} not found" + ) + EnvironmentProfile.delete(scenario.pk) + assert scenario.pk is not None + return scenario.pk + + @self.delete("/relationship/{relationship_id}", response_model=str) + async def delete_relationship(relationship_id: str) -> str: + RelationshipProfile.delete(relationship_id) + return relationship_id + + @self.delete("/episodes/{episode_id}", response_model=str) + async def delete_episode(episode_id: str) -> str: + EpisodeLog.delete(episode_id) + return episode_id + + @self.websocket("/ws/simulation") + async def websocket_endpoint(websocket: WebSocket, token: str) -> None: + manager = SimulationManager() + + token_status = await manager.verify_token(token) + if not token_status["is_valid"]: + await websocket.close(code=1008, reason=token_status["msg"]) + return + + try: + await websocket.accept() + + while True: + start_msg = await websocket.receive_json() + if start_msg.get("type") != WSMessageType.START_SIM.value: + continue + + async with manager.state.start_simulation(token): + simulator = await manager.create_simulator( + env_id=start_msg["data"]["env_id"], + agent_ids=start_msg["data"]["agent_ids"], + agent_models=start_msg["data"].get( + "agent_models", ["gpt-4o-mini", "gpt-4o-mini"] + ), + evaluator_model=start_msg["data"].get( + "evaluator_model", "gpt-4o" + ), + evaluation_dimension_list_name=start_msg["data"].get( + "evaluation_dimension_list_name", "sotopia" + ), + ) + await manager.run_simulation(websocket, simulator) + + except WebSocketDisconnect: + logger.info(f"Client disconnected: {token}") + except Exception as e: + logger.error(f"Unexpected error: {e}") + await manager.send_error(websocket, ErrorType.SIMULATION_ISSUE, str(e)) + finally: + try: + await websocket.close() + except Exception as e: + logger.error(f"Error closing websocket: {e}") + +app = SotopiaFastAPI() if __name__ == "__main__": uvicorn.run(app, host="127.0.0.1", port=8800) diff --git a/sotopia/ui/websocket_utils.py b/sotopia/ui/websocket_utils.py index 5b29da73..999a9981 100644 --- a/sotopia/ui/websocket_utils.py +++ b/sotopia/ui/websocket_utils.py @@ -2,16 +2,20 @@ EvaluationForTwoAgents, ReachGoalLLMEvaluator, RuleBasedTerminatedEvaluator, - SotopiaDimensions, ) from sotopia.agents import Agents, LLMAgent from sotopia.messages import Observation from sotopia.envs import ParallelSotopiaEnv -from sotopia.database import EnvironmentProfile, AgentProfile, EpisodeLog +from sotopia.database import ( + EnvironmentProfile, + AgentProfile, + EpisodeLog, + EvaluationDimensionBuilder, +) from sotopia.server import arun_one_episode from enum import Enum -from typing import TypedDict, Any, AsyncGenerator +from typing import Type, TypedDict, Any, AsyncGenerator from pydantic import BaseModel @@ -57,6 +61,7 @@ def get_env_agents( agent_ids: list[str], agent_models: list[str], evaluator_model: str, + evaluation_dimension_list_name: str, ) -> tuple[ParallelSotopiaEnv, Agents, dict[str, Observation]]: # environment_profile = EnvironmentProfile.find().all()[0] # agent_profiles = AgentProfile.find().all()[:2] @@ -79,6 +84,12 @@ def get_env_agents( for idx, goal in enumerate(environment_profile.agent_goals): agent_list[idx].goal = goal + evaluation_dimensions: Type[BaseModel] = ( + EvaluationDimensionBuilder.select_existing_dimension_model_by_list_name( + list_name=evaluation_dimension_list_name + ) + ) + agents = Agents({agent.agent_name: agent for agent in agent_list}) env = ParallelSotopiaEnv( action_order="round-robin", @@ -89,7 +100,7 @@ def get_env_agents( terminal_evaluators=[ ReachGoalLLMEvaluator( evaluator_model, - EvaluationForTwoAgents[SotopiaDimensions], + EvaluationForTwoAgents[evaluation_dimensions], # type: ignore ), ], env_profile=environment_profile, @@ -124,9 +135,14 @@ def __init__( agent_ids: list[str], agent_models: list[str] = ["gpt-4o-mini", "gpt-4o-mini"], evaluator_model: str = "gpt-4o", + evaluation_dimension_list_name: str = "sotopia", ) -> None: self.env, self.agents, self.environment_messages = get_env_agents( - env_id, agent_ids, agent_models, evaluator_model + env_id, + agent_ids, + agent_models, + evaluator_model, + evaluation_dimension_list_name, ) self.messages: list[list[tuple[str, str, str]]] = [] self.messages.append( @@ -151,17 +167,17 @@ async def arun(self) -> AsyncGenerator[dict[str, Any], None]: streaming=True, ) - assert isinstance( - generator, AsyncGenerator - ), "generator should be async generator" + # assert isinstance( + # generator, AsyncGenerator + # ), "generator should be async generator, but got {}".format( + # type(generator) + # ) async for messages in await generator: # type: ignore reasoning, rewards = "", [0.0, 0.0] - eval_available = False if messages[-1][0][0] == "Evaluation": reasoning = messages[-1][0][2].to_natural_language() rewards = eval(messages[-2][0][2].to_natural_language()) - eval_available = True epilog = EpisodeLog( environment=self.env.profile.pk, @@ -176,11 +192,11 @@ async def arun(self) -> AsyncGenerator[dict[str, Any], None]: rewards=rewards, rewards_prompt="", ) - agent_profiles, parsed_messages = epilog.render_for_humans() - if not eval_available: - parsed_messages = parsed_messages[:-2] + # agent_profiles, parsed_messages = epilog.render_for_humans() + # if not eval_available: + # parsed_messages = parsed_messages[:-2] yield { "type": "messages", - "messages": parsed_messages, + "messages": epilog.dict(), } From cb6b2d1d1d59474cccf9e0bb0e2e4d5f17606294 Mon Sep 17 00:00:00 2001 From: Xuhui Zhou Date: Tue, 31 Dec 2024 15:52:16 -0500 Subject: [PATCH 11/26] Feature/sotopia demo UI (#261) * initial * initial ui * merge main * add new ui * switch to fastAPI * websocket check * fix render episode error * add page; make a simplified page and still WIP * [autofix.ci] apply automated fixes * fix simplified streaming version * semi-done character page + avatar assets * Fixed character card styling * [autofix.ci] apply automated fixes * unified rendering and chat display * updated chat character icons * add some tags * add typing * temp fix * add characters avatar to simulation * fix episode full avatar * go to modal config * clean up code * add modal streamlit app * clean codebase except websocket * remove repeated local css * clean websocket * fix get name error * fix errors * pre render scenario * add custom eval * change streamlit to dynamic path * new uv * revert to previous install commands * a fix for modal * add customized dimension * [autofix.ci] apply automated fixes * sort scenarios in simulation * for demo video * update deploy instruction * update intro page * update intro page * [autofix.ci] apply automated fixes * update intro page * add customized dimensions * update api link and modal environment * move folder * fix relative import * update modal image build * use uv to build environment * change folder name * change test * fix modal serve * environment change * refactor * fix ui --------- Co-authored-by: Zhe Su <360307598@qq.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: astrophie --- docs/pages/contribution/contribution.md | 2 +- .../websocket/websocket_test_client.py | 69 +- pyproject.toml | 3 + scripts/modal/modal_api_server.py | 2 +- sotopia/{ui => api}/README.md | 8 + sotopia/{ui => api}/__init__.py | 0 sotopia/{ui => api}/fastapi_server.py | 61 +- sotopia/{ui => api}/websocket_utils.py | 15 +- sotopia/database/__init__.py | 14 +- sotopia/database/logs.py | 8 +- sotopia/database/persistent_profile.py | 20 +- tests/{ui => api}/test_fastapi.py | 2 +- ui/streamlit_ui/.streamlit/config.toml | 11 + ui/streamlit_ui/README.md | 19 + ui/streamlit_ui/__init__.py | 0 ui/streamlit_ui/app.py | 138 ++ ui/streamlit_ui/css/style.css | 34 + ui/streamlit_ui/modal_streamlit_app.py | 62 + ui/streamlit_ui/pages/__init__.py | 0 ui/streamlit_ui/pages/add_characters.py | 64 + .../pages/add_evaluation_dimension.py | 184 ++ ui/streamlit_ui/pages/add_scenarios.py | 119 ++ ui/streamlit_ui/pages/display_characters.py | 25 + ui/streamlit_ui/pages/display_episodes.py | 74 + .../pages/display_evaluation_dimensions.py | 48 + ui/streamlit_ui/pages/display_scenarios.py | 75 + ui/streamlit_ui/pages/intro.py | 33 + .../pages/render_chat_websocket.py | 371 ++++ ui/streamlit_ui/rendering/__init__.py | 40 + ui/streamlit_ui/rendering/get_elements.py | 53 + ui/streamlit_ui/rendering/render_elements.py | 297 +++ ui/streamlit_ui/rendering/render_utils.py | 216 +++ uv.lock | 1659 ++++++++++------- 33 files changed, 2921 insertions(+), 805 deletions(-) rename sotopia/{ui => api}/README.md (96%) rename sotopia/{ui => api}/__init__.py (100%) rename sotopia/{ui => api}/fastapi_server.py (94%) rename sotopia/{ui => api}/websocket_utils.py (93%) rename tests/{ui => api}/test_fastapi.py (99%) create mode 100644 ui/streamlit_ui/.streamlit/config.toml create mode 100644 ui/streamlit_ui/README.md create mode 100644 ui/streamlit_ui/__init__.py create mode 100644 ui/streamlit_ui/app.py create mode 100644 ui/streamlit_ui/css/style.css create mode 100644 ui/streamlit_ui/modal_streamlit_app.py create mode 100644 ui/streamlit_ui/pages/__init__.py create mode 100644 ui/streamlit_ui/pages/add_characters.py create mode 100644 ui/streamlit_ui/pages/add_evaluation_dimension.py create mode 100644 ui/streamlit_ui/pages/add_scenarios.py create mode 100644 ui/streamlit_ui/pages/display_characters.py create mode 100644 ui/streamlit_ui/pages/display_episodes.py create mode 100644 ui/streamlit_ui/pages/display_evaluation_dimensions.py create mode 100644 ui/streamlit_ui/pages/display_scenarios.py create mode 100644 ui/streamlit_ui/pages/intro.py create mode 100644 ui/streamlit_ui/pages/render_chat_websocket.py create mode 100644 ui/streamlit_ui/rendering/__init__.py create mode 100644 ui/streamlit_ui/rendering/get_elements.py create mode 100644 ui/streamlit_ui/rendering/render_elements.py create mode 100644 ui/streamlit_ui/rendering/render_utils.py diff --git a/docs/pages/contribution/contribution.md b/docs/pages/contribution/contribution.md index 97021868..33803adf 100644 --- a/docs/pages/contribution/contribution.md +++ b/docs/pages/contribution/contribution.md @@ -133,7 +133,7 @@ Please refer to [Dev Containers](https://containers.dev/supporting#editors) to s You can also set up the development environment without Dev Containers. There are three things you will need to set up manually: -- Python and uv: Please start from an environment supporting Python 3.10+ and install uv using `pip install uv; uv sync --all-extra`. +- Python and uv: Please start from an environment supporting Python 3.10+ and install uv using `pip install uv; uv sync --all-extras`. (Note that this will install all the extra dependencies) - Redis: Please refer to introduction page for the set up of Redis. - Local LLM (optional): If you don't have access to model endpoints (e.g. OpenAI, Anthropic or others), you can use a local model. You can use Ollama, Llama.cpp, vLLM or many others which support OpenAI compatible endpoints. diff --git a/examples/experimental/websocket/websocket_test_client.py b/examples/experimental/websocket/websocket_test_client.py index ab9f3027..c535d363 100644 --- a/examples/experimental/websocket/websocket_test_client.py +++ b/examples/experimental/websocket/websocket_test_client.py @@ -6,7 +6,7 @@ from sotopia.database import EnvironmentProfile, AgentProfile import asyncio -import websockets +import aiohttp import sys from pathlib import Path @@ -28,40 +28,39 @@ async def connect(self) -> None: url_with_token = f"{self.url}?token=test_token_{self.client_id}" try: - async with websockets.connect(url_with_token) as websocket: - print(f"Client {self.client_id}: Connected to {self.url}") - - # Send initial message - # Note: You'll need to implement the logic to get agent_ids and env_id - # This is just an example structure - agent_ids = [agent.pk for agent in AgentProfile.find().all()[:2]] - env_id = EnvironmentProfile.find().all()[0].pk - start_message = { - "type": "START_SIM", - "data": { - "env_id": env_id, # Replace with actual env_id - "agent_ids": agent_ids, # Replace with actual agent_ids - }, - } - await websocket.send(json.dumps(start_message)) - print(f"Client {self.client_id}: Sent START_SIM message") - - # Receive and process messages - while True: - try: - message = await websocket.recv() - print( - f"\nClient {self.client_id} received message:", - json.dumps(json.loads(message), indent=2), - ) - assert isinstance(message, str) - await self.save_message(message) - except websockets.ConnectionClosed: - print(f"Client {self.client_id}: Connection closed") - break - except Exception as e: - print(f"Client {self.client_id} error:", str(e)) - break + async with aiohttp.ClientSession() as session: + async with session.ws_connect(url_with_token) as ws: + print(f"Client {self.client_id}: Connected to {self.url}") + + # Send initial message + # Note: You'll need to implement the logic to get agent_ids and env_id + # This is just an example structure + agent_ids = [agent.pk for agent in AgentProfile.find().all()[:2]] + env_id = EnvironmentProfile.find().all()[0].pk + start_message = { + "type": "START_SIM", + "data": { + "env_id": env_id, # Replace with actual env_id + "agent_ids": agent_ids, # Replace with actual agent_ids + }, + } + await ws.send_json(start_message) + print(f"Client {self.client_id}: Sent START_SIM message") + + # Receive and process messages + async for msg in ws: + if msg.type == aiohttp.WSMsgType.TEXT: + print( + f"\nClient {self.client_id} received message:", + json.dumps(json.loads(msg.data), indent=2), + ) + await self.save_message(msg.data) + elif msg.type == aiohttp.WSMsgType.CLOSED: + print(f"Client {self.client_id}: Connection closed") + break + elif msg.type == aiohttp.WSMsgType.ERROR: + print(f"Client {self.client_id}: Connection error") + break except Exception as e: print(f"Client {self.client_id} connection error:", str(e)) diff --git a/pyproject.toml b/pyproject.toml index b9edcc94..1b8e91ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,9 @@ examples = ["transformers", "datasets", "scipy", "torch", "pandas"] api = [ "fastapi[standard]", "uvicorn", + "streamlit", + "websockets", + "modal" ] test = ["pytest", "pytest-cov", "pytest-asyncio"] diff --git a/scripts/modal/modal_api_server.py b/scripts/modal/modal_api_server.py index 30c8c8da..1e7e85f4 100644 --- a/scripts/modal/modal_api_server.py +++ b/scripts/modal/modal_api_server.py @@ -4,7 +4,7 @@ import os import redis -from sotopia.ui.fastapi_server import SotopiaFastAPI +from sotopia.api.fastapi_server import SotopiaFastAPI # Create persistent volume for Redis data redis_volume = modal.Volume.from_name("sotopia-api", create_if_missing=True) diff --git a/sotopia/ui/README.md b/sotopia/api/README.md similarity index 96% rename from sotopia/ui/README.md rename to sotopia/api/README.md index f483f3ea..cf876af0 100644 --- a/sotopia/ui/README.md +++ b/sotopia/api/README.md @@ -2,6 +2,14 @@ > [!CAUTION] > Work in progress: the API endpoints are being implemented. And will be released in the future major version. +## Deploy to Modal +First you need to have a Modal account and logged in with `modal setup` + +To deploy the FastAPI server to Modal, run the following command: +```bash +cd sotopia/ui/fastapi_server +modal deploy modal_api_server.py +``` ## FastAPI Server To run the FastAPI server, you can use the following command: diff --git a/sotopia/ui/__init__.py b/sotopia/api/__init__.py similarity index 100% rename from sotopia/ui/__init__.py rename to sotopia/api/__init__.py diff --git a/sotopia/ui/fastapi_server.py b/sotopia/api/fastapi_server.py similarity index 94% rename from sotopia/ui/fastapi_server.py rename to sotopia/api/fastapi_server.py index ce5e1728..6eaab153 100644 --- a/sotopia/ui/fastapi_server.py +++ b/sotopia/api/fastapi_server.py @@ -17,6 +17,9 @@ NonStreamingSimulationStatus, CustomEvaluationDimensionList, CustomEvaluationDimension, + BaseEnvironmentProfile, + BaseAgentProfile, + BaseRelationshipProfile, ) from sotopia.envs.parallel import ParallelSotopiaEnv from sotopia.envs.evaluators import ( @@ -37,7 +40,7 @@ from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, model_validator, field_validator, Field -from sotopia.ui.websocket_utils import ( +from sotopia.api.websocket_utils import ( WebSocketSotopiaSimulator, WSMessageType, ErrorType, @@ -68,56 +71,6 @@ ] = {} # TODO check whether this is the correct way to store the active simulations -class RelationshipWrapper(BaseModel): - pk: str = "" - agent_1_id: str = "" - agent_2_id: str = "" - relationship: Literal[0, 1, 2, 3, 4, 5] = 0 - backstory: str = "" - tag: str = "" - - -class AgentProfileWrapper(BaseModel): - """ - Wrapper for AgentProfile to avoid pydantic v2 issues - """ - - pk: str = "" - first_name: str - last_name: str - age: int = 0 - occupation: str = "" - gender: str = "" - gender_pronoun: str = "" - public_info: str = "" - big_five: str = "" - moral_values: list[str] = [] - schwartz_personal_values: list[str] = [] - personality_and_values: str = "" - decision_making_style: str = "" - secret: str = "" - model_id: str = "" - mbti: str = "" - tag: str = "" - - -class EnvironmentProfileWrapper(BaseModel): - """ - Wrapper for EnvironmentProfile to avoid pydantic v2 issues - """ - - pk: str = "" - codename: str - source: str = "" - scenario: str = "" - agent_goals: list[str] = [] - relationship: Literal[0, 1, 2, 3, 4, 5] = 0 - age_constraint: str | None = None - occupation_constraint: str | None = None - agent_constraint: list[list[str]] | None = None - tag: str = "" - - class CustomEvaluationDimensionsWrapper(BaseModel): pk: str = "" name: str = Field( @@ -484,7 +437,7 @@ def setup_routes(self) -> None: )(get_evaluation_dimensions) @self.post("/scenarios/", response_model=str) - async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: + async def create_scenario(scenario: BaseEnvironmentProfile) -> str: scenario_profile = EnvironmentProfile(**scenario.model_dump()) scenario_profile.save() pk = scenario_profile.pk @@ -492,7 +445,7 @@ async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: return pk @self.post("/agents/", response_model=str) - async def create_agent(agent: AgentProfileWrapper) -> str: + async def create_agent(agent: BaseAgentProfile) -> str: agent_profile = AgentProfile(**agent.model_dump()) agent_profile.save() pk = agent_profile.pk @@ -500,7 +453,7 @@ async def create_agent(agent: AgentProfileWrapper) -> str: return pk @self.post("/relationship/", response_model=str) - async def create_relationship(relationship: RelationshipWrapper) -> str: + async def create_relationship(relationship: BaseRelationshipProfile) -> str: relationship_profile = RelationshipProfile(**relationship.model_dump()) relationship_profile.save() pk = relationship_profile.pk diff --git a/sotopia/ui/websocket_utils.py b/sotopia/api/websocket_utils.py similarity index 93% rename from sotopia/ui/websocket_utils.py rename to sotopia/api/websocket_utils.py index 999a9981..36cb20c5 100644 --- a/sotopia/ui/websocket_utils.py +++ b/sotopia/api/websocket_utils.py @@ -160,20 +160,18 @@ def __init__( async def arun(self) -> AsyncGenerator[dict[str, Any], None]: # Use sotopia to run the simulation - generator = arun_one_episode( + generator = await arun_one_episode( env=self.env, agent_list=list(self.agents.values()), push_to_db=False, streaming=True, ) - # assert isinstance( - # generator, AsyncGenerator - # ), "generator should be async generator, but got {}".format( - # type(generator) - # ) + assert isinstance( + generator, AsyncGenerator + ), "generator should be async generator, but got {}".format(type(generator)) - async for messages in await generator: # type: ignore + async for messages in generator: reasoning, rewards = "", [0.0, 0.0] if messages[-1][0][0] == "Evaluation": reasoning = messages[-1][0][2].to_natural_language() @@ -192,9 +190,6 @@ async def arun(self) -> AsyncGenerator[dict[str, Any], None]: rewards=rewards, rewards_prompt="", ) - # agent_profiles, parsed_messages = epilog.render_for_humans() - # if not eval_available: - # parsed_messages = parsed_messages[:-2] yield { "type": "messages", diff --git a/sotopia/database/__init__.py b/sotopia/database/__init__.py index d9156a98..6b640620 100644 --- a/sotopia/database/__init__.py +++ b/sotopia/database/__init__.py @@ -2,10 +2,18 @@ from redis_om import JsonModel, Migrator from .annotators import Annotator from .env_agent_combo_storage import EnvAgentComboStorage -from .logs import AnnotationForEpisode, EpisodeLog, NonStreamingSimulationStatus +from .logs import ( + AnnotationForEpisode, + BaseEpisodeLog, + NonStreamingSimulationStatus, + EpisodeLog, +) from .persistent_profile import ( AgentProfile, + BaseAgentProfile, EnvironmentProfile, + BaseEnvironmentProfile, + BaseRelationshipProfile, RelationshipProfile, RelationshipType, ) @@ -42,12 +50,16 @@ __all__ = [ "AgentProfile", + "BaseAgentProfile", "EnvironmentProfile", + "BaseEnvironmentProfile", "EpisodeLog", + "BaseEpisodeLog", "NonStreamingSimulationStatus", "EnvAgentComboStorage", "AnnotationForEpisode", "Annotator", + "BaseRelationshipProfile", "RelationshipProfile", "RelationshipType", "RedisCommunicationMixin", diff --git a/sotopia/database/logs.py b/sotopia/database/logs.py index 4a2551ae..e05618f4 100644 --- a/sotopia/database/logs.py +++ b/sotopia/database/logs.py @@ -5,7 +5,7 @@ else: from typing_extensions import Self -from pydantic import model_validator +from pydantic import model_validator, BaseModel from redis_om import JsonModel from redis_om.model.model import Field from typing import Literal @@ -17,7 +17,7 @@ class NonStreamingSimulationStatus(JsonModel): status: Literal["Started", "Error", "Completed"] -class EpisodeLog(JsonModel): +class BaseEpisodeLog(BaseModel): # Note that we did not validate the following constraints: # 1. The number of turns in messages and rewards should be the same or off by 1 # 2. The agents in the messages are the same as the agetns @@ -77,6 +77,10 @@ def render_for_humans(self) -> tuple[list[AgentProfile], list[str]]: return agent_profiles, messages_and_rewards +class EpisodeLog(BaseEpisodeLog, JsonModel): + pass + + class AnnotationForEpisode(JsonModel): episode: str = Field(index=True, description="the pk id of episode log") annotator_id: str = Field(index=True, full_text_search=True) diff --git a/sotopia/database/persistent_profile.py b/sotopia/database/persistent_profile.py index c2e0e8e8..42b1897c 100644 --- a/sotopia/database/persistent_profile.py +++ b/sotopia/database/persistent_profile.py @@ -6,7 +6,7 @@ else: from typing_extensions import Self -from pydantic import model_validator +from pydantic import model_validator, BaseModel from redis_om import JsonModel from redis_om.model.model import Field @@ -20,7 +20,7 @@ class RelationshipType(IntEnum): family_member = 5 -class AgentProfile(JsonModel): +class BaseAgentProfile(BaseModel): first_name: str = Field(index=True) last_name: str = Field(index=True) age: int = Field(index=True, default_factory=lambda: 0) @@ -43,7 +43,11 @@ class AgentProfile(JsonModel): ) -class EnvironmentProfile(JsonModel): +class AgentProfile(BaseAgentProfile, JsonModel): + pass + + +class BaseEnvironmentProfile(BaseModel): codename: str = Field( index=True, default_factory=lambda: "", @@ -86,7 +90,11 @@ class EnvironmentProfile(JsonModel): ) -class RelationshipProfile(JsonModel): +class EnvironmentProfile(BaseEnvironmentProfile, JsonModel): + pass + + +class BaseRelationshipProfile(BaseModel): agent_1_id: str = Field(index=True) agent_2_id: str = Field(index=True) relationship: RelationshipType = Field( @@ -101,6 +109,10 @@ class RelationshipProfile(JsonModel): ) +class RelationshipProfile(BaseRelationshipProfile, JsonModel): + pass + + class EnvironmentList(JsonModel): name: str = Field(index=True) environments: list[str] = Field(default_factory=lambda: []) diff --git a/tests/ui/test_fastapi.py b/tests/api/test_fastapi.py similarity index 99% rename from tests/ui/test_fastapi.py rename to tests/api/test_fastapi.py index 0ce87278..4e43ec2d 100644 --- a/tests/ui/test_fastapi.py +++ b/tests/api/test_fastapi.py @@ -8,7 +8,7 @@ CustomEvaluationDimensionList, ) from sotopia.messages import SimpleMessage -from sotopia.ui.fastapi_server import app +from sotopia.api.fastapi_server import app import pytest from typing import Generator, Callable diff --git a/ui/streamlit_ui/.streamlit/config.toml b/ui/streamlit_ui/.streamlit/config.toml new file mode 100644 index 00000000..73d9cf82 --- /dev/null +++ b/ui/streamlit_ui/.streamlit/config.toml @@ -0,0 +1,11 @@ +[theme] +base="light" +primaryColor = "#26184e" +backgroundColor="#FFFFFF" +secondaryBackgroundColor="#F0F2F6" +textColor="#31333F" +font="sans serif" + + +[server] +enableStaticServing = true diff --git a/ui/streamlit_ui/README.md b/ui/streamlit_ui/README.md new file mode 100644 index 00000000..03937b77 --- /dev/null +++ b/ui/streamlit_ui/README.md @@ -0,0 +1,19 @@ +To deploy the Streamlit UI to Modal, run the following command: +```bash +cd ui/streamlit_ui +modal deploy modal_streamlit_app.py +``` + +To serve the Streamlit UI, run the following command: +```bash +modal serve modal_streamlit_app.py +``` + +Before deploying the Streamlit UI, do check the `API_BASE` and `WS_BASE` in the `streamlit_ui/app.py` and set to your API server's endpoint (which could either be local or your Modal endpoint). + +## Streamlit UI +To run the Streamlit UI, run the following command: +```bash +cd sotopia/ui/streamlit_ui +uv run streamlit run app.py +``` diff --git a/ui/streamlit_ui/__init__.py b/ui/streamlit_ui/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ui/streamlit_ui/app.py b/ui/streamlit_ui/app.py new file mode 100644 index 00000000..3b2f7e03 --- /dev/null +++ b/ui/streamlit_ui/app.py @@ -0,0 +1,138 @@ +import os + +import streamlit as st + +# Page Configuration +st.set_page_config(page_title="SocialStream_Demo", page_icon="🧊", layout="wide") + +# PORT = 8800 +# st.session_state.API_BASE = f"http://localhost:{PORT}" +# st.session_state.WS_BASE = f"ws://localhost:{PORT}" + +DEFAULT_BASE = "sotopia-lab--sotopia-fastapi-webapi-serve-dev.modal.run" + +# Modal Configuration + +if "API_BASE" not in st.session_state: + st.session_state.API_BASE = f"https://{DEFAULT_BASE}" + st.session_state.WS_BASE = f"ws://{DEFAULT_BASE}" + + +def update_database_callback() -> None: + new_database_url = st.session_state.new_database_url + updated_url = ( + new_database_url if new_database_url != "" else st.session_state.DEFAULT_DB_URL + ) + try: + pass + except Exception as e: + st.error(f"Error occurred while updating database: {e}, please try again.") + + st.session_state.current_database_url = updated_url + print("Updated DB URL: ", st.session_state.current_database_url) + + +base_path = os.path.dirname(os.path.abspath(__file__)) +page_path = os.path.join(base_path, "pages") + +display_intro = st.Page( + f"{page_path}/intro.py", title="Introduction", icon=":material/home:" +) + +display_scenarios = st.Page( + f"{page_path}/display_scenarios.py", + title="Scenarios", + icon=":material/insert_drive_file:", +) +display_characters = st.Page( + f"{page_path}/display_characters.py", title="Characters", icon=":material/people:" +) +display_episodes = st.Page( + f"{page_path}/display_episodes.py", title="Episode", icon=":material/photo_library:" +) + +display_chat = st.Page( + f"{page_path}/render_chat_websocket.py", + title="Simulation", + # icon=":material/add:", +) + +display_evaluation_dimensions = st.Page( + f"{page_path}/display_evaluation_dimensions.py", + title="Evaluation Dimensions", + # icon=":material/add:", +) + +add_characters = st.Page( + f"{page_path}/add_characters.py", title="Add Characters", icon=":material/add:" +) + +add_scenarios = st.Page( + f"{page_path}/add_scenarios.py", title="Add Scenarios", icon=":material/add:" +) +add_evaluation_dimensions = st.Page( + f"{page_path}/add_evaluation_dimension.py", + title="Add Evaluation Dimensions", + icon=":material/add:", +) + +pg = st.navigation( + [ + display_intro, + display_scenarios, + display_characters, + display_episodes, + display_chat, + display_evaluation_dimensions, + add_characters, + add_scenarios, + add_evaluation_dimensions, + ] +) + +# Reset active agent when switching modes across pages +if "mode" not in st.session_state or pg.title != st.session_state.get("mode", None): + if "active" in st.session_state: + del st.session_state["active"] + # print("Active agent reset.") + + st.session_state.mode = pg.title + + +# DB URL Configuration +if "DEFAULT_DB_URL" not in st.session_state: + st.session_state.DEFAULT_DB_URL = os.environ.get("REDIS_OM_URL", "") + st.session_state.current_database_url = st.session_state.DEFAULT_DB_URL + print("Default DB URL: ", st.session_state.DEFAULT_DB_URL) + +# impl 2: popup update URL +with st.sidebar.popover("(Optional) Enter Database URL"): + new_database_url = st.text_input( + "URL: (starting in redis://)", + value="", + on_change=update_database_callback, + key="new_database_url", + ) + +with st.sidebar: + with st.expander("API Configuration", expanded=False): + st.session_state.API_BASE = st.text_input( + "API Base URL:", + value=st.session_state.API_BASE, + placeholder="Enter API base URL", + ) + + st.session_state.WS_BASE = st.text_input( + "WebSocket Base URL:", + value=st.session_state.WS_BASE, + placeholder="Enter WebSocket base URL", + ) + + # Optional: Add a reset button + if st.button("Reset to Default"): + st.session_state.API_BASE = f"https://{DEFAULT_BASE}" + st.session_state.WS_BASE = f"ws://{DEFAULT_BASE}" + st.rerun() + + +pg.run() diff --git a/ui/streamlit_ui/css/style.css b/ui/streamlit_ui/css/style.css new file mode 100644 index 00000000..b9f63b40 --- /dev/null +++ b/ui/streamlit_ui/css/style.css @@ -0,0 +1,34 @@ +.truncate { + max-height: 2em; +} + +.character-truncate { + max-height: 8em; +} +.truncate, .character-truncate{ + margin-bottom: 12px; + cursor: pointer; + word-break: break-all; + overflow:hidden; + white-space: nowrap; + transition: max-height 1s, white-space 1s; +} +.truncate:hover, .character-truncate:hover{ + overflow: auto; + overflow-y: scroll; + white-space: normal; + max-height: 200px; +} + +/* +[data-testid="stAppViewContainer"] { + background-image: url("https://images.unsplash.com/photo-1729096532452-50549f2bb63c"); + background-size: 40%; + background-position: top right; + background-repeat: no-repeat; + background-attachment: local; +} */ + +[data-testid="stHeader"] { + background: rgba(0,0,0,0); +} diff --git a/ui/streamlit_ui/modal_streamlit_app.py b/ui/streamlit_ui/modal_streamlit_app.py new file mode 100644 index 00000000..fd0e5f2f --- /dev/null +++ b/ui/streamlit_ui/modal_streamlit_app.py @@ -0,0 +1,62 @@ +import shlex +import subprocess +from pathlib import Path + +import modal + + +image = ( + modal.Image.debian_slim(python_version="3.11") + .apt_install( + "git", + "curl", + "gpg", + "lsb-release", + "wget", + "procps", # for ps command + "redis-tools", # for redis-cli + ) + .pip_install("streamlit~=1.40.2", "uv") + .run_commands( + "rm -rf sotopia && git clone https://github.com/sotopia-lab/sotopia.git && cd sotopia && git checkout feature/sotopia-demo-ui && uv pip install pyproject.toml --system && git status && pip install -e . && cd ui && cd streamlit_ui" + ) + # .pip_install("pydantic==2.8.2") + .run_commands("pip list") +) + + +streamlit_script_local_path = Path(__file__).parent +print("streamlit_script_local_path************************") +print(streamlit_script_local_path) +streamlit_script_remote_path = Path("/root") + + +if not streamlit_script_local_path.exists(): + raise RuntimeError( + "app.py not found! Place the script with your streamlit app in the same directory." + ) + +streamlit_project_mount = modal.Mount.from_local_dir( + local_path=f"{str(streamlit_script_local_path)}", + remote_path=f"{str(streamlit_script_remote_path)}", +) + +# streamlit_script_mount = modal.Mount.from_local_file( +# local_path=f"{str(streamlit_script_local_path)}/app.py", +# remote_path=f"{str(streamlit_script_remote_path)}/app.py", +# ) + +app = modal.App(name="example-modal-streamlit-dev", image=image) + + +@app.function( + allow_concurrent_inputs=100, + mounts=[streamlit_project_mount], +) +@modal.web_server(8000) +def run() -> None: + target = shlex.quote(f"{str(streamlit_script_remote_path)}/app.py") + print("target************************") + print(target) + cmd = f"streamlit run {target} --server.port 8000 --server.enableCORS=true --server.enableXsrfProtection=false" + subprocess.Popen(cmd, shell=True) diff --git a/ui/streamlit_ui/pages/__init__.py b/ui/streamlit_ui/pages/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ui/streamlit_ui/pages/add_characters.py b/ui/streamlit_ui/pages/add_characters.py new file mode 100644 index 00000000..35ccc5a3 --- /dev/null +++ b/ui/streamlit_ui/pages/add_characters.py @@ -0,0 +1,64 @@ +""" +Definition +@app.post("/agents/", response_model=str) +async def create_agent(agent: BaseAgentProfile) -> str: + agent_profile = BaseAgentProfile(**agent.model_dump()) + agent_profile.save() + pk = agent_profile.pk + assert pk is not None + return pk +""" + +import streamlit as st +import requests + +from sotopia.database import BaseAgentProfile +from ui.streamlit_ui.rendering import local_css + +# add fields for agent profiles + + +def rendering_character_form() -> None: + local_css("././css/style.css") + st.markdown("

Character Creation

", unsafe_allow_html=True) + st.write("Fill in the fields below to create a new character:") + + first_name = st.text_input("First Name") + last_name = st.text_input("Last Name") + age = st.number_input("Age", min_value=0) + occupation = st.text_input("Occupation") + gender = st.text_input("Gender") + gender_pronoun = st.text_input("Gender Pronoun") + public_info = st.text_area("Public Info") + + if st.button("Create Character"): + agent_profile = BaseAgentProfile( + first_name=first_name, + last_name=last_name, + age=age, + occupation=occupation, + gender=gender, + gender_pronoun=gender_pronoun, + public_info=public_info, + tag="customized_agent", + ) + print(agent_profile) + + response = requests.post( + f"{st.session_state.API_BASE}/agents/", + json=agent_profile.model_dump(), + ) + + if response.status_code != 200: + st.error("Failed to create character. Error message: " + response.text) + + else: + agent_id = response.json() + st.success("Character created successfully! ID: " + agent_id) + retrieved_agent = requests.get( + f"{st.session_state.API_BASE}/agents/id/{agent_id}" + ) + st.write(retrieved_agent.json()) + + +rendering_character_form() diff --git a/ui/streamlit_ui/pages/add_evaluation_dimension.py b/ui/streamlit_ui/pages/add_evaluation_dimension.py new file mode 100644 index 00000000..ab9d0b90 --- /dev/null +++ b/ui/streamlit_ui/pages/add_evaluation_dimension.py @@ -0,0 +1,184 @@ +""" +Definition +@app.get( + "/evaluation_dimensions/", response_model=dict[str, list[CustomEvaluationDimension]] +) +async def get_evaluation_dimensions() -> dict[str, list[CustomEvaluationDimension]]: + custom_evaluation_dimensions: dict[str, list[CustomEvaluationDimension]] = {} + all_custom_evaluation_dimension_list = CustomEvaluationDimensionList.all() + for custom_evaluation_dimension_list in all_custom_evaluation_dimension_list: + assert isinstance( + custom_evaluation_dimension_list, CustomEvaluationDimensionList + ) + custom_evaluation_dimensions[custom_evaluation_dimension_list.name] = [ + CustomEvaluationDimension.get(pk=pk) + for pk in custom_evaluation_dimension_list.dimension_pks + ] + return custom_evaluation_dimensions + +class CustomEvaluationDimensionsWrapper(BaseModel): + pk: str = "" + name: str = Field( + default="", description="The name of the custom evaluation dimension list" + ) + dimensions: list[CustomEvaluationDimension] = Field( + default=[], description="The dimensions of the custom evaluation dimension list" + ) + +class CustomEvaluationDimension(JsonModel): + name: str = Field(index=True) + description: str = Field(index=True) + range_high: int = Field(index=True) + range_low: int = Field(index=True) + +""" + +import streamlit as st +import requests +from sotopia.database import CustomEvaluationDimension +from sotopia.api.fastapi_server import CustomEvaluationDimensionsWrapper +from ui.streamlit_ui.rendering.get_elements import ( + get_distinct_evaluation_dimensions, +) + + +def add_evaluation_dimension() -> None: + st.title("Add Evaluation Dimensions") + + # Initialize session state for dimensions list if it doesn't exist + if "dimensions" not in st.session_state: + st.session_state.dimensions = [] + + # Get existing dimensions + existing_dimensions = get_distinct_evaluation_dimensions() + + # Tab-based interface for adding dimensions + tab1, tab2 = st.tabs(["Add New Dimension", "Select Existing Dimension"]) + + # Tab 1: Add New Dimension + with tab1: + with st.form(key="add_dimension_form"): + st.subheader("Add New Dimension") + dim_name = st.text_input("Dimension Name") + dim_description = st.text_area("Dimension Description") + col1, col2 = st.columns(2) + with col1: + range_low = st.number_input("Range Low", value=0) + with col2: + range_high = st.number_input("Range High", value=10) + + add_dimension = st.form_submit_button("Add Dimension") + + if add_dimension and dim_name and dim_description: + new_dimension = CustomEvaluationDimension( + name=dim_name, + description=dim_description, + range_low=range_low, + range_high=range_high, + ) + st.session_state.dimensions.append(new_dimension) + st.success(f"Added dimension: {dim_name}") + st.rerun() + + # Tab 2: Select Existing Dimension + with tab2: + with st.form(key="select_dimension_form"): + st.subheader("Select Existing Dimension") + + # Create a list of dimension names for the selectbox + dimension_options = [ + f"{dim.name} (Range: [{dim.range_low}, {dim.range_high}])" + for dim in existing_dimensions + ] + + if dimension_options: + selected_dimension = st.selectbox( + "Choose a dimension", + options=dimension_options, + format_func=lambda x: x.split(" (Range")[ + 0 + ], # Show only the name in the dropdown + ) + + # Show details of selected dimension + if selected_dimension: + selected_idx = dimension_options.index(selected_dimension) + dim = existing_dimensions[selected_idx] + st.info(f"Description: {dim.description}") + + add_existing = st.form_submit_button("Add Selected Dimension") + + if add_existing: + selected_idx = dimension_options.index(selected_dimension) + dim_to_add = existing_dimensions[selected_idx] + + # Check if dimension already exists in current list + if any( + d.name == dim_to_add.name for d in st.session_state.dimensions + ): + st.error( + f"Dimension '{dim_to_add.name}' already exists in the list" + ) + else: + st.session_state.dimensions.append(dim_to_add) + st.success(f"Added existing dimension: {dim_to_add.name}") + st.rerun() + else: + st.warning("No existing dimensions available") + + # Display current dimensions with delete buttons + if st.session_state.dimensions: + st.subheader("Current Dimensions") + for idx, dim in enumerate(st.session_state.dimensions): + col1, col2, col3 = st.columns([3, 1, 0.5]) + with col1: + with st.expander(f"Dimension {idx + 1}: {dim.name}", expanded=False): + st.write(f"Description: {dim.description}") + st.write(f"Range: {dim.range_low} - {dim.range_high}") + with col2: + if st.button("Delete", key=f"delete_{idx}"): + st.session_state.dimensions.pop(idx) + st.success(f"Deleted dimension: {dim.name}") + st.rerun() + with col3: + st.write("") # Spacer + + # Submit form at the bottom + with st.form(key="submit_form"): + st.subheader("Submit Evaluation Dimension List") + list_name = st.text_input("List Name") + + col1, col2 = st.columns(2) + with col1: + submit_button = st.form_submit_button("Submit All") + with col2: + clear_button = st.form_submit_button("Clear All") + + if submit_button and list_name: + try: + wrapper = CustomEvaluationDimensionsWrapper( + name=list_name, dimensions=st.session_state.dimensions + ) + # st.write(wrapper.dict()) + + response = requests.post( + f"{st.session_state.API_BASE}/evaluation_dimensions/", + json=wrapper.dict(), + ) + + if response.status_code == 200: + st.success("Successfully created evaluation dimension list!") + # st.session_state.dimensions = [] + # st.rerun() + else: + st.error(f"Error: {response.json()['detail']}") + + except Exception as e: + st.error(f"An error occurred: {str(e)}") + + if clear_button: + st.session_state.dimensions = [] + st.rerun() + + +add_evaluation_dimension() diff --git a/ui/streamlit_ui/pages/add_scenarios.py b/ui/streamlit_ui/pages/add_scenarios.py new file mode 100644 index 00000000..0c8ad722 --- /dev/null +++ b/ui/streamlit_ui/pages/add_scenarios.py @@ -0,0 +1,119 @@ +""" +Definition +@app.post("/scenarios/", response_model=str) +async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: + scenario_profile = EnvironmentProfile(**scenario.model_dump()) + scenario_profile.save() + pk = scenario_profile.pk + assert pk is not None + return pk + +class EnvironmentProfileWrapper(BaseModel): + Wrapper for EnvironmentProfile to avoid pydantic v2 issues + + codename: str + source: str = "" + scenario: str = "" + agent_goals: list[str] = [] + relationship: Literal[0, 1, 2, 3, 4, 5] = 0 + age_constraint: str | None = None + occupation_constraint: str | None = None + agent_constraint: list[list[str]] | None = None + tag: str = "" + +class RelationshipType(IntEnum): + stranger = 0 + know_by_name = 1 + acquaintance = 2 + friend = 3 + romantic_relationship = 4 + family_member = 5 + +The age constraint of the environment, a list of tuples, each tuple is a range of age, e.g., '[(18, 25), (30, 40)]' +means the environment is only available to agent one between 18 and 25, and agent two between 30 and 40 +""" + +import streamlit as st +from ui.streamlit_ui.rendering import local_css +from sotopia.database import BaseEnvironmentProfile, RelationshipType +import requests + + +def rendering_scenario_form() -> None: + local_css("././css/style.css") + st.markdown("

Scenario Creation

", unsafe_allow_html=True) + + codename = st.text_input("Codename") + source = st.text_input("Source") + scenario = st.text_area("Scenario") + # present relationship type with the descriptions, only accept one choice, then map to the enum + relationship_mapping = { + "Stranger": RelationshipType.stranger, + "Know by Name": RelationshipType.know_by_name, + "Acquaintance": RelationshipType.acquaintance, + "Friend": RelationshipType.friend, + "Romantic Relationship": RelationshipType.romantic_relationship, + "Family Member": RelationshipType.family_member, + } + + selected_relationship = st.selectbox( + "Relationship", + list(relationship_mapping.keys()), + ) + relationship = relationship_mapping[selected_relationship] + + # first choose whether to use age constraint and then choose age range + use_age_constraint = st.checkbox("Use Age Constraint") + if use_age_constraint: + min_age = st.number_input("Min Age", min_value=0, max_value=100) + max_age = st.number_input("Max Age", min_value=0, max_value=100) + age_constraint = f"[({min_age}, {max_age})]" + if min_age > max_age: + st.error("Min age cannot be greater than max age") + else: + age_constraint = None + + # first choose whether to use occupation constraint and then choose occupation + use_occupation_constraint = st.checkbox( + "Use Occupation Constraint, use comma to separate multiple occupations" + ) + if use_occupation_constraint: + occupation = st.text_input("Occupation") + occupation_constraint = f"[{occupation}]" + else: + occupation_constraint = None + + agent1_goal = st.text_input("Agent 1 Goal") + agent2_goal = st.text_input("Agent 2 Goal") + + if st.button("Create Scenario"): + scenario_profile = BaseEnvironmentProfile( + codename="[Customize]" + codename, + source=source, + scenario=scenario, + relationship=relationship, + age_constraint=age_constraint, + occupation_constraint=occupation_constraint, + agent_goals=[agent1_goal, agent2_goal], + tag="customized_scenario", + ) + + response = requests.post( + f"{st.session_state.API_BASE}/scenarios/", + json=scenario_profile.model_dump(), + ) + + if response.status_code != 200: + st.error("Failed to create scenario. Error: " + response.text) + else: + # there are quotes in the response + scenario_id = response.json() + + st.success("Scenario created successfully! Scenario ID: " + scenario_id) + retrieved_scenario = requests.get( + f"{st.session_state.API_BASE}/scenarios/id/{scenario_id}" + ) + st.write(retrieved_scenario.json()) + + +rendering_scenario_form() diff --git a/ui/streamlit_ui/pages/display_characters.py b/ui/streamlit_ui/pages/display_characters.py new file mode 100644 index 00000000..cde880cc --- /dev/null +++ b/ui/streamlit_ui/pages/display_characters.py @@ -0,0 +1,25 @@ +import streamlit as st +import random +from sotopia.database import BaseAgentProfile + +# Importing avatars +from ui.streamlit_ui.rendering import render_character, local_css, get_agents + + +local_css("./css/style.css") + + +def display_characters() -> None: + st.title("Characters") + all_characters = get_agents() + # Randomize the order of characters + all_characters = dict(sorted(all_characters.items(), key=lambda _: random.random())) + col1, col2 = st.columns(2, gap="medium") + for i, (name, character) in enumerate(all_characters.items()): + with col1 if i % 2 == 0 else col2: + character_profile = BaseAgentProfile(**character) + render_character(character_profile) + st.write("---") + + +display_characters() diff --git a/ui/streamlit_ui/pages/display_episodes.py b/ui/streamlit_ui/pages/display_episodes.py new file mode 100644 index 00000000..26d5ff4a --- /dev/null +++ b/ui/streamlit_ui/pages/display_episodes.py @@ -0,0 +1,74 @@ +import streamlit as st +from ui.streamlit_ui.rendering import ( + render_environment_profile, + render_conversation_and_evaluation, +) +from sotopia.database import BaseEnvironmentProfile, BaseEpisodeLog +import requests + +st.title("Episode") + +st.write("Here are some instructions about using the episode renderer.") + + +def render_episodes() -> None: + tags = [ + "gpt-4_gpt-4_v0.0.1_clean", + "1019_hiring_equal_cooperative_salary_start_date_trust-bigfive-low_transparency-high_competence-low_adaptability-Agreeableness", + ] + if "current_episodes" not in st.session_state: + response = requests.get( + f"{st.session_state.API_BASE}/episodes", params={"tag": tags[0]} + ) + episodes = [BaseEpisodeLog(**episode) for episode in response.json()] + if response.status_code == 200: + st.session_state["current_episodes"] = episodes + else: + st.error("Failed to fetch episodes") + st.session_state["current_episodes"] = [] + + def update() -> None: + tag = st.session_state.selected_tag + response = requests.get( + f"{st.session_state.API_BASE}/episodes", params={"tag": tag} + ) + if response.status_code == 200: + episodes = [BaseEpisodeLog(**episode) for episode in response.json()] + st.session_state.current_episodes = episodes + else: + st.error("Failed to fetch episodes") + st.session_state.current_episodes = [] + + with st.container(): + # Dropdown for codename selection + st.selectbox( + "Choose a tag:", + tags, + index=0, + on_change=update, + key="selected_tag", + ) + + selected_index = st.number_input( + "Specify the index of the episode to display:", + min_value=0, + max_value=len(st.session_state.current_episodes) - 1, + value=0, + step=1, + ) + + if selected_index < len(st.session_state.current_episodes): + episode = st.session_state.current_episodes[selected_index] + response = requests.get( + f"{st.session_state.API_BASE}/scenarios/id/{episode.environment}" + ) # return a list of scenarios + scenario = response.json() if response.status_code == 200 else [] + scenario = scenario[0] + environment_profile = BaseEnvironmentProfile(**scenario) + render_environment_profile(environment_profile) + + st.markdown("---") + render_conversation_and_evaluation(episode) + + +render_episodes() diff --git a/ui/streamlit_ui/pages/display_evaluation_dimensions.py b/ui/streamlit_ui/pages/display_evaluation_dimensions.py new file mode 100644 index 00000000..2a96a506 --- /dev/null +++ b/ui/streamlit_ui/pages/display_evaluation_dimensions.py @@ -0,0 +1,48 @@ +import streamlit as st + +from sotopia.database import CustomEvaluationDimension + +from ui.streamlit_ui.rendering import ( + get_evaluation_dimensions, + render_evaluation_dimension, + get_distinct_evaluation_dimensions, + render_evaluation_dimension_list, +) + +st.title("Evaluation Dimensions") + +st.write("Here are some instructions about using the evaluation dimension renderer.") + + +def display_evaluation_dimensions() -> None: + # st.title("Evaluation Dimensions") + distinct_dimensions: list[CustomEvaluationDimension] = ( + get_distinct_evaluation_dimensions() + ) + + # sort the dimensions by name + distinct_dimensions.sort(key=lambda x: x.name) + + with st.expander("Evaluation Dimensions", expanded=True): + all_dimensions = [] + col1, col2 = st.columns(2, gap="medium") + for i, dimension in enumerate(distinct_dimensions): + with col1 if i % 2 == 0 else col2: + render_evaluation_dimension(dimension) + + with st.expander("Evaluation Dimension Lists", expanded=True): + all_dimension_lists: dict[str, list[CustomEvaluationDimension]] = ( + get_evaluation_dimensions() + ) + col1, col2 = st.columns(2, gap="medium") + for i, (dimension_list_name, dimensions) in enumerate( + all_dimension_lists.items() + ): + all_dimensions: list[CustomEvaluationDimension] = [ + CustomEvaluationDimension(**dimension) for dimension in dimensions + ] + with col1 if i % 2 == 0 else col2: + render_evaluation_dimension_list(dimension_list_name, all_dimensions) + + +display_evaluation_dimensions() diff --git a/ui/streamlit_ui/pages/display_scenarios.py b/ui/streamlit_ui/pages/display_scenarios.py new file mode 100644 index 00000000..7412e7ec --- /dev/null +++ b/ui/streamlit_ui/pages/display_scenarios.py @@ -0,0 +1,75 @@ +# isort: skip_file +# ruff: noqa: E402 +import streamlit as st +from ui.streamlit_ui.rendering import ( + render_environment_profile, + local_css, + get_scenarios, +) +from sotopia.database import BaseEnvironmentProfile +from redis import Redis, ConnectionError, AuthenticationError + + +local_css("./css/style.css") + + +def verify_redis_connection(url: str) -> tuple[bool, str]: + """Verify Redis connection and return status.""" + try: + # Parse URL components for direct connection test + if "@" in url: + _, credentials_and_host = url.split("//") + password, host_and_port = credentials_and_host.split("@") + password = password.split(":")[1] + host, port = host_and_port.split(":") + else: + host = "localhost" + port = "6379" + password = None + + # Test connection + redis_client = Redis( + host=host, port=int(port), password=password, socket_timeout=5 + ) + redis_client.ping() + return True, "Connection successful" + except ConnectionError: + return ( + False, + f"Could not connect to Redis at {host}:{port}. Please verify the server is running and accessible.", + ) + except AuthenticationError: + return False, "Authentication failed. Please verify the password." + except Exception as e: + return False, f"Unexpected error: {str(e)}" + + +def display_scenarios() -> None: + # Set Redis connection URL + # connection_successful, message = verify_redis_connection(redis_url) + + # if not connection_successful: + # st.error(f"Redis Connection Error: {message}") + # print(f"Redis Connection Error: {message}************************") + # st.stop() + # else: + # st.success(f"Redis Connection Successful: {message}") + # print(f"Redis Connection Successful: {message}************************") + + st.title("Scenarios") + scenarios = get_scenarios() + try: + col1, col2 = st.columns(2, gap="medium") + for index, (codename, scenario) in enumerate(scenarios.items()): + with col1 if index % 2 == 0 else col2: + environment_profile = BaseEnvironmentProfile(**scenario) + render_environment_profile(environment_profile) + st.write("---") + except Exception as e: + print(f"Error getting scenarios: {e}") + st.error( + "Failed to retrieve scenarios. Please check your Redis connection and try again." + ) + + +display_scenarios() diff --git a/ui/streamlit_ui/pages/intro.py b/ui/streamlit_ui/pages/intro.py new file mode 100644 index 00000000..ef8b842b --- /dev/null +++ b/ui/streamlit_ui/pages/intro.py @@ -0,0 +1,33 @@ +import streamlit as st + +st.title("Sotopia Demo App") +st.markdown( + """ + Sotopia is a place where you can create and explore social interactions between agents. + You can build your own **agents**, **scenarios**, and **evaluation dimensions**. + Then try out your ideas and evaluate the results with your custom settings. + + For more interesting projects using Sotopia, check out [our website](https://sotopia.world). + """ +) + +st.markdown("### Getting Started") +st.markdown( + """ + - **Browse Content**: Check out ready-made characters, scenarios, and episodes in their tabs. + - **Create Your Own**: Add your own characters, scenarios, or evaluation dimensions by clicking the corresponding tab name with a "+" button in the tabs. + - **Simulate**: Try out simulation by going to the "Simulate" tab and choose your customized settings. + - **Use Your Database**: If you have a remote Redis database, enter its URL in the sidebar to use your custom data. + """ +) + +st.markdown("### API Documentation") +st.markdown( + """ + For larger scale experiments you may need to use the API instead of the Streamlit UI. + - The API documentation for current set of Sotopia is [here](https://sotopia-lab--sotopia-fastapi-webapi-serve.modal.run/) + - When you are hosting your own API, find it in `{YOUR_API_BASE}/docs`. + - Also see [Sotopia examples](https://github.com/sotopia-lab/sotopia/example) for more information. + """ +) +st.markdown("Current API Base: " + st.session_state.API_BASE) diff --git a/ui/streamlit_ui/pages/render_chat_websocket.py b/ui/streamlit_ui/pages/render_chat_websocket.py new file mode 100644 index 00000000..5e4ea17e --- /dev/null +++ b/ui/streamlit_ui/pages/render_chat_websocket.py @@ -0,0 +1,371 @@ +import asyncio +import json +import threading +import time +from queue import Queue +from typing import Any, Optional + +import aiohttp +import streamlit as st + +from sotopia.database import EpisodeLog, EnvironmentProfile +from ui.streamlit_ui.rendering import ( + get_scenarios, + get_agents, + get_models, + get_evaluation_dimensions, + render_environment_profile, + get_abstract, + render_conversation_and_evaluation, +) + + +def initialize_session_state() -> None: + if "active" not in st.session_state: + # Initialize base state + st.session_state.scenarios = get_scenarios() + st.session_state.agent_dict = get_agents() + st.session_state.agent_model_dict = get_models() + st.session_state.evaluation_dimension_dict = get_evaluation_dimensions() + + st.session_state.scenarios = dict( + sorted( + st.session_state.scenarios.items(), + key=lambda item: item[0], + reverse=True, + ) + ) + st.session_state.agent_dict = dict( + sorted( + st.session_state.agent_dict.items(), + key=lambda item: item[0], + ) + ) + + # Use first items as default choices + st.session_state.scenario_choice = list(st.session_state.scenarios.keys())[1] + st.session_state.agent_choice_1 = list(st.session_state.agent_dict.keys())[0] + st.session_state.agent_choice_2 = list(st.session_state.agent_dict.keys())[0] + st.session_state.agent1_model_choice = list( + st.session_state.agent_model_dict.keys() + )[0] + st.session_state.agent2_model_choice = list( + st.session_state.agent_model_dict.keys() + )[0] + st.session_state.evaluation_dimension_choice = list( + st.session_state.evaluation_dimension_dict.keys() + )[0] + + # Initialize websocket manager and message list + st.session_state.messages = [] + # Set initial active state + st.session_state.active = False + + st.session_state.websocket_manager = WebSocketManager( + f"{st.session_state.WS_BASE}/ws/simulation?token=demo-token" + ) + print("Session state initialized") + + +chat_history_container = st.empty() + + +class WebSocketManager: + def __init__(self, url: str): + self.url = url + self.websocket: Optional[aiohttp.ClientWebSocketResponse] = None + self.message_queue: Queue[str | dict[str, Any]] = Queue() + self.running: bool = False + self.receive_queue: Queue[dict[str, Any]] = Queue() + self._closed = threading.Event() + + def start(self) -> None: + """Start the client in a separate thread""" + self._closed.clear() + self.running = True + self.thread = threading.Thread(target=self._run_event_loop) + self.thread.start() + + def stop(self) -> None: + """Stop the client""" + print("Stopping websocket manager...") + self.running = False + self._closed.wait(timeout=5.0) + if self.thread.is_alive(): + print("Thread is still alive after stop") + else: + print("Thread has been closed") + + def send_message(self, message: str | dict[str, Any]) -> None: + """Add a message to the queue to be sent""" + if isinstance(message, dict): + message = json.dumps(message) + self.message_queue.put(message) + + def _run_event_loop(self) -> None: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(self._connect()) # type: ignore + + async def _connect(self) -> None: + """Connect to the WebSocket server and handle messages""" + try: + async with aiohttp.ClientSession() as session: + async with session.ws_connect(self.url) as ws: + self.websocket = ws + + # Start tasks for sending and receiving messages + send_task = asyncio.create_task(self._send_messages()) # type: ignore + receive_task = asyncio.create_task(self._receive_messages()) # type: ignore + + # Wait for both tasks to complete + try: + await asyncio.gather(send_task, receive_task) + except Exception as e: + print(f"Error in tasks: {e}") + finally: + send_task.cancel() + receive_task.cancel() + finally: + print("WebSocket connection closed") + self._closed.set() + + async def _send_messages(self) -> None: + """Send messages from the queue""" + while self.running: + if not self.message_queue.empty(): + message = self.message_queue.get() + await self.websocket.send_str(message) # type: ignore + await asyncio.sleep(0.1) # Small delay to prevent busy waiting + + async def _receive_messages(self) -> None: + """Receive and handle incoming messages""" + while self.running: + try: + msg = await self.websocket.receive() # type: ignore + if msg.type == aiohttp.WSMsgType.TEXT: + print(f"Received message: {msg.data}") + self.receive_queue.put(json.loads(msg.data)) + elif msg.type == aiohttp.WSMsgType.CLOSED: + break + elif msg.type == aiohttp.WSMsgType.ERROR: + break + except Exception as e: + print(f"Error receiving message: {e}") + break + + +def set_active(value: bool) -> None: + st.session_state.active = value + + +def handle_end(message: dict[str, Any]) -> None: + set_active(False) + st.session_state.websocket_manager.stop() + + +def handle_error_msg(message: dict[str, Any]) -> None: + # TODO handle different error + print("[!!] Error in message: ", message) + st.error(f"Error in message: {message['data']['content']}") + + +def handle_server_msg(message: dict[str, Any]) -> None: + msg_type = message["data"]["type"] + if msg_type == "messages": + epilog = EpisodeLog(**message["data"]["messages"]) + st.session_state.messages.append(epilog) + + +def handle_message(message: dict[str, Any]) -> None: + # "END_SIM", "SERVER_MSG", "ERROR", + match message["type"]: + case "END_SIM": + st.session_state.websocket_manager.stop() + st.rerun() + case "SERVER_MSG": + handle_server_msg(message) + case "ERROR": + handle_error_msg(message) + case _: + st.error(f"Unknown message type: {message['data']['type']}") + + +def start_callback() -> None: + if st.session_state.agent_choice_1 == st.session_state.agent_choice_2: + st.error("Please select different agents") + else: + st.session_state.active = True + st.session_state.messages = [] + chat_history_container.empty() + st.session_state.websocket_manager.start() + st.session_state.websocket_manager.send_message( + { + "type": "START_SIM", + "data": { + "env_id": st.session_state.scenarios[ + st.session_state.scenario_choice + ]["pk"], + "agent_ids": [ + st.session_state.agent_dict[st.session_state.agent_choice_1][ + "pk" + ], + st.session_state.agent_dict[st.session_state.agent_choice_2][ + "pk" + ], + ], + "agent_models": [ + st.session_state.agent_model_dict[ + st.session_state.agent_model_choice_1 + ], + st.session_state.agent_model_dict[ + st.session_state.agent_model_choice_2 + ], + ], + "evaluation_dimension_list_name": st.session_state.evaluation_dimension_choice, + }, + } + ) + + +def stop_callback() -> None: + st.session_state.stop_sim = True + st.session_state.websocket_manager.send_message( + { + "type": "FINISH_SIM", + "data": "", + } + ) + + +def update_scenario_description() -> None: + scenario = st.session_state.scenarios[st.session_state.scenario_choice] + environment_profile = EnvironmentProfile(**scenario) + render_environment_profile(environment_profile) + + +def is_active() -> bool: + active_state = st.session_state.websocket_manager.running + assert isinstance(active_state, bool) + return active_state + + +def chat_demo() -> None: + initialize_session_state() + update_scenario_description() + + with st.sidebar: + with st.container(): + # Scenario and Agent Selection + with st.expander("Simulation Setup", expanded=True): + scenario_col, scenario_desc_col = st.columns(2) + with scenario_col: + st.selectbox( + "Choose a scenario:", + st.session_state.scenarios.keys(), + key="scenario_choice", + disabled=is_active(), + ) + + with scenario_desc_col: + st.markdown( + f"""**Description:** {get_abstract(st.session_state.scenarios[st.session_state.scenario_choice]["scenario"])}""", + unsafe_allow_html=True, + ) + + agent1_col, agent2_col = st.columns(2) + with agent1_col: + st.selectbox( + "Choose Agent 1:", + list(st.session_state.agent_dict.keys()), + key="agent_choice_1", + disabled=is_active(), + ) + + with agent2_col: + st.selectbox( + "Choose Agent 2:", + list(st.session_state.agent_dict.keys()), + key="agent_choice_2", + disabled=is_active(), + ) + + model1_col, model2_col = st.columns(2) + with model1_col: + st.selectbox( + "Choose Agent 1 Model:", + list(st.session_state.agent_model_dict.keys()), + key="agent_model_choice_1", + disabled=is_active(), + ) + + with model2_col: + st.selectbox( + "Choose Agent 2 Model:", + list(st.session_state.agent_model_dict.keys()), + key="agent_model_choice_2", + disabled=is_active(), + ) + + st.selectbox( + "Choose evaluation dimensions:", + list(st.session_state.evaluation_dimension_dict.keys()), + key="evaluation_dimension_choice", + disabled=is_active(), + ) + + evaluation_dimension_str = f"**Evaluation Dimensions:** {st.session_state.evaluation_dimension_choice}.
**Metric includes:** " + for eval_dim in st.session_state.evaluation_dimension_dict[ + st.session_state.evaluation_dimension_choice + ]: + evaluation_dimension_str += f"{eval_dim['name']}, " + + st.markdown( + evaluation_dimension_str[:-2] + ".", + unsafe_allow_html=True, + ) + + with st.expander("Other Options", expanded=False): + st.text_input("Max Turns", key="max_turns", value="20") + st.text_input("Max Stale Turns", key="max_stale_turns", value="3") + + # Control Buttons + col1, col2, col3 = st.columns([2, 2, 2]) + + with col1: + st.button( + "Start Simulation", + disabled=is_active(), + on_click=start_callback, + ) + + with col2: + st.button( + "Stop Simulation", + disabled=not is_active(), + on_click=stop_callback, + ) + + chat_history_container = st.empty() + while is_active(): + if ( + "websocket_manager" in st.session_state + and st.session_state.websocket_manager.receive_queue.qsize() > 0 + ): + # get messages one by one and process them + + while not st.session_state.websocket_manager.receive_queue.empty(): + message = st.session_state.websocket_manager.receive_queue.get() + handle_message(message) + + with chat_history_container.container(): + if st.session_state.messages: + render_conversation_and_evaluation(st.session_state.messages[-1]) + time.sleep(1) + + with chat_history_container.container(): + if st.session_state.messages: + render_conversation_and_evaluation(st.session_state.messages[-1]) + + +chat_demo() diff --git a/ui/streamlit_ui/rendering/__init__.py b/ui/streamlit_ui/rendering/__init__.py new file mode 100644 index 00000000..365528ef --- /dev/null +++ b/ui/streamlit_ui/rendering/__init__.py @@ -0,0 +1,40 @@ +from .render_elements import ( # type: ignore + render_environment_profile, + render_conversation_and_evaluation, + render_character, + render_evaluation_dimension, + render_evaluation_dimension_list, +) +from .render_utils import ( + avatar_mapping, + render_messages, + get_full_name, + get_abstract, + local_css, +) + +from .get_elements import ( # type: ignore + get_scenarios, + get_agents, + get_models, + get_evaluation_dimensions, + get_distinct_evaluation_dimensions, +) + +__all__ = [ + "render_conversation_and_evaluation", + "avatar_mapping", + "render_character", + "render_environment_profile", + "render_messages", + "get_full_name", + "get_abstract", + "local_css", + "get_scenarios", + "get_agents", + "get_models", + "get_evaluation_dimensions", + "get_distinct_evaluation_dimensions", + "render_evaluation_dimension", + "render_evaluation_dimension_list", +] diff --git a/ui/streamlit_ui/rendering/get_elements.py b/ui/streamlit_ui/rendering/get_elements.py new file mode 100644 index 00000000..ff86de83 --- /dev/null +++ b/ui/streamlit_ui/rendering/get_elements.py @@ -0,0 +1,53 @@ +import requests +import streamlit as st +from typing import Any +from ui.streamlit_ui.rendering.render_utils import get_full_name +from sotopia.database import CustomEvaluationDimension + + +def get_models() -> dict[str, dict[Any, Any]]: + # use synchronous code to get the agents + with requests.get(f"{st.session_state.API_BASE}/models") as resp: + models = resp.json() + return {model: model for model in models} + + +def get_scenarios() -> dict[str, dict[Any, Any]]: + # use synchronous code to get the scenarios + with requests.get(f"{st.session_state.API_BASE}/scenarios") as resp: + scenarios = resp.json() + return {scenario["codename"]: scenario for scenario in scenarios} + + +def get_agents(id: str = "") -> dict[str, dict[Any, Any]]: + # use synchronous code to get the agents + if id: + with requests.get(f"{st.session_state.API_BASE}/agents/id/{id}") as resp: + agents = resp.json() + else: + with requests.get(f"{st.session_state.API_BASE}/agents") as resp: + agents = resp.json() + return {get_full_name(agent): agent for agent in agents} + + +def get_evaluation_dimensions() -> dict[str, dict[Any, Any]]: + # use synchronous code to get the evaluation dimensions + with requests.get(f"{st.session_state.API_BASE}/evaluation_dimensions") as resp: + evaluation_dimensions = resp.json() + + return evaluation_dimensions + + +def get_distinct_evaluation_dimensions() -> list[CustomEvaluationDimension]: + all_dimension_lists: dict[str, list[Any]] = get_evaluation_dimensions() + distinct_dimensions: list[CustomEvaluationDimension] = [] + distinct_dimension_names: set[str] = set() + + for dimension_list_name, dimensions in all_dimension_lists.items(): + for dimension in dimensions: + custom_dimension = CustomEvaluationDimension(**dimension) + if custom_dimension.name not in distinct_dimension_names: + distinct_dimensions.append(custom_dimension) + distinct_dimension_names.add(custom_dimension.name) + + return distinct_dimensions diff --git a/ui/streamlit_ui/rendering/render_elements.py b/ui/streamlit_ui/rendering/render_elements.py new file mode 100644 index 00000000..e70e818a --- /dev/null +++ b/ui/streamlit_ui/rendering/render_elements.py @@ -0,0 +1,297 @@ +import json + +import streamlit as st +from sotopia.database import ( + AgentProfile, + BaseEnvironmentProfile, + EpisodeLog, + CustomEvaluationDimension, +) +from sotopia.envs.parallel import render_text_for_environment + +from ui.streamlit_ui.rendering.render_utils import ( + get_full_name, + render_messages, + local_css, +) +from ui.streamlit_ui.rendering.get_elements import get_agents + +from .render_utils import avatar_mapping + + +role_mapping = { + "Background Info": "background", + "System": "info", + "Environment": "env", + "Observation": "obs", + "General": "eval", +} + + +def update_database_callback() -> None: + pass + + +def display_field(label: str, value: str) -> str: + if value: + return f"

{label}: {value}

" + return "" + + +def render_evaluation_dimension(dimension: CustomEvaluationDimension) -> None: + local_css("././css/style.css") + + st.markdown( + f""" +
+

{dimension.name}:

+
+

{dimension.description}

+
+

Range: [{dimension.range_low}, {dimension.range_high}]

+
+
+
+ """, + unsafe_allow_html=True, + ) + + +def render_evaluation_dimension_list( + name: str, + dimensions: list[CustomEvaluationDimension], +) -> None: + local_css("././css/style.css") + + all_dimension_names = [dimension.name for dimension in dimensions] + + st.markdown( + f""" +
+

{name}:

+
+

Includes: {', '.join(all_dimension_names)}

+
+
+ """, + unsafe_allow_html=True, + ) + + +def render_character(character: AgentProfile) -> None: + local_css("././css/style.css") + + full_name = f"{character.first_name} {character.last_name}" + avatar_file = avatar_mapping.get(full_name, avatar_mapping["default_avatar"]) + + # Create two columns: one for the avatar and one for the markdown + col1, col2 = st.columns([1, 3]) # Adjust the ratio to control column width + + with col1: + # Display avatar in the first column + st.image( + str(avatar_file), + caption=full_name, + width=150, # Set the desired width + ) + + with col2: + # Display demographic info in the second column + int_age = getattr(character, "age", 0) + age = str(int_age) if int_age else "" + gender = getattr(character, "gender", "") + occupation = getattr(character, "occupation", "") + pronouns = getattr(character, "gender_pronoun", "") + + basic_info = [age, gender, pronouns, occupation] + + sub_header = " · ".join(filter(None, basic_info)) + + secret = getattr(character, "secret", "") + secret_header = "" + if secret: + secret_header = f"Secret: {secret}" + st.markdown( + f""" +
+

{sub_header}

+
+

{getattr(character, "public_info", "")}

+
+

{secret_header}

+
+
+
+ """, + unsafe_allow_html=True, + ) + with st.expander("Personality Info", expanded=False): + additional_info = "" + + additional_info += display_field( + "Personality and Values", character.personality_and_values + ) + additional_info += display_field("Big Five", character.big_five) + additional_info += display_field( + "Moral Values", ", ".join(character.moral_values) + ) + additional_info += display_field( + "Schwartz Personal Values", ", ".join(character.schwartz_personal_values) + ) + additional_info += display_field( + "Decision Making Style", character.decision_making_style + ) + additional_info += display_field("Model ID", character.model_id) + additional_info += display_field("MBTI", character.mbti) + + st.markdown( + f""" +
+ {additional_info} +
+ """, + unsafe_allow_html=True, + ) + + +def render_environment_profile(profile: BaseEnvironmentProfile) -> None: + # Render the codename as a subheader + # Render the scenario with domain and realism in styled tags + if len(profile.agent_goals) == 2: + processed_agent1_goal = render_text_for_environment( + profile.agent_goals[0] + ).replace("\n", "
") + processed_agent2_goal = render_text_for_environment( + profile.agent_goals[1] + ).replace("\n", "
") + else: + processed_agent1_goal = "" + processed_agent2_goal = "" + + st.markdown( + f""" +
+

Scenario: {profile.scenario}

+
+
+

Agent 1's Goal

+
+

{processed_agent1_goal}

+
+
+
+

Agent 2's Goal

+
+

{processed_agent2_goal}

+
+
+
+
+ """, + unsafe_allow_html=True, + ) + + # Foldable green container for additional information + with st.expander("Additional Information", expanded=False): + st.write( + f""" +
+

Codename

+

{profile.codename}

+

Source

+

{profile.source}

+

Relationship

+

{profile.relationship.name}

+

Age Constraint

+

{profile.age_constraint if profile.age_constraint else 'None'}

+

Occupation Constraint

+

{profile.occupation_constraint if profile.occupation_constraint else 'None'}

+

Agent Constraint

+

{profile.agent_constraint if profile.agent_constraint else 'None'}

+

Tag

+

{profile.tag}

+
+ """, + unsafe_allow_html=True, + ) + + +def render_conversation_and_evaluation(episode: EpisodeLog) -> None: + local_css("./././css/style.css") + agents = [list(get_agents(agent).values())[0] for agent in episode.agents] + agent_names = [get_full_name(agent) for agent in agents] + + messages = render_messages(episode) + + background_messages = [ + message for message in messages if message["role"] == "Background Info" + ] + evaluation_messages = [ + message for message in messages if message["type"] == "comment" + ] + conversation_messages = [ + message + for message in messages + if message not in background_messages and message not in evaluation_messages + ] + + assert ( + len(background_messages) == 2 + ), f"Need 2 background messages, but got {len(background_messages)}" + + st.markdown("---") + + st.subheader("Conversation & Evaluation") + with st.expander("Conversation", expanded=True): + for index, message in enumerate(conversation_messages): + role = role_mapping.get(message["role"], message["role"]) + content = message["content"] + # escape doller sign + content = content.replace("$", "$") + + if role == "obs" or message.get("type") == "action": + try: + content = json.loads(content) + except Exception as e: + print(e) + + with st.chat_message( + role, + avatar=str( + avatar_mapping.get(message["role"], avatar_mapping["default"]) + ), + ): + if isinstance(content, dict): + st.json(content) + elif role == "info": + st.markdown( + f""" +
+ {content} +
+ """, + unsafe_allow_html=True, + ) + else: + if role == agent_names[1]: + # add background grey color + st.write(f"**{role}**") + st.markdown( + f""" +
+ {content} +
+ """, + unsafe_allow_html=True, + ) + else: + st.write(f"**{role}**") + st.markdown( + content.replace("\n", "
"), unsafe_allow_html=True + ) + + with st.expander("Evaluation Results", expanded=True): + for message in evaluation_messages: + st.markdown( + message["content"].replace("\n", "
"), unsafe_allow_html=True + ) diff --git a/ui/streamlit_ui/rendering/render_utils.py b/ui/streamlit_ui/rendering/render_utils.py new file mode 100644 index 00000000..a6e92a13 --- /dev/null +++ b/ui/streamlit_ui/rendering/render_utils.py @@ -0,0 +1,216 @@ +from typing import TypedDict, Any +from sotopia.database import EpisodeLog +from pathlib import Path +import streamlit as st + + +class messageForRendering(TypedDict): + role: str + type: str + content: str + + +male_links = [ + "https://cmu.box.com/shared/static/a4dx4ys1j5twsxlf5sus4hzmzrlf8fp1.svg", + "https://cmu.box.com/shared/static/317hzxng0tnnj6osw3bgove5o6ao0wgg.svg", + "https://cmu.box.com/shared/static/iok10ur3qtsi9dey5jo14151jvvpoppr.svg", + "https://cmu.box.com/shared/static/317hzxng0tnnj6osw3bgove5o6ao0wgg.svg", + "https://cmu.box.com/shared/static/ybz7475ouyfdfx6l5rmkz1gfg4118643.svg", +] + +female_links = [ + "https://cmu.box.com/shared/static/wahd1spibvl2cxh7nxv3ssa8ev7inqlj.svg", + "https://cmu.box.com/shared/static/pqyi0rqn8ttvj0ivxgyh9f5pkp03mq6q.svg", + "https://cmu.box.com/shared/static/0616n35jzpuolzi35236s8kx6ontpiv3.svg", + "https://cmu.box.com/shared/static/ps2k6j31nowl4z74ospldgxcfiuhgjff.svg", + "https://cmu.box.com/shared/static/dmonscgihkfm2slcxx2ldtbyil6n1jha.svg", +] + +avatar_path = Path("./assets/avatars") +avatar_mapping = { + "default": "https://cmu.box.com/shared/static/r5xkl977ktt0bke29iuqiafasn900y45.png", + "default_avatar": "https://cmu.box.com/shared/static/a4dx4ys1j5twsxlf5sus4hzmzrlf8fp1.svg", + "Samuel Anderson": male_links[0], + "Zane Bennett": male_links[1], + "William Brown": male_links[2], + "Rafael Cortez": male_links[3], + "Noah Davis": male_links[4], + "Eli Dawson": male_links[0], + "Miles Hawkins": male_links[1], + "Hendrick Heinz": male_links[2], + "Benjamin Jackson": male_links[3], + "Ethan Johnson": male_links[4], + "Liam Johnson": male_links[0], + "Leo Williams": male_links[1], + "Finnegan O'Malley": male_links[2], + "Jaxon Prentice": male_links[3], + "Donovan Reeves": male_links[4], + "Micah Stevens": male_links[0], + "Oliver Thompson": male_links[1], + "Ethan Smith": male_links[2], + "Oliver Smith": male_links[3], + "Baxter Sterling": male_links[4], + "Jasmine Blake": female_links[0], + "Sophia Brown": female_links[1], + "Mia Davis": female_links[2], + "Naomi Fletcher": female_links[3], + "Lena Goodwin": female_links[4], + "Lily Greenberg": female_links[0], + "Emily Harrison": female_links[1], + "Amara Hartley": female_links[2], + "Sophia James": female_links[3], + "Ava Martinez": female_links[4], + "Isabelle Martinez": female_links[0], + "Gwen Pierce": female_links[1], + "Sasha Ramirez": female_links[2], + "Giselle Rousseau": female_links[3], + "Mia Sanders": female_links[4], + "Calista Sinclair": female_links[0], + "Esmeralda Solis": female_links[1], + "Ava Thompson": female_links[2], + "Imelda Thorne": female_links[3], + "Isabella White": female_links[4], +} + + +def get_abstract(description: str) -> str: + return " ".join(description.split()[:50]) + "..." + + +def local_css(file_name: str) -> None: + with open(file_name) as f: + st.markdown(f"", unsafe_allow_html=True) + + +def get_full_name(agent_profile: dict[str, Any]) -> str: + return f"{agent_profile['first_name']} {agent_profile['last_name']}" + + +def parse_reasoning(reasoning: str, num_agents: int) -> tuple[list[str], str]: + """Parse the reasoning string into a dictionary.""" + sep_token = "SEPSEP" + for i in range(1, num_agents + 1): + reasoning = ( + reasoning.replace(f"Agent {i} comments:\n", sep_token) + .strip(" ") + .strip("\n") + ) + all_chunks = reasoning.split(sep_token) + general_comment = all_chunks[0].strip(" ").strip("\n") + comment_chunks = all_chunks[-num_agents:] + + return comment_chunks, general_comment + + +def render_messages(episode: EpisodeLog) -> list[messageForRendering]: + """Generate a list of messages for human-readable version of the episode log.""" + + messages_for_rendering: list[messageForRendering] = [] + + for idx, turn in enumerate(episode.messages): + is_observation_printed = False + + if idx == 0: + assert ( + len(turn) >= 2 + ), "The first turn should have at least environment messages" + + messages_for_rendering.append( + {"role": "Background Info", "type": "info", "content": turn[0][2]} + ) + messages_for_rendering.append( + {"role": "Background Info", "type": "info", "content": turn[1][2]} + ) + messages_for_rendering.append( + {"role": "System", "type": "divider", "content": "Start Simulation"} + ) + + for sender, receiver, message in turn: + if not is_observation_printed and "Observation:" in message and idx != 0: + extract_observation = message.split("Observation:")[1].strip() + if extract_observation: + messages_for_rendering.append( + { + "role": "Observation", + "type": "observation", + "content": extract_observation, + } + ) + is_observation_printed = True + + if receiver == "Environment": + if sender != "Environment": + if "did nothing" in message: + continue + elif "left the conversation" in message: + messages_for_rendering.append( + { + "role": "Environment", + "type": "leave", + "content": f"{sender} left the conversation", + } + ) + else: + if "said:" in message: + message = message.split("said:")[1].strip() + messages_for_rendering.append( + {"role": sender, "type": "said", "content": message} + ) + else: + message = message.replace("[action]", "") + messages_for_rendering.append( + {"role": sender, "type": "action", "content": message} + ) + else: + messages_for_rendering.append( + { + "role": "Environment", + "type": "environment", + "content": message, + } + ) + + messages_for_rendering.append( + {"role": "System", "type": "divider", "content": "End Simulation"} + ) + + reasoning_per_agent, general_comment = parse_reasoning( + episode.reasoning, + len( + set( + msg["role"] + for msg in messages_for_rendering + if msg["type"] in {"said", "action"} + ) + ), + ) + + if general_comment == "": + return messages_for_rendering[:-1] + + messages_for_rendering.append( + {"role": "General", "type": "comment", "content": general_comment} + ) + + for idx, reasoning in enumerate(reasoning_per_agent): + reasoning_lines = reasoning.split("\n") + new_reasoning = "" + for reasoning_line in reasoning_lines: + dimension = reasoning_line.split(":")[0] + new_reasoning += ( + (f"**{dimension}**: {':'.join(reasoning_line.split(':')[1:])}" + "\n") + if dimension != "" + else reasoning_line + "\n" + ) + messages_for_rendering.append( + { + "role": f"Agent {idx + 1}", + "type": "comment", + "content": f"**Agent {idx + 1} reasoning**:\n{new_reasoning}\n\n**Rewards**: {str(episode.rewards[idx])}", + } + ) + + for item in messages_for_rendering: + item["content"] = item["content"].replace("$", "\\$") + + return messages_for_rendering diff --git a/uv.lock b/uv.lock index 217200dd..d2e999d4 100644 --- a/uv.lock +++ b/uv.lock @@ -44,16 +44,16 @@ wheels = [ [[package]] name = "aiohappyeyeballs" -version = "2.4.3" +version = "2.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/69/2f6d5a019bd02e920a3417689a89887b39ad1e350b562f9955693d900c40/aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586", size = 21809 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/55/e4373e888fdacb15563ef6fa9fa8c8252476ea071e96fb46defac9f18bf2/aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745", size = 21977 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/d8/120cd0fe3e8530df0539e71ba9683eade12cae103dd7543e50d15f737917/aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572", size = 14742 }, + { url = "https://files.pythonhosted.org/packages/b9/74/fbb6559de3607b3300b9be3cc64e97548d55678e44623db17820dbd20002/aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8", size = 14756 }, ] [[package]] name = "aiohttp" -version = "3.11.7" +version = "3.11.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -65,65 +65,65 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4b/cb/f9bb10e0cf6f01730b27d370b10cc15822bea4395acd687abc8cc5fed3ed/aiohttp-3.11.7.tar.gz", hash = "sha256:01a8aca4af3da85cea5c90141d23f4b0eee3cbecfd33b029a45a80f28c66c668", size = 7666482 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/7e/fb4723d280b4de2642c57593cb94f942bfdc15def510d12b5d22a1b955a6/aiohttp-3.11.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8bedb1f6cb919af3b6353921c71281b1491f948ca64408871465d889b4ee1b66", size = 706857 }, - { url = "https://files.pythonhosted.org/packages/57/f1/4eb447ad029801b1007ff23025c2bcb2519af2e03085717efa333f1803a5/aiohttp-3.11.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f5022504adab881e2d801a88b748ea63f2a9d130e0b2c430824682a96f6534be", size = 466733 }, - { url = "https://files.pythonhosted.org/packages/ed/7e/e385e54fa3d9360f9d1ea502a5627f2f4bdd141dd227a1f8785335c4fca9/aiohttp-3.11.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e22d1721c978a6494adc824e0916f9d187fa57baeda34b55140315fa2f740184", size = 453993 }, - { url = "https://files.pythonhosted.org/packages/ee/41/660cba8b4b10a9072ae77ce81558cca94d98aaec649a3085e50b8226fc17/aiohttp-3.11.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e993676c71288618eb07e20622572b1250d8713e7e00ab3aabae28cb70f3640d", size = 1576329 }, - { url = "https://files.pythonhosted.org/packages/e1/51/4c59724afde127001b22cf09b28171829329cf2c838cb05f6de521f125cf/aiohttp-3.11.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e13a05db87d3b241c186d0936808d0e4e12decc267c617d54e9c643807e968b6", size = 1630344 }, - { url = "https://files.pythonhosted.org/packages/c7/66/513f15cec950410dbc4439926ea4d9361136df7a97ddffab0deea1b68131/aiohttp-3.11.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ba8d043fed7ffa117024d7ba66fdea011c0e7602327c6d73cacaea38abe4491", size = 1666837 }, - { url = "https://files.pythonhosted.org/packages/7a/c0/3e59d4cd8fd4c0e365d0ec962e0679dfc7629bdf0e67be398ca842ad4661/aiohttp-3.11.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda3ed0a7869d2fa16aa41f9961ade73aa2c2e3b2fcb0a352524e7b744881889", size = 1580628 }, - { url = "https://files.pythonhosted.org/packages/22/a6/c4aea2cf583821e02f7a92c43f5f554d2334e22b741e21e8f31da2b2386b/aiohttp-3.11.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43bfd25113c1e98aec6c70e26d5f4331efbf4aa9037ba9ad88f090853bf64d7f", size = 1539922 }, - { url = "https://files.pythonhosted.org/packages/7b/54/52f33fc9cecaf28f8400e92d9c22e37939c856c4a8af26a71023ec1de689/aiohttp-3.11.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3dd3e7e7c9ef3e7214f014f1ae260892286647b3cf7c7f1b644a568fd410f8ca", size = 1527342 }, - { url = "https://files.pythonhosted.org/packages/d4/e0/fc91528bfb0283691b0448e93fe64d2416254a9ca34c58c666240440db89/aiohttp-3.11.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78c657ece7a73b976905ab9ec8be9ef2df12ed8984c24598a1791c58ce3b4ce4", size = 1534194 }, - { url = "https://files.pythonhosted.org/packages/34/be/c6d571f46e9ef1720a850dce4c04dbfe38627a64bfdabdefb448c547e267/aiohttp-3.11.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:db70a47987e34494b451a334605bee57a126fe8d290511349e86810b4be53b01", size = 1609532 }, - { url = "https://files.pythonhosted.org/packages/3d/af/1da6918c83fb427e0f23401dca03b8d6ec776fb61ad25d2f5a8d564418e6/aiohttp-3.11.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9e67531370a3b07e49b280c1f8c2df67985c790ad2834d1b288a2f13cd341c5f", size = 1630627 }, - { url = "https://files.pythonhosted.org/packages/32/20/fd3f4d8bc60227f1eb2fc20e75679e270ef05f81ae618cd869a68f19a32c/aiohttp-3.11.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9202f184cc0582b1db15056f2225ab4c1e3dac4d9ade50dd0613ac3c46352ac2", size = 1565670 }, - { url = "https://files.pythonhosted.org/packages/b0/9f/db692e10567acb0970618557be3bfe47fe92eac69fa7d3e81315d39b4a8b/aiohttp-3.11.7-cp310-cp310-win32.whl", hash = "sha256:2257bdd5cf54a4039a4337162cd8048f05a724380a2283df34620f55d4e29341", size = 415107 }, - { url = "https://files.pythonhosted.org/packages/0b/8c/9fb539a8a773356df3dbddd77d4a3aff3eda448a602a90e5582d8b1903a4/aiohttp-3.11.7-cp310-cp310-win_amd64.whl", hash = "sha256:b7215bf2b53bc6cb35808149980c2ae80a4ae4e273890ac85459c014d5aa60ac", size = 440569 }, - { url = "https://files.pythonhosted.org/packages/13/7f/272fa1adf68fe2fbebfe686a67b50cfb40d86dfe47d0441aff6f0b7c4c0e/aiohttp-3.11.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cea52d11e02123f125f9055dfe0ccf1c3857225fb879e4a944fae12989e2aef2", size = 706820 }, - { url = "https://files.pythonhosted.org/packages/79/3c/6d612ef77cdba75364393f04c5c577481e3b5123a774eea447ada1ddd14f/aiohttp-3.11.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ce18f703b7298e7f7633efd6a90138d99a3f9a656cb52c1201e76cb5d79cf08", size = 466654 }, - { url = "https://files.pythonhosted.org/packages/4f/b8/1052667d4800cd49bb4f869f1ed42f5e9d5acd4676275e64ccc244c9c040/aiohttp-3.11.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:670847ee6aeb3a569cd7cdfbe0c3bec1d44828bbfbe78c5d305f7f804870ef9e", size = 454041 }, - { url = "https://files.pythonhosted.org/packages/9f/07/80fa7302314a6ee1c9278550e9d95b77a4c895999bfbc5364ed0ee28dc7c/aiohttp-3.11.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dda726f89bfa5c465ba45b76515135a3ece0088dfa2da49b8bb278f3bdeea12", size = 1684778 }, - { url = "https://files.pythonhosted.org/packages/2e/30/a71eb45197ad6bb6af87dfb39be8b56417d24d916047d35ef3f164af87f4/aiohttp-3.11.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25b74a811dba37c7ea6a14d99eb9402d89c8d739d50748a75f3cf994cf19c43", size = 1740992 }, - { url = "https://files.pythonhosted.org/packages/22/74/0f9394429f3c4197129333a150a85cb2a642df30097a39dd41257f0b3bdc/aiohttp-3.11.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5522ee72f95661e79db691310290c4618b86dff2d9b90baedf343fd7a08bf79", size = 1781816 }, - { url = "https://files.pythonhosted.org/packages/7f/1a/1e256b39179c98d16d53ac62f64bfcfe7c5b2c1e68b83cddd4165854524f/aiohttp-3.11.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fbf41a6bbc319a7816ae0f0177c265b62f2a59ad301a0e49b395746eb2a9884", size = 1676692 }, - { url = "https://files.pythonhosted.org/packages/9b/37/f19d2e00efcabb9183b16bd91244de1d9c4ff7bf0fb5b8302e29a78f3286/aiohttp-3.11.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59ee1925b5a5efdf6c4e7be51deee93984d0ac14a6897bd521b498b9916f1544", size = 1619523 }, - { url = "https://files.pythonhosted.org/packages/ae/3c/af50cf5e06b98783fd776f17077f7b7e755d461114af5d6744dc037fc3b0/aiohttp-3.11.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24054fce8c6d6f33a3e35d1c603ef1b91bbcba73e3f04a22b4f2f27dac59b347", size = 1644084 }, - { url = "https://files.pythonhosted.org/packages/c0/a6/4e0233b085cbf2b6de573515c1eddde82f1c1f17e69347e32a5a5f2617ff/aiohttp-3.11.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:351849aca2c6f814575c1a485c01c17a4240413f960df1bf9f5deb0003c61a53", size = 1648332 }, - { url = "https://files.pythonhosted.org/packages/06/20/7062e76e7817318c421c0f9d7b650fb81aaecf6d2f3a9833805b45ec2ea8/aiohttp-3.11.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:12724f3a211fa243570e601f65a8831372caf1a149d2f1859f68479f07efec3d", size = 1730912 }, - { url = "https://files.pythonhosted.org/packages/6c/1c/ff6ae4b1789894e6faf8a4e260cd3861cad618dc80ad15326789a7765750/aiohttp-3.11.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7ea4490360b605804bea8173d2d086b6c379d6bb22ac434de605a9cbce006e7d", size = 1752619 }, - { url = "https://files.pythonhosted.org/packages/33/58/ddd5cba5ca245c00b04e9d28a7988b0f0eda02de494f8e62ecd2780655c2/aiohttp-3.11.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e0bf378db07df0a713a1e32381a1b277e62ad106d0dbe17b5479e76ec706d720", size = 1692801 }, - { url = "https://files.pythonhosted.org/packages/b2/fc/32d5e2070b43d3722b7ea65ddc6b03ffa39bcc4b5ab6395a825cde0872ad/aiohttp-3.11.7-cp311-cp311-win32.whl", hash = "sha256:cd8d62cab363dfe713067027a5adb4907515861f1e4ce63e7be810b83668b847", size = 414899 }, - { url = "https://files.pythonhosted.org/packages/ec/7e/50324c6d3df4540f5963def810b9927f220c99864065849a1dfcae77a6ce/aiohttp-3.11.7-cp311-cp311-win_amd64.whl", hash = "sha256:bf0e6cce113596377cadda4e3ac5fb89f095bd492226e46d91b4baef1dd16f60", size = 440938 }, - { url = "https://files.pythonhosted.org/packages/bf/1e/2e96b2526c590dcb99db0b94ac4f9b927ecc07f94735a8a941dee143d48b/aiohttp-3.11.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4bb7493c3e3a36d3012b8564bd0e2783259ddd7ef3a81a74f0dbfa000fce48b7", size = 702326 }, - { url = "https://files.pythonhosted.org/packages/b5/ce/b5d7f3e68849f1f5e0b85af4ac9080b9d3c0a600857140024603653c2209/aiohttp-3.11.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e143b0ef9cb1a2b4f74f56d4fbe50caa7c2bb93390aff52f9398d21d89bc73ea", size = 461944 }, - { url = "https://files.pythonhosted.org/packages/28/fa/f4d98db1b7f8f0c3f74bdbd6d0d98cfc89984205cd33f1b8ee3f588ee5ad/aiohttp-3.11.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7c58a240260822dc07f6ae32a0293dd5bccd618bb2d0f36d51c5dbd526f89c0", size = 454348 }, - { url = "https://files.pythonhosted.org/packages/04/f0/c238dda5dc9a3d12b76636e2cf0ea475890ac3a1c7e4ff0fd6c3cea2fc2d/aiohttp-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d20cfe63a1c135d26bde8c1d0ea46fd1200884afbc523466d2f1cf517d1fe33", size = 1678795 }, - { url = "https://files.pythonhosted.org/packages/79/ee/3a18f792247e6d95dba13aaedc9dc317c3c6e75f4b88c2dd4b960d20ad2f/aiohttp-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12e4d45847a174f77b2b9919719203769f220058f642b08504cf8b1cf185dacf", size = 1734411 }, - { url = "https://files.pythonhosted.org/packages/f5/79/3eb84243087a9a32cae821622c935107b4b55a5b21b76772e8e6c41092e9/aiohttp-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf4efa2d01f697a7dbd0509891a286a4af0d86902fc594e20e3b1712c28c0106", size = 1788959 }, - { url = "https://files.pythonhosted.org/packages/91/93/ad77782c5edfa17aafc070bef978fbfb8459b2f150595ffb01b559c136f9/aiohttp-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee6a4cdcbf54b8083dc9723cdf5f41f722c00db40ccf9ec2616e27869151129", size = 1687463 }, - { url = "https://files.pythonhosted.org/packages/ba/48/db35bd21b7877efa0be5f28385d8978c55323c5ce7685712e53f3f6c0bd9/aiohttp-3.11.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6095aaf852c34f42e1bd0cf0dc32d1e4b48a90bfb5054abdbb9d64b36acadcb", size = 1618374 }, - { url = "https://files.pythonhosted.org/packages/ba/77/30f87db55c79fd145ed5fd15b92f2e820ce81065d41ae437797aaa550e3b/aiohttp-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1cf03d27885f8c5ebf3993a220cc84fc66375e1e6e812731f51aab2b2748f4a6", size = 1637021 }, - { url = "https://files.pythonhosted.org/packages/af/76/10b188b78ee18d0595af156d6a238bc60f9d8571f0f546027eb7eaf65b25/aiohttp-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1a17f6a230f81eb53282503823f59d61dff14fb2a93847bf0399dc8e87817307", size = 1650792 }, - { url = "https://files.pythonhosted.org/packages/fa/33/4411bbb8ad04c47d0f4c7bd53332aaf350e49469cf6b65b132d4becafe27/aiohttp-3.11.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:481f10a1a45c5f4c4a578bbd74cff22eb64460a6549819242a87a80788461fba", size = 1696248 }, - { url = "https://files.pythonhosted.org/packages/fe/2d/6135d0dc1851a33d3faa937b20fef81340bc95e8310536d4c7f1f8ecc026/aiohttp-3.11.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:db37248535d1ae40735d15bdf26ad43be19e3d93ab3f3dad8507eb0f85bb8124", size = 1729188 }, - { url = "https://files.pythonhosted.org/packages/f5/76/a57ceff577ae26fe9a6f31ac799bc638ecf26e4acdf04295290b9929b349/aiohttp-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d18a8b44ec8502a7fde91446cd9c9b95ce7c49f1eacc1fb2358b8907d4369fd", size = 1690038 }, - { url = "https://files.pythonhosted.org/packages/4b/81/b20e09003b6989a7f23a721692137a6143420a151063c750ab2a04878e3c/aiohttp-3.11.7-cp312-cp312-win32.whl", hash = "sha256:3d1c9c15d3999107cbb9b2d76ca6172e6710a12fda22434ee8bd3f432b7b17e8", size = 409887 }, - { url = "https://files.pythonhosted.org/packages/b7/0b/607c98bff1d07bb21e0c39e7711108ef9ff4f2a361a3ec1ce8dce93623a5/aiohttp-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:018f1b04883a12e77e7fc161934c0f298865d3a484aea536a6a2ca8d909f0ba0", size = 436462 }, +sdist = { url = "https://files.pythonhosted.org/packages/94/c4/3b5a937b16f6c2a0ada842a9066aad0b7a5708427d4a202a07bf09c67cbb/aiohttp-3.11.10.tar.gz", hash = "sha256:b1fc6b45010a8d0ff9e88f9f2418c6fd408c99c211257334aff41597ebece42e", size = 7668832 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/f2/ba44492f257a296c4bb910bf47acf41672421fd455540911b3f13d10d6cd/aiohttp-3.11.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cbad88a61fa743c5d283ad501b01c153820734118b65aee2bd7dbb735475ce0d", size = 708322 }, + { url = "https://files.pythonhosted.org/packages/2b/c7/22b0ed548c8660e978e736671f166907fb272d0a4281b2b6833310bce529/aiohttp-3.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80886dac673ceaef499de2f393fc80bb4481a129e6cb29e624a12e3296cc088f", size = 468211 }, + { url = "https://files.pythonhosted.org/packages/c9/0b/d326251888bb86ff7cb00b171e1cf3b0f0ed695622857f84a98bbc5f254b/aiohttp-3.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61b9bae80ed1f338c42f57c16918853dc51775fb5cb61da70d590de14d8b5fb4", size = 455370 }, + { url = "https://files.pythonhosted.org/packages/4e/83/28feef5a0bda728adf76e0d076566c26c6da3d29f0ccd998d07c260cae9d/aiohttp-3.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e2e576caec5c6a6b93f41626c9c02fc87cd91538b81a3670b2e04452a63def6", size = 1584399 }, + { url = "https://files.pythonhosted.org/packages/dc/97/6bdd39c4134ef243ffa9fd19a072ac9a0758d64b6d51eaaaaa34e67b8bcb/aiohttp-3.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02c13415b5732fb6ee7ff64583a5e6ed1c57aa68f17d2bda79c04888dfdc2769", size = 1632131 }, + { url = "https://files.pythonhosted.org/packages/1b/f1/8c3a1623b9d526986f03d8158c9c856e00531217998275cc6b4a14b2fb85/aiohttp-3.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfce37f31f20800a6a6620ce2cdd6737b82e42e06e6e9bd1b36f546feb3c44f", size = 1668081 }, + { url = "https://files.pythonhosted.org/packages/9c/3e/a2f4cee0dca934b1d2c4b6a7821040ce4452b9b2e4347c9be6cb10eaa835/aiohttp-3.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bbbfff4c679c64e6e23cb213f57cc2c9165c9a65d63717108a644eb5a7398df", size = 1589313 }, + { url = "https://files.pythonhosted.org/packages/fd/9c/93e9a8f39c78f0c6d938721101e28c57597046f78057ffced8a3fd571839/aiohttp-3.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49c7dbbc1a559ae14fc48387a115b7d4bbc84b4a2c3b9299c31696953c2a5219", size = 1544349 }, + { url = "https://files.pythonhosted.org/packages/68/d2/2054efe02be87a1af92cfcaf6875d7b2c34906c3ee2b90ce82afbc8927a5/aiohttp-3.11.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68386d78743e6570f054fe7949d6cb37ef2b672b4d3405ce91fafa996f7d9b4d", size = 1529018 }, + { url = "https://files.pythonhosted.org/packages/10/b0/a258bfd5ddd3d9c871a8d24e96531cb6e6f0cd98dc3028f0b98302454b23/aiohttp-3.11.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9ef405356ba989fb57f84cac66f7b0260772836191ccefbb987f414bcd2979d9", size = 1536357 }, + { url = "https://files.pythonhosted.org/packages/76/7f/8b60b93e7dc58d371813a9b8d451b7c9c9c4350f9c505edf6fae80e0812b/aiohttp-3.11.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5d6958671b296febe7f5f859bea581a21c1d05430d1bbdcf2b393599b1cdce77", size = 1607214 }, + { url = "https://files.pythonhosted.org/packages/2a/10/97a11dba0f6d16878164b92ce75e2e0196a2fd25560cae8283388a24289b/aiohttp-3.11.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:99b7920e7165be5a9e9a3a7f1b680f06f68ff0d0328ff4079e5163990d046767", size = 1628573 }, + { url = "https://files.pythonhosted.org/packages/45/66/70419d6cb9495ddcebfa54d3db07e6a9716049ef341ded1edd8982f9b7f9/aiohttp-3.11.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0dc49f42422163efb7e6f1df2636fe3db72713f6cd94688e339dbe33fe06d61d", size = 1564058 }, + { url = "https://files.pythonhosted.org/packages/2d/d6/d94506afaea3aca15ab3f4732d666ad80acd5a035a7478aa6377c9816cf3/aiohttp-3.11.10-cp310-cp310-win32.whl", hash = "sha256:40d1c7a7f750b5648642586ba7206999650208dbe5afbcc5284bcec6579c9b91", size = 416360 }, + { url = "https://files.pythonhosted.org/packages/55/03/731d1116d09ea7a3c6be731ab0eb1faa37b844d3e54fed28e3a6785ba5ab/aiohttp-3.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:68ff6f48b51bd78ea92b31079817aff539f6c8fc80b6b8d6ca347d7c02384e33", size = 441763 }, + { url = "https://files.pythonhosted.org/packages/db/7c/584d5ca19343c9462d054337828f72628e6dc204424f525df59ebfe75d1e/aiohttp-3.11.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:77c4aa15a89847b9891abf97f3d4048f3c2d667e00f8a623c89ad2dccee6771b", size = 708395 }, + { url = "https://files.pythonhosted.org/packages/cd/2d/61c33e01baeb23aebd07620ee4d780ff40f4c17c42289bf02a405f2ac312/aiohttp-3.11.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:909af95a72cedbefe5596f0bdf3055740f96c1a4baa0dd11fd74ca4de0b4e3f1", size = 468281 }, + { url = "https://files.pythonhosted.org/packages/ab/70/0ddb3a61b835068eb0badbe8016b4b65b966bad5f8af0f2d63998ff4cfa4/aiohttp-3.11.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:386fbe79863eb564e9f3615b959e28b222259da0c48fd1be5929ac838bc65683", size = 455345 }, + { url = "https://files.pythonhosted.org/packages/44/8c/4e14e9c1767d9a6ab1af1fbad9df9c77e050b39b6afe9e8343ec1ba96508/aiohttp-3.11.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de34936eb1a647aa919655ff8d38b618e9f6b7f250cc19a57a4bf7fd2062b6d", size = 1685464 }, + { url = "https://files.pythonhosted.org/packages/ef/6e/1bab78ebb4f5a1c54f0fc10f8d52abc06816a9cb1db52b9c908e3d69f9a8/aiohttp-3.11.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c9527819b29cd2b9f52033e7fb9ff08073df49b4799c89cb5754624ecd98299", size = 1743427 }, + { url = "https://files.pythonhosted.org/packages/5d/5e/c1b03bef621a8cc51ff551ef223c6ac606fabe0e35c950f56d01423ec2aa/aiohttp-3.11.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a96e3e03300b41f261bbfd40dfdbf1c301e87eab7cd61c054b1f2e7c89b9e8", size = 1785188 }, + { url = "https://files.pythonhosted.org/packages/7c/b8/df6d76a149cbd969a58da478baec0be617287c496c842ddf21fe6bce07b3/aiohttp-3.11.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f5635f7b74bcd4f6f72fcd85bea2154b323a9f05226a80bc7398d0c90763b0", size = 1674911 }, + { url = "https://files.pythonhosted.org/packages/ee/8e/e460e7bb820a08cec399971fc3176afc8090dc32fb941f386e0c68bc4ecc/aiohttp-3.11.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03b6002e20938fc6ee0918c81d9e776bebccc84690e2b03ed132331cca065ee5", size = 1619570 }, + { url = "https://files.pythonhosted.org/packages/c2/ae/3b597e09eae4e75b77ee6c65443593d245bfa067ae6a5d895abaf27cce6c/aiohttp-3.11.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6362cc6c23c08d18ddbf0e8c4d5159b5df74fea1a5278ff4f2c79aed3f4e9f46", size = 1653772 }, + { url = "https://files.pythonhosted.org/packages/b8/d1/99852f2925992c4d7004e590344e5398eb163750de2a7c1fbe07f182d3c8/aiohttp-3.11.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3691ed7726fef54e928fe26344d930c0c8575bc968c3e239c2e1a04bd8cf7838", size = 1649787 }, + { url = "https://files.pythonhosted.org/packages/39/c0/ea24627e08d722d5a6a00b3f6c9763fe3ad4650b8485f7a7a56ff932e3af/aiohttp-3.11.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31d5093d3acd02b31c649d3a69bb072d539d4c7659b87caa4f6d2bcf57c2fa2b", size = 1732666 }, + { url = "https://files.pythonhosted.org/packages/f1/27/ab52dee4443ef8bdb26473b53c841caafd2bb637a8d85751694e089913bb/aiohttp-3.11.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8b3cf2dc0f0690a33f2d2b2cb15db87a65f1c609f53c37e226f84edb08d10f52", size = 1754910 }, + { url = "https://files.pythonhosted.org/packages/cd/08/57c919d6b1f3b70bc14433c080a6152bf99454b636eb8a88552de8baaca9/aiohttp-3.11.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbbaea811a2bba171197b08eea288b9402faa2bab2ba0858eecdd0a4105753a3", size = 1692502 }, + { url = "https://files.pythonhosted.org/packages/ae/37/015006f669275735049e0549c37cb79c7a4a9350cbee070bbccb5a5b4b8a/aiohttp-3.11.10-cp311-cp311-win32.whl", hash = "sha256:4b2c7ac59c5698a7a8207ba72d9e9c15b0fc484a560be0788b31312c2c5504e4", size = 416178 }, + { url = "https://files.pythonhosted.org/packages/cf/8d/7bb48ae503989b15114baf9f9b19398c86ae93d30959065bc061b31331ee/aiohttp-3.11.10-cp311-cp311-win_amd64.whl", hash = "sha256:974d3a2cce5fcfa32f06b13ccc8f20c6ad9c51802bb7f829eae8a1845c4019ec", size = 442269 }, + { url = "https://files.pythonhosted.org/packages/25/17/1dbe2f619f77795409c1a13ab395b98ed1b215d3e938cacde9b8ffdac53d/aiohttp-3.11.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b78f053a7ecfc35f0451d961dacdc671f4bcbc2f58241a7c820e9d82559844cf", size = 704448 }, + { url = "https://files.pythonhosted.org/packages/e3/9b/112247ad47e9d7f6640889c6e42cc0ded8c8345dd0033c66bcede799b051/aiohttp-3.11.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab7485222db0959a87fbe8125e233b5a6f01f4400785b36e8a7878170d8c3138", size = 463829 }, + { url = "https://files.pythonhosted.org/packages/8a/36/a64b583771fc673062a7a1374728a6241d49e2eda5a9041fbf248e18c804/aiohttp-3.11.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf14627232dfa8730453752e9cdc210966490992234d77ff90bc8dc0dce361d5", size = 455774 }, + { url = "https://files.pythonhosted.org/packages/e5/75/ee1b8f510978b3de5f185c62535b135e4fc3f5a247ca0c2245137a02d800/aiohttp-3.11.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076bc454a7e6fd646bc82ea7f98296be0b1219b5e3ef8a488afbdd8e81fbac50", size = 1682134 }, + { url = "https://files.pythonhosted.org/packages/87/46/65e8259432d5f73ca9ebf5edb645ef90e5303724e4e52477516cb4042240/aiohttp-3.11.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:482cafb7dc886bebeb6c9ba7925e03591a62ab34298ee70d3dd47ba966370d2c", size = 1736757 }, + { url = "https://files.pythonhosted.org/packages/03/f6/a6d1e791b7153fb2d101278f7146c0771b0e1569c547f8a8bc3035651984/aiohttp-3.11.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf3d1a519a324af764a46da4115bdbd566b3c73fb793ffb97f9111dbc684fc4d", size = 1793033 }, + { url = "https://files.pythonhosted.org/packages/a8/e9/1ac90733e36e7848693aece522936a13bf17eeb617da662f94adfafc1c25/aiohttp-3.11.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24213ba85a419103e641e55c27dc7ff03536c4873470c2478cce3311ba1eee7b", size = 1691609 }, + { url = "https://files.pythonhosted.org/packages/6d/a6/77b33da5a0bc04566c7ddcca94500f2c2a2334eecab4885387fffd1fc600/aiohttp-3.11.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b99acd4730ad1b196bfb03ee0803e4adac371ae8efa7e1cbc820200fc5ded109", size = 1619082 }, + { url = "https://files.pythonhosted.org/packages/48/94/5bf5f927d9a2fedd2c978adfb70a3680e16f46d178361685b56244eb52ed/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:14cdb5a9570be5a04eec2ace174a48ae85833c2aadc86de68f55541f66ce42ab", size = 1641186 }, + { url = "https://files.pythonhosted.org/packages/99/2d/e85103aa01d1064e51bc50cb51e7b40150a8ff5d34e5a3173a46b241860b/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7e97d622cb083e86f18317282084bc9fbf261801b0192c34fe4b1febd9f7ae69", size = 1646280 }, + { url = "https://files.pythonhosted.org/packages/7b/e0/44651fda8c1d865a51b3a81f1956ea55ce16fc568fe7a3e05db7fc22f139/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:012f176945af138abc10c4a48743327a92b4ca9adc7a0e078077cdb5dbab7be0", size = 1701862 }, + { url = "https://files.pythonhosted.org/packages/4e/1e/0804459ae325a5b95f6f349778fb465f29d2b863e522b6a349db0aaad54c/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44224d815853962f48fe124748227773acd9686eba6dc102578defd6fc99e8d9", size = 1734373 }, + { url = "https://files.pythonhosted.org/packages/07/87/b8f6721668cad74bcc9c7cfe6d0230b304d1250196b221e54294a0d78dbe/aiohttp-3.11.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c87bf31b7fdab94ae3adbe4a48e711bfc5f89d21cf4c197e75561def39e223bc", size = 1694343 }, + { url = "https://files.pythonhosted.org/packages/4b/20/42813fc60d9178ba9b1b86c58a5441ddb6cf8ffdfe66387345bff173bcff/aiohttp-3.11.10-cp312-cp312-win32.whl", hash = "sha256:06a8e2ee1cbac16fe61e51e0b0c269400e781b13bcfc33f5425912391a542985", size = 411118 }, + { url = "https://files.pythonhosted.org/packages/3a/51/df9c263c861ce93998b5ad2ba3212caab2112d5b66dbe91ddbe90c41ded4/aiohttp-3.11.10-cp312-cp312-win_amd64.whl", hash = "sha256:be2b516f56ea883a3e14dda17059716593526e10fb6303189aaf5503937db408", size = 437424 }, ] [[package]] name = "aiosignal" -version = "1.3.1" +version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/67/0952ed97a9793b4958e5736f6d2b346b414a2cd63e82d05940032f45b32f/aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", size = 19422 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/ac/a7305707cb852b7e16ff80eaf5692309bde30e2b1100a1fcacdc8f731d97/aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17", size = 7617 }, + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, ] [[package]] @@ -138,6 +138,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/5c/639dc59441df1d5cec49a09c36eb89af651476760f950b7d018bdf0ec4a7/aiostream-0.6.4-py3-none-any.whl", hash = "sha256:bd8c6a8b90a52c0325a3b19406f0f2a131448e596c06398886f5be1c73b4cea9", size = 53665 }, ] +[[package]] +name = "altair" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "narwhals" }, + { name = "packaging" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200 }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -149,7 +165,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.39.0" +version = "0.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -160,24 +176,24 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/02/2ea51930009d7537c4648f51d1bb3202ec76704cbb39a2a863ab38bee3dd/anthropic-0.39.0.tar.gz", hash = "sha256:94671cc80765f9ce693f76d63a97ee9bef4c2d6063c044e983d21a2e262f63ba", size = 189339 } +sdist = { url = "https://files.pythonhosted.org/packages/4d/d9/c39005f04c602607d68d48d1c917b35af8d16b687b7ca427ca787c39d8b9/anthropic-0.40.0.tar.gz", hash = "sha256:3efeca6d9e97813f93ed34322c6c7ea2279bf0824cd0aa71b59ce222665e2b87", size = 190939 } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/61/2580eaa171cab20708d59d39cadd15f78a6c617759e8d0a12e18fe3302d1/anthropic-0.39.0-py3-none-any.whl", hash = "sha256:ea17093ae0ce0e1768b0c46501d6086b5bcd74ff39d68cd2d6396374e9de7c09", size = 198392 }, + { url = "https://files.pythonhosted.org/packages/cb/18/a68cfb9a11990377650c36c25b5dfb0baece900e9e505b68e1aa06ad0227/anthropic-0.40.0-py3-none-any.whl", hash = "sha256:442028ae8790ff9e3b6f8912043918755af1230d193904ae2ef78cc22995280c", size = 199484 }, ] [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c", size = 173422 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/40/318e58f669b1a9e00f5c4453910682e2d9dd594334539c7b7817dabb765f/anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48", size = 177076 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 }, + { url = "https://files.pythonhosted.org/packages/a0/7a/4daaf3b6c08ad7ceffea4634ec206faeff697526421c20f07628c7372156/anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352", size = 93052 }, ] [[package]] @@ -191,14 +207,11 @@ wheels = [ [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/1d/f03bcb60c4a3212e15f99a56085d93093a497718adf828d050b9d675da81/asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0", size = 62284 } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/86/4736ac618d82a20d87d2f92ae19441ebc7ac9e7a581d7e58bbe79233b24a/asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", size = 27764 }, + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, ] [[package]] @@ -228,6 +241,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/69/f6db6e4cb2fe2f887dead40b76caa91af4844cb647dd2c7223bb010aa416/beartype-0.19.0-py3-none-any.whl", hash = "sha256:33b2694eda0daf052eb2aff623ed9a8a586703bbf0a90bbc475a83bbf427f699", size = 1039760 }, ] +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, +] + [[package]] name = "cachetools" version = "5.5.0" @@ -239,11 +261,11 @@ wheels = [ [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, + { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, ] [[package]] @@ -378,12 +400,13 @@ wheels = [ [[package]] name = "cohere" -version = "5.11.4" +version = "5.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastavro" }, { name = "httpx" }, { name = "httpx-sse" }, + { name = "numpy" }, { name = "parameterized" }, { name = "pydantic" }, { name = "pydantic-core" }, @@ -392,9 +415,9 @@ dependencies = [ { name = "types-requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/45/7aff8f9b24fe926a8f092491e56a90560ed2227d1dfdf1b9ada665f607ed/cohere-5.11.4.tar.gz", hash = "sha256:5586335a20de3bf6816f34151f9d9f2928880cdf776c57aae793b5cca58d1826", size = 129889 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/6c/1dbca36cddffb6239b48f94061079e4c9798929582b3246ab846c5635eca/cohere-5.13.3.tar.gz", hash = "sha256:70d87e0d5ce48aaee5ba70ead5efbade226cb2a4b11bfcfb676f6a2db3642819", size = 130391 } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/5c/909862a2eb2a0eaf3bc2024058207e342286e015d60ff03fe38f3efdde6d/cohere-5.11.4-py3-none-any.whl", hash = "sha256:59fb427e5426e0ee1c25b9deec83f0418a1c082240c57007f41384b34cd41552", size = 249700 }, + { url = "https://files.pythonhosted.org/packages/02/6f/d87183d347498e2141016e1a37ecedfcd810ad34a0a208129b7a4f70e219/cohere-5.13.3-py3-none-any.whl", hash = "sha256:076c88fdd3d670b6577eb8e813a9072bf18b59648d4092c6f0263af3c27bf81f", size = 249851 }, ] [[package]] @@ -420,41 +443,41 @@ wheels = [ [[package]] name = "coverage" -version = "7.6.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/68/26895f8b068e384b1ec9ab122565b913b735e6b4c618b3d265a280607edc/coverage-7.6.7.tar.gz", hash = "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24", size = 799938 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/c9/84898713e61208ddbe71b991d8f311d9ca175629ce5f1a46018acc643572/coverage-7.6.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:108bb458827765d538abcbf8288599fee07d2743357bdd9b9dad456c287e121e", size = 206875 }, - { url = "https://files.pythonhosted.org/packages/f0/69/7dfd65f0e284617f72d974f6dfedc7bc16f86172e5bc6ebc8b63430263f3/coverage-7.6.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c973b2fe4dc445cb865ab369df7521df9c27bf40715c837a113edaa2aa9faf45", size = 207307 }, - { url = "https://files.pythonhosted.org/packages/d1/ce/6e356b2bc751bdaadd77c714336b98ec45ccaf0cfe085b6b25d34f7cceb8/coverage-7.6.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c6b24007c4bcd0b19fac25763a7cac5035c735ae017e9a349b927cfc88f31c1", size = 235744 }, - { url = "https://files.pythonhosted.org/packages/35/49/a7ab3d5a507d32344994cab856784e8d603c0b698070f7667c3ae41e8e50/coverage-7.6.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acbb8af78f8f91b3b51f58f288c0994ba63c646bc1a8a22ad072e4e7e0a49f1c", size = 233645 }, - { url = "https://files.pythonhosted.org/packages/bd/41/de07328d2e79916fcc6cd53a5a1d18d163483519ab95f7f60fe15276811c/coverage-7.6.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad32a981bcdedb8d2ace03b05e4fd8dace8901eec64a532b00b15217d3677dd2", size = 234807 }, - { url = "https://files.pythonhosted.org/packages/e4/cc/2a669319b1295e0c52e8cfbbb163b32188b62f3b0bbe7014ef402b24b7cf/coverage-7.6.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:34d23e28ccb26236718a3a78ba72744212aa383141961dd6825f6595005c8b06", size = 233902 }, - { url = "https://files.pythonhosted.org/packages/68/71/a1bb90cb177358a2d364b3968a2069225f614d6824c3d959dee688ca0902/coverage-7.6.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e25bacb53a8c7325e34d45dddd2f2fbae0dbc230d0e2642e264a64e17322a777", size = 232363 }, - { url = "https://files.pythonhosted.org/packages/eb/dc/87551219d3437214523d1c7de0a717bead7a3369ed9bae05a7fd2854476f/coverage-7.6.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af05bbba896c4472a29408455fe31b3797b4d8648ed0a2ccac03e074a77e2314", size = 233493 }, - { url = "https://files.pythonhosted.org/packages/ca/a4/d74ae3a3fb9e55fe5d9b811ce68a6bd8df3ae0a92c336acbc00075bc24fa/coverage-7.6.7-cp310-cp310-win32.whl", hash = "sha256:796c9b107d11d2d69e1849b2dfe41730134b526a49d3acb98ca02f4985eeff7a", size = 209593 }, - { url = "https://files.pythonhosted.org/packages/77/cb/7984c4d0404e8fcc4ada226b240965ef056e7a20e61a18c9038bf88e7624/coverage-7.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:987a8e3da7da4eed10a20491cf790589a8e5e07656b6dc22d3814c4d88faf163", size = 210398 }, - { url = "https://files.pythonhosted.org/packages/c6/d7/1bf7bb0943237149ad01977190ac5c2e17add1f4fe7cabc06401682137f6/coverage-7.6.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469", size = 206979 }, - { url = "https://files.pythonhosted.org/packages/83/eb/863b2cd654353b94c6ad834008df813424bf3e8f110e5f655fe5dc4c423b/coverage-7.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99", size = 207431 }, - { url = "https://files.pythonhosted.org/packages/35/c9/d7a02a9654c41174fb99402c0fbd9583d0d2cb8714e7f948117fa7f919c4/coverage-7.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec", size = 239368 }, - { url = "https://files.pythonhosted.org/packages/11/64/6c43a0ec43e5ddc5e09b0b589e3fd31def05fc463920d084e5af35fe527d/coverage-7.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b", size = 236769 }, - { url = "https://files.pythonhosted.org/packages/1c/dc/e77d98ae433c556c29328712a07fed0e6d159a63b2ec81039ce0a13a24a3/coverage-7.6.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a", size = 238634 }, - { url = "https://files.pythonhosted.org/packages/cc/84/50df3a8426d686057496171b4ccdb64528dacc4f42e94dceb7de3c598a69/coverage-7.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b", size = 237562 }, - { url = "https://files.pythonhosted.org/packages/2e/0f/9560196247574c1ccdab64cb923d69119fd5abd5b3db28d601ab2b452861/coverage-7.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d", size = 236197 }, - { url = "https://files.pythonhosted.org/packages/df/14/38b7c081e86e845df1867143ddb6e05bf8395f60ab3923c023a56d97cca1/coverage-7.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4", size = 236970 }, - { url = "https://files.pythonhosted.org/packages/8b/f3/af34f814ca3814f798878ae368b638edb91298595470614f5265f3f416fa/coverage-7.6.7-cp311-cp311-win32.whl", hash = "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2", size = 209557 }, - { url = "https://files.pythonhosted.org/packages/5a/9e/5d1080d83d752873bd9dedea5524c0f5fe68a3d5e1e58c590865bd724591/coverage-7.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f", size = 210402 }, - { url = "https://files.pythonhosted.org/packages/84/30/30e9df650b9038962c62d900b093a17414d5b43b4d07d47b8698d9e7ce26/coverage-7.6.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f07ff574986bc3edb80e2c36391678a271d555f91fd1d332a1e0f4b5ea4b6ea9", size = 207172 }, - { url = "https://files.pythonhosted.org/packages/88/8b/e28f86412317b9514692fd6f9d8ac6faa12494c3f470c3c63f202e10c756/coverage-7.6.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ed5ee4109258973630c1f9d099c7e72c5c36605029f3a91fe9982c6076c82b", size = 207406 }, - { url = "https://files.pythonhosted.org/packages/ac/46/da1bd9a3a893f74f5ce85f35e2755fcb00a80ed21e18d300c54f64938b1c/coverage-7.6.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3e8796434a8106b3ac025fd15417315d7a58ee3e600ad4dbcfddc3f4b14342c", size = 240424 }, - { url = "https://files.pythonhosted.org/packages/f6/12/af8e932496de1997bf4a36785d025ddac6427cbaf6954f26c2edaf21a58a/coverage-7.6.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3b925300484a3294d1c70f6b2b810d6526f2929de954e5b6be2bf8caa1f12c1", size = 237456 }, - { url = "https://files.pythonhosted.org/packages/60/a2/23eb11eb60f825a84397cb94701d6f41d2e8e88ad7d0ba2b4339f38435fb/coverage-7.6.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c42ec2c522e3ddd683dec5cdce8e62817afb648caedad9da725001fa530d354", size = 239527 }, - { url = "https://files.pythonhosted.org/packages/47/9e/63b318bc469308a32b0fbd6c80e2ea05dd7a2b7e840a46b3974843083a8c/coverage-7.6.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0266b62cbea568bd5e93a4da364d05de422110cbed5056d69339bd5af5685433", size = 239011 }, - { url = "https://files.pythonhosted.org/packages/99/47/1e84b067df3f021dfbc9cba09ec9acd4cb64938648a234e5bdf3006fd08b/coverage-7.6.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e5f2a0f161d126ccc7038f1f3029184dbdf8f018230af17ef6fd6a707a5b881f", size = 237316 }, - { url = "https://files.pythonhosted.org/packages/12/9d/96baaafc948d4a0ef2248a611d41051eea0917ef881d744879dd20be7c4a/coverage-7.6.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c132b5a22821f9b143f87446805e13580b67c670a548b96da945a8f6b4f2efbb", size = 238980 }, - { url = "https://files.pythonhosted.org/packages/87/d9/97af1886ca3f612d0cea2999d33e90d2f5b8fdf9bedc2d3bc75883efec4c/coverage-7.6.7-cp312-cp312-win32.whl", hash = "sha256:7c07de0d2a110f02af30883cd7dddbe704887617d5c27cf373362667445a4c76", size = 209801 }, - { url = "https://files.pythonhosted.org/packages/f8/4d/1e31c2018b1b3738154639f94188b1f54098fbf0f80c7ec104928576d0bb/coverage-7.6.7-cp312-cp312-win_amd64.whl", hash = "sha256:fd49c01e5057a451c30c9b892948976f5d38f2cbd04dc556a82743ba8e27ed8c", size = 210587 }, - { url = "https://files.pythonhosted.org/packages/e1/ec/dc663f7d34651aca74a531d10800595d9ec28a78b8306705721900b17a23/coverage-7.6.7-pp39.pp310-none-any.whl", hash = "sha256:0ddcb70b3a3a57581b450571b31cb774f23eb9519c2aaa6176d3a84c9fc57671", size = 199113 }, +version = "7.6.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/d2/c25011f4d036cf7e8acbbee07a8e09e9018390aee25ba085596c4b83d510/coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", size = 801710 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/f3/f830fb53bf7e4f1d5542756f61d9b740352a188f43854aab9409c8cdeb18/coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", size = 207024 }, + { url = "https://files.pythonhosted.org/packages/4e/e3/ea5632a3a6efd00ab0a791adc0f3e48512097a757ee7dcbee5505f57bafa/coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", size = 207463 }, + { url = "https://files.pythonhosted.org/packages/e4/ae/18ff8b5580e27e62ebcc888082aa47694c2772782ea7011ddf58e377e98f/coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", size = 235902 }, + { url = "https://files.pythonhosted.org/packages/6a/52/57030a8d15ab935624d298360f0a6704885578e39f7b4f68569e59f5902d/coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", size = 233806 }, + { url = "https://files.pythonhosted.org/packages/d0/c5/4466602195ecaced298d55af1e29abceb812addabefd5bd9116a204f7bab/coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", size = 234966 }, + { url = "https://files.pythonhosted.org/packages/b0/1c/55552c3009b7bf96732e36548596ade771c87f89cf1f5a8e3975b33539b5/coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", size = 234029 }, + { url = "https://files.pythonhosted.org/packages/bb/7d/da3dca6878701182ea42c51df47a47c80eaef2a76f5aa3e891dc2a8cce3f/coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", size = 232494 }, + { url = "https://files.pythonhosted.org/packages/28/cc/39de85ac1d5652bc34ff2bee39ae251b1fdcaae53fab4b44cab75a432bc0/coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", size = 233611 }, + { url = "https://files.pythonhosted.org/packages/d1/2b/7eb011a9378911088708f121825a71134d0c15fac96972a0ae7a8f5a4049/coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", size = 209712 }, + { url = "https://files.pythonhosted.org/packages/5b/35/c3f40a2269b416db34ce1dedf682a7132c26f857e33596830fa4deebabf9/coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", size = 210553 }, + { url = "https://files.pythonhosted.org/packages/b1/91/b3dc2f7f38b5cca1236ab6bbb03e84046dd887707b4ec1db2baa47493b3b/coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", size = 207133 }, + { url = "https://files.pythonhosted.org/packages/0d/2b/53fd6cb34d443429a92b3ec737f4953627e38b3bee2a67a3c03425ba8573/coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", size = 207577 }, + { url = "https://files.pythonhosted.org/packages/74/f2/68edb1e6826f980a124f21ea5be0d324180bf11de6fd1defcf9604f76df0/coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", size = 239524 }, + { url = "https://files.pythonhosted.org/packages/d3/83/8fec0ee68c2c4a5ab5f0f8527277f84ed6f2bd1310ae8a19d0c5532253ab/coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", size = 236925 }, + { url = "https://files.pythonhosted.org/packages/8b/20/8f50e7c7ad271144afbc2c1c6ec5541a8c81773f59352f8db544cad1a0ec/coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", size = 238792 }, + { url = "https://files.pythonhosted.org/packages/6f/62/4ac2e5ad9e7a5c9ec351f38947528e11541f1f00e8a0cdce56f1ba7ae301/coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", size = 237682 }, + { url = "https://files.pythonhosted.org/packages/58/2f/9d2203f012f3b0533c73336c74134b608742be1ce475a5c72012573cfbb4/coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", size = 236310 }, + { url = "https://files.pythonhosted.org/packages/33/6d/31f6ab0b4f0f781636075f757eb02141ea1b34466d9d1526dbc586ed7078/coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", size = 237096 }, + { url = "https://files.pythonhosted.org/packages/7d/fb/e14c38adebbda9ed8b5f7f8e03340ac05d68d27b24397f8d47478927a333/coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", size = 209682 }, + { url = "https://files.pythonhosted.org/packages/a4/11/a782af39b019066af83fdc0e8825faaccbe9d7b19a803ddb753114b429cc/coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", size = 210542 }, + { url = "https://files.pythonhosted.org/packages/60/52/b16af8989a2daf0f80a88522bd8e8eed90b5fcbdecf02a6888f3e80f6ba7/coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", size = 207325 }, + { url = "https://files.pythonhosted.org/packages/0f/79/6b7826fca8846c1216a113227b9f114ac3e6eacf168b4adcad0cb974aaca/coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", size = 207563 }, + { url = "https://files.pythonhosted.org/packages/a7/07/0bc73da0ccaf45d0d64ef86d33b7d7fdeef84b4c44bf6b85fb12c215c5a6/coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", size = 240580 }, + { url = "https://files.pythonhosted.org/packages/71/8a/9761f409910961647d892454687cedbaccb99aae828f49486734a82ede6e/coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3", size = 237613 }, + { url = "https://files.pythonhosted.org/packages/8b/10/ee7d696a17ac94f32f2dbda1e17e730bf798ae9931aec1fc01c1944cd4de/coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae", size = 239684 }, + { url = "https://files.pythonhosted.org/packages/16/60/aa1066040d3c52fff051243c2d6ccda264da72dc6d199d047624d395b2b2/coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4", size = 239112 }, + { url = "https://files.pythonhosted.org/packages/4e/e5/69f35344c6f932ba9028bf168d14a79fedb0dd4849b796d43c81ce75a3c9/coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6", size = 237428 }, + { url = "https://files.pythonhosted.org/packages/32/20/adc895523c4a28f63441b8ac645abd74f9bdd499d2d175bef5b41fc7f92d/coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f", size = 239098 }, + { url = "https://files.pythonhosted.org/packages/a9/a6/e0e74230c9bb3549ec8ffc137cfd16ea5d56e993d6bffed2218bff6187e3/coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692", size = 209940 }, + { url = "https://files.pythonhosted.org/packages/3e/18/cb5b88349d4aa2f41ec78d65f92ea32572b30b3f55bc2b70e87578b8f434/coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97", size = 210726 }, + { url = "https://files.pythonhosted.org/packages/15/0e/4ac9035ee2ee08d2b703fdad2d84283ec0bad3b46eb4ad6affb150174cb6/coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", size = 199270 }, ] [package.optional-dependencies] @@ -464,40 +487,44 @@ toml = [ [[package]] name = "cryptography" -version = "43.0.3" +version = "44.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/05/07b55d1fa21ac18c3a8c79f764e2514e6f6a9698f1be44994f5adf0d29db/cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", size = 686989 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/f3/01fdf26701a26f4b4dbc337a26883ad5bccaa6f1bbbdd29cd89e22f18a1c/cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", size = 6225303 }, - { url = "https://files.pythonhosted.org/packages/a3/01/4896f3d1b392025d4fcbecf40fdea92d3df8662123f6835d0af828d148fd/cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", size = 3760905 }, - { url = "https://files.pythonhosted.org/packages/0a/be/f9a1f673f0ed4b7f6c643164e513dbad28dd4f2dcdf5715004f172ef24b6/cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", size = 3977271 }, - { url = "https://files.pythonhosted.org/packages/4e/49/80c3a7b5514d1b416d7350830e8c422a4d667b6d9b16a9392ebfd4a5388a/cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", size = 3746606 }, - { url = "https://files.pythonhosted.org/packages/0e/16/a28ddf78ac6e7e3f25ebcef69ab15c2c6be5ff9743dd0709a69a4f968472/cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", size = 3986484 }, - { url = "https://files.pythonhosted.org/packages/01/f5/69ae8da70c19864a32b0315049866c4d411cce423ec169993d0434218762/cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", size = 3852131 }, - { url = "https://files.pythonhosted.org/packages/fd/db/e74911d95c040f9afd3612b1f732e52b3e517cb80de8bf183be0b7d413c6/cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", size = 4075647 }, - { url = "https://files.pythonhosted.org/packages/56/48/7b6b190f1462818b324e674fa20d1d5ef3e24f2328675b9b16189cbf0b3c/cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", size = 2623873 }, - { url = "https://files.pythonhosted.org/packages/eb/b1/0ebff61a004f7f89e7b65ca95f2f2375679d43d0290672f7713ee3162aff/cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", size = 3068039 }, - { url = "https://files.pythonhosted.org/packages/30/d5/c8b32c047e2e81dd172138f772e81d852c51f0f2ad2ae8a24f1122e9e9a7/cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", size = 6222984 }, - { url = "https://files.pythonhosted.org/packages/2f/78/55356eb9075d0be6e81b59f45c7b48df87f76a20e73893872170471f3ee8/cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", size = 3762968 }, - { url = "https://files.pythonhosted.org/packages/2a/2c/488776a3dc843f95f86d2f957ca0fc3407d0242b50bede7fad1e339be03f/cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", size = 3977754 }, - { url = "https://files.pythonhosted.org/packages/7c/04/2345ca92f7a22f601a9c62961741ef7dd0127c39f7310dffa0041c80f16f/cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", size = 3749458 }, - { url = "https://files.pythonhosted.org/packages/ac/25/e715fa0bc24ac2114ed69da33adf451a38abb6f3f24ec207908112e9ba53/cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", size = 3988220 }, - { url = "https://files.pythonhosted.org/packages/21/ce/b9c9ff56c7164d8e2edfb6c9305045fbc0df4508ccfdb13ee66eb8c95b0e/cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", size = 3853898 }, - { url = "https://files.pythonhosted.org/packages/2a/33/b3682992ab2e9476b9c81fff22f02c8b0a1e6e1d49ee1750a67d85fd7ed2/cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", size = 4076592 }, - { url = "https://files.pythonhosted.org/packages/81/1e/ffcc41b3cebd64ca90b28fd58141c5f68c83d48563c88333ab660e002cd3/cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", size = 2623145 }, - { url = "https://files.pythonhosted.org/packages/87/5c/3dab83cc4aba1f4b0e733e3f0c3e7d4386440d660ba5b1e3ff995feb734d/cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", size = 3068026 }, - { url = "https://files.pythonhosted.org/packages/6f/db/d8b8a039483f25fc3b70c90bc8f3e1d4497a99358d610c5067bf3bd4f0af/cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", size = 3144545 }, - { url = "https://files.pythonhosted.org/packages/93/90/116edd5f8ec23b2dc879f7a42443e073cdad22950d3c8ee834e3b8124543/cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", size = 3679828 }, - { url = "https://files.pythonhosted.org/packages/d8/32/1e1d78b316aa22c0ba6493cc271c1c309969e5aa5c22c830a1d7ce3471e6/cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", size = 3908132 }, - { url = "https://files.pythonhosted.org/packages/91/bb/cd2c13be3332e7af3cdf16154147952d39075b9f61ea5e6b5241bf4bf436/cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", size = 2988811 }, +sdist = { url = "https://files.pythonhosted.org/packages/91/4c/45dfa6829acffa344e3967d6006ee4ae8be57af746ae2eba1c431949b32c/cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", size = 710657 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/09/8cc67f9b84730ad330b3b72cf867150744bf07ff113cda21a15a1c6d2c7c/cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", size = 6541833 }, + { url = "https://files.pythonhosted.org/packages/7e/5b/3759e30a103144e29632e7cb72aec28cedc79e514b2ea8896bb17163c19b/cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", size = 3922710 }, + { url = "https://files.pythonhosted.org/packages/5f/58/3b14bf39f1a0cfd679e753e8647ada56cddbf5acebffe7db90e184c76168/cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", size = 4137546 }, + { url = "https://files.pythonhosted.org/packages/98/65/13d9e76ca19b0ba5603d71ac8424b5694415b348e719db277b5edc985ff5/cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", size = 3915420 }, + { url = "https://files.pythonhosted.org/packages/b1/07/40fe09ce96b91fc9276a9ad272832ead0fddedcba87f1190372af8e3039c/cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", size = 4154498 }, + { url = "https://files.pythonhosted.org/packages/75/ea/af65619c800ec0a7e4034207aec543acdf248d9bffba0533342d1bd435e1/cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", size = 3932569 }, + { url = "https://files.pythonhosted.org/packages/c7/af/d1deb0c04d59612e3d5e54203159e284d3e7a6921e565bb0eeb6269bdd8a/cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", size = 4016721 }, + { url = "https://files.pythonhosted.org/packages/bd/69/7ca326c55698d0688db867795134bdfac87136b80ef373aaa42b225d6dd5/cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", size = 4240915 }, + { url = "https://files.pythonhosted.org/packages/ef/d4/cae11bf68c0f981e0413906c6dd03ae7fa864347ed5fac40021df1ef467c/cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", size = 2757925 }, + { url = "https://files.pythonhosted.org/packages/64/b1/50d7739254d2002acae64eed4fc43b24ac0cc44bf0a0d388d1ca06ec5bb1/cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", size = 3202055 }, + { url = "https://files.pythonhosted.org/packages/11/18/61e52a3d28fc1514a43b0ac291177acd1b4de00e9301aaf7ef867076ff8a/cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", size = 6542801 }, + { url = "https://files.pythonhosted.org/packages/1a/07/5f165b6c65696ef75601b781a280fc3b33f1e0cd6aa5a92d9fb96c410e97/cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", size = 3922613 }, + { url = "https://files.pythonhosted.org/packages/28/34/6b3ac1d80fc174812486561cf25194338151780f27e438526f9c64e16869/cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", size = 4137925 }, + { url = "https://files.pythonhosted.org/packages/d0/c7/c656eb08fd22255d21bc3129625ed9cd5ee305f33752ef2278711b3fa98b/cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", size = 3915417 }, + { url = "https://files.pythonhosted.org/packages/ef/82/72403624f197af0db6bac4e58153bc9ac0e6020e57234115db9596eee85d/cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", size = 4155160 }, + { url = "https://files.pythonhosted.org/packages/a2/cd/2f3c440913d4329ade49b146d74f2e9766422e1732613f57097fea61f344/cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", size = 3932331 }, + { url = "https://files.pythonhosted.org/packages/7f/df/8be88797f0a1cca6e255189a57bb49237402b1880d6e8721690c5603ac23/cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", size = 4017372 }, + { url = "https://files.pythonhosted.org/packages/af/36/5ccc376f025a834e72b8e52e18746b927f34e4520487098e283a719c205e/cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", size = 4239657 }, + { url = "https://files.pythonhosted.org/packages/46/b0/f4f7d0d0bcfbc8dd6296c1449be326d04217c57afb8b2594f017eed95533/cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", size = 2758672 }, + { url = "https://files.pythonhosted.org/packages/97/9b/443270b9210f13f6ef240eff73fd32e02d381e7103969dc66ce8e89ee901/cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", size = 3202071 }, + { url = "https://files.pythonhosted.org/packages/77/d4/fea74422326388bbac0c37b7489a0fcb1681a698c3b875959430ba550daa/cryptography-44.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", size = 3338857 }, + { url = "https://files.pythonhosted.org/packages/1a/aa/ba8a7467c206cb7b62f09b4168da541b5109838627f582843bbbe0235e8e/cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4", size = 3850615 }, + { url = "https://files.pythonhosted.org/packages/89/fa/b160e10a64cc395d090105be14f399b94e617c879efd401188ce0fea39ee/cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", size = 4081622 }, + { url = "https://files.pythonhosted.org/packages/47/8f/20ff0656bb0cf7af26ec1d01f780c5cfbaa7666736063378c5f48558b515/cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", size = 3867546 }, + { url = "https://files.pythonhosted.org/packages/38/d9/28edf32ee2fcdca587146bcde90102a7319b2f2c690edfa627e46d586050/cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", size = 4090937 }, + { url = "https://files.pythonhosted.org/packages/cc/9d/37e5da7519de7b0b070a3fedd4230fe76d50d2a21403e0f2153d70ac4163/cryptography-44.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", size = 3128774 }, ] [[package]] name = "datasets" -version = "3.1.0" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -515,30 +542,30 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/b2/25ef6d54dcb2d1412e5efec59449d1af92e7a6d969e27adfe9965e780c1f/datasets-3.1.0.tar.gz", hash = "sha256:c92cac049e0f9f85b0dd63739c68e564c657b1624bc2b66b1e13489062832e27", size = 558115 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/48/744286c044e2b942d4fa67f92816126522ad1f0675def0ea3264e6242005/datasets-3.2.0.tar.gz", hash = "sha256:9a6e1a356052866b5dbdd9c9eedb000bf3fc43d986e3584d9b028f4976937229", size = 558366 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/a5/33cf000137545a08b0a3a6ea76c8ccbd87917f78bb5d737f9f56f3b11ef6/datasets-3.1.0-py3-none-any.whl", hash = "sha256:dc8808a6d17838fe05e13b39aa7ac3ea0fd0806ed7004eaf4d4eb2c2a356bc61", size = 480554 }, + { url = "https://files.pythonhosted.org/packages/d7/84/0df6c5981f5fc722381662ff8cfbdf8aad64bec875f75d80b55bfef394ce/datasets-3.2.0-py3-none-any.whl", hash = "sha256:f3d2ba2698b7284a4518019658596a6a8bc79f31e51516524249d6c59cf0fe2a", size = 480647 }, ] [[package]] name = "debugpy" -version = "1.8.9" +version = "1.8.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/92/15b454c516c4c53cc8c03967e4be12b65a1ea36db3bb4513a7453f75c8d8/debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e", size = 4921695 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/e7/666f4c9b0e24796af50aadc28d36d21c2e01e831a934535f956e09b3650c/debugpy-1.8.11.tar.gz", hash = "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57", size = 1640124 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/2e/92fda96b1b773e454daae3e2962726dd9f7aedb1f26d7f2ca353d91a930b/debugpy-1.8.9-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e", size = 2080529 }, - { url = "https://files.pythonhosted.org/packages/87/c0/d13cdbae394c7ae65ef93d7ccde2ff364445248e367bda93fc0650c08849/debugpy-1.8.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f", size = 3565151 }, - { url = "https://files.pythonhosted.org/packages/23/40/237c0a7a68cb982dcced4a0199b7c464630f75b9280d6bebde32490135d1/debugpy-1.8.9-cp310-cp310-win32.whl", hash = "sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037", size = 5117068 }, - { url = "https://files.pythonhosted.org/packages/00/89/e0be9f01ee461e3369dde418492244acb1b67adaf04cb5ea98f1380ab101/debugpy-1.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e", size = 5149364 }, - { url = "https://files.pythonhosted.org/packages/f7/bf/c41b688ad490d644b3bcca505a87ea58ec0442234def9a641ba62dce9c11/debugpy-1.8.9-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040", size = 2179080 }, - { url = "https://files.pythonhosted.org/packages/f4/dd/e9de11423db7bde62469fbd932243c64f66d6d87924976f49ec336415522/debugpy-1.8.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70", size = 3137893 }, - { url = "https://files.pythonhosted.org/packages/2c/bf/e1f2c81220591728f35585b4abd67e71e9b39b3cb983f428b23d4ca6c22e/debugpy-1.8.9-cp311-cp311-win32.whl", hash = "sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66", size = 5042644 }, - { url = "https://files.pythonhosted.org/packages/96/20/a407252954fd2812771e4ea3ab523f73889fd5027e305dec5ee4f0af149a/debugpy-1.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d", size = 5066943 }, - { url = "https://files.pythonhosted.org/packages/da/ab/1420baf8404d2b499349a44de5037133e06d489009032ce016fedb66eea1/debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2", size = 2504180 }, - { url = "https://files.pythonhosted.org/packages/58/ec/e0f88c6764314bda7887274e0b980812709b3d6363dcae124a49a9ceaa3c/debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe", size = 4224563 }, - { url = "https://files.pythonhosted.org/packages/dd/49/d9ea004ee2e4531d2b528841689ee2ba01c6a4b58840efd15e57dd866a86/debugpy-1.8.9-cp312-cp312-win32.whl", hash = "sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11", size = 5163641 }, - { url = "https://files.pythonhosted.org/packages/b1/63/c8b0718024c1187a446316037680e1564bf063c6665c815f17b42c244aba/debugpy-1.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53", size = 5203862 }, - { url = "https://files.pythonhosted.org/packages/2d/23/3f5804202da11c950dc0caae4a62d0c9aadabdb2daeb5f7aa09838647b5d/debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899", size = 5166094 }, + { url = "https://files.pythonhosted.org/packages/26/e6/4cf7422eaa591b4c7d6a9fde224095dac25283fdd99d90164f28714242b0/debugpy-1.8.11-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd", size = 2075100 }, + { url = "https://files.pythonhosted.org/packages/83/3a/e163de1df5995d95760a4d748b02fbefb1c1bf19e915b664017c40435dbf/debugpy-1.8.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f", size = 3559724 }, + { url = "https://files.pythonhosted.org/packages/27/6c/327e19fd1bf428a1efe1a6f97b306689c54c2cebcf871b66674ead718756/debugpy-1.8.11-cp310-cp310-win32.whl", hash = "sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737", size = 5178068 }, + { url = "https://files.pythonhosted.org/packages/49/80/359ff8aa388f0bd4a48f0fa9ce3606396d576657ac149c6fba3cc7de8adb/debugpy-1.8.11-cp310-cp310-win_amd64.whl", hash = "sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1", size = 5210109 }, + { url = "https://files.pythonhosted.org/packages/7c/58/8e3f7ec86c1b7985a232667b5df8f3b1b1c8401028d8f4d75e025c9556cd/debugpy-1.8.11-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296", size = 2173656 }, + { url = "https://files.pythonhosted.org/packages/d2/03/95738a68ade2358e5a4d63a2fd8e7ed9ad911001cfabbbb33a7f81343945/debugpy-1.8.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1", size = 3132464 }, + { url = "https://files.pythonhosted.org/packages/ca/f4/18204891ab67300950615a6ad09b9de236203a9138f52b3b596fa17628ca/debugpy-1.8.11-cp311-cp311-win32.whl", hash = "sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9", size = 5103637 }, + { url = "https://files.pythonhosted.org/packages/3b/90/3775e301cfa573b51eb8a108285681f43f5441dc4c3916feed9f386ef861/debugpy-1.8.11-cp311-cp311-win_amd64.whl", hash = "sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e", size = 5127862 }, + { url = "https://files.pythonhosted.org/packages/c6/ae/2cf26f3111e9d94384d9c01e9d6170188b0aeda15b60a4ac6457f7c8a26f/debugpy-1.8.11-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308", size = 2498756 }, + { url = "https://files.pythonhosted.org/packages/b0/16/ec551789d547541a46831a19aa15c147741133da188e7e6acf77510545a7/debugpy-1.8.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768", size = 4219136 }, + { url = "https://files.pythonhosted.org/packages/72/6f/b2b3ce673c55f882d27a6eb04a5f0c68bcad6b742ac08a86d8392ae58030/debugpy-1.8.11-cp312-cp312-win32.whl", hash = "sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b", size = 5224440 }, + { url = "https://files.pythonhosted.org/packages/77/09/b1f05be802c1caef5b3efc042fc6a7cadd13d8118b072afd04a9b9e91e06/debugpy-1.8.11-cp312-cp312-win_amd64.whl", hash = "sha256:44b1b8e6253bceada11f714acf4309ffb98bfa9ac55e4fce14f9e5d4484287a1", size = 5264578 }, + { url = "https://files.pythonhosted.org/packages/77/0a/d29a5aacf47b4383ed569b8478c02d59ee3a01ad91224d2cff8562410e43/debugpy-1.8.11-py2.py3-none-any.whl", hash = "sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920", size = 5226874 }, ] [[package]] @@ -637,16 +664,16 @@ wheels = [ [[package]] name = "fastapi" -version = "0.115.5" +version = "0.115.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/29/f71316b9273b6552a263748e49cd7b83898dc9499a663d30c7b9cb853cb8/fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289", size = 301047 } +sdist = { url = "https://files.pythonhosted.org/packages/93/72/d83b98cd106541e8f5e5bfab8ef2974ab45a62e8a6c5b5e6940f26d2ed4b/fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654", size = 301336 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/c4/148d5046a96c428464557264877ae5a9338a83bbe0df045088749ec89820/fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796", size = 94866 }, + { url = "https://files.pythonhosted.org/packages/52/b3/7e4df40e585df024fac2f80d1a2d579c854ac37109675db2b0cc22c0bb9e/fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305", size = 94843 }, ] [package.optional-dependencies] @@ -661,15 +688,16 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.5" +version = "0.0.6" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/f8/1ad5ce32d029aeb9117e9a5a9b3e314a8477525d60c12a9b7730a3c186ec/fastapi_cli-0.0.5.tar.gz", hash = "sha256:d30e1239c6f46fcb95e606f02cdda59a1e2fa778a54b64686b3ff27f6211ff9f", size = 15571 } +sdist = { url = "https://files.pythonhosted.org/packages/36/9d/9659cee212eeaecd8f54ab5f73d6d6d49a1dc4a46d2519fb9c1e66c8913e/fastapi_cli-0.0.6.tar.gz", hash = "sha256:2835a8f0c44b68e464d5cafe5ec205265f02dc1ad1d640db33a994ba3338003b", size = 16516 } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/ea/4b5011012ac925fe2f83b19d0e09cee9d324141ec7bf5e78bb2817f96513/fastapi_cli-0.0.5-py3-none-any.whl", hash = "sha256:e94d847524648c748a5350673546bbf9bcaeb086b33c24f2e82e021436866a46", size = 9489 }, + { url = "https://files.pythonhosted.org/packages/8f/ab/0c8b6ec19594fe4c2e7fe12e8074648f59903047787ac05512c2935d5f7a/fastapi_cli-0.0.6-py3-none-any.whl", hash = "sha256:43288efee46338fae8902f9bf4559aed3aed639f9516f5d394a7ff19edcc8faf", size = 10687 }, ] [package.optional-dependencies] @@ -705,11 +733,11 @@ wheels = [ [[package]] name = "fastjsonschema" -version = "2.20.0" +version = "2.21.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/3f/3ad5e7be13b4b8b55f4477141885ab2364f65d5f6ad5f7a9daffd634d066/fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23", size = 373056 } +sdist = { url = "https://files.pythonhosted.org/packages/8b/50/4b769ce1ac4071a1ef6d86b1a3fb56cdc3a37615e8c5519e1af96cdac366/fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4", size = 373939 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/ca/086311cdfc017ec964b2436fe0c98c1f4efcb7e4c328956a22456e497655/fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a", size = 23543 }, + { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924 }, ] [[package]] @@ -798,6 +826,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/11/8f/9f1894efa1bb15e98613244b24dfbacfe2309e0ac3cfc27d4c608c2270d2/gin_config-0.5.0-py3-none-any.whl", hash = "sha256:bddb7ca221ea2b46cdb59321e79fecf02d6e3b728906047fcd4076c297609fd6", size = 61327 }, ] +[[package]] +name = "gitdb" +version = "4.0.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/0d/bbb5b5ee188dec84647a4664f3e11b06ade2bde568dbd489d9d64adef8ed/gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b", size = 394469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/5b/8f0c4a5bb9fd491c277c21eff7ccae71b47d43c4446c9d0c6cff2fe8c2c4/gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4", size = 62721 }, +] + +[[package]] +name = "gitpython" +version = "3.1.43" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/a1/106fd9fa2dd989b6fb36e5893961f82992cf676381707253e0bf93eb1662/GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c", size = 214149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/bd/cc3a402a6439c15c3d4294333e13042b915bbeab54edc457c723931fed3f/GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff", size = 207337 }, +] + [[package]] name = "google-ai-generativelanguage" version = "0.6.10" @@ -815,7 +867,7 @@ wheels = [ [[package]] name = "google-api-core" -version = "2.23.0" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, @@ -824,9 +876,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/6b/b98553c2061c4e2186f5bbfb1aa1a6ef13fc0775c096d18595d3c99ba023/google_api_core-2.23.0.tar.gz", hash = "sha256:2ceb087315e6af43f256704b871d99326b1f12a9d6ce99beaedec99ba26a0ace", size = 160094 } +sdist = { url = "https://files.pythonhosted.org/packages/81/56/d70d66ed1b5ab5f6c27bf80ec889585ad8f865ff32acbafd3b2ef0bfb5d0/google_api_core-2.24.0.tar.gz", hash = "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf", size = 162647 } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/a4/c26886d57d90032c5f74c2e80aefdc38ec58551fc46bd4ce79fb2c9389fa/google_api_core-2.23.0-py3-none-any.whl", hash = "sha256:c20100d4c4c41070cf365f1d8ddf5365915291b5eb11b83829fbd1c999b5122f", size = 156554 }, + { url = "https://files.pythonhosted.org/packages/a1/76/65b8b94e74bf1b6d1cc38d916089670c4da5029d25762441d8c5c19e51dd/google_api_core-2.24.0-py3-none-any.whl", hash = "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9", size = 158576 }, ] [package.optional-dependencies] @@ -837,7 +889,7 @@ grpc = [ [[package]] name = "google-api-python-client" -version = "2.154.0" +version = "2.155.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -846,23 +898,23 @@ dependencies = [ { name = "httplib2" }, { name = "uritemplate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/fa/861931cf33f3d91d0d17001af3249cfc2af2b5a1b8472604420c025f3339/google_api_python_client-2.154.0.tar.gz", hash = "sha256:1b420062e03bfcaa1c79e2e00a612d29a6a934151ceb3d272fe150a656dc8f17", size = 12070143 } +sdist = { url = "https://files.pythonhosted.org/packages/c8/7c/95defad65947571c152de64d67689e34185bfcafeb1e9c09d3a58f463027/google_api_python_client-2.155.0.tar.gz", hash = "sha256:25529f89f0d13abcf3c05c089c423fb2858ac16e0b3727543393468d0d7af67c", size = 12195015 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/3f/adf5f6b963307765209fc27943516c7c605d250234b01899d524b8f01fe3/google_api_python_client-2.154.0-py2.py3-none-any.whl", hash = "sha256:a521bbbb2ec0ba9d6f307cdd64ed6e21eeac372d1bd7493a4ab5022941f784ad", size = 12573774 }, + { url = "https://files.pythonhosted.org/packages/4d/6f/d8d446026a6b5a5e1f25604d6b7ddff06dcffcfaea2e95d61a3d5086cbe4/google_api_python_client-2.155.0-py2.py3-none-any.whl", hash = "sha256:83fe9b5aa4160899079d7c93a37be306546a17e6686e2549bcc9584f1a229747", size = 12703592 }, ] [[package]] name = "google-auth" -version = "2.36.0" +version = "2.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/71/4c5387d8a3e46e3526a8190ae396659484377a73b33030614dd3b28e7ded/google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1", size = 268336 } +sdist = { url = "https://files.pythonhosted.org/packages/46/af/b25763b9d35dfc2c6f9c3ec34d8d3f1ba760af3a7b7e8d5c5f0579522c45/google_auth-2.37.0.tar.gz", hash = "sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00", size = 268878 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/9a/3d5087d27865c2f0431b942b5c4500b7d1b744dd3262fdc973a4c39d099e/google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb", size = 209519 }, + { url = "https://files.pythonhosted.org/packages/8d/8d/4d5d5f9f500499f7bd4c93903b43e8d6976f3fc6f064637ded1a85d09b07/google_auth-2.37.0-py2.py3-none-any.whl", hash = "sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0", size = 209829 }, ] [[package]] @@ -945,7 +997,7 @@ wheels = [ [[package]] name = "groq" -version = "0.12.0" +version = "0.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -955,60 +1007,70 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/3e/f3861c5386adf1145465b01ca548cb35d683d07dbc9a13a06d7d1352da6d/groq-0.12.0.tar.gz", hash = "sha256:569229e2dadfc428b0df3d2987407691a4e3bc035b5849a65ef4909514a4605e", size = 107684 } +sdist = { url = "https://files.pythonhosted.org/packages/27/55/31d57ca9fc0c8dd29eed0f2f30112f7477d3b68e9a826a22f7edb5bc5c36/groq-0.13.0.tar.gz", hash = "sha256:aaa213821c94d8974e57bab5fe59cb45c8871875d0cd53ec179afed928bad18e", size = 108654 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/13/8489c3df2047abed5acb4d3b188eedda7dc42d6c1fa4d853c22192de1115/groq-0.12.0-py3-none-any.whl", hash = "sha256:e8aa1529f82a01b2d15394b7ea242af9ee9387f65bdd1b91ce9a10f5a911dac1", size = 108852 }, + { url = "https://files.pythonhosted.org/packages/fb/d2/2ee77e5c12b39d45df6e053cabcfec62b367e92bc613a9e29d66e74997a9/groq-0.13.0-py3-none-any.whl", hash = "sha256:1c2b16fd1c4665c2d09509ee42ead9cc7d32534b37b2051fee0ea8b6216b899d", size = 108777 }, ] [[package]] name = "grpcio" -version = "1.68.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/da/132615afbfc722df4bba963844843a205aa298fd5f9a03fa2995e8dddf11/grpcio-1.68.0.tar.gz", hash = "sha256:7e7483d39b4a4fddb9906671e9ea21aaad4f031cdfc349fec76bdfa1e404543a", size = 12682655 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/31/31de69f683298451ec7663cb1e0c36ef07835e9a2b486dfd3665a189f7d1/grpcio-1.68.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:619b5d0f29f4f5351440e9343224c3e19912c21aeda44e0c49d0d147a8d01544", size = 5170661 }, - { url = "https://files.pythonhosted.org/packages/0a/7b/caed06de43176982308404aff36141c0af03d279990baad45195254069cf/grpcio-1.68.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:a59f5822f9459bed098ffbceb2713abbf7c6fd13f2b9243461da5c338d0cd6c3", size = 11077092 }, - { url = "https://files.pythonhosted.org/packages/57/b1/330966311df3097ca153c38e5ecc7fd872c82485ebda2b999c256cfc20e3/grpcio-1.68.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c03d89df516128febc5a7e760d675b478ba25802447624edf7aa13b1e7b11e2a", size = 5689632 }, - { url = "https://files.pythonhosted.org/packages/11/ee/4d2b67b244ecb765cf02ede2e8b8f7d5dbb9d6cd8290d6edc495dbade950/grpcio-1.68.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44bcbebb24363d587472089b89e2ea0ab2e2b4df0e4856ba4c0b087c82412121", size = 6316787 }, - { url = "https://files.pythonhosted.org/packages/d0/64/3a90fac70af2279477483d189007e244fec90ef0ba372043f21f8a6f5b80/grpcio-1.68.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79f81b7fbfb136247b70465bd836fa1733043fdee539cd6031cb499e9608a110", size = 5941966 }, - { url = "https://files.pythonhosted.org/packages/cc/a4/a30b344da85d680d834fed903e78c44a33028eadb9efae6850a7d398fd9b/grpcio-1.68.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:88fb2925789cfe6daa20900260ef0a1d0a61283dfb2d2fffe6194396a354c618", size = 6646172 }, - { url = "https://files.pythonhosted.org/packages/c7/5a/b97371b4dbecbae31cafee6655f1982193a6e6412da2808c87c9b1734589/grpcio-1.68.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:99f06232b5c9138593ae6f2e355054318717d32a9c09cdc5a2885540835067a1", size = 6212496 }, - { url = "https://files.pythonhosted.org/packages/47/df/dc797a6cb827fc6cf1ed4da812b3721b7705869d84d50d112543bb4ecf1b/grpcio-1.68.0-cp310-cp310-win32.whl", hash = "sha256:a6213d2f7a22c3c30a479fb5e249b6b7e648e17f364598ff64d08a5136fe488b", size = 3649704 }, - { url = "https://files.pythonhosted.org/packages/24/9a/91796bf4d7c6adb867add0ea37f83ea29d8a9743809f3478e5d284926775/grpcio-1.68.0-cp310-cp310-win_amd64.whl", hash = "sha256:15327ab81131ef9b94cb9f45b5bd98803a179c7c61205c8c0ac9aff9d6c4e82a", size = 4399061 }, - { url = "https://files.pythonhosted.org/packages/cf/5f/019594ff8130ce84f9317cfc1e3d2c2beef2b74fd8822c5f1dfe237cb0d5/grpcio-1.68.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:3b2b559beb2d433129441783e5f42e3be40a9e1a89ec906efabf26591c5cd415", size = 5180685 }, - { url = "https://files.pythonhosted.org/packages/7b/59/34dae935bbb42f3e8929c90e9dfff49090cef412cf767cf4f14cd01ded18/grpcio-1.68.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e46541de8425a4d6829ac6c5d9b16c03c292105fe9ebf78cb1c31e8d242f9155", size = 11150577 }, - { url = "https://files.pythonhosted.org/packages/a6/5e/3df718124aadfc5d565c70ebe6a32c9ee747a9ccf211041596dd471fd763/grpcio-1.68.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c1245651f3c9ea92a2db4f95d37b7597db6b246d5892bca6ee8c0e90d76fb73c", size = 5685490 }, - { url = "https://files.pythonhosted.org/packages/4c/57/4e39ac1030875e0497debc9d5a4b3a1478ee1bd957ba4b87c27fcd7a3545/grpcio-1.68.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1931c7aa85be0fa6cea6af388e576f3bf6baee9e5d481c586980c774debcb4", size = 6316329 }, - { url = "https://files.pythonhosted.org/packages/26/fe/9208707b0c07d28bb9f466340e4f052142fe40d54ea5c2d57870ba0d6860/grpcio-1.68.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ff09c81e3aded7a183bc6473639b46b6caa9c1901d6f5e2cba24b95e59e30", size = 5939890 }, - { url = "https://files.pythonhosted.org/packages/05/b9/e344bf744e095e2795fe942ce432add2d03761c3c440a5747705ff5b8efb/grpcio-1.68.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8c73f9fbbaee1a132487e31585aa83987ddf626426d703ebcb9a528cf231c9b1", size = 6644776 }, - { url = "https://files.pythonhosted.org/packages/ef/bf/0856c5fa93c3e1bd9f42da62a7aa6988c7a8f95f30dc4f9a3d631f75bb8e/grpcio-1.68.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6b2f98165ea2790ea159393a2246b56f580d24d7da0d0342c18a085299c40a75", size = 6211889 }, - { url = "https://files.pythonhosted.org/packages/63/40/eac5203baf7f45c56b16645c81a4c8ed515510fe81322371e8625758239b/grpcio-1.68.0-cp311-cp311-win32.whl", hash = "sha256:e1e7ed311afb351ff0d0e583a66fcb39675be112d61e7cfd6c8269884a98afbc", size = 3650597 }, - { url = "https://files.pythonhosted.org/packages/e4/31/120ec7132e6b82a0df91952f71aa0aa5e9f23d70152b58d96fac9b3e7cfe/grpcio-1.68.0-cp311-cp311-win_amd64.whl", hash = "sha256:e0d2f68eaa0a755edd9a47d40e50dba6df2bceda66960dee1218da81a2834d27", size = 4400445 }, - { url = "https://files.pythonhosted.org/packages/30/66/79508e13feee4182e6f2ea260ad4eea96b8b396bbf81334660142a6eecab/grpcio-1.68.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8af6137cc4ae8e421690d276e7627cfc726d4293f6607acf9ea7260bd8fc3d7d", size = 5147575 }, - { url = "https://files.pythonhosted.org/packages/41/8d/19ffe12a736f57e9860bad506c0e711dd3c9c7c9f06030cfd87fa3eb6b45/grpcio-1.68.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4028b8e9a3bff6f377698587d642e24bd221810c06579a18420a17688e421af7", size = 11126767 }, - { url = "https://files.pythonhosted.org/packages/9c/c6/9aa8178d0fa3c893531a3ef38fa65a0e9997047ded9a8a20e3aa5706f923/grpcio-1.68.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f60fa2adf281fd73ae3a50677572521edca34ba373a45b457b5ebe87c2d01e1d", size = 5644649 }, - { url = "https://files.pythonhosted.org/packages/36/91/e2c451a103b8b595d3e3725fc78c76242d38a96cfe22dd9a47c31faba99d/grpcio-1.68.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e18589e747c1e70b60fab6767ff99b2d0c359ea1db8a2cb524477f93cdbedf5b", size = 6292623 }, - { url = "https://files.pythonhosted.org/packages/0b/5f/cbb2c0dfb3f7b893b30d6daca0a7829067f302c55f20b9c470111f48e6e3/grpcio-1.68.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d30f3fee9372796f54d3100b31ee70972eaadcc87314be369360248a3dcffe", size = 5905873 }, - { url = "https://files.pythonhosted.org/packages/9d/37/ddc32a46baccac6a0a3cdcabd6908d23dfa526f061a1b81211fe029489c7/grpcio-1.68.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7e0a3e72c0e9a1acab77bef14a73a416630b7fd2cbd893c0a873edc47c42c8cd", size = 6630863 }, - { url = "https://files.pythonhosted.org/packages/45/69/4f74f67ae33be4422bd20050e09ad8b5318f8827a7eb153507de8fb78aef/grpcio-1.68.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a831dcc343440969aaa812004685ed322cdb526cd197112d0db303b0da1e8659", size = 6200368 }, - { url = "https://files.pythonhosted.org/packages/91/e9/25e51915cd972e8c66daf29644e653135f967d7411eccd2651fa347a6337/grpcio-1.68.0-cp312-cp312-win32.whl", hash = "sha256:5a180328e92b9a0050958ced34dddcb86fec5a8b332f5a229e353dafc16cd332", size = 3637786 }, - { url = "https://files.pythonhosted.org/packages/e2/1d/b1250907a727f08de6508d752f367e4b46d113d4eac9eb919ebd9da6a5d6/grpcio-1.68.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bddd04a790b69f7a7385f6a112f46ea0b34c4746f361ebafe9ca0be567c78e9", size = 4390622 }, +version = "1.68.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/ec/b76ff6d86bdfd1737a5ec889394b54c18b1ec3832d91041e25023fbcb67d/grpcio-1.68.1.tar.gz", hash = "sha256:44a8502dd5de653ae6a73e2de50a401d84184f0331d0ac3daeb044e66d5c5054", size = 12694654 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/88/d1ac9676a0809e3efec154d45246474ec12a4941686da71ffb3d34190294/grpcio-1.68.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:d35740e3f45f60f3c37b1e6f2f4702c23867b9ce21c6410254c9c682237da68d", size = 5171054 }, + { url = "https://files.pythonhosted.org/packages/ec/cb/94ca41e100201fee8876a4b44d64e43ac7405929909afe1fa943d65b25ef/grpcio-1.68.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:d99abcd61760ebb34bdff37e5a3ba333c5cc09feda8c1ad42547bea0416ada78", size = 11078566 }, + { url = "https://files.pythonhosted.org/packages/d5/b0/ad4c66f2e3181b4eab99885686c960c403ae2300bacfe427526282facc07/grpcio-1.68.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:f8261fa2a5f679abeb2a0a93ad056d765cdca1c47745eda3f2d87f874ff4b8c9", size = 5690039 }, + { url = "https://files.pythonhosted.org/packages/67/1e/f5d3410674d021831c9fef2d1d7ca2357b08d09c840ad4e054ea8ffc302e/grpcio-1.68.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0feb02205a27caca128627bd1df4ee7212db051019a9afa76f4bb6a1a80ca95e", size = 6317470 }, + { url = "https://files.pythonhosted.org/packages/91/93/701d5f33b163a621c8f2d4453f9e22f6c14e996baed54118d0dea93fc8c7/grpcio-1.68.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:919d7f18f63bcad3a0f81146188e90274fde800a94e35d42ffe9eadf6a9a6330", size = 5941884 }, + { url = "https://files.pythonhosted.org/packages/67/44/06917ffaa35ca463b93dde60f324015fe4192312b0f4dd0faec061e7ca7f/grpcio-1.68.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:963cc8d7d79b12c56008aabd8b457f400952dbea8997dd185f155e2f228db079", size = 6646332 }, + { url = "https://files.pythonhosted.org/packages/d4/94/074db039532687ec8ef07ebbcc747c46547c94329016e22b97d97b9e5f3b/grpcio-1.68.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ccf2ebd2de2d6661e2520dae293298a3803a98ebfc099275f113ce1f6c2a80f1", size = 6212515 }, + { url = "https://files.pythonhosted.org/packages/c5/f2/0c939264c36c6038fae1732a2a3e01a7075ba171a2154d86842ee0ac9b0a/grpcio-1.68.1-cp310-cp310-win32.whl", hash = "sha256:2cc1fd04af8399971bcd4f43bd98c22d01029ea2e56e69c34daf2bf8470e47f5", size = 3650459 }, + { url = "https://files.pythonhosted.org/packages/b6/90/b0e9278e88f747879d13b79fb893c9acb381fb90541ad9e416c7816c5eaf/grpcio-1.68.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2e743e51cb964b4975de572aa8fb95b633f496f9fcb5e257893df3be854746", size = 4399144 }, + { url = "https://files.pythonhosted.org/packages/fe/0d/fde5a5777d65696c39bb3e622fe1239dd0a878589bf6c5066980e7d19154/grpcio-1.68.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:55857c71641064f01ff0541a1776bfe04a59db5558e82897d35a7793e525774c", size = 5180919 }, + { url = "https://files.pythonhosted.org/packages/07/fd/e5fa75b5ddf5d9f16606196973f9c2b4b1adf5a1735117eb7129fc33d2ec/grpcio-1.68.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4b177f5547f1b995826ef529d2eef89cca2f830dd8b2c99ffd5fde4da734ba73", size = 11150922 }, + { url = "https://files.pythonhosted.org/packages/86/1e/aaf5a1dae87fe47f277c5a1be72b31d2c209d095bebb0ce1d2df5cb8779c/grpcio-1.68.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:3522c77d7e6606d6665ec8d50e867f13f946a4e00c7df46768f1c85089eae515", size = 5685685 }, + { url = "https://files.pythonhosted.org/packages/a9/69/c4fdf87d5c5696207e2ed232e4bdde656d8c99ba91f361927f3f06aa41ca/grpcio-1.68.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d1fae6bbf0816415b81db1e82fb3bf56f7857273c84dcbe68cbe046e58e1ccd", size = 6316535 }, + { url = "https://files.pythonhosted.org/packages/6f/c6/539660516ea7db7bc3d39e07154512ae807961b14ec6b5b0c58d15657ff1/grpcio-1.68.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298ee7f80e26f9483f0b6f94cc0a046caf54400a11b644713bb5b3d8eb387600", size = 5939920 }, + { url = "https://files.pythonhosted.org/packages/38/f3/97a74dc4dd95bf195168d6da2ca4731ab7d3d0b03078f2833b4ff9c4f48f/grpcio-1.68.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cbb5780e2e740b6b4f2d208e90453591036ff80c02cc605fea1af8e6fc6b1bbe", size = 6644770 }, + { url = "https://files.pythonhosted.org/packages/cb/36/79a5e04073e58106aff442509a0c459151fa4f43202395db3eb8f77b78e9/grpcio-1.68.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ddda1aa22495d8acd9dfbafff2866438d12faec4d024ebc2e656784d96328ad0", size = 6211743 }, + { url = "https://files.pythonhosted.org/packages/73/0f/2250f4a0de1a0bec0726c47a021cbf71af6105f512ecaf67703e2eb1ad2f/grpcio-1.68.1-cp311-cp311-win32.whl", hash = "sha256:b33bd114fa5a83f03ec6b7b262ef9f5cac549d4126f1dc702078767b10c46ed9", size = 3650734 }, + { url = "https://files.pythonhosted.org/packages/4b/29/061c93a35f498238dc35eb8fb039ce168aa99cac2f0f1ce0c8a0a4bdb274/grpcio-1.68.1-cp311-cp311-win_amd64.whl", hash = "sha256:7f20ebec257af55694d8f993e162ddf0d36bd82d4e57f74b31c67b3c6d63d8b2", size = 4400816 }, + { url = "https://files.pythonhosted.org/packages/f5/15/674a1468fef234fa996989509bbdfc0d695878cbb385b9271f5d690d5cd3/grpcio-1.68.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:8829924fffb25386995a31998ccbbeaa7367223e647e0122043dfc485a87c666", size = 5148351 }, + { url = "https://files.pythonhosted.org/packages/62/f5/edce368682d6d0b3573b883b134df022a44b1c888ea416dd7d78d480ab24/grpcio-1.68.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3aed6544e4d523cd6b3119b0916cef3d15ef2da51e088211e4d1eb91a6c7f4f1", size = 11127559 }, + { url = "https://files.pythonhosted.org/packages/ce/14/a6fde3114eafd9e4e345d1ebd0291c544d83b22f0554b1678a2968ae39e1/grpcio-1.68.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:4efac5481c696d5cb124ff1c119a78bddbfdd13fc499e3bc0ca81e95fc573684", size = 5645221 }, + { url = "https://files.pythonhosted.org/packages/21/21/d1865bd6a22f9a26217e4e1b35f9105f7a0cdfb7a5fffe8be48e1a1afafc/grpcio-1.68.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ab2d912ca39c51f46baf2a0d92aa265aa96b2443266fc50d234fa88bf877d8e", size = 6292270 }, + { url = "https://files.pythonhosted.org/packages/3a/f6/19798be6c3515a7b1fb9570198c91710472e2eb21f1900109a76834829e3/grpcio-1.68.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c87ce2a97434dffe7327a4071839ab8e8bffd0054cc74cbe971fba98aedd60", size = 5905978 }, + { url = "https://files.pythonhosted.org/packages/9b/43/c3670a657445cd55be1246f64dbc3a6a33cab0f0141c5836df2e04f794c8/grpcio-1.68.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e4842e4872ae4ae0f5497bf60a0498fa778c192cc7a9e87877abd2814aca9475", size = 6630444 }, + { url = "https://files.pythonhosted.org/packages/80/69/fbbebccffd266bea4268b685f3e8e03613405caba69e93125dc783036465/grpcio-1.68.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:255b1635b0ed81e9f91da4fcc8d43b7ea5520090b9a9ad9340d147066d1d3613", size = 6200324 }, + { url = "https://files.pythonhosted.org/packages/65/5c/27a26c21916f94f0c1585111974a5d5a41d8420dcb42c2717ee514c97a97/grpcio-1.68.1-cp312-cp312-win32.whl", hash = "sha256:7dfc914cc31c906297b30463dde0b9be48e36939575eaf2a0a22a8096e69afe5", size = 3638381 }, + { url = "https://files.pythonhosted.org/packages/a3/ba/ba6b65ccc93c7df1031c6b41e45b79a5a37e46b81d816bb3ea68ba476d77/grpcio-1.68.1-cp312-cp312-win_amd64.whl", hash = "sha256:a0c8ddabef9c8f41617f213e527254c41e8b96ea9d387c632af878d05db9229c", size = 4389959 }, ] [[package]] name = "grpcio-status" -version = "1.68.0" +version = "1.68.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/95/500b24fc02a98ea16097e978958dd7ab9e16db9316a3f0bdc88d5ff239df/grpcio_status-1.68.0.tar.gz", hash = "sha256:8369823de22ab6a2cddb3804669c149ae7a71819e127c2dca7c2322028d52bea", size = 13666 } +sdist = { url = "https://files.pythonhosted.org/packages/57/db/db3911a9009f03b55e60cf13e3e29dfce423c0e501ec976794c7cbbbcd1b/grpcio_status-1.68.1.tar.gz", hash = "sha256:e1378d036c81a1610d7b4c7a146cd663dd13fcc915cf4d7d053929dba5bbb6e1", size = 13667 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/ba/dc535631a9dffa421b327ebfc961911af54c396aa5324efd122a94f72464/grpcio_status-1.68.0-py3-none-any.whl", hash = "sha256:0a71b15d989f02df803b4ba85c5bf1f43aeaa58ac021e5f9974b8cadc41f784d", size = 14427 }, + { url = "https://files.pythonhosted.org/packages/86/1c/59dfc81f27f252bef2cd52c57157bf381cb3738185d3087ac4c9ff3376b0/grpcio_status-1.68.1-py3-none-any.whl", hash = "sha256:66f3d8847f665acfd56221333d66f7ad8927903d87242a482996bdb45e8d28fd", size = 14427 }, ] +[[package]] +name = "grpclib" +version = "0.4.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h2" }, + { name = "multidict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/b9/55936e462a5925190d7427e880b3033601d1effd13809b483d13a926061a/grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3", size = 61254 } + [[package]] name = "gymnasium" version = "1.0.0" @@ -1033,63 +1095,85 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, ] +[[package]] +name = "h2" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/32/fec683ddd10629ea4ea46d206752a95a2d8a48c22521edd70b142488efe1/h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb", size = 2145593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e5/db6d438da759efbb488c4f3fbdab7764492ff3c3f953132efa6b9f0e9e53/h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d", size = 57488 }, +] + [[package]] name = "hiredis" -version = "3.0.0" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/80/740fb0dfa7a42416ce8376490f41dcdb1e5deed9c3739dfe4200fad865a9/hiredis-3.0.0.tar.gz", hash = "sha256:fed8581ae26345dea1f1e0d1a96e05041a727a45e7d8d459164583e23c6ac441", size = 87581 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/cc/41521d38c77f404c31e08a0118f369f37dc6a9e19cf315dbbc8b0b8afaba/hiredis-3.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:4b182791c41c5eb1d9ed736f0ff81694b06937ca14b0d4dadde5dadba7ff6dae", size = 81483 }, - { url = "https://files.pythonhosted.org/packages/99/35/0138fe68b0da01ea91ad67910577905b7f4a34b5c11e2f665d44067c52df/hiredis-3.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:13c275b483a052dd645eb2cb60d6380f1f5215e4c22d6207e17b86be6dd87ffa", size = 44763 }, - { url = "https://files.pythonhosted.org/packages/45/53/64fa74d43c17a406c2dc3cb4f1a3729ac00c5451f31f5940ca577b24afa9/hiredis-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1018cc7f12824506f165027eabb302735b49e63af73eb4d5450c66c88f47026", size = 42452 }, - { url = "https://files.pythonhosted.org/packages/af/b8/40c58b7db70e3850adeac85d5fca67e2fce6bf15c2705ca6af9c8bb32b5d/hiredis-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83a29cc7b21b746cb6a480189e49f49b2072812c445e66a9e38d2004d496b81c", size = 165712 }, - { url = "https://files.pythonhosted.org/packages/ff/8e/7afd36941d58cb0a7f0142ba3a043a5b3743dfff60596e98b355fb048113/hiredis-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e241fab6332e8fb5f14af00a4a9c6aefa22f19a336c069b7ddbf28ef8341e8d6", size = 176842 }, - { url = "https://files.pythonhosted.org/packages/ff/39/482970200e65cdcea037a595083e145fc089b8368312f6f2b0d3c5a7c266/hiredis-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1fb8de899f0145d6c4d5d4bd0ee88a78eb980a7ffabd51e9889251b8f58f1785", size = 166127 }, - { url = "https://files.pythonhosted.org/packages/3a/2b/655e8b4b54ff28c88e2ac536d4aa24c9119c6160169c043351a91db69bca/hiredis-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b23291951959141173eec10f8573538e9349fa27f47a0c34323d1970bf891ee5", size = 165983 }, - { url = "https://files.pythonhosted.org/packages/81/d8/bc917412f95da9904a83a04263aa2760051c118d0199eac7250623bfcf17/hiredis-3.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e421ac9e4b5efc11705a0d5149e641d4defdc07077f748667f359e60dc904420", size = 162249 }, - { url = "https://files.pythonhosted.org/packages/77/93/d6585264bb50f9f79537429fa90f4a2a5c29fd5e70d57dec7705ff161a7c/hiredis-3.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:77c8006c12154c37691b24ff293c077300c22944018c3ff70094a33e10c1d795", size = 160013 }, - { url = "https://files.pythonhosted.org/packages/48/a5/302868a60e963c1b768bd5622f125f5b38a3ea084bdcb374c9251dcc7c02/hiredis-3.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:41afc0d3c18b59eb50970479a9c0e5544fb4b95e3a79cf2fbaece6ddefb926fe", size = 159315 }, - { url = "https://files.pythonhosted.org/packages/82/77/c02d516ab8f31d85378916055dbf980ef7ca431d93ba1f7ac11ac4304863/hiredis-3.0.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:04ccae6dcd9647eae6025425ab64edb4d79fde8b9e6e115ebfabc6830170e3b2", size = 171008 }, - { url = "https://files.pythonhosted.org/packages/e1/28/c080805a340b418b1d022fa58465e365636c0ed201837e0fe70cc7beb0d3/hiredis-3.0.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fe91d62b0594db5ea7d23fc2192182b1a7b6973f628a9b8b2e0a42a2be721ac6", size = 163290 }, - { url = "https://files.pythonhosted.org/packages/6a/f9/caacca69987de597487360565e34dfd191ab23ce147144c13df1f2db6c8d/hiredis-3.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:99516d99316062824a24d145d694f5b0d030c80da693ea6f8c4ecf71a251d8bb", size = 161037 }, - { url = "https://files.pythonhosted.org/packages/88/3a/0d560473ca21facc1de5ba538f655aeae71303afd71f2a5e35fadee0c698/hiredis-3.0.0-cp310-cp310-win32.whl", hash = "sha256:562eaf820de045eb487afaa37e6293fe7eceb5b25e158b5a1974b7e40bf04543", size = 20034 }, - { url = "https://files.pythonhosted.org/packages/9c/af/23c2ce80faffb0ceb1775fe4581829c229400d6faacc0e2567ae179e8bc2/hiredis-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a1c81c89ed765198da27412aa21478f30d54ef69bf5e4480089d9c3f77b8f882", size = 21863 }, - { url = "https://files.pythonhosted.org/packages/42/3e/502e2ce2487673214fbb4cc733b1a279bc71309a689803d9ba8ad6f2fa8f/hiredis-3.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:4664dedcd5933364756d7251a7ea86d60246ccf73a2e00912872dacbfcef8978", size = 81442 }, - { url = "https://files.pythonhosted.org/packages/18/0b/171d85b2ee0ac51f94e993a323beffdb6b273b838a4f86d9abaaca22e2f7/hiredis-3.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:47de0bbccf4c8a9f99d82d225f7672b9dd690d8fd872007b933ef51a302c9fa6", size = 44742 }, - { url = "https://files.pythonhosted.org/packages/6a/67/466e0b16caff07bc8df8f3ff8b0b279f81066e0fb6a201b0ec66288fe5a4/hiredis-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e43679eca508ba8240d016d8cca9d27342d70184773c15bea78a23c87a1922f1", size = 42424 }, - { url = "https://files.pythonhosted.org/packages/01/50/e1f21e1cc9426bdf62e9ca8106294fbc3e5d27ddbae2e85e47fb9f251d1b/hiredis-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13c345e7278c210317e77e1934b27b61394fee0dec2e8bd47e71570900f75823", size = 166331 }, - { url = "https://files.pythonhosted.org/packages/98/40/8d8e4e15045ce066570f82f49604c6273b186eda1e5c9b93b450dd25d7b9/hiredis-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00018f22f38530768b73ea86c11f47e8d4df65facd4e562bd78773bd1baef35e", size = 177350 }, - { url = "https://files.pythonhosted.org/packages/5d/9c/f7b6d7afa2bd9c6671de853069222d9d874725e387100dfb0f1a22aab122/hiredis-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ea3a86405baa8eb0d3639ced6926ad03e07113de54cb00fd7510cb0db76a89d", size = 166794 }, - { url = "https://files.pythonhosted.org/packages/53/0c/1076e0c045412081ec44dc81969373cda15c093a0692e10f2941e154e583/hiredis-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c073848d2b1d5561f3903879ccf4e1a70c9b1e7566c7bdcc98d082fa3e7f0a1d", size = 166566 }, - { url = "https://files.pythonhosted.org/packages/05/69/e081b023f86b0128fcf9f76c8ed5a5f9426895ad86de234b0332c18a57b8/hiredis-3.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a8dffb5f5b3415a4669d25de48b617fd9d44b0bccfc4c2ab24b06406ecc9ecb", size = 162561 }, - { url = "https://files.pythonhosted.org/packages/96/e0/7f957fb2158c6f6800b6faa2f90bedcc485ca038a2d42166761d400683a3/hiredis-3.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:22c17c96143c2a62dfd61b13803bc5de2ac526b8768d2141c018b965d0333b66", size = 160472 }, - { url = "https://files.pythonhosted.org/packages/5c/31/d68020aa6276bd1a7436ece96d540ad17c204d97285639e0757ef1c3d430/hiredis-3.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c3ece960008dab66c6b8bb3a1350764677ee7c74ccd6270aaf1b1caf9ccebb46", size = 159705 }, - { url = "https://files.pythonhosted.org/packages/f7/68/5d101f8ffd764a96c2b959815adebb1e4b7e06db68122f9d3dbbc19b81eb/hiredis-3.0.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f75999ae00a920f7dce6ecae76fa5e8674a3110e5a75f12c7a2c75ae1af53396", size = 171498 }, - { url = "https://files.pythonhosted.org/packages/83/86/66131743a2012f668f84aa2eddc07e7b2462b4a07a753b27125f14e4b8bc/hiredis-3.0.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e069967cbd5e1900aafc4b5943888f6d34937fc59bf8918a1a546cb729b4b1e4", size = 163951 }, - { url = "https://files.pythonhosted.org/packages/a5/ea/58976d9c21086975a90c7fa2337591ea3903eeb55083e366b5ea36b99ca5/hiredis-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0aacc0a78e1d94d843a6d191f224a35893e6bdfeb77a4a89264155015c65f126", size = 161566 }, - { url = "https://files.pythonhosted.org/packages/39/69/cdb255e3d37f82f31f4b7b2db5bbd8500eae8d22c0d7992fe474fd02babd/hiredis-3.0.0-cp311-cp311-win32.whl", hash = "sha256:719c32147ba29528cb451f037bf837dcdda4ff3ddb6cdb12c4216b0973174718", size = 20037 }, - { url = "https://files.pythonhosted.org/packages/9d/cf/40d209e0458ac28a26973d1449df2922c7b8259f7f88d7738d11c87f9ff6/hiredis-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:bdc144d56333c52c853c31b4e2e52cfbdb22d3da4374c00f5f3d67c42158970f", size = 21862 }, - { url = "https://files.pythonhosted.org/packages/ae/09/0a3eace00115d8c82a8e7d8e58e60aacec10334f4f1512f09ffbac3252e3/hiredis-3.0.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:484025d2eb8f6348f7876fc5a2ee742f568915039fcb31b478fd5c242bb0fe3a", size = 81540 }, - { url = "https://files.pythonhosted.org/packages/1c/e8/1a7a5ded4fb11e91aafc5ba5518392f22883d54e79c4b47f188fb712ea46/hiredis-3.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:fcdb552ffd97151dab8e7bc3ab556dfa1512556b48a367db94b5c20253a35ee1", size = 44814 }, - { url = "https://files.pythonhosted.org/packages/3b/f5/4e055dc9b55484644afb18063f28649cdbd19be4f15bc152bd633dccd6f7/hiredis-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bb6f9fd92f147ba11d338ef5c68af4fd2908739c09e51f186e1d90958c68cc1", size = 42478 }, - { url = "https://files.pythonhosted.org/packages/65/7b/e06f55b9dcdf10cb6b3f08d7917d3080096cd83deaef1bd4927720fbb280/hiredis-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa86bf9a0ed339ec9e8a9a9d0ae4dccd8671625c83f9f9f2640729b15e07fbfd", size = 168303 }, - { url = "https://files.pythonhosted.org/packages/f4/16/081e90137bb896acd9dc2e1e68480cc84d652af4d959e75e52d6ce9dd602/hiredis-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e194a0d5df9456995d8f510eab9f529213e7326af6b94770abf8f8b7952ddcaa", size = 179151 }, - { url = "https://files.pythonhosted.org/packages/1e/0f/f5aba1c82977f4b639e5b450c0d8685333f1200cd1972647eb3f4d972e55/hiredis-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a1df39d74ec507d79c7a82c8063eee60bf80537cdeee652f576059b9cdd15c", size = 168580 }, - { url = "https://files.pythonhosted.org/packages/60/86/aa24c20f6d3038bf244bc60a2fe8cde61fb3c0d6a82e2bed30b08d55f96c/hiredis-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f91456507427ba36fd81b2ca11053a8e112c775325acc74e993201ea912d63e9", size = 169147 }, - { url = "https://files.pythonhosted.org/packages/6e/03/a4c7a28b6320ef3e36062c1c51e9d66e889c9e09ee7d7ae38b8a2ffdb365/hiredis-3.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9862db92ef67a8a02e0d5370f07d380e14577ecb281b79720e0d7a89aedb9ee5", size = 164722 }, - { url = "https://files.pythonhosted.org/packages/cd/66/d60106b56ba0ddd9789656d204a577591ff0cd91ab94178bb96c84d0d918/hiredis-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d10fcd9e0eeab835f492832b2a6edb5940e2f1230155f33006a8dfd3bd2c94e4", size = 162561 }, - { url = "https://files.pythonhosted.org/packages/6a/30/f33f2b782096efe9fe6b24c67a4df13b5055d9c859f615a74fb4f18cce41/hiredis-3.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:48727d7d405d03977d01885f317328dc21d639096308de126c2c4e9950cbd3c9", size = 161388 }, - { url = "https://files.pythonhosted.org/packages/45/02/34d9b151f9ea4655bfe00e0230f7db8fd8a52c7b7bd728efdf1c17655860/hiredis-3.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e0bb6102ebe2efecf8a3292c6660a0e6fac98176af6de67f020bea1c2343717", size = 173561 }, - { url = "https://files.pythonhosted.org/packages/cf/54/68285d208918b6d83e32d872d8dcbf8d479ed2c74b863b836e48a2702a3f/hiredis-3.0.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:df274e3abb4df40f4c7274dd3e587dfbb25691826c948bc98d5fead019dfb001", size = 165914 }, - { url = "https://files.pythonhosted.org/packages/56/4f/5f36865f9f032caf00d603ff9cbde21506d2b1e0e0ce0b5d2ce2851411c9/hiredis-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:034925b5fb514f7b11aac38cd55b3fd7e9d3af23bd6497f3f20aa5b8ba58e232", size = 163968 }, - { url = "https://files.pythonhosted.org/packages/d3/ee/c38693bd1dbce34806ecc3536dc425e87e420030de7018194865511860c2/hiredis-3.0.0-cp312-cp312-win32.whl", hash = "sha256:120f2dda469b28d12ccff7c2230225162e174657b49cf4cd119db525414ae281", size = 20189 }, - { url = "https://files.pythonhosted.org/packages/4e/67/f50b45071bb8652fa9a28a84ee470a02042fb7a096a16f3c08842f2a5c2b/hiredis-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:e584fe5f4e6681d8762982be055f1534e0170f6308a7a90f58d737bab12ff6a8", size = 21971 }, - { url = "https://files.pythonhosted.org/packages/6c/26/fee1a29d7d0cbb76e27ac0914bb17565b1d7cfa24d58922010a667190afc/hiredis-3.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:50da7a9edf371441dfcc56288d790985ee9840d982750580710a9789b8f4a290", size = 39805 }, - { url = "https://files.pythonhosted.org/packages/c7/da/4e9fadc0615958b58e6632d6e85375062f80b60b268b21fa3f449aeee02e/hiredis-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9b285ef6bf1581310b0d5e8f6ce64f790a1c40e89c660e1320b35f7515433672", size = 36883 }, - { url = "https://files.pythonhosted.org/packages/cf/d5/cc88b23e466ee070e0109a3e7d7e7835608ad90f80d8415bf7c8c726e71d/hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dcfa684966f25b335072115de2f920228a3c2caf79d4bfa2b30f6e4f674a948", size = 47867 }, - { url = "https://files.pythonhosted.org/packages/09/5b/848006ee860cf543a8b964c17ef04a61ea16967c9b5f173557286ae1afd2/hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a41be8af1fd78ca97bc948d789a09b730d1e7587d07ca53af05758f31f4b985d", size = 48254 }, - { url = "https://files.pythonhosted.org/packages/91/41/ef57d7f6f324ea5052d707a510093ec61fde8c5f271029116490790168cf/hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:038756db735e417ab36ee6fd7725ce412385ed2bd0767e8179a4755ea11b804f", size = 55556 }, - { url = "https://files.pythonhosted.org/packages/81/52/150658b3006241f2de243e2ccb7f94cfeb74a855435e872dbde7d87f6842/hiredis-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fcecbd39bd42cef905c0b51c9689c39d0cc8b88b1671e7f40d4fb213423aef3a", size = 21938 }, +sdist = { url = "https://files.pythonhosted.org/packages/38/e5/789cfa8993ced0061a6ef7ea758302ef5cf3439629bf0d39c85a6ede4641/hiredis-3.1.0.tar.gz", hash = "sha256:51d40ac3611091020d7dea6b05ed62cb152bff595fa4f931e7b6479d777acf7c", size = 87616 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/13/636d4eedc20ac6962439f72b4dc7c2906e68c4f1df88cea5ebf916125cd5/hiredis-3.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:2892db9db21f0cf7cc298d09f85d3e1f6dc4c4c24463ab67f79bc7a006d51867", size = 81243 }, + { url = "https://files.pythonhosted.org/packages/18/99/af3f3720c769292d159b12684f867641a47331d918bc3b820162c50c1861/hiredis-3.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:93cfa6cc25ee2ceb0be81dc61eca9995160b9e16bdb7cca4a00607d57e998918", size = 44499 }, + { url = "https://files.pythonhosted.org/packages/e5/1a/bc12c0e7688f23c33baad05bbd6fd2b944a35c2d3adad59a794ce7e62d70/hiredis-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2af62070aa9433802cae7be7364d5e82f76462c6a2ae34e53008b637aaa9a156", size = 42446 }, + { url = "https://files.pythonhosted.org/packages/65/8c/95c95a2bd6fb04b549083530c08bb1004c4a18a870c2eb8ac0e7f5b37879/hiredis-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:072c162260ebb1d892683107da22d0d5da7a1414739eae4e185cac22fe89627f", size = 165614 }, + { url = "https://files.pythonhosted.org/packages/e0/34/88c4fafe7c6df13c81c02bc51bda9190830b2e7d0d51e685f054573a2caa/hiredis-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6b232c43e89755ba332c2745ddab059c0bc1a0f01448a3a14d506f8448b1ce6", size = 176743 }, + { url = "https://files.pythonhosted.org/packages/ca/ae/f6fefb942ab27e33416b9734dc06995d4fd6f4daaf6855da520c851417c3/hiredis-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5316c9a65c4dde80796aa245b76011bab64eb84461a77b0a61c1bf2970bcc9", size = 166029 }, + { url = "https://files.pythonhosted.org/packages/84/85/c77ff1a95318e12810f529abe08744c06137de84f17114297773b76825b8/hiredis-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e812a4e656bbd1c1c15c844b28259c49e26bb384837e44e8d2aa55412c91d2f7", size = 165884 }, + { url = "https://files.pythonhosted.org/packages/8b/a2/e98faec792f49361f2913d5b537270d180db67ec19be86324aa1610b1371/hiredis-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93a6c9230e5a5565847130c0e1005c8d3aa5ca681feb0ed542c4651323d32feb", size = 162150 }, + { url = "https://files.pythonhosted.org/packages/c4/30/242b5795025ecd89c8c14de0bd8da9057e9e31348e5d3e739b63aac37f3e/hiredis-3.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a5f65e89ce50a94d9490d5442a649c6116f53f216c8c14eb37cf9637956482b2", size = 159914 }, + { url = "https://files.pythonhosted.org/packages/06/46/e5539f1db6e88fa4ebcffc6691713ae45d6d764c7ef8600d943da75d6b6a/hiredis-3.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9b2d6e33601c67c074c367fdccdd6033e642284e7a56adc130f18f724c378ca8", size = 159216 }, + { url = "https://files.pythonhosted.org/packages/25/47/f60b4d488bfba93eeaade3c859733f073cde40305c96437ff466b303143a/hiredis-3.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:bad3b1e0c83849910f28c95953417106f539277035a4b515d1425f93947bc28f", size = 170914 }, + { url = "https://files.pythonhosted.org/packages/12/83/2ba481bb58b99a8e921289b1d57e69a4516b954bfd8aab00dd28749a2ed7/hiredis-3.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9646de31f5994e6218311dcf216e971703dbf804c510fd3f84ddb9813c495824", size = 163194 }, + { url = "https://files.pythonhosted.org/packages/09/9d/e4f886d1db94f7cf0ccfc16e40da9a785fdd37cb6ba4d0b984477ff4d198/hiredis-3.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59a9230f3aa38a33d09d8171400de202f575d7a38869e5ce2947829bca6fe359", size = 160940 }, + { url = "https://files.pythonhosted.org/packages/35/f6/fee28cf6eb54ce5c3bd21e1f157c99f451e76e145ac7a9aa04f7df83097d/hiredis-3.1.0-cp310-cp310-win32.whl", hash = "sha256:0322d70f3328b97da14b6e98b18f0090a12ed8a8bf7ae20932e2eb9d1bb0aa2c", size = 20083 }, + { url = "https://files.pythonhosted.org/packages/75/8a/a86859e5bdef1cab3299bdaeb3facaead074f246383312305a62aa877e97/hiredis-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:802474c18e878b3f9905e160a8b7df87d57885758083eda76c5978265acb41aa", size = 21911 }, + { url = "https://files.pythonhosted.org/packages/7c/85/9f738bab9f446e40a3a29aff0aa7766568b2680407e862667eaa3ec78bfe/hiredis-3.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:c339ff4b4739b2a40da463763dd566129762f72926bca611ad9a457a9fe64abd", size = 81205 }, + { url = "https://files.pythonhosted.org/packages/ad/9c/c64ddce9768c3a95797db10f85ed80f80389693b558801a0256e7c8ea3db/hiredis-3.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:0ffa2552f704a45954627697a378fc2f559004e53055b82f00daf30bd4305330", size = 44479 }, + { url = "https://files.pythonhosted.org/packages/65/68/b0d0909f86b01bb7be738be306dc536431f2af90a42155a2fafa05d528b9/hiredis-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9acf7f0e7106f631cd618eb60ec9bbd6e43045addd5310f66ba1177209567e59", size = 42422 }, + { url = "https://files.pythonhosted.org/packages/20/3a/625227d3c26ee69ef0f5881c2e329f19d1d5fe6a880a0b5f7eaf2a1ae6ab/hiredis-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea4f5ecf9dbea93c827486f59c606684c3496ea71c7ba9a8131932780696e61a", size = 166230 }, + { url = "https://files.pythonhosted.org/packages/b9/e1/c14f3c66c42f5746cd54156584dcf60540a9063f351e101e99fd074e80ae/hiredis-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39efab176fca3d5111075f6ba56cd864f18db46d858289d39360c5672e0e5c3e", size = 177251 }, + { url = "https://files.pythonhosted.org/packages/1d/f4/a1d6972feb3be634ae7cdf719a56d5c7a8334f4202a05935b9c1b53d5ef6/hiredis-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1110eae007f30e70a058d743e369c24430327cd01fd97d99519d6794a58dd587", size = 166696 }, + { url = "https://files.pythonhosted.org/packages/87/6f/630581e3c62a4651cb914da1619ddeb7b07f182e74748277244df914c107/hiredis-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b390f63191bcccbb6044d4c118acdf4fa55f38e5658ac4cfd5a33a6f0c07659", size = 166463 }, + { url = "https://files.pythonhosted.org/packages/fd/7b/bcf5562fa50cdce19169d48bb3bc25690c27fde321f147b68781140c9d5d/hiredis-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72a98ccc7b8ec9ce0100ecf59f45f05d2023606e8e3676b07a316d1c1c364072", size = 162461 }, + { url = "https://files.pythonhosted.org/packages/f3/bd/902a6ad2832f6a517bc13b2fe30ee1f45714c4922faa6eb61c0113314dbc/hiredis-3.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7c76e751fd1e2f221dec09cdc24040ee486886e943d5d7ffc256e8cf15c75e51", size = 160376 }, + { url = "https://files.pythonhosted.org/packages/12/07/2f4be5e827d5c7d59061f2dfc882ceceb60eb9a263e8eebfbc0093b9c75d/hiredis-3.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7d3880f213b6f14e9c69ce52beffd1748eecc8669698c4782761887273b6e1bd", size = 159601 }, + { url = "https://files.pythonhosted.org/packages/2b/5e/ee606c694ac086ba28753b02d842868118830bcb1fb47e25091484677bec/hiredis-3.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:87c2b3fe7e7c96eba376506a76e11514e07e848f737b254e0973e4b5c3a491e9", size = 171404 }, + { url = "https://files.pythonhosted.org/packages/c3/1a/c2afd5ebb556ad06fe8ab99d1917709e3b0c4ee07f503ca31dab8d66ef1e/hiredis-3.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d3cfb4089e96f8f8ee9554da93148a9261aa6612ad2cc202c1a494c7b712e31f", size = 163846 }, + { url = "https://files.pythonhosted.org/packages/89/79/e1f0097a53110622c00c51f747f3edec69e24b74539ff23f68085dc547b4/hiredis-3.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f12018e5c5f866a1c3f7017cb2d88e5c6f9440df2281e48865a2b6c40f247f4", size = 161469 }, + { url = "https://files.pythonhosted.org/packages/aa/ca/531e287fc5c066d9f39bbc3a6a50ac34c84425c06bf2001af4bd2337037a/hiredis-3.1.0-cp311-cp311-win32.whl", hash = "sha256:107b66ce977bb2dff8f2239e68344360a75d05fed3d9fa0570ac4d3020ce2396", size = 20086 }, + { url = "https://files.pythonhosted.org/packages/b1/e1/c555f03a189624ed600118d39ab775e5d507e521a61db1a1dfa1c20f3d02/hiredis-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f1240bde53d3d1676f0aba61b3661560dc9a681cae24d9de33e650864029aa4", size = 21915 }, + { url = "https://files.pythonhosted.org/packages/cc/64/9f9c1648853cd34e52b2af04c26cebb7f086cb4cd8ce056fecedd7664be9/hiredis-3.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:f7c7f89e0bc4246115754e2eda078a111282f6d6ecc6fb458557b724fe6f2aac", size = 81304 }, + { url = "https://files.pythonhosted.org/packages/42/18/f70f8366c4abcbb830480d72968502192e422ebd60b7ca5f7739872e78cd/hiredis-3.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:3dbf9163296fa45fbddcfc4c5900f10e9ddadda37117dbfb641e327e536b53e0", size = 44551 }, + { url = "https://files.pythonhosted.org/packages/a8/a0/bf584a34a8b8e7194c3386700113cd7380a585c3e37b57b45bcf036a3305/hiredis-3.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af46a4be0e82df470f68f35316fa16cd1e134d1c5092fc1082e1aad64cce716d", size = 42471 }, + { url = "https://files.pythonhosted.org/packages/97/90/a709dad5fcfa6a3d0480709fd9e24d1e0ba70cbe4b853a1fe63cf7026207/hiredis-3.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc63d698c43aea500a84d8b083f830c03808b6cf3933ae4d35a27f0a3d881652", size = 168205 }, + { url = "https://files.pythonhosted.org/packages/14/29/33f943cc874d4cc6269d472b2c8ebb7385008fbde192aa5108d617d99504/hiredis-3.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:676b3d88674134bfaaf70dac181d1790b0f33b3187bfb9da9221e17e0e624f83", size = 179055 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/a1315d474ec36c89e68ac8a3a258431b6f266af7bc4a31265a9527e494df/hiredis-3.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aed10d9df1e2fb0011db2713ac64497462e9c2c0208b648c97569da772b959ca", size = 168484 }, + { url = "https://files.pythonhosted.org/packages/a1/4f/14aca28a24463b92274464000691610eb41a9afab1e16a7a739be496f274/hiredis-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b5bd8adfe8742e331a94cccd782bffea251fa70d9a709e71f4510f50794d700", size = 169050 }, + { url = "https://files.pythonhosted.org/packages/77/8d/e5aa6857a70c0e3ca423973ea27065fa3cf2567d25cc397b649a1d45043e/hiredis-3.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9fc4e35b4afb0af6da55495dd0742ad32ab88150428a6ecdbb3085cbd60714e8", size = 164624 }, + { url = "https://files.pythonhosted.org/packages/62/5d/c167de0a8c841cb4ea0e25a8145bbdb7e33b5028eaf905cd0901381f0a83/hiredis-3.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:89b83e76eb00ab0464e7b0752a3ffcb02626e742e9509bc141424a9c3202e8dc", size = 162461 }, + { url = "https://files.pythonhosted.org/packages/70/b8/fa7e9ae73237999a5c7eb9f59e6c2198ed65eca5cad948b85e2c82c12cc2/hiredis-3.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98ebf08c907836b70a8f40e030df8ab6f174dc7f6fa765251d813e89f14069d8", size = 161292 }, + { url = "https://files.pythonhosted.org/packages/04/af/6b6db2d29e2455e97cbf7e19bae0ef1a6e5b61c08d42377a3511ef9cc3bb/hiredis-3.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6c840b9cec086328f2ee2cfee0038b5d6bbb514bac7b5e579da6e346eaac056c", size = 173465 }, + { url = "https://files.pythonhosted.org/packages/dc/50/c49d53832d71e1fdb1fe7c91a99b2d47043655cb0d535437264dccc19e2e/hiredis-3.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c5c44e9fa6f4462d0330cb5f5d46fa652512fc86b41d4d1974d0356f263e9105", size = 165818 }, + { url = "https://files.pythonhosted.org/packages/5f/47/81992b4b27b59152abf7e279c4adba7a5a0e1d99ccbee674a82c6e65b9bf/hiredis-3.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e665b14ab50aa175cfa306fcb00fffd4e3ff02ceb36ca6a4df00b1246d6a73c4", size = 163871 }, + { url = "https://files.pythonhosted.org/packages/dd/f6/1ee81c373a2087557c6020bf201b4d27d6aec173c8414c3d06900e91d9bd/hiredis-3.1.0-cp312-cp312-win32.whl", hash = "sha256:bd33db977ac7af97e8d035ffadb163b00546be22e5f1297b2123f5f9bf0f8a21", size = 20206 }, + { url = "https://files.pythonhosted.org/packages/b7/67/46d5a8d44812c6293c8088d642e473b0dd9e12478ef539eb4a77df643450/hiredis-3.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:37aed4aa9348600145e2d019c7be27855e503ecc4906c6976ff2f3b52e3d5d97", size = 21997 }, + { url = "https://files.pythonhosted.org/packages/dd/11/13f2af303ed3891ed459527b0183bb743c43eeffd22b8771e7260a0b0281/hiredis-3.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:07ab990d0835f36bf358dbb84db4541ac0a8f533128ec09af8f80a576eef2e88", size = 39509 }, + { url = "https://files.pythonhosted.org/packages/ba/3e/0938e733ad08b6cabd1c56d973207769278b9d971fe6d5ed6480232a7b37/hiredis-3.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c54a88eb9d8ebc4e5eefaadbe2102a4f7499f9e413654172f40aefd25350959", size = 36900 }, + { url = "https://files.pythonhosted.org/packages/a7/a7/39d9521519b69056365892a51e2614275f3ae1f149e2c5d9885a909586fe/hiredis-3.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8095ef159896e5999a795b0f80e4d64281301a109e442a8d29cd750ca6bd8303", size = 47771 }, + { url = "https://files.pythonhosted.org/packages/e5/0a/e82918ac75213a47d8fbdcf7f6e2d3fd09a1eeb4e253d6babe47c00602f7/hiredis-3.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f8ca13e2476ffd6d5be4763f5868133506ddcfa5ce54b4dac231ebdc19be6c6", size = 48160 }, + { url = "https://files.pythonhosted.org/packages/56/cf/8de09573adcaa0906dd689904e24250561bc792c7f9ae7910f154fbba9b0/hiredis-3.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d25aa25c10f966d5415795ed271da84605044dbf436c054966cea5442451b3", size = 55458 }, + { url = "https://files.pythonhosted.org/packages/5d/ff/e1603c3c6926c1fa6ae85595e983d7206def21e455ee6f4578bbf31c479f/hiredis-3.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4180dc5f646b426e5fa1212e1348c167ee2a864b3a70d56579163d64a847dd1e", size = 21976 }, +] + +[[package]] +name = "hpack" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/9b/fda93fb4d957db19b0f6b370e79d586b3e8528b20252c729c476a2c02954/hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095", size = 49117 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/34/e8b383f35b77c402d28563d2b8f83159319b509bc5f760b15d60b0abf165/hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c", size = 32611 }, ] [[package]] @@ -1148,18 +1232,17 @@ wheels = [ [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "certifi" }, { name = "httpcore" }, { name = "idna" }, - { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395 }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] [[package]] @@ -1173,7 +1256,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "0.26.2" +version = "0.26.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -1184,18 +1267,27 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/a8/882ae5d1cfa7c9c5be32feee4cee56d9873078913953423e47a756da110d/huggingface_hub-0.26.2.tar.gz", hash = "sha256:b100d853465d965733964d123939ba287da60a547087783ddff8a323f340332b", size = 375621 } +sdist = { url = "https://files.pythonhosted.org/packages/51/21/2be5c66f29e798650a3e66bb350dee63bd9ab02cfc3ed7197cf4a905203e/huggingface_hub-0.26.5.tar.gz", hash = "sha256:1008bd18f60bfb65e8dbc0a97249beeeaa8c99d3c2fa649354df9fa5a13ed83b", size = 375951 } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/bf/cea0b9720c32fa01b0c4ec4b16b9f4ae34ca106b202ebbae9f03ab98cd8f/huggingface_hub-0.26.2-py3-none-any.whl", hash = "sha256:98c2a5a8e786c7b2cb6fdeb2740893cba4d53e312572ed3d8afafda65b128c46", size = 447536 }, + { url = "https://files.pythonhosted.org/packages/44/5a/dc6af87c61f89b23439eb95521e4e99862636cfd538ae12fd36be5483e5f/huggingface_hub-0.26.5-py3-none-any.whl", hash = "sha256:fb7386090bbe892072e64b85f7c4479fd2d65eea5f2543327c970d5169e83924", size = 447766 }, +] + +[[package]] +name = "hyperframe" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/2a/4747bff0a17f7281abe73e955d60d80aae537a5d203f417fa1c2e7578ebb/hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914", size = 25008 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/de/85a784bcc4a3779d1753a7ec2dee5de90e18c7bcf402e71b51fcf150b129/hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15", size = 12389 }, ] [[package]] name = "identify" -version = "2.6.2" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/79/7a520fc5011e02ca3f3285b5f6820eaf80443eb73e3733f73c02fb42ba0b/identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd", size = 99113 } +sdist = { url = "https://files.pythonhosted.org/packages/1a/5f/05f0d167be94585d502b4adf8c7af31f1dc0b1c7e14f9938a88fdbbcf4a7/identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02", size = 99179 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/86/c4395700f3c5475424fb5c41e20c16be28d10c904aee4d005ba3217fc8e7/identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3", size = 98982 }, + { url = "https://files.pythonhosted.org/packages/c9/f5/09644a3ad803fae9eca8efa17e1f2aef380c7f0b02f7ec4e8d446e51d64a/identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd", size = 99049 }, ] [[package]] @@ -1242,7 +1334,7 @@ wheels = [ [[package]] name = "ipython" -version = "8.29.0" +version = "8.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1257,9 +1349,9 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/e0/a3f36dde97e12121106807d80485423ae4c5b27ce60d40d4ab0bab18a9db/ipython-8.29.0.tar.gz", hash = "sha256:40b60e15b22591450eef73e40a027cf77bd652e757523eebc5bd7c7c498290eb", size = 5497513 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/8b/710af065ab8ed05649afa5bd1e07401637c9ec9fb7cfda9eac7e91e9fbd4/ipython-8.30.0.tar.gz", hash = "sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e", size = 5592205 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/a5/c15ed187f1b3fac445bb42a2dedd8dec1eee1718b35129242049a13a962f/ipython-8.29.0-py3-none-any.whl", hash = "sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8", size = 819911 }, + { url = "https://files.pythonhosted.org/packages/1d/f3/1332ba2f682b07b304ad34cad2f003adcfeb349486103f4b632335074a7c/ipython-8.30.0-py3-none-any.whl", hash = "sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321", size = 820765 }, ] [[package]] @@ -1288,46 +1380,46 @@ wheels = [ [[package]] name = "jiter" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/e5/50ff23c9bba2722d2f0f55ba51e57f7cbab9a4be758e6b9b263ef51e6024/jiter-0.7.1.tar.gz", hash = "sha256:448cf4f74f7363c34cdef26214da527e8eeffd88ba06d0b80b485ad0667baf5d", size = 162334 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f5/c6ccaa2331037cbb69d82e3ab2f2beaec485dd9e42a2ac409e6219b2d7f0/jiter-0.7.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:262e96d06696b673fad6f257e6a0abb6e873dc22818ca0e0600f4a1189eb334f", size = 291030 }, - { url = "https://files.pythonhosted.org/packages/31/7e/6abbccd6f22c4d90b836b654a3e12fa56c167f65439249c23ee36644630b/jiter-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be6de02939aac5be97eb437f45cfd279b1dc9de358b13ea6e040e63a3221c40d", size = 303756 }, - { url = "https://files.pythonhosted.org/packages/f7/65/a20de47be232547ee8a32f47989ac6a8f912a357af12eb48365ba7497003/jiter-0.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935f10b802bc1ce2b2f61843e498c7720aa7f4e4bb7797aa8121eab017293c3d", size = 328525 }, - { url = "https://files.pythonhosted.org/packages/5b/73/b62c385cd8389a984449e749840d3ffcac727eadb59d636514b30681144e/jiter-0.7.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9cd3cccccabf5064e4bb3099c87bf67db94f805c1e62d1aefd2b7476e90e0ee2", size = 347344 }, - { url = "https://files.pythonhosted.org/packages/75/b7/6cd1e8b42a1dbc9d4c3a05401a80383ddb5d3d740015bf786cfb0a1c36db/jiter-0.7.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aa919ebfc5f7b027cc368fe3964c0015e1963b92e1db382419dadb098a05192", size = 373480 }, - { url = "https://files.pythonhosted.org/packages/a2/5e/6bb5d28e9e7e046737b617ad9a05777ffa45d5ba355edd116efdad1fcd14/jiter-0.7.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ae2d01e82c94491ce4d6f461a837f63b6c4e6dd5bb082553a70c509034ff3d4", size = 390794 }, - { url = "https://files.pythonhosted.org/packages/1d/0c/e7ac4ac5327eff2b93bcd70e74ca76a0d6b0a6d2fc27f40707ddabfe7739/jiter-0.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f9568cd66dbbdab67ae1b4c99f3f7da1228c5682d65913e3f5f95586b3cb9a9", size = 325247 }, - { url = "https://files.pythonhosted.org/packages/72/19/0188794f2e3f8aedd16e4758fff810438f9f4207977553a8453c7e7382c4/jiter-0.7.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9ecbf4e20ec2c26512736284dc1a3f8ed79b6ca7188e3b99032757ad48db97dc", size = 365216 }, - { url = "https://files.pythonhosted.org/packages/55/dd/f4c0c9bbff937fd90de576df44feb310a533b9389a7b61542261fd9b29be/jiter-0.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1a0508fddc70ce00b872e463b387d49308ef02b0787992ca471c8d4ba1c0fa1", size = 514977 }, - { url = "https://files.pythonhosted.org/packages/82/56/f1dc02e26f780aa8e1c7bd856a7cd8d343c8e0e8111bdab2d2c0b77972c7/jiter-0.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f84c9996664c460f24213ff1e5881530abd8fafd82058d39af3682d5fd2d6316", size = 498020 }, - { url = "https://files.pythonhosted.org/packages/16/b7/489465de0f73896a3507c028faff791f16e0010c790264717fb557d299cb/jiter-0.7.1-cp310-none-win32.whl", hash = "sha256:c915e1a1960976ba4dfe06551ea87063b2d5b4d30759012210099e712a414d9f", size = 198725 }, - { url = "https://files.pythonhosted.org/packages/b6/1b/79d038a632e2d00657ca4965b6f93454eb0e563ad9168f40050f320e5460/jiter-0.7.1-cp310-none-win_amd64.whl", hash = "sha256:75bf3b7fdc5c0faa6ffffcf8028a1f974d126bac86d96490d1b51b3210aa0f3f", size = 201730 }, - { url = "https://files.pythonhosted.org/packages/26/e5/18b30b3015ae1df916cadd42b428f9a47a7277a52b041e4caf939e6c3c21/jiter-0.7.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ad04a23a91f3d10d69d6c87a5f4471b61c2c5cd6e112e85136594a02043f462c", size = 291198 }, - { url = "https://files.pythonhosted.org/packages/7d/e3/a70e5b98602d24c617e829d6714b3e4f7f9912fdc2844682653dafb5eba0/jiter-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e47a554de88dff701226bb5722b7f1b6bccd0b98f1748459b7e56acac2707a5", size = 303513 }, - { url = "https://files.pythonhosted.org/packages/34/35/c44064c12e2d189dd3a6135e3b4f9f64167d81abada4eeb0dd75313ad9ad/jiter-0.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e44fff69c814a2e96a20b4ecee3e2365e9b15cf5fe4e00869d18396daa91dab", size = 328470 }, - { url = "https://files.pythonhosted.org/packages/de/88/55747df3d3472a08d25ab59752cc7a2682c9bfb38d8dbe00d5fadc35ae49/jiter-0.7.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df0a1d05081541b45743c965436f8b5a1048d6fd726e4a030113a2699a6046ea", size = 347484 }, - { url = "https://files.pythonhosted.org/packages/8e/f6/afa8d156b7c166dceae66e01d0aa9933f7c3afcc5af3901e717237e5b98c/jiter-0.7.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f22cf8f236a645cb6d8ffe2a64edb5d2b66fb148bf7c75eea0cb36d17014a7bc", size = 373483 }, - { url = "https://files.pythonhosted.org/packages/b7/ab/38c652c71bfd7a8e00fc0962a6985186e9741cfd9dde00a0d8c0138a9c07/jiter-0.7.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8589f50b728ea4bf22e0632eefa125c8aa9c38ed202a5ee6ca371f05eeb3ff", size = 390563 }, - { url = "https://files.pythonhosted.org/packages/62/02/1dacf3b95d13f36298a261723c52b45701db485ee104f7677cb931697395/jiter-0.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f20de711224f2ca2dbb166a8d512f6ff48c9c38cc06b51f796520eb4722cc2ce", size = 325508 }, - { url = "https://files.pythonhosted.org/packages/b9/a8/ac3509099030304b28c226b432347f5420297e8bec4cb1f27f716a4f23cf/jiter-0.7.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a9803396032117b85ec8cbf008a54590644a062fedd0425cbdb95e4b2b60479", size = 365355 }, - { url = "https://files.pythonhosted.org/packages/92/fe/85fc2dd31473bf71b1e78311d09bb1f90211b5b327b9b884684d45e9ae48/jiter-0.7.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3d8bae77c82741032e9d89a4026479061aba6e646de3bf5f2fc1ae2bbd9d06e0", size = 514802 }, - { url = "https://files.pythonhosted.org/packages/06/8c/fa1f8b98618b476c346ad57e9eb85293cd2acd7680926b2f27f884c7aebc/jiter-0.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3dc9939e576bbc68c813fc82f6620353ed68c194c7bcf3d58dc822591ec12490", size = 497983 }, - { url = "https://files.pythonhosted.org/packages/ee/13/0e726eaee6c5dcd14582d28e90a1381ff4ab1c6b583e597f4e0d4a0950bd/jiter-0.7.1-cp311-none-win32.whl", hash = "sha256:f7605d24cd6fab156ec89e7924578e21604feee9c4f1e9da34d8b67f63e54892", size = 198800 }, - { url = "https://files.pythonhosted.org/packages/2b/30/6a79fd25f36660cec4fb46c5fd0d52375584fdc7a874889b24111cb666af/jiter-0.7.1-cp311-none-win_amd64.whl", hash = "sha256:f3ea649e7751a1a29ea5ecc03c4ada0a833846c59c6da75d747899f9b48b7282", size = 203785 }, - { url = "https://files.pythonhosted.org/packages/10/b3/de89eae8f57dc0ee5f6e3aa1ffcdee0364ef9ef85be81006fd17d7710ffa/jiter-0.7.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ad36a1155cbd92e7a084a568f7dc6023497df781adf2390c345dd77a120905ca", size = 291900 }, - { url = "https://files.pythonhosted.org/packages/c0/ff/0d804eff4751fceeabc6311d4b07e956daa06fa58f05931887dc7454466b/jiter-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7ba52e6aaed2dc5c81a3d9b5e4ab95b039c4592c66ac973879ba57c3506492bb", size = 304390 }, - { url = "https://files.pythonhosted.org/packages/e8/26/c258bef532d113a7ac26242893fc9760040a4846dec731098b7f5ac3fca7/jiter-0.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b7de0b6f6728b678540c7927587e23f715284596724be203af952418acb8a2d", size = 328710 }, - { url = "https://files.pythonhosted.org/packages/71/92/644dc215cbb9816112e28f3b43a8c8e769f083434a05fc3afd269c444f51/jiter-0.7.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9463b62bd53c2fb85529c700c6a3beb2ee54fde8bef714b150601616dcb184a6", size = 347569 }, - { url = "https://files.pythonhosted.org/packages/c6/02/795a3535262c54595bd97e375cc03b443717febb37723a7f9c077049825b/jiter-0.7.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:627164ec01d28af56e1f549da84caf0fe06da3880ebc7b7ee1ca15df106ae172", size = 373641 }, - { url = "https://files.pythonhosted.org/packages/7d/35/c7e9a06a49116e3618954f6c8a26816a7959c0f9e5617b0073e4145c5d6d/jiter-0.7.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25d0e5bf64e368b0aa9e0a559c3ab2f9b67e35fe7269e8a0d81f48bbd10e8963", size = 388828 }, - { url = "https://files.pythonhosted.org/packages/fb/05/894144e4cbc1b9d46756db512268a90f84fc1d8bd28f1a17e0fef5aaf5c5/jiter-0.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c244261306f08f8008b3087059601997016549cb8bb23cf4317a4827f07b7d74", size = 325511 }, - { url = "https://files.pythonhosted.org/packages/19/d3/e6674ac34de53787504e4fb309084f824df321f24113121d94bf53808be3/jiter-0.7.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7ded4e4b75b68b843b7cea5cd7c55f738c20e1394c68c2cb10adb655526c5f1b", size = 365940 }, - { url = "https://files.pythonhosted.org/packages/e9/ca/c773f0ce186090cc69a2c97b8dab3dad14ae9988a657a20d879458a8407e/jiter-0.7.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:80dae4f1889b9d09e5f4de6b58c490d9c8ce7730e35e0b8643ab62b1538f095c", size = 515430 }, - { url = "https://files.pythonhosted.org/packages/16/5f/c98f6e6362fbc7c87ad384ba8506983fca9bb55ea0af7efcb23e7dd22817/jiter-0.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5970cf8ec943b51bce7f4b98d2e1ed3ada170c2a789e2db3cb484486591a176a", size = 497389 }, - { url = "https://files.pythonhosted.org/packages/30/60/f60e12469afc9096bac3df0fda53de707ed5105d84322a0d1bc4ad03ee3e/jiter-0.7.1-cp312-none-win32.whl", hash = "sha256:701d90220d6ecb3125d46853c8ca8a5bc158de8c49af60fd706475a49fee157e", size = 198546 }, - { url = "https://files.pythonhosted.org/packages/01/d2/d8ec257544f7991384a46fccee6abdc5065cfede26354bb2c86251858a92/jiter-0.7.1-cp312-none-win_amd64.whl", hash = "sha256:7824c3ecf9ecf3321c37f4e4d4411aad49c666ee5bc2a937071bdd80917e4533", size = 202792 }, +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/70/90bc7bd3932e651486861df5c8ffea4ca7c77d28e8532ddefe2abc561a53/jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d", size = 163007 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/f3/8c11e0e87bd5934c414f9b1cfae3cbfd4a938d4669d57cb427e1c4d11a7f/jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b", size = 303381 }, + { url = "https://files.pythonhosted.org/packages/ea/28/4cd3f0bcbf40e946bc6a62a82c951afc386a25673d3d8d5ee461f1559bbe/jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393", size = 311718 }, + { url = "https://files.pythonhosted.org/packages/0d/17/57acab00507e60bd954eaec0837d9d7b119b4117ff49b8a62f2b646f32ed/jiter-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c826a221851a8dc028eb6d7d6429ba03184fa3c7e83ae01cd6d3bd1d4bd17d", size = 335465 }, + { url = "https://files.pythonhosted.org/packages/74/b9/1a3ddd2bc95ae17c815b021521020f40c60b32137730126bada962ef32b4/jiter-0.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d35c864c2dff13dfd79fb070fc4fc6235d7b9b359efe340e1261deb21b9fcb66", size = 355570 }, + { url = "https://files.pythonhosted.org/packages/78/69/6d29e2296a934199a7d0dde673ecccf98c9c8db44caf0248b3f2b65483cb/jiter-0.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f557c55bc2b7676e74d39d19bcb8775ca295c7a028246175d6a8b431e70835e5", size = 381383 }, + { url = "https://files.pythonhosted.org/packages/22/d7/fbc4c3fb1bf65f9be22a32759b539f88e897aeb13fe84ab0266e4423487a/jiter-0.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:580ccf358539153db147e40751a0b41688a5ceb275e6f3e93d91c9467f42b2e3", size = 390454 }, + { url = "https://files.pythonhosted.org/packages/4d/a0/3993cda2e267fe679b45d0bcc2cef0b4504b0aa810659cdae9737d6bace9/jiter-0.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af102d3372e917cffce49b521e4c32c497515119dc7bd8a75665e90a718bbf08", size = 345039 }, + { url = "https://files.pythonhosted.org/packages/b9/ef/69c18562b4c09ce88fab5df1dcaf643f6b1a8b970b65216e7221169b81c4/jiter-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cadcc978f82397d515bb2683fc0d50103acff2a180552654bb92d6045dec2c49", size = 376200 }, + { url = "https://files.pythonhosted.org/packages/4d/17/0b5a8de46a6ab4d836f70934036278b49b8530c292b29dde3483326d4555/jiter-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba5bdf56969cad2019d4e8ffd3f879b5fdc792624129741d3d83fc832fef8c7d", size = 511158 }, + { url = "https://files.pythonhosted.org/packages/6c/b2/c401a0a2554b36c9e6d6e4876b43790d75139cf3936f0222e675cbc23451/jiter-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3b94a33a241bee9e34b8481cdcaa3d5c2116f575e0226e421bed3f7a6ea71cff", size = 503956 }, + { url = "https://files.pythonhosted.org/packages/d4/02/a0291ed7d72c0ac130f172354ee3cf0b2556b69584de391463a8ee534f40/jiter-0.8.2-cp310-cp310-win32.whl", hash = "sha256:6e5337bf454abddd91bd048ce0dca5134056fc99ca0205258766db35d0a2ea43", size = 202846 }, + { url = "https://files.pythonhosted.org/packages/ad/20/8c988831ae4bf437e29f1671e198fc99ba8fe49f2895f23789acad1d1811/jiter-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:4a9220497ca0cb1fe94e3f334f65b9b5102a0b8147646118f020d8ce1de70105", size = 204414 }, + { url = "https://files.pythonhosted.org/packages/cb/b0/c1a7caa7f9dc5f1f6cfa08722867790fe2d3645d6e7170ca280e6e52d163/jiter-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2dd61c5afc88a4fda7d8b2cf03ae5947c6ac7516d32b7a15bf4b49569a5c076b", size = 303666 }, + { url = "https://files.pythonhosted.org/packages/f5/97/0468bc9eeae43079aaa5feb9267964e496bf13133d469cfdc135498f8dd0/jiter-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a6c710d657c8d1d2adbbb5c0b0c6bfcec28fd35bd6b5f016395f9ac43e878a15", size = 311934 }, + { url = "https://files.pythonhosted.org/packages/e5/69/64058e18263d9a5f1e10f90c436853616d5f047d997c37c7b2df11b085ec/jiter-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9584de0cd306072635fe4b89742bf26feae858a0683b399ad0c2509011b9dc0", size = 335506 }, + { url = "https://files.pythonhosted.org/packages/9d/14/b747f9a77b8c0542141d77ca1e2a7523e854754af2c339ac89a8b66527d6/jiter-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a90a923338531b7970abb063cfc087eebae6ef8ec8139762007188f6bc69a9f", size = 355849 }, + { url = "https://files.pythonhosted.org/packages/53/e2/98a08161db7cc9d0e39bc385415890928ff09709034982f48eccfca40733/jiter-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21974d246ed0181558087cd9f76e84e8321091ebfb3a93d4c341479a736f099", size = 381700 }, + { url = "https://files.pythonhosted.org/packages/7a/38/1674672954d35bce3b1c9af99d5849f9256ac8f5b672e020ac7821581206/jiter-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32475a42b2ea7b344069dc1e81445cfc00b9d0e3ca837f0523072432332e9f74", size = 389710 }, + { url = "https://files.pythonhosted.org/packages/f8/9b/92f9da9a9e107d019bcf883cd9125fa1690079f323f5a9d5c6986eeec3c0/jiter-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9931fd36ee513c26b5bf08c940b0ac875de175341cbdd4fa3be109f0492586", size = 345553 }, + { url = "https://files.pythonhosted.org/packages/44/a6/6d030003394e9659cd0d7136bbeabd82e869849ceccddc34d40abbbbb269/jiter-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0820f4a3a59ddced7fce696d86a096d5cc48d32a4183483a17671a61edfddc", size = 376388 }, + { url = "https://files.pythonhosted.org/packages/ad/8d/87b09e648e4aca5f9af89e3ab3cfb93db2d1e633b2f2931ede8dabd9b19a/jiter-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ffc86ae5e3e6a93765d49d1ab47b6075a9c978a2b3b80f0f32628f39caa0c88", size = 511226 }, + { url = "https://files.pythonhosted.org/packages/77/95/8008ebe4cdc82eac1c97864a8042ca7e383ed67e0ec17bfd03797045c727/jiter-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5127dc1abd809431172bc3fbe8168d6b90556a30bb10acd5ded41c3cfd6f43b6", size = 504134 }, + { url = "https://files.pythonhosted.org/packages/26/0d/3056a74de13e8b2562e4d526de6dac2f65d91ace63a8234deb9284a1d24d/jiter-0.8.2-cp311-cp311-win32.whl", hash = "sha256:66227a2c7b575720c1871c8800d3a0122bb8ee94edb43a5685aa9aceb2782d44", size = 203103 }, + { url = "https://files.pythonhosted.org/packages/4e/1e/7f96b798f356e531ffc0f53dd2f37185fac60fae4d6c612bbbd4639b90aa/jiter-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cde031d8413842a1e7501e9129b8e676e62a657f8ec8166e18a70d94d4682855", size = 206717 }, + { url = "https://files.pythonhosted.org/packages/a1/17/c8747af8ea4e045f57d6cfd6fc180752cab9bc3de0e8a0c9ca4e8af333b1/jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f", size = 302027 }, + { url = "https://files.pythonhosted.org/packages/3c/c1/6da849640cd35a41e91085723b76acc818d4b7d92b0b6e5111736ce1dd10/jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44", size = 310326 }, + { url = "https://files.pythonhosted.org/packages/06/99/a2bf660d8ccffee9ad7ed46b4f860d2108a148d0ea36043fd16f4dc37e94/jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f", size = 334242 }, + { url = "https://files.pythonhosted.org/packages/a7/5f/cea1c17864828731f11427b9d1ab7f24764dbd9aaf4648a7f851164d2718/jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60", size = 356654 }, + { url = "https://files.pythonhosted.org/packages/e9/13/62774b7e5e7f5d5043efe1d0f94ead66e6d0f894ae010adb56b3f788de71/jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57", size = 379967 }, + { url = "https://files.pythonhosted.org/packages/ec/fb/096b34c553bb0bd3f2289d5013dcad6074948b8d55212aa13a10d44c5326/jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e", size = 389252 }, + { url = "https://files.pythonhosted.org/packages/17/61/beea645c0bf398ced8b199e377b61eb999d8e46e053bb285c91c3d3eaab0/jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887", size = 345490 }, + { url = "https://files.pythonhosted.org/packages/d5/df/834aa17ad5dcc3cf0118821da0a0cf1589ea7db9832589278553640366bc/jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d", size = 376991 }, + { url = "https://files.pythonhosted.org/packages/67/80/87d140399d382fb4ea5b3d56e7ecaa4efdca17cd7411ff904c1517855314/jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152", size = 510822 }, + { url = "https://files.pythonhosted.org/packages/5c/37/3394bb47bac1ad2cb0465601f86828a0518d07828a650722e55268cdb7e6/jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29", size = 503730 }, + { url = "https://files.pythonhosted.org/packages/f9/e2/253fc1fa59103bb4e3aa0665d6ceb1818df1cd7bf3eb492c4dad229b1cd4/jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e", size = 203375 }, + { url = "https://files.pythonhosted.org/packages/41/69/6d4bbe66b3b3b4507e47aa1dd5d075919ad242b4b1115b3f80eecd443687/jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c", size = 204740 }, ] [[package]] @@ -1476,7 +1568,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.1.145" +version = "0.1.147" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1485,9 +1577,9 @@ dependencies = [ { name = "requests" }, { name = "requests-toolbelt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/e9/adb0c961d26fcefd17770e4ed02dd522c18484f10555b0924c56aebee587/langsmith-0.1.145.tar.gz", hash = "sha256:b6e53f7b1624846f03769a1fce673baf2a0a262b4f4129b1f6bd127a1f44f8fd", size = 299444 } +sdist = { url = "https://files.pythonhosted.org/packages/6c/56/201dd94d492ae47c1bf9b50cacc1985113dc2288d8f15857e1f4a6818376/langsmith-0.1.147.tar.gz", hash = "sha256:2e933220318a4e73034657103b3b1a3a6109cc5db3566a7e8e03be8d6d7def7a", size = 300453 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/e7/a5ed65f6b425ce0079f25e3f0540c9c4838abb155f65170f59585e64e02c/langsmith-0.1.145-py3-none-any.whl", hash = "sha256:bd3001fc6738ad9061a2709d60f62a6bdf4892a17e63c7751f73a6f32a100729", size = 310947 }, + { url = "https://files.pythonhosted.org/packages/de/f0/63b06b99b730b9954f8709f6f7d9b8d076fa0a973e472efe278089bde42b/langsmith-0.1.147-py3-none-any.whl", hash = "sha256:7166fc23b965ccf839d64945a78e9f1157757add228b086141eb03a60d699a15", size = 311812 }, ] [[package]] @@ -1635,6 +1727,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] +[[package]] +name = "modal" +version = "0.68.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "certifi" }, + { name = "click" }, + { name = "fastapi" }, + { name = "grpclib" }, + { name = "protobuf" }, + { name = "rich" }, + { name = "synchronicity" }, + { name = "toml" }, + { name = "typer" }, + { name = "types-certifi" }, + { name = "types-toml" }, + { name = "typing-extensions" }, + { name = "watchfiles" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/0f/ae029a752c31aa5bd775c9c9356f4a3dc2f69d1dec1094f4da40f76e08f7/modal-0.68.18-py3-none-any.whl", hash = "sha256:035eb6b3601cb40836b4696058ffee06ef2341295609caf4efe0ac43bd91b3ce", size = 502442 }, +] + [[package]] name = "more-itertools" version = "10.5.0" @@ -1766,6 +1882,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, ] +[[package]] +name = "narwhals" +version = "1.18.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/b5/0095051fe8a73f1092592ae34eaa7a10aa3871ea3ae56e4ec9e2115e7e4b/narwhals-1.18.3.tar.gz", hash = "sha256:30de15a97b7aeb8f9115e0bf1bba2ece99caaa8f8e570573a11c919e7dab29e9", size = 212764 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/e3/d3ac1fefcd5d534a2a741860c7ae6149b93d8e1be280130ba3dc17fd288f/narwhals-1.18.3-py3-none-any.whl", hash = "sha256:e2c6861bde081308934fb9bdae9613a32c7bd731f835353d84d2fb027e645487", size = 250306 }, +] + [[package]] name = "nbclient" version = "0.6.8" @@ -1993,7 +2118,7 @@ wheels = [ [[package]] name = "openai" -version = "1.55.0" +version = "1.57.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2005,47 +2130,56 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/7b/b1a3b6fa17dc523c603121dd23615bcd895a9fc3ab23be92307b9347bc50/openai-1.55.0.tar.gz", hash = "sha256:6c0975ac8540fe639d12b4ff5a8e0bf1424c844c4a4251148f59f06c4b2bd5db", size = 313963 } +sdist = { url = "https://files.pythonhosted.org/packages/29/e4/267d7f3f93432454f6dde95f08c737f48f031090693e558e2da7e63909ec/openai-1.57.4.tar.gz", hash = "sha256:a8f071a3e9198e2818f63aade68e759417b9f62c0971bdb83de82504b70b77f7", size = 316459 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/b2/9d8939c9ef73e6a2d5cb3366ef3fabd728a8de4729210d9af785c0edc6ec/openai-1.55.0-py3-none-any.whl", hash = "sha256:446e08918f8dd70d8723274be860404c8c7cc46b91b93bbc0ef051f57eb503c1", size = 389528 }, + { url = "https://files.pythonhosted.org/packages/3e/be/b466c8b64b224d285a338fbc705dc9d58cd60068bbfb8be2e47b1691e55c/openai-1.57.4-py3-none-any.whl", hash = "sha256:7def1ab2d52f196357ce31b9cfcf4181529ce00838286426bb35be81c035dafb", size = 390267 }, ] [[package]] name = "orjson" -version = "3.10.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/3a/10320029954badc7eaa338a15ee279043436f396e965dafc169610e4933f/orjson-3.10.11.tar.gz", hash = "sha256:e35b6d730de6384d5b2dab5fd23f0d76fae8bbc8c353c2f78210aa5fa4beb3ef", size = 5444879 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/63/f7d412e09f6e2c4e2562ddc44e86f2316a7ce9d7f353afa7cbce4f6a78d5/orjson-3.10.11-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6dade64687f2bd7c090281652fe18f1151292d567a9302b34c2dbb92a3872f1f", size = 266434 }, - { url = "https://files.pythonhosted.org/packages/a2/6a/3dfcd3a8c0e588581c8d1f3d9002cca970432da8a8096c1a42b99914a34d/orjson-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82f07c550a6ccd2b9290849b22316a609023ed851a87ea888c0456485a7d196a", size = 151884 }, - { url = "https://files.pythonhosted.org/packages/41/02/8981bc5ccbc04a2bd49cd86224d5b1e2c7417fb33e83590c66c3a028ede5/orjson-3.10.11-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd9a187742d3ead9df2e49240234d728c67c356516cf4db018833a86f20ec18c", size = 167371 }, - { url = "https://files.pythonhosted.org/packages/df/3f/772a12a417444eccc54fa597955b689848eb121d5e43dd7da9f6658c314d/orjson-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77b0fed6f209d76c1c39f032a70df2d7acf24b1812ca3e6078fd04e8972685a3", size = 154367 }, - { url = "https://files.pythonhosted.org/packages/8a/63/d0d6ba28410ec603fc31726a49dc782c72c0a64f4cd0a6734a6d8bc07a4a/orjson-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63fc9d5fe1d4e8868f6aae547a7b8ba0a2e592929245fff61d633f4caccdcdd6", size = 165726 }, - { url = "https://files.pythonhosted.org/packages/97/6e/d291bf382173af7788b368e4c22d02c7bdb9b7ac29b83e92930841321c16/orjson-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65cd3e3bb4fbb4eddc3c1e8dce10dc0b73e808fcb875f9fab40c81903dd9323e", size = 142522 }, - { url = "https://files.pythonhosted.org/packages/6d/3b/7364c10fcadf7c08e3658fe7103bf3b0408783f91022be4691fbe0b5ba1d/orjson-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f67c570602300c4befbda12d153113b8974a3340fdcf3d6de095ede86c06d92", size = 146931 }, - { url = "https://files.pythonhosted.org/packages/95/8c/43f454e642cc85ef845cda6efcfddc6b5fe46b897b692412022012e1272c/orjson-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1f39728c7f7d766f1f5a769ce4d54b5aaa4c3f92d5b84817053cc9995b977acc", size = 142900 }, - { url = "https://files.pythonhosted.org/packages/bb/29/ca24efe043501b4a4584d728fdc65af5cfc070ab9021a07fb195bce98919/orjson-3.10.11-cp310-none-win32.whl", hash = "sha256:1789d9db7968d805f3d94aae2c25d04014aae3a2fa65b1443117cd462c6da647", size = 144456 }, - { url = "https://files.pythonhosted.org/packages/b7/ec/f15dc012928459cfb96ed86178d92fddb5c01847f2c53fd8be2fa62dee6c/orjson-3.10.11-cp310-none-win_amd64.whl", hash = "sha256:5576b1e5a53a5ba8f8df81872bb0878a112b3ebb1d392155f00f54dd86c83ff6", size = 136442 }, - { url = "https://files.pythonhosted.org/packages/1e/25/c869a1fbd481dcb02c70032fd6a7243de7582bc48c7cae03d6f0985a11c0/orjson-3.10.11-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1444f9cb7c14055d595de1036f74ecd6ce15f04a715e73f33bb6326c9cef01b6", size = 266432 }, - { url = "https://files.pythonhosted.org/packages/6a/a4/2307155ee92457d28345308f7d8c0e712348404723025613adeffcb531d0/orjson-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdec57fe3b4bdebcc08a946db3365630332dbe575125ff3d80a3272ebd0ddafe", size = 151884 }, - { url = "https://files.pythonhosted.org/packages/aa/82/daf1b2596dd49fe44a1bd92367568faf6966dcb5d7f99fd437c3d0dc2de6/orjson-3.10.11-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eed32f33a0ea6ef36ccc1d37f8d17f28a1d6e8eefae5928f76aff8f1df85e67", size = 167371 }, - { url = "https://files.pythonhosted.org/packages/63/a8/680578e4589be5fdcfe0186bdd7dc6fe4a39d30e293a9da833cbedd5a56e/orjson-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80df27dd8697242b904f4ea54820e2d98d3f51f91e97e358fc13359721233e4b", size = 154368 }, - { url = "https://files.pythonhosted.org/packages/6e/ce/9cb394b5b01ef34579eeca6d704b21f97248f607067ce95a24ba9ea2698e/orjson-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:705f03cee0cb797256d54de6695ef219e5bc8c8120b6654dd460848d57a9af3d", size = 165725 }, - { url = "https://files.pythonhosted.org/packages/49/24/55eeb05cfb36b9e950d05743e6f6fdb7d5f33ca951a27b06ea6d03371aed/orjson-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03246774131701de8e7059b2e382597da43144a9a7400f178b2a32feafc54bd5", size = 142522 }, - { url = "https://files.pythonhosted.org/packages/94/0c/3a6a289e56dcc9fe67dc6b6d33c91dc5491f9ec4a03745efd739d2acf0ff/orjson-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8b5759063a6c940a69c728ea70d7c33583991c6982915a839c8da5f957e0103a", size = 146934 }, - { url = "https://files.pythonhosted.org/packages/1d/5c/a08c0e90a91e2526029a4681ff8c6fc4495b8bab77d48801144e378c7da9/orjson-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:677f23e32491520eebb19c99bb34675daf5410c449c13416f7f0d93e2cf5f981", size = 142904 }, - { url = "https://files.pythonhosted.org/packages/2c/c9/710286a60b14e88288ca014d43befb08bb0a4a6a0f51b875f8c2f05e8205/orjson-3.10.11-cp311-none-win32.whl", hash = "sha256:a11225d7b30468dcb099498296ffac36b4673a8398ca30fdaec1e6c20df6aa55", size = 144459 }, - { url = "https://files.pythonhosted.org/packages/7d/68/ef7b920e0a09e02b1a30daca1b4864938463797995c2fabe457c1500220a/orjson-3.10.11-cp311-none-win_amd64.whl", hash = "sha256:df8c677df2f9f385fcc85ab859704045fa88d4668bc9991a527c86e710392bec", size = 136444 }, - { url = "https://files.pythonhosted.org/packages/78/f2/a712dbcef6d84ff53e13056e7dc69d9d4844bd1e35e51b7431679ddd154d/orjson-3.10.11-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:360a4e2c0943da7c21505e47cf6bd725588962ff1d739b99b14e2f7f3545ba51", size = 266505 }, - { url = "https://files.pythonhosted.org/packages/94/54/53970831786d71f98fdc13c0f80451324c9b5c20fbf42f42ef6147607ee7/orjson-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:496e2cb45de21c369079ef2d662670a4892c81573bcc143c4205cae98282ba97", size = 151745 }, - { url = "https://files.pythonhosted.org/packages/35/38/482667da1ca7ef95d44d4d2328257a144fd2752383e688637c53ed474d2a/orjson-3.10.11-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7dfa8db55c9792d53c5952900c6a919cfa377b4f4534c7a786484a6a4a350c19", size = 167274 }, - { url = "https://files.pythonhosted.org/packages/23/2f/5bb0a03e819781d82dadb733fde8ebbe20d1777d1a33715d45ada4d82ce8/orjson-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f3382415747e0dbda9dade6f1e1a01a9d37f630d8c9049a8ed0e385b7a90c0", size = 154605 }, - { url = "https://files.pythonhosted.org/packages/49/e9/14cc34d45c7bd51665aff9b1bb6b83475a61c52edb0d753fffe1adc97764/orjson-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f35a1b9f50a219f470e0e497ca30b285c9f34948d3c8160d5ad3a755d9299433", size = 165874 }, - { url = "https://files.pythonhosted.org/packages/7b/61/c2781ecf90f99623e97c67a31e8553f38a1ecebaf3189485726ac8641576/orjson-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f3b7c5803138e67028dde33450e054c87e0703afbe730c105f1fcd873496d5", size = 142813 }, - { url = "https://files.pythonhosted.org/packages/4d/4f/18c83f78b501b6608569b1610fcb5a25c9bb9ab6a7eb4b3a55131e0fba37/orjson-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f91d9eb554310472bd09f5347950b24442600594c2edc1421403d7610a0998fd", size = 146762 }, - { url = "https://files.pythonhosted.org/packages/ba/19/ea80d5b575abd3f76a790409c2b7b8a60f3fc9447965c27d09613b8bddf4/orjson-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dfbb2d460a855c9744bbc8e36f9c3a997c4b27d842f3d5559ed54326e6911f9b", size = 143186 }, - { url = "https://files.pythonhosted.org/packages/2c/f5/d835fee01a0284d4b78effc24d16e7609daac2ff6b6851ca1bdd3b6194fc/orjson-3.10.11-cp312-none-win32.whl", hash = "sha256:d4a62c49c506d4d73f59514986cadebb7e8d186ad510c518f439176cf8d5359d", size = 144489 }, - { url = "https://files.pythonhosted.org/packages/03/60/748e0e205060dec74328dfd835e47902eb5522ae011766da76bfff64e2f4/orjson-3.10.11-cp312-none-win_amd64.whl", hash = "sha256:f1eec3421a558ff7a9b010a6c7effcfa0ade65327a71bb9b02a1c3b77a247284", size = 136614 }, +version = "3.10.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/04/bb9f72987e7f62fb591d6c880c0caaa16238e4e530cbc3bdc84a7372d75f/orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff", size = 5438647 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/d2/78652b67f86d093dca984ce3fa5bf819ee1462627da83e7d0b784a9a7c45/orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d", size = 248688 }, + { url = "https://files.pythonhosted.org/packages/70/cb/f8b6a52f3bc724edf8a62d8d1d8ee17cf19d6ae1cac89f077f0e7c30f396/orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7", size = 136952 }, + { url = "https://files.pythonhosted.org/packages/a6/43/c55700df9814545bc8c35d87395ec4b9ee473a3c1f5ed72f8d3ad0298ee9/orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e", size = 149089 }, + { url = "https://files.pythonhosted.org/packages/07/da/e7e7d73bd971710b736fbd8330b8830c5fa4fc0ac003b31af61f03b26dfc/orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced", size = 140479 }, + { url = "https://files.pythonhosted.org/packages/08/49/c9dfddba56ff24eecfacf2f01a76cae4d249ac2995b1359bf63a74b1b318/orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56", size = 156564 }, + { url = "https://files.pythonhosted.org/packages/96/df/174d2eff227dc23b4540a0c2efa6ec8fe406c442c4b7f0f556242f026d1f/orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2", size = 131282 }, + { url = "https://files.pythonhosted.org/packages/6a/96/8628c53a52e2a0a1ee861d809092df72aabbd312c71de9ad6d49e2c039ab/orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13", size = 139764 }, + { url = "https://files.pythonhosted.org/packages/38/17/08becb49e59e7bb7b29dc1dad19bc0c48635e627ee27e60eb5b64efcf7b1/orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05", size = 131913 }, + { url = "https://files.pythonhosted.org/packages/2a/05/f32acc2500e3fafee9445eb8b2a6ff19c4641035e6059c6c8d7bdb3abc9e/orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85", size = 415782 }, + { url = "https://files.pythonhosted.org/packages/06/03/6cc740d998d8bb60e75d4b7e228d18964475239ac842cc1865d49d092545/orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885", size = 142383 }, + { url = "https://files.pythonhosted.org/packages/f8/30/39cac82547fe021615376245c558b216d3ae8c99bd6b2274f312e49f1c94/orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2", size = 130661 }, + { url = "https://files.pythonhosted.org/packages/95/29/c6837f4fc1eaa742eaf5abcd767ab6805493f44fe1f72b37c1743706c1d8/orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3", size = 143625 }, + { url = "https://files.pythonhosted.org/packages/f6/62/c6b955f2144421108fa441b5471e1d5f8654a7df9840b261106e04d5d15c/orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509", size = 135075 }, + { url = "https://files.pythonhosted.org/packages/d3/48/7c3cd094488f5a3bc58488555244609a8c4d105bc02f2b77e509debf0450/orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74", size = 248687 }, + { url = "https://files.pythonhosted.org/packages/ff/90/e55f0e25c7fdd1f82551fe787f85df6f378170caca863c04c810cd8f2730/orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23", size = 136953 }, + { url = "https://files.pythonhosted.org/packages/2a/b3/109c020cf7fee747d400de53b43b183ca9d3ebda3906ad0b858eb5479718/orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252", size = 149090 }, + { url = "https://files.pythonhosted.org/packages/96/d4/35c0275dc1350707d182a1b5da16d1184b9439848060af541285407f18f9/orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef", size = 140480 }, + { url = "https://files.pythonhosted.org/packages/3b/79/f863ff460c291ad2d882cc3b580cc444bd4ec60c9df55f6901e6c9a3f519/orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252", size = 156564 }, + { url = "https://files.pythonhosted.org/packages/98/7e/8d5835449ddd873424ee7b1c4ba73a0369c1055750990d824081652874d6/orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4", size = 131279 }, + { url = "https://files.pythonhosted.org/packages/46/f5/d34595b6d7f4f984c6fef289269a7f98abcdc2445ebdf90e9273487dda6b/orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae", size = 139764 }, + { url = "https://files.pythonhosted.org/packages/b3/5b/ee6e9ddeab54a7b7806768151c2090a2d36025bc346a944f51cf172ef7f7/orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b", size = 131915 }, + { url = "https://files.pythonhosted.org/packages/c4/45/febee5951aef6db5cd8cdb260548101d7ece0ca9d4ddadadf1766306b7a4/orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da", size = 415783 }, + { url = "https://files.pythonhosted.org/packages/27/a5/5a8569e49f3a6c093bee954a3de95062a231196f59e59df13a48e2420081/orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07", size = 142387 }, + { url = "https://files.pythonhosted.org/packages/6e/05/02550fb38c5bf758f3994f55401233a2ef304e175f473f2ac6dbf464cc8b/orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd", size = 130664 }, + { url = "https://files.pythonhosted.org/packages/8c/f4/ba31019d0646ce51f7ac75af6dabf98fd89dbf8ad87a9086da34710738e7/orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79", size = 143623 }, + { url = "https://files.pythonhosted.org/packages/83/fe/babf08842b989acf4c46103fefbd7301f026423fab47e6f3ba07b54d7837/orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8", size = 135074 }, + { url = "https://files.pythonhosted.org/packages/a1/2f/989adcafad49afb535da56b95d8f87d82e748548b2a86003ac129314079c/orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d", size = 248678 }, + { url = "https://files.pythonhosted.org/packages/69/b9/8c075e21a50c387649db262b618ebb7e4d40f4197b949c146fc225dd23da/orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f", size = 136763 }, + { url = "https://files.pythonhosted.org/packages/87/d3/78edf10b4ab14c19f6d918cf46a145818f4aca2b5a1773c894c5490d3a4c/orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70", size = 149137 }, + { url = "https://files.pythonhosted.org/packages/16/81/5db8852bdf990a0ddc997fa8f16b80895b8cc77c0fe3701569ed2b4b9e78/orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69", size = 140567 }, + { url = "https://files.pythonhosted.org/packages/fa/a6/9ce1e3e3db918512efadad489630c25841eb148513d21dab96f6b4157fa1/orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9", size = 156620 }, + { url = "https://files.pythonhosted.org/packages/47/d4/05133d6bea24e292d2f7628b1e19986554f7d97b6412b3e51d812e38db2d/orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192", size = 131555 }, + { url = "https://files.pythonhosted.org/packages/b9/7a/b3fbffda8743135c7811e95dc2ab7cdbc5f04999b83c2957d046f1b3fac9/orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559", size = 139743 }, + { url = "https://files.pythonhosted.org/packages/b5/13/95bbcc9a6584aa083da5ce5004ce3d59ea362a542a0b0938d884fd8790b6/orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc", size = 131733 }, + { url = "https://files.pythonhosted.org/packages/e8/29/dddbb2ea6e7af426fcc3da65a370618a88141de75c6603313d70768d1df1/orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f", size = 415788 }, + { url = "https://files.pythonhosted.org/packages/53/df/4aea59324ac539975919b4705ee086aced38e351a6eb3eea0f5071dd5661/orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be", size = 142347 }, + { url = "https://files.pythonhosted.org/packages/55/55/a52d83d7c49f8ff44e0daab10554490447d6c658771569e1c662aa7057fe/orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c", size = 130829 }, + { url = "https://files.pythonhosted.org/packages/a1/8b/b1beb1624dd4adf7d72e2d9b73c4b529e7851c0c754f17858ea13e368b33/orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708", size = 143659 }, + { url = "https://files.pythonhosted.org/packages/13/91/634c9cd0bfc6a857fc8fab9bf1a1bd9f7f3345e0d6ca5c3d4569ceb6dcfa/orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb", size = 135221 }, ] [[package]] @@ -2094,15 +2228,15 @@ wheels = [ [[package]] name = "pandas-stubs" -version = "2.2.3.241009" +version = "2.2.3.241126" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "types-pytz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/30/1ca31098512cdcfbc6ce366072848dff497880d4285281606b5895244bbc/pandas_stubs-2.2.3.241009.tar.gz", hash = "sha256:d4ab618253f0acf78a5d0d2bfd6dffdd92d91a56a69bdc8144e5a5c6d25be3b5", size = 103801 } +sdist = { url = "https://files.pythonhosted.org/packages/90/86/93c545d149c3e1fe1c4c55478cc3a69859d0ea3467e1d9892e9eb28cb1e7/pandas_stubs-2.2.3.241126.tar.gz", hash = "sha256:cf819383c6d9ae7d4dabf34cd47e1e45525bb2f312e6ad2939c2c204cb708acd", size = 104204 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/be/d9ba3109c4c19a78e125f63074c4e436e447f30ece15f0ef1865e7178233/pandas_stubs-2.2.3.241009-py3-none-any.whl", hash = "sha256:3a6f8f142105a42550be677ba741ba532621f4e0acad2155c0e7b2450f114cfa", size = 157883 }, + { url = "https://files.pythonhosted.org/packages/6f/ab/ed42acf15bab2e86e5c49fad4aa038315233c4c2d22f41b49faa4d837516/pandas_stubs-2.2.3.241126-py3-none-any.whl", hash = "sha256:74aa79c167af374fe97068acc90776c0ebec5266a6e5c69fe11e9c2cf51f2267", size = 158280 }, ] [[package]] @@ -2244,59 +2378,59 @@ wheels = [ [[package]] name = "propcache" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/4d/5e5a60b78dbc1d464f8a7bbaeb30957257afdc8512cbb9dfd5659304f5cd/propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70", size = 40951 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/08/1963dfb932b8d74d5b09098507b37e9b96c835ba89ab8aad35aa330f4ff3/propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58", size = 80712 }, - { url = "https://files.pythonhosted.org/packages/e6/59/49072aba9bf8a8ed958e576182d46f038e595b17ff7408bc7e8807e721e1/propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b", size = 46301 }, - { url = "https://files.pythonhosted.org/packages/33/a2/6b1978c2e0d80a678e2c483f45e5443c15fe5d32c483902e92a073314ef1/propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110", size = 45581 }, - { url = "https://files.pythonhosted.org/packages/43/95/55acc9adff8f997c7572f23d41993042290dfb29e404cdadb07039a4386f/propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2", size = 208659 }, - { url = "https://files.pythonhosted.org/packages/bd/2c/ef7371ff715e6cd19ea03fdd5637ecefbaa0752fee5b0f2fe8ea8407ee01/propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a", size = 222613 }, - { url = "https://files.pythonhosted.org/packages/5e/1c/fef251f79fd4971a413fa4b1ae369ee07727b4cc2c71e2d90dfcde664fbb/propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577", size = 221067 }, - { url = "https://files.pythonhosted.org/packages/8d/e7/22e76ae6fc5a1708bdce92bdb49de5ebe89a173db87e4ef597d6bbe9145a/propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850", size = 208920 }, - { url = "https://files.pythonhosted.org/packages/04/3e/f10aa562781bcd8a1e0b37683a23bef32bdbe501d9cc7e76969becaac30d/propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61", size = 200050 }, - { url = "https://files.pythonhosted.org/packages/d0/98/8ac69f638358c5f2a0043809c917802f96f86026e86726b65006830f3dc6/propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37", size = 202346 }, - { url = "https://files.pythonhosted.org/packages/ee/78/4acfc5544a5075d8e660af4d4e468d60c418bba93203d1363848444511ad/propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48", size = 199750 }, - { url = "https://files.pythonhosted.org/packages/a2/8f/90ada38448ca2e9cf25adc2fe05d08358bda1b9446f54a606ea38f41798b/propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630", size = 201279 }, - { url = "https://files.pythonhosted.org/packages/08/31/0e299f650f73903da851f50f576ef09bfffc8e1519e6a2f1e5ed2d19c591/propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394", size = 211035 }, - { url = "https://files.pythonhosted.org/packages/85/3e/e356cc6b09064bff1c06d0b2413593e7c925726f0139bc7acef8a21e87a8/propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b", size = 215565 }, - { url = "https://files.pythonhosted.org/packages/8b/54/4ef7236cd657e53098bd05aa59cbc3cbf7018fba37b40eaed112c3921e51/propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336", size = 207604 }, - { url = "https://files.pythonhosted.org/packages/1f/27/d01d7799c068443ee64002f0655d82fb067496897bf74b632e28ee6a32cf/propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad", size = 40526 }, - { url = "https://files.pythonhosted.org/packages/bb/44/6c2add5eeafb7f31ff0d25fbc005d930bea040a1364cf0f5768750ddf4d1/propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99", size = 44958 }, - { url = "https://files.pythonhosted.org/packages/e0/1c/71eec730e12aec6511e702ad0cd73c2872eccb7cad39de8ba3ba9de693ef/propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354", size = 80811 }, - { url = "https://files.pythonhosted.org/packages/89/c3/7e94009f9a4934c48a371632197406a8860b9f08e3f7f7d922ab69e57a41/propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de", size = 46365 }, - { url = "https://files.pythonhosted.org/packages/c0/1d/c700d16d1d6903aeab28372fe9999762f074b80b96a0ccc953175b858743/propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87", size = 45602 }, - { url = "https://files.pythonhosted.org/packages/2e/5e/4a3e96380805bf742712e39a4534689f4cddf5fa2d3a93f22e9fd8001b23/propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016", size = 236161 }, - { url = "https://files.pythonhosted.org/packages/a5/85/90132481183d1436dff6e29f4fa81b891afb6cb89a7306f32ac500a25932/propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb", size = 244938 }, - { url = "https://files.pythonhosted.org/packages/4a/89/c893533cb45c79c970834274e2d0f6d64383ec740be631b6a0a1d2b4ddc0/propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2", size = 243576 }, - { url = "https://files.pythonhosted.org/packages/8c/56/98c2054c8526331a05f205bf45cbb2cda4e58e56df70e76d6a509e5d6ec6/propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4", size = 236011 }, - { url = "https://files.pythonhosted.org/packages/2d/0c/8b8b9f8a6e1abd869c0fa79b907228e7abb966919047d294ef5df0d136cf/propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504", size = 224834 }, - { url = "https://files.pythonhosted.org/packages/18/bb/397d05a7298b7711b90e13108db697732325cafdcd8484c894885c1bf109/propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178", size = 224946 }, - { url = "https://files.pythonhosted.org/packages/25/19/4fc08dac19297ac58135c03770b42377be211622fd0147f015f78d47cd31/propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d", size = 217280 }, - { url = "https://files.pythonhosted.org/packages/7e/76/c79276a43df2096ce2aba07ce47576832b1174c0c480fe6b04bd70120e59/propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2", size = 220088 }, - { url = "https://files.pythonhosted.org/packages/c3/9a/8a8cf428a91b1336b883f09c8b884e1734c87f724d74b917129a24fe2093/propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db", size = 233008 }, - { url = "https://files.pythonhosted.org/packages/25/7b/768a8969abd447d5f0f3333df85c6a5d94982a1bc9a89c53c154bf7a8b11/propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b", size = 237719 }, - { url = "https://files.pythonhosted.org/packages/ed/0d/e5d68ccc7976ef8b57d80613ac07bbaf0614d43f4750cf953f0168ef114f/propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b", size = 227729 }, - { url = "https://files.pythonhosted.org/packages/05/64/17eb2796e2d1c3d0c431dc5f40078d7282f4645af0bb4da9097fbb628c6c/propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1", size = 40473 }, - { url = "https://files.pythonhosted.org/packages/83/c5/e89fc428ccdc897ade08cd7605f174c69390147526627a7650fb883e0cd0/propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71", size = 44921 }, - { url = "https://files.pythonhosted.org/packages/7c/46/a41ca1097769fc548fc9216ec4c1471b772cc39720eb47ed7e38ef0006a9/propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2", size = 80800 }, - { url = "https://files.pythonhosted.org/packages/75/4f/93df46aab9cc473498ff56be39b5f6ee1e33529223d7a4d8c0a6101a9ba2/propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7", size = 46443 }, - { url = "https://files.pythonhosted.org/packages/0b/17/308acc6aee65d0f9a8375e36c4807ac6605d1f38074b1581bd4042b9fb37/propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8", size = 45676 }, - { url = "https://files.pythonhosted.org/packages/65/44/626599d2854d6c1d4530b9a05e7ff2ee22b790358334b475ed7c89f7d625/propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793", size = 246191 }, - { url = "https://files.pythonhosted.org/packages/f2/df/5d996d7cb18df076debae7d76ac3da085c0575a9f2be6b1f707fe227b54c/propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09", size = 251791 }, - { url = "https://files.pythonhosted.org/packages/2e/6d/9f91e5dde8b1f662f6dd4dff36098ed22a1ef4e08e1316f05f4758f1576c/propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89", size = 253434 }, - { url = "https://files.pythonhosted.org/packages/3c/e9/1b54b7e26f50b3e0497cd13d3483d781d284452c2c50dd2a615a92a087a3/propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e", size = 248150 }, - { url = "https://files.pythonhosted.org/packages/a7/ef/a35bf191c8038fe3ce9a414b907371c81d102384eda5dbafe6f4dce0cf9b/propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9", size = 233568 }, - { url = "https://files.pythonhosted.org/packages/97/d9/d00bb9277a9165a5e6d60f2142cd1a38a750045c9c12e47ae087f686d781/propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4", size = 229874 }, - { url = "https://files.pythonhosted.org/packages/8e/78/c123cf22469bdc4b18efb78893e69c70a8b16de88e6160b69ca6bdd88b5d/propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c", size = 225857 }, - { url = "https://files.pythonhosted.org/packages/31/1b/fd6b2f1f36d028820d35475be78859d8c89c8f091ad30e377ac49fd66359/propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887", size = 227604 }, - { url = "https://files.pythonhosted.org/packages/99/36/b07be976edf77a07233ba712e53262937625af02154353171716894a86a6/propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57", size = 238430 }, - { url = "https://files.pythonhosted.org/packages/0d/64/5822f496c9010e3966e934a011ac08cac8734561842bc7c1f65586e0683c/propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23", size = 244814 }, - { url = "https://files.pythonhosted.org/packages/fd/bd/8657918a35d50b18a9e4d78a5df7b6c82a637a311ab20851eef4326305c1/propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348", size = 235922 }, - { url = "https://files.pythonhosted.org/packages/a8/6f/ec0095e1647b4727db945213a9f395b1103c442ef65e54c62e92a72a3f75/propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5", size = 40177 }, - { url = "https://files.pythonhosted.org/packages/20/a2/bd0896fdc4f4c1db46d9bc361c8c79a9bf08ccc08ba054a98e38e7ba1557/propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3", size = 44446 }, - { url = "https://files.pythonhosted.org/packages/3d/b6/e6d98278f2d49b22b4d033c9f792eda783b9ab2094b041f013fc69bcde87/propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036", size = 11603 }, +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c8/2a13f78d82211490855b2fb303b6721348d0787fdd9a12ac46d99d3acde1/propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", size = 41735 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a5/0ea64c9426959ef145a938e38c832fc551843481d356713ececa9a8a64e8/propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6", size = 79296 }, + { url = "https://files.pythonhosted.org/packages/76/5a/916db1aba735f55e5eca4733eea4d1973845cf77dfe67c2381a2ca3ce52d/propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2", size = 45622 }, + { url = "https://files.pythonhosted.org/packages/2d/62/685d3cf268b8401ec12b250b925b21d152b9d193b7bffa5fdc4815c392c2/propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea", size = 45133 }, + { url = "https://files.pythonhosted.org/packages/4d/3d/31c9c29ee7192defc05aa4d01624fd85a41cf98e5922aaed206017329944/propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212", size = 204809 }, + { url = "https://files.pythonhosted.org/packages/10/a1/e4050776f4797fc86140ac9a480d5dc069fbfa9d499fe5c5d2fa1ae71f07/propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3", size = 219109 }, + { url = "https://files.pythonhosted.org/packages/c9/c0/e7ae0df76343d5e107d81e59acc085cea5fd36a48aa53ef09add7503e888/propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d", size = 217368 }, + { url = "https://files.pythonhosted.org/packages/fc/e1/e0a2ed6394b5772508868a977d3238f4afb2eebaf9976f0b44a8d347ad63/propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634", size = 205124 }, + { url = "https://files.pythonhosted.org/packages/50/c1/e388c232d15ca10f233c778bbdc1034ba53ede14c207a72008de45b2db2e/propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2", size = 195463 }, + { url = "https://files.pythonhosted.org/packages/0a/fd/71b349b9def426cc73813dbd0f33e266de77305e337c8c12bfb0a2a82bfb/propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958", size = 198358 }, + { url = "https://files.pythonhosted.org/packages/02/f2/d7c497cd148ebfc5b0ae32808e6c1af5922215fe38c7a06e4e722fe937c8/propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c", size = 195560 }, + { url = "https://files.pythonhosted.org/packages/bb/57/f37041bbe5e0dfed80a3f6be2612a3a75b9cfe2652abf2c99bef3455bbad/propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583", size = 196895 }, + { url = "https://files.pythonhosted.org/packages/83/36/ae3cc3e4f310bff2f064e3d2ed5558935cc7778d6f827dce74dcfa125304/propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf", size = 207124 }, + { url = "https://files.pythonhosted.org/packages/8c/c4/811b9f311f10ce9d31a32ff14ce58500458443627e4df4ae9c264defba7f/propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034", size = 210442 }, + { url = "https://files.pythonhosted.org/packages/18/dd/a1670d483a61ecac0d7fc4305d91caaac7a8fc1b200ea3965a01cf03bced/propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b", size = 203219 }, + { url = "https://files.pythonhosted.org/packages/f9/2d/30ced5afde41b099b2dc0c6573b66b45d16d73090e85655f1a30c5a24e07/propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4", size = 40313 }, + { url = "https://files.pythonhosted.org/packages/23/84/bd9b207ac80da237af77aa6e153b08ffa83264b1c7882495984fcbfcf85c/propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba", size = 44428 }, + { url = "https://files.pythonhosted.org/packages/bc/0f/2913b6791ebefb2b25b4efd4bb2299c985e09786b9f5b19184a88e5778dd/propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16", size = 79297 }, + { url = "https://files.pythonhosted.org/packages/cf/73/af2053aeccd40b05d6e19058419ac77674daecdd32478088b79375b9ab54/propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717", size = 45611 }, + { url = "https://files.pythonhosted.org/packages/3c/09/8386115ba7775ea3b9537730e8cf718d83bbf95bffe30757ccf37ec4e5da/propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3", size = 45146 }, + { url = "https://files.pythonhosted.org/packages/03/7a/793aa12f0537b2e520bf09f4c6833706b63170a211ad042ca71cbf79d9cb/propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9", size = 232136 }, + { url = "https://files.pythonhosted.org/packages/f1/38/b921b3168d72111769f648314100558c2ea1d52eb3d1ba7ea5c4aa6f9848/propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787", size = 239706 }, + { url = "https://files.pythonhosted.org/packages/14/29/4636f500c69b5edea7786db3c34eb6166f3384b905665ce312a6e42c720c/propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465", size = 238531 }, + { url = "https://files.pythonhosted.org/packages/85/14/01fe53580a8e1734ebb704a3482b7829a0ef4ea68d356141cf0994d9659b/propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af", size = 231063 }, + { url = "https://files.pythonhosted.org/packages/33/5c/1d961299f3c3b8438301ccfbff0143b69afcc30c05fa28673cface692305/propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7", size = 220134 }, + { url = "https://files.pythonhosted.org/packages/00/d0/ed735e76db279ba67a7d3b45ba4c654e7b02bc2f8050671ec365d8665e21/propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f", size = 220009 }, + { url = "https://files.pythonhosted.org/packages/75/90/ee8fab7304ad6533872fee982cfff5a53b63d095d78140827d93de22e2d4/propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54", size = 212199 }, + { url = "https://files.pythonhosted.org/packages/eb/ec/977ffaf1664f82e90737275873461695d4c9407d52abc2f3c3e24716da13/propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505", size = 214827 }, + { url = "https://files.pythonhosted.org/packages/57/48/031fb87ab6081764054821a71b71942161619549396224cbb242922525e8/propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82", size = 228009 }, + { url = "https://files.pythonhosted.org/packages/1a/06/ef1390f2524850838f2390421b23a8b298f6ce3396a7cc6d39dedd4047b0/propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca", size = 231638 }, + { url = "https://files.pythonhosted.org/packages/38/2a/101e6386d5a93358395da1d41642b79c1ee0f3b12e31727932b069282b1d/propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e", size = 222788 }, + { url = "https://files.pythonhosted.org/packages/db/81/786f687951d0979007e05ad9346cd357e50e3d0b0f1a1d6074df334b1bbb/propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034", size = 40170 }, + { url = "https://files.pythonhosted.org/packages/cf/59/7cc7037b295d5772eceb426358bb1b86e6cab4616d971bd74275395d100d/propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3", size = 44404 }, + { url = "https://files.pythonhosted.org/packages/4c/28/1d205fe49be8b1b4df4c50024e62480a442b1a7b818e734308bb0d17e7fb/propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a", size = 79588 }, + { url = "https://files.pythonhosted.org/packages/21/ee/fc4d893f8d81cd4971affef2a6cb542b36617cd1d8ce56b406112cb80bf7/propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0", size = 45825 }, + { url = "https://files.pythonhosted.org/packages/4a/de/bbe712f94d088da1d237c35d735f675e494a816fd6f54e9db2f61ef4d03f/propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d", size = 45357 }, + { url = "https://files.pythonhosted.org/packages/7f/14/7ae06a6cf2a2f1cb382586d5a99efe66b0b3d0c6f9ac2f759e6f7af9d7cf/propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4", size = 241869 }, + { url = "https://files.pythonhosted.org/packages/cc/59/227a78be960b54a41124e639e2c39e8807ac0c751c735a900e21315f8c2b/propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d", size = 247884 }, + { url = "https://files.pythonhosted.org/packages/84/58/f62b4ffaedf88dc1b17f04d57d8536601e4e030feb26617228ef930c3279/propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5", size = 248486 }, + { url = "https://files.pythonhosted.org/packages/1c/07/ebe102777a830bca91bbb93e3479cd34c2ca5d0361b83be9dbd93104865e/propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24", size = 243649 }, + { url = "https://files.pythonhosted.org/packages/ed/bc/4f7aba7f08f520376c4bb6a20b9a981a581b7f2e385fa0ec9f789bb2d362/propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff", size = 229103 }, + { url = "https://files.pythonhosted.org/packages/fe/d5/04ac9cd4e51a57a96f78795e03c5a0ddb8f23ec098b86f92de028d7f2a6b/propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f", size = 226607 }, + { url = "https://files.pythonhosted.org/packages/e3/f0/24060d959ea41d7a7cc7fdbf68b31852331aabda914a0c63bdb0e22e96d6/propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec", size = 221153 }, + { url = "https://files.pythonhosted.org/packages/77/a7/3ac76045a077b3e4de4859a0753010765e45749bdf53bd02bc4d372da1a0/propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348", size = 222151 }, + { url = "https://files.pythonhosted.org/packages/e7/af/5e29da6f80cebab3f5a4dcd2a3240e7f56f2c4abf51cbfcc99be34e17f0b/propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6", size = 233812 }, + { url = "https://files.pythonhosted.org/packages/8c/89/ebe3ad52642cc5509eaa453e9f4b94b374d81bae3265c59d5c2d98efa1b4/propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6", size = 238829 }, + { url = "https://files.pythonhosted.org/packages/e9/2f/6b32f273fa02e978b7577159eae7471b3cfb88b48563b1c2578b2d7ca0bb/propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518", size = 230704 }, + { url = "https://files.pythonhosted.org/packages/5c/2e/f40ae6ff5624a5f77edd7b8359b208b5455ea113f68309e2b00a2e1426b6/propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246", size = 40050 }, + { url = "https://files.pythonhosted.org/packages/3b/77/a92c3ef994e47180862b9d7d11e37624fb1c00a16d61faf55115d970628b/propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1", size = 44117 }, + { url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 }, ] [[package]] @@ -2313,16 +2447,16 @@ wheels = [ [[package]] name = "protobuf" -version = "5.28.3" +version = "5.29.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/74/6e/e69eb906fddcb38f8530a12f4b410699972ab7ced4e21524ece9d546ac27/protobuf-5.28.3.tar.gz", hash = "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b", size = 422479 } +sdist = { url = "https://files.pythonhosted.org/packages/d2/4f/1639b7b1633d8fd55f216ba01e21bf2c43384ab25ef3ddb35d85a52033e8/protobuf-5.29.1.tar.gz", hash = "sha256:683be02ca21a6ffe80db6dd02c0b5b2892322c59ca57fd6c872d652cb80549cb", size = 424965 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/c5/05163fad52d7c43e124a545f1372d18266db36036377ad29de4271134a6a/protobuf-5.28.3-cp310-abi3-win32.whl", hash = "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24", size = 419624 }, - { url = "https://files.pythonhosted.org/packages/9c/4c/4563ebe001ff30dca9d7ed12e471fa098d9759712980cde1fd03a3a44fb7/protobuf-5.28.3-cp310-abi3-win_amd64.whl", hash = "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868", size = 431464 }, - { url = "https://files.pythonhosted.org/packages/1c/f2/baf397f3dd1d3e4af7e3f5a0382b868d25ac068eefe1ebde05132333436c/protobuf-5.28.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687", size = 414743 }, - { url = "https://files.pythonhosted.org/packages/85/50/cd61a358ba1601f40e7d38bcfba22e053f40ef2c50d55b55926aecc8fec7/protobuf-5.28.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584", size = 316511 }, - { url = "https://files.pythonhosted.org/packages/5d/ae/3257b09328c0b4e59535e497b0c7537d4954038bdd53a2f0d2f49d15a7c4/protobuf-5.28.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135", size = 316624 }, - { url = "https://files.pythonhosted.org/packages/ad/c3/2377c159e28ea89a91cf1ca223f827ae8deccb2c9c401e5ca233cd73002f/protobuf-5.28.3-py3-none-any.whl", hash = "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed", size = 169511 }, + { url = "https://files.pythonhosted.org/packages/50/c7/28669b04691a376cf7d0617d612f126aa0fff763d57df0142f9bf474c5b8/protobuf-5.29.1-cp310-abi3-win32.whl", hash = "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110", size = 422706 }, + { url = "https://files.pythonhosted.org/packages/e3/33/dc7a7712f457456b7e0b16420ab8ba1cc8686751d3f28392eb43d0029ab9/protobuf-5.29.1-cp310-abi3-win_amd64.whl", hash = "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34", size = 434505 }, + { url = "https://files.pythonhosted.org/packages/e5/39/44239fb1c6ec557e1731d996a5de89a9eb1ada7a92491fcf9c5d714052ed/protobuf-5.29.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:d473655e29c0c4bbf8b69e9a8fb54645bc289dead6d753b952e7aa660254ae18", size = 417822 }, + { url = "https://files.pythonhosted.org/packages/fb/4a/ec56f101d38d4bef2959a9750209809242d86cf8b897db00f2f98bfa360e/protobuf-5.29.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5ba1d0e4c8a40ae0496d0e2ecfdbb82e1776928a205106d14ad6985a09ec155", size = 319572 }, + { url = "https://files.pythonhosted.org/packages/04/52/c97c58a33b3d6c89a8138788576d372a90a6556f354799971c6b4d16d871/protobuf-5.29.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ee1461b3af56145aca2800e6a3e2f928108c749ba8feccc6f5dd0062c410c0d", size = 319671 }, + { url = "https://files.pythonhosted.org/packages/3b/24/c8c49df8f6587719e1d400109b16c10c6902d0c9adddc8fff82840146f99/protobuf-5.29.1-py3-none-any.whl", hash = "sha256:32600ddb9c2a53dedc25b8581ea0f1fd8ea04956373c0c07577ce58d312522e0", size = 172547 }, ] [[package]] @@ -2360,31 +2494,31 @@ wheels = [ [[package]] name = "pyarrow" -version = "18.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/41/6bfd027410ba2cc35da4682394fdc4285dc345b1d99f7bd55e96255d0c7d/pyarrow-18.0.0.tar.gz", hash = "sha256:a6aa027b1a9d2970cf328ccd6dbe4a996bc13c39fd427f502782f5bdb9ca20f5", size = 1118457 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/de/f11a218ecc75e7af307058cb68cecff52b261d00cb59abf3ecdb51863cf1/pyarrow-18.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:2333f93260674e185cfbf208d2da3007132572e56871f451ba1a556b45dae6e2", size = 29508785 }, - { url = "https://files.pythonhosted.org/packages/d1/c9/de5d8997aa1c140043006beefe527ed377c8820192f14866f31f3659ffcb/pyarrow-18.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:4c381857754da44326f3a49b8b199f7f87a51c2faacd5114352fc78de30d3aba", size = 30813634 }, - { url = "https://files.pythonhosted.org/packages/b8/07/d1c7e83ab5be551ecd6acafa9dfbabd486038d351ef99c25fa9e7736f582/pyarrow-18.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:603cd8ad4976568954598ef0a6d4ed3dfb78aff3d57fa8d6271f470f0ce7d34f", size = 39179444 }, - { url = "https://files.pythonhosted.org/packages/ad/ed/c601fb60c4360fcc49a40b8db5c600c17670a26e3a803d8c9ab01c7023c8/pyarrow-18.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58a62549a3e0bc9e03df32f350e10e1efb94ec6cf63e3920c3385b26663948ce", size = 40092678 }, - { url = "https://files.pythonhosted.org/packages/e8/8b/f9fee7f9a69896ad888e2545a1b75bae205acdcc9feb18671f5c48880820/pyarrow-18.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bc97316840a349485fbb137eb8d0f4d7057e1b2c1272b1a20eebbbe1848f5122", size = 38590888 }, - { url = "https://files.pythonhosted.org/packages/90/d2/ea2413fcf338634530b71c617d45c87b004149575d142d906c0f92b618e0/pyarrow-18.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2e549a748fa8b8715e734919923f69318c953e077e9c02140ada13e59d043310", size = 40025239 }, - { url = "https://files.pythonhosted.org/packages/d9/0b/7ef63050a163ea2cb14ca0506bae8a2f6aab6760857cb4a99a4a8a55de09/pyarrow-18.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:606e9a3dcb0f52307c5040698ea962685fb1c852d72379ee9412be7de9c5f9e2", size = 25102382 }, - { url = "https://files.pythonhosted.org/packages/d6/63/a4854246fb3d1387e176e2989d919b8186ce3806ca244fbed27217608708/pyarrow-18.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d5795e37c0a33baa618c5e054cd61f586cf76850a251e2b21355e4085def6280", size = 29532160 }, - { url = "https://files.pythonhosted.org/packages/53/dc/9a6672fb35d36323f4548b08064fb264353024538f60adaedf0c6df6b31d/pyarrow-18.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:5f0510608ccd6e7f02ca8596962afb8c6cc84c453e7be0da4d85f5f4f7b0328a", size = 30844030 }, - { url = "https://files.pythonhosted.org/packages/8e/f9/cfcee70dcb48bc0fee6265a5d2502ea85ccdab54957fd2dd5b327dfc8807/pyarrow-18.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616ea2826c03c16e87f517c46296621a7c51e30400f6d0a61be645f203aa2b93", size = 39177238 }, - { url = "https://files.pythonhosted.org/packages/17/de/cd37c379dc1aa379956b15d9c89ff920cf48c239f64fbed0ca97dffa3acc/pyarrow-18.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1824f5b029ddd289919f354bc285992cb4e32da518758c136271cf66046ef22", size = 40089208 }, - { url = "https://files.pythonhosted.org/packages/dd/80/83453dcceaa49d7aa42b0b6aaa7a0797231b9aee1cc213f286e0be3bdf89/pyarrow-18.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6dd1b52d0d58dd8f685ced9971eb49f697d753aa7912f0a8f50833c7a7426319", size = 38606715 }, - { url = "https://files.pythonhosted.org/packages/18/f4/5687ead1672920b5ed8840398551cc3a96a1389be68b68d18aca3944e525/pyarrow-18.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:320ae9bd45ad7ecc12ec858b3e8e462578de060832b98fc4d671dee9f10d9954", size = 40040879 }, - { url = "https://files.pythonhosted.org/packages/49/11/ea314ad45f45d3245f0768dba711fd3d5deb25a9e08af298d0924ab94aee/pyarrow-18.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:2c992716cffb1088414f2b478f7af0175fd0a76fea80841b1706baa8fb0ebaad", size = 25105360 }, - { url = "https://files.pythonhosted.org/packages/e4/ea/a7f77688e6c529723b37589af4db3e7179414e223878301907c5bd49d6bc/pyarrow-18.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:e7ab04f272f98ebffd2a0661e4e126036f6936391ba2889ed2d44c5006237802", size = 29493113 }, - { url = "https://files.pythonhosted.org/packages/79/8a/a3af902af623a1cf4f9d4d27d81e634caf1585a819b7530728a8147e391c/pyarrow-18.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:03f40b65a43be159d2f97fd64dc998f769d0995a50c00f07aab58b0b3da87e1f", size = 30833386 }, - { url = "https://files.pythonhosted.org/packages/46/1e/f38b22e12e2ce9ee7c9d805ce234f68b23a0568b9a6bea223e3a99ca0068/pyarrow-18.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be08af84808dff63a76860847c48ec0416928a7b3a17c2f49a072cac7c45efbd", size = 39170798 }, - { url = "https://files.pythonhosted.org/packages/f8/fb/fd0ef3e0f03227ab183f8dc941f4ef59636d8c382e246954601dd29cf1b0/pyarrow-18.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c70c1965cde991b711a98448ccda3486f2a336457cf4ec4dca257a926e149c9", size = 40103326 }, - { url = "https://files.pythonhosted.org/packages/7c/bd/5de139adba486db5ccc1b7ecab51e328a9dce354c82c6d26c2f642b178d3/pyarrow-18.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:00178509f379415a3fcf855af020e3340254f990a8534294ec3cf674d6e255fd", size = 38583592 }, - { url = "https://files.pythonhosted.org/packages/8d/1f/9bb3b3a644892d631dbbe99053cdb5295092d2696b4bcd3d21f29624c689/pyarrow-18.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a71ab0589a63a3e987beb2bc172e05f000a5c5be2636b4b263c44034e215b5d7", size = 40043128 }, - { url = "https://files.pythonhosted.org/packages/74/39/323621402c2b1ce7ba600d03c81cf9645b862350d7c495f3fcef37850d1d/pyarrow-18.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe92efcdbfa0bcf2fa602e466d7f2905500f33f09eb90bf0bcf2e6ca41b574c8", size = 25075300 }, +version = "18.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/7b/640785a9062bb00314caa8a387abce547d2a420cf09bd6c715fe659ccffb/pyarrow-18.1.0.tar.gz", hash = "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73", size = 1118671 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/bb/8d4a1573f66e0684f190dd2b55fd0b97a7214de8882d58a3867e777bf640/pyarrow-18.1.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e21488d5cfd3d8b500b3238a6c4b075efabc18f0f6d80b29239737ebd69caa6c", size = 29531620 }, + { url = "https://files.pythonhosted.org/packages/30/90/893acfad917533b624a97b9e498c0e8393908508a0a72d624fe935e632bf/pyarrow-18.1.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b516dad76f258a702f7ca0250885fc93d1fa5ac13ad51258e39d402bd9e2e1e4", size = 30836521 }, + { url = "https://files.pythonhosted.org/packages/a3/2a/526545a7464b5fb2fa6e2c4bad16ca90e59e1843025c534fd907b7f73e5a/pyarrow-18.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f443122c8e31f4c9199cb23dca29ab9427cef990f283f80fe15b8e124bcc49b", size = 39213905 }, + { url = "https://files.pythonhosted.org/packages/8a/77/4b3fab91a30e19e233e738d0c5eca5a8f6dd05758bc349a2ca262c65de79/pyarrow-18.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a03da7f2758645d17b7b4f83c8bffeae5bbb7f974523fe901f36288d2eab71", size = 40128881 }, + { url = "https://files.pythonhosted.org/packages/aa/e2/a88e16c5e45e562449c52305bd3bc2f9d704295322d3434656e7ccac1444/pyarrow-18.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ba17845efe3aa358ec266cf9cc2800fa73038211fb27968bfa88acd09261a470", size = 38627517 }, + { url = "https://files.pythonhosted.org/packages/6d/84/8037c20005ccc7b869726465be0957bd9c29cfc88612962030f08292ad06/pyarrow-18.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3c35813c11a059056a22a3bef520461310f2f7eea5c8a11ef9de7062a23f8d56", size = 40060187 }, + { url = "https://files.pythonhosted.org/packages/2a/38/d6435c723ff73df8ae74626ea778262fbcc2b9b0d1a4f3db915b61711b05/pyarrow-18.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9736ba3c85129d72aefa21b4f3bd715bc4190fe4426715abfff90481e7d00812", size = 25118314 }, + { url = "https://files.pythonhosted.org/packages/9e/4d/a4988e7d82f4fbc797715db4185939a658eeffb07a25bab7262bed1ea076/pyarrow-18.1.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eaeabf638408de2772ce3d7793b2668d4bb93807deed1725413b70e3156a7854", size = 29554860 }, + { url = "https://files.pythonhosted.org/packages/59/03/3a42c5c1e4bd4c900ab62aa1ff6b472bdb159ba8f1c3e5deadab7222244f/pyarrow-18.1.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:3b2e2239339c538f3464308fd345113f886ad031ef8266c6f004d49769bb074c", size = 30867076 }, + { url = "https://files.pythonhosted.org/packages/75/7e/332055ac913373e89256dce9d14b7708f55f7bd5be631456c897f0237738/pyarrow-18.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39a2e0ed32a0970e4e46c262753417a60c43a3246972cfc2d3eb85aedd01b21", size = 39212135 }, + { url = "https://files.pythonhosted.org/packages/8c/64/5099cdb325828722ef7ffeba9a4696f238eb0cdeae227f831c2d77fcf1bd/pyarrow-18.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31e9417ba9c42627574bdbfeada7217ad8a4cbbe45b9d6bdd4b62abbca4c6f6", size = 40125195 }, + { url = "https://files.pythonhosted.org/packages/83/88/1938d783727db1b178ff71bc6a6143d7939e406db83a9ec23cad3dad325c/pyarrow-18.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:01c034b576ce0eef554f7c3d8c341714954be9b3f5d5bc7117006b85fcf302fe", size = 38641884 }, + { url = "https://files.pythonhosted.org/packages/5e/b5/9e14e9f7590e0eaa435ecea84dabb137284a4dbba7b3c337b58b65b76d95/pyarrow-18.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f266a2c0fc31995a06ebd30bcfdb7f615d7278035ec5b1cd71c48d56daaf30b0", size = 40076877 }, + { url = "https://files.pythonhosted.org/packages/4d/a3/817ac7fe0891a2d66e247e223080f3a6a262d8aefd77e11e8c27e6acf4e1/pyarrow-18.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4f13eee18433f99adefaeb7e01d83b59f73360c231d4782d9ddfaf1c3fbde0a", size = 25119811 }, + { url = "https://files.pythonhosted.org/packages/6a/50/12829e7111b932581e51dda51d5cb39207a056c30fe31ef43f14c63c4d7e/pyarrow-18.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d", size = 29514620 }, + { url = "https://files.pythonhosted.org/packages/d1/41/468c944eab157702e96abab3d07b48b8424927d4933541ab43788bb6964d/pyarrow-18.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee", size = 30856494 }, + { url = "https://files.pythonhosted.org/packages/68/f9/29fb659b390312a7345aeb858a9d9c157552a8852522f2c8bad437c29c0a/pyarrow-18.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992", size = 39203624 }, + { url = "https://files.pythonhosted.org/packages/6e/f6/19360dae44200e35753c5c2889dc478154cd78e61b1f738514c9f131734d/pyarrow-18.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54", size = 40139341 }, + { url = "https://files.pythonhosted.org/packages/bb/e6/9b3afbbcf10cc724312e824af94a2e993d8ace22994d823f5c35324cebf5/pyarrow-18.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33", size = 38618629 }, + { url = "https://files.pythonhosted.org/packages/3a/2e/3b99f8a3d9e0ccae0e961978a0d0089b25fb46ebbcfb5ebae3cca179a5b3/pyarrow-18.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30", size = 40078661 }, + { url = "https://files.pythonhosted.org/packages/76/52/f8da04195000099d394012b8d42c503d7041b79f778d854f410e5f05049a/pyarrow-18.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99", size = 25092330 }, ] [[package]] @@ -2419,16 +2553,16 @@ wheels = [ [[package]] name = "pydantic" -version = "2.10.1" +version = "2.10.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/bd/7fc610993f616d2398958d0028d15eaf53bde5f80cb2edb7aa4f1feaf3a7/pydantic-2.10.1.tar.gz", hash = "sha256:a4daca2dc0aa429555e0656d6bf94873a7dc5f54ee42b1f5873d666fb3f35560", size = 783717 } +sdist = { url = "https://files.pythonhosted.org/packages/45/0f/27908242621b14e649a84e62b133de45f84c255eecb350ab02979844a788/pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9", size = 786486 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/fc/fda48d347bd50a788dd2a0f318a52160f911b86fc2d8b4c86f4d7c9bceea/pydantic-2.10.1-py3-none-any.whl", hash = "sha256:a8d20db84de64cf4a7d59e899c2caf0fe9d660c7cfc482528e7020d7dd189a7e", size = 455329 }, + { url = "https://files.pythonhosted.org/packages/62/51/72c18c55cf2f46ff4f91ebcc8f75aa30f7305f3d726be3f4ebffb4ae972b/pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", size = 456997 }, ] [[package]] @@ -2492,6 +2626,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/72/f881b5e18fbb67cf2fb4ab253660de3c6899dbb2dba409d0b757e3559e3d/pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c", size = 2001864 }, ] +[[package]] +name = "pydeck" +version = "0.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, +] + [[package]] name = "pygments" version = "2.18.0" @@ -2512,7 +2659,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -2522,21 +2669,21 @@ dependencies = [ { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, ] [[package]] name = "pytest-asyncio" -version = "0.24.0" +version = "0.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 } +sdist = { url = "https://files.pythonhosted.org/packages/94/18/82fcb4ee47d66d99f6cd1efc0b11b2a25029f303c599a5afda7c1bca4254/pytest_asyncio-0.25.0.tar.gz", hash = "sha256:8c0610303c9e0442a5db8604505fc0f545456ba1528824842b37b4a626cbf609", size = 53298 } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 }, + { url = "https://files.pythonhosted.org/packages/88/56/2ee0cab25c11d4e38738a2a98c645a8f002e2ecf7b5ed774c70d53b92bb1/pytest_asyncio-0.25.0-py3-none-any.whl", hash = "sha256:db5432d18eac6b7e28b46dcd9b69921b55c3b1086e85febfe04e70b18d9e81b3", size = 19245 }, ] [[package]] @@ -2575,11 +2722,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.17" +version = "0.0.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/22/edea41c2d4a22e666c0c7db7acdcbf7bc8c1c1f7d3b3ca246ec982fec612/python_multipart-0.0.17.tar.gz", hash = "sha256:41330d831cae6e2f22902704ead2826ea038d0419530eadff3ea80175aec5538", size = 36452 } +sdist = { url = "https://files.pythonhosted.org/packages/c1/19/93bfb43a3c41b1dd0fa1fa66a08286f6467d36d30297a7aaab8c0b176a26/python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc", size = 36886 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/fb/275137a799169392f1fa88fff2be92f16eee38e982720a8aaadefc4a36b2/python_multipart-0.0.17-py3-none-any.whl", hash = "sha256:15dc4f487e0a9476cc1201261188ee0940165cffc94429b6fc565c4d3045cb5d", size = 24453 }, + { url = "https://files.pythonhosted.org/packages/e1/f4/ddd0fcdc454cf3870153ae16a818256523d31c3c8136e216bc6836ed4cd1/python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d", size = 24448 }, ] [[package]] @@ -2705,14 +2852,14 @@ wheels = [ [[package]] name = "redis" -version = "5.2.0" +version = "5.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/53/17/2f4a87ffa4cd93714cf52edfa3ea94589e9de65f71e9f99cbcfa84347a53/redis-5.2.0.tar.gz", hash = "sha256:0b1087665a771b1ff2e003aa5bdd354f15a70c9e25d5a7dbf9c722c16528a7b0", size = 4607878 } +sdist = { url = "https://files.pythonhosted.org/packages/47/da/d283a37303a995cd36f8b92db85135153dc4f7a8e4441aa827721b442cfb/redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f", size = 4608355 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/f5/ffa560ecc4bafbf25f7961c3d6f50d627a90186352e27e7d0ba5b1f6d87d/redis-5.2.0-py3-none-any.whl", hash = "sha256:ae174f2bb3b1bf2b09d54bf3e51fbc1469cf6c10aa03e21141f51969801a7897", size = 261428 }, + { url = "https://files.pythonhosted.org/packages/3c/5f/fa26b9b2672cbe30e07d9a5bdf39cf16e3b80b42916757c5f92bca88e4ba/redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4", size = 261502 }, ] [[package]] @@ -2844,62 +2991,76 @@ wheels = [ ] [[package]] -name = "rpds-py" -version = "0.21.0" +name = "rich-toolkit" +version = "0.12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/80/afdf96daf9b27d61483ef05b38f282121db0e38f5fd4e89f40f5c86c2a4f/rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db", size = 26335 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/a4/91747f902f166c589f1753cbd8bda713aceb75817c8bb597058a38aa85e6/rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590", size = 327473 }, - { url = "https://files.pythonhosted.org/packages/8a/72/75a30a07f96ae210e732c50c7339e742945fdc83661e65a1c80fcf39ceea/rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250", size = 318359 }, - { url = "https://files.pythonhosted.org/packages/dc/63/87d469d7628cd71366fd1baa32573acd37385843b8d39b6e2b69f16eec48/rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c", size = 361377 }, - { url = "https://files.pythonhosted.org/packages/dd/b1/78da258a4cafa1d8606a21b7d9ed4cc9d72d1c663583060ab02444b9bd9c/rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e", size = 369494 }, - { url = "https://files.pythonhosted.org/packages/44/47/6fdb7273cc80066d434e83cd49a3cfedb6d96ff70908480870877fb64b1e/rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0", size = 403639 }, - { url = "https://files.pythonhosted.org/packages/5f/4a/8c6c46afc050b5243be579be7f7b194d00b9731e83cc0845e9c70db127bb/rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1", size = 430551 }, - { url = "https://files.pythonhosted.org/packages/d4/31/2dd40abc26fc0fc037b86006583276dc375d38ac821d4ca2394274e8045b/rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5", size = 360795 }, - { url = "https://files.pythonhosted.org/packages/9d/2a/665b9ebef76f54764f1437ac03373a95a69480b7ce56c480360f88730cae/rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e", size = 382663 }, - { url = "https://files.pythonhosted.org/packages/e8/8c/e056f0c887d29baa256f8c8d7f7079a72d80395c35c14219de45ab19dce2/rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153", size = 546477 }, - { url = "https://files.pythonhosted.org/packages/33/11/588568f6c2ed5c9d6d121c188c71ca0f76e0e369a6d66f835737189e5a75/rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624", size = 549477 }, - { url = "https://files.pythonhosted.org/packages/15/86/c1401e2f70fbdf963c2ac9157994ebeb00c101ddf87975a90507f27cb2f4/rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664", size = 527966 }, - { url = "https://files.pythonhosted.org/packages/66/f2/452420f1493112825e975c87b3b4fd8b334e0e228cdb641597a92e0c3267/rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682", size = 200978 }, - { url = "https://files.pythonhosted.org/packages/35/4c/674b2e2d75607acdbc7a162ace36dcaad225c9e760cef5defa5c0f5ddd2d/rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5", size = 218549 }, - { url = "https://files.pythonhosted.org/packages/80/61/615929ea79f5fd0b3aca000411a33bcc1753607ccc1af0ce7b05b56e6e56/rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95", size = 327267 }, - { url = "https://files.pythonhosted.org/packages/a5/f5/28e89dda55b731d78cbfea284dc9789d265a8a06523f0adf60e9b05cade7/rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9", size = 318227 }, - { url = "https://files.pythonhosted.org/packages/e4/ef/eb90feb3e384543c48e2f867551075c43a429aa4c9a44e9c4bd71f4f786b/rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027", size = 361235 }, - { url = "https://files.pythonhosted.org/packages/ed/e7/8ea2d3d3398266c5c8ddd957d86003493b6d14f8f158b726dd09c8f43dee/rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9", size = 369467 }, - { url = "https://files.pythonhosted.org/packages/51/25/a286abda9da7820c971a0b1abcf1d31fb81c44a1088a128ad26c77206622/rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3", size = 403482 }, - { url = "https://files.pythonhosted.org/packages/7a/1e/9c3c0463fe142456dcd9e9be0ffd15b66a77adfcdf3ecf94fa2b12d95fcb/rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8", size = 429943 }, - { url = "https://files.pythonhosted.org/packages/e1/fd/f1fd7e77fef8e5a442ce7fd80ba957730877515fe18d7195f646408a60ce/rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d", size = 360437 }, - { url = "https://files.pythonhosted.org/packages/55/83/347932db075847f4f8172c3b53ad70fe725edd9058f0d4098080ad45e3bc/rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75", size = 382400 }, - { url = "https://files.pythonhosted.org/packages/22/9b/2a6eeab4e6752adba751cfee19bdf35d11e1073509f74883cbf14d42d682/rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f", size = 546560 }, - { url = "https://files.pythonhosted.org/packages/3c/19/6e51a141fe6f017d07b7d899b10a4af9e0f268deffacc1107d70fcd9257b/rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a", size = 549334 }, - { url = "https://files.pythonhosted.org/packages/cf/40/4ae09a07e4531278e6bee41ef3e4f166c23468135afc2c6c98917bfc28e6/rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8", size = 527855 }, - { url = "https://files.pythonhosted.org/packages/eb/45/2135be31543677687a426117c56d8b33e8b581bc4a8b7abfa53721012162/rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a", size = 200968 }, - { url = "https://files.pythonhosted.org/packages/68/fa/e66c3aaf13ef91c203ba47c102cd7c5dca92dde8837e5093577968d6d36d/rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e", size = 218502 }, - { url = "https://files.pythonhosted.org/packages/d9/5a/3aa6f5d8bacbe4f55ebf9a3c9628dad40cdb57f845124cf13c78895ea156/rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d", size = 329516 }, - { url = "https://files.pythonhosted.org/packages/df/c0/67c8c8ac850c6e3681e356a59d46315bf73bc77cb50c9a32db8ae44325b7/rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72", size = 321245 }, - { url = "https://files.pythonhosted.org/packages/64/83/bf31341f21fa594035891ff04a497dc86b210cc1a903a9cc01b097cc614f/rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266", size = 363951 }, - { url = "https://files.pythonhosted.org/packages/a2/e1/8218bba36737621262df316fbb729639af25ff611cc07bfeaadc1bfa6292/rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be", size = 373113 }, - { url = "https://files.pythonhosted.org/packages/39/8d/4afcd688e3ad33ec273900f42e6a41e9bd9f43cfc509b6d498683d2d0338/rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab", size = 405944 }, - { url = "https://files.pythonhosted.org/packages/fa/65/3326efa721b6ecd70262aab69a26c9bc19398cdb0a2a416ef30b58326460/rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7", size = 422874 }, - { url = "https://files.pythonhosted.org/packages/31/fb/48a647d0afab74289dd21a4128002d58684c22600a22c4bfb76cb9e3bfb0/rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf", size = 364227 }, - { url = "https://files.pythonhosted.org/packages/f1/b0/1cdd179d7382dd52d65b1fd19c54d090b6bd0688dfbe259bb5ab7548c359/rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4", size = 386447 }, - { url = "https://files.pythonhosted.org/packages/dc/41/84ace07f31aac3a96b73a374d89106cf252f7d3274e7cae85d17a27c602d/rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca", size = 549386 }, - { url = "https://files.pythonhosted.org/packages/33/ce/bf51bc5a3aa539171ea8c7737ab5ac06cef54c79b6b2a0511afc41533c89/rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b", size = 554777 }, - { url = "https://files.pythonhosted.org/packages/76/b1/950568e55a94c2979c2b61ec24e76e648a525fbc7551ccfc1f2841e39d44/rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11", size = 530918 }, - { url = "https://files.pythonhosted.org/packages/78/84/93f00e3613426c8a7a9ca16782d2828f2ac55296dd5c6b599379d9f59ee2/rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952", size = 203112 }, - { url = "https://files.pythonhosted.org/packages/e6/08/7a186847dd78881a781d2be9b42c8e49c3261c0f4a6d0289ba9a1e4cde71/rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd", size = 220735 }, - { url = "https://files.pythonhosted.org/packages/ff/d3/ffb04445d29c03d380047c62bed01b979adb9204424e2c833817012f679e/rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035", size = 328265 }, - { url = "https://files.pythonhosted.org/packages/dc/9d/894ff29a2be8f85fd1acff6e0c1b52b629aee019da8651125af9ee4894e1/rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919", size = 319238 }, - { url = "https://files.pythonhosted.org/packages/43/3d/0e5b835c22933a5bdc4413e4a91de55a8c1ef33f55eb2514a5cf24729173/rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c", size = 362136 }, - { url = "https://files.pythonhosted.org/packages/67/81/c9f29da910ac19758f170633c0937fc2f0898b84389bd05bfc255c985f19/rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f", size = 370411 }, - { url = "https://files.pythonhosted.org/packages/a8/df/b989044f90b81093e454eb54799e7ee5b085ebf957a75d07d5e21eac2fb5/rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333", size = 404598 }, - { url = "https://files.pythonhosted.org/packages/8f/09/f79cd575f503932f41138c4bec4c902eb3b71ea8570436688145cc77b8ef/rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356", size = 430224 }, - { url = "https://files.pythonhosted.org/packages/34/46/7fae3500bc188df2feee09dd72df262b97d31e8e4bd2ff4a8be4e28bf1d3/rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a", size = 361660 }, - { url = "https://files.pythonhosted.org/packages/5b/1d/d850242d30e68f99ad80815576f38b378b5aba393613e3357ed5e593499e/rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061", size = 384008 }, - { url = "https://files.pythonhosted.org/packages/c9/16/df4cfd1de216c25de24f8631f17380f8edee92201ec7810d1e2ba1dd9f85/rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d", size = 546855 }, - { url = "https://files.pythonhosted.org/packages/c0/b8/03d4561095d4fbf2ab62ed651a2b5cb674fe5245b1ab2f7909e8056bd014/rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18", size = 550599 }, - { url = "https://files.pythonhosted.org/packages/f4/54/d93867e2bf4acf57314798181faf3bd7d1a4f51a3aa81cb6211d56f74d3f/rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c", size = 528963 }, - { url = "https://files.pythonhosted.org/packages/66/86/6f72984a284d720d84fba5ee7b0d1b0d320978b516497cbfd6e335e95a3e/rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677", size = 219621 }, +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/88/58c193e2e353b0ef8b4b9a91031bbcf8a9a3b431f5ebb4f55c3f3b1992e8/rich_toolkit-0.12.0.tar.gz", hash = "sha256:facb0b40418010309f77abd44e2583b4936656f6ee5c8625da807564806a6c40", size = 71673 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/3c/3b66696fc8a6c980674851108d7d57fbcbfedbefb3d8b61a64166dc9b18e/rich_toolkit-0.12.0-py3-none-any.whl", hash = "sha256:a2da4416384410ae871e890db7edf8623e1f5e983341dbbc8cc03603ce24f0ab", size = 13012 }, +] + +[[package]] +name = "rpds-py" +version = "0.22.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/80/cce854d0921ff2f0a9fa831ba3ad3c65cee3a46711addf39a2af52df2cfd/rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", size = 26771 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/2a/ead1d09e57449b99dcc190d8d2323e3a167421d8f8fdf0f217c6f6befe47/rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967", size = 359514 }, + { url = "https://files.pythonhosted.org/packages/8f/7e/1254f406b7793b586c68e217a6a24ec79040f85e030fff7e9049069284f4/rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37", size = 349031 }, + { url = "https://files.pythonhosted.org/packages/aa/da/17c6a2c73730d426df53675ff9cc6653ac7a60b6438d03c18e1c822a576a/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24", size = 381485 }, + { url = "https://files.pythonhosted.org/packages/aa/13/2dbacd820466aa2a3c4b747afb18d71209523d353cf865bf8f4796c969ea/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff", size = 386794 }, + { url = "https://files.pythonhosted.org/packages/6d/62/96905d0a35ad4e4bc3c098b2f34b2e7266e211d08635baa690643d2227be/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c", size = 423523 }, + { url = "https://files.pythonhosted.org/packages/eb/1b/d12770f2b6a9fc2c3ec0d810d7d440f6d465ccd8b7f16ae5385952c28b89/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e", size = 446695 }, + { url = "https://files.pythonhosted.org/packages/4d/cf/96f1fd75512a017f8e07408b6d5dbeb492d9ed46bfe0555544294f3681b3/rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec", size = 381959 }, + { url = "https://files.pythonhosted.org/packages/ab/f0/d1c5b501c8aea85aeb938b555bfdf7612110a2f8cdc21ae0482c93dd0c24/rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c", size = 410420 }, + { url = "https://files.pythonhosted.org/packages/33/3b/45b6c58fb6aad5a569ae40fb890fc494c6b02203505a5008ee6dc68e65f7/rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09", size = 557620 }, + { url = "https://files.pythonhosted.org/packages/83/62/3fdd2d3d47bf0bb9b931c4c73036b4ab3ec77b25e016ae26fab0f02be2af/rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00", size = 584202 }, + { url = "https://files.pythonhosted.org/packages/04/f2/5dced98b64874b84ca824292f9cee2e3f30f3bcf231d15a903126684f74d/rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf", size = 552787 }, + { url = "https://files.pythonhosted.org/packages/67/13/2273dea1204eda0aea0ef55145da96a9aa28b3f88bb5c70e994f69eda7c3/rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652", size = 220088 }, + { url = "https://files.pythonhosted.org/packages/4e/80/8c8176b67ad7f4a894967a7a4014ba039626d96f1d4874d53e409b58d69f/rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8", size = 231737 }, + { url = "https://files.pythonhosted.org/packages/15/ad/8d1ddf78f2805a71253fcd388017e7b4a0615c22c762b6d35301fef20106/rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f", size = 359773 }, + { url = "https://files.pythonhosted.org/packages/c8/75/68c15732293a8485d79fe4ebe9045525502a067865fa4278f178851b2d87/rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a", size = 349214 }, + { url = "https://files.pythonhosted.org/packages/3c/4c/7ce50f3070083c2e1b2bbd0fb7046f3da55f510d19e283222f8f33d7d5f4/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5", size = 380477 }, + { url = "https://files.pythonhosted.org/packages/9a/e9/835196a69cb229d5c31c13b8ae603bd2da9a6695f35fe4270d398e1db44c/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb", size = 386171 }, + { url = "https://files.pythonhosted.org/packages/f9/8e/33fc4eba6683db71e91e6d594a2cf3a8fbceb5316629f0477f7ece5e3f75/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2", size = 422676 }, + { url = "https://files.pythonhosted.org/packages/37/47/2e82d58f8046a98bb9497a8319604c92b827b94d558df30877c4b3c6ccb3/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0", size = 446152 }, + { url = "https://files.pythonhosted.org/packages/e1/78/79c128c3e71abbc8e9739ac27af11dc0f91840a86fce67ff83c65d1ba195/rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1", size = 381300 }, + { url = "https://files.pythonhosted.org/packages/c9/5b/2e193be0e8b228c1207f31fa3ea79de64dadb4f6a4833111af8145a6bc33/rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d", size = 409636 }, + { url = "https://files.pythonhosted.org/packages/c2/3f/687c7100b762d62186a1c1100ffdf99825f6fa5ea94556844bbbd2d0f3a9/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648", size = 556708 }, + { url = "https://files.pythonhosted.org/packages/8c/a2/c00cbc4b857e8b3d5e7f7fc4c81e23afd8c138b930f4f3ccf9a41a23e9e4/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74", size = 583554 }, + { url = "https://files.pythonhosted.org/packages/d0/08/696c9872cf56effdad9ed617ac072f6774a898d46b8b8964eab39ec562d2/rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a", size = 552105 }, + { url = "https://files.pythonhosted.org/packages/18/1f/4df560be1e994f5adf56cabd6c117e02de7c88ee238bb4ce03ed50da9d56/rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64", size = 220199 }, + { url = "https://files.pythonhosted.org/packages/b8/1b/c29b570bc5db8237553002788dc734d6bd71443a2ceac2a58202ec06ef12/rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c", size = 231775 }, + { url = "https://files.pythonhosted.org/packages/75/47/3383ee3bd787a2a5e65a9b9edc37ccf8505c0a00170e3a5e6ea5fbcd97f7/rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e", size = 352334 }, + { url = "https://files.pythonhosted.org/packages/40/14/aa6400fa8158b90a5a250a77f2077c0d0cd8a76fce31d9f2b289f04c6dec/rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56", size = 342111 }, + { url = "https://files.pythonhosted.org/packages/7d/06/395a13bfaa8a28b302fb433fb285a67ce0ea2004959a027aea8f9c52bad4/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45", size = 384286 }, + { url = "https://files.pythonhosted.org/packages/43/52/d8eeaffab047e6b7b7ef7f00d5ead074a07973968ffa2d5820fa131d7852/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e", size = 391739 }, + { url = "https://files.pythonhosted.org/packages/83/31/52dc4bde85c60b63719610ed6f6d61877effdb5113a72007679b786377b8/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d", size = 427306 }, + { url = "https://files.pythonhosted.org/packages/70/d5/1bab8e389c2261dba1764e9e793ed6830a63f830fdbec581a242c7c46bda/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38", size = 442717 }, + { url = "https://files.pythonhosted.org/packages/82/a1/a45f3e30835b553379b3a56ea6c4eb622cf11e72008229af840e4596a8ea/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15", size = 385721 }, + { url = "https://files.pythonhosted.org/packages/a6/27/780c942de3120bdd4d0e69583f9c96e179dfff082f6ecbb46b8d6488841f/rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059", size = 415824 }, + { url = "https://files.pythonhosted.org/packages/94/0b/aa0542ca88ad20ea719b06520f925bae348ea5c1fdf201b7e7202d20871d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e", size = 561227 }, + { url = "https://files.pythonhosted.org/packages/0d/92/3ed77d215f82c8f844d7f98929d56cc321bb0bcfaf8f166559b8ec56e5f1/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61", size = 587424 }, + { url = "https://files.pythonhosted.org/packages/09/42/cacaeb047a22cab6241f107644f230e2935d4efecf6488859a7dd82fc47d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7", size = 555953 }, + { url = "https://files.pythonhosted.org/packages/e6/52/c921dc6d5f5d45b212a456c1f5b17df1a471127e8037eb0972379e39dff4/rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627", size = 221339 }, + { url = "https://files.pythonhosted.org/packages/f2/c7/f82b5be1e8456600395366f86104d1bd8d0faed3802ad511ef6d60c30d98/rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4", size = 235786 }, + { url = "https://files.pythonhosted.org/packages/8b/63/e29f8ee14fcf383574f73b6bbdcbec0fbc2e5fc36b4de44d1ac389b1de62/rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d", size = 360786 }, + { url = "https://files.pythonhosted.org/packages/d3/e0/771ee28b02a24e81c8c0e645796a371350a2bb6672753144f36ae2d2afc9/rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd", size = 350589 }, + { url = "https://files.pythonhosted.org/packages/cf/49/abad4c4a1e6f3adf04785a99c247bfabe55ed868133e2d1881200aa5d381/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493", size = 381848 }, + { url = "https://files.pythonhosted.org/packages/3a/7d/f4bc6d6fbe6af7a0d2b5f2ee77079efef7c8528712745659ec0026888998/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96", size = 387879 }, + { url = "https://files.pythonhosted.org/packages/13/b0/575c797377fdcd26cedbb00a3324232e4cb2c5d121f6e4b0dbf8468b12ef/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123", size = 423916 }, + { url = "https://files.pythonhosted.org/packages/54/78/87157fa39d58f32a68d3326f8a81ad8fb99f49fe2aa7ad9a1b7d544f9478/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad", size = 448410 }, + { url = "https://files.pythonhosted.org/packages/59/69/860f89996065a88be1b6ff2d60e96a02b920a262d8aadab99e7903986597/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9", size = 382841 }, + { url = "https://files.pythonhosted.org/packages/bd/d7/bc144e10d27e3cb350f98df2492a319edd3caaf52ddfe1293f37a9afbfd7/rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e", size = 409662 }, + { url = "https://files.pythonhosted.org/packages/14/2a/6bed0b05233c291a94c7e89bc76ffa1c619d4e1979fbfe5d96024020c1fb/rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338", size = 558221 }, + { url = "https://files.pythonhosted.org/packages/11/23/cd8f566de444a137bc1ee5795e47069a947e60810ba4152886fe5308e1b7/rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566", size = 583780 }, + { url = "https://files.pythonhosted.org/packages/8d/63/79c3602afd14d501f751e615a74a59040328da5ef29ed5754ae80d236b84/rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe", size = 553619 }, + { url = "https://files.pythonhosted.org/packages/9f/2e/c5c1689e80298d4e94c75b70faada4c25445739d91b94c211244a3ed7ed1/rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d", size = 233338 }, ] [[package]] @@ -2929,27 +3090,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.8.0" +version = "0.8.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/d6/a2373f3ba7180ddb44420d2a9d1f1510e1a4d162b3d27282bedcb09c8da9/ruff-0.8.0.tar.gz", hash = "sha256:a7ccfe6331bf8c8dad715753e157457faf7351c2b69f62f32c165c2dbcbacd44", size = 3276537 } +sdist = { url = "https://files.pythonhosted.org/packages/bf/5e/683c7ef7a696923223e7d95ca06755d6e2acbc5fd8382b2912a28008137c/ruff-0.8.3.tar.gz", hash = "sha256:5e7558304353b84279042fc584a4f4cb8a07ae79b2bf3da1a7551d960b5626d3", size = 3378522 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/77/e889ee3ce7fd8baa3ed1b77a03b9fb8ec1be68be1418261522fd6a5405e0/ruff-0.8.0-py3-none-linux_armv6l.whl", hash = "sha256:fcb1bf2cc6706adae9d79c8d86478677e3bbd4ced796ccad106fd4776d395fea", size = 10518283 }, - { url = "https://files.pythonhosted.org/packages/da/c8/0a47de01edf19fb22f5f9b7964f46a68d0bdff20144d134556ffd1ba9154/ruff-0.8.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:295bb4c02d58ff2ef4378a1870c20af30723013f441c9d1637a008baaf928c8b", size = 10317691 }, - { url = "https://files.pythonhosted.org/packages/41/17/9885e4a0eeae07abd2a4ebabc3246f556719f24efa477ba2739146c4635a/ruff-0.8.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7b1f1c76b47c18fa92ee78b60d2d20d7e866c55ee603e7d19c1e991fad933a9a", size = 9940999 }, - { url = "https://files.pythonhosted.org/packages/3e/cd/46b6f7043597eb318b5f5482c8ae8f5491cccce771e85f59d23106f2d179/ruff-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb0d4f250a7711b67ad513fde67e8870109e5ce590a801c3722580fe98c33a99", size = 10772437 }, - { url = "https://files.pythonhosted.org/packages/5d/87/afc95aeb8bc78b1d8a3461717a4419c05aa8aa943d4c9cbd441630f85584/ruff-0.8.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e55cce9aa93c5d0d4e3937e47b169035c7e91c8655b0974e61bb79cf398d49c", size = 10299156 }, - { url = "https://files.pythonhosted.org/packages/65/fa/04c647bb809c4d65e8eae1ed1c654d9481b21dd942e743cd33511687b9f9/ruff-0.8.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f4cd64916d8e732ce6b87f3f5296a8942d285bbbc161acee7fe561134af64f9", size = 11325819 }, - { url = "https://files.pythonhosted.org/packages/90/26/7dad6e7d833d391a8a1afe4ee70ca6f36c4a297d3cca83ef10e83e9aacf3/ruff-0.8.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c5c1466be2a2ebdf7c5450dd5d980cc87c8ba6976fb82582fea18823da6fa362", size = 12023927 }, - { url = "https://files.pythonhosted.org/packages/24/a0/be5296dda6428ba8a13bda8d09fbc0e14c810b485478733886e61597ae2b/ruff-0.8.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2dabfd05b96b7b8f2da00d53c514eea842bff83e41e1cceb08ae1966254a51df", size = 11589702 }, - { url = "https://files.pythonhosted.org/packages/26/3f/7602eb11d2886db545834182a9dbe500b8211fcbc9b4064bf9d358bbbbb4/ruff-0.8.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:facebdfe5a5af6b1588a1d26d170635ead6892d0e314477e80256ef4a8470cf3", size = 12782936 }, - { url = "https://files.pythonhosted.org/packages/4c/5d/083181bdec4ec92a431c1291d3fff65eef3ded630a4b55eb735000ef5f3b/ruff-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87a8e86bae0dbd749c815211ca11e3a7bd559b9710746c559ed63106d382bd9c", size = 11138488 }, - { url = "https://files.pythonhosted.org/packages/b7/23/c12cdef58413cee2436d6a177aa06f7a366ebbca916cf10820706f632459/ruff-0.8.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:85e654f0ded7befe2d61eeaf3d3b1e4ef3894469cd664ffa85006c7720f1e4a2", size = 10744474 }, - { url = "https://files.pythonhosted.org/packages/29/61/a12f3b81520083cd7c5caa24ba61bb99fd1060256482eff0ef04cc5ccd1b/ruff-0.8.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:83a55679c4cb449fa527b8497cadf54f076603cc36779b2170b24f704171ce70", size = 10369029 }, - { url = "https://files.pythonhosted.org/packages/08/2a/c013f4f3e4a54596c369cee74c24870ed1d534f31a35504908b1fc97017a/ruff-0.8.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:812e2052121634cf13cd6fddf0c1871d0ead1aad40a1a258753c04c18bb71bbd", size = 10867481 }, - { url = "https://files.pythonhosted.org/packages/d5/f7/685b1e1d42a3e94ceb25eab23c70bdd8c0ab66a43121ef83fe6db5a58756/ruff-0.8.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:780d5d8523c04202184405e60c98d7595bdb498c3c6abba3b6d4cdf2ca2af426", size = 11237117 }, - { url = "https://files.pythonhosted.org/packages/03/20/401132c0908e8837625e3b7e32df9962e7cd681a4df1e16a10e2a5b4ecda/ruff-0.8.0-py3-none-win32.whl", hash = "sha256:5fdb6efecc3eb60bba5819679466471fd7d13c53487df7248d6e27146e985468", size = 8783511 }, - { url = "https://files.pythonhosted.org/packages/1d/5c/4d800fca7854f62ad77f2c0d99b4b585f03e2d87a6ec1ecea85543a14a3c/ruff-0.8.0-py3-none-win_amd64.whl", hash = "sha256:582891c57b96228d146725975fbb942e1f30a0c4ba19722e692ca3eb25cc9b4f", size = 9559876 }, - { url = "https://files.pythonhosted.org/packages/5b/bc/cc8a6a5ca4960b226dc15dd8fb511dd11f2014ff89d325c0b9b9faa9871f/ruff-0.8.0-py3-none-win_arm64.whl", hash = "sha256:ba93e6294e9a737cd726b74b09a6972e36bb511f9a102f1d9a7e1ce94dd206a6", size = 8939733 }, + { url = "https://files.pythonhosted.org/packages/f8/c4/bfdbb8b9c419ff3b52479af8581026eeaac3764946fdb463dec043441b7d/ruff-0.8.3-py3-none-linux_armv6l.whl", hash = "sha256:8d5d273ffffff0acd3db5bf626d4b131aa5a5ada1276126231c4174543ce20d6", size = 10535860 }, + { url = "https://files.pythonhosted.org/packages/ef/c5/0aabdc9314b4b6f051168ac45227e2aa8e1c6d82718a547455e40c9c9faa/ruff-0.8.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e4d66a21de39f15c9757d00c50c8cdd20ac84f55684ca56def7891a025d7e939", size = 10346327 }, + { url = "https://files.pythonhosted.org/packages/1a/78/4843a59e7e7b398d6019cf91ab06502fd95397b99b2b858798fbab9151f5/ruff-0.8.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c356e770811858bd20832af696ff6c7e884701115094f427b64b25093d6d932d", size = 9942585 }, + { url = "https://files.pythonhosted.org/packages/91/5a/642ed8f1ba23ffc2dd347697e01eef3c42fad6ac76603be4a8c3a9d6311e/ruff-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c0a60a825e3e177116c84009d5ebaa90cf40dfab56e1358d1df4e29a9a14b13", size = 10797597 }, + { url = "https://files.pythonhosted.org/packages/30/25/2e654bc7226da09a49730a1a2ea6e89f843b362db80b4b2a7a4f948ac986/ruff-0.8.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fb782f4db39501210ac093c79c3de581d306624575eddd7e4e13747e61ba18", size = 10307244 }, + { url = "https://files.pythonhosted.org/packages/c0/2d/a224d56bcd4383583db53c2b8f410ebf1200866984aa6eb9b5a70f04e71f/ruff-0.8.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f26bc76a133ecb09a38b7868737eded6941b70a6d34ef53a4027e83913b6502", size = 11362439 }, + { url = "https://files.pythonhosted.org/packages/82/01/03e2857f9c371b8767d3e909f06a33bbdac880df17f17f93d6f6951c3381/ruff-0.8.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:01b14b2f72a37390c1b13477c1c02d53184f728be2f3ffc3ace5b44e9e87b90d", size = 12078538 }, + { url = "https://files.pythonhosted.org/packages/af/ae/ff7f97b355da16d748ceec50e1604a8215d3659b36b38025a922e0612e9b/ruff-0.8.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53babd6e63e31f4e96ec95ea0d962298f9f0d9cc5990a1bbb023a6baf2503a82", size = 11616172 }, + { url = "https://files.pythonhosted.org/packages/6a/d0/6156d4d1e53ebd17747049afe801c5d7e3014d9b2f398b9236fe36ba4320/ruff-0.8.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ae441ce4cf925b7f363d33cd6570c51435972d697e3e58928973994e56e1452", size = 12919886 }, + { url = "https://files.pythonhosted.org/packages/4e/84/affcb30bacb94f6036a128ad5de0e29f543d3f67ee42b490b17d68e44b8a/ruff-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7c65bc0cadce32255e93c57d57ecc2cca23149edd52714c0c5d6fa11ec328cd", size = 11212599 }, + { url = "https://files.pythonhosted.org/packages/60/b9/5694716bdefd8f73df7c0104334156c38fb0f77673d2966a5a1345bab94d/ruff-0.8.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5be450bb18f23f0edc5a4e5585c17a56ba88920d598f04a06bd9fd76d324cb20", size = 10784637 }, + { url = "https://files.pythonhosted.org/packages/24/7e/0e8f835103ac7da81c3663eedf79dec8359e9ae9a3b0d704bae50be59176/ruff-0.8.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8faeae3827eaa77f5721f09b9472a18c749139c891dbc17f45e72d8f2ca1f8fc", size = 10390591 }, + { url = "https://files.pythonhosted.org/packages/27/da/180ec771fc01c004045962ce017ca419a0281f4bfaf867ed0020f555b56e/ruff-0.8.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:db503486e1cf074b9808403991663e4277f5c664d3fe237ee0d994d1305bb060", size = 10894298 }, + { url = "https://files.pythonhosted.org/packages/6d/f8/29f241742ed3954eb2222314b02db29f531a15cab3238d1295e8657c5f18/ruff-0.8.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6567be9fb62fbd7a099209257fef4ad2c3153b60579818b31a23c886ed4147ea", size = 11275965 }, + { url = "https://files.pythonhosted.org/packages/79/e9/5b81dc9afc8a80884405b230b9429efeef76d04caead904bd213f453b973/ruff-0.8.3-py3-none-win32.whl", hash = "sha256:19048f2f878f3ee4583fc6cb23fb636e48c2635e30fb2022b3a1cd293402f964", size = 8807651 }, + { url = "https://files.pythonhosted.org/packages/ea/67/7291461066007617b59a707887b90e319b6a043c79b4d19979f86b7a20e7/ruff-0.8.3-py3-none-win_amd64.whl", hash = "sha256:f7df94f57d7418fa7c3ffb650757e0c2b96cf2501a0b192c18e4fb5571dfada9", size = 9625289 }, + { url = "https://files.pythonhosted.org/packages/03/8f/e4fa95288b81233356d9a9dcaed057e5b0adc6399aa8fd0f6d784041c9c3/ruff-0.8.3-py3-none-win_arm64.whl", hash = "sha256:fe2756edf68ea79707c8d68b78ca9a58ed9af22e430430491ee03e718b5e4936", size = 9078754 }, ] [[package]] @@ -3056,13 +3217,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, ] +[[package]] +name = "sigtools" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/db/669ca14166814da187b3087b908ca924cf83f5b504fe23b3859a3ef67d4f/sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c", size = 71910 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/91/853dbf6ec096197dba9cd5fd0c836c5fc19142038b7db60ebe6332b1bab1/sigtools-4.0.1-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c", size = 76419 }, +] + [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "smmap" +version = "5.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +sdist = { url = "https://files.pythonhosted.org/packages/88/04/b5bf6d21dc4041000ccba7eb17dd3055feb237e7ffc2c20d3fae3af62baa/smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62", size = 22291 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, + { url = "https://files.pythonhosted.org/packages/a7/a5/10f97f73544edcdef54409f1d839f6049a0d79df68adbc1ceb24d1aaca42/smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da", size = 24282 }, ] [[package]] @@ -3101,7 +3283,10 @@ anthropic = [ ] api = [ { name = "fastapi", extra = ["standard"] }, + { name = "modal" }, + { name = "streamlit" }, { name = "uvicorn" }, + { name = "websockets" }, ] cohere = [ { name = "cohere" }, @@ -3155,6 +3340,7 @@ requires-dist = [ { name = "langchain", specifier = ">=0.2.5,<0.4.0" }, { name = "langchain-openai", specifier = ">=0.1.8,<0.2" }, { name = "lxml", specifier = ">=4.9.3,<6.0.0" }, + { name = "modal", marker = "extra == 'api'" }, { name = "openai", specifier = ">=1.11.0,<2.0.0" }, { name = "pandas", marker = "extra == 'examples'" }, { name = "pettingzoo", specifier = "==1.24.3" }, @@ -3165,10 +3351,12 @@ requires-dist = [ { name = "redis-om", specifier = ">=0.3.0,<0.4.0" }, { name = "rich", specifier = ">=13.6.0,<14.0.0" }, { name = "scipy", marker = "extra == 'examples'" }, + { name = "streamlit", marker = "extra == 'api'" }, { name = "together", specifier = ">=0.2.4,<1.4.0" }, { name = "torch", marker = "extra == 'examples'" }, { name = "transformers", marker = "extra == 'examples'" }, { name = "uvicorn", marker = "extra == 'api'" }, + { name = "websockets", marker = "extra == 'api'" }, ] [package.metadata.requires-dev] @@ -3248,6 +3436,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/00/2b325970b3060c7cecebab6d295afe763365822b1306a12eeab198f74323/starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7", size = 73225 }, ] +[[package]] +name = "streamlit" +version = "1.41.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altair" }, + { name = "blinker" }, + { name = "cachetools" }, + { name = "click" }, + { name = "gitpython" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pydeck" }, + { name = "requests" }, + { name = "rich" }, + { name = "tenacity" }, + { name = "toml" }, + { name = "tornado" }, + { name = "typing-extensions" }, + { name = "watchdog", marker = "platform_system != 'Darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/33/14b5ac0369ecf0af675911e5e84b934e6fcc2cec850857d2390eb373b0a6/streamlit-1.41.1.tar.gz", hash = "sha256:6626d32b098ba1458b71eebdd634c62af2dd876380e59c4b6a1e828a39d62d69", size = 8712473 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/87/b2e162869500062a94dde7589c167367b5538dab6eacce2e7c0f00d5c9c5/streamlit-1.41.1-py2.py3-none-any.whl", hash = "sha256:0def00822480071d642e6df36cd63c089f991da3a69fd9eb4ab8f65ce27de4e0", size = 9100386 }, +] + [[package]] name = "sympy" version = "1.13.1" @@ -3260,6 +3478,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177 }, ] +[[package]] +name = "synchronicity" +version = "0.9.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sigtools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/6c/121909d2f01a48e14ca7e08a698708821db189f1181edd4394bd56571cf1/synchronicity-0.9.6.tar.gz", hash = "sha256:755c99881700256038939a39a1bbf1052b5da9bcd73524659ba686db0a340254", size = 46909 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/0c/2d51a8021cdd3585681c988205b03099f69d6a2be89ebf151e5f1cf6b1e9/synchronicity-0.9.6-py3-none-any.whl", hash = "sha256:4bcb170500c044004198f2ebbd52bd5c7c318e3862b8be3f0ab7321482babb44", size = 34390 }, +] + [[package]] name = "tabulate" version = "0.9.0" @@ -3334,65 +3565,65 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.20.3" +version = "0.21.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/25/b1681c1c30ea3ea6e584ae3fffd552430b12faa599b558c4c4783f56d7ff/tokenizers-0.20.3.tar.gz", hash = "sha256:2278b34c5d0dd78e087e1ca7f9b1dcbf129d80211afa645f214bd6e051037539", size = 340513 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/51/421bb0052fc4333f7c1e3231d8c6607552933d919b628c8fabd06f60ba1e/tokenizers-0.20.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:31ccab28dbb1a9fe539787210b0026e22debeab1662970f61c2d921f7557f7e4", size = 2674308 }, - { url = "https://files.pythonhosted.org/packages/a6/e9/f651f8d27614fd59af387f4dfa568b55207e5fac8d06eec106dc00b921c4/tokenizers-0.20.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6361191f762bda98c773da418cf511cbaa0cb8d0a1196f16f8c0119bde68ff8", size = 2559363 }, - { url = "https://files.pythonhosted.org/packages/e3/e8/0e9f81a09ab79f409eabfd99391ca519e315496694671bebca24c3e90448/tokenizers-0.20.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f128d5da1202b78fa0a10d8d938610472487da01b57098d48f7e944384362514", size = 2892896 }, - { url = "https://files.pythonhosted.org/packages/b0/72/15fdbc149e05005e99431ecd471807db2241983deafe1e704020f608f40e/tokenizers-0.20.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:79c4121a2e9433ad7ef0769b9ca1f7dd7fa4c0cd501763d0a030afcbc6384481", size = 2802785 }, - { url = "https://files.pythonhosted.org/packages/26/44/1f8aea48f9bb117d966b7272484671b33a509f6217a8e8544d79442c90db/tokenizers-0.20.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7850fde24197fe5cd6556e2fdba53a6d3bae67c531ea33a3d7c420b90904141", size = 3086060 }, - { url = "https://files.pythonhosted.org/packages/2e/83/82ba40da99870b3a0b801cffaf4f099f088a84c7e07d32cc6ca751ce08e6/tokenizers-0.20.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b357970c095dc134978a68c67d845a1e3803ab7c4fbb39195bde914e7e13cf8b", size = 3096760 }, - { url = "https://files.pythonhosted.org/packages/f3/46/7a025404201d937f86548928616c0a164308aa3998e546efdf798bf5ee9c/tokenizers-0.20.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a333d878c4970b72d6c07848b90c05f6b045cf9273fc2bc04a27211721ad6118", size = 3380165 }, - { url = "https://files.pythonhosted.org/packages/aa/49/15fae66ac62e49255eeedbb7f4127564b2c3f3aef2009913f525732d1a08/tokenizers-0.20.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd9fee817f655a8f50049f685e224828abfadd436b8ff67979fc1d054b435f1", size = 2994038 }, - { url = "https://files.pythonhosted.org/packages/f4/64/693afc9ba2393c2eed85c02bacb44762f06a29f0d1a5591fa5b40b39c0a2/tokenizers-0.20.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9e7816808b402129393a435ea2a509679b41246175d6e5e9f25b8692bfaa272b", size = 8977285 }, - { url = "https://files.pythonhosted.org/packages/be/7e/6126c18694310fe07970717929e889898767c41fbdd95b9078e8aec0f9ef/tokenizers-0.20.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba96367db9d8a730d3a1d5996b4b7babb846c3994b8ef14008cd8660f55db59d", size = 9294890 }, - { url = "https://files.pythonhosted.org/packages/71/7d/5e3307a1091c8608a1e58043dff49521bc19553c6e9548c7fac6840cc2c4/tokenizers-0.20.3-cp310-none-win32.whl", hash = "sha256:ee31ba9d7df6a98619426283e80c6359f167e2e9882d9ce1b0254937dbd32f3f", size = 2196883 }, - { url = "https://files.pythonhosted.org/packages/47/62/aaf5b2a526b3b10c20985d9568ff8c8f27159345eaef3347831e78cd5894/tokenizers-0.20.3-cp310-none-win_amd64.whl", hash = "sha256:a845c08fdad554fe0871d1255df85772f91236e5fd6b9287ef8b64f5807dbd0c", size = 2381637 }, - { url = "https://files.pythonhosted.org/packages/c6/93/6742ef9206409d5ce1fdf44d5ca1687cdc3847ba0485424e2c731e6bcf67/tokenizers-0.20.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:585b51e06ca1f4839ce7759941e66766d7b060dccfdc57c4ca1e5b9a33013a90", size = 2674224 }, - { url = "https://files.pythonhosted.org/packages/aa/14/e75ece72e99f6ef9ae07777ca9fdd78608f69466a5cecf636e9bd2f25d5c/tokenizers-0.20.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61cbf11954f3b481d08723ebd048ba4b11e582986f9be74d2c3bdd9293a4538d", size = 2558991 }, - { url = "https://files.pythonhosted.org/packages/46/54/033b5b2ba0c3ae01e026c6f7ced147d41a2fa1c573d00a66cb97f6d7f9b3/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef820880d5e4e8484e2fa54ff8d297bb32519eaa7815694dc835ace9130a3eea", size = 2892476 }, - { url = "https://files.pythonhosted.org/packages/e6/b0/cc369fb3297d61f3311cab523d16d48c869dc2f0ba32985dbf03ff811041/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:67ef4dcb8841a4988cd00dd288fb95dfc8e22ed021f01f37348fd51c2b055ba9", size = 2802775 }, - { url = "https://files.pythonhosted.org/packages/1a/74/62ad983e8ea6a63e04ed9c5be0b605056bf8aac2f0125f9b5e0b3e2b89fa/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff1ef8bd47a02b0dc191688ccb4da53600df5d4c9a05a4b68e1e3de4823e78eb", size = 3086138 }, - { url = "https://files.pythonhosted.org/packages/6b/ac/4637ba619db25094998523f9e6f5b456e1db1f8faa770a3d925d436db0c3/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:444d188186eab3148baf0615b522461b41b1f0cd58cd57b862ec94b6ac9780f1", size = 3098076 }, - { url = "https://files.pythonhosted.org/packages/58/ce/9793f2dc2ce529369807c9c74e42722b05034af411d60f5730b720388c7d/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37c04c032c1442740b2c2d925f1857885c07619224a533123ac7ea71ca5713da", size = 3379650 }, - { url = "https://files.pythonhosted.org/packages/50/f6/2841de926bc4118af996eaf0bdf0ea5b012245044766ffc0347e6c968e63/tokenizers-0.20.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453c7769d22231960ee0e883d1005c93c68015025a5e4ae56275406d94a3c907", size = 2994005 }, - { url = "https://files.pythonhosted.org/packages/a3/b2/00915c4fed08e9505d37cf6eaab45b12b4bff8f6719d459abcb9ead86a4b/tokenizers-0.20.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4bb31f7b2847e439766aaa9cc7bccf7ac7088052deccdb2275c952d96f691c6a", size = 8977488 }, - { url = "https://files.pythonhosted.org/packages/e9/ac/1c069e7808181ff57bcf2d39e9b6fbee9133a55410e6ebdaa89f67c32e83/tokenizers-0.20.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:843729bf0f991b29655a069a2ff58a4c24375a553c70955e15e37a90dd4e045c", size = 9294935 }, - { url = "https://files.pythonhosted.org/packages/50/47/722feb70ee68d1c4412b12d0ea4acc2713179fd63f054913990f9e259492/tokenizers-0.20.3-cp311-none-win32.whl", hash = "sha256:efcce3a927b1e20ca694ba13f7a68c59b0bd859ef71e441db68ee42cf20c2442", size = 2197175 }, - { url = "https://files.pythonhosted.org/packages/75/68/1b4f928b15a36ed278332ac75d66d7eb65d865bf344d049c452c18447bf9/tokenizers-0.20.3-cp311-none-win_amd64.whl", hash = "sha256:88301aa0801f225725b6df5dea3d77c80365ff2362ca7e252583f2b4809c4cc0", size = 2381616 }, - { url = "https://files.pythonhosted.org/packages/07/00/92a08af2a6b0c88c50f1ab47d7189e695722ad9714b0ee78ea5e1e2e1def/tokenizers-0.20.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:49d12a32e190fad0e79e5bdb788d05da2f20d8e006b13a70859ac47fecf6ab2f", size = 2667951 }, - { url = "https://files.pythonhosted.org/packages/ec/9a/e17a352f0bffbf415cf7d73756f5c73a3219225fc5957bc2f39d52c61684/tokenizers-0.20.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:282848cacfb9c06d5e51489f38ec5aa0b3cd1e247a023061945f71f41d949d73", size = 2555167 }, - { url = "https://files.pythonhosted.org/packages/27/37/d108df55daf4f0fcf1f58554692ff71687c273d870a34693066f0847be96/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abe4e08c7d0cd6154c795deb5bf81d2122f36daf075e0c12a8b050d824ef0a64", size = 2898389 }, - { url = "https://files.pythonhosted.org/packages/b2/27/32f29da16d28f59472fa7fb38e7782069748c7e9ab9854522db20341624c/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca94fc1b73b3883c98f0c88c77700b13d55b49f1071dfd57df2b06f3ff7afd64", size = 2795866 }, - { url = "https://files.pythonhosted.org/packages/29/4e/8a9a3c89e128c4a40f247b501c10279d2d7ade685953407c4d94c8c0f7a7/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef279c7e239f95c8bdd6ff319d9870f30f0d24915b04895f55b1adcf96d6c60d", size = 3085446 }, - { url = "https://files.pythonhosted.org/packages/b4/3b/a2a7962c496ebcd95860ca99e423254f760f382cd4bd376f8895783afaf5/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16384073973f6ccbde9852157a4fdfe632bb65208139c9d0c0bd0176a71fd67f", size = 3094378 }, - { url = "https://files.pythonhosted.org/packages/1f/f4/a8a33f0192a1629a3bd0afcad17d4d221bbf9276da4b95d226364208d5eb/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:312d522caeb8a1a42ebdec87118d99b22667782b67898a76c963c058a7e41d4f", size = 3385755 }, - { url = "https://files.pythonhosted.org/packages/9e/65/c83cb3545a65a9eaa2e13b22c93d5e00bd7624b354a44adbdc93d5d9bd91/tokenizers-0.20.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2b7cb962564785a83dafbba0144ecb7f579f1d57d8c406cdaa7f32fe32f18ad", size = 2997679 }, - { url = "https://files.pythonhosted.org/packages/55/e9/a80d4e592307688a67c7c59ab77e03687b6a8bd92eb5db763a2c80f93f57/tokenizers-0.20.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:124c5882ebb88dadae1fc788a582299fcd3a8bd84fc3e260b9918cf28b8751f5", size = 8989296 }, - { url = "https://files.pythonhosted.org/packages/90/af/60c957af8d2244321124e893828f1a4817cde1a2d08d09d423b73f19bd2f/tokenizers-0.20.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2b6e54e71f84c4202111a489879005cb14b92616a87417f6c102c833af961ea2", size = 9303621 }, - { url = "https://files.pythonhosted.org/packages/be/a9/96172310ee141009646d63a1ca267c099c462d747fe5ef7e33f74e27a683/tokenizers-0.20.3-cp312-none-win32.whl", hash = "sha256:83d9bfbe9af86f2d9df4833c22e94d94750f1d0cd9bfb22a7bb90a86f61cdb1c", size = 2188979 }, - { url = "https://files.pythonhosted.org/packages/bd/68/61d85ae7ae96dde7d0974ff3538db75d5cdc29be2e4329cd7fc51a283e22/tokenizers-0.20.3-cp312-none-win_amd64.whl", hash = "sha256:44def74cee574d609a36e17c8914311d1b5dbcfe37c55fd29369d42591b91cf2", size = 2380725 }, - { url = "https://files.pythonhosted.org/packages/29/cd/ff1586dd572aaf1637d59968df3f6f6532fa255f4638fbc29f6d27e0b690/tokenizers-0.20.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e919f2e3e68bb51dc31de4fcbbeff3bdf9c1cad489044c75e2b982a91059bd3c", size = 2672044 }, - { url = "https://files.pythonhosted.org/packages/b5/9e/7a2c00abbc8edb021ee0b1f12aab76a7b7824b49f94bcd9f075d0818d4b0/tokenizers-0.20.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b8e9608f2773996cc272156e305bd79066163a66b0390fe21750aff62df1ac07", size = 2558841 }, - { url = "https://files.pythonhosted.org/packages/8e/c1/6af62ef61316f33ecf785bbb2bee4292f34ea62b491d4480ad9b09acf6b6/tokenizers-0.20.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39270a7050deaf50f7caff4c532c01b3c48f6608d42b3eacdebdc6795478c8df", size = 2897936 }, - { url = "https://files.pythonhosted.org/packages/9a/0b/c076b2ff3ee6dc70c805181fbe325668b89cfee856f8dfa24cc9aa293c84/tokenizers-0.20.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e005466632b1c5d2d2120f6de8aa768cc9d36cd1ab7d51d0c27a114c91a1e6ee", size = 3082688 }, - { url = "https://files.pythonhosted.org/packages/0a/60/56510124933136c2e90879e1c81603cfa753ae5a87830e3ef95056b20d8f/tokenizers-0.20.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a07962340b36189b6c8feda552ea1bfeee6cf067ff922a1d7760662c2ee229e5", size = 2998924 }, - { url = "https://files.pythonhosted.org/packages/68/60/4107b618b7b9155cb34ad2e0fc90946b7e71f041b642122fb6314f660688/tokenizers-0.20.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:55046ad3dd5f2b3c67501fcc8c9cbe3e901d8355f08a3b745e9b57894855f85b", size = 8989514 }, - { url = "https://files.pythonhosted.org/packages/e8/bd/48475818e614b73316baf37ac1e4e51b578bbdf58651812d7e55f43b88d8/tokenizers-0.20.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:efcf0eb939988b627558aaf2b9dc3e56d759cad2e0cfa04fcab378e4b48fc4fd", size = 9303476 }, +sdist = { url = "https://files.pythonhosted.org/packages/20/41/c2be10975ca37f6ec40d7abd7e98a5213bb04f284b869c1a24e6504fd94d/tokenizers-0.21.0.tar.gz", hash = "sha256:ee0894bf311b75b0c03079f33859ae4b2334d675d4e93f5a4132e1eae2834fe4", size = 343021 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/5c/8b09607b37e996dc47e70d6a7b6f4bdd4e4d5ab22fe49d7374565c7fefaf/tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2", size = 2647461 }, + { url = "https://files.pythonhosted.org/packages/22/7a/88e58bb297c22633ed1c9d16029316e5b5ac5ee44012164c2edede599a5e/tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e", size = 2563639 }, + { url = "https://files.pythonhosted.org/packages/f7/14/83429177c19364df27d22bc096d4c2e431e0ba43e56c525434f1f9b0fd00/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b177fb54c4702ef611de0c069d9169f0004233890e0c4c5bd5508ae05abf193", size = 2903304 }, + { url = "https://files.pythonhosted.org/packages/7e/db/3433eab42347e0dc5452d8fcc8da03f638c9accffefe5a7c78146666964a/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b43779a269f4629bebb114e19c3fca0223296ae9fea8bb9a7a6c6fb0657ff8e", size = 2804378 }, + { url = "https://files.pythonhosted.org/packages/57/8b/7da5e6f89736c2ade02816b4733983fca1c226b0c42980b1ae9dc8fcf5cc/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aeb255802be90acfd363626753fda0064a8df06031012fe7d52fd9a905eb00e", size = 3095488 }, + { url = "https://files.pythonhosted.org/packages/4d/f6/5ed6711093dc2c04a4e03f6461798b12669bc5a17c8be7cce1240e0b5ce8/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b09dbeb7a8d73ee204a70f94fc06ea0f17dcf0844f16102b9f414f0b7463ba", size = 3121410 }, + { url = "https://files.pythonhosted.org/packages/81/42/07600892d48950c5e80505b81411044a2d969368cdc0d929b1c847bf6697/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:400832c0904f77ce87c40f1a8a27493071282f785724ae62144324f171377273", size = 3388821 }, + { url = "https://files.pythonhosted.org/packages/22/06/69d7ce374747edaf1695a4f61b83570d91cc8bbfc51ccfecf76f56ab4aac/tokenizers-0.21.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84ca973b3a96894d1707e189c14a774b701596d579ffc7e69debfc036a61a04", size = 3008868 }, + { url = "https://files.pythonhosted.org/packages/c8/69/54a0aee4d576045b49a0eb8bffdc495634309c823bf886042e6f46b80058/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eb7202d231b273c34ec67767378cd04c767e967fda12d4a9e36208a34e2f137e", size = 8975831 }, + { url = "https://files.pythonhosted.org/packages/f7/f3/b776061e4f3ebf2905ba1a25d90380aafd10c02d406437a8ba22d1724d76/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:089d56db6782a73a27fd8abf3ba21779f5b85d4a9f35e3b493c7bbcbbf0d539b", size = 8920746 }, + { url = "https://files.pythonhosted.org/packages/d8/ee/ce83d5ec8b6844ad4c3ecfe3333d58ecc1adc61f0878b323a15355bcab24/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c87ca3dc48b9b1222d984b6b7490355a6fdb411a2d810f6f05977258400ddb74", size = 9161814 }, + { url = "https://files.pythonhosted.org/packages/18/07/3e88e65c0ed28fa93aa0c4d264988428eef3df2764c3126dc83e243cb36f/tokenizers-0.21.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4145505a973116f91bc3ac45988a92e618a6f83eb458f49ea0790df94ee243ff", size = 9357138 }, + { url = "https://files.pythonhosted.org/packages/15/b0/dc4572ca61555fc482ebc933f26cb407c6aceb3dc19c301c68184f8cad03/tokenizers-0.21.0-cp39-abi3-win32.whl", hash = "sha256:eb1702c2f27d25d9dd5b389cc1f2f51813e99f8ca30d9e25348db6585a97e24a", size = 2202266 }, + { url = "https://files.pythonhosted.org/packages/44/69/d21eb253fa91622da25585d362a874fa4710be600f0ea9446d8d0217cec1/tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c", size = 2389192 }, ] [[package]] -name = "tomli" -version = "2.1.0" +name = "toml" +version = "0.10.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/e4/1b6cbcc82d8832dd0ce34767d5c560df8a3547ad8cbc427f34601415930a/tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8", size = 16622 } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/f7/4da0ffe1892122c9ea096c57f64c2753ae5dd3ce85488802d11b0992cc6d/tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391", size = 13750 }, + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, ] [[package]] @@ -3465,14 +3696,14 @@ wheels = [ [[package]] name = "tqdm" -version = "4.67.0" +version = "4.67.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "platform_system == 'Windows'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/4f/0153c21dc5779a49a0598c445b1978126b1344bab9ee71e53e44877e14e0/tqdm-4.67.0.tar.gz", hash = "sha256:fe5a6f95e6fe0b9755e9469b77b9c3cf850048224ecaa8293d7d2d31f97d869a", size = 169739 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/78/57043611a16c655c8350b4c01b8d6abfb38cc2acb475238b62c2146186d7/tqdm-4.67.0-py3-none-any.whl", hash = "sha256:0cd8af9d56911acab92182e88d763100d4788bdf421d251616040cc4d44863be", size = 78590 }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, ] [[package]] @@ -3486,7 +3717,7 @@ wheels = [ [[package]] name = "transformers" -version = "4.46.3" +version = "4.47.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -3500,9 +3731,9 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/5a/58f96c83e566f907ae39f16d4401bbefd8bb85c60bd1e6a95c419752ab90/transformers-4.46.3.tar.gz", hash = "sha256:8ee4b3ae943fe33e82afff8e837f4b052058b07ca9be3cb5b729ed31295f72cc", size = 8627944 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/5a/0ecfde3264bed0579c37f249e04e15f3c1451ba864d78bbe390177664cac/transformers-4.47.0.tar.gz", hash = "sha256:f8ead7a5a4f6937bb507e66508e5e002dc5930f7b6122a9259c37b099d0f3b19", size = 8693668 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/51/b87caa939fedf307496e4dbf412f4b909af3d9ca8b189fc3b65c1faa456f/transformers-4.46.3-py3-none-any.whl", hash = "sha256:a12ef6f52841fd190a3e5602145b542d03507222f2c64ebb7ee92e8788093aef", size = 10034536 }, + { url = "https://files.pythonhosted.org/packages/d0/a7/7eedcf6a359e1e1eff3bc204ad022485aa5d88c08e1e3e0e0aee8a2e2235/transformers-4.47.0-py3-none-any.whl", hash = "sha256:a8e1bafdaae69abdda3cad638fe392e37c86d2ce0ecfcae11d60abb8f949ff4d", size = 10133426 }, ] [[package]] @@ -3533,6 +3764,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/69/e90a0b4d0c16e095901679216c8ecdc728110c7c54e7b5f43a623bc4c789/typer-0.13.1-py3-none-any.whl", hash = "sha256:5b59580fd925e89463a29d363e0a43245ec02765bde9fb77d39e5d0f29dd7157", size = 44723 }, ] +[[package]] +name = "types-certifi" +version = "2021.10.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136 }, +] + [[package]] name = "types-cffi" version = "1.16.0.20240331" @@ -3603,11 +3843,20 @@ wheels = [ [[package]] name = "types-setuptools" -version = "75.5.0.20241122" +version = "75.6.0.20241126" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/98/de2e82c19552d10901ded893dc182ccfbd87bc7e5a32e90644d45010218b/types_setuptools-75.5.0.20241122.tar.gz", hash = "sha256:196aaf1811cbc1c77ac1d4c4879d5308b6fdf426e56b73baadbca2a1827dadef", size = 48541 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/d2/15ede73bc3faf647af2c7bfefa90dde563a4b6bb580b1199f6255463c272/types_setuptools-75.6.0.20241126.tar.gz", hash = "sha256:7bf25ad4be39740e469f9268b6beddda6e088891fa5a27e985c6ce68bf62ace0", size = 48569 } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/98/5690400593952e3a77d9749416f75faf48af999291c615164062a40293be/types_setuptools-75.5.0.20241122-py3-none-any.whl", hash = "sha256:d69c445f7bdd5e49d1b2441aadcee1388febcc9ad9d9d5fd33648b555e0b1c31", size = 72739 }, + { url = "https://files.pythonhosted.org/packages/3b/a0/898a1363592d372d4103b76b7c723d84fcbde5fa4ed0c3a29102805ed7db/types_setuptools-75.6.0.20241126-py3-none-any.whl", hash = "sha256:aaae310a0e27033c1da8457d4d26ac673b0c8a0de7272d6d4708e263f2ea3b9b", size = 72732 }, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 }, ] [[package]] @@ -3660,16 +3909,16 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.32.1" +version = "0.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/3c/21dba3e7d76138725ef307e3d7ddd29b763119b3aa459d02cc05fefcff75/uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175", size = 77630 } +sdist = { url = "https://files.pythonhosted.org/packages/cb/81/a083ae41716b00df56d45d4b5f6ca8e90fc233a62e6c04ab3ad3c476b6c4/uvicorn-0.33.0.tar.gz", hash = "sha256:3577119f82b7091cf4d3d4177bfda0bae4723ed92ab1439e8d779de880c9cc59", size = 76590 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/c1/2d27b0a15826c2b71dcf6e2f5402181ef85acf439617bb2f1453125ce1f3/uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e", size = 63828 }, + { url = "https://files.pythonhosted.org/packages/98/79/2e2620337ef1e4ef7a058b351603b765f59ac28e6e3ac7c5e7cdee9ea1ab/uvicorn-0.33.0-py3-none-any.whl", hash = "sha256:2c30de4aeea83661a520abab179b24084a0019c0c1bbe137e5409f741cbde5f8", size = 62297 }, ] [package.optional-dependencies] @@ -3711,69 +3960,87 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.27.1" +version = "20.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/b3/7b6a79c5c8cf6d90ea681310e169cf2db2884f4d583d16c6e1d5a75a4e04/virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba", size = 6491145 } +sdist = { url = "https://files.pythonhosted.org/packages/bf/75/53316a5a8050069228a2f6d11f32046cfa94fbb6cc3f08703f59b873de2e/virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa", size = 7650368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/f9/0919cf6f1432a8c4baa62511f8f8da8225432d22e83e3476f5be1a1edc6e/virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0", size = 4276702 }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/92/78324ff89391e00c8f4cf6b8526c41c6ef36b4ea2d2c132250b1a6fc2b8d/virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4", size = 3117838 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, ] [[package]] name = "watchfiles" -version = "0.24.0" +version = "1.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c8/27/2ba23c8cc85796e2d41976439b08d52f691655fdb9401362099502d1f0cf/watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1", size = 37870 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/a1/631c12626378b9f1538664aa221feb5c60dfafbd7f60b451f8d0bdbcdedd/watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0", size = 375096 }, - { url = "https://files.pythonhosted.org/packages/f7/5c/f27c979c8a10aaa2822286c1bffdce3db731cd1aa4224b9f86623e94bbfe/watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c", size = 367425 }, - { url = "https://files.pythonhosted.org/packages/74/0d/1889e5649885484d29f6c792ef274454d0a26b20d6ed5fdba5409335ccb6/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82ae557a8c037c42a6ef26c494d0631cacca040934b101d001100ed93d43f361", size = 437705 }, - { url = "https://files.pythonhosted.org/packages/85/8a/01d9a22e839f0d1d547af11b1fcac6ba6f889513f1b2e6f221d9d60d9585/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acbfa31e315a8f14fe33e3542cbcafc55703b8f5dcbb7c1eecd30f141df50db3", size = 433636 }, - { url = "https://files.pythonhosted.org/packages/62/32/a93db78d340c7ef86cde469deb20e36c6b2a873edee81f610e94bbba4e06/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74fdffce9dfcf2dc296dec8743e5b0332d15df19ae464f0e249aa871fc1c571", size = 451069 }, - { url = "https://files.pythonhosted.org/packages/99/c2/e9e2754fae3c2721c9a7736f92dab73723f1968ed72535fff29e70776008/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:449f43f49c8ddca87c6b3980c9284cab6bd1f5c9d9a2b00012adaaccd5e7decd", size = 469306 }, - { url = "https://files.pythonhosted.org/packages/4c/45/f317d9e3affb06c3c27c478de99f7110143e87f0f001f0f72e18d0e1ddce/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf4ad269856618f82dee296ac66b0cd1d71450fc3c98532d93798e73399b7a", size = 476187 }, - { url = "https://files.pythonhosted.org/packages/ac/d3/f1f37248abe0114916921e638f71c7d21fe77e3f2f61750e8057d0b68ef2/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f895d785eb6164678ff4bb5cc60c5996b3ee6df3edb28dcdeba86a13ea0465e", size = 425743 }, - { url = "https://files.pythonhosted.org/packages/2b/e8/c7037ea38d838fd81a59cd25761f106ee3ef2cfd3261787bee0c68908171/watchfiles-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ae3e208b31be8ce7f4c2c0034f33406dd24fbce3467f77223d10cd86778471c", size = 612327 }, - { url = "https://files.pythonhosted.org/packages/a0/c5/0e6e228aafe01a7995fbfd2a4edb221bb11a2744803b65a5663fb85e5063/watchfiles-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2efec17819b0046dde35d13fb8ac7a3ad877af41ae4640f4109d9154ed30a188", size = 595096 }, - { url = "https://files.pythonhosted.org/packages/63/d5/4780e8bf3de3b4b46e7428a29654f7dc041cad6b19fd86d083e4b6f64bbe/watchfiles-0.24.0-cp310-none-win32.whl", hash = "sha256:6bdcfa3cd6fdbdd1a068a52820f46a815401cbc2cb187dd006cb076675e7b735", size = 264149 }, - { url = "https://files.pythonhosted.org/packages/fe/1b/5148898ba55fc9c111a2a4a5fb67ad3fa7eb2b3d7f0618241ed88749313d/watchfiles-0.24.0-cp310-none-win_amd64.whl", hash = "sha256:54ca90a9ae6597ae6dc00e7ed0a040ef723f84ec517d3e7ce13e63e4bc82fa04", size = 277542 }, - { url = "https://files.pythonhosted.org/packages/85/02/366ae902cd81ca5befcd1854b5c7477b378f68861597cef854bd6dc69fbe/watchfiles-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bdcd5538e27f188dd3c804b4a8d5f52a7fc7f87e7fd6b374b8e36a4ca03db428", size = 375579 }, - { url = "https://files.pythonhosted.org/packages/bc/67/d8c9d256791fe312fea118a8a051411337c948101a24586e2df237507976/watchfiles-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2dadf8a8014fde6addfd3c379e6ed1a981c8f0a48292d662e27cabfe4239c83c", size = 367726 }, - { url = "https://files.pythonhosted.org/packages/b1/dc/a8427b21ef46386adf824a9fec4be9d16a475b850616cfd98cf09a97a2ef/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6509ed3f467b79d95fc62a98229f79b1a60d1b93f101e1c61d10c95a46a84f43", size = 437735 }, - { url = "https://files.pythonhosted.org/packages/3a/21/0b20bef581a9fbfef290a822c8be645432ceb05fb0741bf3c032e0d90d9a/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8360f7314a070c30e4c976b183d1d8d1585a4a50c5cb603f431cebcbb4f66327", size = 433644 }, - { url = "https://files.pythonhosted.org/packages/1c/e8/d5e5f71cc443c85a72e70b24269a30e529227986096abe091040d6358ea9/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:316449aefacf40147a9efaf3bd7c9bdd35aaba9ac5d708bd1eb5763c9a02bef5", size = 450928 }, - { url = "https://files.pythonhosted.org/packages/61/ee/bf17f5a370c2fcff49e1fec987a6a43fd798d8427ea754ce45b38f9e117a/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bde715f940bea845a95247ea3e5eb17769ba1010efdc938ffcb967c634fa61", size = 469072 }, - { url = "https://files.pythonhosted.org/packages/a3/34/03b66d425986de3fc6077e74a74c78da298f8cb598887f664a4485e55543/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3770e260b18e7f4e576edca4c0a639f704088602e0bc921c5c2e721e3acb8d15", size = 475517 }, - { url = "https://files.pythonhosted.org/packages/70/eb/82f089c4f44b3171ad87a1b433abb4696f18eb67292909630d886e073abe/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0fd7248cf533c259e59dc593a60973a73e881162b1a2f73360547132742823", size = 425480 }, - { url = "https://files.pythonhosted.org/packages/53/20/20509c8f5291e14e8a13104b1808cd7cf5c44acd5feaecb427a49d387774/watchfiles-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7a2e3b7f5703ffbd500dabdefcbc9eafeff4b9444bbdd5d83d79eedf8428fab", size = 612322 }, - { url = "https://files.pythonhosted.org/packages/df/2b/5f65014a8cecc0a120f5587722068a975a692cadbe9fe4ea56b3d8e43f14/watchfiles-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d831ee0a50946d24a53821819b2327d5751b0c938b12c0653ea5be7dea9c82ec", size = 595094 }, - { url = "https://files.pythonhosted.org/packages/18/98/006d8043a82c0a09d282d669c88e587b3a05cabdd7f4900e402250a249ac/watchfiles-0.24.0-cp311-none-win32.whl", hash = "sha256:49d617df841a63b4445790a254013aea2120357ccacbed00253f9c2b5dc24e2d", size = 264191 }, - { url = "https://files.pythonhosted.org/packages/8a/8b/badd9247d6ec25f5f634a9b3d0d92e39c045824ec7e8afcedca8ee52c1e2/watchfiles-0.24.0-cp311-none-win_amd64.whl", hash = "sha256:d3dcb774e3568477275cc76554b5a565024b8ba3a0322f77c246bc7111c5bb9c", size = 277527 }, - { url = "https://files.pythonhosted.org/packages/af/19/35c957c84ee69d904299a38bae3614f7cede45f07f174f6d5a2f4dbd6033/watchfiles-0.24.0-cp311-none-win_arm64.whl", hash = "sha256:9301c689051a4857d5b10777da23fafb8e8e921bcf3abe6448a058d27fb67633", size = 266253 }, - { url = "https://files.pythonhosted.org/packages/35/82/92a7bb6dc82d183e304a5f84ae5437b59ee72d48cee805a9adda2488b237/watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a", size = 374137 }, - { url = "https://files.pythonhosted.org/packages/87/91/49e9a497ddaf4da5e3802d51ed67ff33024597c28f652b8ab1e7c0f5718b/watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370", size = 367733 }, - { url = "https://files.pythonhosted.org/packages/0d/d8/90eb950ab4998effea2df4cf3a705dc594f6bc501c5a353073aa990be965/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6", size = 437322 }, - { url = "https://files.pythonhosted.org/packages/6c/a2/300b22e7bc2a222dd91fce121cefa7b49aa0d26a627b2777e7bdfcf1110b/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b", size = 433409 }, - { url = "https://files.pythonhosted.org/packages/99/44/27d7708a43538ed6c26708bcccdde757da8b7efb93f4871d4cc39cffa1cc/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e", size = 452142 }, - { url = "https://files.pythonhosted.org/packages/b0/ec/c4e04f755be003129a2c5f3520d2c47026f00da5ecb9ef1e4f9449637571/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea", size = 469414 }, - { url = "https://files.pythonhosted.org/packages/c5/4e/cdd7de3e7ac6432b0abf282ec4c1a1a2ec62dfe423cf269b86861667752d/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f", size = 472962 }, - { url = "https://files.pythonhosted.org/packages/27/69/e1da9d34da7fc59db358424f5d89a56aaafe09f6961b64e36457a80a7194/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234", size = 425705 }, - { url = "https://files.pythonhosted.org/packages/e8/c1/24d0f7357be89be4a43e0a656259676ea3d7a074901f47022f32e2957798/watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef", size = 612851 }, - { url = "https://files.pythonhosted.org/packages/c7/af/175ba9b268dec56f821639c9893b506c69fd999fe6a2e2c51de420eb2f01/watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968", size = 594868 }, - { url = "https://files.pythonhosted.org/packages/44/81/1f701323a9f70805bc81c74c990137123344a80ea23ab9504a99492907f8/watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444", size = 264109 }, - { url = "https://files.pythonhosted.org/packages/b4/0b/32cde5bc2ebd9f351be326837c61bdeb05ad652b793f25c91cac0b48a60b/watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896", size = 277055 }, - { url = "https://files.pythonhosted.org/packages/4b/81/daade76ce33d21dbec7a15afd7479de8db786e5f7b7d249263b4ea174e08/watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418", size = 266169 }, - { url = "https://files.pythonhosted.org/packages/df/94/1ad200e937ec91b2a9d6b39ae1cf9c2b1a9cc88d5ceb43aa5c6962eb3c11/watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f", size = 376986 }, - { url = "https://files.pythonhosted.org/packages/ee/fd/d9e020d687ccf90fe95efc513fbb39a8049cf5a3ff51f53c59fcf4c47a5d/watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b", size = 369445 }, - { url = "https://files.pythonhosted.org/packages/43/cb/c0279b35053555d10ef03559c5aebfcb0c703d9c70a7b4e532df74b9b0e8/watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4", size = 439383 }, - { url = "https://files.pythonhosted.org/packages/8b/c4/08b3c2cda45db5169148a981c2100c744a4a222fa7ae7644937c0c002069/watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a", size = 426804 }, +sdist = { url = "https://files.pythonhosted.org/packages/3c/7e/4569184ea04b501840771b8fcecee19b2233a8b72c196061263c0ef23c0b/watchfiles-1.0.3.tar.gz", hash = "sha256:f3ff7da165c99a5412fe5dd2304dd2dbaaaa5da718aad942dcb3a178eaa70c56", size = 38185 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/6c/7be04641c81209ea281b83b1174aa9d5ba53bec2a896d75a6b10428b4063/watchfiles-1.0.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1da46bb1eefb5a37a8fb6fd52ad5d14822d67c498d99bda8754222396164ae42", size = 395213 }, + { url = "https://files.pythonhosted.org/packages/bd/d6/99438baa225891bda882adefefc14c9023ef3cdaf9772cd47973bb566e96/watchfiles-1.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2b961b86cd3973f5822826017cad7f5a75795168cb645c3a6b30c349094e02e3", size = 384755 }, + { url = "https://files.pythonhosted.org/packages/88/93/b10295ce8696e5e37f480ba4ae89e387e88ba425d72808c87d30f4cdefb1/watchfiles-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34e87c7b3464d02af87f1059fedda5484e43b153ef519e4085fe1a03dd94801e", size = 441701 }, + { url = "https://files.pythonhosted.org/packages/c5/3a/0359b7bddb1b7cbe6fb7096805b6e2f859f0de3d6130dcab9ac635db87e2/watchfiles-1.0.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d9dd2b89a16cf7ab9c1170b5863e68de6bf83db51544875b25a5f05a7269e678", size = 447540 }, + { url = "https://files.pythonhosted.org/packages/e2/a7/3400b4f105c68804495b76398165ffe6c00af93eab395279285f43cd0e42/watchfiles-1.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b4691234d31686dca133c920f94e478b548a8e7c750f28dbbc2e4333e0d3da9", size = 472467 }, + { url = "https://files.pythonhosted.org/packages/c3/1a/8f928800d038d4fdb1e9df6e0c380c8cee17e6fb180e1faceb3f94de6df7/watchfiles-1.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90b0fe1fcea9bd6e3084b44875e179b4adcc4057a3b81402658d0eb58c98edf8", size = 494467 }, + { url = "https://files.pythonhosted.org/packages/13/70/af75edf5b763f09e31a0f19ce045f3731db22599cb521807760b7d82b196/watchfiles-1.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b90651b4cf9e158d01faa0833b073e2e37719264bcee3eac49fc3c74e7d304b", size = 492671 }, + { url = "https://files.pythonhosted.org/packages/4a/6e/8723f4b0967cc8d94f33fc531c33d66b596090b024f449983d3a8d97cfca/watchfiles-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2e9fe695ff151b42ab06501820f40d01310fbd58ba24da8923ace79cf6d702d", size = 443811 }, + { url = "https://files.pythonhosted.org/packages/ee/5d/f3ca68a71d978d43168a65a1b4e1f72290c5350379aa148917e4ed0b2c46/watchfiles-1.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62691f1c0894b001c7cde1195c03b7801aaa794a837bd6eef24da87d1542838d", size = 615477 }, + { url = "https://files.pythonhosted.org/packages/0d/d0/3d27a26f276ef07ca4cd3c6766684444317ddd147943e00bdb157cfdf3c3/watchfiles-1.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:275c1b0e942d335fccb6014d79267d1b9fa45b5ac0639c297f1e856f2f532552", size = 614237 }, + { url = "https://files.pythonhosted.org/packages/97/e9/ff30b210099d75cfa407924b3c265d3054f14b83ddf02072bd637394aab6/watchfiles-1.0.3-cp310-cp310-win32.whl", hash = "sha256:06ce08549e49ba69ccc36fc5659a3d0ff4e3a07d542b895b8a9013fcab46c2dc", size = 270798 }, + { url = "https://files.pythonhosted.org/packages/ed/86/694f07eb91d3e81a359661b48ff6984543e50be767c50c08196155d417bf/watchfiles-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f280b02827adc9d87f764972fbeb701cf5611f80b619c20568e1982a277d6146", size = 284192 }, + { url = "https://files.pythonhosted.org/packages/24/a8/06e2d5f840b285718a09be7c71ea19b7177b005cec87b8923dd7e8541b20/watchfiles-1.0.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ffe709b1d0bc2e9921257569675674cafb3a5f8af689ab9f3f2b3f88775b960f", size = 394821 }, + { url = "https://files.pythonhosted.org/packages/57/9f/f98a57ada3d4b1fcd0e325aa6c307e2248ecb048f71c96fba34a602f02e7/watchfiles-1.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:418c5ce332f74939ff60691e5293e27c206c8164ce2b8ce0d9abf013003fb7fe", size = 384898 }, + { url = "https://files.pythonhosted.org/packages/a3/31/33ba914010cbfd01033ca3727aff6585b6b2ea2b051b6fbaecdf4e2160b9/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f492d2907263d6d0d52f897a68647195bc093dafed14508a8d6817973586b6b", size = 441710 }, + { url = "https://files.pythonhosted.org/packages/d9/dd/e56b2ef07c2c34e4152950f0ce98a1081215ef027cf39e5dab61a0f8bd95/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48c9f3bc90c556a854f4cab6a79c16974099ccfa3e3e150673d82d47a4bc92c9", size = 447681 }, + { url = "https://files.pythonhosted.org/packages/60/8f/3837df33f3d0cbef8ae59559891d688490bf2960373ea077ff11cbf79115/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75d3bcfa90454dba8df12adc86b13b6d85fda97d90e708efc036c2760cc6ba44", size = 472312 }, + { url = "https://files.pythonhosted.org/packages/5a/b3/95d103e5bb609b20f175e8acdf8b32c4b091f96f781c066fd3bff2b17778/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5691340f259b8f76b45fb31b98e594d46c36d1dc8285efa7975f7f50230c9093", size = 494779 }, + { url = "https://files.pythonhosted.org/packages/4f/f0/9fdc60daf5abf7b0deb225c9b0a37fd72dc407fa33f071ae2f70e84e268c/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e263cc718545b7f897baeac1f00299ab6fabe3e18caaacacb0edf6d5f35513c", size = 492090 }, + { url = "https://files.pythonhosted.org/packages/96/e5/a9967e77f173280ab1abbfd7ead90f2b94060574968baf5e6d7cbe9dd490/watchfiles-1.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6cf7709ed3e55704cc06f6e835bf43c03bc8e3cb8ff946bf69a2e0a78d9d77", size = 443713 }, + { url = "https://files.pythonhosted.org/packages/60/38/e5390d4633a558878113e45d32e39d30cf58eb94e0359f41737be209321b/watchfiles-1.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:703aa5e50e465be901e0e0f9d5739add15e696d8c26c53bc6fc00eb65d7b9469", size = 615306 }, + { url = "https://files.pythonhosted.org/packages/5c/27/8a1ee74544c93e3242ca073087b45c64367aeb6897b622e43c8172c2b421/watchfiles-1.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bfcae6aecd9e0cb425f5145afee871465b98b75862e038d42fe91fd753ddd780", size = 614333 }, + { url = "https://files.pythonhosted.org/packages/fc/f8/25698f5b734907662b50acf3e81996053abdfe26fcf38804d028412876a8/watchfiles-1.0.3-cp311-cp311-win32.whl", hash = "sha256:6a76494d2c5311584f22416c5a87c1e2cb954ff9b5f0988027bc4ef2a8a67181", size = 270987 }, + { url = "https://files.pythonhosted.org/packages/39/78/f600dee7b387e6088c8d1f4c898a4340d07aecfe6406bd90ec4c1925ef08/watchfiles-1.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:cf745cbfad6389c0e331786e5fe9ae3f06e9d9c2ce2432378e1267954793975c", size = 284098 }, + { url = "https://files.pythonhosted.org/packages/ca/6f/27ba8aec0a4b45a6063454465eefb42777158081d9df18eab5f1d6a3bd8a/watchfiles-1.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:2dcc3f60c445f8ce14156854a072ceb36b83807ed803d37fdea2a50e898635d6", size = 276804 }, + { url = "https://files.pythonhosted.org/packages/bf/a9/c8b5ab33444306e1a324cb2b51644f8458dd459e30c3841f925012893e6a/watchfiles-1.0.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:93436ed550e429da007fbafb723e0769f25bae178fbb287a94cb4ccdf42d3af3", size = 391395 }, + { url = "https://files.pythonhosted.org/packages/ad/d3/403af5f07359863c03951796ddab265ee8cce1a6147510203d0bf43950e7/watchfiles-1.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c18f3502ad0737813c7dad70e3e1cc966cc147fbaeef47a09463bbffe70b0a00", size = 381432 }, + { url = "https://files.pythonhosted.org/packages/f6/5f/921f2f2beabaf24b1ad81ac22bb69df8dd5771fdb68d6f34a5912a420941/watchfiles-1.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5bc3ca468bb58a2ef50441f953e1f77b9a61bd1b8c347c8223403dc9b4ac9a", size = 441448 }, + { url = "https://files.pythonhosted.org/packages/63/d7/67d0d750b246f248ccdb400a85a253e93e419ea5b6cbe968fa48b97a5f30/watchfiles-1.0.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d1ec043f02ca04bf21b1b32cab155ce90c651aaf5540db8eb8ad7f7e645cba8", size = 446852 }, + { url = "https://files.pythonhosted.org/packages/53/7c/d7cd94c7d0905f1e2f1c2232ea9bc39b1a48affd007e09c547ead96edb8f/watchfiles-1.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f58d3bfafecf3d81c15d99fc0ecf4319e80ac712c77cf0ce2661c8cf8bf84066", size = 471662 }, + { url = "https://files.pythonhosted.org/packages/26/81/738f8e66f7525753996b8aa292f78dcec1ef77887d62e6cdfb04cc2f352f/watchfiles-1.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1df924ba82ae9e77340101c28d56cbaff2c991bd6fe8444a545d24075abb0a87", size = 493765 }, + { url = "https://files.pythonhosted.org/packages/d2/50/78e21f5da24ab39114e9b24f7b0945ea1c6fc7bc9ae86cd87f8eaeb47325/watchfiles-1.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:632a52dcaee44792d0965c17bdfe5dc0edad5b86d6a29e53d6ad4bf92dc0ff49", size = 490558 }, + { url = "https://files.pythonhosted.org/packages/a8/93/1873fea6354b2858eae8970991d64e9a449d87726d596490d46bf00af8ed/watchfiles-1.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bf4b459d94a0387617a1b499f314aa04d8a64b7a0747d15d425b8c8b151da0", size = 442808 }, + { url = "https://files.pythonhosted.org/packages/4f/b4/2fc4c92fb28b029f66d04a4d430fe929284e9ff717b04bb7a3bb8a7a5605/watchfiles-1.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca94c85911601b097d53caeeec30201736ad69a93f30d15672b967558df02885", size = 615287 }, + { url = "https://files.pythonhosted.org/packages/1e/d4/93da24db39257e440240d338b617c5153ad11d361c34108f5c0e1e0743eb/watchfiles-1.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65ab1fb635476f6170b07e8e21db0424de94877e4b76b7feabfe11f9a5fc12b5", size = 612812 }, + { url = "https://files.pythonhosted.org/packages/c6/67/9fd3661c2dc0309abd6021876653d91e8b64fb279529e2cadaa3520ef3e3/watchfiles-1.0.3-cp312-cp312-win32.whl", hash = "sha256:49bc1bc26abf4f32e132652f4b3bfeec77d8f8f62f57652703ef127e85a3e38d", size = 271642 }, + { url = "https://files.pythonhosted.org/packages/ae/aa/8c887edb78cd67f5d4d6a35c3aeb46d748643ebf962163130fb1871e2ee0/watchfiles-1.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:48681c86f2cb08348631fed788a116c89c787fdf1e6381c5febafd782f6c3b44", size = 285505 }, + { url = "https://files.pythonhosted.org/packages/7b/31/d212fa6390f0e73a91913ada0b925b294a78d67794795371208baf73f0b5/watchfiles-1.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:9e080cf917b35b20c889225a13f290f2716748362f6071b859b60b8847a6aa43", size = 277263 }, + { url = "https://files.pythonhosted.org/packages/26/48/5a75b18ad40cc69ea6e0003bb748db162a3215bbe44a1293e073876d51bd/watchfiles-1.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:84fac88278f42d61c519a6c75fb5296fd56710b05bbdcc74bdf85db409a03780", size = 396233 }, + { url = "https://files.pythonhosted.org/packages/dc/b2/03ce3447a3271483b030b8bafc39be19739f9a4a23edec31c6688e8a066d/watchfiles-1.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c68be72b1666d93b266714f2d4092d78dc53bd11cf91ed5a3c16527587a52e29", size = 386050 }, + { url = "https://files.pythonhosted.org/packages/ab/0c/38914f56a95aa6ec911bb7cee617762d93aaf5a11efecadbb698d6b0b9a2/watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889a37e2acf43c377b5124166bece139b4c731b61492ab22e64d371cce0e6e80", size = 442404 }, + { url = "https://files.pythonhosted.org/packages/4d/8c/a95d3ba1ccfa33a43649668f699150cce1ea795e4300c33b4c3e974a444b/watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca05cacf2e5c4a97d02a2878a24020daca21dbb8823b023b978210a75c79098", size = 444461 }, ] [[package]] @@ -3893,62 +4160,62 @@ wheels = [ [[package]] name = "yarl" -version = "1.18.0" +version = "1.18.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/4b/53db4ecad4d54535aff3dfda1f00d6363d79455f62b11b8ca97b82746bd2/yarl-1.18.0.tar.gz", hash = "sha256:20d95535e7d833889982bfe7cc321b7f63bf8879788fee982c76ae2b24cfb715", size = 180098 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/8b/305e1bde6bbf900bb8909a4884488764ee5950dda4da06cec885c06dae68/yarl-1.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:074fee89caab89a97e18ef5f29060ef61ba3cae6cd77673acc54bfdd3214b7b7", size = 141186 }, - { url = "https://files.pythonhosted.org/packages/6a/85/a15e439d8faa6bd09a536d87ca7a32daa50cf8820cf220edbced702348a0/yarl-1.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b026cf2c32daf48d90c0c4e406815c3f8f4cfe0c6dfccb094a9add1ff6a0e41a", size = 94097 }, - { url = "https://files.pythonhosted.org/packages/12/9d/7d39082baae943f138df1bb96914f8d53fd65eb131b9d0965917b009b35d/yarl-1.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae38bd86eae3ba3d2ce5636cc9e23c80c9db2e9cb557e40b98153ed102b5a736", size = 91915 }, - { url = "https://files.pythonhosted.org/packages/c0/35/7e6fbfeb413f281dda59d4a9fce7a0c43cb1f22cb6ac25151d4c4ce51651/yarl-1.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:685cc37f3f307c6a8e879986c6d85328f4c637f002e219f50e2ef66f7e062c1d", size = 315086 }, - { url = "https://files.pythonhosted.org/packages/76/2e/61b854cca176d8952d1448b15d59b9b4df27648e4cc9c1a2a01449238b21/yarl-1.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8254dbfce84ee5d1e81051ee7a0f1536c108ba294c0fdb5933476398df0654f3", size = 330221 }, - { url = "https://files.pythonhosted.org/packages/98/66/975c36deeb069888274c2edfa9d6aef44c7574e9b11bb0687130ddd02558/yarl-1.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20de4a8b04de70c49698dc2390b7fd2d18d424d3b876371f9b775e2b462d4b41", size = 326650 }, - { url = "https://files.pythonhosted.org/packages/a4/06/511e5ac4e562cbd605a05c90875e36ec5bac93da0dc55c730b4b3b09face/yarl-1.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0a2074a37285570d54b55820687de3d2f2b9ecf1b714e482e48c9e7c0402038", size = 319437 }, - { url = "https://files.pythonhosted.org/packages/7c/6a/8f6f8b17b28ed6eaaf20f5a80d391ae1c1bd5437af9ed552b9eb8903b11c/yarl-1.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f576ed278860df2721a5d57da3381040176ef1d07def9688a385c8330db61a1", size = 309966 }, - { url = "https://files.pythonhosted.org/packages/b5/54/4d9dcbdaba18a948f8bea5b65835bfcc5a931426c79d8d2dafe45264ece8/yarl-1.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3a3709450a574d61be6ac53d582496014342ea34876af8dc17cc16da32826c9a", size = 319519 }, - { url = "https://files.pythonhosted.org/packages/42/b7/de7fcde2c414d33a2be5ac9c31469ad33874a26a5e3421b2a9505a1a10ee/yarl-1.18.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bd80ed29761490c622edde5dd70537ca8c992c2952eb62ed46984f8eff66d6e8", size = 321455 }, - { url = "https://files.pythonhosted.org/packages/4e/49/8ed0dc1973876f20b63fe66986f300fd0721f3d644b6a64be12ec436c197/yarl-1.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:32141e13a1d5a48525e519c9197d3f4d9744d818d5c7d6547524cc9eccc8971e", size = 324564 }, - { url = "https://files.pythonhosted.org/packages/0c/76/63209f71efde8875670441875ef1a46383a06f578f6babf819b0cf79ebd7/yarl-1.18.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8b8d3e4e014fb4274f1c5bf61511d2199e263909fb0b8bda2a7428b0894e8dc6", size = 336798 }, - { url = "https://files.pythonhosted.org/packages/a8/f3/77e0cdee76359dade383b61eb995a3a2efcef3d64da3222f3cf52d38bd38/yarl-1.18.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:701bb4a8f4de191c8c0cc9a1e6d5142f4df880e9d1210e333b829ca9425570ed", size = 337902 }, - { url = "https://files.pythonhosted.org/packages/96/d9/0f97875e2498196a9b5561de32f3f25208485c7b43d676a65a2ee6c12fd7/yarl-1.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a45d94075ac0647621eaaf693c8751813a3eccac455d423f473ffed38c8ac5c9", size = 331620 }, - { url = "https://files.pythonhosted.org/packages/71/a3/e3bd136838d29fec4acc4919bcfd2bd33296f6c281c829fa277e72bc2590/yarl-1.18.0-cp310-cp310-win32.whl", hash = "sha256:34176bfb082add67cb2a20abd85854165540891147f88b687a5ed0dc225750a0", size = 84045 }, - { url = "https://files.pythonhosted.org/packages/fd/20/a474648c2b49c9ed5eb0e7137add6373e5d9220eda7e6d4b43d306e67672/yarl-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:73553bbeea7d6ec88c08ad8027f4e992798f0abc459361bf06641c71972794dc", size = 90221 }, - { url = "https://files.pythonhosted.org/packages/06/45/6ad7135d1c4ad3a6a49e2c37dc78a1805a7871879c03c3495d64c9605d49/yarl-1.18.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b8e8c516dc4e1a51d86ac975b0350735007e554c962281c432eaa5822aa9765c", size = 141283 }, - { url = "https://files.pythonhosted.org/packages/45/6d/24b70ae33107d6eba303ed0ebfdf1164fe2219656e7594ca58628ebc0f1d/yarl-1.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e6b4466714a73f5251d84b471475850954f1fa6acce4d3f404da1d55d644c34", size = 94082 }, - { url = "https://files.pythonhosted.org/packages/8a/0e/da720989be11b662ca847ace58f468b52310a9b03e52ac62c144755f9d75/yarl-1.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c893f8c1a6d48b25961e00922724732d00b39de8bb0b451307482dc87bddcd74", size = 92017 }, - { url = "https://files.pythonhosted.org/packages/f5/76/e5c91681fa54658943cb88673fb19b3355c3a8ae911a33a2621b6320990d/yarl-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13aaf2bdbc8c86ddce48626b15f4987f22e80d898818d735b20bd58f17292ee8", size = 340359 }, - { url = "https://files.pythonhosted.org/packages/cf/77/02cf72f09dea20980dea4ebe40dfb2c24916b864aec869a19f715428e0f0/yarl-1.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd21c0128e301851de51bc607b0a6da50e82dc34e9601f4b508d08cc89ee7929", size = 356336 }, - { url = "https://files.pythonhosted.org/packages/17/66/83a88d04e4fc243dd26109f3e3d6412f67819ab1142dadbce49706ef4df4/yarl-1.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205de377bd23365cd85562c9c6c33844050a93661640fda38e0567d2826b50df", size = 353730 }, - { url = "https://files.pythonhosted.org/packages/76/77/0b205a532d22756ab250ab21924d362f910a23d641c82faec1c4ad7f6077/yarl-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed69af4fe2a0949b1ea1d012bf065c77b4c7822bad4737f17807af2adb15a73c", size = 343882 }, - { url = "https://files.pythonhosted.org/packages/0b/47/2081ddce3da6096889c3947bdc21907d0fa15939909b10219254fe116841/yarl-1.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e1c18890091aa3cc8a77967943476b729dc2016f4cfe11e45d89b12519d4a93", size = 335873 }, - { url = "https://files.pythonhosted.org/packages/25/3c/437304394494e757ae927c9a81bacc4bcdf7351a1d4e811d95b02cb6dbae/yarl-1.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:91b8fb9427e33f83ca2ba9501221ffaac1ecf0407f758c4d2f283c523da185ee", size = 347725 }, - { url = "https://files.pythonhosted.org/packages/c6/fb/fa6c642bc052fbe6370ed5da765579650510157dea354fe9e8177c3bc34a/yarl-1.18.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:536a7a8a53b75b2e98ff96edb2dfb91a26b81c4fed82782035767db5a465be46", size = 346161 }, - { url = "https://files.pythonhosted.org/packages/b0/09/8c0cf68a0fcfe3b060c9e5857bb35735bc72a4cf4075043632c636d007e9/yarl-1.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a64619a9c47c25582190af38e9eb382279ad42e1f06034f14d794670796016c0", size = 349924 }, - { url = "https://files.pythonhosted.org/packages/bf/4b/1efe10fd51e2cedf53195d688fa270efbcd64a015c61d029d49c20bf0af7/yarl-1.18.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c73a6bbc97ba1b5a0c3c992ae93d721c395bdbb120492759b94cc1ac71bc6350", size = 361865 }, - { url = "https://files.pythonhosted.org/packages/0b/1b/2b5efd6df06bf938f7e154dee8e2ab22d148f3311a92bf4da642aaaf2fc5/yarl-1.18.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a173401d7821a2a81c7b47d4e7d5c4021375a1441af0c58611c1957445055056", size = 366030 }, - { url = "https://files.pythonhosted.org/packages/f8/db/786a5684f79278e62271038a698f56a51960f9e643be5d3eff82712f0b1c/yarl-1.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7520e799b1f84e095cce919bd6c23c9d49472deeef25fe1ef960b04cca51c3fc", size = 358902 }, - { url = "https://files.pythonhosted.org/packages/91/2f/437d0de062f1a3e3cb17573971b3832232443241133580c2ba3da5001d06/yarl-1.18.0-cp311-cp311-win32.whl", hash = "sha256:c4cb992d8090d5ae5f7afa6754d7211c578be0c45f54d3d94f7781c495d56716", size = 84138 }, - { url = "https://files.pythonhosted.org/packages/9d/85/035719a9266bce85ecde820aa3f8c46f3b18c3d7ba9ff51367b2fa4ae2a2/yarl-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:52c136f348605974c9b1c878addd6b7a60e3bf2245833e370862009b86fa4689", size = 90765 }, - { url = "https://files.pythonhosted.org/packages/23/36/c579b80a5c76c0d41c8e08baddb3e6940dfc20569db579a5691392c52afa/yarl-1.18.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ece25e2251c28bab737bdf0519c88189b3dd9492dc086a1d77336d940c28ced", size = 142376 }, - { url = "https://files.pythonhosted.org/packages/0c/5f/e247dc7c0607a0c505fea6c839721844bee55686dfb183c7d7b8ef8a9cb1/yarl-1.18.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:454902dc1830d935c90b5b53c863ba2a98dcde0fbaa31ca2ed1ad33b2a7171c6", size = 94692 }, - { url = "https://files.pythonhosted.org/packages/eb/e1/3081b578a6f21961711b9a1c49c2947abb3b0d0dd9537378fb06777ce8ee/yarl-1.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01be8688fc211dc237e628fcc209dda412d35de7642453059a0553747018d075", size = 92527 }, - { url = "https://files.pythonhosted.org/packages/2f/fa/d9e1b9fbafa4cc82cd3980b5314741b33c2fe16308d725449a23aed32021/yarl-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d26f1fa9fa2167bb238f6f4b20218eb4e88dd3ef21bb8f97439fa6b5313e30d", size = 332096 }, - { url = "https://files.pythonhosted.org/packages/93/b6/dd27165114317875838e216214fb86338dc63d2e50855a8f2a12de2a7fe5/yarl-1.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b234a4a9248a9f000b7a5dfe84b8cb6210ee5120ae70eb72a4dcbdb4c528f72f", size = 342047 }, - { url = "https://files.pythonhosted.org/packages/fc/9f/bad434b5279ae7a356844e14dc771c3d29eb928140bbc01621af811c8a27/yarl-1.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe94d1de77c4cd8caff1bd5480e22342dbd54c93929f5943495d9c1e8abe9f42", size = 341712 }, - { url = "https://files.pythonhosted.org/packages/9a/9f/63864f43d131ba8c8cdf1bde5dd3f02f0eff8a7c883a5d7fad32f204fda5/yarl-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b4c90c5363c6b0a54188122b61edb919c2cd1119684999d08cd5e538813a28e", size = 336654 }, - { url = "https://files.pythonhosted.org/packages/20/30/b4542bbd9be73de155213207eec019f6fe6495885f7dd59aa1ff705a041b/yarl-1.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a98ecadc5a241c9ba06de08127ee4796e1009555efd791bac514207862b43d", size = 325484 }, - { url = "https://files.pythonhosted.org/packages/69/bc/e2a9808ec26989cf0d1b98fe7b3cc45c1c6506b5ea4fe43ece5991f28f34/yarl-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9106025c7f261f9f5144f9aa7681d43867eed06349a7cfb297a1bc804de2f0d1", size = 344213 }, - { url = "https://files.pythonhosted.org/packages/e2/17/0ee5a68886aca1a8071b0d24a1e1c0fd9970dead2ef2d5e26e027fb7ce88/yarl-1.18.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f275ede6199d0f1ed4ea5d55a7b7573ccd40d97aee7808559e1298fe6efc8dbd", size = 340517 }, - { url = "https://files.pythonhosted.org/packages/fd/db/1fe4ef38ee852bff5ec8f5367d718b3a7dac7520f344b8e50306f68a2940/yarl-1.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f7edeb1dcc7f50a2c8e08b9dc13a413903b7817e72273f00878cb70e766bdb3b", size = 346234 }, - { url = "https://files.pythonhosted.org/packages/b4/ee/5e5bccdb821eb9949ba66abb4d19e3299eee00282e37b42f65236120e892/yarl-1.18.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c083f6dd6951b86e484ebfc9c3524b49bcaa9c420cb4b2a78ef9f7a512bfcc85", size = 359625 }, - { url = "https://files.pythonhosted.org/packages/3f/43/95a64d9e7ab4aa1c34fc5ea0edb35b581bc6ad33fd960a8ae34c2040b319/yarl-1.18.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:80741ec5b471fbdfb997821b2842c59660a1c930ceb42f8a84ba8ca0f25a66aa", size = 364239 }, - { url = "https://files.pythonhosted.org/packages/40/19/09ce976c624c9d3cc898f0be5035ddef0c0759d85b2313321cfe77b69915/yarl-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b1a3297b9cad594e1ff0c040d2881d7d3a74124a3c73e00c3c71526a1234a9f7", size = 357599 }, - { url = "https://files.pythonhosted.org/packages/7d/35/6f33fd29791af2ec161aebe8abe63e788c2b74a6c7e8f29c92e5f5e96849/yarl-1.18.0-cp312-cp312-win32.whl", hash = "sha256:cd6ab7d6776c186f544f893b45ee0c883542b35e8a493db74665d2e594d3ca75", size = 83832 }, - { url = "https://files.pythonhosted.org/packages/4e/8e/cdb40ef98597be107de67b11e2f1f23f911e0f1416b938885d17a338e304/yarl-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:039c299a0864d1f43c3e31570045635034ea7021db41bf4842693a72aca8df3a", size = 90132 }, - { url = "https://files.pythonhosted.org/packages/30/9c/3f7ab894a37b1520291247cbc9ea6756228d098dae5b37eec848d404a204/yarl-1.18.0-py3-none-any.whl", hash = "sha256:dbf53db46f7cf176ee01d8d98c39381440776fcda13779d269a8ba664f69bec0", size = 44840 }, +sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/98/e005bc608765a8a5569f58e650961314873c8469c333616eb40bff19ae97/yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34", size = 141458 }, + { url = "https://files.pythonhosted.org/packages/df/5d/f8106b263b8ae8a866b46d9be869ac01f9b3fb7f2325f3ecb3df8003f796/yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7", size = 94365 }, + { url = "https://files.pythonhosted.org/packages/56/3e/d8637ddb9ba69bf851f765a3ee288676f7cf64fb3be13760c18cbc9d10bd/yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed", size = 92181 }, + { url = "https://files.pythonhosted.org/packages/76/f9/d616a5c2daae281171de10fba41e1c0e2d8207166fc3547252f7d469b4e1/yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde", size = 315349 }, + { url = "https://files.pythonhosted.org/packages/bb/b4/3ea5e7b6f08f698b3769a06054783e434f6d59857181b5c4e145de83f59b/yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b", size = 330494 }, + { url = "https://files.pythonhosted.org/packages/55/f1/e0fc810554877b1b67420568afff51b967baed5b53bcc983ab164eebf9c9/yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5", size = 326927 }, + { url = "https://files.pythonhosted.org/packages/a9/42/b1753949b327b36f210899f2dd0a0947c0c74e42a32de3f8eb5c7d93edca/yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc", size = 319703 }, + { url = "https://files.pythonhosted.org/packages/f0/6d/e87c62dc9635daefb064b56f5c97df55a2e9cc947a2b3afd4fd2f3b841c7/yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd", size = 310246 }, + { url = "https://files.pythonhosted.org/packages/e3/ef/e2e8d1785cdcbd986f7622d7f0098205f3644546da7919c24b95790ec65a/yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990", size = 319730 }, + { url = "https://files.pythonhosted.org/packages/fc/15/8723e22345bc160dfde68c4b3ae8b236e868f9963c74015f1bc8a614101c/yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db", size = 321681 }, + { url = "https://files.pythonhosted.org/packages/86/09/bf764e974f1516efa0ae2801494a5951e959f1610dd41edbfc07e5e0f978/yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62", size = 324812 }, + { url = "https://files.pythonhosted.org/packages/f6/4c/20a0187e3b903c97d857cf0272d687c1b08b03438968ae8ffc50fe78b0d6/yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760", size = 337011 }, + { url = "https://files.pythonhosted.org/packages/c9/71/6244599a6e1cc4c9f73254a627234e0dad3883ece40cc33dce6265977461/yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b", size = 338132 }, + { url = "https://files.pythonhosted.org/packages/af/f5/e0c3efaf74566c4b4a41cb76d27097df424052a064216beccae8d303c90f/yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690", size = 331849 }, + { url = "https://files.pythonhosted.org/packages/8a/b8/3d16209c2014c2f98a8f658850a57b716efb97930aebf1ca0d9325933731/yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6", size = 84309 }, + { url = "https://files.pythonhosted.org/packages/fd/b7/2e9a5b18eb0fe24c3a0e8bae994e812ed9852ab4fd067c0107fadde0d5f0/yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8", size = 90484 }, + { url = "https://files.pythonhosted.org/packages/40/93/282b5f4898d8e8efaf0790ba6d10e2245d2c9f30e199d1a85cae9356098c/yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069", size = 141555 }, + { url = "https://files.pythonhosted.org/packages/6d/9c/0a49af78df099c283ca3444560f10718fadb8a18dc8b3edf8c7bd9fd7d89/yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193", size = 94351 }, + { url = "https://files.pythonhosted.org/packages/5a/a1/205ab51e148fdcedad189ca8dd587794c6f119882437d04c33c01a75dece/yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889", size = 92286 }, + { url = "https://files.pythonhosted.org/packages/ed/fe/88b690b30f3f59275fb674f5f93ddd4a3ae796c2b62e5bb9ece8a4914b83/yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8", size = 340649 }, + { url = "https://files.pythonhosted.org/packages/07/eb/3b65499b568e01f36e847cebdc8d7ccb51fff716dbda1ae83c3cbb8ca1c9/yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca", size = 356623 }, + { url = "https://files.pythonhosted.org/packages/33/46/f559dc184280b745fc76ec6b1954de2c55595f0ec0a7614238b9ebf69618/yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8", size = 354007 }, + { url = "https://files.pythonhosted.org/packages/af/ba/1865d85212351ad160f19fb99808acf23aab9a0f8ff31c8c9f1b4d671fc9/yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae", size = 344145 }, + { url = "https://files.pythonhosted.org/packages/94/cb/5c3e975d77755d7b3d5193e92056b19d83752ea2da7ab394e22260a7b824/yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3", size = 336133 }, + { url = "https://files.pythonhosted.org/packages/19/89/b77d3fd249ab52a5c40859815765d35c91425b6bb82e7427ab2f78f5ff55/yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb", size = 347967 }, + { url = "https://files.pythonhosted.org/packages/35/bd/f6b7630ba2cc06c319c3235634c582a6ab014d52311e7d7c22f9518189b5/yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e", size = 346397 }, + { url = "https://files.pythonhosted.org/packages/18/1a/0b4e367d5a72d1f095318344848e93ea70da728118221f84f1bf6c1e39e7/yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59", size = 350206 }, + { url = "https://files.pythonhosted.org/packages/b5/cf/320fff4367341fb77809a2d8d7fe75b5d323a8e1b35710aafe41fdbf327b/yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d", size = 362089 }, + { url = "https://files.pythonhosted.org/packages/57/cf/aadba261d8b920253204085268bad5e8cdd86b50162fcb1b10c10834885a/yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e", size = 366267 }, + { url = "https://files.pythonhosted.org/packages/54/58/fb4cadd81acdee6dafe14abeb258f876e4dd410518099ae9a35c88d8097c/yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a", size = 359141 }, + { url = "https://files.pythonhosted.org/packages/9a/7a/4c571597589da4cd5c14ed2a0b17ac56ec9ee7ee615013f74653169e702d/yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1", size = 84402 }, + { url = "https://files.pythonhosted.org/packages/ae/7b/8600250b3d89b625f1121d897062f629883c2f45339623b69b1747ec65fa/yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5", size = 91030 }, + { url = "https://files.pythonhosted.org/packages/33/85/bd2e2729752ff4c77338e0102914897512e92496375e079ce0150a6dc306/yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50", size = 142644 }, + { url = "https://files.pythonhosted.org/packages/ff/74/1178322cc0f10288d7eefa6e4a85d8d2e28187ccab13d5b844e8b5d7c88d/yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576", size = 94962 }, + { url = "https://files.pythonhosted.org/packages/be/75/79c6acc0261e2c2ae8a1c41cf12265e91628c8c58ae91f5ff59e29c0787f/yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640", size = 92795 }, + { url = "https://files.pythonhosted.org/packages/6b/32/927b2d67a412c31199e83fefdce6e645247b4fb164aa1ecb35a0f9eb2058/yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2", size = 332368 }, + { url = "https://files.pythonhosted.org/packages/19/e5/859fca07169d6eceeaa4fde1997c91d8abde4e9a7c018e371640c2da2b71/yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75", size = 342314 }, + { url = "https://files.pythonhosted.org/packages/08/75/76b63ccd91c9e03ab213ef27ae6add2e3400e77e5cdddf8ed2dbc36e3f21/yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512", size = 341987 }, + { url = "https://files.pythonhosted.org/packages/1a/e1/a097d5755d3ea8479a42856f51d97eeff7a3a7160593332d98f2709b3580/yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba", size = 336914 }, + { url = "https://files.pythonhosted.org/packages/0b/42/e1b4d0e396b7987feceebe565286c27bc085bf07d61a59508cdaf2d45e63/yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb", size = 325765 }, + { url = "https://files.pythonhosted.org/packages/7e/18/03a5834ccc9177f97ca1bbb245b93c13e58e8225276f01eedc4cc98ab820/yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272", size = 344444 }, + { url = "https://files.pythonhosted.org/packages/c8/03/a713633bdde0640b0472aa197b5b86e90fbc4c5bc05b727b714cd8a40e6d/yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6", size = 340760 }, + { url = "https://files.pythonhosted.org/packages/eb/99/f6567e3f3bbad8fd101886ea0276c68ecb86a2b58be0f64077396cd4b95e/yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e", size = 346484 }, + { url = "https://files.pythonhosted.org/packages/8e/a9/84717c896b2fc6cb15bd4eecd64e34a2f0a9fd6669e69170c73a8b46795a/yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb", size = 359864 }, + { url = "https://files.pythonhosted.org/packages/1e/2e/d0f5f1bef7ee93ed17e739ec8dbcb47794af891f7d165fa6014517b48169/yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393", size = 364537 }, + { url = "https://files.pythonhosted.org/packages/97/8a/568d07c5d4964da5b02621a517532adb8ec5ba181ad1687191fffeda0ab6/yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285", size = 357861 }, + { url = "https://files.pythonhosted.org/packages/7d/e3/924c3f64b6b3077889df9a1ece1ed8947e7b61b0a933f2ec93041990a677/yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2", size = 84097 }, + { url = "https://files.pythonhosted.org/packages/34/45/0e055320daaabfc169b21ff6174567b2c910c45617b0d79c68d7ab349b02/yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477", size = 90399 }, + { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 }, ] From 70293aa3ffa1e056478aa03342be61d84c1ce8c5 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Tue, 31 Dec 2024 16:04:05 -0500 Subject: [PATCH 12/26] remove dev tag --- ui/streamlit_ui/app.py | 2 +- ui/streamlit_ui/modal_streamlit_app.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/streamlit_ui/app.py b/ui/streamlit_ui/app.py index 3b2f7e03..e9b640c9 100644 --- a/ui/streamlit_ui/app.py +++ b/ui/streamlit_ui/app.py @@ -9,7 +9,7 @@ # st.session_state.API_BASE = f"http://localhost:{PORT}" # st.session_state.WS_BASE = f"ws://localhost:{PORT}" -DEFAULT_BASE = "sotopia-lab--sotopia-fastapi-webapi-serve-dev.modal.run" +DEFAULT_BASE = "sotopia-lab--sotopia-fastapi-webapi-serve.modal.run" # Modal Configuration diff --git a/ui/streamlit_ui/modal_streamlit_app.py b/ui/streamlit_ui/modal_streamlit_app.py index fd0e5f2f..86ba6eb8 100644 --- a/ui/streamlit_ui/modal_streamlit_app.py +++ b/ui/streamlit_ui/modal_streamlit_app.py @@ -18,7 +18,7 @@ ) .pip_install("streamlit~=1.40.2", "uv") .run_commands( - "rm -rf sotopia && git clone https://github.com/sotopia-lab/sotopia.git && cd sotopia && git checkout feature/sotopia-demo-ui && uv pip install pyproject.toml --system && git status && pip install -e . && cd ui && cd streamlit_ui" + "rm -rf sotopia && git clone https://github.com/sotopia-lab/sotopia.git && cd sotopia && git checkout demo && uv pip install pyproject.toml --system && git status && pip install -e . && cd ui && cd streamlit_ui" ) # .pip_install("pydantic==2.8.2") .run_commands("pip list") From 2526be13364024076adb7d57a4abb5bf540b2cf6 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Tue, 31 Dec 2024 16:21:22 -0500 Subject: [PATCH 13/26] add custom eval --- sotopia/database/__init__.py | 4 ++++ sotopia/database/evaluation_dimensions.py | 14 +++++++++++--- ui/streamlit_ui/pages/add_evaluation_dimension.py | 7 +++---- .../pages/display_evaluation_dimensions.py | 10 +++++----- ui/streamlit_ui/pages/render_chat_websocket.py | 6 +++--- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/sotopia/database/__init__.py b/sotopia/database/__init__.py index 6b640620..0977a518 100644 --- a/sotopia/database/__init__.py +++ b/sotopia/database/__init__.py @@ -41,7 +41,9 @@ from .evaluation_dimensions import ( EvaluationDimensionBuilder, CustomEvaluationDimension, + BaseCustomEvaluationDimension, CustomEvaluationDimensionList, + BaseCustomEvaluationDimensionList, ) from logging import Logger @@ -85,7 +87,9 @@ "get_rewards_from_episode", "EvaluationDimensionBuilder", "CustomEvaluationDimension", + "BaseCustomEvaluationDimension", "CustomEvaluationDimensionList", + "BaseCustomEvaluationDimensionList", "NonStreamingSimulationStatus", ] diff --git a/sotopia/database/evaluation_dimensions.py b/sotopia/database/evaluation_dimensions.py index 4b2a2c2a..8a3954df 100644 --- a/sotopia/database/evaluation_dimensions.py +++ b/sotopia/database/evaluation_dimensions.py @@ -1,21 +1,29 @@ from redis_om import JsonModel from redis_om.model.model import Field -from pydantic import BaseModel, create_model +from pydantic import create_model, BaseModel from typing import Type, Callable, Tuple, Annotated, Union, cast, Any -class CustomEvaluationDimension(JsonModel): +class BaseCustomEvaluationDimension(BaseModel): name: str = Field(index=True) description: str = Field(index=True) range_high: int = Field(index=True) range_low: int = Field(index=True) -class CustomEvaluationDimensionList(JsonModel): +class CustomEvaluationDimension(BaseCustomEvaluationDimension, JsonModel): + pass + + +class BaseCustomEvaluationDimensionList(BaseModel): name: str = Field(index=True) dimension_pks: list[str] = Field(default_factory=lambda: [], index=True) +class CustomEvaluationDimensionList(BaseCustomEvaluationDimensionList, JsonModel): + pass + + class EvaluationDimensionBuilder: """ EvaluationDimensionBuilder is a utility class for creating and managing evaluation dimensions. diff --git a/ui/streamlit_ui/pages/add_evaluation_dimension.py b/ui/streamlit_ui/pages/add_evaluation_dimension.py index ab9d0b90..4010a8e5 100644 --- a/ui/streamlit_ui/pages/add_evaluation_dimension.py +++ b/ui/streamlit_ui/pages/add_evaluation_dimension.py @@ -35,8 +35,7 @@ class CustomEvaluationDimension(JsonModel): import streamlit as st import requests -from sotopia.database import CustomEvaluationDimension -from sotopia.api.fastapi_server import CustomEvaluationDimensionsWrapper +from sotopia.database import BaseCustomEvaluationDimension from ui.streamlit_ui.rendering.get_elements import ( get_distinct_evaluation_dimensions, ) @@ -70,7 +69,7 @@ def add_evaluation_dimension() -> None: add_dimension = st.form_submit_button("Add Dimension") if add_dimension and dim_name and dim_description: - new_dimension = CustomEvaluationDimension( + new_dimension = BaseCustomEvaluationDimension( name=dim_name, description=dim_description, range_low=range_low, @@ -156,7 +155,7 @@ def add_evaluation_dimension() -> None: if submit_button and list_name: try: - wrapper = CustomEvaluationDimensionsWrapper( + wrapper = BaseCustomEvaluationDimension( name=list_name, dimensions=st.session_state.dimensions ) # st.write(wrapper.dict()) diff --git a/ui/streamlit_ui/pages/display_evaluation_dimensions.py b/ui/streamlit_ui/pages/display_evaluation_dimensions.py index 2a96a506..ef8fa614 100644 --- a/ui/streamlit_ui/pages/display_evaluation_dimensions.py +++ b/ui/streamlit_ui/pages/display_evaluation_dimensions.py @@ -1,6 +1,6 @@ import streamlit as st -from sotopia.database import CustomEvaluationDimension +from sotopia.database import BaseCustomEvaluationDimension from ui.streamlit_ui.rendering import ( get_evaluation_dimensions, @@ -16,7 +16,7 @@ def display_evaluation_dimensions() -> None: # st.title("Evaluation Dimensions") - distinct_dimensions: list[CustomEvaluationDimension] = ( + distinct_dimensions: list[BaseCustomEvaluationDimension] = ( get_distinct_evaluation_dimensions() ) @@ -31,15 +31,15 @@ def display_evaluation_dimensions() -> None: render_evaluation_dimension(dimension) with st.expander("Evaluation Dimension Lists", expanded=True): - all_dimension_lists: dict[str, list[CustomEvaluationDimension]] = ( + all_dimension_lists: dict[str, list[BaseCustomEvaluationDimension]] = ( get_evaluation_dimensions() ) col1, col2 = st.columns(2, gap="medium") for i, (dimension_list_name, dimensions) in enumerate( all_dimension_lists.items() ): - all_dimensions: list[CustomEvaluationDimension] = [ - CustomEvaluationDimension(**dimension) for dimension in dimensions + all_dimensions: list[BaseCustomEvaluationDimension] = [ + BaseCustomEvaluationDimension(**dimension) for dimension in dimensions ] with col1 if i % 2 == 0 else col2: render_evaluation_dimension_list(dimension_list_name, all_dimensions) diff --git a/ui/streamlit_ui/pages/render_chat_websocket.py b/ui/streamlit_ui/pages/render_chat_websocket.py index 5e4ea17e..acda082d 100644 --- a/ui/streamlit_ui/pages/render_chat_websocket.py +++ b/ui/streamlit_ui/pages/render_chat_websocket.py @@ -8,7 +8,7 @@ import aiohttp import streamlit as st -from sotopia.database import EpisodeLog, EnvironmentProfile +from sotopia.database import BaseEpisodeLog, BaseEnvironmentProfile from ui.streamlit_ui.rendering import ( get_scenarios, get_agents, @@ -173,7 +173,7 @@ def handle_error_msg(message: dict[str, Any]) -> None: def handle_server_msg(message: dict[str, Any]) -> None: msg_type = message["data"]["type"] if msg_type == "messages": - epilog = EpisodeLog(**message["data"]["messages"]) + epilog = BaseEpisodeLog(**message["data"]["messages"]) st.session_state.messages.append(epilog) @@ -240,7 +240,7 @@ def stop_callback() -> None: def update_scenario_description() -> None: scenario = st.session_state.scenarios[st.session_state.scenario_choice] - environment_profile = EnvironmentProfile(**scenario) + environment_profile = BaseEnvironmentProfile(**scenario) render_environment_profile(environment_profile) From b0b53d82cad5aa3c45cf0df5cb404a6ed7b9545c Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Tue, 31 Dec 2024 16:33:10 -0500 Subject: [PATCH 14/26] base dimension --- ui/streamlit_ui/modal_streamlit_app.py | 2 +- ui/streamlit_ui/rendering/get_elements.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/streamlit_ui/modal_streamlit_app.py b/ui/streamlit_ui/modal_streamlit_app.py index 86ba6eb8..111d91a9 100644 --- a/ui/streamlit_ui/modal_streamlit_app.py +++ b/ui/streamlit_ui/modal_streamlit_app.py @@ -18,7 +18,7 @@ ) .pip_install("streamlit~=1.40.2", "uv") .run_commands( - "rm -rf sotopia && git clone https://github.com/sotopia-lab/sotopia.git && cd sotopia && git checkout demo && uv pip install pyproject.toml --system && git status && pip install -e . && cd ui && cd streamlit_ui" + "rm -rf sotopia && git clone https://github.com/sotopia-lab/sotopia.git && cd sotopia && git checkout demo && uv pip install pyproject.toml --system && pip install -e . && cd ui/streamlit_ui" ) # .pip_install("pydantic==2.8.2") .run_commands("pip list") diff --git a/ui/streamlit_ui/rendering/get_elements.py b/ui/streamlit_ui/rendering/get_elements.py index ff86de83..33031a5e 100644 --- a/ui/streamlit_ui/rendering/get_elements.py +++ b/ui/streamlit_ui/rendering/get_elements.py @@ -2,7 +2,7 @@ import streamlit as st from typing import Any from ui.streamlit_ui.rendering.render_utils import get_full_name -from sotopia.database import CustomEvaluationDimension +from sotopia.database import BaseCustomEvaluationDimension def get_models() -> dict[str, dict[Any, Any]]: @@ -38,14 +38,14 @@ def get_evaluation_dimensions() -> dict[str, dict[Any, Any]]: return evaluation_dimensions -def get_distinct_evaluation_dimensions() -> list[CustomEvaluationDimension]: +def get_distinct_evaluation_dimensions() -> list[BaseCustomEvaluationDimension]: all_dimension_lists: dict[str, list[Any]] = get_evaluation_dimensions() - distinct_dimensions: list[CustomEvaluationDimension] = [] + distinct_dimensions: list[BaseCustomEvaluationDimension] = [] distinct_dimension_names: set[str] = set() for dimension_list_name, dimensions in all_dimension_lists.items(): for dimension in dimensions: - custom_dimension = CustomEvaluationDimension(**dimension) + custom_dimension = BaseCustomEvaluationDimension(**dimension) if custom_dimension.name not in distinct_dimension_names: distinct_dimensions.append(custom_dimension) distinct_dimension_names.add(custom_dimension.name) From 22a1ecf27fbf4d5f280f29b30a35ed1f9ac25aba Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Thu, 2 Jan 2025 17:38:56 -0500 Subject: [PATCH 15/26] fix ui mypy --- ui/{streamlit_ui => }/.streamlit/config.toml | 0 ui/{streamlit_ui => }/README.md | 0 ui/{streamlit_ui => }/__init__.py | 0 ui/{streamlit_ui => }/app.py | 0 ui/{streamlit_ui => }/css/style.css | 0 ui/{streamlit_ui => }/modal_streamlit_app.py | 3 ++- ui/{streamlit_ui => }/pages/__init__.py | 0 ui/{streamlit_ui => }/pages/add_characters.py | 2 +- .../pages/add_evaluation_dimension.py | 2 +- ui/{streamlit_ui => }/pages/add_scenarios.py | 2 +- .../pages/display_characters.py | 2 +- ui/{streamlit_ui => }/pages/display_episodes.py | 2 +- .../pages/display_evaluation_dimensions.py | 7 +++---- .../pages/display_scenarios.py | 2 +- ui/{streamlit_ui => }/pages/intro.py | 0 .../pages/render_chat_websocket.py | 2 +- ui/{streamlit_ui => }/rendering/__init__.py | 0 ui/{streamlit_ui => }/rendering/get_elements.py | 8 +++----- .../rendering/render_elements.py | 17 ++++++++--------- ui/{streamlit_ui => }/rendering/render_utils.py | 0 20 files changed, 23 insertions(+), 26 deletions(-) rename ui/{streamlit_ui => }/.streamlit/config.toml (100%) rename ui/{streamlit_ui => }/README.md (100%) rename ui/{streamlit_ui => }/__init__.py (100%) rename ui/{streamlit_ui => }/app.py (100%) rename ui/{streamlit_ui => }/css/style.css (100%) rename ui/{streamlit_ui => }/modal_streamlit_app.py (98%) rename ui/{streamlit_ui => }/pages/__init__.py (100%) rename ui/{streamlit_ui => }/pages/add_characters.py (97%) rename ui/{streamlit_ui => }/pages/add_evaluation_dimension.py (99%) rename ui/{streamlit_ui => }/pages/add_scenarios.py (98%) rename ui/{streamlit_ui => }/pages/display_characters.py (89%) rename ui/{streamlit_ui => }/pages/display_episodes.py (98%) rename ui/{streamlit_ui => }/pages/display_evaluation_dimensions.py (91%) rename ui/{streamlit_ui => }/pages/display_scenarios.py (98%) rename ui/{streamlit_ui => }/pages/intro.py (100%) rename ui/{streamlit_ui => }/pages/render_chat_websocket.py (99%) rename ui/{streamlit_ui => }/rendering/__init__.py (100%) rename ui/{streamlit_ui => }/rendering/get_elements.py (89%) rename ui/{streamlit_ui => }/rendering/render_elements.py (96%) rename ui/{streamlit_ui => }/rendering/render_utils.py (100%) diff --git a/ui/streamlit_ui/.streamlit/config.toml b/ui/.streamlit/config.toml similarity index 100% rename from ui/streamlit_ui/.streamlit/config.toml rename to ui/.streamlit/config.toml diff --git a/ui/streamlit_ui/README.md b/ui/README.md similarity index 100% rename from ui/streamlit_ui/README.md rename to ui/README.md diff --git a/ui/streamlit_ui/__init__.py b/ui/__init__.py similarity index 100% rename from ui/streamlit_ui/__init__.py rename to ui/__init__.py diff --git a/ui/streamlit_ui/app.py b/ui/app.py similarity index 100% rename from ui/streamlit_ui/app.py rename to ui/app.py diff --git a/ui/streamlit_ui/css/style.css b/ui/css/style.css similarity index 100% rename from ui/streamlit_ui/css/style.css rename to ui/css/style.css diff --git a/ui/streamlit_ui/modal_streamlit_app.py b/ui/modal_streamlit_app.py similarity index 98% rename from ui/streamlit_ui/modal_streamlit_app.py rename to ui/modal_streamlit_app.py index 111d91a9..d8bfe13b 100644 --- a/ui/streamlit_ui/modal_streamlit_app.py +++ b/ui/modal_streamlit_app.py @@ -18,7 +18,8 @@ ) .pip_install("streamlit~=1.40.2", "uv") .run_commands( - "rm -rf sotopia && git clone https://github.com/sotopia-lab/sotopia.git && cd sotopia && git checkout demo && uv pip install pyproject.toml --system && pip install -e . && cd ui/streamlit_ui" + "rm -rf sotopia && git clone https://github.com/sotopia-lab/sotopia.git && cd sotopia && git checkout demo && uv pip install pyproject.toml --system && pip install -e . && cd ui/streamlit_ui", + force_build=True, ) # .pip_install("pydantic==2.8.2") .run_commands("pip list") diff --git a/ui/streamlit_ui/pages/__init__.py b/ui/pages/__init__.py similarity index 100% rename from ui/streamlit_ui/pages/__init__.py rename to ui/pages/__init__.py diff --git a/ui/streamlit_ui/pages/add_characters.py b/ui/pages/add_characters.py similarity index 97% rename from ui/streamlit_ui/pages/add_characters.py rename to ui/pages/add_characters.py index 35ccc5a3..5aea3cc1 100644 --- a/ui/streamlit_ui/pages/add_characters.py +++ b/ui/pages/add_characters.py @@ -13,7 +13,7 @@ async def create_agent(agent: BaseAgentProfile) -> str: import requests from sotopia.database import BaseAgentProfile -from ui.streamlit_ui.rendering import local_css +from ui.rendering import local_css # add fields for agent profiles diff --git a/ui/streamlit_ui/pages/add_evaluation_dimension.py b/ui/pages/add_evaluation_dimension.py similarity index 99% rename from ui/streamlit_ui/pages/add_evaluation_dimension.py rename to ui/pages/add_evaluation_dimension.py index 4010a8e5..69b57e7a 100644 --- a/ui/streamlit_ui/pages/add_evaluation_dimension.py +++ b/ui/pages/add_evaluation_dimension.py @@ -36,7 +36,7 @@ class CustomEvaluationDimension(JsonModel): import streamlit as st import requests from sotopia.database import BaseCustomEvaluationDimension -from ui.streamlit_ui.rendering.get_elements import ( +from ui.rendering.get_elements import ( get_distinct_evaluation_dimensions, ) diff --git a/ui/streamlit_ui/pages/add_scenarios.py b/ui/pages/add_scenarios.py similarity index 98% rename from ui/streamlit_ui/pages/add_scenarios.py rename to ui/pages/add_scenarios.py index 0c8ad722..66f291e5 100644 --- a/ui/streamlit_ui/pages/add_scenarios.py +++ b/ui/pages/add_scenarios.py @@ -34,7 +34,7 @@ class RelationshipType(IntEnum): """ import streamlit as st -from ui.streamlit_ui.rendering import local_css +from ui.rendering import local_css from sotopia.database import BaseEnvironmentProfile, RelationshipType import requests diff --git a/ui/streamlit_ui/pages/display_characters.py b/ui/pages/display_characters.py similarity index 89% rename from ui/streamlit_ui/pages/display_characters.py rename to ui/pages/display_characters.py index cde880cc..d6cce86c 100644 --- a/ui/streamlit_ui/pages/display_characters.py +++ b/ui/pages/display_characters.py @@ -3,7 +3,7 @@ from sotopia.database import BaseAgentProfile # Importing avatars -from ui.streamlit_ui.rendering import render_character, local_css, get_agents +from ui.rendering import render_character, local_css, get_agents local_css("./css/style.css") diff --git a/ui/streamlit_ui/pages/display_episodes.py b/ui/pages/display_episodes.py similarity index 98% rename from ui/streamlit_ui/pages/display_episodes.py rename to ui/pages/display_episodes.py index 26d5ff4a..c5e4ae4c 100644 --- a/ui/streamlit_ui/pages/display_episodes.py +++ b/ui/pages/display_episodes.py @@ -1,5 +1,5 @@ import streamlit as st -from ui.streamlit_ui.rendering import ( +from ui.rendering import ( render_environment_profile, render_conversation_and_evaluation, ) diff --git a/ui/streamlit_ui/pages/display_evaluation_dimensions.py b/ui/pages/display_evaluation_dimensions.py similarity index 91% rename from ui/streamlit_ui/pages/display_evaluation_dimensions.py rename to ui/pages/display_evaluation_dimensions.py index ef8fa614..f9b6e19c 100644 --- a/ui/streamlit_ui/pages/display_evaluation_dimensions.py +++ b/ui/pages/display_evaluation_dimensions.py @@ -1,8 +1,8 @@ import streamlit as st - +from typing import Any from sotopia.database import BaseCustomEvaluationDimension -from ui.streamlit_ui.rendering import ( +from ui.rendering import ( get_evaluation_dimensions, render_evaluation_dimension, get_distinct_evaluation_dimensions, @@ -24,14 +24,13 @@ def display_evaluation_dimensions() -> None: distinct_dimensions.sort(key=lambda x: x.name) with st.expander("Evaluation Dimensions", expanded=True): - all_dimensions = [] col1, col2 = st.columns(2, gap="medium") for i, dimension in enumerate(distinct_dimensions): with col1 if i % 2 == 0 else col2: render_evaluation_dimension(dimension) with st.expander("Evaluation Dimension Lists", expanded=True): - all_dimension_lists: dict[str, list[BaseCustomEvaluationDimension]] = ( + all_dimension_lists: dict[str, list[dict[str, Any]]] = ( get_evaluation_dimensions() ) col1, col2 = st.columns(2, gap="medium") diff --git a/ui/streamlit_ui/pages/display_scenarios.py b/ui/pages/display_scenarios.py similarity index 98% rename from ui/streamlit_ui/pages/display_scenarios.py rename to ui/pages/display_scenarios.py index 7412e7ec..9d282dd3 100644 --- a/ui/streamlit_ui/pages/display_scenarios.py +++ b/ui/pages/display_scenarios.py @@ -1,7 +1,7 @@ # isort: skip_file # ruff: noqa: E402 import streamlit as st -from ui.streamlit_ui.rendering import ( +from ui.rendering import ( render_environment_profile, local_css, get_scenarios, diff --git a/ui/streamlit_ui/pages/intro.py b/ui/pages/intro.py similarity index 100% rename from ui/streamlit_ui/pages/intro.py rename to ui/pages/intro.py diff --git a/ui/streamlit_ui/pages/render_chat_websocket.py b/ui/pages/render_chat_websocket.py similarity index 99% rename from ui/streamlit_ui/pages/render_chat_websocket.py rename to ui/pages/render_chat_websocket.py index acda082d..b3429df8 100644 --- a/ui/streamlit_ui/pages/render_chat_websocket.py +++ b/ui/pages/render_chat_websocket.py @@ -9,7 +9,7 @@ import streamlit as st from sotopia.database import BaseEpisodeLog, BaseEnvironmentProfile -from ui.streamlit_ui.rendering import ( +from ui.rendering import ( get_scenarios, get_agents, get_models, diff --git a/ui/streamlit_ui/rendering/__init__.py b/ui/rendering/__init__.py similarity index 100% rename from ui/streamlit_ui/rendering/__init__.py rename to ui/rendering/__init__.py diff --git a/ui/streamlit_ui/rendering/get_elements.py b/ui/rendering/get_elements.py similarity index 89% rename from ui/streamlit_ui/rendering/get_elements.py rename to ui/rendering/get_elements.py index 33031a5e..c88715a5 100644 --- a/ui/streamlit_ui/rendering/get_elements.py +++ b/ui/rendering/get_elements.py @@ -1,7 +1,7 @@ import requests import streamlit as st from typing import Any -from ui.streamlit_ui.rendering.render_utils import get_full_name +from .render_utils import get_full_name from sotopia.database import BaseCustomEvaluationDimension @@ -30,12 +30,10 @@ def get_agents(id: str = "") -> dict[str, dict[Any, Any]]: return {get_full_name(agent): agent for agent in agents} -def get_evaluation_dimensions() -> dict[str, dict[Any, Any]]: - # use synchronous code to get the evaluation dimensions +def get_evaluation_dimensions() -> dict[str, list[dict[Any, Any]]]: with requests.get(f"{st.session_state.API_BASE}/evaluation_dimensions") as resp: evaluation_dimensions = resp.json() - - return evaluation_dimensions + return {dimension["name"]: dimension for dimension in evaluation_dimensions} def get_distinct_evaluation_dimensions() -> list[BaseCustomEvaluationDimension]: diff --git a/ui/streamlit_ui/rendering/render_elements.py b/ui/rendering/render_elements.py similarity index 96% rename from ui/streamlit_ui/rendering/render_elements.py rename to ui/rendering/render_elements.py index e70e818a..c4db2ed2 100644 --- a/ui/streamlit_ui/rendering/render_elements.py +++ b/ui/rendering/render_elements.py @@ -2,21 +2,20 @@ import streamlit as st from sotopia.database import ( - AgentProfile, + BaseAgentProfile, BaseEnvironmentProfile, EpisodeLog, - CustomEvaluationDimension, + BaseCustomEvaluationDimension, ) from sotopia.envs.parallel import render_text_for_environment -from ui.streamlit_ui.rendering.render_utils import ( +from .render_utils import ( get_full_name, render_messages, local_css, + avatar_mapping, ) -from ui.streamlit_ui.rendering.get_elements import get_agents - -from .render_utils import avatar_mapping +from .get_elements import get_agents role_mapping = { @@ -38,7 +37,7 @@ def display_field(label: str, value: str) -> str: return "" -def render_evaluation_dimension(dimension: CustomEvaluationDimension) -> None: +def render_evaluation_dimension(dimension: BaseCustomEvaluationDimension) -> None: local_css("././css/style.css") st.markdown( @@ -59,7 +58,7 @@ def render_evaluation_dimension(dimension: CustomEvaluationDimension) -> None: def render_evaluation_dimension_list( name: str, - dimensions: list[CustomEvaluationDimension], + dimensions: list[BaseCustomEvaluationDimension], ) -> None: local_css("././css/style.css") @@ -78,7 +77,7 @@ def render_evaluation_dimension_list( ) -def render_character(character: AgentProfile) -> None: +def render_character(character: BaseAgentProfile) -> None: local_css("././css/style.css") full_name = f"{character.first_name} {character.last_name}" diff --git a/ui/streamlit_ui/rendering/render_utils.py b/ui/rendering/render_utils.py similarity index 100% rename from ui/streamlit_ui/rendering/render_utils.py rename to ui/rendering/render_utils.py From 302835a6133a91ed019d530916d7cd5f912dfbde Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Thu, 2 Jan 2025 18:02:19 -0500 Subject: [PATCH 16/26] fix mypy --- ui/pages/display_evaluation_dimensions.py | 8 ++------ ui/pages/render_chat_websocket.py | 8 ++++---- ui/rendering/__init__.py | 4 ++-- ui/rendering/get_elements.py | 20 +++++++++++++------- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/ui/pages/display_evaluation_dimensions.py b/ui/pages/display_evaluation_dimensions.py index f9b6e19c..66665da6 100644 --- a/ui/pages/display_evaluation_dimensions.py +++ b/ui/pages/display_evaluation_dimensions.py @@ -1,5 +1,4 @@ import streamlit as st -from typing import Any from sotopia.database import BaseCustomEvaluationDimension from ui.rendering import ( @@ -30,18 +29,15 @@ def display_evaluation_dimensions() -> None: render_evaluation_dimension(dimension) with st.expander("Evaluation Dimension Lists", expanded=True): - all_dimension_lists: dict[str, list[dict[str, Any]]] = ( + all_dimension_lists: dict[str, list[BaseCustomEvaluationDimension]] = ( get_evaluation_dimensions() ) col1, col2 = st.columns(2, gap="medium") for i, (dimension_list_name, dimensions) in enumerate( all_dimension_lists.items() ): - all_dimensions: list[BaseCustomEvaluationDimension] = [ - BaseCustomEvaluationDimension(**dimension) for dimension in dimensions - ] with col1 if i % 2 == 0 else col2: - render_evaluation_dimension_list(dimension_list_name, all_dimensions) + render_evaluation_dimension_list(dimension_list_name, dimensions) display_evaluation_dimensions() diff --git a/ui/pages/render_chat_websocket.py b/ui/pages/render_chat_websocket.py index b3429df8..a486296c 100644 --- a/ui/pages/render_chat_websocket.py +++ b/ui/pages/render_chat_websocket.py @@ -105,7 +105,7 @@ def send_message(self, message: str | dict[str, Any]) -> None: def _run_event_loop(self) -> None: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) - loop.run_until_complete(self._connect()) # type: ignore + loop.run_until_complete(self._connect()) async def _connect(self) -> None: """Connect to the WebSocket server and handle messages""" @@ -115,8 +115,8 @@ async def _connect(self) -> None: self.websocket = ws # Start tasks for sending and receiving messages - send_task = asyncio.create_task(self._send_messages()) # type: ignore - receive_task = asyncio.create_task(self._receive_messages()) # type: ignore + send_task = asyncio.create_task(self._send_messages()) + receive_task = asyncio.create_task(self._receive_messages()) # Wait for both tasks to complete try: @@ -318,7 +318,7 @@ def chat_demo() -> None: for eval_dim in st.session_state.evaluation_dimension_dict[ st.session_state.evaluation_dimension_choice ]: - evaluation_dimension_str += f"{eval_dim['name']}, " + evaluation_dimension_str += f"{eval_dim.name}, " st.markdown( evaluation_dimension_str[:-2] + ".", diff --git a/ui/rendering/__init__.py b/ui/rendering/__init__.py index 365528ef..a2bb070e 100644 --- a/ui/rendering/__init__.py +++ b/ui/rendering/__init__.py @@ -1,4 +1,4 @@ -from .render_elements import ( # type: ignore +from .render_elements import ( render_environment_profile, render_conversation_and_evaluation, render_character, @@ -13,7 +13,7 @@ local_css, ) -from .get_elements import ( # type: ignore +from .get_elements import ( get_scenarios, get_agents, get_models, diff --git a/ui/rendering/get_elements.py b/ui/rendering/get_elements.py index c88715a5..fca79c0d 100644 --- a/ui/rendering/get_elements.py +++ b/ui/rendering/get_elements.py @@ -30,22 +30,28 @@ def get_agents(id: str = "") -> dict[str, dict[Any, Any]]: return {get_full_name(agent): agent for agent in agents} -def get_evaluation_dimensions() -> dict[str, list[dict[Any, Any]]]: +def get_evaluation_dimensions() -> dict[str, list[BaseCustomEvaluationDimension]]: with requests.get(f"{st.session_state.API_BASE}/evaluation_dimensions") as resp: evaluation_dimensions = resp.json() - return {dimension["name"]: dimension for dimension in evaluation_dimensions} + return { + dimension_list_name: [ + BaseCustomEvaluationDimension(**dimension) for dimension in dimension_list + ] + for dimension_list_name, dimension_list in evaluation_dimensions.items() + } def get_distinct_evaluation_dimensions() -> list[BaseCustomEvaluationDimension]: - all_dimension_lists: dict[str, list[Any]] = get_evaluation_dimensions() + all_dimension_lists: dict[str, list[BaseCustomEvaluationDimension]] = ( + get_evaluation_dimensions() + ) distinct_dimensions: list[BaseCustomEvaluationDimension] = [] distinct_dimension_names: set[str] = set() for dimension_list_name, dimensions in all_dimension_lists.items(): for dimension in dimensions: - custom_dimension = BaseCustomEvaluationDimension(**dimension) - if custom_dimension.name not in distinct_dimension_names: - distinct_dimensions.append(custom_dimension) - distinct_dimension_names.add(custom_dimension.name) + if dimension.name not in distinct_dimension_names: + distinct_dimensions.append(dimension) + distinct_dimension_names.add(dimension.name) return distinct_dimensions From 0e446037879ae38b741ae3a77d19d41f3dc18649 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Sun, 5 Jan 2025 13:14:18 -0500 Subject: [PATCH 17/26] add delete dimension --- sotopia/api/fastapi_server.py | 21 ++++++++++++++++----- tests/api/test_fastapi.py | 6 +++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/sotopia/api/fastapi_server.py b/sotopia/api/fastapi_server.py index 6eaab153..57ffa0f1 100644 --- a/sotopia/api/fastapi_server.py +++ b/sotopia/api/fastapi_server.py @@ -392,6 +392,7 @@ async def get_evaluation_dimensions() -> dict[str, list[CustomEvaluationDimensio CustomEvaluationDimension.get(pk=pk) for pk in custom_evaluation_dimension_list.dimension_pks ] + print(custom_evaluation_dimensions) return custom_evaluation_dimensions @@ -436,7 +437,7 @@ def setup_routes(self) -> None: response_model=dict[str, list[CustomEvaluationDimension]], )(get_evaluation_dimensions) - @self.post("/scenarios/", response_model=str) + @self.post("/scenarios", response_model=str) async def create_scenario(scenario: BaseEnvironmentProfile) -> str: scenario_profile = EnvironmentProfile(**scenario.model_dump()) scenario_profile.save() @@ -444,7 +445,7 @@ async def create_scenario(scenario: BaseEnvironmentProfile) -> str: assert pk is not None return pk - @self.post("/agents/", response_model=str) + @self.post("/agents", response_model=str) async def create_agent(agent: BaseAgentProfile) -> str: agent_profile = AgentProfile(**agent.model_dump()) agent_profile.save() @@ -452,7 +453,7 @@ async def create_agent(agent: BaseAgentProfile) -> str: assert pk is not None return pk - @self.post("/relationship/", response_model=str) + @self.post("/relationship", response_model=str) async def create_relationship(relationship: BaseRelationshipProfile) -> str: relationship_profile = RelationshipProfile(**relationship.model_dump()) relationship_profile.save() @@ -460,7 +461,7 @@ async def create_relationship(relationship: BaseRelationshipProfile) -> str: assert pk is not None return pk - @self.post("/evaluation_dimensions/", response_model=str) + @self.post("/evaluation_dimensions", response_model=str) async def create_evaluation_dimensions( evaluation_dimensions: CustomEvaluationDimensionsWrapper, ) -> str: @@ -504,7 +505,7 @@ async def create_evaluation_dimensions( assert pk is not None return pk - @self.post("/simulate/", response_model=str) + @self.post("/simulate", response_model=str) def simulate(simulation_request: SimulationRequest) -> Response: try: _: EnvironmentProfile = EnvironmentProfile.get( @@ -601,6 +602,16 @@ async def delete_episode(episode_id: str) -> str: EpisodeLog.delete(episode_id) return episode_id + @self.delete( + "/evaluation_dimensions/{evaluation_dimension_list_name}", + response_model=str, + ) + async def delete_evaluation_dimension_list( + evaluation_dimension_list_name: str, + ) -> str: + CustomEvaluationDimensionList.delete(evaluation_dimension_list_name) + return evaluation_dimension_list_name + @self.websocket("/ws/simulation") async def websocket_endpoint(websocket: WebSocket, token: str) -> None: manager = SimulationManager() diff --git a/tests/api/test_fastapi.py b/tests/api/test_fastapi.py index 4e43ec2d..d86bcc37 100644 --- a/tests/api/test_fastapi.py +++ b/tests/api/test_fastapi.py @@ -251,7 +251,7 @@ def test_get_relationship(create_mock_data: Callable[[], None]) -> None: def test_get_evaluation_dimensions(create_mock_data: Callable[[], None]) -> None: - response = client.get("/evaluation_dimensions/") + response = client.get("/evaluation_dimensions") assert response.status_code == 200 assert isinstance(response.json(), dict) assert response.json()["test_dimension_list"][0]["name"] == "test_dimension" @@ -264,7 +264,7 @@ def test_create_agent(create_mock_data: Callable[[], None]) -> None: "first_name": "test_first_name", "last_name": "test_last_name", } - response = client.post("/agents/", json=agent_data) + response = client.post("/agents", json=agent_data) assert response.status_code == 200 assert isinstance(response.json(), str) @@ -277,7 +277,7 @@ def test_create_scenario(create_mock_data: Callable[[], None]) -> None: "scenario": "test_scenario", "tag": "test", } - response = client.post("/scenarios/", json=scenario_data) + response = client.post("/scenarios", json=scenario_data) assert response.status_code == 200 assert isinstance(response.json(), str) From 520a1ddcab20d43608f97adba6eafbbe3eb0f0c1 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Sun, 5 Jan 2025 13:20:12 -0500 Subject: [PATCH 18/26] update streamlit ui --- ui/pages/add_characters.py | 4 ++-- ui/pages/add_evaluation_dimension.py | 4 ++-- ui/pages/add_scenarios.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/pages/add_characters.py b/ui/pages/add_characters.py index 5aea3cc1..65986fb1 100644 --- a/ui/pages/add_characters.py +++ b/ui/pages/add_characters.py @@ -1,6 +1,6 @@ """ Definition -@app.post("/agents/", response_model=str) +@app.post("/agents", response_model=str) async def create_agent(agent: BaseAgentProfile) -> str: agent_profile = BaseAgentProfile(**agent.model_dump()) agent_profile.save() @@ -45,7 +45,7 @@ def rendering_character_form() -> None: print(agent_profile) response = requests.post( - f"{st.session_state.API_BASE}/agents/", + f"{st.session_state.API_BASE}/agents", json=agent_profile.model_dump(), ) diff --git a/ui/pages/add_evaluation_dimension.py b/ui/pages/add_evaluation_dimension.py index 69b57e7a..56c7c1e0 100644 --- a/ui/pages/add_evaluation_dimension.py +++ b/ui/pages/add_evaluation_dimension.py @@ -1,7 +1,7 @@ """ Definition @app.get( - "/evaluation_dimensions/", response_model=dict[str, list[CustomEvaluationDimension]] + "/evaluation_dimensions", response_model=dict[str, list[CustomEvaluationDimension]] ) async def get_evaluation_dimensions() -> dict[str, list[CustomEvaluationDimension]]: custom_evaluation_dimensions: dict[str, list[CustomEvaluationDimension]] = {} @@ -161,7 +161,7 @@ def add_evaluation_dimension() -> None: # st.write(wrapper.dict()) response = requests.post( - f"{st.session_state.API_BASE}/evaluation_dimensions/", + f"{st.session_state.API_BASE}/evaluation_dimensions", json=wrapper.dict(), ) diff --git a/ui/pages/add_scenarios.py b/ui/pages/add_scenarios.py index 66f291e5..c6118d27 100644 --- a/ui/pages/add_scenarios.py +++ b/ui/pages/add_scenarios.py @@ -99,7 +99,7 @@ def rendering_scenario_form() -> None: ) response = requests.post( - f"{st.session_state.API_BASE}/scenarios/", + f"{st.session_state.API_BASE}/scenarios", json=scenario_profile.model_dump(), ) From 5ffdee349cd7f2bef6ec2aed0b132f5eb782637e Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Mon, 6 Jan 2025 11:39:17 -0500 Subject: [PATCH 19/26] ignores the ui directory --- .github/.codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/.codecov.yml b/.github/.codecov.yml index 3caf7156..bdedab05 100644 --- a/.github/.codecov.yml +++ b/.github/.codecov.yml @@ -6,6 +6,7 @@ ignore: - ".github" # ignore the .github directory - "docs" # ignore the tests directory - "figs" # ignore the figs directory + - "ui" # ignore the ui directory coverage: status: From f9e2ea3629f97546e61d1222532d1baaa3bece2b Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 6 Jan 2025 18:28:55 +0000 Subject: [PATCH 20/26] Committing changes before push --- tests/database/test_evaluation_dimensions.py | 42 ++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/database/test_evaluation_dimensions.py diff --git a/tests/database/test_evaluation_dimensions.py b/tests/database/test_evaluation_dimensions.py new file mode 100644 index 00000000..21e5dff4 --- /dev/null +++ b/tests/database/test_evaluation_dimensions.py @@ -0,0 +1,42 @@ +import pytest +from pydantic import ValidationError +from sotopia.database.evaluation_dimensions import ( + EvaluationDimensionBuilder, + CustomEvaluationDimension, +) + + +def test_create_range_validator(): + validator = EvaluationDimensionBuilder.create_range_validator(0, 10) + assert validator(("test", 5)) == ("test", 5) + + with pytest.raises(ValueError): + validator(("test", 11)) + + with pytest.raises(ValueError): + validator(("test", -1)) + + with pytest.raises(ValueError): + validator("invalid") + + +def test_build_dimension_model(): + dimension = CustomEvaluationDimension( + name="test_dimension", + description="A test dimension", + range_high=10, + range_low=0 + ) + dimension.save() + + model = EvaluationDimensionBuilder.build_dimension_model([dimension.pk]) + instance = model(test_dimension=("example", 5)) + assert instance.test_dimension == ("example", 5) + + with pytest.raises(ValidationError): + model(test_dimension=("example", 11)) + + with pytest.raises(ValidationError): + model(test_dimension=("example", -1)) + + dimension.delete() \ No newline at end of file From a45e440847acc93e95bc1b627b5a8a5f67a92386 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Mon, 6 Jan 2025 15:27:06 -0500 Subject: [PATCH 21/26] pytest for eval dimension --- sotopia/database/evaluation_dimensions.py | 11 ++- tests/database/test_evaluation_dimensions.py | 91 +++++++++++++++++--- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/sotopia/database/evaluation_dimensions.py b/sotopia/database/evaluation_dimensions.py index 8a3954df..64648b39 100644 --- a/sotopia/database/evaluation_dimensions.py +++ b/sotopia/database/evaluation_dimensions.py @@ -1,6 +1,6 @@ from redis_om import JsonModel from redis_om.model.model import Field -from pydantic import create_model, BaseModel +from pydantic import create_model, BaseModel, AfterValidator from typing import Type, Callable, Tuple, Annotated, Union, cast, Any @@ -56,7 +56,8 @@ def build_dimension_model(dimension_ids: list[str]) -> Type[BaseModel]: range_validator = EvaluationDimensionBuilder.create_range_validator( dimension.range_low, dimension.range_high ) - field_type = Annotated[Tuple[str, int], range_validator] + # Need to use AfterValidator to ensure validation happens after type checking + field_type = Annotated[Tuple[str, int], AfterValidator(range_validator)] fields[dimension.name] = ( field_type, @@ -84,7 +85,8 @@ def build_dimension_model_from_dict( range_validator = EvaluationDimensionBuilder.create_range_validator( dimension.range_low, dimension.range_high ) - field_type = Annotated[Tuple[str, int], range_validator] + # Need to use AfterValidator to ensure validation happens after type checking + field_type = Annotated[Tuple[str, int], AfterValidator(range_validator)] fields[dimension.name] = ( field_type, @@ -118,7 +120,8 @@ def select_existing_dimension_model_by_name( range_validator = EvaluationDimensionBuilder.create_range_validator( dimension.range_low, dimension.range_high ) - field_type = Annotated[Tuple[str, int], range_validator] + # Need to use AfterValidator to ensure validation happens after type checking + field_type = Annotated[Tuple[str, int], AfterValidator(range_validator)] fields[dimension.name] = ( field_type, diff --git a/tests/database/test_evaluation_dimensions.py b/tests/database/test_evaluation_dimensions.py index 21e5dff4..66a3d86f 100644 --- a/tests/database/test_evaluation_dimensions.py +++ b/tests/database/test_evaluation_dimensions.py @@ -3,10 +3,12 @@ from sotopia.database.evaluation_dimensions import ( EvaluationDimensionBuilder, CustomEvaluationDimension, + CustomEvaluationDimensionList, ) +from typing import Generator, Callable -def test_create_range_validator(): +def test_create_range_validator() -> None: validator = EvaluationDimensionBuilder.create_range_validator(0, 10) assert validator(("test", 5)) == ("test", 5) @@ -16,27 +18,96 @@ def test_create_range_validator(): with pytest.raises(ValueError): validator(("test", -1)) - with pytest.raises(ValueError): - validator("invalid") + +@pytest.fixture +def test_dimension() -> Generator[None, None, None]: + dimension = CustomEvaluationDimension( + pk="tmppk_test_dimension", + name="test_dimension", + description="A test dimension", + range_high=10, + range_low=0, + ) + dimension.save() + yield + dimension.delete(dimension.pk) -def test_build_dimension_model(): +@pytest.fixture +def test_dimension_list() -> Generator[None, None, None]: dimension = CustomEvaluationDimension( + pk="tmppk_test_dimension", name="test_dimension", description="A test dimension", range_high=10, - range_low=0 + range_low=0, ) dimension.save() + dimension_list = CustomEvaluationDimensionList( + pk="tmppk_test_dimension_list", + name="test_list", + dimension_pks=["tmppk_test_dimension"], # type: ignore + ) + dimension_list.save() + yield + dimension.delete("tmppk_test_dimension") + dimension_list.delete("tmppk_test_dimension_list") - model = EvaluationDimensionBuilder.build_dimension_model([dimension.pk]) - instance = model(test_dimension=("example", 5)) - assert instance.test_dimension == ("example", 5) +def test_build_dimension_model(test_dimension: Callable[[], None]) -> None: + # Test building model from dimension id + model = EvaluationDimensionBuilder.build_dimension_model(["tmppk_test_dimension"]) # type: ignore + instance = model(test_dimension=("example", 5)) + assert instance.dict()["test_dimension"] == ("example", 5) + # Test validation errors for out of range values with pytest.raises(ValidationError): model(test_dimension=("example", 11)) - with pytest.raises(ValidationError): model(test_dimension=("example", -1)) - dimension.delete() \ No newline at end of file + +def test_build_dimension_model_from_dict() -> None: + # Test building model from dictionary + dimensions: list[dict[str, str | int]] = [ + { + "name": "test_dim", + "description": "A test dimension", + "range_high": 10, + "range_low": 0, + } + ] + model = EvaluationDimensionBuilder.build_dimension_model_from_dict(dimensions) + + instance = model(test_dim=("example", 5)) + assert instance.dict()["test_dim"] == ("example", 5) + + with pytest.raises(ValidationError): + model(test_dim=("example", 11)) + + +def test_select_existing_dimension_model_by_name( + test_dimension: Callable[[], None], +) -> None: + # Test building model from dimension names + model = EvaluationDimensionBuilder.select_existing_dimension_model_by_name( + ["test_dimension"] + ) + instance = model(test_dimension=("example", 5)) + assert instance.dict()["test_dimension"] == ("example", 5) + + with pytest.raises(ValidationError): + model(test_dimension=("example", 11)) + + +def test_select_existing_dimension_model_by_list_name( + test_dimension_list: Callable[[], None], +) -> None: + # Test building model from list name + model = EvaluationDimensionBuilder.select_existing_dimension_model_by_list_name( + "test_list" + ) + instance = model(test_dimension=("example", 5)) + assert instance.dict()["test_dimension"] == ("example", 5) + + with pytest.raises(ValidationError): + model(test_dimension=("example", 11)) From 24ca6a39551420c1400a7d8ce0ec747770eefb51 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Mon, 6 Jan 2025 15:34:57 -0500 Subject: [PATCH 22/26] fix mypy --- tests/database/test_evaluation_dimensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/database/test_evaluation_dimensions.py b/tests/database/test_evaluation_dimensions.py index 66a3d86f..c319a58a 100644 --- a/tests/database/test_evaluation_dimensions.py +++ b/tests/database/test_evaluation_dimensions.py @@ -46,7 +46,7 @@ def test_dimension_list() -> Generator[None, None, None]: dimension_list = CustomEvaluationDimensionList( pk="tmppk_test_dimension_list", name="test_list", - dimension_pks=["tmppk_test_dimension"], # type: ignore + dimension_pks=["tmppk_test_dimension"], ) dimension_list.save() yield @@ -56,7 +56,7 @@ def test_dimension_list() -> Generator[None, None, None]: def test_build_dimension_model(test_dimension: Callable[[], None]) -> None: # Test building model from dimension id - model = EvaluationDimensionBuilder.build_dimension_model(["tmppk_test_dimension"]) # type: ignore + model = EvaluationDimensionBuilder.build_dimension_model(["tmppk_test_dimension"]) instance = model(test_dimension=("example", 5)) assert instance.dict()["test_dimension"] == ("example", 5) # Test validation errors for out of range values From 6b2db2a366dce6f36bb0c2e8f59717e76543b993 Mon Sep 17 00:00:00 2001 From: Zhe Su <360307598@qq.com> Date: Mon, 6 Jan 2025 21:43:25 -0500 Subject: [PATCH 23/26] clean up comments --- examples/use_custom_dimensions.py | 19 +-------------- ui/pages/add_characters.py | 11 --------- ui/pages/add_evaluation_dimension.py | 35 ---------------------------- ui/pages/add_scenarios.py | 35 ---------------------------- 4 files changed, 1 insertion(+), 99 deletions(-) diff --git a/examples/use_custom_dimensions.py b/examples/use_custom_dimensions.py index ff712224..48c7a195 100644 --- a/examples/use_custom_dimensions.py +++ b/examples/use_custom_dimensions.py @@ -192,24 +192,7 @@ def run_simple_sample_with_custom_samples( if __name__ == "__main__": - """ - A sample dimension: - custom_dimensions: list[dict[str, Union[str, int]]] = [ - { - "name": "transactivity", - "description": "Analyze the provided social interaction episode between the given pair/team, focusing on identifying instances of transactive exchanges. Evaluate the level of transactivity by considering the following aspects: elaboration, building upon ideas, questioning, argumentation. Analyze whether these transactive patterns persist consistently across the entire interaction or if there are notable variations throughout the exchange. In the 'reasoning' field, provide a comprehensive account of the logic and thought process that led to your conclusion. Consider how the observed instances of transactivity contribute to or detract from the overall quality and depth of the interaction. In the 'score' field, provide an integer score ranging from 0 to 10, where a higher score indicates a higher level of transactivity.", - "range_high": 10, - "range_low": 0, - }, - { - "name": "verbal_equity", - "description": "Analyze the script and measure the level of verbal equity reflected in the interaction between the agents. And then analyze the extent to which the interaction shows a balanced distribution of speaking opportunities among team members. In the 'reasoning' field, provide a comprehensive account of the logic or thought process that led you to your conclusion. Further, provide an integer score ranging from 0 and 10 in the 'score' field. A higher score indicates a higher level of verbal equity.", - "range_high": 10, - "range_low": 0, - }, - ] - """ - + # here is a sample dimension custom_dimensions: list[dict[str, Union[str, int]]] = [ { "name": "transactivity", diff --git a/ui/pages/add_characters.py b/ui/pages/add_characters.py index 65986fb1..0bf2f698 100644 --- a/ui/pages/add_characters.py +++ b/ui/pages/add_characters.py @@ -1,14 +1,3 @@ -""" -Definition -@app.post("/agents", response_model=str) -async def create_agent(agent: BaseAgentProfile) -> str: - agent_profile = BaseAgentProfile(**agent.model_dump()) - agent_profile.save() - pk = agent_profile.pk - assert pk is not None - return pk -""" - import streamlit as st import requests diff --git a/ui/pages/add_evaluation_dimension.py b/ui/pages/add_evaluation_dimension.py index 56c7c1e0..e838cd1a 100644 --- a/ui/pages/add_evaluation_dimension.py +++ b/ui/pages/add_evaluation_dimension.py @@ -1,38 +1,3 @@ -""" -Definition -@app.get( - "/evaluation_dimensions", response_model=dict[str, list[CustomEvaluationDimension]] -) -async def get_evaluation_dimensions() -> dict[str, list[CustomEvaluationDimension]]: - custom_evaluation_dimensions: dict[str, list[CustomEvaluationDimension]] = {} - all_custom_evaluation_dimension_list = CustomEvaluationDimensionList.all() - for custom_evaluation_dimension_list in all_custom_evaluation_dimension_list: - assert isinstance( - custom_evaluation_dimension_list, CustomEvaluationDimensionList - ) - custom_evaluation_dimensions[custom_evaluation_dimension_list.name] = [ - CustomEvaluationDimension.get(pk=pk) - for pk in custom_evaluation_dimension_list.dimension_pks - ] - return custom_evaluation_dimensions - -class CustomEvaluationDimensionsWrapper(BaseModel): - pk: str = "" - name: str = Field( - default="", description="The name of the custom evaluation dimension list" - ) - dimensions: list[CustomEvaluationDimension] = Field( - default=[], description="The dimensions of the custom evaluation dimension list" - ) - -class CustomEvaluationDimension(JsonModel): - name: str = Field(index=True) - description: str = Field(index=True) - range_high: int = Field(index=True) - range_low: int = Field(index=True) - -""" - import streamlit as st import requests from sotopia.database import BaseCustomEvaluationDimension diff --git a/ui/pages/add_scenarios.py b/ui/pages/add_scenarios.py index c6118d27..dd05cd98 100644 --- a/ui/pages/add_scenarios.py +++ b/ui/pages/add_scenarios.py @@ -1,38 +1,3 @@ -""" -Definition -@app.post("/scenarios/", response_model=str) -async def create_scenario(scenario: EnvironmentProfileWrapper) -> str: - scenario_profile = EnvironmentProfile(**scenario.model_dump()) - scenario_profile.save() - pk = scenario_profile.pk - assert pk is not None - return pk - -class EnvironmentProfileWrapper(BaseModel): - Wrapper for EnvironmentProfile to avoid pydantic v2 issues - - codename: str - source: str = "" - scenario: str = "" - agent_goals: list[str] = [] - relationship: Literal[0, 1, 2, 3, 4, 5] = 0 - age_constraint: str | None = None - occupation_constraint: str | None = None - agent_constraint: list[list[str]] | None = None - tag: str = "" - -class RelationshipType(IntEnum): - stranger = 0 - know_by_name = 1 - acquaintance = 2 - friend = 3 - romantic_relationship = 4 - family_member = 5 - -The age constraint of the environment, a list of tuples, each tuple is a range of age, e.g., '[(18, 25), (30, 40)]' -means the environment is only available to agent one between 18 and 25, and agent two between 30 and 40 -""" - import streamlit as st from ui.rendering import local_css from sotopia.database import BaseEnvironmentProfile, RelationshipType From 56d248785cf79e34fff0bf0fc3aaf341c2ec1634 Mon Sep 17 00:00:00 2001 From: XuhuiZhou Date: Tue, 7 Jan 2025 17:16:31 -0500 Subject: [PATCH 24/26] run non streaming simulation --- sotopia/api/fastapi_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sotopia/api/fastapi_server.py b/sotopia/api/fastapi_server.py index 57ffa0f1..f1a3220c 100644 --- a/sotopia/api/fastapi_server.py +++ b/sotopia/api/fastapi_server.py @@ -229,7 +229,7 @@ async def send_error( ) -async def run_simulation( +async def nonstreaming_simulation( episode_pk: str, simulation_request: SimulationRequest, simulation_status: NonStreamingSimulationStatus, @@ -548,7 +548,7 @@ def simulate(simulation_request: SimulationRequest) -> Response: simulation_status.save() queue = rq.Queue("default", connection=get_redis_connection()) queue.enqueue( - run_simulation, + nonstreaming_simulation, episode_pk=episode_pk, simulation_request=simulation_request, simulation_status=simulation_status, From 2f03b97628ef96d95b1135e67151d1f53e0107b9 Mon Sep 17 00:00:00 2001 From: Zhe Su <360307598@qq.com> Date: Wed, 8 Jan 2025 00:43:39 -0500 Subject: [PATCH 25/26] add pytest for websocket --- tests/api/test_fastapi.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/api/test_fastapi.py b/tests/api/test_fastapi.py index d86bcc37..6549f517 100644 --- a/tests/api/test_fastapi.py +++ b/tests/api/test_fastapi.py @@ -341,6 +341,37 @@ def test_delete_evaluation_dimension(create_mock_data: Callable[[], None]) -> No assert isinstance(response.json(), str) +def test_websocket_simulate(create_mock_data: Callable[[], None]) -> None: + LOCAL_MODEL = "custom/llama3.2:1b@http://localhost:8000/v1" + with client.websocket_connect("/ws/simulation?token=test") as websocket: + start_msg = { + "type": "START_SIM", + "data": { + "env_id": "tmppk_env_profile", + "agent_ids": ["tmppk_agent1", "tmppk_agent2"], + "agent_models": [LOCAL_MODEL, LOCAL_MODEL], + "evaluator_model": LOCAL_MODEL, + "evaluation_dimension_list_name": "test_dimension_list", + }, + } + websocket.send_json(start_msg) + + # check the streaming response, stop when we received 2 messages + messages = [] + while len(messages) < 2: + message = websocket.receive_json() + assert ( + message["type"] == "SERVER_MSG" + ), f"Expected SERVER_MSG, got {message['type']}, full msg: {message}" + messages.append(message) + + # send the end message + end_msg = { + "type": "FINISH_SIM", + } + websocket.send_json(end_msg) + + # def test_simulate(create_mock_data: Callable[[], None]) -> None: # response = client.post( # "/simulate", From 9fe8478f8ae1f9aa52e82b47ed626eb76030e605 Mon Sep 17 00:00:00 2001 From: Zhe Su <360307598@qq.com> Date: Wed, 8 Jan 2025 01:16:46 -0500 Subject: [PATCH 26/26] fix mypy issue --- tests/api/test_fastapi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/api/test_fastapi.py b/tests/api/test_fastapi.py index 6549f517..0a4f6c3c 100644 --- a/tests/api/test_fastapi.py +++ b/tests/api/test_fastapi.py @@ -10,7 +10,7 @@ from sotopia.messages import SimpleMessage from sotopia.api.fastapi_server import app import pytest -from typing import Generator, Callable +from typing import Generator, Callable, Any client = TestClient(app) @@ -357,7 +357,7 @@ def test_websocket_simulate(create_mock_data: Callable[[], None]) -> None: websocket.send_json(start_msg) # check the streaming response, stop when we received 2 messages - messages = [] + messages: list[dict[str, Any]] = [] while len(messages) < 2: message = websocket.receive_json() assert (